From 12d55cef370348ac867d03518f1631b414be15c5 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Wed, 9 May 2018 19:23:17 +0100 Subject: [PATCH] Initial commit. --- .gitignore | 10 + APRSTransmit/APRSParser.cpp | 601 ++++ APRSTransmit/APRSParser.h | 79 + APRSTransmit/APRSTransmit.cpp | 174 + APRSTransmit/APRSTransmit.h | 44 + APRSTransmit/APRSTransmit.vcxproj | 188 ++ APRSTransmit/APRSTransmit.vcxproj.filters | 32 + APRSTransmit/APRSTransmitApp.cpp | 100 + APRSTransmit/APRSTransmitAppD.cpp | 272 ++ APRSTransmit/APRSTransmitAppD.h | 54 + APRSTransmit/APRSTransmitD.vcxproj | 189 ++ APRSTransmit/APRSTransmitD.vcxproj.filters | 35 + CHANGES.txt | 1478 ++++++++ COPYING.txt | 340 ++ Common/AMBEData.cpp | 629 ++++ Common/AMBEData.h | 108 + Common/APRSCollector.cpp | 661 ++++ Common/APRSCollector.h | 84 + Common/APRSWriter.cpp | 394 +++ Common/APRSWriter.h | 98 + Common/APRSWriterThread.cpp | 263 ++ Common/APRSWriterThread.h | 61 + Common/AnnouncementUnit.cpp | 151 + Common/AnnouncementUnit.h | 60 + Common/AudioUnit.cpp | 505 +++ Common/AudioUnit.h | 120 + Common/CCITTChecksum.cpp | 142 + Common/CCITTChecksum.h | 39 + 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/CacheManager.cpp | 128 + Common/CacheManager.h | 158 + Common/CallsignList.cpp | 76 + Common/CallsignList.h | 40 + Common/CallsignServer.cpp | 173 + Common/CallsignServer.h | 54 + Common/Common.vcxproj | 295 ++ Common/Common.vcxproj.filters | 437 +++ Common/ConnectData.cpp | 543 +++ Common/ConnectData.h | 81 + Common/DCSHandler.cpp | 884 +++++ Common/DCSHandler.h | 140 + Common/DCSProtocolHandler.cpp | 196 ++ Common/DCSProtocolHandler.h | 75 + Common/DCSProtocolHandlerPool.cpp | 132 + Common/DCSProtocolHandlerPool.h | 56 + Common/DDData.cpp | 313 ++ Common/DDData.h | 89 + Common/DDHandler.cpp | 394 +++ Common/DDHandler.h | 73 + Common/DExtraHandler.cpp | 1004 ++++++ Common/DExtraHandler.h | 138 + Common/DExtraProtocolHandler.cpp | 228 ++ Common/DExtraProtocolHandler.h | 79 + Common/DExtraProtocolHandlerPool.cpp | 137 + Common/DExtraProtocolHandlerPool.h | 57 + Common/DPlusAuthenticator.cpp | 286 ++ Common/DPlusAuthenticator.h | 59 + Common/DPlusHandler.cpp | 953 ++++++ Common/DPlusHandler.h | 147 + Common/DPlusProtocolHandler.cpp | 233 ++ Common/DPlusProtocolHandler.h | 79 + Common/DPlusProtocolHandlerPool.cpp | 137 + Common/DPlusProtocolHandlerPool.h | 57 + Common/DRATSServer.cpp | 375 +++ Common/DRATSServer.h | 65 + Common/DStarDefines.h | 177 + Common/DTMF.cpp | 317 ++ Common/DTMF.h | 49 + Common/DVTOOLFileReader.cpp | 181 + Common/DVTOOLFileReader.h | 61 + Common/Defs.h | 143 + Common/DummyRepeaterProtocolHandler.cpp | 133 + Common/DummyRepeaterProtocolHandler.h | 67 + Common/EchoUnit.cpp | 154 + Common/EchoUnit.h | 63 + Common/G2Handler.cpp | 198 ++ Common/G2Handler.h | 72 + Common/G2ProtocolHandler.cpp | 150 + Common/G2ProtocolHandler.h | 68 + Common/GatewayCache.cpp | 57 + Common/GatewayCache.h | 106 + Common/HBRepeaterProtocolHandler.cpp | 285 ++ Common/HBRepeaterProtocolHandler.h | 76 + Common/HeaderData.cpp | 951 ++++++ Common/HeaderData.h | 130 + Common/HeaderLogger.cpp | 103 + Common/HeaderLogger.h | 46 + Common/HeardData.cpp | 134 + Common/HeardData.h | 61 + Common/HostFile.cpp | 107 + Common/HostFile.h | 45 + Common/IRCDDBGatewayConfig.cpp | 2651 +++++++++++++++ Common/IRCDDBGatewayConfig.h | 311 ++ Common/IcomRepeaterProtocolHandler.cpp | 650 ++++ Common/IcomRepeaterProtocolHandler.h | 176 + Common/LogEvent.cpp | 45 + Common/LogEvent.h | 40 + Common/Logger.cpp | 122 + Common/Logger.h | 45 + Common/PollData.cpp | 284 ++ Common/PollData.h | 76 + Common/ReflectorCallback.h | 44 + Common/RemoteHandler.cpp | 214 ++ Common/RemoteHandler.h | 51 + Common/RemoteLinkData.cpp | 57 + Common/RemoteLinkData.h | 45 + Common/RemoteProtocolHandler.cpp | 441 +++ Common/RemoteProtocolHandler.h | 83 + Common/RemoteRepeaterData.cpp | 67 + Common/RemoteRepeaterData.h | 50 + Common/RemoteStarNetGroup.cpp | 75 + Common/RemoteStarNetGroup.h | 52 + Common/RemoteStarNetUser.cpp | 45 + Common/RemoteStarNetUser.h | 39 + Common/RepeaterCache.cpp | 54 + Common/RepeaterCache.h | 70 + Common/RepeaterCallback.h | 38 + Common/RepeaterHandler.cpp | 2970 +++++++++++++++++ Common/RepeaterHandler.h | 305 ++ Common/RepeaterProtocolHandler.h | 67 + Common/RingBuffer.h | 105 + Common/SHA256.cpp | 369 ++ Common/SHA256.h | 73 + Common/SlowDataEncoder.cpp | 358 ++ Common/SlowDataEncoder.h | 67 + Common/StarNetHandler.cpp | 1338 ++++++++ Common/StarNetHandler.h | 221 ++ Common/StatusData.cpp | 75 + Common/StatusData.h | 49 + Common/TCPReaderWriterClient.cpp | 292 ++ Common/TCPReaderWriterClient.h | 60 + Common/TCPReaderWriterServer.cpp | 319 ++ Common/TCPReaderWriterServer.h | 65 + Common/TextCollector.cpp | 141 + Common/TextCollector.h | 52 + Common/TextData.cpp | 106 + Common/TextData.h | 51 + Common/Timer.cpp | 67 + Common/Timer.h | 89 + Common/UDPReaderWriter.cpp | 230 ++ Common/UDPReaderWriter.h | 58 + Common/UserCache.cpp | 56 + Common/UserCache.h | 82 + Common/Utils.cpp | 257 ++ Common/Utils.h | 40 + Common/Version.h | 32 + Common/VersionUnit.cpp | 142 + Common/VersionUnit.h | 57 + Common/XLXHostsFileDownloader.cpp | 103 + Common/XLXHostsFileDownloader.h | 30 + Data/CCS_Hosts.txt | 15 + Data/DCS_Hosts.txt | 244 ++ Data/DExtra_Hosts.txt | 199 ++ Data/DPlus_Hosts.txt | 110 + Data/TIME_de_DE.ambe | Bin 0 -> 17248 bytes Data/TIME_de_DE.indx | 31 + Data/TIME_en_GB.ambe | Bin 0 -> 17941 bytes Data/TIME_en_GB.indx | 26 + Data/TIME_en_US.ambe | Bin 0 -> 16231 bytes Data/TIME_en_US.indx | 26 + Data/TIME_fr_FR.ambe | Bin 0 -> 10984 bytes Data/TIME_fr_FR.indx | 20 + Data/TIME_se_SE.ambe | Bin 0 -> 15340 bytes Data/TIME_se_SE.indx | 17 + Data/de_DE.ambe | Bin 0 -> 21262 bytes Data/de_DE.indx | 44 + Data/dk_DK.ambe | Bin 0 -> 19669 bytes Data/dk_DK.indx | 44 + Data/en_GB.ambe | Bin 0 -> 22000 bytes Data/en_GB.indx | 44 + Data/en_US.ambe | Bin 0 -> 26734 bytes Data/en_US.indx | 44 + Data/es_ES.ambe | Bin 0 -> 22315 bytes Data/es_ES.indx | 44 + Data/fr_FR.ambe | Bin 0 -> 20137 bytes Data/fr_FR.indx | 44 + Data/it_IT.ambe | Bin 0 -> 22099 bytes Data/it_IT.indx | 44 + Data/no_NO.ambe | Bin 0 -> 20164 bytes Data/no_NO.indx | 44 + Data/pl_PL.ambe | Bin 0 -> 24169 bytes Data/pl_PL.indx | 44 + Data/se_SE.ambe | Bin 0 -> 24205 bytes Data/se_SE.indx | 44 + GUICommon/AddressTextCtrl.cpp | 29 + GUICommon/AddressTextCtrl.h | 37 + GUICommon/CallsignTextCtrl.cpp | 29 + GUICommon/CallsignTextCtrl.h | 37 + GUICommon/DCSSet.cpp | 156 + GUICommon/DCSSet.h | 46 + GUICommon/DExtraSet.cpp | 93 + GUICommon/DExtraSet.h | 40 + GUICommon/DPRSSet.cpp | 114 + GUICommon/DPRSSet.h | 44 + GUICommon/DPlusSet.cpp | 122 + GUICommon/DPlusSet.h | 45 + GUICommon/DescriptionTextCtrl.cpp | 29 + GUICommon/DescriptionTextCtrl.h | 37 + GUICommon/GUICommon.vcxproj | 169 + GUICommon/GUICommon.vcxproj.filters | 101 + GUICommon/PortTextCtrl.cpp | 29 + GUICommon/PortTextCtrl.h | 37 + GUICommon/RemoteSet.cpp | 120 + GUICommon/RemoteSet.h | 44 + GUICommon/RepeaterDataSet.cpp | 554 +++ GUICommon/RepeaterDataSet.h | 71 + GUICommon/RepeaterInfoSet.cpp | 218 ++ GUICommon/RepeaterInfoSet.h | 58 + GUICommon/RestrictedTextCtrl.cpp | 40 + GUICommon/RestrictedTextCtrl.h | 32 + GUICommon/StarNetSet.cpp | 509 +++ GUICommon/StarNetSet.h | 68 + GUICommon/XLXSet.cpp | 143 + GUICommon/XLXSet.h | 46 + README.md | 24 + RemoteControl/RemoteControl.vcxproj | 214 ++ RemoteControl/RemoteControl.vcxproj.filters | 98 + RemoteControl/RemoteControlApp.cpp | 141 + RemoteControl/RemoteControlApp.h | 59 + RemoteControl/RemoteControlAppD.cpp | 287 ++ RemoteControl/RemoteControlCallsignData.cpp | 59 + RemoteControl/RemoteControlCallsignData.h | 43 + RemoteControl/RemoteControlConfig.cpp | 98 + RemoteControl/RemoteControlConfig.h | 42 + RemoteControl/RemoteControlDefs.h | 28 + RemoteControl/RemoteControlFrame.cpp | 348 ++ RemoteControl/RemoteControlFrame.h | 79 + RemoteControl/RemoteControlLinkData.cpp | 59 + RemoteControl/RemoteControlLinkData.h | 45 + RemoteControl/RemoteControlPreferences.cpp | 70 + RemoteControl/RemoteControlPreferences.h | 42 + .../RemoteControlRemoteControlHandler.cpp | 557 ++++ .../RemoteControlRemoteControlHandler.h | 86 + RemoteControl/RemoteControlRemoteSet.cpp | 112 + RemoteControl/RemoteControlRemoteSet.h | 44 + RemoteControl/RemoteControlRepeaterData.cpp | 67 + RemoteControl/RemoteControlRepeaterData.h | 50 + RemoteControl/RemoteControlRepeaterPanel.cpp | 397 +++ RemoteControl/RemoteControlRepeaterPanel.h | 55 + RemoteControl/RemoteControlStarNetGroup.cpp | 75 + RemoteControl/RemoteControlStarNetGroup.h | 52 + RemoteControl/RemoteControlStarNetPanel.cpp | 188 ++ RemoteControl/RemoteControlStarNetPanel.h | 51 + RemoteControl/RemoteControlStarNetUser.cpp | 45 + RemoteControl/RemoteControlStarNetUser.h | 39 + StarNetServer/StarNetServer.vcxproj | 212 ++ StarNetServer/StarNetServer.vcxproj.filters | 80 + StarNetServer/StarNetServerApp.cpp | 947 ++++++ StarNetServer/StarNetServerApp.h | 172 + StarNetServer/StarNetServerAppD.cpp | 561 ++++ StarNetServer/StarNetServerAppD.h | 46 + StarNetServer/StarNetServerCallsignSet.cpp | 94 + StarNetServer/StarNetServerCallsignSet.h | 44 + StarNetServer/StarNetServerConfig.cpp | 2415 ++++++++++++++ StarNetServer/StarNetServerConfig.h | 314 ++ StarNetServer/StarNetServerDefs.h | 35 + StarNetServer/StarNetServerFrame.cpp | 719 ++++ StarNetServer/StarNetServerFrame.h | 50 + StarNetServer/StarNetServerIrcDDBSet.cpp | 107 + StarNetServer/StarNetServerIrcDDBSet.h | 42 + StarNetServer/StarNetServerLogRedirect.cpp | 56 + StarNetServer/StarNetServerLogRedirect.h | 35 + .../StarNetServerMiscellaneousSet.cpp | 64 + StarNetServer/StarNetServerMiscellaneousSet.h | 40 + StarNetServer/StarNetServerPreferences.cpp | 1091 ++++++ StarNetServer/StarNetServerPreferences.h | 305 ++ StarNetServer/StarNetServerThread.cpp | 627 ++++ StarNetServer/StarNetServerThread.h | 88 + StarNetServer/StarNetServerThreadHelper.cpp | 58 + StarNetServer/StarNetServerThreadHelper.h | 42 + TextTransmit/TextTransmit.cpp | 209 ++ TextTransmit/TextTransmit.h | 42 + TextTransmit/TextTransmit.vcxproj | 185 + TextTransmit/TextTransmit.vcxproj.filters | 23 + TimeServer/TimeServer.vcxproj | 206 ++ TimeServer/TimeServer.vcxproj.filters | 74 + TimeServer/TimeServerAnnouncementsSet.cpp | 116 + TimeServer/TimeServerAnnouncementsSet.h | 46 + TimeServer/TimeServerApp.cpp | 241 ++ TimeServer/TimeServerApp.h | 75 + TimeServer/TimeServerConfig.cpp | 331 ++ TimeServer/TimeServerConfig.h | 69 + TimeServer/TimeServerD.cpp | 180 + TimeServer/TimeServerD.h | 48 + TimeServer/TimeServerDefs.h | 57 + TimeServer/TimeServerFrame.cpp | 232 ++ TimeServer/TimeServerFrame.h | 50 + TimeServer/TimeServerGatewaySet.cpp | 184 + TimeServer/TimeServerGatewaySet.h | 56 + TimeServer/TimeServerLogRedirect.cpp | 56 + TimeServer/TimeServerLogRedirect.h | 35 + TimeServer/TimeServerPreferences.cpp | 114 + TimeServer/TimeServerPreferences.h | 54 + TimeServer/TimeServerThread.cpp | 1466 ++++++++ TimeServer/TimeServerThread.h | 122 + TimeServer/TimeServerThreadHelper.cpp | 58 + TimeServer/TimeServerThreadHelper.h | 42 + TimerControl/TimerControl.vcxproj | 209 ++ TimerControl/TimerControl.vcxproj.filters | 83 + TimerControl/TimerControlApp.cpp | 262 ++ TimerControl/TimerControlApp.h | 74 + TimerControl/TimerControlAppD.cpp | 206 ++ TimerControl/TimerControlAppD.h | 47 + TimerControl/TimerControlConfig.cpp | 251 ++ TimerControl/TimerControlConfig.h | 62 + TimerControl/TimerControlDefs.h | 32 + TimerControl/TimerControlFrame.cpp | 343 ++ TimerControl/TimerControlFrame.h | 72 + TimerControl/TimerControlItem.h | 35 + TimerControl/TimerControlItemFile.cpp | 137 + TimerControl/TimerControlItemFile.h | 40 + TimerControl/TimerControlPreferences.cpp | 75 + TimerControl/TimerControlPreferences.h | 43 + .../TimerControlRemoteControlHandler.cpp | 295 ++ .../TimerControlRemoteControlHandler.h | 76 + TimerControl/TimerControlRemoteSet.cpp | 136 + TimerControl/TimerControlRemoteSet.h | 46 + TimerControl/TimerControlRepeaterPanel.cpp | 592 ++++ TimerControl/TimerControlRepeaterPanel.h | 61 + TimerControl/TimerControlThread.cpp | 291 ++ TimerControl/TimerControlThread.h | 59 + TimerControl/TimerControlThreadHelper.cpp | 65 + TimerControl/TimerControlThreadHelper.h | 44 + VoiceTransmit/VoiceStore.cpp | 119 + VoiceTransmit/VoiceStore.h | 48 + VoiceTransmit/VoiceTransmit.cpp | 175 + VoiceTransmit/VoiceTransmit.h | 43 + VoiceTransmit/VoiceTransmit.vcxproj | 187 ++ VoiceTransmit/VoiceTransmit.vcxproj.filters | 29 + debian/README | 6 + debian/README.Debian | 6 + debian/README.source | 10 + debian/aprstransmitd.aprstransmitd@.service | 14 + debian/aprstransmitd.conf | 3 + debian/aprstransmitd.install | 2 + debian/changelog | 5 + debian/compat | 1 + debian/control | 91 + debian/copyright | 34 + debian/docs | 3 + debian/ircddbgateway-tools.install | 3 + debian/ircddbgateway.install | 2 + debian/ircddbgateway.logrotate | 7 + debian/ircddbgatewayd.install | 1 + debian/ircddbgatewayd.ircddbgatewayd.service | 11 + debian/ircddbgatewayd.logrotate | 7 + debian/opendv-base.dirs | 2 + debian/opendv-base.install | 34 + debian/opendv-base.postinst | 48 + debian/remotecontrol.install | 1 + debian/remotecontrold.install | 1 + debian/rules | 36 + debian/source/format | 1 + debian/starnetserver.install | 1 + debian/starnetserver.logrotate | 7 + debian/starnetserverd.install | 1 + debian/starnetserverd.logrotate | 7 + debian/starnetserverd.starnetserverd.service | 11 + debian/timercontrol.install | 1 + debian/timercontrol.logrotate | 7 + debian/timercontrold.install | 1 + debian/timercontrold.logrotate | 7 + debian/timercontrold.timercontrold.service | 12 + debian/timeserver.install | 1 + debian/timeserver.logrotate | 7 + debian/timeserverd.install | 1 + debian/timeserverd.logrotate | 7 + debian/timeserverd.timeserverd.service | 12 + ircDDB/IRCApplication.h | 50 + ircDDB/IRCClient.cpp | 515 +++ ircDDB/IRCClient.h | 77 + ircDDB/IRCDDB.cpp | 32 + ircDDB/IRCDDB.h | 163 + ircDDB/IRCDDBApp.cpp | 1353 ++++++++ ircDDB/IRCDDBApp.h | 103 + ircDDB/IRCDDBClient.cpp | 487 +++ ircDDB/IRCDDBClient.h | 162 + ircDDB/IRCDDBMultiClient.cpp | 354 ++ ircDDB/IRCDDBMultiClient.h | 172 + ircDDB/IRCMessage.cpp | 177 + ircDDB/IRCMessage.h | 71 + ircDDB/IRCMessageQueue.cpp | 137 + ircDDB/IRCMessageQueue.h | 79 + ircDDB/IRCProtocol.cpp | 447 +++ ircDDB/IRCProtocol.h | 65 + ircDDB/IRCReceiver.cpp | 237 ++ ircDDB/IRCReceiver.h | 63 + ircDDB/IRCutils.cpp | 179 + ircDDB/IRCutils.h | 31 + ircDDB/LICENSE | 340 ++ ircDDB/README | 0 ircDDB/ircDDB.vcxproj | 171 + ircDDB/ircDDB.vcxproj.filters | 83 + ircDDBGateway.sln | 151 + ircDDBGateway/IRCDDBGatewayApp.cpp | 923 +++++ ircDDBGateway/IRCDDBGatewayApp.h | 71 + ircDDBGateway/IRCDDBGatewayAppD.cpp | 916 +++++ ircDDBGateway/IRCDDBGatewayAppD.h | 51 + ircDDBGateway/IRCDDBGatewayDefs.h | 49 + ircDDBGateway/IRCDDBGatewayFrame.cpp | 322 ++ ircDDBGateway/IRCDDBGatewayFrame.h | 56 + ircDDBGateway/IRCDDBGatewayLogRedirect.cpp | 56 + ircDDBGateway/IRCDDBGatewayLogRedirect.h | 35 + ircDDBGateway/IRCDDBGatewayStatusData.cpp | 87 + ircDDBGateway/IRCDDBGatewayStatusData.h | 55 + ircDDBGateway/IRCDDBGatewayThread.cpp | 1389 ++++++++ ircDDBGateway/IRCDDBGatewayThread.h | 155 + ircDDBGateway/IRCDDBGatewayThreadHelper.cpp | 65 + ircDDBGateway/IRCDDBGatewayThreadHelper.h | 45 + ircDDBGateway/ircDDBGateway.vcxproj | 200 ++ ircDDBGateway/ircDDBGateway.vcxproj.filters | 56 + .../IRCDDBGatewayConfigApp.cpp | 94 + ircDDBGatewayConfig/IRCDDBGatewayConfigApp.h | 47 + ircDDBGatewayConfig/IRCDDBGatewayConfigDefs.h | 39 + .../IRCDDBGatewayConfigFrame.cpp | 636 ++++ .../IRCDDBGatewayConfigFrame.h | 82 + .../IRCDDBGatewayConfigGatewaySet.cpp | 320 ++ .../IRCDDBGatewayConfigGatewaySet.h | 66 + .../IRCDDBGatewayConfigIrcDDBSet.cpp | 130 + .../IRCDDBGatewayConfigIrcDDBSet.h | 44 + .../IRCDDBGatewayConfigMiscellaneousSet.cpp | 185 + .../IRCDDBGatewayConfigMiscellaneousSet.h | 55 + .../ircDDBGatewayConfig.vcxproj | 198 ++ .../ircDDBGatewayConfig.vcxproj.filters | 50 + 430 files changed, 72067 insertions(+) create mode 100644 .gitignore create mode 100644 APRSTransmit/APRSParser.cpp create mode 100644 APRSTransmit/APRSParser.h create mode 100644 APRSTransmit/APRSTransmit.cpp create mode 100644 APRSTransmit/APRSTransmit.h create mode 100644 APRSTransmit/APRSTransmit.vcxproj create mode 100644 APRSTransmit/APRSTransmit.vcxproj.filters create mode 100644 APRSTransmit/APRSTransmitApp.cpp create mode 100644 APRSTransmit/APRSTransmitAppD.cpp create mode 100644 APRSTransmit/APRSTransmitAppD.h create mode 100644 APRSTransmit/APRSTransmitD.vcxproj create mode 100644 APRSTransmit/APRSTransmitD.vcxproj.filters create mode 100644 CHANGES.txt create mode 100644 COPYING.txt create mode 100644 Common/AMBEData.cpp create mode 100644 Common/AMBEData.h create mode 100644 Common/APRSCollector.cpp create mode 100644 Common/APRSCollector.h create mode 100644 Common/APRSWriter.cpp create mode 100644 Common/APRSWriter.h create mode 100644 Common/APRSWriterThread.cpp create mode 100644 Common/APRSWriterThread.h create mode 100644 Common/AnnouncementUnit.cpp create mode 100644 Common/AnnouncementUnit.h create mode 100644 Common/AudioUnit.cpp create mode 100644 Common/AudioUnit.h create mode 100644 Common/CCITTChecksum.cpp create mode 100644 Common/CCITTChecksum.h 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 Common/CacheManager.cpp create mode 100644 Common/CacheManager.h create mode 100644 Common/CallsignList.cpp create mode 100644 Common/CallsignList.h create mode 100644 Common/CallsignServer.cpp create mode 100644 Common/CallsignServer.h create mode 100644 Common/Common.vcxproj create mode 100644 Common/Common.vcxproj.filters create mode 100644 Common/ConnectData.cpp create mode 100644 Common/ConnectData.h create mode 100644 Common/DCSHandler.cpp create mode 100644 Common/DCSHandler.h create mode 100644 Common/DCSProtocolHandler.cpp create mode 100644 Common/DCSProtocolHandler.h create mode 100644 Common/DCSProtocolHandlerPool.cpp create mode 100644 Common/DCSProtocolHandlerPool.h create mode 100644 Common/DDData.cpp create mode 100644 Common/DDData.h create mode 100644 Common/DDHandler.cpp create mode 100644 Common/DDHandler.h create mode 100644 Common/DExtraHandler.cpp create mode 100644 Common/DExtraHandler.h create mode 100644 Common/DExtraProtocolHandler.cpp create mode 100644 Common/DExtraProtocolHandler.h create mode 100644 Common/DExtraProtocolHandlerPool.cpp create mode 100644 Common/DExtraProtocolHandlerPool.h create mode 100644 Common/DPlusAuthenticator.cpp create mode 100644 Common/DPlusAuthenticator.h create mode 100644 Common/DPlusHandler.cpp create mode 100644 Common/DPlusHandler.h create mode 100644 Common/DPlusProtocolHandler.cpp create mode 100644 Common/DPlusProtocolHandler.h create mode 100644 Common/DPlusProtocolHandlerPool.cpp create mode 100644 Common/DPlusProtocolHandlerPool.h create mode 100644 Common/DRATSServer.cpp create mode 100644 Common/DRATSServer.h create mode 100644 Common/DStarDefines.h create mode 100644 Common/DTMF.cpp create mode 100644 Common/DTMF.h create mode 100644 Common/DVTOOLFileReader.cpp create mode 100644 Common/DVTOOLFileReader.h create mode 100644 Common/Defs.h create mode 100644 Common/DummyRepeaterProtocolHandler.cpp create mode 100644 Common/DummyRepeaterProtocolHandler.h create mode 100644 Common/EchoUnit.cpp create mode 100644 Common/EchoUnit.h create mode 100644 Common/G2Handler.cpp create mode 100644 Common/G2Handler.h create mode 100644 Common/G2ProtocolHandler.cpp create mode 100644 Common/G2ProtocolHandler.h create mode 100644 Common/GatewayCache.cpp create mode 100644 Common/GatewayCache.h create mode 100644 Common/HBRepeaterProtocolHandler.cpp create mode 100644 Common/HBRepeaterProtocolHandler.h create mode 100644 Common/HeaderData.cpp create mode 100644 Common/HeaderData.h create mode 100644 Common/HeaderLogger.cpp create mode 100644 Common/HeaderLogger.h create mode 100644 Common/HeardData.cpp create mode 100644 Common/HeardData.h create mode 100644 Common/HostFile.cpp create mode 100644 Common/HostFile.h create mode 100644 Common/IRCDDBGatewayConfig.cpp create mode 100644 Common/IRCDDBGatewayConfig.h create mode 100644 Common/IcomRepeaterProtocolHandler.cpp create mode 100644 Common/IcomRepeaterProtocolHandler.h create mode 100644 Common/LogEvent.cpp create mode 100644 Common/LogEvent.h create mode 100644 Common/Logger.cpp create mode 100644 Common/Logger.h create mode 100644 Common/PollData.cpp create mode 100644 Common/PollData.h create mode 100644 Common/ReflectorCallback.h create mode 100644 Common/RemoteHandler.cpp create mode 100644 Common/RemoteHandler.h create mode 100644 Common/RemoteLinkData.cpp create mode 100644 Common/RemoteLinkData.h create mode 100644 Common/RemoteProtocolHandler.cpp create mode 100644 Common/RemoteProtocolHandler.h create mode 100644 Common/RemoteRepeaterData.cpp create mode 100644 Common/RemoteRepeaterData.h create mode 100644 Common/RemoteStarNetGroup.cpp create mode 100644 Common/RemoteStarNetGroup.h create mode 100644 Common/RemoteStarNetUser.cpp create mode 100644 Common/RemoteStarNetUser.h create mode 100644 Common/RepeaterCache.cpp create mode 100644 Common/RepeaterCache.h create mode 100644 Common/RepeaterCallback.h create mode 100644 Common/RepeaterHandler.cpp create mode 100644 Common/RepeaterHandler.h create mode 100644 Common/RepeaterProtocolHandler.h create mode 100644 Common/RingBuffer.h create mode 100644 Common/SHA256.cpp create mode 100644 Common/SHA256.h create mode 100644 Common/SlowDataEncoder.cpp create mode 100644 Common/SlowDataEncoder.h create mode 100644 Common/StarNetHandler.cpp create mode 100644 Common/StarNetHandler.h create mode 100644 Common/StatusData.cpp create mode 100644 Common/StatusData.h create mode 100644 Common/TCPReaderWriterClient.cpp create mode 100644 Common/TCPReaderWriterClient.h create mode 100644 Common/TCPReaderWriterServer.cpp create mode 100644 Common/TCPReaderWriterServer.h create mode 100644 Common/TextCollector.cpp create mode 100644 Common/TextCollector.h create mode 100644 Common/TextData.cpp create mode 100644 Common/TextData.h create mode 100644 Common/Timer.cpp create mode 100644 Common/Timer.h create mode 100644 Common/UDPReaderWriter.cpp create mode 100644 Common/UDPReaderWriter.h create mode 100644 Common/UserCache.cpp create mode 100644 Common/UserCache.h create mode 100644 Common/Utils.cpp create mode 100644 Common/Utils.h create mode 100644 Common/Version.h create mode 100644 Common/VersionUnit.cpp create mode 100644 Common/VersionUnit.h create mode 100644 Common/XLXHostsFileDownloader.cpp create mode 100644 Common/XLXHostsFileDownloader.h create mode 100644 Data/CCS_Hosts.txt create mode 100644 Data/DCS_Hosts.txt create mode 100644 Data/DExtra_Hosts.txt create mode 100644 Data/DPlus_Hosts.txt create mode 100644 Data/TIME_de_DE.ambe create mode 100644 Data/TIME_de_DE.indx create mode 100644 Data/TIME_en_GB.ambe create mode 100644 Data/TIME_en_GB.indx create mode 100644 Data/TIME_en_US.ambe create mode 100644 Data/TIME_en_US.indx create mode 100644 Data/TIME_fr_FR.ambe create mode 100644 Data/TIME_fr_FR.indx create mode 100644 Data/TIME_se_SE.ambe create mode 100644 Data/TIME_se_SE.indx create mode 100644 Data/de_DE.ambe create mode 100644 Data/de_DE.indx create mode 100644 Data/dk_DK.ambe create mode 100644 Data/dk_DK.indx create mode 100644 Data/en_GB.ambe create mode 100644 Data/en_GB.indx create mode 100644 Data/en_US.ambe create mode 100644 Data/en_US.indx create mode 100644 Data/es_ES.ambe create mode 100644 Data/es_ES.indx create mode 100644 Data/fr_FR.ambe create mode 100644 Data/fr_FR.indx create mode 100644 Data/it_IT.ambe create mode 100644 Data/it_IT.indx create mode 100644 Data/no_NO.ambe create mode 100644 Data/no_NO.indx create mode 100644 Data/pl_PL.ambe create mode 100644 Data/pl_PL.indx create mode 100644 Data/se_SE.ambe create mode 100644 Data/se_SE.indx create mode 100644 GUICommon/AddressTextCtrl.cpp create mode 100644 GUICommon/AddressTextCtrl.h create mode 100644 GUICommon/CallsignTextCtrl.cpp create mode 100644 GUICommon/CallsignTextCtrl.h create mode 100644 GUICommon/DCSSet.cpp create mode 100644 GUICommon/DCSSet.h create mode 100644 GUICommon/DExtraSet.cpp create mode 100644 GUICommon/DExtraSet.h create mode 100644 GUICommon/DPRSSet.cpp create mode 100644 GUICommon/DPRSSet.h create mode 100644 GUICommon/DPlusSet.cpp create mode 100644 GUICommon/DPlusSet.h create mode 100644 GUICommon/DescriptionTextCtrl.cpp create mode 100644 GUICommon/DescriptionTextCtrl.h create mode 100644 GUICommon/GUICommon.vcxproj create mode 100644 GUICommon/GUICommon.vcxproj.filters create mode 100644 GUICommon/PortTextCtrl.cpp create mode 100644 GUICommon/PortTextCtrl.h create mode 100644 GUICommon/RemoteSet.cpp create mode 100644 GUICommon/RemoteSet.h create mode 100644 GUICommon/RepeaterDataSet.cpp create mode 100644 GUICommon/RepeaterDataSet.h create mode 100644 GUICommon/RepeaterInfoSet.cpp create mode 100644 GUICommon/RepeaterInfoSet.h create mode 100644 GUICommon/RestrictedTextCtrl.cpp create mode 100644 GUICommon/RestrictedTextCtrl.h create mode 100644 GUICommon/StarNetSet.cpp create mode 100644 GUICommon/StarNetSet.h create mode 100644 GUICommon/XLXSet.cpp create mode 100644 GUICommon/XLXSet.h create mode 100644 README.md create mode 100644 RemoteControl/RemoteControl.vcxproj create mode 100644 RemoteControl/RemoteControl.vcxproj.filters create mode 100644 RemoteControl/RemoteControlApp.cpp create mode 100644 RemoteControl/RemoteControlApp.h create mode 100644 RemoteControl/RemoteControlAppD.cpp create mode 100644 RemoteControl/RemoteControlCallsignData.cpp create mode 100644 RemoteControl/RemoteControlCallsignData.h create mode 100644 RemoteControl/RemoteControlConfig.cpp create mode 100644 RemoteControl/RemoteControlConfig.h create mode 100644 RemoteControl/RemoteControlDefs.h create mode 100644 RemoteControl/RemoteControlFrame.cpp create mode 100644 RemoteControl/RemoteControlFrame.h create mode 100644 RemoteControl/RemoteControlLinkData.cpp create mode 100644 RemoteControl/RemoteControlLinkData.h create mode 100644 RemoteControl/RemoteControlPreferences.cpp create mode 100644 RemoteControl/RemoteControlPreferences.h create mode 100644 RemoteControl/RemoteControlRemoteControlHandler.cpp create mode 100644 RemoteControl/RemoteControlRemoteControlHandler.h create mode 100644 RemoteControl/RemoteControlRemoteSet.cpp create mode 100644 RemoteControl/RemoteControlRemoteSet.h create mode 100644 RemoteControl/RemoteControlRepeaterData.cpp create mode 100644 RemoteControl/RemoteControlRepeaterData.h create mode 100644 RemoteControl/RemoteControlRepeaterPanel.cpp create mode 100644 RemoteControl/RemoteControlRepeaterPanel.h create mode 100644 RemoteControl/RemoteControlStarNetGroup.cpp create mode 100644 RemoteControl/RemoteControlStarNetGroup.h create mode 100644 RemoteControl/RemoteControlStarNetPanel.cpp create mode 100644 RemoteControl/RemoteControlStarNetPanel.h create mode 100644 RemoteControl/RemoteControlStarNetUser.cpp create mode 100644 RemoteControl/RemoteControlStarNetUser.h create mode 100644 StarNetServer/StarNetServer.vcxproj create mode 100644 StarNetServer/StarNetServer.vcxproj.filters create mode 100644 StarNetServer/StarNetServerApp.cpp create mode 100644 StarNetServer/StarNetServerApp.h create mode 100644 StarNetServer/StarNetServerAppD.cpp create mode 100644 StarNetServer/StarNetServerAppD.h create mode 100644 StarNetServer/StarNetServerCallsignSet.cpp create mode 100644 StarNetServer/StarNetServerCallsignSet.h create mode 100644 StarNetServer/StarNetServerConfig.cpp create mode 100644 StarNetServer/StarNetServerConfig.h create mode 100644 StarNetServer/StarNetServerDefs.h create mode 100644 StarNetServer/StarNetServerFrame.cpp create mode 100644 StarNetServer/StarNetServerFrame.h create mode 100644 StarNetServer/StarNetServerIrcDDBSet.cpp create mode 100644 StarNetServer/StarNetServerIrcDDBSet.h create mode 100644 StarNetServer/StarNetServerLogRedirect.cpp create mode 100644 StarNetServer/StarNetServerLogRedirect.h create mode 100644 StarNetServer/StarNetServerMiscellaneousSet.cpp create mode 100644 StarNetServer/StarNetServerMiscellaneousSet.h create mode 100644 StarNetServer/StarNetServerPreferences.cpp create mode 100644 StarNetServer/StarNetServerPreferences.h create mode 100644 StarNetServer/StarNetServerThread.cpp create mode 100644 StarNetServer/StarNetServerThread.h create mode 100644 StarNetServer/StarNetServerThreadHelper.cpp create mode 100644 StarNetServer/StarNetServerThreadHelper.h create mode 100644 TextTransmit/TextTransmit.cpp create mode 100644 TextTransmit/TextTransmit.h create mode 100644 TextTransmit/TextTransmit.vcxproj create mode 100644 TextTransmit/TextTransmit.vcxproj.filters create mode 100644 TimeServer/TimeServer.vcxproj create mode 100644 TimeServer/TimeServer.vcxproj.filters create mode 100644 TimeServer/TimeServerAnnouncementsSet.cpp create mode 100644 TimeServer/TimeServerAnnouncementsSet.h create mode 100644 TimeServer/TimeServerApp.cpp create mode 100644 TimeServer/TimeServerApp.h create mode 100644 TimeServer/TimeServerConfig.cpp create mode 100644 TimeServer/TimeServerConfig.h create mode 100644 TimeServer/TimeServerD.cpp create mode 100644 TimeServer/TimeServerD.h create mode 100644 TimeServer/TimeServerDefs.h create mode 100644 TimeServer/TimeServerFrame.cpp create mode 100644 TimeServer/TimeServerFrame.h create mode 100644 TimeServer/TimeServerGatewaySet.cpp create mode 100644 TimeServer/TimeServerGatewaySet.h create mode 100644 TimeServer/TimeServerLogRedirect.cpp create mode 100644 TimeServer/TimeServerLogRedirect.h create mode 100644 TimeServer/TimeServerPreferences.cpp create mode 100644 TimeServer/TimeServerPreferences.h create mode 100644 TimeServer/TimeServerThread.cpp create mode 100644 TimeServer/TimeServerThread.h create mode 100644 TimeServer/TimeServerThreadHelper.cpp create mode 100644 TimeServer/TimeServerThreadHelper.h create mode 100644 TimerControl/TimerControl.vcxproj create mode 100644 TimerControl/TimerControl.vcxproj.filters create mode 100644 TimerControl/TimerControlApp.cpp create mode 100644 TimerControl/TimerControlApp.h create mode 100644 TimerControl/TimerControlAppD.cpp create mode 100644 TimerControl/TimerControlAppD.h create mode 100644 TimerControl/TimerControlConfig.cpp create mode 100644 TimerControl/TimerControlConfig.h create mode 100644 TimerControl/TimerControlDefs.h create mode 100644 TimerControl/TimerControlFrame.cpp create mode 100644 TimerControl/TimerControlFrame.h create mode 100644 TimerControl/TimerControlItem.h create mode 100644 TimerControl/TimerControlItemFile.cpp create mode 100644 TimerControl/TimerControlItemFile.h create mode 100644 TimerControl/TimerControlPreferences.cpp create mode 100644 TimerControl/TimerControlPreferences.h create mode 100644 TimerControl/TimerControlRemoteControlHandler.cpp create mode 100644 TimerControl/TimerControlRemoteControlHandler.h create mode 100644 TimerControl/TimerControlRemoteSet.cpp create mode 100644 TimerControl/TimerControlRemoteSet.h create mode 100644 TimerControl/TimerControlRepeaterPanel.cpp create mode 100644 TimerControl/TimerControlRepeaterPanel.h create mode 100644 TimerControl/TimerControlThread.cpp create mode 100644 TimerControl/TimerControlThread.h create mode 100644 TimerControl/TimerControlThreadHelper.cpp create mode 100644 TimerControl/TimerControlThreadHelper.h create mode 100644 VoiceTransmit/VoiceStore.cpp create mode 100644 VoiceTransmit/VoiceStore.h create mode 100644 VoiceTransmit/VoiceTransmit.cpp create mode 100644 VoiceTransmit/VoiceTransmit.h create mode 100644 VoiceTransmit/VoiceTransmit.vcxproj create mode 100644 VoiceTransmit/VoiceTransmit.vcxproj.filters create mode 100644 debian/README create mode 100644 debian/README.Debian create mode 100644 debian/README.source create mode 100644 debian/aprstransmitd.aprstransmitd@.service create mode 100644 debian/aprstransmitd.conf create mode 100644 debian/aprstransmitd.install create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/docs create mode 100644 debian/ircddbgateway-tools.install create mode 100644 debian/ircddbgateway.install create mode 100644 debian/ircddbgateway.logrotate create mode 100644 debian/ircddbgatewayd.install create mode 100644 debian/ircddbgatewayd.ircddbgatewayd.service create mode 100644 debian/ircddbgatewayd.logrotate create mode 100644 debian/opendv-base.dirs create mode 100644 debian/opendv-base.install create mode 100644 debian/opendv-base.postinst create mode 100644 debian/remotecontrol.install create mode 100644 debian/remotecontrold.install create mode 100644 debian/rules create mode 100644 debian/source/format create mode 100644 debian/starnetserver.install create mode 100644 debian/starnetserver.logrotate create mode 100644 debian/starnetserverd.install create mode 100644 debian/starnetserverd.logrotate create mode 100644 debian/starnetserverd.starnetserverd.service create mode 100644 debian/timercontrol.install create mode 100644 debian/timercontrol.logrotate create mode 100644 debian/timercontrold.install create mode 100644 debian/timercontrold.logrotate create mode 100644 debian/timercontrold.timercontrold.service create mode 100644 debian/timeserver.install create mode 100644 debian/timeserver.logrotate create mode 100644 debian/timeserverd.install create mode 100644 debian/timeserverd.logrotate create mode 100644 debian/timeserverd.timeserverd.service create mode 100644 ircDDB/IRCApplication.h create mode 100644 ircDDB/IRCClient.cpp create mode 100644 ircDDB/IRCClient.h create mode 100644 ircDDB/IRCDDB.cpp create mode 100644 ircDDB/IRCDDB.h create mode 100644 ircDDB/IRCDDBApp.cpp create mode 100644 ircDDB/IRCDDBApp.h create mode 100644 ircDDB/IRCDDBClient.cpp create mode 100644 ircDDB/IRCDDBClient.h create mode 100644 ircDDB/IRCDDBMultiClient.cpp create mode 100644 ircDDB/IRCDDBMultiClient.h create mode 100644 ircDDB/IRCMessage.cpp create mode 100644 ircDDB/IRCMessage.h create mode 100644 ircDDB/IRCMessageQueue.cpp create mode 100644 ircDDB/IRCMessageQueue.h create mode 100644 ircDDB/IRCProtocol.cpp create mode 100644 ircDDB/IRCProtocol.h create mode 100644 ircDDB/IRCReceiver.cpp create mode 100644 ircDDB/IRCReceiver.h create mode 100644 ircDDB/IRCutils.cpp create mode 100644 ircDDB/IRCutils.h create mode 100644 ircDDB/LICENSE create mode 100644 ircDDB/README create mode 100644 ircDDB/ircDDB.vcxproj create mode 100644 ircDDB/ircDDB.vcxproj.filters create mode 100644 ircDDBGateway.sln create mode 100644 ircDDBGateway/IRCDDBGatewayApp.cpp create mode 100644 ircDDBGateway/IRCDDBGatewayApp.h create mode 100644 ircDDBGateway/IRCDDBGatewayAppD.cpp create mode 100644 ircDDBGateway/IRCDDBGatewayAppD.h create mode 100644 ircDDBGateway/IRCDDBGatewayDefs.h create mode 100644 ircDDBGateway/IRCDDBGatewayFrame.cpp create mode 100644 ircDDBGateway/IRCDDBGatewayFrame.h create mode 100644 ircDDBGateway/IRCDDBGatewayLogRedirect.cpp create mode 100644 ircDDBGateway/IRCDDBGatewayLogRedirect.h create mode 100644 ircDDBGateway/IRCDDBGatewayStatusData.cpp create mode 100644 ircDDBGateway/IRCDDBGatewayStatusData.h create mode 100644 ircDDBGateway/IRCDDBGatewayThread.cpp create mode 100644 ircDDBGateway/IRCDDBGatewayThread.h create mode 100644 ircDDBGateway/IRCDDBGatewayThreadHelper.cpp create mode 100644 ircDDBGateway/IRCDDBGatewayThreadHelper.h create mode 100644 ircDDBGateway/ircDDBGateway.vcxproj create mode 100644 ircDDBGateway/ircDDBGateway.vcxproj.filters create mode 100644 ircDDBGatewayConfig/IRCDDBGatewayConfigApp.cpp create mode 100644 ircDDBGatewayConfig/IRCDDBGatewayConfigApp.h create mode 100644 ircDDBGatewayConfig/IRCDDBGatewayConfigDefs.h create mode 100644 ircDDBGatewayConfig/IRCDDBGatewayConfigFrame.cpp create mode 100644 ircDDBGatewayConfig/IRCDDBGatewayConfigFrame.h create mode 100644 ircDDBGatewayConfig/IRCDDBGatewayConfigGatewaySet.cpp create mode 100644 ircDDBGatewayConfig/IRCDDBGatewayConfigGatewaySet.h create mode 100644 ircDDBGatewayConfig/IRCDDBGatewayConfigIrcDDBSet.cpp create mode 100644 ircDDBGatewayConfig/IRCDDBGatewayConfigIrcDDBSet.h create mode 100644 ircDDBGatewayConfig/IRCDDBGatewayConfigMiscellaneousSet.cpp create mode 100644 ircDDBGatewayConfig/IRCDDBGatewayConfigMiscellaneousSet.h create mode 100644 ircDDBGatewayConfig/ircDDBGatewayConfig.vcxproj create mode 100644 ircDDBGatewayConfig/ircDDBGatewayConfig.vcxproj.filters diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0f42248 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +*.o +*.obj +x86 +Debug +Release +*.user +*~ +*.bak +.vs + diff --git a/APRSTransmit/APRSParser.cpp b/APRSTransmit/APRSParser.cpp new file mode 100644 index 0000000..711297a --- /dev/null +++ b/APRSTransmit/APRSParser.cpp @@ -0,0 +1,601 @@ +/* + * Copyright (C) 2014 by Jonathan Naylor G4KLX + * APRSTransmit Copyright (C) 2015 Geoffrey Merck F4FXL / KC3FRA + * + * 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. + */ + +// Most of the code has been borrowed from APRX Software. I just kept the parts I need for my purpose and C++ed it. Hence the copyright text below. +//https://github.com/PhirePhly/aprx/blob/master/parse_aprs.c + +/******************************************************************** + * APRX -- 2nd generation APRS-i-gate with * + * minimal requirement of esoteric facilities or * + * libraries of any kind beyond UNIX system libc. * + * * + * (c) Matti Aarnio - OH2MQK, 2007-2014 * + * * + ********************************************************************/ +/* + * Some parts of this code are copied from: + * + * aprsc + * + * (c) Heikki Hannikainen, OH7LZB + * + * This program is licensed under the BSD license, which can be found + * in the file LICENSE. + * + */ + +#include "APRSParser.h" +#include +#include +#include + +CAPRSPacket::CAPRSPacket() : +m_type(APT_Unknown), +m_latitude(0.0F), +m_longitude(0.0F), +m_symbol('\0'), +m_symbolTable('\0'), +m_typeChar('\0'), +m_body(), +m_raw() +{ +} +CAPRSPacket::CAPRSPacket(APRSPacketType type, float latitude, float longitude, const wxString& infoText, const wxString raw) : +m_type(type), +m_latitude(latitude), +m_longitude(longitude), +m_symbol('\0'), +m_symbolTable('\0'), +m_typeChar('\0'), +m_body(infoText), +m_raw(raw), +m_destCall(), +m_srcCall() +{ +} + +float& CAPRSPacket::Longitude(){ + return m_longitude; +} +float& CAPRSPacket::Latitude(){ + return m_latitude; +} +wxString& CAPRSPacket::Body(){ + return m_body; +} +wxString& CAPRSPacket::Raw(){ + return m_raw; +} + +wxChar& CAPRSPacket::Symbol(){ + return m_symbol; +} + +wxChar& CAPRSPacket::SymbolTable(){ + return m_symbolTable; +} + +wxChar& CAPRSPacket::TypeChar(){ + return m_typeChar; +} + +APRSPacketType& CAPRSPacket::Type(){ + return m_type; +} + + +wxString& CAPRSPacket::SourceCall(){ + return m_srcCall; +} +wxString& CAPRSPacket::DestinationCall(){ + return m_destCall; +} + +/***************************************************************************** +* Parsed an APRS string into an APRS frame +* currently it does nothing but determine if the packet is Icom supported +* I already added provision for conversion of unsupported packet formats to supported formats +******************************************************************************/ +bool CAPRSParser::Parse(const wxString& aprsFrame, CAPRSPacket& packet) +{ + wxString aprsFrameLocal(aprsFrame); + stripFrame(aprsFrameLocal); + if(!preprocessFrame(packet, aprsFrameLocal)) + return false; + + switch(packet.TypeChar()) + { + case '\'': + case '`' : + { + //Packet is of MicE type + //Icom radios do not support MicE so convert + if(parse_aprs_mice(packet)){ + wxLogMessage(wxT("Got MicE format, converting to Icom supported")); + convertToIcomCompatible(packet); + return Parse(packet.Raw(), packet); + } + packet.Type() = APT_Position; + return false; + } + case '!' : + if(packet.Body().GetChar(0) == '!'){ + //Ultimeter 2000 weather station + //same procedure as above, ignore for now + return false; + } + case '=' : + case '/' : + case '@' : + { + if(packet.Body().Length() < 10) return false;//enough chars to have a chance to parse it ? + /* Normal or compressed location packet, with or without + * timestamp, with or without messaging capability + * + * ! and / have messaging, / and @ have a prepended timestamp + */ + packet.Type() = APT_Position; + if(packet.TypeChar() == '/' || packet.TypeChar() == '@')//With a prepended timestamp, jump over it. + packet.Body() = packet.Body().Mid(7); + + wxChar posChar = packet.Body().GetChar(0); + if(valid_sym_table_compressed(posChar)//Compressed format + && packet.Body().Length() >= 13){//we need at least 13 char + //icom unsupported, ignore for now + return false;//parse_aprs_compressed(pb, body, body_end); + } + else if(posChar >= '0' && posChar <= '9' //Normal uncompressed format + && packet.Body().Length() >=19){//we need at least 19 chars for it to be valid + + if(ensureIsIcomCompatible(packet)) + return Parse(packet.Raw(), packet); + return true; + } + break; + } + case '$' : //NMEA packet + if(packet.Body().Length() > 10){ + packet.Type() = APT_NMEA; + return true; + } + break; + case ':' : + //We have an APRS message + packet.Type() = APT_Message; + //Nice future feature would be to add conversion to icom Android App messaging + return false; + case ';' : + //we have an object + if(packet.Body().Length() > 29){ + //TODO : Parsing ... + packet.Type() = APT_Object; + return true; + } + break; + case ')' : + //this is an item + if(packet.Body().Length() > 19){ + //TODO : Parsing ... + packet.Type() = APT_Item; + return true; + } + break; + case '>' ://APRS status... + case '<' ://stat capa + case '?' ://Query + case 'T' ://Telemetry + return false;//not supported + case '#': /* Peet Bros U-II Weather Station */ + case '*': /* Peet Bros U-I Weather Station */ + case '_': /* Weather report without position */ + packet.Type() = APT_WX; + return true; // AFAIK _ is supported, not sure for the others + case '{' ://custom + return false; + } + + return false; +} + +void CAPRSParser::convertToIcomCompatible(CAPRSPacket& packet) +{ + //first build the coordinate string + float degrees = ::floor(::fabs(packet.Latitude())); + float minutes = (::fabs(packet.Latitude()) - degrees) * 60.0f; + char hemisphere = packet.Latitude() > 0.0f ? 'N' : 'S'; + wxString latString = wxString::Format(wxT("%02.0f%05.2f%c"), degrees, minutes, hemisphere); + + degrees = ::floor(::fabs(packet.Longitude())); + minutes = (::fabs(packet.Longitude()) - degrees) * 60.0f; + hemisphere = packet.Longitude() > 0.0f ? 'E' : 'W'; + wxString longString = wxString::Format(wxT("%03.0f%05.2f%c"), degrees, minutes, hemisphere); + + /*packet.Raw() = wxString::Format(wxT("%s>%s:=%s%c%s%c/%s"), + packet.SourceCall().mb_str().data(), + packet.DestinationCall().mb_str().data(), + latString.mb_str().data(), + (char)packet.SymbolTable(), + longString.mb_str().data(), + (char)packet.Symbol(), + packet.Body().mb_str().data());*/ + + //above code behaved in different manner under wx2.8 let's do it the brutal way ! + packet.Raw() = packet.SourceCall() + + wxT(">") + + packet.DestinationCall() + + wxT(":=") + + latString + + packet.SymbolTable() + + longString + + packet.Symbol() + + wxT("/") + + packet.Body(); +} + +//configures the packet's raw, source call and dest call +bool CAPRSParser::preprocessFrame(CAPRSPacket& packet, const wxString& aprsFrame) +{ + wxString addressField = aprsFrame.BeforeFirst(':'); + wxString body = aprsFrame.AfterFirst(':'); + if(addressField == aprsFrame || body.IsEmpty()){ + wxLogWarning(wxT("Failed to find body of aprs frame")); + return false; + } + + wxString srcCall = addressField.BeforeFirst('>'); + wxString destCall = addressField.AfterFirst('>'); + if(srcCall == addressField){ + wxLogWarning(wxT("Failed to extract source and destination call")); + return false; + } + + packet.TypeChar() = body.GetChar(0); + packet.Body() = body.Mid(1);//strip the type char + packet.SourceCall() = srcCall; + packet.DestinationCall() = destCall; + packet.Raw() = wxString(aprsFrame); + packet.Type() = APT_Unknown; + + return true; +} + +/* Stupidly icom always expects a / after the symbol char, even if no data extension is present -_- +* This function makes sure the frame is icom compatible. In case the frame is invalid, A COMPLETE aprsFrame is built. +* If no modifications were made true is returned, otherwise false */ +bool CAPRSParser::ensureIsIcomCompatible(CAPRSPacket& packet) +{ + bool changeMade = false; + wxChar symbol = packet.Body().GetChar(18); + wxChar charAfterSymbol = packet.Body().GetChar(19); + wxString newBody(packet.Body()); + wxString aprsFrame(packet.Raw()); + + if(charAfterSymbol != '/' + && symbol != '_'){//do not do this for weather packets ! + newBody = packet.Body().Mid(0, 19) + wxT("/") + packet.Body().Mid(19); + aprsFrame.Replace(packet.Body(), newBody); + changeMade = true; + } + + if(stripFrame(aprsFrame)) changeMade = true; + + packet.Body() = newBody; + packet.Raw() = aprsFrame; + + return changeMade; +} + +bool CAPRSParser::stripFrame(wxString& aprsFrame) +{ + const unsigned int maxAprsFrameLen = 64U; + + bool changeMade = false; + + //Trim the path, only keep from and to. Icom device do not support too long frames. + if(aprsFrame.Length() > maxAprsFrameLen){ + wxString dataField = aprsFrame.AfterFirst(':'); + wxString addressField = aprsFrame.BeforeFirst(':'); + addressField = addressField.BeforeFirst(','); + aprsFrame = addressField + wxT(":") + dataField; + changeMade = true; + } + + + + //Still too long ? + if(aprsFrame.Length() > maxAprsFrameLen){ + aprsFrame = aprsFrame.Left(maxAprsFrameLen); + changeMade = true; + } + + return changeMade; +} + + +bool CAPRSParser::parse_aprs_mice(CAPRSPacket& packet) +{ + float lat = 0.0, lng = 0.0; + unsigned int lat_deg = 0, lat_min = 0, lat_min_frag = 0, lng_deg = 0, lng_min = 0, lng_min_frag = 0; + //const char *d_start; + char dstcall[7]; + char *p; + char sym_table, sym_code; + int posambiguity = 0; + int i; + + //code below is just to map wxWidgets stuff to original APRX pointer based logic. + char* body = new char[packet.Body().length()]; + for (unsigned int i = 0U; i < packet.Body().length(); i++) + body[i] = packet.Body().GetChar(i); + char* body_end = body + packet.Body().length(); + + char* d_start = new char[packet.Body().length()]; + for (unsigned int i = 0U; i < packet.Body().length(); i++) + d_start[i] = packet.DestinationCall().GetChar(i); + char* dstcall_end_or_ssid = d_start + packet.DestinationCall().length(); + + + //Original APRX code follows.. Just a few minor changes + + /* check packet length */ + if (body_end - body < 8) { + delete[] body; + delete[] d_start; + return false; + } + + /* check that the destination call exists and is of the right size for mic-e */ + //d_start = pb->srccall_end+1; + if (dstcall_end_or_ssid - d_start != 6) { + //DEBUG_LOG(".. bad destcall length! "); + delete[] body; + delete[] d_start; + return false; /* eh...? */ + } + + /* validate destination call: + * A-K characters are not used in the last 3 characters + * and MNO are never used + */ + //if (debug)printf(" destcall='%6.6s'",d_start); + for (i = 0; i < 3; i++) + if (!((d_start[i] >= '0' && d_start[i] <= '9') + || (d_start[i] >= 'A' && d_start[i] <= 'L') + || (d_start[i] >= 'P' && d_start[i] <= 'Z'))) { + //DEBUG_LOG(".. bad destcall characters in posits 1..3"); + delete[] body; + delete[] d_start; + return false; + } + + for (i = 3; i < 6; i++) + if (!((d_start[i] >= '0' && d_start[i] <= '9') + || (d_start[i] == 'L') + || (d_start[i] >= 'P' && d_start[i] <= 'Z'))) { + //DEBUG_LOG(".. bad destcall characters in posits 4..6"); + delete[] body; + delete[] d_start; + return false; + } + + //DEBUG_LOG("\tpassed dstcall format check"); + + /* validate information field (longitude, course, speed and + * symbol table and code are checked). Not bullet proof.. + * + * 0 1 23 4 5 6 7 + * /^[\x26-\x7f][\x26-\x61][\x1c-\x7f]{2}[\x1c-\x7d][\x1c-\x7f][\x21-\x7b\x7d][\/\\A-Z0-9]/ + */ + if (body[0] < 0x26 || body[0] > 0x7f) { + //DEBUG_LOG("..bad infofield column 1"); + delete[] body; + delete[] d_start; + return false; + } + if (body[1] < 0x26 || body[1] > 0x61) { + //DEBUG_LOG("..bad infofield column 2"); + delete[] body; + delete[] d_start; + return false; + } + if (body[2] < 0x1c || body[2] > 0x7f) { + //DEBUG_LOG("..bad infofield column 3"); + delete[] body; + delete[] d_start; + return false; + } + if (body[3] < 0x1c || body[3] > 0x7f) { + //DEBUG_LOG("..bad infofield column 4"); + delete[] body; + delete[] d_start; + return false; + } + if (body[4] < 0x1c || body[4] > 0x7d) { + //DEBUG_LOG("..bad infofield column 5"); + //delete[] body; + //delete[] d_start; + //return false; + } + if (body[5] < 0x1c || body[5] > 0x7f) { + //DEBUG_LOG("..bad infofield column 6"); + delete[] body; + delete[] d_start; + return false; + } + if ((body[6] < 0x21 || body[6] > 0x7b) + && body[6] != 0x7d) { + //DEBUG_LOG("..bad infofield column 7"); + delete[] body; + delete[] d_start; + return false; + } + if (!valid_sym_table_uncompressed(body[7])) { + //DEBUG_LOG("..bad symbol table entry on column 8"); + delete[] body; + delete[] d_start; + return false; + } + + //DEBUG_LOG("\tpassed info format check"); + + /* make a local copy, we're going to modify it */ + strncpy(dstcall, d_start, 6); + dstcall[6] = 0; + + /* First do the destination callsign + * (latitude, message bits, N/S and W/E indicators and long. offset) + * + * Translate the characters to get the latitude + */ + + //fprintf(stderr, "\tuntranslated dstcall: %s\n", dstcall); + for (p = dstcall; *p; p++) { + if (*p >= 'A' && *p <= 'J') + *p -= 'A' - '0'; + else if (*p >= 'P' && *p <= 'Y') + *p -= 'P' - '0'; + else if (*p == 'K' || *p == 'L' || *p == 'Z') + *p = '_'; + } + //fprintf(stderr, "\ttranslated dstcall: %s\n", dstcall); + + // position ambiquity is going to get ignored now, + // it's not needed in this application. + + if (dstcall[5] == '_') { dstcall[5] = '5'; posambiguity = 1; } + if (dstcall[4] == '_') { dstcall[4] = '5'; posambiguity = 2; } + if (dstcall[3] == '_') { dstcall[3] = '5'; posambiguity = 3; } + if (dstcall[2] == '_') { dstcall[2] = '3'; posambiguity = 4; } + if (dstcall[1] == '_' || dstcall[0] == '_') { + //DEBUG_LOG("..bad pos-ambiguity on destcall"); + delete[] body; + delete[] d_start; + return false; + } // cannot use posamb here + + // convert to degrees, minutes and decimal degrees, + // and then to a float lat + + if (sscanf(dstcall, "%2u%2u%2u", + &lat_deg, &lat_min, &lat_min_frag) != 3) { + //DEBUG_LOG("\tsscanf failed"); + delete[] body; + delete[] d_start; + return false; + } + lat = (float)lat_deg + (float)lat_min / 60.0 + (float)lat_min_frag / 6000.0; + + // check the north/south direction and correct the latitude if necessary + if (d_start[3] <= 0x4c) + lat = 0 - lat; + + /* Decode the longitude, the first three bytes of the body + * after the data type indicator. First longitude degrees, + * remember the longitude offset. + */ + lng_deg = body[0] - 28; + if (d_start[4] >= 0x50) + lng_deg += 100; + if (lng_deg >= 180 && lng_deg <= 189) + lng_deg -= 80; + else if (lng_deg >= 190 && lng_deg <= 199) + lng_deg -= 190; + + /* Decode the longitude minutes */ + lng_min = body[1] - 28; + if (lng_min >= 60) + lng_min -= 60; + + /* ... and minute decimals */ + lng_min_frag = body[2] - 28; + + /* apply position ambiguity to longitude */ + switch (posambiguity) { + case 0: + /* use everything */ + lng = (float)lng_deg + (float)lng_min / 60.0 + + (float)lng_min_frag / 6000.0; + break; + case 1: + /* ignore last number of lng_min_frag */ + lng = (float)lng_deg + (float)lng_min / 60.0 + + (float)(lng_min_frag - lng_min_frag % 10 + 5) / 6000.0; + break; + case 2: + /* ignore lng_min_frag */ + lng = (float)lng_deg + ((float)lng_min + 0.5) / 60.0; + break; + case 3: + /* ignore lng_min_frag and last number of lng_min */ + lng = (float)lng_deg + (float)(lng_min - lng_min % 10 + 5) / 60.0; + break; + case 4: + /* minute is unused -> add 0.5 degrees to longitude */ + lng = (float)lng_deg + 0.5; + break; + default: + //DEBUG_LOG(".. posambiguity code BUG!"); + delete[] body; + delete[] d_start; + return false; + } + + /* check the longitude E/W sign */ + if (d_start[5] >= 0x50) + lng = 0 - lng; + + /* save the symbol table and code */ + sym_code = body[6]; + sym_table = body[7]; + + /* ok, we're done */ + /* + fprintf(stderr, "\tlat %u %u.%u (%.4f) lng %u %u.%u (%.4f)\n", + lat_deg, lat_min, lat_min_frag, lat, + lng_deg, lng_min, lng_min_frag, lng); + fprintf(stderr, "\tsym '%c' '%c'\n", sym_table, sym_code); + */ + + + //return pbuf_fill_pos(pb, lat, lng, sym_table, sym_code); + + ///End of APRX original code + packet.Latitude() = lat; + packet.Longitude() = lng; + packet.Symbol() = sym_code; + packet.SymbolTable() = sym_table; + packet.Body() = packet.Body().Mid(9);//if MicE has additional info like heading it'll be longer than 9, ignore for now + + delete[] body; + delete[] d_start; + + return true; +} + +bool CAPRSParser::valid_sym_table_compressed(wxChar c) +{ + return (c == '/' || c == '\\' || (c >= 0x41 && c <= 0x5A) + || (c >= 0x61 && c <= 0x6A)); /* [\/\\A-Za-j] */ +} + +bool CAPRSParser::valid_sym_table_uncompressed(wxChar c) +{ + return (c == '/' || c == '\\' || (c >= 0x41 && c <= 0x5A) + || (c >= 0x30 && c <= 0x39)); /* [\/\\A-Z0-9] */ +} diff --git a/APRSTransmit/APRSParser.h b/APRSTransmit/APRSParser.h new file mode 100644 index 0000000..73683d8 --- /dev/null +++ b/APRSTransmit/APRSParser.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2014 by Jonathan Naylor G4KLX + * APRSTransmit Copyright (C) 2015 Geoffrey Merck F4FXL / KC3FRA + * + * 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 APRSParserAppD_H +#define APRSParserAppD_H + +#include + +enum APRSPacketType { + APT_Unknown, + APT_Position, + APT_WX, + APT_Object, + APT_Item, + APT_Message, + APT_NMEA +}; + +class CAPRSPacket{ +public : + CAPRSPacket(); + CAPRSPacket(APRSPacketType type, float latitude, float longitude, const wxString& infoText, const wxString raw); + + float& Longitude(); + float& Latitude(); + wxString& SourceCall(); + wxChar& Symbol(); + wxChar& SymbolTable(); + wxChar& TypeChar(); + wxString& DestinationCall(); + wxString& Body(); + wxString& Raw(); + APRSPacketType& Type(); + +private: + APRSPacketType m_type; + float m_latitude; + float m_longitude; + wxChar m_symbol; + wxChar m_symbolTable; + wxChar m_typeChar; + wxString m_body; + wxString m_raw;//raw string in TNC2 format, including '\r' + wxString m_destCall; + wxString m_srcCall; +}; + +class CAPRSParser{ +public: + static bool Parse(const wxString& aprsString, CAPRSPacket& packet); + +private : + static bool valid_sym_table_compressed(wxChar c); + static bool valid_sym_table_uncompressed(wxChar c); + static bool parse_aprs_mice(CAPRSPacket& packet); + + static bool ensureIsIcomCompatible(CAPRSPacket& packet); + static bool stripFrame(wxString& aprsFrame); + static bool preprocessFrame(CAPRSPacket& packet, const wxString& aprsFrame); + static void convertToIcomCompatible(CAPRSPacket& packet); +}; + +#endif diff --git a/APRSTransmit/APRSTransmit.cpp b/APRSTransmit/APRSTransmit.cpp new file mode 100644 index 0000000..e50393d --- /dev/null +++ b/APRSTransmit/APRSTransmit.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2014 by Jonathan Naylor G4KLX + * APRSTransmit Copyright (C) 2015 Geoffrey Merck F4FXL / KC3FRA + * + * 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 "SlowDataEncoder.h" +#include "DStarDefines.h" +#include "APRSTransmit.h" +#include "APRSParser.h" + + +CAPRSTransmit::CAPRSTransmit(const wxString& callsign, const wxString& text) : +m_socket(wxEmptyString, 0U), +m_repeaterCallsign(callsign), +m_APRSCallsign(callsign), +m_text(text) +{ + int index = m_text.Find(wxT(">")); + if(index != wxNOT_FOUND) + m_APRSCallsign = m_text.Left(index); +} + +CAPRSTransmit::~CAPRSTransmit() +{ +} + +bool CAPRSTransmit::run() +{ + //First see if the packet is Icom supported... + CAPRSPacket aprsPacket; + if(!CAPRSParser::Parse(m_text, aprsPacket)){ + wxLogWarning(wxT("Unsupported APRS Format, ignoring => ") + m_text.Trim(true)); + return false; + } + + wxString textWithCRC(aprsPacket.Raw()); + wxLogMessage(wxT("Supported APRS Format => ") + textWithCRC.Trim(true)); + //add nececessary stuff to text, but keep it the original + textWithCRC.Replace(wxT("\n"), wxEmptyString); + if(!textWithCRC.EndsWith(wxT("\r"))) textWithCRC.Append(wxT("\r")); + wxString crc = wxString::Format(wxT("$$CRC%04X,"), calcCRC(textWithCRC)); + textWithCRC.Prepend(crc); + + bool opened = m_socket.open(); + if (!opened) + return false; + + in_addr address = CUDPReaderWriter::lookup(wxT("127.0.0.1")); + + unsigned int id = CHeaderData::createId(); + + wxString callsignG = m_repeaterCallsign.Left(LONG_CALLSIGN_LENGTH - 1U); + callsignG.Append(wxT("G")); + + CHeaderData header; + header.setId(id); + header.setMyCall1(m_APRSCallsign); + header.setMyCall2(wxT("APRS")); + header.setRptCall1(callsignG); + header.setRptCall2(m_repeaterCallsign); + header.setYourCall(wxT("CQCQCQ ")); + header.setDestination(address, G2_DV_PORT); + + sendHeader(header); + + CSlowDataEncoder encoder; + encoder.setHeaderData(header); + encoder.setGPSData(textWithCRC); + encoder.setTextData(wxT("APRS to DPRS")); + + CAMBEData data; + data.setDestination(address, G2_DV_PORT); + data.setId(id); + + wxStopWatch timer; + timer.Start(); + + unsigned int out = 0U; + unsigned int dataOut = 0U; + unsigned int needed = (encoder.getInterleavedDataLength() / (DATA_FRAME_LENGTH_BYTES)) * 2U; + + while (dataOut < needed) { + data.setSeq(out); + + unsigned char buffer[DV_FRAME_LENGTH_BYTES]; + ::memcpy(buffer + 0U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + + // Insert sync bytes when the sequence number is zero, slow data otherwise + if (out == 0U) { + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); + } else { + encoder.getInterleavedData(buffer + VOICE_FRAME_LENGTH_BYTES); + dataOut++; + } + + data.setData(buffer, DV_FRAME_LENGTH_BYTES); + + sendData(data); + out++; + + if (out == 21U) out = 0U; + } + + data.setData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES); + data.setSeq(out >= 21U ? 0U : out); + data.setEnd(true); + + sendData(data); + + m_socket.close(); + + return true; +} + +bool CAPRSTransmit::sendHeader(const CHeaderData& header) +{ + unsigned char buffer[60U]; + unsigned int length = header.getG2Data(buffer, 60U, true); + + for (unsigned int i = 0U; i < 2U; i++) { + bool res = m_socket.write(buffer, length, header.getYourAddress(), header.getYourPort()); + if (!res) + return false; + } + + return true; +} + +bool CAPRSTransmit::sendData(const CAMBEData& data) +{ + unsigned char buffer[60U]; + unsigned int length = data.getG2Data(buffer, 60U); + + return m_socket.write(buffer, length, data.getYourAddress(), data.getYourPort()); +} + +unsigned int CAPRSTransmit::calcCRC(const wxString& gpsData) +{ + size_t length = gpsData.length(); + wxASSERT(length > 0U); + + unsigned int icomcrc = 0xFFFFU; + + for (unsigned int j = 0U; j < length; j++) { + unsigned char ch = gpsData.GetChar(j); + + for (unsigned int i = 0U; i < 8U; i++) { + bool xorflag = (((icomcrc ^ ch) & 0x01U) == 0x01U); + + icomcrc >>= 1; + + if (xorflag) + icomcrc ^= 0x8408U; + + ch >>= 1; + } + } + + return ~icomcrc & 0xFFFFU; +} diff --git a/APRSTransmit/APRSTransmit.h b/APRSTransmit/APRSTransmit.h new file mode 100644 index 0000000..f24987c --- /dev/null +++ b/APRSTransmit/APRSTransmit.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 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. + */ + +#ifndef TextTransmit_H +#define TextTransmit_H + +#include "UDPReaderWriter.h" +#include "HeaderData.h" +#include "AMBEData.h" + +class CAPRSTransmit { +public: + CAPRSTransmit(const wxString& callsign, const wxString& text); + ~CAPRSTransmit(); + + bool run(); + +private: + CUDPReaderWriter m_socket; + wxString m_repeaterCallsign; + wxString m_APRSCallsign; + wxString m_text; + + bool sendHeader(const CHeaderData& header); + bool sendData(const CAMBEData& data); + unsigned int calcCRC(const wxString& gpsData); +}; + +#endif diff --git a/APRSTransmit/APRSTransmit.vcxproj b/APRSTransmit/APRSTransmit.vcxproj new file mode 100644 index 0000000..96a54ae --- /dev/null +++ b/APRSTransmit/APRSTransmit.vcxproj @@ -0,0 +1,188 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {F26EA1DB-74CF-4C52-A425-00235C8ABED2} + APRSTransmit + Win32Proj + 10.0.16299.0 + + + + Application + v141 + Unicode + true + + + Application + v141 + Unicode + true + + + Application + v141 + Unicode + + + Application + v141 + Unicode + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>14.0.24720.0 + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\$(ProjectName) + true + $(WXWIN)\include;$(WXWIN)\lib\vc_dll\mswud;$(IncludePath) + + + true + $(WXWIN)\include;$(WXWIN)\lib\vc_x64_dll\mswud;$(IncludePath) + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\$(ProjectName) + false + + + false + + + + Disabled + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATEWIN32;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + Level3 + EditAndContinue + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_dll;%(AdditionalLibraryDirectories) + true + Console + MachineX86 + + + + + Disabled + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATEWIN32;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + ProgramDatabase + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_x64_dll;%(AdditionalLibraryDirectories) + true + Console + + + + + MaxSpeed + true + $(WXWIN)\include\msvc;$(WXWIN)\include;../ircDDB;../Common;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + Level3 + ProgramDatabase + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_dll;%(AdditionalLibraryDirectories) + true + Console + true + true + MachineX86 + + + + + MaxSpeed + true + $(WXWIN)\include\msvc;$(WXWIN)\include;../ircDDB;../Common;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_x64_dll;%(AdditionalLibraryDirectories) + true + Console + true + true + + + + + + + + + + + + + + {e793cb8e-2ac9-431a-bbfc-3f52537bb3cf} + false + + + + + + \ No newline at end of file diff --git a/APRSTransmit/APRSTransmit.vcxproj.filters b/APRSTransmit/APRSTransmit.vcxproj.filters new file mode 100644 index 0000000..afc4767 --- /dev/null +++ b/APRSTransmit/APRSTransmit.vcxproj.filters @@ -0,0 +1,32 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/APRSTransmit/APRSTransmitApp.cpp b/APRSTransmit/APRSTransmitApp.cpp new file mode 100644 index 0000000..86c580b --- /dev/null +++ b/APRSTransmit/APRSTransmitApp.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2014 by Jonathan Naylor G4KLX + * APRSTransmit Copyright (C) 2015 Geoffrey Merck F4FXL / KC3FRA + * + * 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 "APRSTransmit.h" + +#include +#include + +const wxChar* REPEATER_PARAM = wxT("Repeater"); +const wxChar* FILE_OPTION = wxT("file"); +const wxChar* APRS_OPTION = wxT("aprs"); + +int main(int argc, char** argv) +{ + bool res = ::wxInitialize(); + if (!res) { + ::fprintf(stderr, "aprstransmit: failed to initialise the wxWidgets library, exiting\n"); + return 1; + } + + wxCmdLineParser parser(argc, argv); + parser.AddParam(REPEATER_PARAM, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption(APRS_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption(FILE_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + + int cmd = parser.Parse(); + if (cmd != 0) { + ::wxUninitialize(); + return 1; + } + + if (parser.GetParamCount() < 1U) { + ::fprintf(stderr, "aprstransmit: invalid command line usage: aprstransmit -aprs |-file , exiting\n"); + ::wxUninitialize(); + return 1; + } + + wxString text; + bool aprsFound = parser.Found(APRS_OPTION, &text); + + wxString filename; + bool fileFound = parser.Found(FILE_OPTION, &filename); + + if (!aprsFound && !fileFound) { + ::fprintf(stderr, "aprstransmit: invalid command line usage: aprstransmit -aprs |-file , exiting\n"); + ::wxUninitialize(); + return 1; + } + + if (aprsFound && fileFound) { + ::fprintf(stderr, "aprstransmit: invalid command line usage: aprstransmit -aprs |-file , exiting\n"); + ::wxUninitialize(); + return 1; + } + + wxString repeater = parser.GetParam(0U); + repeater.Replace(wxT("_"), wxT(" ")); + repeater.resize(LONG_CALLSIGN_LENGTH, wxT(' ')); + repeater.MakeUpper(); + + if (fileFound) { + wxTextFile file; + bool found = file.Open(filename); + if (!found) { + ::fprintf(stderr, "aprstransmit: unable to open the file, exiting\n"); + ::wxUninitialize(); + return 1; + } + + text = file.GetFirstLine(); + + file.Close(); + } + + + CAPRSTransmit tt(repeater, text); + bool ret = tt.run(); + + ::wxUninitialize(); + + return ret ? 0 : 1; +} + diff --git a/APRSTransmit/APRSTransmitAppD.cpp b/APRSTransmit/APRSTransmitAppD.cpp new file mode 100644 index 0000000..60f18aa --- /dev/null +++ b/APRSTransmit/APRSTransmitAppD.cpp @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2014 by Jonathan Naylor G4KLX + * APRSTransmit Copyright (C) 2015 Geoffrey Merck F4FXL / KC3FRA + * + * 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 "APRSTransmit.h" +#include "APRSTransmitAppD.h" +#include "APRSWriterThread.h" + +#include +#include +#include +#include + +#if defined(__WINDOWS__) +#include +#include +#endif + +const wxChar* REPEATER_PARAM = wxT("Repeater"); +const wxChar* APRS_HOST = wxT("host"); +const wxChar* APRS_PORT = wxT("port"); +const wxChar* APRS_FILTER = wxT("filter"); +const wxChar* DAEMON_SWITCH = wxT("daemon"); + +static CAPRSTransmitAppD* m_aprsTransmit = NULL; + +static void handler(int signum) +{ + m_aprsTransmit->kill(); +} + +static void aprsFrameCallback(const wxString& aprsFrame) +{ + //wxLogMessage(wxT("Received APRS Frame : ") + aprsFrame); + m_aprsTransmit->m_aprsFramesQueue->addData(new wxString(aprsFrame.Clone())); +} + +int main(int argc, char** argv) +{ + bool res = ::wxInitialize(); + if (!res) { + ::fprintf(stderr, "aprstransmit: failed to initialise the wxWidgets library, exiting\n"); + return 1; + } + + wxCmdLineParser parser(argc, argv); + parser.AddParam(REPEATER_PARAM, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption(APRS_HOST, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption(APRS_PORT, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_NUMBER, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption(APRS_FILTER, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddSwitch(DAEMON_SWITCH, wxEmptyString, wxEmptyString, wxCMD_LINE_PARAM_OPTIONAL); + + int cmd = parser.Parse(); + if (cmd != 0) { + ::wxUninitialize(); + return 1; + } + + if (parser.GetParamCount() < 1U) { + ::fprintf(stderr, "aprstransmitd: invalid command line usage: aprstransmitd [-host ] [-port ] [-filter [;init()) { + ::wxUninitialize(); + return 1; + } +#else + if (daemon) { + pid_t pid = ::fork(); + + if (pid < 0) { + ::fprintf(stderr, "aprstransmitd: error in fork(), exiting\n"); + ::wxUninitialize(); + return 1; + } + + // If this is the parent, exit + if (pid > 0) + return 0; + + // We are the child from here onwards + ::setsid(); + ::chdir("/"); + ::umask(0); + } + + //create a pid file + wxString pidFileName = wxT("/var/run/aprstransmit.pid"); + FILE* fp = ::fopen(pidFileName.mb_str(), "wt"); + if (fp != NULL) { + ::fprintf(fp, "%u\n", ::getpid()); + ::fclose(fp); + } + + m_aprsTransmit = new CAPRSTransmitAppD(repeater, aprsHost, aprsPort, aprsFilter, daemon); + if (!m_aprsTransmit->init()) { + ::wxUninitialize(); + return 1; + } + + ::signal(SIGUSR1, handler); + + + ::unlink(pidFileName.mb_str()); +#endif + m_aprsTransmit->run(); + delete m_aprsTransmit; + ::wxUninitialize(); + return 0; +} + + + +CAPRSTransmitAppD::CAPRSTransmitAppD(const wxString& repeater, const wxString& aprsHost, unsigned int aprsPort, const wxString& aprsFilter, bool daemon) : +m_aprsFramesQueue(NULL), +m_repeater(repeater), +m_aprsHost(aprsHost), +m_aprsFilter(aprsFilter), +m_aprsPort(aprsPort), +m_aprsThread(NULL), +m_run(false), +m_checker(NULL), +m_daemon(daemon) +{ +} + +CAPRSTransmitAppD::~CAPRSTransmitAppD() +{ + cleanup(); +} + + +bool CAPRSTransmitAppD::init() +{ +#if defined(__WINDOWS__) + wxString tempPath = wxFileName::GetTempDir(); + m_checker = new wxSingleInstanceChecker(wxT("aprstransmit"), tempPath); +#else + m_checker = new wxSingleInstanceChecker(wxT("aprstransmit"), wxT("/tmp")); +#endif + bool ret = m_checker->IsAnotherRunning(); + if (ret) { + wxLogError(wxT("Another copy of APRSTransmit is running, exiting")); + return false; + } + +#if defined(__WINDOWS__) + wxLog* logger = new wxLogStream(&std::cout); + wxLog::SetActiveTarget(logger); + wxLog::SetLogLevel(wxLOG_Message); + if(m_daemon) + wxLogMessage(wxT("Daemon not supported under Windows, ignoring")); + m_daemon = false; +#else + if(!m_daemon){ + wxLog* logger = new wxLogStream(&std::cout); + wxLog::SetActiveTarget(logger); + wxLog::SetLogLevel(wxLOG_Message); + } else { + new wxLogNull; + } +#endif + + return true; +} + +void CAPRSTransmitAppD::run() +{ + if(m_run) return; + + m_aprsFramesQueue = new CRingBuffer(30U); + m_aprsThread = new CAPRSWriterThread(m_repeater, wxT("0.0.0.0"), m_aprsHost, m_aprsPort, m_aprsFilter, wxT("APRSTransmit 1.1")); + m_aprsThread->setReadAPRSCallback(aprsFrameCallback); + m_aprsThread->start(); + + wxString * aprsFrame; + + m_run = true; + while(m_run){ + wxMilliSleep(10U); + aprsFrame = m_aprsFramesQueue->getData(); + if(aprsFrame){ + CAPRSTransmit aprsTransmit(m_repeater, wxString(*aprsFrame)); + aprsTransmit.run(); + delete aprsFrame; + } + } + + m_aprsThread->stop(); + + cleanup(); +} + + +void CAPRSTransmitAppD::cleanup() +{ + m_aprsThread->setReadAPRSCallback(NULL); + + if(m_aprsFramesQueue) + { + while(m_aprsFramesQueue->peek()) delete m_aprsFramesQueue->getData(); + delete m_aprsFramesQueue; + m_aprsFramesQueue = NULL; + } + if(m_checker) { + delete m_checker; + m_checker = NULL; + } + + if(m_aprsThread) + { + delete m_aprsThread; + m_aprsThread = NULL; + } +} + +void CAPRSTransmitAppD::kill() +{ + m_run = false; +} + diff --git a/APRSTransmit/APRSTransmitAppD.h b/APRSTransmit/APRSTransmitAppD.h new file mode 100644 index 0000000..815a16b --- /dev/null +++ b/APRSTransmit/APRSTransmitAppD.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2014 by Jonathan Naylor G4KLX + * APRSTransmit Copyright (C) 2015 Geoffrey Merck F4FXL / KC3FRA + * + * 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 APRSTransmitAppD_H +#define APRSTransmitAppD_H + +#include "RingBuffer.h" +#include "APRSWriterThread.h" + +#include +#include +#include + + +class CAPRSTransmitAppD { + +public: + CAPRSTransmitAppD(const wxString& repeater, const wxString& aprsHost, unsigned int aprsPort, const wxString& aprsFilter, bool daemon); + ~CAPRSTransmitAppD(); + + CRingBuffer * m_aprsFramesQueue; + + bool init(); + void run(); + void kill(); + +private: + wxString m_repeater, m_aprsHost, m_aprsFilter; + unsigned int m_aprsPort; + CAPRSWriterThread * m_aprsThread; + bool m_run; + wxSingleInstanceChecker * m_checker; + bool m_daemon; + + void cleanup(); +}; + +#endif diff --git a/APRSTransmit/APRSTransmitD.vcxproj b/APRSTransmit/APRSTransmitD.vcxproj new file mode 100644 index 0000000..7c3486c --- /dev/null +++ b/APRSTransmit/APRSTransmitD.vcxproj @@ -0,0 +1,189 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {C706EF5D-3917-4796-8BEB-823498A1B13C} + APRSTransmit + Win32Proj + 10.0.16299.0 + + + + Application + v141 + Unicode + true + + + Application + v141 + Unicode + true + + + Application + v141 + Unicode + + + Application + v141 + Unicode + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>14.0.24720.0 + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\$(ProjectName)\ + true + $(WXWIN)\include;$(WXWIN)\lib\vc_dll\mswud;$(IncludePath) + + + true + $(WXWIN)\include;$(WXWIN)\lib\vc_dll\mswud;$(IncludePath) + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\$(ProjectName)\ + false + + + false + + + + Disabled + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATEWIN32;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + Level3 + EditAndContinue + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_dll;%(AdditionalLibraryDirectories) + true + Console + MachineX86 + + + + + Disabled + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATEWIN32;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + ProgramDatabase + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_x64_dll;%(AdditionalLibraryDirectories) + true + Console + + + + + MaxSpeed + true + $(WXWIN)\include\msvc;$(WXWIN)\include;../ircDDB;../Common;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + Level3 + ProgramDatabase + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_dll;%(AdditionalLibraryDirectories) + true + Console + true + true + MachineX86 + + + + + MaxSpeed + true + $(WXWIN)\include\msvc;$(WXWIN)\include;../ircDDB;../Common;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_x64_dll;%(AdditionalLibraryDirectories) + true + Console + true + true + + + + + + + + + + + + + + + {e793cb8e-2ac9-431a-bbfc-3f52537bb3cf} + false + + + + + + \ No newline at end of file diff --git a/APRSTransmit/APRSTransmitD.vcxproj.filters b/APRSTransmit/APRSTransmitD.vcxproj.filters new file mode 100644 index 0000000..7910ae4 --- /dev/null +++ b/APRSTransmit/APRSTransmitD.vcxproj.filters @@ -0,0 +1,35 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/CHANGES.txt b/CHANGES.txt new file mode 100644 index 0000000..4f15778 --- /dev/null +++ b/CHANGES.txt @@ -0,0 +1,1478 @@ +ircDDB Gateway - 20180509 +========================= + +20101010 +-------- + +First preview release, lots of bugs left. +CIRCDDB v0.3. + +20101017 +-------- + +Lots of bugs fixed. +Linking to a reflector at startup is now implemented. +Implemented reflector linking at startup. +CIRCDDB v0.4. + +20101018 +-------- + +Initial Linux support. +Drop links on gateway exit. +CIRCDDB v0.4. + +20101023 +-------- + +Incoming DExtra links should now work. +The GUI shows the last ten lines of the log. +Began support for Icom RP2C controller. +Added local cross-band RPT2 routing. +CIRCDDB v0.4.2. + +20101024 +-------- + +First version of Icom RP2C controller support. +Not generally released. + +20101024a +--------- + +Completed Icom RP2C support. + +20101026 +-------- + +Implemented the reconnect timer. +Added language option for the slow data in the ack. +Added German and Danish. + +20101028 +-------- + +Fixed bugs in the 20101026 beta release. +Implemented a proper stop-and-wait protocol handler for communicating with Icom RP2C controllers. +Log ircDDB connection status. +CIRCDDB v1.0. + +20101031 +-------- + +Small changes and bug fixes here and there. + +20101101 +-------- + +Small changes and bug fixes here and there. + +20101101a +--------- + +Small changes and bug fixes here and there. +Added French and Italian translations for the ack text. + +20101102 +-------- + +Added status file for active links for web GUIs. + +20101105 +-------- + +Added -gui command line flag to switch the GUI on. +Recognise another DPlus packet type. +Create a header status file. + +20101107 +-------- + +Added the slow data export UDP socket, details in Usage.txt. +On Linux the PID of the main process is written to /var/run/ircDDBG.pid. +Various bugs fixed. + +20101109 +-------- + +More changes in the Icom RP2C support. + +20101112 +-------- + +All logging under Linux is now in /var/log. +Can force unlinking when the reconnect timer has expired and the reflector is . +More changes to the Icom controller support. + +20101113 +-------- + +Fixed a Linux compile bug. +Invalid cross-band routing is handled nicely. +More work on the Icom controller support. + +20101114 +-------- + +Slight change to the German text for length reasons. +Upgraded the GUI display. +More changes to the Icom support. + +20101117 +-------- + +Clean-up the GUI more. +Add another repeater port and allow for port letter D. +Allow all eight characters for the D-Plus login callsign. +Allow the cache to be written at program exit and re-read when it starts up. +On Icom systems, the ack is generated by the gateway and includes the link state. + +20101118 +-------- + +Removed the cache reading and writing at program start and stop repsectively. +Removed the intelligent ack for Icom systems, they don't seem to like it! + +20101119 +-------- + +Stop gateways from connecting as Dongle users. +More Icom controller changes. +GUI changes for Linux. + +20101127 +-------- + +Added acks for multi-port Icom repeater systems. +Added a UDP port to export all incoming repeater data to. +Removed the slow data UDP port. +Added D-PRS support. +Not generally released. + +20101128 +-------- + +Changed APRS connection to TCP from UDP. +Updated the IP address for XRF008 in DExtra_Hosts.txt. +The repeaters announce themselves via APRS. + +20101201 +-------- + +Put the APRS writing into a thread to improve performance. +Allow Dongle users to hear and be heard over the network. +More Icom controller changes. + +20101203 +-------- + +Handle some odd APRS cases for the repeater locations. +Relax incoming repeater validation to allow CLI tools. +Don't send data to Dongles that are sending the data. + +20101203a +--------- + +Changed to wxWidgets 2.8.11. +Fixed startup with blank entries. + +20101204 +-------- + +Added the Polish language to the ack text. +Changed the Dongle handling to pass all radio audio. +Fixed nasty bug in the DExtra code. + +20101205 +-------- + +Added new ircDDB feature to record the destination and text data. +Added new ircDDB feature to record statistics, but they're not complete yet. +Changed repeater APRS description to be the frequency and offset. +CIRCDDB 1.1. + +20101208 +-------- + +Increased the precision of the latitude and longitude values. +Changed to a new extension protocol to pass data to/from a repeater. +Collect extra statistics. + +20101215 +-------- + +Stop duplicate link requests to the same reflector module. +Display Dongle connections in the GUI. +Upgraded the DPlus handling. +Added the XRF055 reflector. + +20101217 +-------- + +More DPlus changes. + +20110103 +-------- + +Create the /usr/local/etc and /usr/local/bin directories at install time if they don't already exist. +Changed APRS login to be the gateway callsign including the "-G". +Added XRF019, XRF026, XRF027 and XRF044 to DExtra_Hosts.txt. +Added the latest DPlus_Hosts.txt file. + +20110104 +-------- + +Fixed the APRS login. + +20110105 +-------- + +Fixed a bug in the G2 callsign routing when the last letter wasn't blank. + +20110119 +-------- + +Registers with the Dutch*Star DPNS system. +Allow incoming DPlus Dongle links. +Added XRF000 to DExtra_Hosts.txt. + +20110119a +--------- + +More work on DPNS registration. + +20110120 +-------- + +Upgraded the DExtra Dongle support. +More changes to the DPlus protocol. + +20110121 +-------- + +Fixed incoming DPlus callsign mapping. + +20110124 +-------- + +Program no longer overwrites the DPlus_Hosts.txt file on startup. +More DPlus changes to DPNS and port handling. +Commands "E" and "I" are now reserved. + +20110127 +-------- + +The echo command has now been implemented. +Make use of the new DExtra type byte. +Fixed DPlus incoming links, but repeater ports may need changing. + +20110128 +-------- + +Fixed the echo command. + +20110130 +-------- + +Changed the echo replay timing to remove jitter. + +20110201 +-------- + +More echo jitter removal. +The "T" command is now reserved. +Change the UR callsign for the echo reply. + +20110206 +-------- + +Remove the "T" command. +Stop XRF reflectors coming from DPlus going into the cache. +Only write entries from opendstar.org into the cache. +Added the info command with English and German voice files. +Changed the default ports for the gateway and repeaters. + +20110208 +-------- + +Fixed bugs in the audio unit. +Added US English as an option. +Added pauses between phrases and letter/numbers. + +20110213 +-------- + +Improved audio and index files for German, and UK and US English. +Added audio and index files for Italian, French, and Danish. +When connecting to a reflector an audio announcement is made automatically. + +20110215 +-------- + +Allow the Echo and Info command to be disabled. +Swap the RPT1 and RPT2 callsigns for incoming DPlus links. +Added XRF020 to the DExtra_Hosts.txt file. +Added slow data link information to the Info reply. +Info is returned every time a link command is issued, if enabled. + +20110215a +--------- + +Fixed a bug in the info command slow data. +Fixed a bug with headers disrupting DPlus and DExtra links. + +20110215b +--------- + +Fixed Linux compilation bug. + +20110216 +-------- + +Re-swap the DPlus RPT1 and RPT2 callsigns for incoming DPlus links. + +20110219 +-------- + +Fixed the en_US.indx file for the letter X. +Clear the APRS queue if new data arrives. +Allow for linking protocol locking by using the DPlus_Hosts.txt and DExtra_Hosts.txt files. +Added Spanish and Swedish translations. + +20110222 +-------- + +The reconnect timer is now only reset by local RF users. + +20110226 +-------- + +The reconnect timer now includes 60, 90, and 120 minute values. +Reconnect timer works properly with a reflector entry of None. + +20110306 +-------- + +Added daily logs. +Added D-RATS support. +Remove Links.log at startup. +Added XRF007 to the DExtra_Hosts.txt file. + +20110308 +-------- + +Added the -logdir command line option to change the logging directory. +Added XRF021 to the DExtra_Hosts.txt file. + +20110312 +-------- + +Truncate Links.log instead of deleting it at startup. +Fix DPlus being disabled. +Added extra validation to link requests. + +20110313 +-------- + +Added Polish to the Info command. +Updated IP address of XRF008. + +20110315 +-------- + +Networking changes. + +20110320 +-------- + +First version of StarNet. +Not generally released. + +20110320a +--------- + +Bug fixes to StarNet. +Not generally released. + +20110324 +-------- + +Changes to StarNet. +Not generally released. + +20110329 +-------- + +Changes to StarNet. +Not generally released. + +20110330 +-------- + +Allow hostnames in DExtra_Hosts.txt and DPlus_Hosts.txt. +More changes to StarNet. +Not generally released. + +20110404 +-------- + +Beginnings of Icom DD support. +Added two extra StarNet Groups to the configuration. + +20110407 +-------- + +Restructured the source code. +Added the StarNetServer. +Heard message for the StarNet groups now includes the information text. +StarNet headers now appear in the Headers.log if enabled. + +20110409 +-------- + +Fixed reflector relinking when the link fails. +More work on DD support. +Added optional DExtra Link to StarNet. + +20110410 +-------- + +Fixed compilation under Linux when DEXTRA_LINK is defined. + +20110415 +-------- + +Changed from wxWidgets 2.8.11 to 2.8.12. +Added an extra callsign field for StarNet groups to allow a callsign to be used to log off. +Allow StarNet logins, logoffs and info requests when relaying other data. + +20110417 +-------- + +Added the optional StarNet.log to contain StarNet group activity. +Sanitise incoming reflector headers to fix problems created elsewhere. + +20110417a +--------- + +Allow StarNet Server to bind to one IP address. +Not generally released. + +20110419 +-------- + +Added XRF033. +Updated the IP address of XRF013. +The -logdir option works for the output GUI logs too. +Improve the STARnet branding wherever possible. + +20110506 +-------- + +Added XRF031. +Updated the IP address of XRF021 +Randomised the ids of locally generated data. +Increased the delay for sending STARnet control messages. +Removed incoming port restriction for incoming DExtra links. +Increased the number of groups in the StarNet Server from 10 to 15. +First version of the XReflector DExtra/Dplus reflector. + +20110507 +-------- + +Fixed Links.log and added Headers.log to XReflector. +Updated XReflector DPNS registering with additional data. + +20110507a +--------- + +Changed the format of Links.log slightly. +Added -daemon option to create proper daemons on Linux. + +20110511 +-------- + +Special debug version for testing DD mode support. +Not generally released. + +20110512 +-------- + +Added the -confdir option to the daemon versions of the programs. +First version of DD support. + +20110513 +-------- + +Updates to the DD support. +Fixed the reconnect bug in DExtra. +Updated the IP address of XRF020. + +20110523 +-------- + +Upgraded CIRCDDB to v1.2. +Added extra logging when log files cannot be opened. +Extra configuration entries for a description, URL and height above ground level added for QRG & Maps. +Add switches to disable APRS and DExtra. + +20110525 +-------- + +Store and transmit more decimals for the frequency setting(s). +Explicit setting of the DD mode port is now used. +Use the ircDDB watchdog with the software version (repeater 20110525 and newer needed). + +20110529 +-------- + +Improved the software version reporting for Icom controllers and the gateway. + +20110603 +-------- + +Changed "Description" to "QTH" in the gateway configuration tab. +Removed "ircDDB Gateway" as the default first line of the QTH. +Switch off DD mode when not set on one of the repeaters. +Changes to DD mode to fix a crash and short ethernet packets. +Not generally released. + +20110606 +-------- + +Found the bug that suppressed the login and logoff STARnet confirmations. + +20110619 +-------- + +The new Remote Control application has been created to control the ircDDB Gateway and the STARnet Server. + +20110621 +-------- + +Clean-ups and minor fixes for the Remote Control application. + +20110622 +-------- + +Added an optional name to the Remote Control application. +Removed some logging statements. +Added extra user logging to XReflector. +Increased frequency of D-Plus authentication and IP address refreshing. + +20110627 +-------- + +Extra logging to XReflector for user logging. +Removed and simplified internal functions. + +20110629 +-------- + +CIRCDDB v1.2.2. + +20110702 +-------- + +CIRCDDB v1.2.3. + +20110704 +-------- + +Upgraded the APRS reporting of the repeater ports. +Changed DD mode repeater configuration. +Added XRF028. + +20110706 +-------- + +Cleaned up the GUI a bit. +Reverted a change to the XReflector GUI logs. +Fixed Remote Control with DD repeaters. + +20110713 +-------- + +You can now mix homebrew and Icom repeaters on one gateway. +ircDDB queries now have a five second timeout. +Update XRF033. + +20110719 +-------- + +Added Nederlands (NL) and Nederlands (BE) as slow text language options. +Fixed many memory leaks identified by valgrind. +Fixed ircDDB query timer bug. + +20110808 +-------- + +Added debug flags to Linux builds for bug tracking. +Make internal timers more accurate. +Updated to CIRCDDB 1.2.4. + +20110815 +-------- + +Made the Remote Control protocol to be processor independent. +Added wxWidgets and OS version logging to all programs. +Identify as KLX-ware to the DPNS. +Fixed long timeouts with the timer class. +Handle error returns from ircDDB better. + +20110817 +-------- + +Fixed bug in DExtra Link introduced in the last release. + +20110907 +-------- + +Added XRF073. +Removed the Repeater Tap. +Added the MYCALL and TX Msg switches for each STARnet Group. + +20110918 +-------- + +Changed the TX Msg Switch default to true. +Changed XRF002 IP address. +Changed XRF073 IP address. +Added extra DD mode log file. + +20110922 +-------- + +Added timer to DD mode ircDDB reporting, to reduce the amount of traffic. +Not generally relased. + +20110923 +-------- + +More DD mode changes for ircDDB visualisation. + +20111006 +-------- + +Restored the English (US) voice for the info command. +Added XRF069. + +20111022 +-------- + +Updated XRF069 IP address. +Changes to the repeater to gateway protocol. + +20111102 +-------- + +Added XRF123. +The default log directory is now the home directory under Linux. + +20111107 +-------- + +Loosen the internal timing of the gateway. +Added support for reporting private statuses. + +20111112 +-------- + +Found and fixed R2D2 bug. + +20111120 +-------- + +Allow different callsigns to be used for the repeaters. +Updated the D-Plus hosts file. +Updated XRF069 IP address. +Added XRF038. + +20111122 +-------- + +Updated XRF002 IP address. +Removed alternative callsign entry in the GUI. +Added remote script access changes to the remote control system, tnx DL5DI. + +20111125 +-------- + +Internal clean-ups. + +20111126 +-------- + +Fixed bug in APRS reporting of short repeater callsigns. +Changed timing of the audio and echo units. + +20111204 +-------- + +Allow linking commands when a repeater is busy with network traffic. +Enable D-Plus debug mode. +Change for the Echo and Info commands. + +20111205 +-------- + +Large changes to the Echo and Info commands. + +20111207 +-------- + +Allow for the setting of latitude and longitude per repeater. +Allow for the setting of the maximum number of incoming D-Plus and DExtra dongle links. +Allow up to five status messages. +A change in the link status will generate an INFO message if enabled. + +20111209 +-------- + +Pass more information to the repeaters about the link status. +More INFO messages generated on link status changes. +Reverted the English audio changes. +Updated XRF019s IP address. + +20111212 +-------- + +Remove the USAGE.txt file. +Clean up callsign configuration. + +20111218 +-------- + +Added Timer Control application and daemon. + +20111220 +-------- + +Fixed DExtra bug. + +20111223 +-------- + +Updated XRF031s IP address. + +20111231 +-------- + +Allow linking to reflectors with channel E on D-Plus and DExtra. +Added channel E to the XReflector. +Send an end of transmission to the repeater when unlinking from a live reflector. + +20120109 +-------- + +Remove all channel letter restrictions for linking in the gateway. +Removed DPNS support from XReflector, it isn't being used by the DPNS for some reason. +Improved user logging in XReflector. +Added slow data text logging in XReflector. +Added XRF119. + +20120117 +-------- + +Reduce the rate that DD-mode headers are written out to Headers.log. +Updated the Makefiles to handle library dependencies properly. +Updated XRF033s IP address. +Replace the bundled DPlus_Hosts.txt with a new and reduced length one. + +20120125 +-------- + +Added 180 minutes as maximum time for gateway control. +Upgrades to the APRS gating to fully match the specification. +Added GPS mode gating to APRS. +Updated XRF019s IP address. +Added XRF110. + +20120129 +-------- + +Changes to GPS and GPS-A mode to handle malformed data. +Change the relinking behaviour when the link fails. + +20120131 +-------- + +Increase minimum time for consecutive APRS packets to be sent out. +More work on relinking DExtra and D-Plus outgoing links. +More aggressive initialisation of misbehaving Icom RP2C controllers. +Added XRF040. + +20120207 +-------- + +Change in the configuration system for non-Windows platforms. +Convert ircDDB hostname to a drop-down list. + +20120210 +-------- + +Send QRG and range to ircDDB even if the range is zero (DVAP?). +Updated XRF021s IP address. +Small cleanups. + +20120222 +-------- + +Removed the port number from the ircDDB preferences panel. +Added DTMF commands for reflector linking and unlinking. +Updated XRF069s IP address. +Updated the D-Plus host file. + +20120223 +-------- + +Allow for the disabling of DTMF decoding and blanking in the ircDDB Gateway. +Supress DTMF decoding and blanking on crossband traffic. +Hopefully fix D-Plus doubling issue in the ircDDB Gateway and XReflector. + +20120225 +-------- + +Fix any potential problems with doubling with DExtra links. +Fixed XReflector D-Plus user.log format, it was in the wrong order. +Reduce false triggering of the DTMF system by noisy signals. +Validate links to DExtra and D-Plus reflectors at the time of the command. + +20120228 +-------- + +Restore GUI window position on startup. +Allow background DTMF control. +New repeater to gateway protocol implemented. + +20120229 +-------- + +Fixes for background DTMF handling. +Added extra logging and error handling to the XReflector. + +20120302 +-------- + +More changes to the D-Plus support in the XReflector. +Similar changes to the D-Plus support in the gateway. + +20120309 +-------- + +Added repeater watchdog timer to handle poor repeater links. +Updated XRF021s and XRF044s IP address. +Added XRF222, XRF353, and XRF777. +Added support for the new DCS reflector system. +Use dns.xreflector.net to get XRF and DCS reflector IP addresses. + +20120311 +-------- + +Process heard messages from Icom controllers. +Fixed Links.log file updating for DCS. + +20120313 +-------- + +Fall back to the altenate xreflector DNS if the main one fails. +Revert Icom repeater protocol handler change from 20120131. +Extra error handling in XReflector added. +Repeat the repeater header every 21 frames for reliability. +Disable XReflector DNS for DCS reflectors for now. + +20120314 +-------- + +Fix the header repeating for Icom systems. +DExtra Dongle relaying has been fixed, it got broken in 20120213. +Added DCS reflectors to the repeater configuration tab. +Remove Icom heard reporting for now. +Added DCS003. + +20120317 +-------- + +Allow a blank ircDDB password to use the test system. +Handle the callsign server a little better. +Updated XRF021s IP address. +Reinstate the Icom heard reporting. +Disabled DPNS authentication. +Pass reflector link information to ircDDB for ircDDB live. + +20120320 +-------- + +Added XRF000s IP address. +Suppress Icom heard data if a valid header comes in within 100ms. +Last letter of DCS callsigns is never in phonetics. +Allow incoming DCS links. + +20120327 +-------- + +Re-enabled the DPNS authentication. +Updated the DCS and DExtra hosts file to the latest. +Fixed bug in the Linux configuration system. +Added extra reflector logging at startup. + +20120330 +-------- + +Convert the main logs to use UTC instead of local time. +Allow for an IP address lock flag in hosts files. +Updated REF018s IP address. +Removed REF049. +Added DCS006. + +20120403 +-------- + +Minor cleanups. + +20120405 +-------- + +Added the Time Server program. + +20120410 +-------- + +Added the Norwegian language for the gateway announcements. +Added US English and German to the time announcements. +Fix Icom RP2C bug with DCS reflectors. + +20120412 +-------- + +Added DCS007. +Stop duplicate pages appearing in the Timer and Remote Control programs. +Ensure callsigns in the Timer Server are upper case. +Allow software using D-Plus to link into the gateway using the gateway callsign. +Changed default Icom setting in the gateway. + +20120413 +-------- + +Validate with DPNS using the gateway callsign, but the login callsign for opendstar.org. + +20120414 +-------- + +Fixed mistake in the German time announcements. +Fixed Timer Control and handler in the gateway. +Allow all incoming links to hear all other links. + +20120416 +-------- + +Allow for an Every Day entry in the Timer Control program. +Fix audio routing for links in the gateway. +Stop duplicate headers going out over non-RF links. + +20120416a +--------- + +Fix transmitting loop on D-Plus. + +20120417 +-------- + +Fixed the German time in mode 2. +Reorganise the handling of incoming and outgoing links in the gateway. +Add WinSock initialisation to the CIRCDDB library. + +20120420 +-------- + +Introduce a minutes delay to the Timer Control program. +Allow for week day only or weekend only schedule entries into the Timer Control. + +20120422 +-------- + +Rework the Timer Control delay, and make it optional. +Add text only option to the Time Server. +Allow for preloading of the "bands" for an Icom controller. + +20120423 +-------- + +Allow for explicit entry of the Icom band data in the repeater configuration panel. +Allow binding to a specific external IP address. +Allow the passing of a configuration name to the gateway. +Remove the architecture setting in the top level Makefile. +Fixed DD mode logging. + +20120424 +-------- + +Added DCS004. +Allow the Time Server to have a config name to allow more than one instance. +Remove some DD mode logging to reduce the log file size. +Reduced delay in the Timer Control to 20s from 60s. + +20120425 +-------- + +Rework the linking to allow channel change on all types. + +20120428 +-------- + +Add an Unlink button to the Remote Control program. +Rename the output files of the gateway when using it with a config name. + +20120502 +-------- + +Fixed the DTMF blanking in the gateway. +Handle the new callsign server protocol. +Numerous internal cleanups. + +20120511 +-------- + +Improve the timing accuracy throughout the gateway and starnet server. +Upgraded the D-RATS network interface to be more reliable. + +20120512 +-------- + +Upgraded D-RATS RF interface. +Added D-RATS logging to the log file. + +20120524 +-------- + +Added DCS010. +Improved network error logging. +Added Swedish to the Time Server. + +20120605 +-------- + +Removed D-RATS debugging log entries. +Removed XRF009, XRF010, XRF011, XRF030, XRF038, and XRF110. +Updated XRF031s and XRF069s IP addresses. +Added DCS012 and DCS013. +Updated REF001s, REF018s and REF035s IP addresses. +Added REF015, REF049, and REF053. + +20120628 +-------- + +Improve the french times and link status messages. +Allow cross compilation to ARM processors. +Added DCS_LINK to the STARnet Digital servers. + +20120804 +-------- + +Added DCS011, DCS014, and DCS015. +Added REF052 and REF054. +Updated REF008s and REF014s IP address. +Removed REF049. +Removed XRF100. +Fixed ARM cross compilation location of data files. +Fixed AM and PM in English 12-hour clock. +Add an optional permanent user to STARnet Digital groups. +Allow multiple links to the same reflector/gateway from one gateway. +Added the version, " V" command. +Added the two US based ircDDB test servers. +Use new official APRS Ids for APRS reports :-) + +20120805 +-------- + +Fixed the STARnet Digital change, it was slightly wrong. +Make the reported DCS version automatically correct. + +20120826 +-------- + +Upgraded to Visual C++ 2010 on Windows. +Platform dependent settings moved to Makefile includes. +Upgraded the DCS protocol to the latest version. + +20120826a +--------- + +Reverted to Visual C++ 2008. + +20120908 +-------- + +Allow repeater module E to be set. +Allow for multiple links to the same reflector module with internal loopback. +Removed the extra thread created in the Linux command line programs. +Added the ircddb.dstar.su test server. +More changes to the DCS protocol. +Added XRF018 and XRF250. +Added SVN revsion number to the log file. + +20121004 +-------- + +Sort the reflector list in the Remote Control by protocol. +Changed the way that the SVN revision is included. +Change D-RATS handling of incoming network data. +Make the linked/linking/not linked announcement more timely. + +20121023 +-------- + +Make information per repeater. +TimeServer extended for module E. +Added DCS016, DCS017, and DCS018. +Fixed bug in the Time Server that only affected Icom systems. +Changed default APRS host to rotate.aprs2.net. +Changed the announcement audio for B and D for UK English. + +20121029 +-------- + +Fix to the Time Server to make it talk again. +Removed XRF018 and XRF250. +Updated DCS007s IP address. + +20121211 +-------- + +Allow the repeater callsign with a G affixed as the gateway callsign. +DD-Mode multicasts to "all in subnet"/224.0.0.1 and "DX-Cluster"/224.0.0.35 implemented (DX-broadcasts etc.) +Fix APRS reporting when more than one repeater has the same module letter. +Added DCS020. +Updated the DCS protocol. +Fixed DExtra dongle bug. + +20121215 +-------- + +Use link pools for D-Plus, DExtra, and DCS. +Exponential backoff for reflector links. + +20121222 +-------- + +Added REF055, REF056, REF057, and REF058. +Updated REF017s and XRF008s IP address. +Limited UDP port range for D-Plus. +ID rewriting for Icom repeaters. + +20121227 +-------- + +Fixed silly link pool bug(s). +Fixed StarNET Digital compile problem. + +20130111 +-------- + +Updated the host name of the Italian test ircDDB server. +Handle DCS NAK replies to a link request properly. +Handle multiple reflector links better. +Changed gateway loopback behaviour. +Added DCS019. + +20130115 +-------- + +Allow link refusals to be logged and sent as temporary text on the announcement. +Revert DExtra handling to the old behaviour. + +20130122 +-------- + +Add a dummy repeater type for testing. +Fixed slow data text that went missing in 20130115. +Fixed audio problems with multiple reflector links on one gateway. + +20130126 +-------- + +Convert the DCS hosts files to use hostnames. +Update the DCS protocol. +Make ircDDB optional in the gateway. +Removed some log messages (stream ID rewriting and link pools). + +20130131 +-------- + +Fix loopback mode. +Handle APRS failures a little better. + +20130219 +-------- + +Self translated Portugses system messages added. +Only allow DTMF commands when the your call is CQCQCQ. +Modify DExtra DTMF to start with an 'B'. +Updated XRF008s and XRF123s IP addresses. +Allow D-RATS to go over outgoing links. +Removed XReflector. +Added DCS021. +Added XRF333. +Added CCS. + +20130223 +-------- + +Fixed Swedish messages for "Linking to..." and "Linked to...". +Added Spanish, Portuguese, and Norwegian text time messages. +Repeaters announce themselves to CCS for repeater routing. +Correct Portugese translations now used. +Added a DTMF to callsign cache to CCS. +Allow selection of CCS server names. +Internal CCS changes. + +20130302 +-------- + +CCS now updates links.log and the GUI for incoming links. +Suspend outgoing links when a CCS link starts. +Added CCS to the Remote Control application. +Allow voice messages for CCS. +Revision of the Portuguese messages. +Added CCS005 and CCS010 +Added XRF150 + +20130307 +-------- + +Links are not dropped on gateways with incoming CCS links. +CCS is passed to outgoing links on the incoming gateway. +More aggressive link restoration after a CCS link has ended. +Fixed incoming DCS and loopback links. +Fixed the CPU hogging bug. + +20130309 +-------- + +Restored previous CCS behaviour, for now. + +20130315 +-------- + +Fixed port E configuration problem in the GUI. +Don't sort on protocol in the Remote Control GUI. +Remote Control can now unlink incoming links and incoming CCS. +Incoming CCS no longer unlinks existing reflector links. +Info commands are no longer sent over reflector links. +Voice and text messages on incoming CCS links. +Added XRF038. +Updated REF047s IP address. + +20130323 +-------- + +Restrict the characters that can be entered into the description and URL fields. +Allow DTMF "**" to re-link to the default reflector. +Fixed the retry backoff calculation. +Updated XRF003s IP address. + +20130404 +-------- + +Write a .pid file for the gateway (Linux daemon version only). +SIGUSR1 causes the gateway to exit cleanly (Linux daemon version only). +Include code to stop multiple copies of the gateway from running. +Split the configuration of the ircDDB Gateway into a seperate program. +Allow your call command to link to the default reflector. +Allow CCS commands in the your call. +The config directory and file name for the Timer Control are now correct in daemon mode. +The -confdir option now works for the Linux GUI version of the Timer Control. +Check that all repeater modules have seperate callsigns and modules. + +20130405 +-------- + +Added CCS011 and CCS017. +Tweaked GPS-A handling a little with extra debugging. +Makefiles fixed :-) + +20130411 +-------- + +Cleaned up the ircDDB Gateway Config program. +Local echos are not now sent to linked reflectors. +The ircDDB Gateway no longer overwrites the config in GUI mode. +Stop multiple incoming CCS links. +Added French as a language for the Time Server. +Added XRF851. + +20130420 +-------- + +Put the French AMBE and index files into the right place under Linux. +Fixed CCS incoming for "repeater routing". + +20130503 +-------- + +Allow a type to be specified for the gateway. +The local *_Hosts.txt file is now appended to the distributed file. +Read DPlus_Hosts.txt before DExtra_Hosts.txt. +Fix incoming CCS links display in the GUI. + +20130523 +-------- + +Allow for a gateways IP address override file. +Stop the echoed audio from going out over reflector links. +Updated XRF008s, REF035s, REF042s, and REF053s IP addresses. +Added XRF727 and REF060. + +20130611 +-------- + +Added XRF250. +Merge the local *_Hosts.txt file in the RemoteControl and TimerControl programs to match the gateway. +Add another digit to the decimal part of the frequency to handle 6.25 kHz spacing repeaters. +Added the USA QuadNET IRC network to the ircDDB configuration panels. +Allow DTMF numbers to be used for linking to D-Plus and DExtra. +Add remotecontrold to allow command line gateway control. +Handle zero Timer Control actions properly. + +20130622 +-------- + +Allow multiple links to co-operating DExtra reflectors. +Merge the *_Hosts.txt files in the ircDDB Gateway Config program. +Allow for the extra digit in the frequency to be handled by the GUI. +Updated REF047s IP address. +Added XRF310. + +20130725 +-------- + +Added daemon info to the logs. +Fixed STARmet Digital configuration bug in ircDDB Gateway Config. +Restore ircDDB Gateway window at startup. +Added DCS023 and DCS044. +Added XRF121, XRF444, XRF858, and XRFWDX. +Updated XRF901s IP address. + +20131001 +-------- + +Updated XRF012, XRF119, XRF333, and XRFWDXs IP addresses. +Added CCS001(GER), CCS007(NLD), CCS013(NOR), CCS020(USA), CCS022(JPN) and CCS024(USA) +Added DCS024(USA) and DCS025(GER). +Added new Quadnet ircDDB server. +Add homebrew DD data support. +Catch exceptions and other C++ changes in the ircDDB Gateway and STARnet Digital Server. + +20131231 +-------- + +Use latest DExtra and D-Plus host files based on Adrian VK4TUX's. +Added new QuadNet ircDDB server. +Ignore new CCS message type. + +20140101 +-------- + +Fix DEXTRA_LINK compile issues in the StarNET Server. + +20140203 +-------- + +Reduce the network timeout from two to one seconds. +Added the text transmit program. + +20140305 +-------- + +Add IP address logging to the header logging. +Added TextTransmit to Windows. +Write the ircDDB Gateway and STARnet Server configuration files out under Windows. +Rework the CIRCDDB library. +CIRCDDB changes to have better compatibility with QuadNet. +Add STARnet Digital group control to the command-line remote control program. + +20140324 +-------- + +Switch debugging on to enable bug finding on Linux. +Added the voice transmit program. +Allow for multiple STARnet Digital permanent users. + +20140326 +-------- + +Fixed the voice transmit program and added diagnostics. +Fixed the command line remote control program, hopefully. + +20140602 +-------- + +Add StarNet logging for remote control events. +Ensure that duplicate link requests don't initiate a status message. +Stop taking DExtra addresses from xreflector.net +Added the " W" and " M" commands to send weather and messages respectively. + +20150210 +-------- + +Updated the DCS and D-Plus hosts files. +First attempt to fix the multiple linking bug. + +20150213 +-------- + +Slight modification to completely fix the multiple linking bug. + +20150303 +-------- + +A slightly less agressive fix for multiple linking. +Updated the DExtra hosts file. + +20150308 +-------- + +Include .mk file for the Pi2. +Increased the network timeout from one second to two seconds. + +20150507 +-------- + +Update the DExtra_Hosts.txt file. +Handle malformed D-Plus authentication packets better. +Update the copyright date in the GUI Help dialogue boxes. + +20150615 +-------- + +Enable data dumping in the D-RATS server. +Rearrange the source code slightly. +Use hostnames in the DPlus_Hosts.txt file. +Update the DExtra_Hosts.txt file. +Revert to the CIRCDDB library to its previous incarnation. + +20150820 +-------- +Move to the new CCS addressing. + +20151116 +-------- + +CCS7 cleanups and fixes. +Updated DExtra hosts file. +Updated DPlus hosts file to use IP addresses again. +Fix crashing bug in the gateway (from Michael DL1BFF). + +2015xxxx +-------- + +Fix header repeater crashing bug (from Michael DL1BFF). +New slow data encoder from F4FXL. +Added APRSTransmit program from F4FXL. + +20180509 +-------- + +Move to wxWidgets-3.0.x. +UPdate to VS2017 on Windows for 32 and 64 bit compilation. +Simplify the Linux build. diff --git a/COPYING.txt b/COPYING.txt new file mode 100644 index 0000000..3912109 --- /dev/null +++ b/COPYING.txt @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Common/AMBEData.cpp b/Common/AMBEData.cpp new file mode 100644 index 0000000..c30aacd --- /dev/null +++ b/Common/AMBEData.cpp @@ -0,0 +1,629 @@ +/* + * 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 + * 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 "AMBEData.h" + +#include "DStarDefines.h" +#include "Utils.h" + +CAMBEData::CAMBEData() : +m_rptSeq(0U), +m_outSeq(0U), +m_id(0U), +m_band1(0x00U), +m_band2(0x02U), +m_band3(0x01U), +m_data(NULL), +m_yourAddress(), +m_yourPort(0U), +m_myPort(0U), +m_errors(0U), +m_text(), +m_header() +{ + m_data = new unsigned char[DV_FRAME_LENGTH_BYTES]; +} + +CAMBEData::CAMBEData(const CAMBEData& data) : +m_rptSeq(data.m_rptSeq), +m_outSeq(data.m_outSeq), +m_id(data.m_id), +m_band1(data.m_band1), +m_band2(data.m_band2), +m_band3(data.m_band3), +m_data(NULL), +m_yourAddress(data.m_yourAddress), +m_yourPort(data.m_yourPort), +m_myPort(data.m_myPort), +m_errors(data.m_errors), +m_text(data.m_text), +m_header(data.m_header) +{ + m_data = new unsigned char[DV_FRAME_LENGTH_BYTES]; + ::memcpy(m_data, data.m_data, DV_FRAME_LENGTH_BYTES); +} + +CAMBEData::~CAMBEData() +{ + delete[] m_data; +} + +bool CAMBEData::setIcomRepeaterData(const unsigned char *data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort) +{ + wxASSERT(data != NULL); + wxASSERT(length >= 29U); + + m_rptSeq = data[4] * 256U + data[5]; + m_band1 = data[11]; + m_band2 = data[12]; + m_band3 = data[13]; + m_id = data[14] * 256U + data[15]; + m_outSeq = data[16]; + + // A repeater end packet is longer than usual, so we substitute a normal length set of data + if (isEnd()) { + ::memset(m_data, 0x00U, DV_FRAME_LENGTH_BYTES); + ::memcpy(m_data, END_PATTERN_BYTES, END_PATTERN_LENGTH_BYTES); + } else { + ::memcpy(m_data, data + 17U, DV_FRAME_LENGTH_BYTES); + } + + m_yourAddress = yourAddress; + m_yourPort = yourPort; + + return true; +} + +bool CAMBEData::setHBRepeaterData(const unsigned char *data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort) +{ + wxASSERT(data != NULL); + wxASSERT(length >= 21U); + + m_id = data[5U] * 256U + data[6U]; + m_outSeq = data[7U]; + m_errors = data[8U]; + + // A repeater end packet is longer than usual, so we substitute a normal length set of data + if (isEnd()) { + ::memset(m_data, 0x00U, DV_FRAME_LENGTH_BYTES); + ::memcpy(m_data, END_PATTERN_BYTES, END_PATTERN_LENGTH_BYTES); + } else { + ::memcpy(m_data, data + 9U, DV_FRAME_LENGTH_BYTES); + } + + m_yourAddress = yourAddress; + m_yourPort = yourPort; + + return true; +} + +bool CAMBEData::setG2Data(const unsigned char *data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort) +{ + wxASSERT(data != NULL); + wxASSERT(length >= 27U); + + m_band1 = data[9]; + m_band2 = data[10]; + m_band3 = data[11]; + m_id = data[12] * 256U + data[13]; + m_outSeq = data[14]; + + ::memcpy(m_data, data + 15U, DV_FRAME_LENGTH_BYTES); + + m_yourAddress = yourAddress; + m_yourPort = yourPort; + + return true; +} + +bool CAMBEData::setDExtraData(const unsigned char *data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) +{ + wxASSERT(data != NULL); + wxASSERT(length >= 27U); + + m_band1 = data[9]; + m_band2 = data[10]; + m_band3 = data[11]; + m_id = data[12] * 256U + data[13]; + m_outSeq = data[14]; + + ::memcpy(m_data, data + 15U, DV_FRAME_LENGTH_BYTES); + + m_yourAddress = yourAddress; + m_yourPort = yourPort; + m_myPort = myPort; + + return true; +} + +bool CAMBEData::setDPlusData(const unsigned char *data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) +{ + wxASSERT(data != NULL); + wxASSERT(length >= 29U); + + if ((data[0] != 0x1D && data[0] != 0x20) || data[1] != 0x80) { + CUtils::dump(wxT("Invalid AMBE length from D-Plus"), data, length); + return false; + } + + m_band1 = data[11]; + m_band2 = data[12]; + m_band3 = data[13]; + m_id = data[14] * 256U + data[15]; + m_outSeq = data[16]; + + if (isEnd()) { + ::memset(m_data, 0x00U, DV_FRAME_LENGTH_BYTES); + ::memcpy(m_data, END_PATTERN_BYTES, END_PATTERN_LENGTH_BYTES); + } else { + ::memcpy(m_data, data + 17U, DV_FRAME_LENGTH_BYTES); + } + + m_yourAddress = yourAddress; + m_yourPort = yourPort; + m_myPort = myPort; + + return true; +} + +bool CAMBEData::setDCSData(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.setDCSData(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; +} + +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); + wxASSERT(length >= 32U); + + data[0] = 'D'; + data[1] = 'S'; + data[2] = 'T'; + data[3] = 'R'; + + data[4] = m_rptSeq / 256U; // Packet sequence number + data[5] = m_rptSeq % 256U; + + data[6] = 0x73; // Not a response + data[7] = 0x12; // Data type + + data[8] = 0x00; // Length MSB + data[9] = 0x13U; // Length LSB + + data[10] = 0x20; // AMBE plus Slow Data following + + data[11] = m_band1; + data[12] = m_band2; + data[13] = m_band3; + + data[14] = m_id / 256U; // Unique session id + data[15] = m_id % 256U; + + data[16] = m_outSeq; + + ::memcpy(data + 17U, m_data, DV_FRAME_LENGTH_BYTES); + + return 17U + DV_FRAME_LENGTH_BYTES; +} + +unsigned int CAMBEData::getHBRepeaterData(unsigned char *data, unsigned int length) const +{ + wxASSERT(data != NULL); + wxASSERT(length >= 21U); + + data[0] = 'D'; + data[1] = 'S'; + data[2] = 'R'; + data[3] = 'P'; + + data[4] = 0x21U; + + data[5] = m_id / 256U; // Unique session id + data[6] = m_id % 256U; + + data[7] = m_outSeq; + + data[8] = 0U; + + ::memcpy(data + 9U, m_data, DV_FRAME_LENGTH_BYTES); + + return 9U + DV_FRAME_LENGTH_BYTES; +} + +unsigned int CAMBEData::getG2Data(unsigned char *data, unsigned int length) const +{ + wxASSERT(data != NULL); + wxASSERT(length >= 30U); + + data[0] = 'D'; + data[1] = 'S'; + data[2] = 'V'; + data[3] = 'T'; + + data[4] = 0x20; + data[5] = 0x00; + data[6] = 0x15; + data[7] = 0x09; + data[8] = 0x20; + + data[9] = m_band1; + data[10] = m_band2; + data[11] = m_band3; + + data[12] = m_id / 256U; // Unique session id + data[13] = m_id % 256U; + + data[14] = m_outSeq; + + ::memcpy(data + 15U, m_data, DV_FRAME_LENGTH_BYTES); + + return 15U + DV_FRAME_LENGTH_BYTES; +} + +unsigned int CAMBEData::getDExtraData(unsigned char* data, unsigned int length) const +{ + wxASSERT(data != NULL); + wxASSERT(length >= 30U); + + data[0] = 'D'; + data[1] = 'S'; + data[2] = 'V'; + data[3] = 'T'; + + data[4] = 0x20; + data[5] = 0x00; + data[6] = 0x00; + data[7] = 0x00; + data[8] = 0x20; + + data[9] = m_band1; + data[10] = m_band2; + data[11] = m_band3; + + data[12] = m_id % 256U; // Unique session id + data[13] = m_id / 256U; + + data[14] = m_outSeq; + + ::memcpy(data + 15U, m_data, DV_FRAME_LENGTH_BYTES); + + return 15U + DV_FRAME_LENGTH_BYTES; +} + +unsigned int CAMBEData::getDPlusData(unsigned char* data, unsigned int length) const +{ + wxASSERT(data != NULL); + wxASSERT(length >= 32U); + + if (isEnd()) { + data[0] = 0x20; + data[1] = 0x80; + } else { + data[0] = 0x1D; + data[1] = 0x80; + } + + data[2] = 'D'; + data[3] = 'S'; + data[4] = 'V'; + data[5] = 'T'; + + data[6] = 0x20; + data[7] = 0x00; + data[8] = 0x00; + data[9] = 0x00; + data[10] = 0x20; + + data[11] = m_band1; + data[12] = m_band2; + data[13] = m_band3; + + data[14] = m_id % 256U; // Unique session id + data[15] = m_id / 256U; + + data[16] = m_outSeq; + + if (isEnd()) { + ::memcpy(data + 17U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + ::memcpy(data + 26U, END_PATTERN_BYTES, END_PATTERN_LENGTH_BYTES); // Add the end flag + return 17U + DV_FRAME_MAX_LENGTH_BYTES; + } else { + // All other cases, just copy the payload + ::memcpy(data + 17U, m_data, DV_FRAME_LENGTH_BYTES); + return 17U + DV_FRAME_LENGTH_BYTES; + } +} + +unsigned int CAMBEData::getDCSData(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); + + m_header.getDCSData(data, 100U); + + 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; +} + +void CAMBEData::setId(unsigned int id) +{ + m_id = id; +} + +unsigned char CAMBEData::getBand1() const +{ + return m_band1; +} + +unsigned char CAMBEData::getBand2() const +{ + return m_band2; +} + +unsigned char CAMBEData::getBand3() const +{ + return m_band3; +} + +void CAMBEData::setBand1(unsigned char band) +{ + m_band1 = band; +} + +void CAMBEData::setBand2(unsigned char band) +{ + m_band2 = band; +} + +void CAMBEData::setBand3(unsigned char band) +{ + m_band3 = band; +} + +unsigned int CAMBEData::getRptSeq() const +{ + return m_rptSeq; +} + +void CAMBEData::setRptSeq(unsigned int seqNo) +{ + m_rptSeq = seqNo; +} + +unsigned int CAMBEData::getSeq() const +{ + return m_outSeq & 0x1FU; +} + +void CAMBEData::setSeq(unsigned int seqNo) +{ + m_outSeq = seqNo; +} + +bool CAMBEData::isEnd() const +{ + return (m_outSeq & 0x40U) == 0x40U; +} + +void CAMBEData::setEnd(bool end) +{ + if (end) + m_outSeq |= 0x40U; + else + m_outSeq &= ~0x40U; +} + +bool CAMBEData::isSync() const +{ + return (m_outSeq & 0x1FU) == 0x00U; +} + +void CAMBEData::setDestination(const in_addr& address, unsigned int port) +{ + m_yourAddress = address; + m_yourPort = port; +} + +void CAMBEData::setText(const wxString& text) +{ + m_text = text; +} + +in_addr CAMBEData::getYourAddress() const +{ + return m_yourAddress; +} + +unsigned int CAMBEData::getYourPort() const +{ + return m_yourPort; +} + +unsigned int CAMBEData::getMyPort() const +{ + return m_myPort; +} + +CHeaderData& CAMBEData::getHeader() +{ + return m_header; +} + +unsigned int CAMBEData::getErrors() const +{ + return m_errors; +} + +void CAMBEData::setData(const unsigned char *data, unsigned int length) +{ + wxASSERT(data != NULL); + wxASSERT(length >= DV_FRAME_LENGTH_BYTES); + + ::memcpy(m_data, data, DV_FRAME_LENGTH_BYTES); +} + +unsigned int CAMBEData::getData(unsigned char *data, unsigned int length) const +{ + wxASSERT(data != NULL); + wxASSERT(length >= DV_FRAME_LENGTH_BYTES); + + ::memcpy(data, m_data, DV_FRAME_LENGTH_BYTES); + + return DV_FRAME_LENGTH_BYTES; +} + +CAMBEData& CAMBEData::operator=(const CAMBEData& data) +{ + if (&data != this) { + m_rptSeq = data.m_rptSeq; + m_outSeq = data.m_outSeq; + m_id = data.m_id; + m_band1 = data.m_band1; + m_band2 = data.m_band2; + m_band3 = data.m_band3; + m_yourAddress = data.m_yourAddress; + m_yourPort = data.m_yourPort; + m_myPort = data.m_myPort; + m_errors = data.m_errors; + m_text = data.m_text; + m_header = data.m_header; + + ::memcpy(m_data, data.m_data, DV_FRAME_LENGTH_BYTES); + } + + return *this; +} diff --git a/Common/AMBEData.h b/Common/AMBEData.h new file mode 100644 index 0000000..773df7c --- /dev/null +++ b/Common/AMBEData.h @@ -0,0 +1,108 @@ +/* + * 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 + * 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 AMBEData_H +#define AMBEData_H + +#include "HeaderData.h" + +#include + +#if defined(__WINDOWS__) +#include "Inaddr.h" +#else +#include +#endif + +class CAMBEData { +public: + CAMBEData(); + CAMBEData(const CAMBEData& data); + ~CAMBEData(); + + bool setIcomRepeaterData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort); + bool setHBRepeaterData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort); + bool setG2Data(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort); + 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; + void setId(unsigned int id); + + unsigned char getBand1() const; + unsigned char getBand2() const; + unsigned char getBand3() const; + void setBand1(unsigned char band); + void setBand2(unsigned char band); + void setBand3(unsigned char band); + + unsigned int getRptSeq() const; + void setRptSeq(unsigned int seqNo); + + unsigned int getSeq() const; + void setSeq(unsigned int seqNo); + + bool isEnd() const; + void setEnd(bool end); + + bool isSync() const; + + void setData(const unsigned char* data, unsigned int length); + unsigned int getData(unsigned char* data, unsigned int length) const; + + void setDestination(const in_addr& address, unsigned int port); + + void setText(const wxString& text); + + in_addr getYourAddress() const; + unsigned int getYourPort() const; + unsigned int getMyPort() const; + + unsigned int getErrors() const; + + CHeaderData& getHeader(); + + CAMBEData& operator=(const CAMBEData& data); + +private: + unsigned int m_rptSeq; + unsigned char m_outSeq; + unsigned int m_id; + unsigned char m_band1; + unsigned char m_band2; + unsigned char m_band3; + unsigned char* m_data; + in_addr m_yourAddress; + unsigned int m_yourPort; + unsigned int m_myPort; + unsigned int m_errors; + wxString m_text; + CHeaderData m_header; +}; + +#endif diff --git a/Common/APRSCollector.cpp b/Common/APRSCollector.cpp new file mode 100644 index 0000000..1973427 --- /dev/null +++ b/Common/APRSCollector.cpp @@ -0,0 +1,661 @@ +/* + * Copyright (C) 2010,2012,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 "APRSCollector.h" +#include "DStarDefines.h" +#include "Utils.h" + +const unsigned int APRS_CSUM_LENGTH = 4U; +const unsigned int APRS_DATA_LENGTH = 300U; +const unsigned int SLOW_DATA_BLOCK_LENGTH = 6U; + +CAPRSCollector::CAPRSCollector() : +m_state(AS_NONE), +m_ggaData(NULL), +m_ggaLength(0U), +m_ggaValid(false), +m_rmcData(NULL), +m_rmcLength(0U), +m_rmcValid(false), +m_crcData(NULL), +m_crcLength(0U), +m_crcValid(false), +m_txtData(NULL), +m_txtLength(0U), +m_txtValid(false), +m_buffer(NULL), +m_slowData(SS_FIRST) +{ + m_ggaData = new unsigned char[APRS_DATA_LENGTH]; + m_rmcData = new unsigned char[APRS_DATA_LENGTH]; + m_crcData = new unsigned char[APRS_DATA_LENGTH]; + m_txtData = new unsigned char[APRS_DATA_LENGTH]; + m_buffer = new unsigned char[SLOW_DATA_BLOCK_LENGTH]; +} + +CAPRSCollector::~CAPRSCollector() +{ + delete[] m_ggaData; + delete[] m_rmcData; + delete[] m_crcData; + delete[] m_txtData; + delete[] m_buffer; +} + +bool CAPRSCollector::writeData(const unsigned char* data) +{ + wxASSERT(data != NULL); + + switch (m_slowData) { + case SS_FIRST: + m_buffer[0U] = data[0U] ^ SCRAMBLER_BYTE1; + m_buffer[1U] = data[1U] ^ SCRAMBLER_BYTE2; + m_buffer[2U] = data[2U] ^ SCRAMBLER_BYTE3; + m_slowData = SS_SECOND; + return false; + + case SS_SECOND: + m_buffer[3U] = data[0U] ^ SCRAMBLER_BYTE1; + m_buffer[4U] = data[1U] ^ SCRAMBLER_BYTE2; + m_buffer[5U] = data[2U] ^ SCRAMBLER_BYTE3; + m_slowData = SS_FIRST; + break; + } + + // Is it GPS data, or are we collecting data already? + if ((m_buffer[0U] & SLOW_DATA_TYPE_MASK) != SLOW_DATA_TYPE_GPS) + return false; + + return addData(m_buffer + 1U); +} + +void CAPRSCollector::reset() +{ + m_state = AS_NONE; + m_ggaLength = 0U; + m_ggaValid = false; + m_rmcLength = 0U; + m_rmcValid = false; + m_crcLength = 0U; + m_crcValid = false; + m_txtLength = 0U; + m_txtValid = false; + m_slowData = SS_FIRST; +} + +void CAPRSCollector::sync() +{ + m_slowData = SS_FIRST; +} + +bool CAPRSCollector::addData(const unsigned char* data) +{ + wxASSERT(data != NULL); + + if (::memcmp(data, "$GPGG", 5U) == 0) { + m_state = AS_GGA; + m_ggaLength = 0U; + m_ggaValid = false; + m_rmcLength = 0U; + m_rmcValid = false; + m_txtLength = 0U; + m_txtValid = false; + addGGAData(data); + return false; + } else if (::memcmp(data, "$GPRM", 5U) == 0) { + m_state = AS_RMC; + m_rmcLength = 0U; + m_rmcValid = false; + m_txtLength = 0U; + m_txtValid = false; + addRMCData(data); + return false; + } else if (::memcmp(data, "$$CRC", 5U) == 0) { + m_state = AS_CRC; + m_crcLength = 0U; + m_crcValid = false; + return addCRCData(data); + } else if (m_state == AS_RMC && m_rmcLength == 0U) { + m_state = AS_TXT; + m_txtLength = 0U; + m_txtValid = false; + addTXTData(data); + return false; + } else if (m_state == AS_GGA) { + addGGAData(data); + return false; + } else if (m_state == AS_RMC) { + addRMCData(data); + return false; + } else if (m_state == AS_CRC) { + return addCRCData(data); + } else if (m_state == AS_TXT) { + return addTXTData(data); + } + + return false; +} + +void CAPRSCollector::addGGAData(const unsigned char* data) +{ + for (unsigned int i = 0U; i < 5U; i++) { + unsigned char c = data[i]; + + m_ggaData[m_ggaLength] = c & 0x7FU; + m_ggaLength++; + + if (m_ggaLength >= APRS_DATA_LENGTH) { + // CUtils::dump(wxT("Missed end of $GPGGA data"), m_ggaData, m_ggaLength); + m_ggaLength = 0U; + m_ggaValid = false; + m_state = AS_NONE; + return; + } + + if (c == 0x0AU) { + bool ret = checkXOR(m_ggaData + 1U, m_ggaLength - 1U); + if (ret) { + // CUtils::dump(wxT("$GPGGA Valid"), m_ggaData, m_ggaLength); + m_ggaValid = true; + m_state = AS_RMC; + return; + } else { + // CUtils::dump(wxT("$GPGGA Bad checksum"), m_ggaData, m_ggaLength); + m_ggaLength = 0U; + m_ggaValid = false; + m_state = AS_RMC; + return; + } + } + } +} + +void CAPRSCollector::addRMCData(const unsigned char* data) +{ + for (unsigned int i = 0U; i < 5U; i++) { + unsigned char c = data[i]; + + m_rmcData[m_rmcLength] = c & 0x7FU; + m_rmcLength++; + + if (m_rmcLength >= APRS_DATA_LENGTH) { + // CUtils::dump(wxT("Missed end of $GPRMC data"), m_rmcData, m_rmcLength); + m_rmcLength = 0U; + m_rmcValid = false; + m_state = AS_NONE; + return; + } + + if (c == 0x0AU) { + bool ret = checkXOR(m_rmcData + 1U, m_rmcLength - 1U); + if (ret) { + // CUtils::dump(wxT("$GPRMC Valid"), m_rmcData, m_rmcLength); + m_rmcValid = true; + m_state = AS_TXT; + return; + } else { + // CUtils::dump(wxT("$GPRMC Bad checksum"), m_rmcData, m_rmcLength); + m_rmcLength = 0U; + m_rmcValid = false; + m_state = AS_TXT; + return; + } + } + } +} + +bool CAPRSCollector::addCRCData(const unsigned char* data) +{ + for (unsigned int i = 0U; i < 5U; i++) { + unsigned char c = data[i]; + + // m_crcData[m_crcLength] = c & 0x7FU; // XXX + m_crcData[m_crcLength] = c; + m_crcLength++; + + if (m_crcLength >= APRS_DATA_LENGTH) { + // CUtils::dump(wxT("Missed end of $$CRC data"), m_crcData, m_crcLength); + m_state = AS_NONE; + m_crcLength = 0U; + m_crcValid = false; + return false; + } + + if (c == 0x0DU) { + bool ret = checkCRC(m_crcData, m_crcLength); + if (ret) { + // CUtils::dump(wxT("$$CRC Valid"), m_crcData, m_crcLength); + m_state = AS_NONE; + m_crcValid = true; + return true; + } else { + // CUtils::dump(wxT("$$CRC Bad checksum"), m_crcData, m_crcLength); + m_state = AS_NONE; + m_crcLength = 0U; + m_crcValid = false; + return false; + } + } + } + + return false; +} + +bool CAPRSCollector::addTXTData(const unsigned char* data) +{ + for (unsigned int i = 0U; i < 5U; i++) { + unsigned char c = data[i]; + + m_txtData[m_txtLength] = c & 0x7FU; + m_txtLength++; + + if (m_txtLength >= APRS_DATA_LENGTH) { + // CUtils::dump(wxT("Missed end of TEXT data"), m_txtData, m_txtLength); + m_state = AS_NONE; + m_txtLength = 0U; + m_txtValid = false; + return false; + } + + if (c == 0x0AU) { + bool ret = checkXOR(m_txtData, m_txtLength); + if (ret) { + // CUtils::dump(wxT("TEXT Valid"), m_txtData, m_txtLength); + m_state = AS_NONE; + m_txtValid = true; + return true; + } else { + // CUtils::dump(wxT("TEXT Bad checksum"), m_txtData, m_txtLength); + m_state = AS_NONE; + m_txtLength = 0U; + m_txtValid = false; + return false; + } + } + } + + return false; +} + +unsigned int CAPRSCollector::getData(unsigned char* data, unsigned int length) +{ + wxASSERT(data != NULL); + + // Have we got GPS-A data? + if (m_crcValid) { + unsigned int len = m_crcLength - 10U; + if (len > length) + len = length; + + ::memcpy(data, m_crcData + 10U, len); + + m_crcLength = 0U; + m_crcValid = false; + + return len; + } + + // Have we got GGA and text data? + if (m_ggaValid && m_txtValid) { + unsigned int len = convertNMEA1(data, length); + + m_ggaLength = 0U; + m_rmcLength = 0U; + m_txtLength = 0U; + m_ggaValid = false; + m_rmcValid = false; + m_txtValid = false; + + return len; + } + + // Have we got RMC and text data? + if (m_rmcValid && m_txtValid) { + unsigned int len = convertNMEA2(data, length); + + m_ggaLength = 0U; + m_rmcLength = 0U; + m_txtLength = 0U; + m_ggaValid = false; + m_rmcValid = false; + m_txtValid = false; + + return len; + } + + return 0U; +} + +bool CAPRSCollector::checkXOR(const unsigned char* data, unsigned int length) const +{ + unsigned int posStar = 0U; + for (unsigned int i = length - 1U; i > 0U; i--) { + if (data[i] == '*') { + posStar = i; + break; + } + } + + if (posStar == 0U) + return false; + + unsigned char csum = calcXOR(data, posStar); + + char buffer[10U]; + ::sprintf(buffer, "%02X", csum); + + return ::memcmp(buffer, data + posStar + 1U, 2U) == 0; +} + +unsigned char CAPRSCollector::calcXOR(const unsigned char* buffer, unsigned int length) const +{ + wxASSERT(buffer != NULL); + wxASSERT(length > 0U); + + unsigned char res = 0U; + + for (unsigned int i = 0U; i < length; i++) + res ^= buffer[i]; + + return res; +} + +bool CAPRSCollector::checkCRC(const unsigned char* data, unsigned int length) const +{ + unsigned int csum = calcCRC(data + 10U, length - 10U); + + char buffer[10U]; + ::sprintf(buffer, "%04X", csum); + + return ::memcmp(buffer, data + 5U, APRS_CSUM_LENGTH) == 0; +} + +unsigned int CAPRSCollector::calcCRC(const unsigned char* buffer, unsigned int length) const +{ + wxASSERT(buffer != NULL); + wxASSERT(length > 0U); + + unsigned int icomcrc = 0xFFFFU; + + for (unsigned int j = 0U; j < length; j++) { + unsigned char ch = buffer[j]; + + for (unsigned int i = 0U; i < 8U; i++) { + bool xorflag = (((icomcrc ^ ch) & 0x01U) == 0x01U); + + icomcrc >>= 1; + + if (xorflag) + icomcrc ^= 0x8408U; + + ch >>= 1; + } + } + + return ~icomcrc & 0xFFFFU; +} + +unsigned int CAPRSCollector::convertNMEA1(unsigned char* data, unsigned int) +{ + // Parse the $GPGGA string into tokens + char* pGGA[20U]; + ::memset(pGGA, 0x00U, 20U * sizeof(char*)); + unsigned int nGGA = 0U; + + char* str = (char*)m_ggaData; + for (;;) { + char* p = mystrsep(&str, ",\r\n"); + + pGGA[nGGA++] = p; + if (p == NULL) + break; + } + + // Is there any position data? + if (pGGA[2U] == NULL || pGGA[3U] == NULL || pGGA[4U] == NULL || pGGA[5U] == NULL || ::strlen(pGGA[2U]) == 0U || ::strlen(pGGA[3U]) == 0U || ::strlen(pGGA[4U]) == 0 || ::strlen(pGGA[5U]) == 0) + return 0U; + + // Is it a valid GPS fix? + if (::strcmp(pGGA[6U], "0") == 0) + return 0U; + + char callsign[10U]; + ::memset(callsign, ' ', 10U); + ::strncpy(callsign, (char*)m_txtData, 7U); + + // This can't fail! + char* p = ::strchr(callsign, ' '); + + if (m_txtData[6U] == ' ' && m_txtData[7U] != ' ') { + *p++ = '-'; + *p++ = m_txtData[7U]; + } else if (m_txtData[6U] != ' ' && m_txtData[7U] != ' ') { + *p++ = m_txtData[7U]; + } + + *p = '\0'; + + char symbol, overlay; + getSymbol(m_txtData + 9U, symbol, overlay); + + ::sprintf((char*)data, "%s>APDPRS,DSTAR*:!%.7s%s%c%.8s%s%c", callsign, pGGA[2U], pGGA[3U], overlay, pGGA[4U], pGGA[5U], symbol); + + // Get the bearing and speed from the RMC data + if (m_rmcValid) { + // Parse the $GPRMC string into tokens + char* pRMC[20U]; + ::memset(pRMC, 0x00U, 20U * sizeof(char*)); + unsigned int nRMC = 0U; + + str = (char*)m_rmcData; + for (;;) { + p = mystrsep(&str, ",\r\n"); + + pRMC[nRMC++] = p; + if (p == NULL) + break; + } + + // Check that we have a bearing and speed + if (pRMC[7U] != NULL && pRMC[8U] != NULL && ::strlen(pRMC[7U]) > 0U && ::strlen(pRMC[8U]) > 0U) { + int bearing = ::atoi(pRMC[8U]); + int speed = ::atoi(pRMC[7U]); + + ::sprintf((char*)data + ::strlen((char*)data), "%03d/%03d", bearing, speed); + } + } + + ::strcat((char*)data, " "); + + // Insert the message text + unsigned int j = ::strlen((char*)data); + for (unsigned int i = 13U; i < 29U; i++) { + unsigned char c = m_txtData[i]; + + if (c == '*') { + data[j] = '\0'; + break; + } + + data[j++] = c; + } + + if (pGGA[9U] != NULL && ::strlen(pGGA[9U]) > 0U) { + // Convert altitude from metres to feet + int altitude = ::atoi(pGGA[9U]); + ::sprintf((char*)data + ::strlen((char*)data), "/A=%06.0f", float(altitude) * 3.28F); + } + + return ::strlen((char*)data); +} + +unsigned int CAPRSCollector::convertNMEA2(unsigned char* data, unsigned int) +{ + // Parse the $GPRMC string into tokens + char* pRMC[20U]; + ::memset(pRMC, 0x00U, 20U * sizeof(char*)); + unsigned int nRMC = 0U; + + char* str = (char*)m_rmcData; + for (;;) { + char* p = mystrsep(&str, ",\r\n"); + + pRMC[nRMC++] = p; + if (p == NULL) + break; + } + + // Is there any position data? + if (pRMC[3U] == NULL || pRMC[4U] == NULL || pRMC[5U] == NULL || pRMC[6U] == NULL || ::strlen(pRMC[3U]) == 0U || ::strlen(pRMC[4U]) == 0U || ::strlen(pRMC[5U]) == 0 || ::strlen(pRMC[6U]) == 0) + return 0U; + + // Is it a valid GPS fix? + if (::strcmp(pRMC[2U], "A") != 0) + return 0U; + + char callsign[10U]; + ::memset(callsign, ' ', 10U); + ::strncpy(callsign, (char*)m_txtData, 7U); + + // This can't fail! + char* p = ::strchr(callsign, ' '); + + if (m_txtData[6U] == ' ' && m_txtData[7U] != ' ') { + *p++ = '-'; + *p++ = m_txtData[7U]; + } else if (m_txtData[6U] != ' ' && m_txtData[7U] != ' ') { + *p++ = m_txtData[7U]; + } + + *p = '\0'; + + char symbol, overlay; + getSymbol(m_txtData + 9U, symbol, overlay); + + ::sprintf((char*)data, "%s>APDPRS,DSTAR*:!%.7s%s%c%.8s%s%c", callsign, pRMC[3U], pRMC[4U], overlay, pRMC[5U], pRMC[6U], symbol); + + if (pRMC[7U] != NULL && pRMC[8U] != NULL && ::strlen(pRMC[7U]) > 0U && ::strlen(pRMC[8U]) > 0U) { + int bearing = ::atoi(pRMC[8U]); + int speed = ::atoi(pRMC[7U]); + + ::sprintf((char*)data + ::strlen((char*)data), "%03d/%03d", bearing, speed); + } + + if (m_txtData[13U] != '*') + ::strcat((char*)data, " "); + + // Insert the message text + unsigned int j = ::strlen((char*)data); + for (unsigned int i = 13U; i < 29U; i++) { + unsigned char c = m_txtData[i]; + + if (c == '*') { + data[j] = '\0'; + break; + } + + data[j++] = c; + } + + return ::strlen((char*)data); +} + +// Function taken from DPRSIntf.java from Pete Loveall AE5PL +void CAPRSCollector::getSymbol(const unsigned char* data, char& symbol, char& overlay) +{ + symbol = '.'; + + if (data[3U] == ' ') { + int offset = -1; + + switch (data[0U]) { + case 'B': + case 'O': + offset = -33; + break; + case 'P': + case 'A': + offset = 0; + break; + case 'M': + case 'N': + offset = -24; + break; + case 'H': + case 'D': + offset = 8; + break; + case 'L': + case 'S': + offset = 32; + break; + case 'J': + case 'Q': + offset = 74; + break; + default: + break; + } + + if (offset != -1 && ::isalnum(data[1U])) { + bool altIcons = false; + + // x is valid, lets get y + switch (data[0U]) { + case 'O': + case 'A': + case 'N': + case 'D': + case 'S': + case 'Q': + altIcons = true; + break; + } + + symbol = char(data[1U] + offset); + + overlay = '/'; + + if (altIcons) { + if (data[2] == ' ') + overlay = '\\'; + else if (::isalnum(data[2])) + overlay = data[2U]; + else + overlay = 0; + } + } + } +} + +// Source found at +char* CAPRSCollector::mystrsep(char** sp, const char* sep) const +{ + if (sp == NULL || *sp == NULL || **sp == '\0') + return NULL; + + char* s = *sp; + char* p = s + ::strcspn(s, sep); + + if (*p != '\0') + *p++ = '\0'; + + *sp = p; + + return s; +} diff --git a/Common/APRSCollector.h b/Common/APRSCollector.h new file mode 100644 index 0000000..8c79c1a --- /dev/null +++ b/Common/APRSCollector.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2010,2012 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 APRSCollector_H +#define APRSCollector_H + +#include "Defs.h" + +#include + +enum APRS_STATE { + AS_NONE, + AS_GGA, + AS_RMC, + AS_CRC, + AS_TXT +}; + +class CAPRSCollector { +public: + CAPRSCollector(); + ~CAPRSCollector(); + + bool writeData(const unsigned char* data); + + void sync(); + + void reset(); + + unsigned int getData(unsigned char* data, unsigned int length); + +private: + APRS_STATE m_state; + unsigned char* m_ggaData; + unsigned int m_ggaLength; + bool m_ggaValid; + unsigned char* m_rmcData; + unsigned int m_rmcLength; + bool m_rmcValid; + unsigned char* m_crcData; + unsigned int m_crcLength; + bool m_crcValid; + unsigned char* m_txtData; + unsigned int m_txtLength; + bool m_txtValid; + unsigned char* m_buffer; + SLOWDATA_STATE m_slowData; + + bool addData(const unsigned char* data); + void addGGAData(const unsigned char* data); + void addRMCData(const unsigned char* data); + bool addCRCData(const unsigned char* data); + bool addTXTData(const unsigned char* data); + + bool checkXOR(const unsigned char* data, unsigned int length) const; + unsigned char calcXOR(const unsigned char* buffer, unsigned int length) const; + + bool checkCRC(const unsigned char* data, unsigned int length) const; + unsigned int calcCRC(const unsigned char* buffer, unsigned int length) const; + + unsigned int convertNMEA1(unsigned char* data, unsigned int length); + unsigned int convertNMEA2(unsigned char* data, unsigned int length); + + void getSymbol(const unsigned char* data, char& symbol, char& overlay); + + char* mystrsep(char** sp, const char* sep) const; +}; + +#endif diff --git a/Common/APRSWriter.cpp b/Common/APRSWriter.cpp new file mode 100644 index 0000000..ae5d6a0 --- /dev/null +++ b/Common/APRSWriter.cpp @@ -0,0 +1,394 @@ +/* + * 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 + * 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 "APRSWriter.h" + +#include "DStarDefines.h" +#include "Defs.h" + +CAPRSEntry::CAPRSEntry(const wxString& callsign, const wxString& band, double frequency, double offset, double range, double latitude, double longitude, double agl) : +m_callsign(callsign), +m_band(band), +m_frequency(frequency), +m_offset(offset), +m_range(range), +m_latitude(latitude), +m_longitude(longitude), +m_agl(agl), +m_timer(1000U, 10U), +m_first(true), +m_collector(NULL) +{ + m_callsign.Trim(); + + m_collector = new CAPRSCollector; +} + +CAPRSEntry::~CAPRSEntry() +{ + delete m_collector; +} + +wxString CAPRSEntry::getCallsign() const +{ + return m_callsign; +} + +wxString CAPRSEntry::getBand() const +{ + return m_band; +} + +double CAPRSEntry::getFrequency() const +{ + return m_frequency; +} + +double CAPRSEntry::getOffset() const +{ + return m_offset; +} + +double CAPRSEntry::getRange() const +{ + return m_range; +} + +double CAPRSEntry::getLatitude() const +{ + return m_latitude; +} + +double CAPRSEntry::getLongitude() const +{ + return m_longitude; +} + +double CAPRSEntry::getAGL() const +{ + return m_agl; +} + +CAPRSCollector* CAPRSEntry::getCollector() const +{ + return m_collector; +} + +void CAPRSEntry::reset() +{ + m_first = true; + m_timer.stop(); + m_collector->reset(); +} + +void CAPRSEntry::clock(unsigned int ms) +{ + m_timer.clock(ms); +} + +bool CAPRSEntry::isOK() +{ + if (m_first) { + m_first = false; + m_timer.start(); + return true; + } + + if (m_timer.hasExpired()) { + m_timer.start(); + return true; + } else { + m_timer.start(); + return false; + } +} + +CAPRSWriter::CAPRSWriter(const wxString& hostname, unsigned int port, const wxString& gateway, const wxString& address) : +m_thread(NULL), +m_enabled(false), +m_idTimer(1000U, 20U * 60U), // 20 minutes +m_gateway(), +m_array() +{ + wxASSERT(!hostname.IsEmpty()); + wxASSERT(port > 0U); + wxASSERT(!gateway.IsEmpty()); + + m_thread = new CAPRSWriterThread(gateway, address, hostname, port); + + m_gateway = gateway; + m_gateway.Truncate(LONG_CALLSIGN_LENGTH - 1U); + m_gateway.Trim(); +} + +CAPRSWriter::~CAPRSWriter() +{ + for (CEntry_t::iterator it = m_array.begin(); it != m_array.end(); ++it) + delete it->second; + + m_array.clear(); +} + +void CAPRSWriter::setPort(const wxString& callsign, const wxString& band, double frequency, double offset, double range, double latitude, double longitude, double agl) +{ + wxString temp = callsign; + temp.resize(LONG_CALLSIGN_LENGTH - 1U, wxT(' ')); + temp.Append(band); + + m_array[temp] = new CAPRSEntry(callsign, band, frequency, offset, range, latitude, longitude, agl); +} + +bool CAPRSWriter::open() +{ + return m_thread->start(); +} + +void CAPRSWriter::writeData(const wxString& callsign, const CAMBEData& data) +{ + if (data.isEnd()) + return; + + CAPRSEntry* entry = m_array[callsign]; + if (entry == NULL) { + wxLogError(wxT("Cannot find the callsign \"%s\" in the APRS array"), callsign.c_str()); + return; + } + + CAPRSCollector* collector = entry->getCollector(); + + if (data.isSync()) { + collector->sync(); + return; + } + + unsigned char buffer[400U]; + data.getData(buffer, DV_FRAME_MAX_LENGTH_BYTES); + + bool complete = collector->writeData(buffer + VOICE_FRAME_LENGTH_BYTES); + if (!complete) + return; + + if (!m_enabled) { + collector->reset(); + return; + } + + if (!m_thread->isConnected()) { + collector->reset(); + return; + } + + // Check the transmission timer + bool ok = entry->isOK(); + if (!ok) { + collector->reset(); + return; + } + + unsigned int length = collector->getData(buffer, 400U); + wxString text((char*)buffer, wxConvLocal, length); + + int n = text.Find(wxT(':')); + if (n == wxNOT_FOUND) { + collector->reset(); + return; + } + + wxString header = text.Left(n); + wxString body = text.Mid(n + 1U); + + // If we already have a q-construct, don't send it on + n = header.Find(wxT('q')); + if (n != wxNOT_FOUND) + return; + + // Remove the trailing \r + n = body.Find(wxT('\r')); + if (n != wxNOT_FOUND) + body = body.Left(n); + + wxString output; + output.Printf(wxT("%s,qAR,%s-%s:%s"), header.c_str(), entry->getCallsign().c_str(), entry->getBand().c_str(), body.c_str()); + + char ascii[500U]; + ::memset(ascii, 0x00, 500U); + for (unsigned int i = 0U; i < output.Len(); i++) + ascii[i] = output.GetChar(i); + + m_thread->write(ascii); + + collector->reset(); +} + +void CAPRSWriter::reset(const wxString& callsign) +{ + CAPRSEntry* entry = m_array[callsign]; + if (entry == NULL) { + wxLogError(wxT("Cannot find the callsign \"%s\" in the APRS array"), callsign.c_str()); + return; + } + + entry->reset(); +} + +void CAPRSWriter::setEnabled(bool enabled) +{ + m_enabled = enabled; + + if (m_enabled) { + sendIdFrames(); + m_idTimer.start(); + } +} + +void CAPRSWriter::clock(unsigned int ms) +{ + m_idTimer.clock(ms); + + if (m_idTimer.hasExpired()) { + sendIdFrames(); + m_idTimer.start(); + } + + for (CEntry_t::iterator it = m_array.begin(); it != m_array.end(); ++it) + it->second->clock(ms); +} + +bool CAPRSWriter::isConnected() const +{ + return m_thread->isConnected(); +} + +void CAPRSWriter::close() +{ + m_thread->stop(); +} + +void CAPRSWriter::sendIdFrames() +{ + if (!m_thread->isConnected()) + return; + + time_t now; + ::time(&now); + struct tm* tm = ::gmtime(&now); + + for (CEntry_t::iterator it = m_array.begin(); it != m_array.end(); ++it) { + CAPRSEntry* entry = it->second; + if (entry == NULL) + continue; + + // Default values aren't passed on + if (entry->getLatitude() == 0.0 && entry->getLongitude() == 0.0) + continue; + + wxString desc; + if (entry->getBand().Len() > 1U) { + if (entry->getFrequency() != 0.0) + desc.Printf(wxT("Data %.5lfMHz"), entry->getFrequency()); + else + desc = wxT("Data"); + } else { + if (entry->getFrequency() != 0.0) + desc.Printf(wxT("Voice %.5lfMHz %c%.4lfMHz"), + entry->getFrequency(), + entry->getOffset() < 0.0 ? wxT('-') : wxT('+'), + ::fabs(entry->getOffset())); + else + desc = wxT("Voice"); + } + + wxString band; + if (entry->getFrequency() >= 1200.0) + band = wxT("1.2"); + else if (entry->getFrequency() >= 420.0) + band = wxT("440"); + else if (entry->getFrequency() >= 144.0) + band = wxT("2m"); + else if (entry->getFrequency() >= 50.0) + band = wxT("6m"); + else if (entry->getFrequency() >= 28.0) + band = wxT("10m"); + + double tempLat = ::fabs(entry->getLatitude()); + double tempLong = ::fabs(entry->getLongitude()); + + double latitude = ::floor(tempLat); + double longitude = ::floor(tempLong); + + latitude = (tempLat - latitude) * 60.0 + latitude * 100.0; + longitude = (tempLong - longitude) * 60.0 + longitude * 100.0; + + wxString lat; + if (latitude >= 1000.0F) + lat.Printf(wxT("%.2lf"), latitude); + else if (latitude >= 100.0F) + lat.Printf(wxT("0%.2lf"), latitude); + else if (latitude >= 10.0F) + lat.Printf(wxT("00%.2lf"), latitude); + else + lat.Printf(wxT("000%.2lf"), latitude); + + wxString lon; + if (longitude >= 10000.0F) + lon.Printf(wxT("%.2lf"), longitude); + else if (longitude >= 1000.0F) + lon.Printf(wxT("0%.2lf"), longitude); + else if (longitude >= 100.0F) + lon.Printf(wxT("00%.2lf"), longitude); + else if (longitude >= 10.0F) + lon.Printf(wxT("000%.2lf"), longitude); + else + lon.Printf(wxT("0000%.2lf"), longitude); + + // Convert commas to periods in the latitude and longitude + lat.Replace(wxT(","), wxT(".")); + lon.Replace(wxT(","), wxT(".")); + + wxString output; + output.Printf(wxT("%s-S>APDG01,TCPIP*,qAC,%s-GS:;%-7s%-2s*%02d%02d%02dz%s%cD%s%caRNG%04.0lf %s %s"), + m_gateway.c_str(), m_gateway.c_str(), entry->getCallsign().c_str(), entry->getBand().c_str(), + tm->tm_mday, tm->tm_hour, tm->tm_min, + lat.c_str(), (entry->getLatitude() < 0.0F) ? wxT('S') : wxT('N'), + lon.c_str(), (entry->getLongitude() < 0.0F) ? wxT('W') : wxT('E'), + entry->getRange() * 0.6214, band.c_str(), desc.c_str()); + + char ascii[300U]; + ::memset(ascii, 0x00, 300U); + for (unsigned int i = 0U; i < output.Len(); i++) + ascii[i] = output.GetChar(i); + + m_thread->write(ascii); + + if (entry->getBand().Len() == 1U) { + output.Printf(wxT("%s-%s>APDG02,TCPIP*,qAC,%s-%sS:!%s%cD%s%c&RNG%04.0lf %s %s"), + entry->getCallsign().c_str(), entry->getBand().c_str(), entry->getCallsign().c_str(), entry->getBand().c_str(), + lat.c_str(), (entry->getLatitude() < 0.0F) ? wxT('S') : wxT('N'), + lon.c_str(), (entry->getLongitude() < 0.0F) ? wxT('W') : wxT('E'), + entry->getRange() * 0.6214, band.c_str(), desc.c_str()); + + ::memset(ascii, 0x00, 300U); + for (unsigned int i = 0U; i < output.Len(); i++) + ascii[i] = output.GetChar(i); + + m_thread->write(ascii); + } + } + + m_idTimer.start(); +} diff --git a/Common/APRSWriter.h b/Common/APRSWriter.h new file mode 100644 index 0000000..8eb1bf6 --- /dev/null +++ b/Common/APRSWriter.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2010,2011,2012 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 APRSWriter_H +#define APRSWriter_H + +#include "APRSWriterThread.h" +#include "APRSCollector.h" +#include "DStarDefines.h" +#include "AMBEData.h" +#include "Timer.h" +#include "Defs.h" + +#include + +class CAPRSEntry { +public: + CAPRSEntry(const wxString& callsign, const wxString& band, double frequency, double offset, double range, double latitude, double longitude, double agl); + ~CAPRSEntry(); + + wxString getCallsign() const; + wxString getBand() const; + double getFrequency() const; + double getOffset() const; + double getRange() const; + double getLatitude() const; + double getLongitude() const; + double getAGL() const; + CAPRSCollector* getCollector() const; + + // Transmission timer + void reset(); + void clock(unsigned int ms); + bool isOK(); + +private: + wxString m_callsign; + wxString m_band; + double m_frequency; + double m_offset; + double m_range; + double m_latitude; + double m_longitude; + double m_agl; + CTimer m_timer; + bool m_first; + CAPRSCollector* m_collector; +}; + +WX_DECLARE_STRING_HASH_MAP(CAPRSEntry*, CEntry_t); + +class CAPRSWriter { +public: + CAPRSWriter(const wxString& hostname, unsigned int port, const wxString& gateway, const wxString& address); + ~CAPRSWriter(); + + bool open(); + + void setPort(const wxString& callsign, const wxString& band, double frequency, double offset, double range, double latitude, double longitude, double agl); + + void writeData(const wxString& callsign, const CAMBEData& data); + + void reset(const wxString& callsign); + + void setEnabled(bool enable); + + bool isConnected() const; + + void clock(unsigned int ms); + + void close(); + +private: + CAPRSWriterThread* m_thread; + bool m_enabled; + CTimer m_idTimer; + wxString m_gateway; + CEntry_t m_array; + + void sendIdFrames(); +}; + +#endif diff --git a/Common/APRSWriterThread.cpp b/Common/APRSWriterThread.cpp new file mode 100644 index 0000000..dfaf250 --- /dev/null +++ b/Common/APRSWriterThread.cpp @@ -0,0 +1,263 @@ +/* + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "APRSWriterThread.h" +#include "DStarDefines.h" +#include "Utils.h" +#include "Defs.h" + +// #define DUMP_TX + +const unsigned int APRS_TIMEOUT = 10U; + +CAPRSWriterThread::CAPRSWriterThread(const wxString& callsign, const wxString& address, const wxString& hostname, unsigned int port) : +wxThread(wxTHREAD_JOINABLE), +m_username(callsign), +m_ssid(callsign), +m_socket(hostname, port, address), +m_queue(20U), +m_exit(false), +m_connected(false), +m_APRSReadCallback(NULL), +m_filter(wxT("")), +m_clientName(wxT("ircDDBGateway")) +{ + wxASSERT(!callsign.IsEmpty()); + wxASSERT(!hostname.IsEmpty()); + wxASSERT(port > 0U); + + m_username.SetChar(LONG_CALLSIGN_LENGTH - 1U, wxT(' ')); + m_username.Trim(); + m_username.MakeUpper(); + + m_ssid = m_ssid.SubString(LONG_CALLSIGN_LENGTH - 1U, 1); +} + +CAPRSWriterThread::CAPRSWriterThread(const wxString& callsign, const wxString& address, const wxString& hostname, unsigned int port, const wxString& filter, const wxString& clientName) : +wxThread(wxTHREAD_JOINABLE), +m_username(callsign), +m_ssid(callsign), +m_socket(hostname, port, address), +m_queue(20U), +m_exit(false), +m_connected(false), +m_APRSReadCallback(NULL), +m_filter(filter), +m_clientName(clientName) +{ + wxASSERT(!callsign.IsEmpty()); + wxASSERT(!hostname.IsEmpty()); + wxASSERT(port > 0U); + + m_username.SetChar(LONG_CALLSIGN_LENGTH - 1U, wxT(' ')); + m_username.Trim(); + m_username.MakeUpper(); + + m_ssid = m_ssid.SubString(LONG_CALLSIGN_LENGTH - 1U, 1); +} + +CAPRSWriterThread::~CAPRSWriterThread() +{ + m_username.Clear(); +} + +bool CAPRSWriterThread::start() +{ + Create(); + Run(); + + return true; +} + +void* CAPRSWriterThread::Entry() +{ + wxLogMessage(wxT("Starting the APRS Writer thread")); + + m_connected = connect(); + + try { + while (!m_exit) { + if (!m_connected) { + m_connected = connect(); + + if (!m_connected){ + wxLogError(wxT("Reconnect attempt to the APRS server has failed")); + Sleep(10000UL); // 10 secs + } + } + + if (m_connected) { + if(!m_queue.isEmpty()){ + char* p = m_queue.getData(); + + wxString text(p, wxConvLocal); + wxLogMessage(wxT("APRS ==> %s"), text.c_str()); + + ::strcat(p, "\r\n"); + + bool ret = m_socket.write((unsigned char*)p, ::strlen(p)); + if (!ret) { + m_connected = false; + m_socket.close(); + wxLogError(wxT("Connection to the APRS thread has failed")); + } + + delete[] p; + } + { + wxString line; + int length = m_socket.readLine(line, APRS_TIMEOUT); + + /*if (length == 0) + wxLogWarning(wxT("No response from the APRS server after %u seconds"), APRS_TIMEOUT);*/ + + if (length < 0) { + m_connected = false; + m_socket.close(); + wxLogError(wxT("Error when reading from the APRS server")); + } + + if(length > 0 && line.GetChar(0) != '#'//check if we have something and if that something is an APRS frame + && m_APRSReadCallback != NULL)//do we have someone wanting an APRS Frame? + { + //wxLogMessage(wxT("Received APRS Frame : ") + line); + m_APRSReadCallback(wxString(line)); + } + } + + } + } + + if (m_connected) + m_socket.close(); + + while (!m_queue.isEmpty()) { + char* p = m_queue.getData(); + delete[] p; + } + } + catch (std::exception& e) { + wxString message(e.what(), wxConvLocal); + wxLogError(wxT("Exception raised in the APRS Writer thread - \"%s\""), message.c_str()); + } + catch (...) { + wxLogError(wxT("Unknown exception raised in the APRS Writer thread")); + } + + wxLogMessage(wxT("Stopping the APRS Writer thread")); + + return NULL; +} + +void CAPRSWriterThread::setReadAPRSCallback(ReadAPRSFrameCallback cb) +{ + m_APRSReadCallback = cb; +} + +void CAPRSWriterThread::write(const char* data) +{ + wxASSERT(data != NULL); + + if (!m_connected) + return; + + unsigned int len = ::strlen(data); + + char* p = new char[len + 5U]; + ::strcpy(p, data); + + m_queue.addData(p); +} + +bool CAPRSWriterThread::isConnected() const +{ + return m_connected; +} + +void CAPRSWriterThread::stop() +{ + m_exit = true; + + Wait(); +} + +bool CAPRSWriterThread::connect() +{ + unsigned int password = getAPRSPassword(m_username); + + bool ret = m_socket.open(); + if (!ret) + return false; + + //wait for lgin banner + int length; + wxString serverResponse(wxT("")); + length = m_socket.readLine(serverResponse, APRS_TIMEOUT); + if (length == 0) { + wxLogError(wxT("No reply from the APRS server after %u seconds"), APRS_TIMEOUT); + m_socket.close(); + return false; + } + wxLogMessage(wxT("Received login banner : ") + serverResponse); + + wxString filter(m_filter); + if (filter.Length() > 0) filter.Prepend(wxT(" filter ")); + wxString connectString = wxString::Format(wxT("user %s-%s pass %u vers %s%s\n"), m_username.c_str(), m_ssid.c_str(), password, + (m_clientName.Length() ? m_clientName : wxT("ircDDBGateway")).c_str(), + filter.c_str()); + //wxLogMessage(wxT("Connect String : ") + connectString); + ret = m_socket.writeLine(connectString); + if (!ret) { + m_socket.close(); + return false; + } + + + length = m_socket.readLine(serverResponse, APRS_TIMEOUT); + if (length == 0) { + wxLogError(wxT("No reply from the APRS server after %u seconds"), APRS_TIMEOUT); + m_socket.close(); + return false; + } + if (length < 0) { + wxLogError(wxT("Error when reading from the APRS server")); + m_socket.close(); + return false; + } + + wxLogMessage(wxT("Response from APRS server: ") + serverResponse); + + wxLogMessage(wxT("Connected to the APRS server")); + + return true; +} + +unsigned int CAPRSWriterThread::getAPRSPassword(wxString callsign) const +{ + unsigned int len = callsign.Length(); + + wxUint16 hash = 0x73E2U; + + for (unsigned int i = 0U; i < len; i += 2U) { + hash ^= (char)callsign.GetChar(i) << 8; + if(i + 1 < len) + hash ^= (char)callsign.GetChar(i + 1); + } + + return hash & 0x7FFFU; +} diff --git a/Common/APRSWriterThread.h b/Common/APRSWriterThread.h new file mode 100644 index 0000000..1e31254 --- /dev/null +++ b/Common/APRSWriterThread.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010,2011,2012 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 APRSWriterThread_H +#define APRSWriterThread_H + +#include "TCPReaderWriterClient.h" +#include "RingBuffer.h" + +#include +typedef void (*ReadAPRSFrameCallback)(const wxString&); + +class CAPRSWriterThread : public wxThread { +public: + CAPRSWriterThread(const wxString& callsign, const wxString& address, const wxString& hostname, unsigned int port); + CAPRSWriterThread(const wxString& callsign, const wxString& address, const wxString& hostname, unsigned int port, const wxString& filter, const wxString& clientName); + virtual ~CAPRSWriterThread(); + + virtual bool start(); + + virtual bool isConnected() const; + + virtual void write(const char* data); + + virtual void* Entry(); + + virtual void stop(); + + void setReadAPRSCallback(ReadAPRSFrameCallback cb); + +private: + wxString m_username; + wxString m_ssid; + CTCPReaderWriterClient m_socket; + CRingBuffer m_queue; + bool m_exit; + bool m_connected; + ReadAPRSFrameCallback m_APRSReadCallback; + wxString m_filter; + wxString m_clientName; + + bool connect(); + unsigned int getAPRSPassword(wxString username) const; +}; + +#endif diff --git a/Common/AnnouncementUnit.cpp b/Common/AnnouncementUnit.cpp new file mode 100644 index 0000000..c070543 --- /dev/null +++ b/Common/AnnouncementUnit.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (C) 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 "DStarDefines.h" +#include "HeaderData.h" +#include "AnnouncementUnit.h" + +#include + +CAnnouncementUnit::CAnnouncementUnit(IRepeaterCallback* handler, const wxString& callsign, const wxString& fileName, const wxString& name) : +m_handler(handler), +m_callsign(callsign), +m_fileName(fileName), +m_name(name), +m_reader(NULL), +m_status(NS_IDLE), +m_timer(1000U, REPLY_TIME), +m_out(0U), +m_id(0U), +m_time() +{ + wxASSERT(handler != NULL); + + m_name.resize(SHORT_CALLSIGN_LENGTH, wxT(' ')); + + wxLogMessage(wxT("Connected '%s' to %s using file - %s"), name.c_str(), callsign.c_str(), fileName.c_str()); +} + +CAnnouncementUnit::~CAnnouncementUnit() +{ +} + +void CAnnouncementUnit::sendAnnouncement() +{ + if (m_status != NS_IDLE) + return; + + bool ret = wxFile::Exists(m_fileName.c_str()); + if (!ret) + return; + + m_reader = new CDVTOOLFileReader; + + ret = m_reader->open(m_fileName); + if (!ret) { + delete m_reader; + m_reader = NULL; + return; + } + + m_status = NS_WAIT; + m_timer.start(); +} + +void CAnnouncementUnit::clock(unsigned int ms) +{ + m_timer.clock(ms); + + if (m_status == NS_WAIT && m_timer.hasExpired()) { + m_reader->read(); // Pop the first record off the file + + m_id = CHeaderData::createId(); + + CHeaderData header; + header.setMyCall1(m_callsign); + header.setMyCall2(m_name); + header.setYourCall(wxT("CQCQCQ ")); + header.setId(m_id); + + m_handler->process(header, DIR_INCOMING, AS_INFO); + + m_timer.stop(); + + m_out = 0U; + m_status = NS_TRANSMIT; + + m_time.Start(); + + return; + } + + if (m_status == NS_TRANSMIT) { + unsigned int needed = m_time.Time() / DSTAR_FRAME_TIME_MS; + + while (m_out < needed) { + DVTFR_TYPE type = m_reader->read(); + if (type != DVTFR_DATA) { + m_out = 0U; + m_status = NS_IDLE; + m_timer.stop(); + m_reader->close(); + delete m_reader; + m_reader = NULL; + return; + } + + CAMBEData* data = m_reader->readData(); + data->setId(m_id); + + m_out++; + + m_handler->process(*data, DIR_INCOMING, AS_INFO); + + bool end = data->isEnd(); + + delete data; + + if (end) { + m_out = 0U; + m_status = NS_IDLE; + m_reader->close(); + delete m_reader; + m_reader = NULL; + m_timer.stop(); + return; + } + } + + return; + } +} + +void CAnnouncementUnit::cancel() +{ + m_status = NS_IDLE; + m_out = 0U; + m_id = 0U; + + if (m_reader != NULL) { + m_reader->close(); + delete m_reader; + m_reader = NULL; + } + + m_timer.stop(); +} diff --git a/Common/AnnouncementUnit.h b/Common/AnnouncementUnit.h new file mode 100644 index 0000000..57f794b --- /dev/null +++ b/Common/AnnouncementUnit.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 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. + */ + +#ifndef AnnouncementUnit_H +#define AnnouncementUnit_H + +#include "DVTOOLFileReader.h" +#include "RepeaterCallback.h" +#include "AMBEData.h" +#include "Timer.h" +#include "Defs.h" + +#include + +enum ANNOUNCEMENT_STATUS { + NS_IDLE, + NS_WAIT, + NS_TRANSMIT +}; + +class CAnnouncementUnit { +public: + CAnnouncementUnit(IRepeaterCallback* handler, const wxString& callsign, const wxString& fileName, const wxString& name); + ~CAnnouncementUnit(); + + void sendAnnouncement(); + + void cancel(); + + void clock(unsigned int ms); + +private: + IRepeaterCallback* m_handler; + wxString m_callsign; + wxString m_fileName; + wxString m_name; + CDVTOOLFileReader* m_reader; + ANNOUNCEMENT_STATUS m_status; + CTimer m_timer; + unsigned int m_out; + unsigned int m_id; + wxStopWatch m_time; +}; + +#endif diff --git a/Common/AudioUnit.cpp b/Common/AudioUnit.cpp new file mode 100644 index 0000000..da9774b --- /dev/null +++ b/Common/AudioUnit.cpp @@ -0,0 +1,505 @@ +/* + * 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 + * 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 "HeaderData.h" +#include "AudioUnit.h" + +#include +#include +#include +#include + +unsigned char* CAudioUnit::m_ambe = NULL; +unsigned int CAudioUnit::m_ambeLength = 0U; +CIndexList_t CAudioUnit::m_index; + +TEXT_LANG CAudioUnit::m_language = TL_ENGLISH_UK; + +const unsigned int MAX_FRAMES = 60U * DSTAR_FRAMES_PER_SEC; + +const unsigned int SILENCE_LENGTH = 10U; + +void CAudioUnit::initialise() +{ +} + +void CAudioUnit::setLanguage(TEXT_LANG language) +{ + m_language = language; + + wxString ambeFileName; + wxString indxFileName; + + switch (language) { + case TL_DEUTSCH: + ambeFileName = wxT("de_DE.ambe"); + indxFileName = wxT("de_DE.indx"); + break; + case TL_DANSK: + ambeFileName = wxT("dk_DK.ambe"); + indxFileName = wxT("dk_DK.indx"); + break; + case TL_ITALIANO: + ambeFileName = wxT("it_IT.ambe"); + indxFileName = wxT("it_IT.indx"); + break; + case TL_FRANCAIS: + ambeFileName = wxT("fr_FR.ambe"); + indxFileName = wxT("fr_FR.indx"); + break; + case TL_ESPANOL: + ambeFileName = wxT("es_ES.ambe"); + indxFileName = wxT("es_ES.indx"); + break; + case TL_SVENSKA: + ambeFileName = wxT("se_SE.ambe"); + indxFileName = wxT("se_SE.indx"); + break; + case TL_POLSKI: + ambeFileName = wxT("pl_PL.ambe"); + indxFileName = wxT("pl_PL.indx"); + break; + case TL_ENGLISH_US: + ambeFileName = wxT("en_US.ambe"); + indxFileName = wxT("en_US.indx"); + break; + case TL_NORSK: + ambeFileName = wxT("no_NO.ambe"); + indxFileName = wxT("no_NO.indx"); + break; + default: + ambeFileName = wxT("en_GB.ambe"); + indxFileName = wxT("en_GB.indx"); + break; + } + + bool ret = readAMBE(ambeFileName); + if (!ret) { + delete[] m_ambe; + m_ambe = NULL; + return; + } + + ret = readIndex(indxFileName); + if (!ret) { + delete[] m_ambe; + m_ambe = NULL; + } +} + +void CAudioUnit::finalise() +{ + for (CIndexList_t::iterator it = m_index.begin(); it != m_index.end(); ++it) + delete it->second; + + delete[] m_ambe; +} + +CAudioUnit::CAudioUnit(IRepeaterCallback* handler, const wxString& callsign) : +m_handler(handler), +m_callsign(callsign), +m_encoder(), +m_status(AS_IDLE), +m_linkStatus(LS_NONE), +m_tempLinkStatus(LS_NONE), +m_text(), +m_tempText(), +m_reflector(), +m_tempReflector(), +m_hasTemporary(false), +m_timer(1000U, REPLY_TIME), +m_data(NULL), +m_in(0U), +m_out(0U), +m_seqNo(0U), +m_time() +{ + wxASSERT(handler != NULL); + + m_data = new CAMBEData*[MAX_FRAMES]; + + for (unsigned int i = 0U; i < MAX_FRAMES; i++) + m_data[i] = NULL; +} + +CAudioUnit::~CAudioUnit() +{ + delete[] m_data; +} + +void CAudioUnit::sendStatus() +{ + if (m_ambe == NULL) + return; + + if (m_status != AS_IDLE) + return; + + m_status = AS_WAIT; + m_timer.start(); +} + +void CAudioUnit::setStatus(LINK_STATUS status, const wxString& reflector, const wxString& text) +{ + m_linkStatus = status; + m_reflector = reflector; + m_text = text; +} + +void CAudioUnit::setTempStatus(LINK_STATUS status, const wxString& reflector, const wxString& text) +{ + m_tempLinkStatus = status; + m_tempReflector = reflector; + m_tempText = text; + m_hasTemporary = true; +} + +void CAudioUnit::clock(unsigned int ms) +{ + m_timer.clock(ms); + + if (m_status == AS_WAIT && m_timer.hasExpired()) { + if (m_hasTemporary) { + sendStatus(m_tempLinkStatus, m_tempReflector, m_tempText); + m_hasTemporary = false; + } else { + sendStatus(m_linkStatus, m_reflector, m_text); + } + + m_timer.stop(); + + m_out = 0U; + m_seqNo = 0U; + m_status = AS_TRANSMIT; + + m_time.Start(); + + return; + } + + if (m_status == AS_TRANSMIT) { + unsigned int needed = m_time.Time() / DSTAR_FRAME_TIME_MS; + + while (m_out < needed) { + CAMBEData* data = m_data[m_out]; + m_data[m_out] = NULL; + m_out++; + + if (m_in == m_out) + data->setEnd(true); + + m_handler->process(*data, DIR_INCOMING, AS_INFO); + + delete data; + + if (m_in == m_out) { + m_in = 0U; + m_out = 0U; + m_status = AS_IDLE; + m_timer.stop(); + return; + } + } + + return; + } +} + +void CAudioUnit::cancel() +{ + for (unsigned int i = 0U; i < MAX_FRAMES; i++) { + if (m_data[i] != NULL) { + delete m_data[i]; + m_data[i] = NULL; + } + } + + m_status = AS_IDLE; + m_out = 0U; + m_in = 0U; + + m_timer.stop(); +} + +bool CAudioUnit::lookup(unsigned int id, const wxString &name) +{ + CIndexRecord* info = m_index[name]; + if (info == NULL) { + // wxLogError(wxT("Cannot find the AMBE index for *%s*"), name.c_str()); + return false; + } + + unsigned int start = info->getStart(); + unsigned int length = info->getLength(); + + for (unsigned int i = 0U; i < length; i++) { + unsigned char* dataIn = m_ambe + (start + i) * VOICE_FRAME_LENGTH_BYTES; + + CAMBEData* dataOut = new CAMBEData; + dataOut->setSeq(m_seqNo); + dataOut->setId(id); + + unsigned char buffer[DV_FRAME_LENGTH_BYTES]; + ::memcpy(buffer + 0U, dataIn, VOICE_FRAME_LENGTH_BYTES); + + // Insert sync bytes when the sequence number is zero, slow data otherwise + if (m_seqNo == 0U) { + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); + m_encoder.sync(); + } else { + m_encoder.getTextData(buffer + VOICE_FRAME_LENGTH_BYTES); + } + + dataOut->setData(buffer, DV_FRAME_LENGTH_BYTES); + + m_seqNo++; + if (m_seqNo == 21U) + m_seqNo = 0U; + + m_data[m_in] = dataOut; + m_in++; + } + + return true; +} + +void CAudioUnit::spellReflector(unsigned int id, const wxString &reflector) +{ + unsigned int length = reflector.Len(); + + for (unsigned int i = 0U; i < (length - 1U); i++) { + wxString c = reflector.Mid(i, 1U); + + if (!c.IsSameAs(wxT(" "))) + lookup(id, c); + } + + wxChar c = reflector.GetChar(length - 1U); + + if (c == wxT(' ')) + return; + + 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; + } + + switch (c) { + case wxT('A'): + lookup(id, wxT("alpha")); + break; + case wxT('B'): + lookup(id, wxT("bravo")); + break; + case wxT('C'): + lookup(id, wxT("charlie")); + break; + case wxT('D'): + lookup(id, wxT("delta")); + break; + default: + lookup(id, wxString(c)); + break; + } +} + +bool CAudioUnit::readAMBE(const wxString& name) +{ + wxFileName fileName(wxFileName::GetHomeDir(), name); + + if (!fileName.IsFileReadable()) { + wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str()); +#if defined(__WINDOWS__) + fileName.Assign(::wxGetCwd(), name); +#else + fileName.Assign(wxT(DATA_DIR), name); +#endif + if (!fileName.IsFileReadable()) { + wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str()); + return false; + } + } + + wxFFile file; + + bool ret = file.Open(fileName.GetFullPath().c_str(), wxT("rb")); + if (!ret) { + wxLogMessage(wxT("Cannot open %s for reading"), fileName.GetFullPath().c_str()); + return false; + } + + wxLogMessage(wxT("Reading %s"), fileName.GetFullPath().c_str()); + + unsigned char buffer[VOICE_FRAME_LENGTH_BYTES]; + + size_t n = file.Read(buffer, 4U); + if (n != 4U) { + wxLogMessage(wxT("Unable to read the header from %s"), fileName.GetFullPath().c_str()); + file.Close(); + return false; + } + + if (::memcmp(buffer, "AMBE", 4U) != 0) { + wxLogMessage(wxT("Invalid header from %s"), fileName.GetFullPath().c_str()); + file.Close(); + return false; + } + + // Length of the file minus the header + unsigned int length = file.Length() - 4U; + + // Hold the file data plus silence at the end + m_ambe = new unsigned char[length + SILENCE_LENGTH * VOICE_FRAME_LENGTH_BYTES]; + m_ambeLength = length / VOICE_FRAME_LENGTH_BYTES; + + // Add silence to the beginning of the buffer + unsigned char* p = m_ambe; + for (unsigned int i = 0U; i < SILENCE_LENGTH; i++, p += VOICE_FRAME_LENGTH_BYTES) + ::memcpy(p, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + + n = file.Read(p, length); + if (n != length) { + wxLogMessage(wxT("Unable to read the AMBE data from %s"), fileName.GetFullPath().c_str()); + file.Close(); + delete[] m_ambe; + m_ambe = NULL; + return false; + } + + file.Close(); + + return true; +} + +bool CAudioUnit::readIndex(const wxString& name) +{ + wxFileName fileName(wxFileName::GetHomeDir(), name); + + if (!fileName.IsFileReadable()) { + wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str()); +#if defined(__WINDOWS__) + fileName.Assign(::wxGetCwd(), name); +#else + fileName.Assign(wxT(DATA_DIR), name); +#endif + if (!fileName.IsFileReadable()) { + wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str()); + return false; + } + } + + wxTextFile file; + + bool ret = file.Open(fileName.GetFullPath()); + if (!ret) { + wxLogMessage(wxT("Cannot open %s for reading"), fileName.GetFullPath().c_str()); + return false; + } + + // Add a silence entry at the beginning + m_index[wxT(" ")] = new CIndexRecord(wxT(" "), 0U, SILENCE_LENGTH); + + wxLogMessage(wxT("Reading %s"), fileName.GetFullPath().c_str()); + + unsigned int nLines = file.GetLineCount(); + + for (unsigned int i = 0; i < nLines; i++) { + wxString line = file.GetLine(i); + + if (line.length() > 0 && line.GetChar(0) != wxT('#')) { + wxStringTokenizer t(line, wxT(" \t\r\n"), wxTOKEN_STRTOK); + wxString name = t.GetNextToken(); + wxString startTxt = t.GetNextToken(); + wxString lengthTxt = t.GetNextToken(); + + if (!name.IsEmpty() && !startTxt.IsEmpty() && !lengthTxt.IsEmpty()) { + unsigned long start; + startTxt.ToULong(&start); + + unsigned long length; + lengthTxt.ToULong(&length); + + if (start >= m_ambeLength || (start + length) >= m_ambeLength) + wxLogError(wxT("The start or end for *%s* is out of range, start: %lu, end: %lu"), name.c_str(), start, start + length); + else + m_index[name] = new CIndexRecord(name, start + SILENCE_LENGTH, length); + } + } + } + + file.Close(); + + return true; +} + +void CAudioUnit::sendStatus(LINK_STATUS status, const wxString& reflector, const wxString &text) +{ + m_encoder.setTextData(text); + + // Create the message + unsigned int id = CHeaderData::createId(); + + lookup(id, wxT(" ")); + lookup(id, wxT(" ")); + lookup(id, wxT(" ")); + lookup(id, wxT(" ")); + + bool found; + + switch (status) { + case LS_NONE: + lookup(id, wxT("notlinked")); + break; + case LS_LINKED_CCS: + case LS_LINKED_DCS: + case LS_LINKED_DPLUS: + case LS_LINKED_DEXTRA: + case LS_LINKED_LOOPBACK: + found = lookup(id, wxT("linkedto")); + if (!found) { + lookup(id, wxT("linked")); + lookup(id, wxT("2")); + } + spellReflector(id, reflector); + break; + default: + found = lookup(id, wxT("linkingto")); + if (!found) { + lookup(id, wxT("linking")); + lookup(id, wxT("2")); + } + spellReflector(id, reflector); + break; + } + + lookup(id, wxT(" ")); + lookup(id, wxT(" ")); + lookup(id, wxT(" ")); + lookup(id, wxT(" ")); + + // RPT1 and RPT2 will be filled in later + CHeaderData header; + header.setMyCall1(m_callsign); + header.setMyCall2(wxT("INFO")); + header.setYourCall(wxT("CQCQCQ ")); + header.setId(id); + + m_handler->process(header, DIR_INCOMING, AS_INFO); +} diff --git a/Common/AudioUnit.h b/Common/AudioUnit.h new file mode 100644 index 0000000..a910655 --- /dev/null +++ b/Common/AudioUnit.h @@ -0,0 +1,120 @@ +/* + * 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 + * 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 AudioUnit_H +#define AudioUnit_H + +#include "RepeaterCallback.h" +#include "SlowDataEncoder.h" +#include "AMBEData.h" +#include "Timer.h" +#include "Defs.h" + +#include + +class CIndexRecord { +public: + CIndexRecord(const wxString& name, unsigned int start, unsigned int length) : + m_name(name), + m_start(start), + m_length(length) + { + } + + wxString getName() const + { + return m_name; + } + + unsigned int getStart() const + { + return m_start; + } + + unsigned int getLength() const + { + return m_length; + } + +private: + wxString m_name; + unsigned int m_start; + unsigned int m_length; +}; + +WX_DECLARE_STRING_HASH_MAP(CIndexRecord*, CIndexList_t); + +enum AUDIO_STATUS { + AS_IDLE, + AS_WAIT, + AS_TRANSMIT +}; + +class CAudioUnit { +public: + CAudioUnit(IRepeaterCallback* handler, const wxString& callsign); + ~CAudioUnit(); + + void sendStatus(); + + void setStatus(LINK_STATUS status, const wxString& reflector, const wxString& text); + void setTempStatus(LINK_STATUS status, const wxString& reflector, const wxString& text); + + void cancel(); + + void clock(unsigned int ms); + + static void initialise(); + + static void setLanguage(TEXT_LANG language); + + static void finalise(); + +private: + static unsigned char* m_ambe; + static unsigned int m_ambeLength; + static CIndexList_t m_index; + static TEXT_LANG m_language; + + IRepeaterCallback* m_handler; + wxString m_callsign; + CSlowDataEncoder m_encoder; + AUDIO_STATUS m_status; + LINK_STATUS m_linkStatus; + LINK_STATUS m_tempLinkStatus; + wxString m_text; + wxString m_tempText; + wxString m_reflector; + wxString m_tempReflector; + bool m_hasTemporary; + CTimer m_timer; + CAMBEData** m_data; + unsigned int m_in; + unsigned int m_out; + unsigned int m_seqNo; + wxStopWatch m_time; + + bool lookup(unsigned int id, const wxString& name); + void spellReflector(unsigned int id, const wxString& reflector); + void sendStatus(LINK_STATUS status, const wxString& reflector, const wxString& text); + + static bool readAMBE(const wxString& name); + static bool readIndex(const wxString& name); +}; + +#endif diff --git a/Common/CCITTChecksum.cpp b/Common/CCITTChecksum.cpp new file mode 100644 index 0000000..e76b733 --- /dev/null +++ b/Common/CCITTChecksum.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2009 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; version 2 of the License. + * + * 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. + */ + +#include "CCITTChecksum.h" + +#include "Utils.h" + +static const unsigned short ccittTab[] = { + 0x0000,0x1189,0x2312,0x329b,0x4624,0x57ad,0x6536,0x74bf, + 0x8c48,0x9dc1,0xaf5a,0xbed3,0xca6c,0xdbe5,0xe97e,0xf8f7, + 0x1081,0x0108,0x3393,0x221a,0x56a5,0x472c,0x75b7,0x643e, + 0x9cc9,0x8d40,0xbfdb,0xae52,0xdaed,0xcb64,0xf9ff,0xe876, + 0x2102,0x308b,0x0210,0x1399,0x6726,0x76af,0x4434,0x55bd, + 0xad4a,0xbcc3,0x8e58,0x9fd1,0xeb6e,0xfae7,0xc87c,0xd9f5, + 0x3183,0x200a,0x1291,0x0318,0x77a7,0x662e,0x54b5,0x453c, + 0xbdcb,0xac42,0x9ed9,0x8f50,0xfbef,0xea66,0xd8fd,0xc974, + 0x4204,0x538d,0x6116,0x709f,0x0420,0x15a9,0x2732,0x36bb, + 0xce4c,0xdfc5,0xed5e,0xfcd7,0x8868,0x99e1,0xab7a,0xbaf3, + 0x5285,0x430c,0x7197,0x601e,0x14a1,0x0528,0x37b3,0x263a, + 0xdecd,0xcf44,0xfddf,0xec56,0x98e9,0x8960,0xbbfb,0xaa72, + 0x6306,0x728f,0x4014,0x519d,0x2522,0x34ab,0x0630,0x17b9, + 0xef4e,0xfec7,0xcc5c,0xddd5,0xa96a,0xb8e3,0x8a78,0x9bf1, + 0x7387,0x620e,0x5095,0x411c,0x35a3,0x242a,0x16b1,0x0738, + 0xffcf,0xee46,0xdcdd,0xcd54,0xb9eb,0xa862,0x9af9,0x8b70, + 0x8408,0x9581,0xa71a,0xb693,0xc22c,0xd3a5,0xe13e,0xf0b7, + 0x0840,0x19c9,0x2b52,0x3adb,0x4e64,0x5fed,0x6d76,0x7cff, + 0x9489,0x8500,0xb79b,0xa612,0xd2ad,0xc324,0xf1bf,0xe036, + 0x18c1,0x0948,0x3bd3,0x2a5a,0x5ee5,0x4f6c,0x7df7,0x6c7e, + 0xa50a,0xb483,0x8618,0x9791,0xe32e,0xf2a7,0xc03c,0xd1b5, + 0x2942,0x38cb,0x0a50,0x1bd9,0x6f66,0x7eef,0x4c74,0x5dfd, + 0xb58b,0xa402,0x9699,0x8710,0xf3af,0xe226,0xd0bd,0xc134, + 0x39c3,0x284a,0x1ad1,0x0b58,0x7fe7,0x6e6e,0x5cf5,0x4d7c, + 0xc60c,0xd785,0xe51e,0xf497,0x8028,0x91a1,0xa33a,0xb2b3, + 0x4a44,0x5bcd,0x6956,0x78df,0x0c60,0x1de9,0x2f72,0x3efb, + 0xd68d,0xc704,0xf59f,0xe416,0x90a9,0x8120,0xb3bb,0xa232, + 0x5ac5,0x4b4c,0x79d7,0x685e,0x1ce1,0x0d68,0x3ff3,0x2e7a, + 0xe70e,0xf687,0xc41c,0xd595,0xa12a,0xb0a3,0x8238,0x93b1, + 0x6b46,0x7acf,0x4854,0x59dd,0x2d62,0x3ceb,0x0e70,0x1ff9, + 0xf78f,0xe606,0xd49d,0xc514,0xb1ab,0xa022,0x92b9,0x8330, + 0x7bc7,0x6a4e,0x58d5,0x495c,0x3de3,0x2c6a,0x1ef1,0x0f78}; + + +CCCITTChecksum::CCCITTChecksum() : +m_crc(0xFFFF) +{ +} + +CCCITTChecksum::~CCCITTChecksum() +{ +} + +void CCCITTChecksum::update(const unsigned char* data, unsigned int length) +{ + wxASSERT(data != NULL); + + for (unsigned int i = 0U; i < length; i++) { + unsigned short byte = data[i]; + + unsigned short tmp = (m_crc & 0x00FF) ^ byte; + + m_crc = (m_crc >> 8) ^ ccittTab[tmp]; + } +} + +void CCCITTChecksum::update(const bool* data) +{ + wxASSERT(data != NULL); + + unsigned short byte = CUtils::bitsToByte(data); + + unsigned short tmp = (m_crc & 0x00FF) ^ byte; + + m_crc = (m_crc >> 8) ^ ccittTab[tmp]; +} + +void CCCITTChecksum::result(unsigned char* data) // XX FIXME +{ + wxASSERT(data != NULL); + + m_crc = ~m_crc; + + unsigned short tmp = m_crc; + + m_crc = (m_crc << 8) | (tmp >> 8 & 0xFF); + + data[0] = (m_crc >> 8) & 0xFF; + data[1] = (m_crc >> 0) & 0xFF; +} + +void CCCITTChecksum::result(bool* data) +{ + wxASSERT(data != NULL); + + m_crc = ~m_crc; + + unsigned short tmp = m_crc; + + m_crc = (m_crc << 8) | (tmp >> 8 & 0xFF); + + unsigned short mask = 0x8000; + for (unsigned int i = 0U; i < 16U; i++, mask >>= 1) + data[i] = (m_crc & mask) ? true : false; +} + +bool CCCITTChecksum::check(const unsigned char* data) +{ + wxASSERT(data != NULL); + + unsigned char sum[2]; + result(sum); + + return sum[0] == data[0] && sum[1] == data[1]; +} + +bool CCCITTChecksum::check(const bool* data) +{ + wxASSERT(data != NULL); + + bool sum[16]; + result(sum); + + for (unsigned int i = 0U; i < 16U; i++) + if (sum[i] != data[i]) + return false; + + return true; +} + +void CCCITTChecksum::reset() +{ + m_crc = 0xFFFF; +} diff --git a/Common/CCITTChecksum.h b/Common/CCITTChecksum.h new file mode 100644 index 0000000..e3b9101 --- /dev/null +++ b/Common/CCITTChecksum.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2009,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; version 2 of the License. + * + * 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. + */ + +#ifndef CCITTChecksum_H +#define CCITTChecksum_H + +#include + +class CCCITTChecksum { +public: + CCCITTChecksum(); + ~CCCITTChecksum(); + + void update(const unsigned char* data, unsigned int length); + void update(const bool* data); + + void result(unsigned char* data); + void result(bool* data); + + bool check(const unsigned char* data); + bool check(const bool* data); + + void reset(); + +private: + wxUint16 m_crc; +}; + +#endif 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/CacheManager.cpp b/Common/CacheManager.cpp new file mode 100644 index 0000000..7915d1b --- /dev/null +++ b/Common/CacheManager.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2010,2011,2012 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 "CacheManager.h" +#include "DStarDefines.h" + +CCacheManager::CCacheManager() : +m_mutex(), +m_userCache(), +m_gatewayCache(), +m_repeaterCache() +{ +} + +CCacheManager::~CCacheManager() +{ +} + +CUserData* CCacheManager::findUser(const wxString& user) +{ + wxMutexLocker locker(m_mutex); + + CUserRecord* ur = m_userCache.find(user); + if (ur == NULL) + return NULL; + + CRepeaterRecord* rr = m_repeaterCache.find(ur->getRepeater()); + wxString gateway; + if (rr == NULL) { + gateway = ur->getRepeater(); + gateway.Append(wxT(" ")); + gateway.Truncate(LONG_CALLSIGN_LENGTH - 1U); + gateway.Append(wxT("G")); + } else { + gateway = rr->getGateway(); + } + + CGatewayRecord* gr = m_gatewayCache.find(gateway); + if (gr == NULL) + return NULL; + + return new CUserData(user, ur->getRepeater(), gr->getGateway(), gr->getAddress()); +} + +CGatewayData* CCacheManager::findGateway(const wxString& gateway) +{ + wxMutexLocker locker(m_mutex); + + CGatewayRecord* gr = m_gatewayCache.find(gateway); + if (gr == NULL) + return NULL; + + return new CGatewayData(gateway, gr->getAddress(), gr->getProtocol()); +} + +CRepeaterData* CCacheManager::findRepeater(const wxString& repeater) +{ + wxMutexLocker locker(m_mutex); + + CRepeaterRecord* rr = m_repeaterCache.find(repeater); + wxString gateway; + if (rr == NULL) { + gateway = repeater; + gateway.Append(wxT(" ")); + gateway.Truncate(LONG_CALLSIGN_LENGTH - 1U); + gateway.Append(wxT("G")); + } else { + gateway = rr->getGateway(); + } + + CGatewayRecord* gr = m_gatewayCache.find(gateway); + if (gr == NULL) + return NULL; + + return new CRepeaterData(repeater, gr->getGateway(), gr->getAddress(), gr->getProtocol()); +} + +void CCacheManager::updateUser(const wxString& user, const wxString& repeater, const wxString& gateway, const wxString& address, const wxString& timestamp, DSTAR_PROTOCOL protocol, bool addrLock, bool protoLock) +{ + wxMutexLocker locker(m_mutex); + + wxString repeater7 = repeater.Left(LONG_CALLSIGN_LENGTH - 1U); + wxString gateway7 = gateway.Left(LONG_CALLSIGN_LENGTH - 1U); + + m_userCache.update(user, repeater, timestamp); + + // Only store non-standard repeater-gateway pairs + if (!repeater7.IsSameAs(gateway7)) + m_repeaterCache.update(repeater, gateway); + + m_gatewayCache.update(gateway, address, protocol, addrLock, protoLock); +} + +void CCacheManager::updateRepeater(const wxString& repeater, const wxString& gateway, const wxString& address, DSTAR_PROTOCOL protocol, bool addrLock, bool protoLock) +{ + wxMutexLocker locker(m_mutex); + + wxString repeater7 = repeater.Left(LONG_CALLSIGN_LENGTH - 1U); + wxString gateway7 = gateway.Left(LONG_CALLSIGN_LENGTH - 1U); + + // Only store non-standard repeater-gateway pairs + if (!repeater7.IsSameAs(gateway7)) + m_repeaterCache.update(repeater, gateway); + + m_gatewayCache.update(gateway, address, protocol, addrLock, protoLock); +} + +void CCacheManager::updateGateway(const wxString& gateway, const wxString& address, DSTAR_PROTOCOL protocol, bool addrLock, bool protoLock) +{ + wxMutexLocker locker(m_mutex); + + m_gatewayCache.update(gateway, address, protocol, addrLock, protoLock); +} diff --git a/Common/CacheManager.h b/Common/CacheManager.h new file mode 100644 index 0000000..e8d669b --- /dev/null +++ b/Common/CacheManager.h @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2010,2011,2012 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 CacheManager_H +#define CacheManager_H + +#include + +#if defined(__WINDOWS__) +#include "Inaddr.h" +#else +#include +#endif + +#include "RepeaterCache.h" +#include "GatewayCache.h" +#include "UserCache.h" + +class CUserData { +public: + CUserData(const wxString& user, const wxString& repeater, const wxString& gateway, in_addr address) : + m_user(user), + m_repeater(repeater), + m_gateway(gateway), + m_address(address) + { + } + + wxString getUser() const + { + return m_user; + } + + wxString getRepeater() const + { + return m_repeater; + } + + wxString getGateway() const + { + return m_gateway; + } + + in_addr getAddress() const + { + return m_address; + } + +private: + wxString m_user; + wxString m_repeater; + wxString m_gateway; + in_addr m_address; +}; + +class CRepeaterData { +public: + CRepeaterData(const wxString& repeater, const wxString& gateway, in_addr address, DSTAR_PROTOCOL protocol) : + m_repeater(repeater), + m_gateway(gateway), + m_address(address), + m_protocol(protocol) + { + } + + wxString getRepeater() const + { + return m_repeater; + } + + wxString getGateway() const + { + return m_gateway; + } + + in_addr getAddress() const + { + return m_address; + } + + DSTAR_PROTOCOL getProtocol() const + { + return m_protocol; + } + +private: + wxString m_repeater; + wxString m_gateway; + in_addr m_address; + DSTAR_PROTOCOL m_protocol; +}; + +class CGatewayData { +public: + CGatewayData(const wxString& gateway, in_addr address, DSTAR_PROTOCOL protocol) : + m_gateway(gateway), + m_address(address), + m_protocol(protocol) + { + } + + wxString getGateway() const + { + return m_gateway; + } + + in_addr getAddress() const + { + return m_address; + } + + DSTAR_PROTOCOL getProtocol() const + { + return m_protocol; + } + +private: + wxString m_gateway; + in_addr m_address; + DSTAR_PROTOCOL m_protocol; +}; + +class CCacheManager { +public: + CCacheManager(); + ~CCacheManager(); + + CUserData* findUser(const wxString& user); + CGatewayData* findGateway(const wxString& gateway); + CRepeaterData* findRepeater(const wxString& repeater); + + void updateUser(const wxString& user, const wxString& repeater, const wxString& gateway, const wxString& address, const wxString& timeStamp, DSTAR_PROTOCOL protocol, bool addrLock, bool protoLock); + void updateRepeater(const wxString& repeater, const wxString& gateway, const wxString& address, DSTAR_PROTOCOL protocol, bool addrLock, bool protoLock); + void updateGateway(const wxString& gateway, const wxString& address, DSTAR_PROTOCOL protocol, bool addrLock, bool protoLock); + +private: + wxMutex m_mutex; + CUserCache m_userCache; + CGatewayCache m_gatewayCache; + CRepeaterCache m_repeaterCache; +}; + +#endif diff --git a/Common/CallsignList.cpp b/Common/CallsignList.cpp new file mode 100644 index 0000000..f55a1f5 --- /dev/null +++ b/Common/CallsignList.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2011 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 "CallsignList.h" +#include "DStarDefines.h" + +#include + +CCallsignList::CCallsignList(const wxString& filename) : +m_filename(filename), +m_callsigns() +{ +} + + +CCallsignList::~CCallsignList() +{ +} + +bool CCallsignList::load() +{ + wxTextFile file; + + bool res = file.Open(m_filename); + if (!res) + return false; + + unsigned int lines = file.GetLineCount(); + if (lines == 0U) { + file.Close(); + return true; + } + + m_callsigns.Alloc(lines); + + wxString callsign = file.GetFirstLine(); + + while (!file.Eof()) { + callsign.MakeUpper(); + callsign.Append(wxT(" ")); + callsign.Truncate(LONG_CALLSIGN_LENGTH); + + m_callsigns.Add(callsign); + + callsign = file.GetNextLine(); + } + + file.Close(); + + return true; +} + +unsigned int CCallsignList::getCount() const +{ + return m_callsigns.GetCount(); +} + +bool CCallsignList::isInList(const wxString& callsign) const +{ + return m_callsigns.Index(callsign) != wxNOT_FOUND; +} diff --git a/Common/CallsignList.h b/Common/CallsignList.h new file mode 100644 index 0000000..2a8d06c --- /dev/null +++ b/Common/CallsignList.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2011,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 CallsignList_H +#define CallsignList_H + +#include + +class CCallsignList { +public: + CCallsignList(const wxString& filename); + ~CCallsignList(); + + bool load(); + + unsigned int getCount() const; + + bool isInList(const wxString& callsign) const; + +private: + wxString m_filename; + wxArrayString m_callsigns; +}; + +#endif diff --git a/Common/CallsignServer.cpp b/Common/CallsignServer.cpp new file mode 100644 index 0000000..94e59d7 --- /dev/null +++ b/Common/CallsignServer.cpp @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2012,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 "TCPReaderWriterClient.h" +#include "CallsignServer.h" +#include "DStarDefines.h" +#include "Utils.h" +#include "Defs.h" + +const wxString CALLSERVER_HOSTNAME = wxT("dns.xreflector.net"); +const unsigned int CALLSERVER_PORT = 20001U; + +const unsigned int TCP_TIMEOUT = 10U; + +CCallsignServer::CCallsignServer(const wxString& callsign, const wxString& address, CCacheManager* cache) : +wxThread(wxTHREAD_JOINABLE), +m_callsign(callsign), +m_address(address), +m_cache(cache), +m_timer(1U, 1U * 3600U), // 1 hour +m_killed(false) +{ + wxASSERT(!callsign.IsEmpty()); + wxASSERT(cache != NULL); +} + +CCallsignServer::~CCallsignServer() +{ +} + +void CCallsignServer::start() +{ + process(CALLSERVER_HOSTNAME, CALLSERVER_PORT); + + Create(); + Run(); +} + +void* CCallsignServer::Entry() +{ + wxLogMessage(wxT("Starting the Callsign Server thread")); + + m_timer.start(); + + try { + while (!m_killed) { + if (m_timer.hasExpired()) { + process(CALLSERVER_HOSTNAME, CALLSERVER_PORT); + m_timer.start(); + } + + Sleep(1000UL); + + m_timer.clock(); + } + } + catch (std::exception& e) { + wxString message(e.what(), wxConvLocal); + wxLogError(wxT("Exception raised in the Callsign Server thread - \"%s\""), message.c_str()); + } + catch (...) { + wxLogError(wxT("Unknown exception raised in the Callsign Server thread")); + } + + wxLogMessage(wxT("Stopping the Callsign Server thread")); + + return NULL; +} + +void CCallsignServer::stop() +{ + m_killed = true; + + Wait(); +} + +void CCallsignServer::process(const wxString& hostname, unsigned int port) +{ + CTCPReaderWriterClient socket(hostname, port, m_address); + + bool ret = socket.open(); + if (!ret) { + wxLogMessage(wxT("Cannot connect to %s"), hostname.c_str()); + return; + } + + // Space for 5000 entries + unsigned int length = 5000U * (6U + 1U + 15U + 1U); + + unsigned char* buffer = new unsigned char[length + 1U]; + ::memset(buffer, ' ', 29U); + + for (unsigned int i = 0U; i < m_callsign.Len() && i < LONG_CALLSIGN_LENGTH - 1U; i++) + buffer[i + 0U] = m_callsign.GetChar(i); + + ::memcpy(buffer + 9U, "ircDDB Gateway", 14U); + + socket.write(buffer, 29U); + + unsigned int offset = 0U; + + int n = socket.read(buffer, length, TCP_TIMEOUT); + if (n >= 0) + offset += n; + + while (n >= 0 && offset < length) { + n = socket.read(buffer + offset, length - offset, TCP_TIMEOUT); + if (n == 0) + Sleep(TCP_TIMEOUT * 1000UL); + else if (n > 0) + offset += n; + } + + buffer[offset] = 0x00U; + + unsigned int count = 0U; + + char* p = (char*)buffer; + + for (;;) { + // Split into lines + char* p1 = ::strchr(p, 0x0A); + if (p1 != NULL) + *p1 = 0x00; + + if (::strncmp(p, "DCS", 3U) == 0) { + char* p2 = ::strtok(p, " \t\r\n"); + char* p3 = ::strtok(NULL, " \t\r\n"); + + if (p2 != NULL && p3 != NULL) { + wxString name = wxString(p2, wxConvLocal); + wxString address = wxString(p3, wxConvLocal); + + if (!address.IsSameAs(wxT("0.0.0.0"))) { + wxLogMessage(wxT("DCS: %s\t%s"), name.c_str(), address.c_str()); + + name.resize(LONG_CALLSIGN_LENGTH - 1U, wxT(' ')); + name.Append(wxT("G")); + m_cache->updateGateway(name, address, DP_DCS, false, true); + + count++; + } + } + } + + if (p1 == NULL) + break; + + p = p1 + 1U; + } + + wxLogMessage(wxT("Registered with %s using callsign %s"), hostname.c_str(), m_callsign.Left(LONG_CALLSIGN_LENGTH - 1U).c_str()); + + wxLogMessage(wxT("Loaded %u DCS reflectors from %s"), count, hostname.c_str()); + + delete[] buffer; + socket.close(); +} diff --git a/Common/CallsignServer.h b/Common/CallsignServer.h new file mode 100644 index 0000000..1238a44 --- /dev/null +++ b/Common/CallsignServer.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2012,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. + */ + +#ifndef CallsignServer_H +#define CallsignServer_H + +#include "CacheManager.h" +#include "Timer.h" + +#include + +#if defined(__WINDOWS__) +#include "Inaddr.h" +#else +#include +#endif + +class CCallsignServer : public wxThread { +public: + CCallsignServer(const wxString& callsign, const wxString& address, CCacheManager* cache); + virtual ~CCallsignServer(); + + virtual void start(); + + virtual void* Entry(); + + virtual void stop(); + +private: + wxString m_callsign; + wxString m_address; + CCacheManager* m_cache; + CTimer m_timer; + bool m_killed; + + void process(const wxString& hostname, unsigned int port); +}; + +#endif diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj new file mode 100644 index 0000000..bdbcfc3 --- /dev/null +++ b/Common/Common.vcxproj @@ -0,0 +1,295 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {E793CB8E-2AC9-431A-BBFC-3F52537BB3CF} + Common + Win32Proj + 10.0.16299.0 + + + + StaticLibrary + v141 + Unicode + true + + + StaticLibrary + v141 + Unicode + true + + + StaticLibrary + v141 + Unicode + + + StaticLibrary + v141 + Unicode + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>14.0.24720.0 + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + $(WXWIN)\include;$(IncludePath);$(WXWIN)\lib\vc_dll\mswud + + + $(WXWIN)\include;$(IncludePath);$(WXWIN)\lib\vc_dll\mswud + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + + + + + + + Disabled + $(WXWIN)\include\msvc;$(WXWIN)\include;../ircDDB;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_LIB;DCS_LINK;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + Level4 + EditAndContinue + + + + + + + + + Disabled + $(WXWIN)\include\msvc;$(WXWIN)\include;../ircDDB;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_LIB;DCS_LINK;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + + + Level4 + ProgramDatabase + + + + + + + + MaxSpeed + true + $(WXWIN)\include\msvc;$(WXWIN)\include;../ircDDB;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_LIB;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + Level3 + ProgramDatabase + + + + + + + + + MaxSpeed + true + $(WXWIN)\include\msvc;$(WXWIN)\include;../ircDDB;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_LIB;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters new file mode 100644 index 0000000..8365240 --- /dev/null +++ b/Common/Common.vcxproj.filters @@ -0,0 +1,437 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/Common/ConnectData.cpp b/Common/ConnectData.cpp new file mode 100644 index 0000000..2489b73 --- /dev/null +++ b/Common/ConnectData.cpp @@ -0,0 +1,543 @@ +/* + * 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 + * 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 "ConnectData.h" + +#include "DStarDefines.h" +#include "Version.h" +#include "Utils.h" + +const wxChar* HTML = wxT("
%s ircDDB Gateway %s
"); + +CConnectData::CConnectData(GATEWAY_TYPE gatewayType, const wxString& repeater, const wxString& reflector, CD_TYPE type, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) : +m_gatewayType(gatewayType), +m_repeater(repeater), +m_reflector(reflector), +m_type(type), +m_locator(), +m_yourAddress(yourAddress), +m_yourPort(yourPort), +m_myPort(myPort) +{ + wxASSERT(yourPort > 0U); + wxASSERT(!repeater.IsEmpty()); + wxASSERT(!reflector.IsEmpty()); +} + +CConnectData::CConnectData(const wxString& repeater, const wxString& reflector, CD_TYPE type, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) : +m_gatewayType(GT_REPEATER), +m_repeater(repeater), +m_reflector(reflector), +m_type(type), +m_locator(), +m_yourAddress(yourAddress), +m_yourPort(yourPort), +m_myPort(myPort) +{ + wxASSERT(yourPort > 0U); + wxASSERT(!repeater.IsEmpty()); + wxASSERT(!reflector.IsEmpty()); +} + +CConnectData::CConnectData(const wxString& repeater, CD_TYPE type, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) : +m_gatewayType(GT_REPEATER), +m_repeater(repeater), +m_reflector(), +m_type(type), +m_locator(), +m_yourAddress(yourAddress), +m_yourPort(yourPort), +m_myPort(myPort) +{ + wxASSERT(yourPort > 0U); + wxASSERT(!repeater.IsEmpty()); +} + +CConnectData::CConnectData(const wxString& repeater, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) : +m_gatewayType(GT_REPEATER), +m_repeater(repeater), +m_reflector(), +m_type(CT_UNLINK), +m_locator(), +m_yourAddress(yourAddress), +m_yourPort(yourPort), +m_myPort(myPort) +{ + wxASSERT(yourPort > 0U); + wxASSERT(!repeater.IsEmpty()); +} + +CConnectData::CConnectData(CD_TYPE type, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) : +m_gatewayType(GT_REPEATER), +m_repeater(), +m_reflector(), +m_type(type), +m_locator(), +m_yourAddress(yourAddress), +m_yourPort(yourPort), +m_myPort(myPort) +{ + wxASSERT(yourPort > 0U); +} + +CConnectData::CConnectData() : +m_gatewayType(GT_REPEATER), +m_repeater(wxT(" ")), +m_reflector(), +m_type(CT_LINK1), +m_locator(), +m_yourAddress(), +m_yourPort(0U), +m_myPort(0U) +{ +} + +CConnectData::~CConnectData() +{ +} + +bool CConnectData::setDExtraData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) +{ + wxASSERT(data != NULL); + wxASSERT(length >= 11U); + 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]); + + m_reflector = wxT(" "); + m_reflector.SetChar(LONG_CALLSIGN_LENGTH - 1U, data[LONG_CALLSIGN_LENGTH + 1U]); + + switch (length) { + case 11U: + if (m_reflector.IsSameAs(wxT(" "))) + m_type = CT_UNLINK; + else + m_type = CT_LINK1; + break; + + case 14U: + 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; + + break; + + default: + return false; + } + + m_yourAddress = yourAddress; + m_yourPort = yourPort; + m_myPort = myPort; + + return true; +} + +bool CConnectData::setDCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) +{ + wxASSERT(data != NULL); + wxASSERT(length >= 11U); + 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]); + + switch (length) { + case 519U: + m_reflector = wxString((const char*)(data + LONG_CALLSIGN_LENGTH + 3U), wxConvLocal, LONG_CALLSIGN_LENGTH); + m_reflector.SetChar(LONG_CALLSIGN_LENGTH - 1U, data[LONG_CALLSIGN_LENGTH + 1U]); + m_type = CT_LINK1; + break; + + case 19U: + m_reflector = wxString((const char*)(data + LONG_CALLSIGN_LENGTH + 3U), wxConvLocal, LONG_CALLSIGN_LENGTH); + m_reflector.SetChar(LONG_CALLSIGN_LENGTH - 1U, data[LONG_CALLSIGN_LENGTH + 1U]); + m_type = CT_UNLINK; + break; + + case 14U: + 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; + + break; + + default: + return false; + } + + m_yourAddress = yourAddress; + m_yourPort = yourPort; + m_myPort = myPort; + + 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); + wxASSERT(length >= 5U); + wxASSERT(yourPort > 0U); + + switch (length) { + case 5U: + switch (data[4U]) { + case 0x01: + m_type = CT_LINK1; + break; + case 0x00: + m_type = CT_UNLINK; + break; + } + break; + + case 8U: { + wxString reply((const char*)(data + 4U), wxConvLocal, 4U); + wxLogMessage(wxT("D-Plus reply is %.4s"), reply.c_str()); + + if (::memcmp(data + 4U, "OKRW", 4U) == 0) + m_type = CT_ACK; + else + m_type = CT_NAK; + } + break; + + case 28U: + m_repeater = wxString((const char*)(data + 4U), wxConvLocal); + m_type = CT_LINK2; + break; + + default: + return false; + } + + m_yourAddress = yourAddress; + m_yourPort = yourPort; + m_myPort = myPort; + + return true; +} + +unsigned int CConnectData::getDExtraData(unsigned char *data, unsigned int length) const +{ + wxASSERT(data != NULL); + wxASSERT(length >= 11U); + + ::memset(data, ' ', LONG_CALLSIGN_LENGTH); + + 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[LONG_CALLSIGN_LENGTH + 1U] = m_reflector.GetChar(LONG_CALLSIGN_LENGTH - 1U); + data[LONG_CALLSIGN_LENGTH + 2U] = 0x00; + return 11U; + + case CT_UNLINK: + data[LONG_CALLSIGN_LENGTH + 1U] = ' '; + data[LONG_CALLSIGN_LENGTH + 2U] = 0x00; + return 11U; + + case CT_ACK: + data[LONG_CALLSIGN_LENGTH + 1U] = m_reflector.GetChar(LONG_CALLSIGN_LENGTH - 1U); + data[LONG_CALLSIGN_LENGTH + 2U] = 'A'; + data[LONG_CALLSIGN_LENGTH + 3U] = 'C'; + data[LONG_CALLSIGN_LENGTH + 4U] = 'K'; + data[LONG_CALLSIGN_LENGTH + 5U] = 0x00; + return 14U; + + case CT_NAK: + data[LONG_CALLSIGN_LENGTH + 1U] = m_reflector.GetChar(LONG_CALLSIGN_LENGTH - 1U); + data[LONG_CALLSIGN_LENGTH + 2U] = 'N'; + data[LONG_CALLSIGN_LENGTH + 3U] = 'A'; + data[LONG_CALLSIGN_LENGTH + 4U] = 'K'; + data[LONG_CALLSIGN_LENGTH + 5U] = 0x00; + return 14U; + + default: + return 0U; + } +} + +unsigned int CConnectData::getDCSData(unsigned char *data, unsigned int length) const +{ + wxASSERT(data != NULL); + wxASSERT(length >= 519U); + + ::memset(data, ' ', LONG_CALLSIGN_LENGTH); + + 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[LONG_CALLSIGN_LENGTH + 1U] = m_reflector.GetChar(LONG_CALLSIGN_LENGTH - 1U); + data[LONG_CALLSIGN_LENGTH + 2U] = 0x00U; + ::memset(data + 11U, ' ', LONG_CALLSIGN_LENGTH); + for (unsigned int i = 0U; i < m_reflector.Len() && i < (LONG_CALLSIGN_LENGTH - 1U); i++) + data[i + 11U] = m_reflector.GetChar(i); + + wxString html; + switch (m_gatewayType) { + case GT_HOTSPOT: + html.Printf(HTML, wxT("hotspot.jpg"), wxT("HOTSPOT"), VERSION.c_str()); + break; + case GT_DONGLE: + html.Printf(HTML, wxT("dongle.jpg"), wxT("DONGLE"), VERSION.c_str()); + break; + case GT_STARNET: + html.Printf(HTML, wxT("hf.jpg"), wxT("STARNET"), VERSION.c_str()); + break; + default: + html.Printf(HTML, wxT("hf.jpg"), wxT("REPEATER"), VERSION.c_str()); + break; + } + + ::memset(data + 19U, 0x00U, 500U); + for (unsigned int i = 0U; i < html.Len(); i++) + data[i + 19U] = html.GetChar(i); + } + return 519U; + + case CT_UNLINK: + data[LONG_CALLSIGN_LENGTH + 1U] = 0x20U; + data[LONG_CALLSIGN_LENGTH + 2U] = 0x00U; + ::memset(data + 11U, ' ', LONG_CALLSIGN_LENGTH); + for (unsigned int i = 0U; i < m_reflector.Len() && i < (LONG_CALLSIGN_LENGTH - 1U); i++) + data[i + 11U] = m_reflector.GetChar(i); + return 19U; + + case CT_ACK: + data[LONG_CALLSIGN_LENGTH + 1U] = m_reflector.GetChar(LONG_CALLSIGN_LENGTH - 1U); + data[LONG_CALLSIGN_LENGTH + 2U] = 'A'; + data[LONG_CALLSIGN_LENGTH + 3U] = 'C'; + data[LONG_CALLSIGN_LENGTH + 4U] = 'K'; + data[LONG_CALLSIGN_LENGTH + 5U] = 0x00; + return 14U; + + case CT_NAK: + data[LONG_CALLSIGN_LENGTH + 1U] = 0x20U; + data[LONG_CALLSIGN_LENGTH + 2U] = 'N'; + data[LONG_CALLSIGN_LENGTH + 3U] = 'A'; + data[LONG_CALLSIGN_LENGTH + 4U] = 'K'; + data[LONG_CALLSIGN_LENGTH + 5U] = 0x00; + return 14U; + + default: + return 0U; + } +} + +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); + wxASSERT(length >= 28U); + + switch (m_type) { + case CT_LINK1: + data[0U] = 0x05; + data[1U] = 0x00; + data[2U] = 0x18; + data[3U] = 0x00; + data[4U] = 0x01; + return 5U; + + case CT_LINK2: { + data[0U] = 0x1C; + data[1U] = 0xC0; + data[2U] = 0x04; + data[3U] = 0x00; + + for (unsigned int i = 4U; i < 20U; i++) + data[i] = 0x00; + + wxString callsign = m_repeater; + callsign.Trim(); + + for (unsigned int i = 0U; i < callsign.Len(); i++) + data[i + 4U] = callsign.GetChar(i); + + data[20U] = 'D'; + data[21U] = 'V'; + data[22U] = '0'; + data[23U] = '1'; + data[24U] = '9'; + data[25U] = '9'; + data[26U] = '9'; + data[27U] = '9'; + } + return 28U; + + case CT_UNLINK: + data[0U] = 0x05; + data[1U] = 0x00; + data[2U] = 0x18; + data[3U] = 0x00; + data[4U] = 0x00; + return 5U; + + case CT_ACK: + data[0U] = 0x08; + data[1U] = 0xC0; + data[2U] = 0x04; + data[3U] = 0x00; + data[4U] = 'O'; + data[5U] = 'K'; + data[6U] = 'R'; + data[7U] = 'W'; + return 8U; + + case CT_NAK: + data[0U] = 0x08; + data[1U] = 0xC0; + data[2U] = 0x04; + data[3U] = 0x00; + data[4U] = 'B'; + data[5U] = 'U'; + data[6U] = 'S'; + data[7U] = 'Y'; + return 8U; + + default: + return 0U; + } +} + +in_addr CConnectData::getYourAddress() const +{ + return m_yourAddress; +} + +unsigned int CConnectData::getYourPort() const +{ + return m_yourPort; +} + +unsigned int CConnectData::getMyPort() const +{ + return m_myPort; +} + +wxString CConnectData::getRepeater() const +{ + return m_repeater; +} + +wxString CConnectData::getReflector() const +{ + return m_reflector; +} + +CD_TYPE CConnectData::getType() const +{ + return m_type; +} + +void CConnectData::setLocator(const wxString& locator) +{ + m_locator = locator; +} diff --git a/Common/ConnectData.h b/Common/ConnectData.h new file mode 100644 index 0000000..da6ea78 --- /dev/null +++ b/Common/ConnectData.h @@ -0,0 +1,81 @@ +/* + * 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 + * 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 ConnectData_H +#define ConnectData_H + +#include "Defs.h" + +#include + +#if defined(__WINDOWS__) +#include "Inaddr.h" +#else +#include +#endif + +enum CD_TYPE { + CT_LINK1, + CT_LINK2, + CT_UNLINK, + CT_ACK, + CT_NAK +}; + +class CConnectData { +public: + CConnectData(GATEWAY_TYPE gatewayType, const wxString& repeater, const wxString& reflector, CD_TYPE type, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort = 0U); + CConnectData(const wxString& repeater, const wxString& reflector, CD_TYPE type, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort = 0U); + CConnectData(const wxString& repeater, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort = 0U); + CConnectData(const wxString& repeater, CD_TYPE type, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort = 0U); + CConnectData(CD_TYPE type, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort = 0U); + CConnectData(); + ~CConnectData(); + + 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; + CD_TYPE getType() const; + + in_addr getYourAddress() const; + unsigned int getYourPort() const; + unsigned int getMyPort() const; + + void setLocator(const wxString& locator); + +private: + GATEWAY_TYPE m_gatewayType; + wxString m_repeater; + wxString m_reflector; + CD_TYPE m_type; + wxString m_locator; + in_addr m_yourAddress; + unsigned int m_yourPort; + unsigned int m_myPort; +}; + +#endif diff --git a/Common/DCSHandler.cpp b/Common/DCSHandler.cpp new file mode 100644 index 0000000..85888d2 --- /dev/null +++ b/Common/DCSHandler.cpp @@ -0,0 +1,884 @@ +/* + * Copyright (C) 2012-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 + * 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 "DCSHandler.h" +#include "Utils.h" + +unsigned int CDCSHandler::m_maxReflectors = 0U; +CDCSHandler** CDCSHandler::m_reflectors = NULL; + +CDCSProtocolHandlerPool* CDCSHandler::m_pool = NULL; +CDCSProtocolHandler* CDCSHandler::m_incoming = NULL; + +bool CDCSHandler::m_stateChange = false; + +GATEWAY_TYPE CDCSHandler::m_gatewayType = GT_REPEATER; + +CHeaderLogger* CDCSHandler::m_headerLogger = NULL; + +CCallsignList* CDCSHandler::m_whiteList = NULL; +CCallsignList* CDCSHandler::m_blackList = NULL; + + +CDCSHandler::CDCSHandler(IReflectorCallback* handler, const wxString& reflector, const wxString& repeater, CDCSProtocolHandler* protoHandler, const in_addr& address, unsigned int port, DIRECTION direction) : +m_reflector(reflector.Clone()), +m_repeater(repeater.Clone()), +m_handler(protoHandler), +m_yourAddress(address), +m_yourPort(port), +m_myPort(0U), +m_direction(direction), +m_linkState(DCS_LINKING), +m_destination(handler), +m_time(), +m_pollTimer(1000U, 5U), +m_pollInactivityTimer(1000U, 60U), +m_tryTimer(1000U, 1U), +m_tryCount(0U), +m_dcsId(0x00U), +m_dcsSeq(0x00U), +m_seqNo(0x00U), +m_inactivityTimer(1000U, NETWORK_TIMEOUT), +m_yourCall(), +m_myCall1(), +m_myCall2(), +m_rptCall1(), +m_rptCall2() +{ + wxASSERT(protoHandler != NULL); + wxASSERT(handler != NULL); + wxASSERT(port > 0U); + + m_myPort = protoHandler->getPort(); + + m_pollInactivityTimer.start(); + + m_time = ::time(NULL); + + if (direction == DIR_INCOMING) { + m_pollTimer.start(); + m_stateChange = true; + m_linkState = DCS_LINKED; + } else { + m_linkState = DCS_LINKING; + m_tryTimer.start(); + } +} + +CDCSHandler::~CDCSHandler() +{ + if (m_direction == DIR_OUTGOING) + m_pool->release(m_handler); +} + +void CDCSHandler::initialise(unsigned int maxReflectors) +{ + wxASSERT(maxReflectors > 0U); + + m_maxReflectors = maxReflectors; + + m_reflectors = new CDCSHandler*[m_maxReflectors]; + for (unsigned int i = 0U; i < m_maxReflectors; i++) + m_reflectors[i] = NULL; +} + +void CDCSHandler::setDCSProtocolHandlerPool(CDCSProtocolHandlerPool* pool) +{ + wxASSERT(pool != NULL); + + m_pool = pool; +} + +void CDCSHandler::setDCSProtocolIncoming(CDCSProtocolHandler* handler) +{ + wxASSERT(handler != NULL); + + m_incoming = handler; +} + +void CDCSHandler::setHeaderLogger(CHeaderLogger* logger) +{ + m_headerLogger = logger; +} + +void CDCSHandler::setGatewayType(GATEWAY_TYPE type) +{ + m_gatewayType = type; +} + +void CDCSHandler::setWhiteList(CCallsignList* list) +{ + wxASSERT(list != NULL); + + m_whiteList = list; +} + +void CDCSHandler::setBlackList(CCallsignList* list) +{ + wxASSERT(list != NULL); + + m_blackList = list; +} + +wxString CDCSHandler::getIncoming(const wxString& callsign) +{ + wxString incoming; + + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDCSHandler* reflector = m_reflectors[i]; + if (reflector != NULL && reflector->m_direction == DIR_INCOMING && reflector->m_repeater.IsSameAs(callsign)) { + incoming.Append(reflector->m_reflector); + incoming.Append(wxT(" ")); + } + } + + return incoming; +} + +void CDCSHandler::getInfo(IReflectorCallback* handler, CRemoteRepeaterData& data) +{ + wxASSERT(handler != NULL); + + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDCSHandler* reflector = m_reflectors[i]; + if (reflector != NULL) { + if (reflector->m_destination == handler) { + if (reflector->m_direction == DIR_INCOMING && reflector->m_repeater.IsEmpty()) { + if (reflector->m_linkState != DCS_UNLINKING) + data.addLink(reflector->m_reflector, PROTO_DCS, reflector->m_linkState == DCS_LINKED, DIR_INCOMING, true); + } else { + if (reflector->m_linkState != DCS_UNLINKING) + data.addLink(reflector->m_reflector, PROTO_DCS, reflector->m_linkState == DCS_LINKED, reflector->m_direction, false); + } + } + } + } +} + +void CDCSHandler::process(CAMBEData& data) +{ + in_addr yourAddress = data.getYourAddress(); + unsigned int yourPort = data.getYourPort(); + unsigned int myPort = data.getMyPort(); + + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDCSHandler* reflector = m_reflectors[i]; + if (reflector != NULL) { + if (reflector->m_yourAddress.s_addr == yourAddress.s_addr && + reflector->m_yourPort == yourPort && + reflector->m_myPort == myPort) { + reflector->processInt(data); + return; + } + } + } +} + +void CDCSHandler::process(CPollData& poll) +{ + wxString reflector = poll.getData1(); + wxString repeater = poll.getData2(); + in_addr yourAddress = poll.getYourAddress(); + unsigned int yourPort = poll.getYourPort(); + unsigned int myPort = poll.getMyPort(); + unsigned int length = poll.getLength(); + + // Check to see if we already have a link + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDCSHandler* handler = m_reflectors[i]; + + if (handler != NULL) { + if (handler->m_reflector.IsSameAs(reflector) && + handler->m_repeater.IsSameAs(repeater) && + handler->m_yourAddress.s_addr == yourAddress.s_addr && + handler->m_yourPort == yourPort && + handler->m_myPort == myPort && + handler->m_direction == DIR_OUTGOING && + handler->m_linkState == DCS_LINKED && + length == 22U) { + handler->m_pollInactivityTimer.start(); + CPollData reply(handler->m_repeater, handler->m_reflector, handler->m_direction, handler->m_yourAddress, handler->m_yourPort); + handler->m_handler->writePoll(reply); + return; + } else if (handler->m_reflector.Left(LONG_CALLSIGN_LENGTH - 1U).IsSameAs(reflector.Left(LONG_CALLSIGN_LENGTH - 1U)) && + handler->m_yourAddress.s_addr == yourAddress.s_addr && + handler->m_yourPort == yourPort && + handler->m_myPort == myPort && + handler->m_direction == DIR_INCOMING && + handler->m_linkState == DCS_LINKED && + length == 17U) { + handler->m_pollInactivityTimer.start(); + return; + } + } + } + + wxLogMessage(wxT("Unknown incoming DCS poll from %s"), reflector.c_str()); +} + +void CDCSHandler::process(CConnectData& connect) +{ + CD_TYPE type = connect.getType(); + + if (type == CT_ACK || type == CT_NAK || type == CT_UNLINK) { + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + if (m_reflectors[i] != NULL) { + bool res = m_reflectors[i]->processInt(connect, type); + if (res) { + delete m_reflectors[i]; + m_reflectors[i] = NULL; + } + } + } + + return; + } + + // else if type == CT_LINK1 or type == CT_LINK2 + in_addr yourAddress = connect.getYourAddress(); + unsigned int yourPort = connect.getYourPort(); + unsigned int myPort = connect.getMyPort(); + + wxString repeaterCallsign = connect.getRepeater(); + wxString reflectorCallsign = connect.getReflector(); + + // Check that it isn't a duplicate + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + if (m_reflectors[i] != NULL) { + if (m_reflectors[i]->m_direction == DIR_INCOMING && + m_reflectors[i]->m_yourAddress.s_addr == yourAddress.s_addr && + m_reflectors[i]->m_yourPort == yourPort && + m_reflectors[i]->m_myPort == myPort && + m_reflectors[i]->m_repeater.IsSameAs(reflectorCallsign) && + m_reflectors[i]->m_reflector.IsSameAs(repeaterCallsign)) + return; + } + } + + // Check the validity of our repeater callsign + IReflectorCallback* handler = CRepeaterHandler::findDVRepeater(reflectorCallsign); + if (handler == NULL) { + wxLogMessage(wxT("DCS connect to unknown reflector %s from %s"), reflectorCallsign.c_str(), repeaterCallsign.c_str()); + CConnectData reply(repeaterCallsign, reflectorCallsign, CT_NAK, connect.getYourAddress(), connect.getYourPort()); + m_incoming->writeConnect(reply); + return; + } + + // A new connect packet indicates the need for a new entry + wxLogMessage(wxT("New incoming DCS link to %s from %s"), reflectorCallsign.c_str(), repeaterCallsign.c_str()); + + CDCSHandler* dcs = new CDCSHandler(handler, repeaterCallsign, reflectorCallsign, m_incoming, yourAddress, yourPort, DIR_INCOMING); + + bool found = false; + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + if (m_reflectors[i] == NULL) { + m_reflectors[i] = dcs; + found = true; + break; + } + } + + if (found) { + CConnectData reply(repeaterCallsign, reflectorCallsign, CT_ACK, yourAddress, yourPort); + m_incoming->writeConnect(reply); + } else { + CConnectData reply(repeaterCallsign, reflectorCallsign, CT_NAK, yourAddress, yourPort); + m_incoming->writeConnect(reply); + + wxLogError(wxT("No space to add new DCS link, ignoring")); + delete dcs; + } +} + +void CDCSHandler::link(IReflectorCallback* handler, const wxString& repeater, const wxString &gateway, const in_addr& address) +{ + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + if (m_reflectors[i] != NULL) { + if (m_reflectors[i]->m_direction == DIR_OUTGOING && m_reflectors[i]->m_destination == handler && m_reflectors[i]->m_linkState != DCS_UNLINKING) + return; + } + } + + CDCSProtocolHandler* protoHandler = m_pool->getHandler(); + if (protoHandler == NULL) + return; + + CDCSHandler* dcs = new CDCSHandler(handler, gateway, repeater, protoHandler, address, DCS_PORT, DIR_OUTGOING); + + bool found = false; + + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + if (m_reflectors[i] == NULL) { + m_reflectors[i] = dcs; + found = true; + break; + } + } + + if (found) { + CConnectData reply(m_gatewayType, repeater, gateway, CT_LINK1, address, DCS_PORT); + protoHandler->writeConnect(reply); + } else { + wxLogError(wxT("No space to add new DCS link, ignoring")); + delete dcs; + } +} + +void CDCSHandler::unlink(IReflectorCallback* handler, const wxString& callsign, bool exclude) +{ + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDCSHandler* reflector = m_reflectors[i]; + + if (reflector != NULL) { + bool found = false; + + if (exclude) { + if (reflector->m_direction == DIR_OUTGOING && reflector->m_destination == handler && !reflector->m_reflector.IsSameAs(callsign)) { + wxLogMessage(wxT("Removing outgoing DCS link %s, %s"), reflector->m_repeater.c_str(), reflector->m_reflector.c_str()); + + if (reflector->m_linkState == DCS_LINKING || reflector->m_linkState == DCS_LINKED) { + CConnectData connect(reflector->m_repeater, reflector->m_reflector, CT_UNLINK, reflector->m_yourAddress, reflector->m_yourPort); + reflector->m_handler->writeConnect(connect); + + reflector->m_linkState = DCS_UNLINKING; + reflector->m_tryTimer.start(1U); + reflector->m_tryCount = 0U; + } + + found = true; + } + } else { + if (reflector->m_destination == handler && reflector->m_reflector.IsSameAs(callsign)) { + wxLogMessage(wxT("Removing DCS link %s, %s"), reflector->m_repeater.c_str(), reflector->m_reflector.c_str()); + + if (reflector->m_linkState == DCS_LINKING || reflector->m_linkState == DCS_LINKED) { + CConnectData connect(reflector->m_repeater, reflector->m_reflector, CT_UNLINK, reflector->m_yourAddress, reflector->m_yourPort); + reflector->m_handler->writeConnect(connect); + + reflector->m_linkState = DCS_UNLINKING; + reflector->m_tryTimer.start(1U); + reflector->m_tryCount = 0U; + } + + found = true; + } + } + + // If an active link with incoming traffic, send an EOT to the repeater + if (found) { + if (reflector->m_dcsId != 0x00U) { + unsigned int seq = reflector->m_dcsSeq + 1U; + if (seq == 21U) + seq = 0U; + + CAMBEData data; + data.setData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES); + data.setSeq(seq); + data.setEnd(true); + data.setId(reflector->m_dcsId); + + reflector->m_destination->process(data, reflector->m_direction, AS_DCS); + } + + m_stateChange = true; + } + } + } +} + +void CDCSHandler::unlink() +{ + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDCSHandler* reflector = m_reflectors[i]; + + if (reflector != NULL) { + if (!reflector->m_repeater.IsEmpty()) { + wxLogMessage(wxT("Unlinking from DCS reflector %s"), reflector->m_reflector.c_str()); + + CConnectData connect(reflector->m_repeater, reflector->m_reflector, CT_UNLINK, reflector->m_yourAddress, reflector->m_yourPort); + reflector->m_handler->writeConnect(connect); + + reflector->m_linkState = DCS_UNLINKING; + reflector->m_tryTimer.start(1U); + reflector->m_tryCount = 0U; + } + } + } +} + +void CDCSHandler::writeHeader(IReflectorCallback* handler, CHeaderData& header, DIRECTION direction) +{ + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + if (m_reflectors[i] != NULL) + m_reflectors[i]->writeHeaderInt(handler, header, direction); + } +} + +void CDCSHandler::writeAMBE(IReflectorCallback* handler, CAMBEData& data, DIRECTION direction) +{ + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + if (m_reflectors[i] != NULL) + m_reflectors[i]->writeAMBEInt(handler, data, direction); + } +} + +void CDCSHandler::gatewayUpdate(const wxString& reflector, const wxString& address) +{ + wxString gateway = reflector; + gateway.Truncate(LONG_CALLSIGN_LENGTH - 1U); + + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDCSHandler* reflector = m_reflectors[i]; + if (reflector != NULL) { + if (reflector->m_reflector.Left(LONG_CALLSIGN_LENGTH - 1U).IsSameAs(gateway)) { + if (!address.IsEmpty()) { + // A new address, change the value + wxLogMessage(wxT("Changing IP address of DCS gateway or reflector %s to %s"), reflector->m_reflector.c_str(), address.c_str()); + reflector->m_yourAddress.s_addr = ::inet_addr(address.mb_str()); + } else { + wxLogMessage(wxT("IP address for DCS gateway or reflector %s has been removed"), reflector->m_reflector.c_str()); + + // No address, this probably shouldn't happen.... + if (reflector->m_direction == DIR_OUTGOING && reflector->m_destination != NULL) + reflector->m_destination->linkFailed(DP_DCS, reflector->m_reflector, false); + + m_stateChange = true; + + delete m_reflectors[i]; + m_reflectors[i] = NULL; + } + } + } + } +} + +void CDCSHandler::clock(unsigned int ms) +{ + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + if (m_reflectors[i] != NULL) { + bool ret = m_reflectors[i]->clockInt(ms); + if (ret) { + delete m_reflectors[i]; + m_reflectors[i] = NULL; + } + } + } +} + +void CDCSHandler::finalise() +{ + for (unsigned int i = 0U; i < m_maxReflectors; i++) + delete m_reflectors[i]; + + delete[] m_reflectors; +} + +void CDCSHandler::processInt(CAMBEData& data) +{ + // Make a copy of the AMBE data so that any changes made here don't modify the original + CAMBEData temp(data); + + unsigned int id = temp.getId(); + CHeaderData& header = temp.getHeader(); + unsigned int seqNo = temp.getSeq(); + + wxString my = header.getMyCall1(); + wxString rpt2 = header.getRptCall2(); + + if (m_whiteList != NULL) { + bool res = m_whiteList->isInList(my); + if (!res) { + wxLogMessage(wxT("%s rejected from DCS as not found in the white list"), my.c_str()); + m_dcsId = 0x00U; + return; + } + } + + if (m_blackList != NULL) { + bool res = m_blackList->isInList(my); + if (res) { + wxLogMessage(wxT("%s rejected from DCS as found in the black list"), my.c_str()); + m_dcsId = 0x00U; + return; + } + } + + if (m_linkState != DCS_LINKED) + return; + + switch (m_direction) { + case DIR_OUTGOING: + if (!m_reflector.IsSameAs(rpt2)) + return; + + if (m_dcsId == 0x00U && seqNo != 0U) + return; + + if (m_dcsId == 0x00U) { // && seqNo == 0U) { + // Write to Header.log if it's enabled + if (m_headerLogger != NULL) + m_headerLogger->write(wxT("DCS"), header); + + m_dcsId = id; + m_dcsSeq = 0x00U; + m_inactivityTimer.start(); + + header.setCQCQCQ(); + header.setFlags(0x00U, 0x00U, 0x00U); + + m_destination->process(header, m_direction, AS_DCS); + } + + if (id == m_dcsId) { + m_pollInactivityTimer.start(); + m_inactivityTimer.start(); + + m_dcsSeq = seqNo; + + if (m_dcsSeq == 0U) { + // Send the header every 21 frames + header.setCQCQCQ(); + header.setFlags(0x00U, 0x00U, 0x00U); + + m_destination->process(header, m_direction, AS_DUP); + } + + m_destination->process(temp, m_direction, AS_DCS); + + if (temp.isEnd()) { + m_dcsId = 0x00U; + m_dcsSeq = 0x00U; + m_inactivityTimer.stop(); + } + } + break; + + case DIR_INCOMING: + if (!m_repeater.IsSameAs(rpt2)) + return; + + if (m_dcsId == 0x00U && seqNo != 0U) + return; + + if (m_dcsId == 0x00U) { // && seqNo == 0U) { + // Write to Header.log if it's enabled + if (m_headerLogger != NULL) + m_headerLogger->write(wxT("DCS"), header); + + m_dcsId = id; + m_dcsSeq = 0x00U; + m_inactivityTimer.start(); + + header.setCQCQCQ(); + header.setFlags(0x00U, 0x00U, 0x00U); + + m_destination->process(header, m_direction, AS_DCS); + } + + if (id == m_dcsId) { + m_pollInactivityTimer.start(); + m_inactivityTimer.start(); + + m_dcsSeq = seqNo; + + if (m_dcsSeq == 0U) { + // Send the header every 21 frames + header.setCQCQCQ(); + header.setFlags(0x00U, 0x00U, 0x00U); + + m_destination->process(header, m_direction, AS_DUP); + } + + m_destination->process(temp, m_direction, AS_DCS); + + if (temp.isEnd()) { + m_dcsId = 0x00U; + m_dcsSeq = 0x00U; + m_inactivityTimer.stop(); + } + } + break; + } +} + +bool CDCSHandler::processInt(CConnectData& connect, CD_TYPE type) +{ + in_addr yourAddress = connect.getYourAddress(); + unsigned int yourPort = connect.getYourPort(); + unsigned int myPort = connect.getMyPort(); + wxString repeater = connect.getRepeater(); + + if (m_yourAddress.s_addr != yourAddress.s_addr || m_yourPort != yourPort || m_myPort != myPort) + return false; + + switch (type) { + case CT_ACK: + if (!m_repeater.IsSameAs(repeater)) + return false; + + if (m_linkState == DCS_LINKING) { + wxLogMessage(wxT("DCS ACK message received from %s"), m_reflector.c_str()); + + if (m_direction == DIR_OUTGOING && m_destination != NULL) + m_destination->linkUp(DP_DCS, m_reflector); + + m_tryTimer.stop(); + m_stateChange = true; + m_linkState = DCS_LINKED; + } + + return false; + + case CT_NAK: + if (!m_repeater.IsSameAs(repeater)) + return false; + + if (m_linkState == DCS_LINKING) { + wxLogMessage(wxT("DCS NAK message received from %s"), m_reflector.c_str()); + + if (m_direction == DIR_OUTGOING && m_destination != NULL) + m_destination->linkRefused(DP_DCS, m_reflector); + + return true; + } + + if (m_linkState == DCS_UNLINKING) { + wxLogMessage(wxT("DCS NAK message received from %s"), m_reflector.c_str()); + + if (m_direction == DIR_OUTGOING && m_destination != NULL) + m_destination->linkFailed(DP_DCS, m_reflector, false); + + return true; + } + + return false; + + case CT_UNLINK: + if (!m_reflector.IsSameAs(repeater)) + return false; + + if (m_linkState == DCS_LINKED) { + wxLogMessage(wxT("DCS disconnect message received from %s"), m_reflector.c_str()); + + if (m_direction == DIR_OUTGOING && m_destination != NULL) + m_destination->linkFailed(DP_DCS, m_reflector, false); + + m_stateChange = true; + } + + return true; + + default: + return false; + } +} + +bool CDCSHandler::clockInt(unsigned int ms) +{ + m_pollInactivityTimer.clock(ms); + m_inactivityTimer.clock(ms); + m_pollTimer.clock(ms); + m_tryTimer.clock(ms); + + if (m_pollInactivityTimer.isRunning() && m_pollInactivityTimer.hasExpired()) { + m_pollInactivityTimer.start(); + + m_stateChange = true; + m_dcsId = 0x00U; + m_dcsSeq = 0x00U; + + switch (m_linkState) { + case DCS_LINKING: + wxLogMessage(wxT("DCS link to %s has failed to connect"), m_reflector.c_str()); + break; + case DCS_LINKED: + wxLogMessage(wxT("DCS link to %s has failed (poll inactivity)"), m_reflector.c_str()); + break; + case DCS_UNLINKING: + wxLogMessage(wxT("DCS link to %s has failed to disconnect cleanly"), m_reflector.c_str()); + break; + default: + break; + } + + if (m_direction == DIR_OUTGOING) { + bool reconnect = m_destination->linkFailed(DP_DCS, m_reflector, true); + if (reconnect) { + CConnectData reply(m_gatewayType, m_repeater, m_reflector, CT_LINK1, m_yourAddress, m_yourPort); + m_handler->writeConnect(reply); + m_linkState = DCS_LINKING; + m_tryTimer.start(1U); + m_tryCount = 0U; + return false; + } + } + + return true; + } + + if (m_inactivityTimer.isRunning() && m_inactivityTimer.hasExpired()) { + m_dcsId = 0x00U; + m_dcsSeq = 0x00U; + m_inactivityTimer.stop(); + } + + if (m_pollTimer.isRunning() && m_pollTimer.hasExpired()) { + m_pollTimer.start(); + + CPollData poll(m_repeater, m_reflector, m_direction, m_yourAddress, m_yourPort); + m_handler->writePoll(poll); + } + + if (m_linkState == DCS_LINKING) { + if (m_tryTimer.isRunning() && m_tryTimer.hasExpired()) { + CConnectData reply(m_gatewayType, m_repeater, m_reflector, CT_LINK1, m_yourAddress, m_yourPort); + m_handler->writeConnect(reply); + + unsigned int timeout = calcBackoff(); + m_tryTimer.start(timeout); + } + } + + if (m_linkState == DCS_UNLINKING) { + if (m_tryTimer.isRunning() && m_tryTimer.hasExpired()) { + CConnectData connect(m_repeater, m_reflector, CT_UNLINK, m_yourAddress, m_yourPort); + m_handler->writeConnect(connect); + + unsigned int timeout = calcBackoff(); + m_tryTimer.start(timeout); + } + } + + return false; +} + +void CDCSHandler::writeHeaderInt(IReflectorCallback* handler, CHeaderData& header, DIRECTION direction) +{ + if (m_linkState != DCS_LINKED) + return; + + // Is it link in the right direction + if (m_direction != direction) + return; + + if (m_destination != handler) + return; + + // Already in use? + if (m_dcsId != 0x00) + return; + + m_seqNo = 0U; + + m_myCall1 = header.getMyCall1(); + m_myCall2 = header.getMyCall2(); + m_yourCall = header.getYourCall(); + m_rptCall1 = header.getRptCall1(); + m_rptCall2 = header.getRptCall2(); +} + +void CDCSHandler::writeAMBEInt(IReflectorCallback* handler, CAMBEData& data, DIRECTION direction) +{ + if (m_linkState != DCS_LINKED) + return; + + // Is it link in the right direction + if (m_direction != direction) + return; + + if (m_destination != handler) + return; + + // Already in use? + if (m_dcsId != 0x00) + return; + + CHeaderData& header = data.getHeader(); + header.setMyCall1(m_myCall1); + header.setMyCall2(m_myCall2); + header.setRptCall1(m_rptCall1); + header.setRptCall2(m_rptCall2); + header.setCQCQCQ(); + + data.setRptSeq(m_seqNo++); + data.setDestination(m_yourAddress, m_yourPort); + m_handler->writeData(data); +} + +bool CDCSHandler::stateChange() +{ + bool stateChange = m_stateChange; + + m_stateChange = false; + + return stateChange; +} + +void CDCSHandler::writeStatus(wxFFile& file) +{ + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDCSHandler* reflector = m_reflectors[i]; + if (reflector != NULL) { + struct tm* tm = ::gmtime(&reflector->m_time); + + switch (reflector->m_direction) { + case DIR_OUTGOING: + if (reflector->m_linkState == DCS_LINKED) { + wxString text; + text.Printf(wxT("%04d-%02d-%02d %02d:%02d:%02d: DCS link - Type: Repeater Rptr: %s Refl: %s Dir: Outgoing\n"), + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, + reflector->m_repeater.c_str(), reflector->m_reflector.c_str()); + file.Write(text); + } + break; + + case DIR_INCOMING: + if (reflector->m_linkState == DCS_LINKED) { + wxString text; + text.Printf(wxT("%04d-%02d-%02d %02d:%02d:%02d: DCS link - Type: Repeater Rptr: %s Refl: %s Dir: Incoming\n"), + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, + reflector->m_repeater.c_str(), reflector->m_reflector.c_str()); + file.Write(text); + } + break; + } + } + } +} + +unsigned int CDCSHandler::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; +} diff --git a/Common/DCSHandler.h b/Common/DCSHandler.h new file mode 100644 index 0000000..d54334a --- /dev/null +++ b/Common/DCSHandler.h @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2012,2013,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 + * 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 DCSHandler_H +#define DCSHandler_H + +#include "DCSProtocolHandlerPool.h" +#include "ReflectorCallback.h" +#include "DStarDefines.h" +#include "HeaderLogger.h" +#include "CallsignList.h" +#include "ConnectData.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 DCS_STATE { + DCS_LINKING, + DCS_LINKED, + DCS_UNLINKING +}; + +class CDCSHandler { +public: + static void initialise(unsigned int maxReflectors); + + static void setDCSProtocolHandlerPool(CDCSProtocolHandlerPool* pool); + static void setDCSProtocolIncoming(CDCSProtocolHandler* handler); + static void setHeaderLogger(CHeaderLogger* logger); + static void setGatewayType(GATEWAY_TYPE type); + + static void link(IReflectorCallback* handler, const wxString& repeater, const wxString& reflector, const in_addr& address); + static void unlink(IReflectorCallback* handler, const wxString& reflector = wxEmptyString, bool exclude = true); + static void unlink(); + + static void writeHeader(IReflectorCallback* handler, CHeaderData& header, DIRECTION direction); + static void writeAMBE(IReflectorCallback* handler, CAMBEData& data, DIRECTION direction); + + static void process(CAMBEData& header); + static void process(CPollData& data); + static void process(CConnectData& connect); + + static void gatewayUpdate(const wxString& reflector, const wxString& address); + static void clock(unsigned int ms); + + static bool stateChange(); + static void writeStatus(wxFFile& file); + + static void setWhiteList(CCallsignList* list); + static void setBlackList(CCallsignList* list); + + static void finalise(); + + static void getInfo(IReflectorCallback* handler, CRemoteRepeaterData& data); + + static wxString getIncoming(const wxString& callsign); + +protected: + CDCSHandler(IReflectorCallback* handler, const wxString& reflector, const wxString& repeater, CDCSProtocolHandler* protoHandler, const in_addr& address, unsigned int port, DIRECTION direction); + ~CDCSHandler(); + + void processInt(CAMBEData& data); + bool processInt(CConnectData& connect, CD_TYPE type); + + void writeHeaderInt(IReflectorCallback* handler, CHeaderData& header, DIRECTION direction); + void writeAMBEInt(IReflectorCallback* handler, CAMBEData& data, DIRECTION direction); + + bool clockInt(unsigned int ms); + +private: + static unsigned int m_maxReflectors; + static CDCSHandler** m_reflectors; + + static CDCSProtocolHandlerPool* m_pool; + static CDCSProtocolHandler* m_incoming; + + static bool m_stateChange; + + static GATEWAY_TYPE m_gatewayType; + + static CHeaderLogger* m_headerLogger; + + static CCallsignList* m_whiteList; + static CCallsignList* m_blackList; + + wxString m_reflector; + wxString m_repeater; + CDCSProtocolHandler* m_handler; + in_addr m_yourAddress; + unsigned int m_yourPort; + unsigned int m_myPort; + DIRECTION m_direction; + DCS_STATE m_linkState; + IReflectorCallback* m_destination; + time_t m_time; + CTimer m_pollTimer; + CTimer m_pollInactivityTimer; + CTimer m_tryTimer; + unsigned int m_tryCount; + unsigned int m_dcsId; + unsigned int m_dcsSeq; + unsigned int m_seqNo; + CTimer m_inactivityTimer; + + // Header data + wxString m_yourCall; + wxString m_myCall1; + wxString m_myCall2; + wxString m_rptCall1; + wxString m_rptCall2; + + unsigned int calcBackoff(); +}; + +#endif diff --git a/Common/DCSProtocolHandler.cpp b/Common/DCSProtocolHandler.cpp new file mode 100644 index 0000000..74eaf45 --- /dev/null +++ b/Common/DCSProtocolHandler.cpp @@ -0,0 +1,196 @@ +/* + * 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 + * 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 "DCSProtocolHandler.h" + +#include "DStarDefines.h" +#include "Utils.h" + +// #define DUMP_TX + +const unsigned int BUFFER_LENGTH = 2000U; + +CDCSProtocolHandler::CDCSProtocolHandler(unsigned int port, const wxString& addr) : +m_socket(addr, port), +m_type(DC_NONE), +m_buffer(NULL), +m_length(0U), +m_yourAddress(), +m_yourPort(0U), +m_myPort(port) +{ + m_buffer = new unsigned char[BUFFER_LENGTH]; +} + +CDCSProtocolHandler::~CDCSProtocolHandler() +{ + delete[] m_buffer; +} + +bool CDCSProtocolHandler::open() +{ + return m_socket.open(); +} + +unsigned int CDCSProtocolHandler::getPort() const +{ + return m_myPort; +} + +bool CDCSProtocolHandler::writeData(const CAMBEData& data) +{ + unsigned char buffer[100U]; + unsigned int length = data.getDCSData(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 CDCSProtocolHandler::writePoll(const CPollData& poll) +{ + unsigned char buffer[25U]; + unsigned int length = poll.getDCSData(buffer, 25U); + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Poll"), buffer, length); +#endif + + return m_socket.write(buffer, length, poll.getYourAddress(), poll.getYourPort()); +} + +bool CDCSProtocolHandler::writeConnect(const CConnectData& connect) +{ + unsigned char buffer[520U]; + unsigned int length = connect.getDCSData(buffer, 520U); + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Connect"), buffer, length); +#endif + + return m_socket.write(buffer, length, connect.getYourAddress(), connect.getYourPort()); +} + +DCS_TYPE CDCSProtocolHandler::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 CDCSProtocolHandler::readPackets() +{ + m_type = DC_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') { + if (m_length == 100U) { + m_type = DC_DATA; + return false; + } + } else if (m_buffer[0] == 'E' && m_buffer[1] == 'E' && m_buffer[2] == 'E' && m_buffer[3] == 'E') { + // CUtils::dump(wxT("Status data"), m_buffer, m_length); + return true; + } else { + switch (m_length) { + case 17U: + case 22U: + m_type = DC_POLL; + return false; + case 14U: + case 19U: + case 519U: + m_type = DC_CONNECT; + return false; + case 35U: + // CUtils::dump(wxT("Status data"), m_buffer, m_length); + return true; + default: + break; + } + } + + // An unknown type + // CUtils::dump(wxT("Unknown packet type from DCS"), m_buffer, m_length); + return true; +} + +CAMBEData* CDCSProtocolHandler::readData() +{ + if (m_type != DC_DATA) + return NULL; + + CAMBEData* data = new CAMBEData; + + bool res = data->setDCSData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort); + if (!res) { + delete data; + return NULL; + } + + return data; +} + +CPollData* CDCSProtocolHandler::readPoll() +{ + if (m_type != DC_POLL) + return NULL; + + CPollData* poll = new CPollData; + + bool res = poll->setDCSData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort); + if (!res) { + delete poll; + return NULL; + } + + return poll; +} + +CConnectData* CDCSProtocolHandler::readConnect() +{ + if (m_type != DC_CONNECT) + return NULL; + + CConnectData* connect = new CConnectData; + + bool res = connect->setDCSData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort); + if (!res) { + delete connect; + return NULL; + } + + return connect; +} + +void CDCSProtocolHandler::close() +{ + m_socket.close(); +} diff --git a/Common/DCSProtocolHandler.h b/Common/DCSProtocolHandler.h new file mode 100644 index 0000000..c39c92f --- /dev/null +++ b/Common/DCSProtocolHandler.h @@ -0,0 +1,75 @@ +/* + * 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 + * 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 DCSProtocolHandler_H +#define DCSProtocolHandler_H + +#include "UDPReaderWriter.h" +#include "DStarDefines.h" +#include "ConnectData.h" +#include "AMBEData.h" +#include "PollData.h" + +#if defined(__WINDOWS__) +#include "Inaddr.h" +#else +#include +#endif + +#include + +enum DCS_TYPE { + DC_NONE, + DC_DATA, + DC_POLL, + DC_CONNECT +}; + +class CDCSProtocolHandler { +public: + CDCSProtocolHandler(unsigned int port, const wxString& addr = wxEmptyString); + ~CDCSProtocolHandler(); + + bool open(); + + unsigned int getPort() const; + + bool writeData(const CAMBEData& data); + bool writeConnect(const CConnectData& connect); + bool writePoll(const CPollData& poll); + + DCS_TYPE read(); + CAMBEData* readData(); + CPollData* readPoll(); + CConnectData* readConnect(); + + void close(); + +private: + CUDPReaderWriter m_socket; + DCS_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/DCSProtocolHandlerPool.cpp b/Common/DCSProtocolHandlerPool.cpp new file mode 100644 index 0000000..25960d2 --- /dev/null +++ b/Common/DCSProtocolHandlerPool.cpp @@ -0,0 +1,132 @@ +/* + * 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 + * 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 "DCSProtocolHandlerPool.h" + +CDCSProtocolHandlerPool::CDCSProtocolHandlerPool(unsigned int n, unsigned int port, const wxString& addr) : +m_pool(NULL), +m_n(n), +m_index(0U) +{ + wxASSERT(port > 0U); + wxASSERT(n > 0U); + + m_pool = new CDCSProtocolHandlerEntry[n]; + + for (unsigned int i = 0U; i < n; i++) { + m_pool[i].m_handler = new CDCSProtocolHandler(port + i, addr); + m_pool[i].m_port = port + i; + m_pool[i].m_inUse = false; + } + + wxLogMessage(wxT("Allocated UDP ports %u-%u to DCS"), port, port + n - 1U); +} + +CDCSProtocolHandlerPool::~CDCSProtocolHandlerPool() +{ + for (unsigned int i = 0U; i < m_n; i++) + delete m_pool[i].m_handler; + + delete[] m_pool; +} + +bool CDCSProtocolHandlerPool::open() +{ + for (unsigned int i = 0U; i < m_n; i++) { + bool ret = m_pool[i].m_handler->open(); + if (!ret) + return false; + } + + return true; +} + +CDCSProtocolHandler* CDCSProtocolHandlerPool::getHandler(unsigned int port) +{ + if (port == 0U) { + for (unsigned int i = 0U; i < m_n; i++) { + if (!m_pool[i].m_inUse) { + m_pool[i].m_inUse = true; + return m_pool[i].m_handler; + } + } + } else { + for (unsigned int i = 0U; i < m_n; i++) { + if (m_pool[i].m_port == port) { + m_pool[i].m_inUse = true; + return m_pool[i].m_handler; + } + } + } + + wxLogError(wxT("Cannot find a free DCS port in the pool")); + + return NULL; +} + +void CDCSProtocolHandlerPool::release(CDCSProtocolHandler* handler) +{ + wxASSERT(handler != NULL); + + for (unsigned int i = 0U; i < m_n; i++) { + if (m_pool[i].m_handler == handler && m_pool[i].m_inUse) { + m_pool[i].m_inUse = false; + return; + } + } + + wxLogError(wxT("Trying to release an unused DCS port")); +} + +DCS_TYPE CDCSProtocolHandlerPool::read() +{ + while (m_index < m_n) { + if (m_pool[m_index].m_inUse) { + DCS_TYPE type = m_pool[m_index].m_handler->read(); + if (type != DC_NONE) + return type; + } + + m_index++; + } + + m_index = 0U; + + return DC_NONE; +} + +CAMBEData* CDCSProtocolHandlerPool::readData() +{ + return m_pool[m_index].m_handler->readData(); +} + +CPollData* CDCSProtocolHandlerPool::readPoll() +{ + return m_pool[m_index].m_handler->readPoll(); +} + +CConnectData* CDCSProtocolHandlerPool::readConnect() +{ + return m_pool[m_index].m_handler->readConnect(); +} + +void CDCSProtocolHandlerPool::close() +{ + for (unsigned int i = 0U; i < m_n; i++) + m_pool[i].m_handler->close(); +} diff --git a/Common/DCSProtocolHandlerPool.h b/Common/DCSProtocolHandlerPool.h new file mode 100644 index 0000000..6502605 --- /dev/null +++ b/Common/DCSProtocolHandlerPool.h @@ -0,0 +1,56 @@ +/* + * 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 + * 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 DCSProtocolHandlerPool_H +#define DCSProtocolHandlerPool_H + +#include + +#include "DCSProtocolHandler.h" + +class CDCSProtocolHandlerEntry { +public: + CDCSProtocolHandler* m_handler; + unsigned int m_port; + bool m_inUse; +}; + +class CDCSProtocolHandlerPool { +public: + CDCSProtocolHandlerPool(unsigned int n, unsigned int port, const wxString& addr = wxEmptyString); + ~CDCSProtocolHandlerPool(); + + bool open(); + + CDCSProtocolHandler* getHandler(unsigned int port = 0U); + void release(CDCSProtocolHandler* handler); + + DCS_TYPE read(); + CAMBEData* readData(); + CPollData* readPoll(); + CConnectData* readConnect(); + + void close(); + +private: + CDCSProtocolHandlerEntry* m_pool; + unsigned int m_n; + unsigned int m_index; +}; + +#endif diff --git a/Common/DDData.cpp b/Common/DDData.cpp new file mode 100644 index 0000000..884702a --- /dev/null +++ b/Common/DDData.cpp @@ -0,0 +1,313 @@ +/* + * Copyright (C) 2011,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 "DDData.h" +#include "Utils.h" + +unsigned int ETHERNET_ADDRESS_LENGTH = 6U; + +unsigned int BUFFER_LENGTH = 2500U; + +CDDData::CDDData() : +m_header(), +m_length(0U), +m_frame(NULL) +{ + m_frame = new unsigned char[BUFFER_LENGTH]; +} + +CDDData::CDDData(const CDDData& data) : +m_header(data.m_header), +m_length(data.m_length), +m_frame(NULL) +{ + m_frame = new unsigned char[BUFFER_LENGTH]; + ::memcpy(m_frame, data.m_frame, data.m_length); +} + +CDDData::~CDDData() +{ + delete[] m_frame; +} + +bool CDDData::setIcomRepeaterData(const unsigned char *data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort) +{ + wxASSERT(data != NULL); + wxASSERT(length >= 29U); + + bool ret = m_header.setIcomRepeaterData(data, length, true, yourAddress, yourPort); + if (!ret) + return false; + + m_length = data[59] * 256U + data[58]; + + if (m_length > BUFFER_LENGTH) + m_length = BUFFER_LENGTH; + + ::memcpy(m_frame, data + 60U, m_length); + + return true; +} + +bool CDDData::setHBRepeaterData(const unsigned char *data, unsigned int length, const in_addr&, unsigned int) +{ + wxASSERT(data != NULL); + wxASSERT(length >= 60U); + + m_header.setData(data, length, false); + + m_length = length - 44U; + + if (m_length > BUFFER_LENGTH) + m_length = BUFFER_LENGTH; + + ::memcpy(m_frame, data + 44U, m_length); + + return true; +} + +unsigned int CDDData::getIcomRepeaterData(unsigned char *data, unsigned int length) +{ + wxASSERT(data != NULL); + wxASSERT(length >= 32U); + + // This section is used when it's normal data (i.e. not an ack) for an Icom controller + data[0] = 'D'; + data[1] = 'S'; + data[2] = 'T'; + data[3] = 'R'; + + data[4] = m_header.getRptSeq() / 256U; // Packet sequence number + data[5] = m_header.getRptSeq() % 256U; + + data[6] = 0x73; // Not a response + data[7] = 0x11; // DD Data type + + unsigned int dataLength = m_length + 50U; + + data[8] = dataLength / 256U; // Length + data[9] = dataLength % 256U; + + data[10] = 0x40; // DD Data + + data[11] = 0xFF; + data[12] = 0xFF; + data[13] = 0xFF; + + data[14] = 0x00; // Dummy ID + data[15] = 0x00; + + data[16] = 0xC0; // DD Data + + m_header.getData(data + 17U, RADIO_HEADER_LENGTH_BYTES, true); + + // Another length field + data[58] = m_length % 256U; + data[59] = m_length / 256U; + + // Now copy the payload + ::memcpy(data + 60U, m_frame, m_length); + + return 60U + m_length; +} + +unsigned int CDDData::getHBRepeaterData(unsigned char *data, unsigned int length) +{ + wxASSERT(data != NULL); + wxASSERT(length >= 1600U); + + data[0] = 'D'; + data[1] = 'S'; + data[2] = 'R'; + data[3] = 'P'; + + data[4] = 0x24U; + + m_header.getData(data + 5U, RADIO_HEADER_LENGTH_BYTES, false); + + // Now copy the payload + ::memcpy(data + 44U, m_frame, m_length); + + return 44U + m_length; +} + +unsigned int CDDData::getRptSeq() const +{ + return m_header.getRptSeq(); +} + +void CDDData::setRptSeq(unsigned int seqNo) +{ + m_header.setRptSeq(seqNo); +} + +unsigned char CDDData::getBand1() const +{ + return m_header.getBand1(); +} + +unsigned char CDDData::getBand2() const +{ + return m_header.getBand2(); +} + +unsigned char CDDData::getBand3() const +{ + return m_header.getBand3(); +} + +void CDDData::setBand1(unsigned char band) +{ + m_header.setBand1(band); +} + +void CDDData::setBand2(unsigned char band) +{ + m_header.setBand2(band); +} + +void CDDData::setBand3(unsigned char band) +{ + m_header.setBand3(band); +} + +unsigned char CDDData::getFlag1() const +{ + return m_header.getFlag1(); +} + +unsigned char CDDData::getFlag2() const +{ + return m_header.getFlag2(); +} + +unsigned char CDDData::getFlag3() const +{ + return m_header.getFlag3(); +} + +void CDDData::setFlags(unsigned char flag1, unsigned char flag2, unsigned char flag3) +{ + m_header.setFlags(flag1, flag2, flag3); +} + +wxString CDDData::getMyCall1() const +{ + return m_header.getMyCall1(); +} + +wxString CDDData::getMyCall2() const +{ + return m_header.getMyCall2(); +} + +wxString CDDData::getYourCall() const +{ + return m_header.getYourCall(); +} + +wxString CDDData::getRptCall1() const +{ + return m_header.getRptCall1(); +} + +wxString CDDData::getRptCall2() const +{ + return m_header.getRptCall2(); +} + +void CDDData::setMyCall1(const wxString& callsign) +{ + m_header.setMyCall1(callsign); +} + +void CDDData::setMyCall2(const wxString& callsign) +{ + m_header.setMyCall2(callsign); +} + +void CDDData::setYourCall(const wxString& callsign) +{ + m_header.setYourCall(callsign); +} + +void CDDData::setRptCall1(const wxString& callsign) +{ + m_header.setRptCall1(callsign); +} + +void CDDData::setRptCall2(const wxString& callsign) +{ + m_header.setRptCall2(callsign); +} + +void CDDData::setRepeaters(const wxString& rpt1, const wxString& rpt2) +{ + m_header.setRepeaters(rpt1, rpt2); +} + +void CDDData::setDestination(const in_addr& address, unsigned int port) +{ + m_header.setDestination(address, port); +} + +in_addr CDDData::getYourAddress() const +{ + return m_header.getYourAddress(); +} + +unsigned int CDDData::getYourPort() const +{ + return m_header.getYourPort(); +} + +void CDDData::setEthernetFrame(const unsigned char *frame, unsigned int length) +{ + wxASSERT(frame != NULL); + wxASSERT(length > 0U); + + m_length = length; + if (m_length > BUFFER_LENGTH) + m_length = BUFFER_LENGTH; + + ::memcpy(m_frame, frame, m_length); +} + +unsigned int CDDData::getEthernetFrame(unsigned char *frame, unsigned int length) const +{ + wxASSERT(frame != NULL); + wxASSERT(length > 0U); + + if (length > m_length) + length = m_length; + + ::memcpy(frame, m_frame, length); + + return length; +} + +unsigned char* CDDData::getSourceAddress() const +{ + return m_frame + ETHERNET_ADDRESS_LENGTH; +} + +unsigned char* CDDData::getDestinationAddress() const +{ + return m_frame + 0U; +} diff --git a/Common/DDData.h b/Common/DDData.h new file mode 100644 index 0000000..ad89902 --- /dev/null +++ b/Common/DDData.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2011,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 DDData_H +#define DDData_H + +#include "HeaderData.h" + +#include + +#if defined(__WINDOWS__) +#include "Inaddr.h" +#else +#include +#endif + +class CDDData { +public: + CDDData(); + CDDData(const CDDData& data); + ~CDDData(); + + bool setIcomRepeaterData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort); + bool setHBRepeaterData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort); + + unsigned int getIcomRepeaterData(unsigned char* data, unsigned int length); + unsigned int getHBRepeaterData(unsigned char* data, unsigned int length); + + unsigned char getBand1() const; + unsigned char getBand2() const; + unsigned char getBand3() const; + void setBand1(unsigned char band); + void setBand2(unsigned char band); + void setBand3(unsigned char band); + + unsigned char getFlag1() const; + unsigned char getFlag2() const; + unsigned char getFlag3() const; + void setFlags(unsigned char flag1, unsigned char flag2, unsigned char flag3); + + wxString getMyCall1() const; + wxString getMyCall2() const; + wxString getYourCall() const; + wxString getRptCall1() const; + wxString getRptCall2() const; + + void setMyCall1(const wxString& callsign); + void setMyCall2(const wxString& callsign); + void setYourCall(const wxString& callsign); + void setRptCall1(const wxString& callsign); + void setRptCall2(const wxString& callsign); + + unsigned int getRptSeq() const; + void setRptSeq(unsigned int seqNo); + + void setEthernetFrame(const unsigned char* frame, unsigned int length); + unsigned int getEthernetFrame(unsigned char* frame, unsigned int length) const; + + unsigned char* getSourceAddress() const; + unsigned char* getDestinationAddress() const; + + void setRepeaters(const wxString& rpt1, const wxString& rpt2); + void setDestination(const in_addr& address, unsigned int port); + + in_addr getYourAddress() const; + unsigned int getYourPort() const; + +private: + CHeaderData m_header; + unsigned int m_length; + unsigned char* m_frame; +}; + +#endif diff --git a/Common/DDHandler.cpp b/Common/DDHandler.cpp new file mode 100644 index 0000000..cab79e6 --- /dev/null +++ b/Common/DDHandler.cpp @@ -0,0 +1,394 @@ +/* + * 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 + * 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 "DDHandler.h" +#include "Defs.h" + +#include + +#if !defined(WIN32) +// XXX Check these +#include +#include +#include +#include +#include +#include +#endif + +const unsigned int ETHERNET_ADDRESS_LENGTH = 6U; +const unsigned int ETHERNET_MTU = 1500U; +const unsigned int BUFFER_LENGTH = 2000U; + +const unsigned int MIN_HEARD_TIME_SECS = 120U; + +const int MINIMUM_DD_FRAME_LENGTH = 60; + +const unsigned char ETHERNET_BROADCAST_ADDRESS[] = {0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU}; +// Multicast address '01:00:5E:00:00:01' - IP: '224.0.0.1' (send to all) +const unsigned char TOALL_MULTICAST_ADDRESS[] = {0x01U, 0x00U, 0x5EU, 0x00U, 0x00U, 0x01U}; +// Multicast address '01:00:5E:00:00:23' - IP: '224.0.0.35' (DX-Cluster) +const unsigned char DX_MULTICAST_ADDRESS[] = {0x01U, 0x00U, 0x5EU, 0x00U, 0x00U, 0x23U}; + +CIRCDDB* CDDHandler::m_irc = NULL; +CHeaderLogger* CDDHandler::m_headerLogger = NULL; +int CDDHandler::m_fd = -1; +unsigned int CDDHandler::m_maxRoutes = 0U; +CEthernet** CDDHandler::m_list = NULL; +unsigned char* CDDHandler::m_buffer = NULL; +bool CDDHandler::m_logEnabled = false; +wxString CDDHandler::m_logDir = wxEmptyString; +wxString CDDHandler::m_name = wxEmptyString; +CTimer CDDHandler::m_timer = CTimer(1000U, MIN_HEARD_TIME_SECS); + +CEthernet::CEthernet(const unsigned char* address, const wxString& callsign) : +m_address(NULL), +m_callsign(callsign) +{ + wxASSERT(address != NULL); + wxASSERT(!callsign.IsEmpty()); + + m_address = new unsigned char[ETHERNET_ADDRESS_LENGTH]; + ::memcpy(m_address, address, ETHERNET_ADDRESS_LENGTH); +} + +CEthernet::~CEthernet() +{ + delete[] m_address; +} + +unsigned char* CEthernet::getAddress() const +{ + return m_address; +} + +wxString CEthernet::getCallsign() const +{ + return m_callsign; +} + +void CDDHandler::initialise(unsigned int maxRoutes, const wxString& name) +{ + wxASSERT(maxRoutes > 0U); + + m_maxRoutes = maxRoutes; + m_name = name; + + m_buffer = new unsigned char[BUFFER_LENGTH]; + + m_list = new CEthernet*[maxRoutes]; + for (unsigned int i = 0U; i < maxRoutes; i++) + m_list[i] = NULL; + + // Add a dummy entry for broadcasts + m_list[0] = new CEthernet(ETHERNET_BROADCAST_ADDRESS, wxT(" ")); + // Add a dummy entry for "to all" multicast + m_list[1] = new CEthernet(TOALL_MULTICAST_ADDRESS, wxT("CQCQCQ ")); + // Add a dummy entry for "DX-Cluster" multicast + m_list[2] = new CEthernet(DX_MULTICAST_ADDRESS, wxT("CQCQCQ ")); + +#if !defined(WIN32) + m_fd = ::open("/dev/net/tun", O_RDWR); + if (m_fd < 0) { + wxLogError(wxT("Cannot open /dev/net/tun")); + return; + } + + struct ifreq ifr1; + ::memset(&ifr1, 0x00, sizeof(struct ifreq)); + + ifr1.ifr_flags = IFF_TAP | IFF_NO_PI; + ::strcpy(ifr1.ifr_name, "tap%d"); + + if (::ioctl(m_fd, TUNSETIFF, (void *)&ifr1) < 0) { + wxLogError(wxT("TUNSETIFF ioctl failed, closing the tap device")); + ::close(m_fd); + m_fd = -1; + return; + } + + wxString device = wxString(ifr1.ifr_name, wxConvLocal); + wxLogMessage(wxT("DD mode Tap interface created on %s"), device.c_str()); + + int fd = ::socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + wxLogError(wxT("Unable to open the config socket, closing the tap device")); + ::close(m_fd); + m_fd = -1; + return; + } + + struct ifreq ifr2; + ::memset(&ifr2, 0x00, sizeof(struct ifreq)); + ::strcpy(ifr2.ifr_name, ifr1.ifr_name); + + ifr2.ifr_flags = IFF_UP | IFF_BROADCAST | IFF_MULTICAST; + if (::ioctl(fd, SIOCSIFFLAGS, (void *)&ifr2) < 0) { + wxLogError(wxT("SIOCSIFFLAGS ioctl failed, closing the tap device")); + ::close(m_fd); + m_fd = -1; + return; + } + + ::close(fd); +#endif +} + +void CDDHandler::setLogging(bool enabled, const wxString& dir) +{ + m_logDir = dir; + m_logEnabled = enabled; +} + +void CDDHandler::setHeaderLogger(CHeaderLogger* logger) +{ + m_headerLogger = logger; +} + +void CDDHandler::setIRC(CIRCDDB* irc) +{ + wxASSERT(irc != NULL); + + m_irc = irc; +} + +void CDDHandler::process(CDDData& data) +{ + // If we're not initialised, return immediately + if (m_maxRoutes == 0U) + return; + + unsigned char flag1 = data.getFlag1(); + unsigned char flag2 = data.getFlag2(); + unsigned char flag3 = data.getFlag3(); + wxString myCall1 = data.getMyCall1(); + wxString myCall2 = data.getMyCall2(); + wxString yourCall = data.getYourCall(); + wxString rptCall1 = data.getRptCall1(); + wxString rptCall2 = data.getRptCall2(); + + if (!m_timer.isRunning() || m_timer.hasExpired()) { + // Write to Header.log if it's enabled + if (m_headerLogger != NULL) + m_headerLogger->write(wxT("Repeater"), data); + + if (m_irc != NULL) { + m_irc->sendHeardWithTXMsg(myCall1, myCall2, yourCall, rptCall1, rptCall2, flag1, flag2, flag3, wxEmptyString, wxT("Digital Data ")); + m_irc->sendHeardWithTXStats(myCall1, myCall2, yourCall, rptCall1, rptCall2, flag1, flag2, flag3, 1, 0, -1); + } + + m_timer.start(); + } + + // Can we continue? + if (m_fd < 0) + return; + + unsigned char* address = data.getSourceAddress(); + + bool found = false; + for (unsigned int i = 0U; i < m_maxRoutes; i++) { + if (m_list[i] != NULL) { + unsigned char* addr = m_list[i]->getAddress(); + + if (::memcmp(addr, address, ETHERNET_ADDRESS_LENGTH) == 0) { + found = true; + break; + } + } + } + + if (!found) { + wxLogMessage(wxT("Adding DD user %s with ethernet address %02X:%02X:%02X:%02X:%02X:%02X"), myCall1.c_str(), + address[0], address[1], address[2], address[3], address[4], address[5]); + + CEthernet* ethernet = new CEthernet(address, myCall1); + + found = false; + for (unsigned int i = 0U; i < m_maxRoutes; i++) { + if (m_list[i] == NULL) { + m_list[i] = ethernet; + found = true; + if (m_logEnabled) + writeStatus(*ethernet); + break; + } + } + + if (!found) { + wxLogError(wxT("No space to add new DD ethernet address")); + delete ethernet; + return; + } + } + +#if !defined(WIN32) + unsigned int length = data.getEthernetFrame(m_buffer, BUFFER_LENGTH); + + ssize_t len = ::write(m_fd, (char*)m_buffer, length); + if (len != ssize_t(length)) + wxLogError(wxT("Error returned from write()")); +#endif +} + +CDDData* CDDHandler::read() +{ + // If we're not initialised, return immediately + if (m_maxRoutes == 0U) + return NULL; + +#if defined(WIN32) + return NULL; +#else + // Check that the read() won't block + fd_set readFds; + FD_ZERO(&readFds); +#if defined(__WINDOWS__) + FD_SET((unsigned int)m_fd, &readFds); +#else + FD_SET(m_fd, &readFds); +#endif + + // Return immediately + timeval tv; + tv.tv_sec = 0L; + tv.tv_usec = 0L; + + int ret = ::select(m_fd + 1, &readFds, NULL, NULL, &tv); + if (ret < 0) { + wxLogError(wxT("Error returned from select()")); + return NULL; + } + +#if defined(__WINDOWS__) + if (!FD_ISSET((unsigned int)m_fd, &readFds)) + return NULL; +#else + if (!FD_ISSET(m_fd, &readFds)) + return NULL; +#endif + + // Ensure that the minimum length is padded with 0x00s + ::memset(m_buffer, 0x00U, MINIMUM_DD_FRAME_LENGTH); + + ssize_t len = ::read(m_fd, (char*)m_buffer, BUFFER_LENGTH); + if (len <= 0) { + wxLogError(wxT("Error returned from read()")); + return NULL; + } + + // There seems to be a minimum size with DD mode, so pad with zeroes if it's not reached + if (len < MINIMUM_DD_FRAME_LENGTH) + len = MINIMUM_DD_FRAME_LENGTH; + + // Point to the destination ethernet address + unsigned char* address = m_buffer + 0U; + + // Do destination address to callsign lookup + CEthernet* ethernet = NULL; + for (unsigned int i = 0U; i < m_maxRoutes; i++) { + if (m_list[i] != NULL) { + unsigned char* addr = m_list[i]->getAddress(); + + if (::memcmp(addr, address, ETHERNET_ADDRESS_LENGTH) == 0) { + ethernet = m_list[i]; + break; + } + } + } + + if (ethernet == NULL) { + wxLogWarning(wxT("Cannot find the ethernet address of %02X:%02X:%02X:%02X:%02X:%02X in the ethernet list"), address[0], address[1], address[2], address[3], address[4], address[5]); + return NULL; + } + + CRepeaterHandler* handler = CRepeaterHandler::findDDRepeater(); + if (handler == NULL) { + wxLogWarning(wxT("Incoming DD data to unknown repeater")); + return NULL; + } + + // wxLogMessage(wxT("Mapping ethernet address %02X:%02X:%02X:%02X:%02X:%02X to user %s"), + // address[0], address[1], address[2], address[3], address[4], address[5], + // ethernet->getCallsign().c_str()); + + CDDData* data = new CDDData; + data->setEthernetFrame(m_buffer, len); + data->setYourCall(ethernet->getCallsign()); + + handler->process(*data); + + return data; +#endif +} + +void CDDHandler::clock(unsigned int ms) +{ + m_timer.clock(ms); +} + +void CDDHandler::finalise() +{ +#if !defined(WIN32) + if (m_fd >= 0) { + ::close(m_fd); + m_fd = -1; + } +#endif + + delete[] m_buffer; + + for (unsigned int i = 0U; i < m_maxRoutes; i++) + delete m_list[i]; + delete[] m_list; +} + +void CDDHandler::writeStatus(const CEthernet& ethernet) +{ + wxString fullName = DDMODE_BASE_NAME; + + if (!m_name.IsEmpty()) { + fullName.Append(wxT("_")); + fullName.Append(m_name); + } + + wxFileName fileName(m_logDir, fullName, wxT("log")); + + wxFFile file; + bool ret = file.Open(fileName.GetFullPath(), wxT("at")); + if (!ret) { + wxLogError(wxT("Unable to open %s for writing"), fileName.GetFullPath().c_str()); + return; + } + + wxString callsign = ethernet.getCallsign(); + unsigned char* address = ethernet.getAddress(); + + time_t timeNow = ::time(NULL); + struct tm* tm = ::gmtime(&timeNow); + + wxString text; + text.Printf(wxT("%04d-%02d-%02d %02d:%02d:%02d: %02X:%02X:%02X:%02X:%02X:%02X %s\n"), + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, + address[0], address[1], address[2], address[3], address[4], address[5], callsign.c_str()); + + file.Write(text); + + file.Close(); +} diff --git a/Common/DDHandler.h b/Common/DDHandler.h new file mode 100644 index 0000000..91d097d --- /dev/null +++ b/Common/DDHandler.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2011,2012 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 DDHandler_H +#define DDHandler_H + +#include "HeaderLogger.h" +#include "DDData.h" +#include "IRCDDB.h" +#include "Timer.h" + +#include + +class CEthernet { +public: + CEthernet(const unsigned char* address, const wxString& callsign); + ~CEthernet(); + + unsigned char* getAddress() const; + wxString getCallsign() const; + +private: + unsigned char* m_address; + wxString m_callsign; +}; + +class CDDHandler { +public: + static void initialise(unsigned int maxRoutes, const wxString& name); + + static void setLogging(bool enabled, const wxString& dir); + static void setHeaderLogger(CHeaderLogger* logger); + static void setIRC(CIRCDDB* irc); + + static void process(CDDData& data); + + static CDDData* read(); + + static void clock(unsigned int ms); + + static void finalise(); + +private: + static CIRCDDB* m_irc; + static CHeaderLogger* m_headerLogger; + static int m_fd; + static unsigned int m_maxRoutes; + static CEthernet** m_list; + static unsigned char* m_buffer; + static bool m_logEnabled; + static wxString m_logDir; + static wxString m_name; + static CTimer m_timer; + + static void writeStatus(const CEthernet& ethernet); +}; + +#endif diff --git a/Common/DExtraHandler.cpp b/Common/DExtraHandler.cpp new file mode 100644 index 0000000..2ec830e --- /dev/null +++ b/Common/DExtraHandler.cpp @@ -0,0 +1,1004 @@ +/* + * 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 + * 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 "DExtraHandler.h" +#include "DStarDefines.h" +#include "Utils.h" + +unsigned int CDExtraHandler::m_maxReflectors = 0U; +unsigned int CDExtraHandler::m_maxDongles = 0U; +CDExtraHandler** CDExtraHandler::m_reflectors = NULL; + +wxString CDExtraHandler::m_callsign; +CDExtraProtocolHandlerPool* CDExtraHandler::m_pool = NULL; +CDExtraProtocolHandler* CDExtraHandler::m_incoming = NULL; + +bool CDExtraHandler::m_stateChange = false; + +CHeaderLogger* CDExtraHandler::m_headerLogger = NULL; + +CCallsignList* CDExtraHandler::m_whiteList = NULL; +CCallsignList* CDExtraHandler::m_blackList = NULL; + + +CDExtraHandler::CDExtraHandler(IReflectorCallback* handler, const wxString& reflector, const wxString& repeater, CDExtraProtocolHandler* protoHandler, const in_addr& address, unsigned int port, DIRECTION direction) : +m_reflector(reflector.Clone()), +m_repeater(repeater.Clone()), +m_handler(protoHandler), +m_yourAddress(address), +m_yourPort(port), +m_direction(direction), +m_linkState(DEXTRA_LINKING), +m_destination(handler), +m_time(), +m_pollTimer(1000U, 10U), +m_pollInactivityTimer(1000U, 60U), +m_tryTimer(1000U, 1U), +m_tryCount(0U), +m_dExtraId(0x00U), +m_dExtraSeq(0x00U), +m_inactivityTimer(1000U, NETWORK_TIMEOUT), +m_header(NULL) +{ + wxASSERT(protoHandler != NULL); + wxASSERT(handler != NULL); + wxASSERT(port > 0U); + + m_pollInactivityTimer.start(); + + m_time = ::time(NULL); + + if (direction == DIR_INCOMING) { + m_pollTimer.start(); + m_stateChange = true; + m_linkState = DEXTRA_LINKED; + } else { + m_linkState = DEXTRA_LINKING; + m_tryTimer.start(); + } +} + +CDExtraHandler::CDExtraHandler(CDExtraProtocolHandler* protoHandler, const wxString& reflector, const in_addr& address, unsigned int port, DIRECTION direction) : +m_reflector(reflector.Clone()), +m_repeater(), +m_handler(protoHandler), +m_yourAddress(address), +m_yourPort(port), +m_direction(direction), +m_linkState(DEXTRA_LINKING), +m_destination(NULL), +m_time(), +m_pollTimer(1000U, 10U), +m_pollInactivityTimer(1000U, 60U), +m_tryTimer(1000U, 1U), +m_tryCount(0U), +m_dExtraId(0x00U), +m_dExtraSeq(0x00U), +m_inactivityTimer(1000U, NETWORK_TIMEOUT), +m_header(NULL) +{ + wxASSERT(protoHandler != NULL); + wxASSERT(port > 0U); + + m_pollInactivityTimer.start(); + + m_time = ::time(NULL); + + if (direction == DIR_INCOMING) { + m_pollTimer.start(); + m_stateChange = true; + m_linkState = DEXTRA_LINKED; + } else { + m_linkState = DEXTRA_LINKING; + m_tryTimer.start(); + } +} + +CDExtraHandler::~CDExtraHandler() +{ + if (m_direction == DIR_OUTGOING) + m_pool->release(m_handler); + + delete m_header; +} + +void CDExtraHandler::initialise(unsigned int maxReflectors) +{ + wxASSERT(maxReflectors > 0U); + + m_maxReflectors = maxReflectors; + + m_reflectors = new CDExtraHandler*[m_maxReflectors]; + for (unsigned int i = 0U; i < m_maxReflectors; i++) + m_reflectors[i] = NULL; +} + +void CDExtraHandler::setCallsign(const wxString& callsign) +{ + m_callsign = callsign.Clone();//clone makes deep copy of string, this adds thread safety to our code ! + m_callsign.resize(LONG_CALLSIGN_LENGTH, ' '); + m_callsign.SetChar(LONG_CALLSIGN_LENGTH - 1U, wxT(' ')); +} + +void CDExtraHandler::setDExtraProtocolIncoming(CDExtraProtocolHandler* incoming) +{ + wxASSERT(incoming != NULL); + + m_incoming = incoming; +} + +void CDExtraHandler::setDExtraProtocolHandlerPool(CDExtraProtocolHandlerPool* pool) +{ + wxASSERT(pool != NULL); + + m_pool = pool; +} + +void CDExtraHandler::setHeaderLogger(CHeaderLogger* logger) +{ + m_headerLogger = logger; +} + +void CDExtraHandler::setMaxDongles(unsigned int maxDongles) +{ + m_maxDongles = maxDongles; +} + +void CDExtraHandler::setWhiteList(CCallsignList* list) +{ + wxASSERT(list != NULL); + + m_whiteList = list; +} + +void CDExtraHandler::setBlackList(CCallsignList* list) +{ + wxASSERT(list != NULL); + + m_blackList = list; +} + +wxString CDExtraHandler::getIncoming(const wxString& callsign) +{ + wxString incoming; + + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDExtraHandler* reflector = m_reflectors[i]; + if (reflector != NULL && reflector->m_direction == DIR_INCOMING && reflector->m_repeater.IsSameAs(callsign)) { + incoming.Append(reflector->m_reflector); + incoming.Append(wxT(" ")); + } + } + + return incoming; +} + +void CDExtraHandler::getInfo(IReflectorCallback* handler, CRemoteRepeaterData& data) +{ + wxASSERT(handler != NULL); + + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDExtraHandler* reflector = m_reflectors[i]; + if (reflector != NULL) { + if (reflector->m_destination == handler) { + if (reflector->m_direction == DIR_INCOMING && reflector->m_repeater.IsEmpty()) { + if (reflector->m_linkState != DEXTRA_UNLINKING) + data.addLink(reflector->m_reflector, PROTO_DEXTRA, reflector->m_linkState == DEXTRA_LINKED, DIR_INCOMING, true); + } else { + if (reflector->m_linkState != DEXTRA_UNLINKING) + data.addLink(reflector->m_reflector, PROTO_DEXTRA, reflector->m_linkState == DEXTRA_LINKED, reflector->m_direction, false); + } + } + } + } +} + +wxString CDExtraHandler::getDongles() +{ + wxString dongles; + + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDExtraHandler* reflector = m_reflectors[i]; + if (reflector != NULL && reflector->m_direction == DIR_INCOMING && reflector->m_repeater.IsEmpty()) { + dongles.Append(wxT("X:")); + dongles.Append(reflector->m_reflector); + dongles.Append(wxT(" ")); + } + } + + return dongles; +} + +void CDExtraHandler::process(CHeaderData& header) +{ + in_addr yourAddress = header.getYourAddress(); + unsigned int yourPort = header.getYourPort(); + + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDExtraHandler* reflector = m_reflectors[i]; + if (reflector != NULL) { + if (reflector->m_yourAddress.s_addr == yourAddress.s_addr && + reflector->m_yourPort == yourPort) + reflector->processInt(header); + } + } +} + +void CDExtraHandler::process(CAMBEData& data) +{ + in_addr yourAddress = data.getYourAddress(); + unsigned int yourPort = data.getYourPort(); + + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDExtraHandler* reflector = m_reflectors[i]; + if (reflector != NULL) { + if (reflector->m_yourAddress.s_addr == yourAddress.s_addr && + reflector->m_yourPort == yourPort) + reflector->processInt(data); + } + } +} + +void CDExtraHandler::process(const CPollData& poll) +{ + bool found = false; + + wxString reflector = poll.getData1(); + in_addr yourAddress = poll.getYourAddress(); + unsigned int yourPort = poll.getYourPort(); + + // Check to see if we already have a link + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + if (m_reflectors[i] != NULL) { + if (m_reflectors[i]->m_reflector.Left(LONG_CALLSIGN_LENGTH - 1U).IsSameAs(reflector.Left(LONG_CALLSIGN_LENGTH - 1U)) && + m_reflectors[i]->m_yourAddress.s_addr == yourAddress.s_addr && + m_reflectors[i]->m_yourPort == yourPort && + m_reflectors[i]->m_linkState == DEXTRA_LINKED) { + m_reflectors[i]->m_pollInactivityTimer.start(); + found = true; + } + } + } + + if (found) + return; + + // A repeater poll arriving here is an error + if (!poll.isDongle()) + return; + + // Check to see if we are allowed to accept it + unsigned int count = 0U; + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + if (m_reflectors[i] != NULL && + m_reflectors[i]->m_direction == DIR_INCOMING && + m_reflectors[i]->m_repeater.IsEmpty()) + count++; + } + + if (count >= m_maxDongles) + return; + + // An unmatched poll indicates the need for a new entry + wxLogMessage(wxT("New incoming DExtra Dongle from %s"), reflector.c_str()); + + CDExtraHandler* handler = new CDExtraHandler(m_incoming, reflector, yourAddress, yourPort, DIR_INCOMING); + + found = false; + + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + if (m_reflectors[i] == NULL) { + m_reflectors[i] = handler; + found = true; + break; + } + } + + if (found) { + // Return the poll + CPollData poll(m_callsign, yourAddress, yourPort); + m_incoming->writePoll(poll); + } else { + wxLogError(wxT("No space to add new DExtra Dongle, ignoring")); + delete handler; + } +} + +void CDExtraHandler::process(CConnectData& connect) +{ + CD_TYPE type = connect.getType(); + + if (type == CT_ACK || type == CT_NAK || type == CT_UNLINK) { + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + if (m_reflectors[i] != NULL) { + bool res = m_reflectors[i]->processInt(connect, type); + if (res) { + delete m_reflectors[i]; + m_reflectors[i] = NULL; + } + } + } + + return; + } + + // else if type == CT_LINK1 or type == CT_LINK2 + in_addr yourAddress = connect.getYourAddress(); + unsigned int yourPort = connect.getYourPort(); + + wxString repeaterCallsign = connect.getRepeater(); + + wxChar band = connect.getReflector().GetChar(LONG_CALLSIGN_LENGTH - 1U); + + wxString reflectorCallsign = m_callsign; + reflectorCallsign.SetChar(LONG_CALLSIGN_LENGTH - 1U, band); + + // Check that it isn't a duplicate + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + if (m_reflectors[i] != NULL) { + if (m_reflectors[i]->m_direction == DIR_INCOMING && + m_reflectors[i]->m_yourAddress.s_addr == yourAddress.s_addr && + m_reflectors[i]->m_yourPort == yourPort && + m_reflectors[i]->m_repeater.IsSameAs(reflectorCallsign) && + m_reflectors[i]->m_reflector.IsSameAs(repeaterCallsign)) + return; + } + } + + // Check the validity of our repeater callsign + IReflectorCallback* handler = CRepeaterHandler::findDVRepeater(reflectorCallsign); + if (handler == NULL) { + wxLogMessage(wxT("DExtra connect to unknown reflector %s from %s"), reflectorCallsign.c_str(), repeaterCallsign.c_str()); + CConnectData reply(repeaterCallsign, reflectorCallsign, CT_NAK, yourAddress, yourPort); + m_incoming->writeConnect(reply); + return; + } + + // A new connect packet indicates the need for a new entry + wxLogMessage(wxT("New incoming DExtra link to %s from %s"), reflectorCallsign.c_str(), repeaterCallsign.c_str()); + + CDExtraHandler* dextra = new CDExtraHandler(handler, repeaterCallsign, reflectorCallsign, m_incoming, yourAddress, yourPort, DIR_INCOMING); + + bool found = false; + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + if (m_reflectors[i] == NULL) { + m_reflectors[i] = dextra; + found = true; + break; + } + } + + if (found) { + CConnectData reply(repeaterCallsign, reflectorCallsign, CT_ACK, yourAddress, yourPort); + m_incoming->writeConnect(reply); + + wxString callsign = repeaterCallsign; + callsign.SetChar(LONG_CALLSIGN_LENGTH - 1U, wxT(' ')); + CPollData poll(callsign, yourAddress, yourPort); + m_incoming->writePoll(poll); + } else { + CConnectData reply(repeaterCallsign, reflectorCallsign, CT_NAK, yourAddress, yourPort); + m_incoming->writeConnect(reply); + + wxLogError(wxT("No space to add new DExtra repeater, ignoring")); + delete dextra; + } +} + +void CDExtraHandler::link(IReflectorCallback* handler, const wxString& repeater, const wxString &gateway, const in_addr& address) +{ + CDExtraProtocolHandler* protoHandler = m_pool->getHandler(); + if (protoHandler == NULL) + return; + + CDExtraHandler* dextra = new CDExtraHandler(handler, gateway, repeater, protoHandler, address, DEXTRA_PORT, DIR_OUTGOING); + + bool found = false; + + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + if (m_reflectors[i] == NULL) { + m_reflectors[i] = dextra; + found = true; + break; + } + } + + if (found) { + CConnectData reply(repeater, gateway, CT_LINK1, address, DEXTRA_PORT); + protoHandler->writeConnect(reply); + } else { + wxLogError(wxT("No space to add new DExtra link, ignoring")); + delete dextra; + } +} + +void CDExtraHandler::unlink(IReflectorCallback* handler, const wxString& callsign, bool exclude) +{ + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDExtraHandler* reflector = m_reflectors[i]; + + if (reflector != NULL) { + bool found = false; + + if (exclude) { + if (reflector->m_direction == DIR_OUTGOING && reflector->m_destination == handler && !reflector->m_reflector.IsSameAs(callsign)) { + wxLogMessage(wxT("Removing outgoing DExtra link %s, %s"), reflector->m_repeater.c_str(), reflector->m_reflector.c_str()); + + if (reflector->m_linkState == DEXTRA_LINKING || reflector->m_linkState == DEXTRA_LINKED) { + CConnectData connect(reflector->m_repeater, reflector->m_yourAddress, reflector->m_yourPort); + reflector->m_handler->writeConnect(connect); + + reflector->m_linkState = DEXTRA_UNLINKING; + + reflector->m_destination->linkFailed(DP_DEXTRA, reflector->m_reflector, false); + } + + found = true; + } + } else { + if (reflector->m_destination == handler && reflector->m_reflector.IsSameAs(callsign)) { + wxLogMessage(wxT("Removing DExtra link %s, %s"), reflector->m_repeater.c_str(), reflector->m_reflector.c_str()); + + if (reflector->m_linkState == DEXTRA_LINKING || reflector->m_linkState == DEXTRA_LINKED) { + CConnectData connect(reflector->m_repeater, reflector->m_yourAddress, reflector->m_yourPort); + reflector->m_handler->writeConnect(connect); + + reflector->m_linkState = DEXTRA_UNLINKING; + + reflector->m_destination->linkFailed(DP_DEXTRA, reflector->m_reflector, false); + } + + found = true; + } + } + + // If an active link with incoming traffic, send an EOT to the repeater + if (found) { + if (reflector->m_dExtraId != 0x00U) { + unsigned int seq = reflector->m_dExtraSeq + 1U; + if (seq == 21U) + seq = 0U; + + CAMBEData data; + data.setData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES); + data.setSeq(seq); + data.setEnd(true); + data.setId(reflector->m_dExtraId); + + reflector->m_destination->process(data, reflector->m_direction, AS_DEXTRA); + } + + m_stateChange = true; + + delete m_reflectors[i]; + m_reflectors[i] = NULL; + } + } + } +} + +void CDExtraHandler::unlink() +{ + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDExtraHandler* reflector = m_reflectors[i]; + + if (reflector != NULL) { + if (!reflector->m_repeater.IsEmpty()) { + wxLogMessage(wxT("Unlinking from DExtra reflector %s"), reflector->m_reflector.c_str()); + + CConnectData connect(reflector->m_repeater, reflector->m_yourAddress, reflector->m_yourPort); + reflector->m_handler->writeConnect(connect); + + reflector->m_linkState = DEXTRA_UNLINKING; + } + } + } +} + +void CDExtraHandler::writeHeader(IReflectorCallback* handler, CHeaderData& header, DIRECTION direction) +{ + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + if (m_reflectors[i] != NULL) + m_reflectors[i]->writeHeaderInt(handler, header, direction); + } +} + +void CDExtraHandler::writeAMBE(IReflectorCallback* handler, CAMBEData& data, DIRECTION direction) +{ + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + if (m_reflectors[i] != NULL) + m_reflectors[i]->writeAMBEInt(handler, data, direction); + } +} + +void CDExtraHandler::gatewayUpdate(const wxString& reflector, const wxString& address) +{ + wxString gateway = reflector; + gateway.Truncate(LONG_CALLSIGN_LENGTH - 1U); + + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDExtraHandler* reflector = m_reflectors[i]; + if (reflector != NULL) { + if (reflector->m_reflector.Left(LONG_CALLSIGN_LENGTH - 1U).IsSameAs(gateway)) { + if (!address.IsEmpty()) { + // A new address, change the value + wxLogMessage(wxT("Changing IP address of DExtra gateway or reflector %s to %s"), reflector->m_reflector.c_str(), address.c_str()); + reflector->m_yourAddress.s_addr = ::inet_addr(address.mb_str()); + } else { + wxLogMessage(wxT("IP address for DExtra gateway or reflector %s has been removed"), reflector->m_reflector.c_str()); + + // No address, this probably shouldn't happen.... + if (reflector->m_direction == DIR_OUTGOING && reflector->m_destination != NULL) + reflector->m_destination->linkFailed(DP_DEXTRA, reflector->m_reflector, false); + + m_stateChange = true; + + delete m_reflectors[i]; + m_reflectors[i] = NULL; + } + } + } + } +} + +void CDExtraHandler::clock(unsigned int ms) +{ + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + if (m_reflectors[i] != NULL) { + bool ret = m_reflectors[i]->clockInt(ms); + if (ret) { + delete m_reflectors[i]; + m_reflectors[i] = NULL; + } + } + } +} + +void CDExtraHandler::finalise() +{ + for (unsigned int i = 0U; i < m_maxReflectors; i++) + delete m_reflectors[i]; + + delete[] m_reflectors; +} + +void CDExtraHandler::processInt(CHeaderData& header) +{ + wxString my = header.getMyCall1(); + wxString rpt1 = header.getRptCall1(); + wxString rpt2 = header.getRptCall2(); + unsigned int id = header.getId(); + + if (m_whiteList != NULL) { + bool res = m_whiteList->isInList(my); + if (!res) { + wxLogMessage(wxT("%s rejected from DExtra as not found in the white list"), my.c_str()); + m_dExtraId = 0x00U; + return; + } + } + + if (m_blackList != NULL) { + bool res = m_blackList->isInList(my); + if (res) { + wxLogMessage(wxT("%s rejected from DExtra as found in the black list"), my.c_str()); + m_dExtraId = 0x00U; + return; + } + } + + if (m_linkState != DEXTRA_LINKED) + return; + + switch (m_direction) { + case DIR_OUTGOING: { + // Always a repeater connection + if (!m_reflector.IsSameAs(rpt2) && !m_reflector.IsSameAs(rpt1)) + return; + + // If we're already processing, ignore the new header + if (m_dExtraId != 0x00U) + return; + + // Write to Header.log if it's enabled + if (m_headerLogger != NULL) + m_headerLogger->write(wxT("DExtra"), header); + + m_dExtraId = id; + m_dExtraSeq = 0x00U; + m_inactivityTimer.start(); + + delete m_header; + + m_header = new CHeaderData(header); + m_header->setCQCQCQ(); + m_header->setFlags(0x00U, 0x00U, 0x00U); + + m_destination->process(*m_header, m_direction, AS_DEXTRA); + } + break; + + case DIR_INCOMING: + if (!m_repeater.IsEmpty()) { + // A repeater connection + if (!m_repeater.IsSameAs(rpt2) && !m_repeater.IsSameAs(rpt1)) + return; + + // If we're already processing, ignore the new header + if (m_dExtraId != 0x00U) + return; + + // Write to Header.log if it's enabled + if (m_headerLogger != NULL) + m_headerLogger->write(wxT("DExtra"), header); + + m_dExtraId = id; + m_dExtraSeq = 0x00U; + m_inactivityTimer.start(); + + delete m_header; + + m_header = new CHeaderData(header); + m_header->setCQCQCQ(); + m_header->setFlags(0x00U, 0x00U, 0x00U); + + m_destination->process(*m_header, m_direction, AS_DEXTRA); + } else { + // A Dongle connection + // Check the destination callsign + m_destination = CRepeaterHandler::findDVRepeater(rpt2); + if (m_destination == NULL) { + m_destination = CRepeaterHandler::findDVRepeater(rpt1); + if (m_destination == NULL) + return; + } + + // If we're already processing, ignore the new header + if (m_dExtraId != 0x00U) + return; + + // Write to Header.log if it's enabled + if (m_headerLogger != NULL) + m_headerLogger->write(wxT("DExtra"), header); + + m_dExtraId = id; + m_dExtraSeq = 0x00U; + m_inactivityTimer.start(); + + delete m_header; + + m_header = new CHeaderData(header); + m_header->setCQCQCQ(); + m_header->setFlags(0x00U, 0x00U, 0x00U); + + m_destination->process(*m_header, m_direction, AS_DEXTRA); + } + break; + } +} + +void CDExtraHandler::processInt(CAMBEData& data) +{ + if (m_linkState != DEXTRA_LINKED) + return; + + if (m_dExtraId != data.getId()) + return; + + m_pollInactivityTimer.start(); + m_inactivityTimer.start(); + + m_dExtraSeq = data.getSeq(); + + // Send the header every 21 frames, if we have it + if (m_dExtraSeq == 0U && m_header != NULL) + m_destination->process(*m_header, m_direction, AS_DUP); + + // Copy the data to ensure it remains unchanged + CAMBEData temp(data); + + m_destination->process(temp, m_direction, AS_DEXTRA); + + if (temp.isEnd()) { + delete m_header; + m_header = NULL; + + m_dExtraId = 0x00U; + m_dExtraSeq = 0x00U; + + m_inactivityTimer.stop(); + } +} + +bool CDExtraHandler::processInt(CConnectData& connect, CD_TYPE type) +{ + in_addr yourAddress = connect.getYourAddress(); + unsigned int yourPort = connect.getYourPort(); + wxString repeater = connect.getRepeater(); + + if (m_yourAddress.s_addr != yourAddress.s_addr || m_yourPort != yourPort) + return false; + + switch (type) { + case CT_ACK: + if (!m_repeater.IsSameAs(repeater)) + return false; + + if (m_linkState == DEXTRA_LINKING) { + wxLogMessage(wxT("DExtra ACK message received from %s"), m_reflector.c_str()); + + if (m_direction == DIR_OUTGOING && m_destination != NULL) + m_destination->linkUp(DP_DEXTRA, m_reflector); + + m_tryTimer.stop(); + m_pollTimer.start(); + m_stateChange = true; + m_linkState = DEXTRA_LINKED; + } + + return false; + + case CT_NAK: + if (!m_repeater.IsSameAs(repeater)) + return false; + + if (m_linkState == DEXTRA_LINKING) { + wxLogMessage(wxT("DExtra NAK message received from %s"), m_reflector.c_str()); + + if (m_direction == DIR_OUTGOING && m_destination != NULL) + m_destination->linkRefused(DP_DEXTRA, m_reflector); + + return true; + } + + return false; + + case CT_UNLINK: + if (!m_reflector.IsSameAs(repeater)) + return false; + + if (m_linkState == DEXTRA_LINKED) { + wxLogMessage(wxT("DExtra disconnect message received from %s"), m_reflector.c_str()); + + if (m_direction == DIR_OUTGOING && m_destination != NULL) + m_destination->linkFailed(DP_DEXTRA, m_reflector, false); + + m_stateChange = true; + } + + return true; + + default: + return false; + } +} + +bool CDExtraHandler::clockInt(unsigned int ms) +{ + m_tryTimer.clock(ms); + m_pollTimer.clock(ms); + m_inactivityTimer.clock(ms); + m_pollInactivityTimer.clock(ms); + + if (m_pollInactivityTimer.isRunning() && m_pollInactivityTimer.hasExpired()) { + m_pollInactivityTimer.start(); + + delete m_header; + m_header = NULL; + + m_stateChange = true; + m_dExtraId = 0x00U; + m_dExtraSeq = 0x00U; + + switch (m_linkState) { + case DEXTRA_LINKING: + wxLogMessage(wxT("DExtra link to %s has failed to connect"), m_reflector.c_str()); + break; + case DEXTRA_LINKED: + wxLogMessage(wxT("DExtra link to %s has failed (poll inactivity)"), m_reflector.c_str()); + break; + case DEXTRA_UNLINKING: + wxLogMessage(wxT("DExtra link to %s has failed to disconnect cleanly"), m_reflector.c_str()); + break; + default: + break; + } + + if (m_direction == DIR_OUTGOING) { + bool reconnect = m_destination->linkFailed(DP_DEXTRA, m_reflector, true); + if (reconnect) { + CConnectData reply(m_repeater, m_reflector, CT_LINK1, m_yourAddress, m_yourPort); + m_handler->writeConnect(reply); + m_linkState = DEXTRA_LINKING; + m_tryTimer.start(1U); + m_tryCount = 0U; + return false; + } + } + + return true; + } + + if (m_pollTimer.isRunning() && m_pollTimer.hasExpired()) { + if (m_linkState == DEXTRA_LINKED) { + if (!m_repeater.IsEmpty()) { + wxString callsign = m_repeater; + callsign.SetChar(LONG_CALLSIGN_LENGTH - 1U, wxT(' ')); + CPollData poll(callsign, m_yourAddress, m_yourPort); + m_handler->writePoll(poll); + } else { + CPollData poll(m_callsign, m_yourAddress, m_yourPort); + m_handler->writePoll(poll); + } + } + + m_pollTimer.start(); + } + + if (m_inactivityTimer.isRunning() && m_inactivityTimer.hasExpired()) { + delete m_header; + m_header = NULL; + + m_dExtraId = 0x00U; + m_dExtraSeq = 0x00U; + + m_inactivityTimer.stop(); + } + + if (m_linkState == DEXTRA_LINKING) { + if (m_tryTimer.isRunning() && m_tryTimer.hasExpired()) { + CConnectData reply(m_repeater, m_reflector, CT_LINK1, m_yourAddress, m_yourPort); + m_handler->writeConnect(reply); + + unsigned int timeout = calcBackoff(); + m_tryTimer.start(timeout); + } + } + + return false; +} + +void CDExtraHandler::writeHeaderInt(IReflectorCallback* handler, CHeaderData& header, DIRECTION direction) +{ + if (m_linkState != DEXTRA_LINKED) + return; + + // Is it link in the right direction + if (m_direction != direction) + return; + + // Already in use? + if (m_dExtraId != 0x00) + return; + + switch (m_direction) { + case DIR_OUTGOING: + if (m_destination == handler) { + header.setDestination(m_yourAddress, m_yourPort); + m_handler->writeHeader(header); + } + break; + + case DIR_INCOMING: + if (m_repeater.IsEmpty() || m_destination == handler) { + header.setDestination(m_yourAddress, m_yourPort); + m_handler->writeHeader(header); + } + break; + } +} + +void CDExtraHandler::writeAMBEInt(IReflectorCallback* handler, CAMBEData& data, DIRECTION direction) +{ + if (m_linkState != DEXTRA_LINKED) + return; + + // Is it link in the right direction + if (m_direction != direction) + return; + + // Already in use? + if (m_dExtraId != 0x00) + return; + + switch (m_direction) { + case DIR_OUTGOING: + if (m_destination == handler) { + data.setDestination(m_yourAddress, m_yourPort); + m_handler->writeAMBE(data); + } + break; + + case DIR_INCOMING: + if (m_repeater.IsEmpty() || m_destination == handler) { + data.setDestination(m_yourAddress, m_yourPort); + m_handler->writeAMBE(data); + } + break; + } +} + +bool CDExtraHandler::stateChange() +{ + bool stateChange = m_stateChange; + + m_stateChange = false; + + return stateChange; +} + +void CDExtraHandler::writeStatus(wxFFile& file) +{ + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDExtraHandler* reflector = m_reflectors[i]; + if (reflector != NULL) { + wxString text; + + struct tm* tm = ::gmtime(&reflector->m_time); + + switch (reflector->m_direction) { + case DIR_OUTGOING: + if (reflector->m_linkState == DEXTRA_LINKED) { + text.Printf(wxT("%04d-%02d-%02d %02d:%02d:%02d: DExtra link - Type: Repeater Rptr: %s Refl: %s Dir: Outgoing\n"), + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, + reflector->m_repeater.c_str(), reflector->m_reflector.c_str()); + + file.Write(text); + } + break; + + case DIR_INCOMING: + if (reflector->m_linkState == DEXTRA_LINKED) { + if (reflector->m_repeater.IsEmpty()) + text.Printf(wxT("%04d-%02d-%02d %02d:%02d:%02d: DExtra link - Type: Dongle User: %s Dir: Incoming\n"), + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, + reflector->m_reflector.c_str()); + else + text.Printf(wxT("%04d-%02d-%02d %02d:%02d:%02d: DExtra link - Type: Repeater Rptr: %s Refl: %s Dir: Incoming\n"), + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, + reflector->m_repeater.c_str(), reflector->m_reflector.c_str()); + + file.Write(text); + } + break; + } + + } + } +} + +unsigned int CDExtraHandler::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; +} diff --git a/Common/DExtraHandler.h b/Common/DExtraHandler.h new file mode 100644 index 0000000..8d11676 --- /dev/null +++ b/Common/DExtraHandler.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2010-2013,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 + * 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 DExtraHander_H +#define DExtraHander_H + +#include "DExtraProtocolHandlerPool.h" +#include "ReflectorCallback.h" +#include "DStarDefines.h" +#include "HeaderLogger.h" +#include "CallsignList.h" +#include "ConnectData.h" +#include "HeaderData.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 DEXTRA_STATE { + DEXTRA_LINKING, + DEXTRA_LINKED, + DEXTRA_UNLINKING +}; + +class CDExtraHandler { +public: + static void initialise(unsigned int maxReflectors); + + static void setCallsign(const wxString& callsign); + static void setDExtraProtocolHandlerPool(CDExtraProtocolHandlerPool* pool); + static void setDExtraProtocolIncoming(CDExtraProtocolHandler* handler); + static void setHeaderLogger(CHeaderLogger* logger); + static void setMaxDongles(unsigned int maxDongles); + + static void link(IReflectorCallback* handler, const wxString& repeater, const wxString& reflector, const in_addr& address); + static void unlink(IReflectorCallback* handler, const wxString& reflector = wxEmptyString, bool exclude = true); + static void unlink(); + + static void writeHeader(IReflectorCallback* handler, CHeaderData& header, DIRECTION direction); + static void writeAMBE(IReflectorCallback* handler, CAMBEData& data, DIRECTION direction); + + static void process(CHeaderData& header); + static void process(CAMBEData& data); + static void process(const CPollData& poll); + static void process(CConnectData& connect); + + static void gatewayUpdate(const wxString& reflector, const wxString& address); + static void clock(unsigned int ms); + + static bool stateChange(); + static void writeStatus(wxFFile& file); + + static void setWhiteList(CCallsignList* list); + static void setBlackList(CCallsignList* list); + + static void finalise(); + + static void getInfo(IReflectorCallback* handler, CRemoteRepeaterData& data); + + static wxString getIncoming(const wxString& callsign); + static wxString getDongles(); + +protected: + CDExtraHandler(IReflectorCallback* handler, const wxString& reflector, const wxString& repeater, CDExtraProtocolHandler* protoHandler, const in_addr& address, unsigned int port, DIRECTION direction); + CDExtraHandler(CDExtraProtocolHandler* protoHandler, const wxString& reflector, const in_addr& address, unsigned int port, DIRECTION direction); + ~CDExtraHandler(); + + void processInt(CHeaderData& header); + void processInt(CAMBEData& data); + bool processInt(CConnectData& connect, CD_TYPE type); + + void writeHeaderInt(IReflectorCallback* handler, CHeaderData& header, DIRECTION direction); + void writeAMBEInt(IReflectorCallback* handler, CAMBEData& data, DIRECTION direction); + + bool clockInt(unsigned int ms); + +private: + static unsigned int m_maxReflectors; + static unsigned int m_maxDongles; + static CDExtraHandler** m_reflectors; + + static wxString m_callsign; + static CDExtraProtocolHandlerPool* m_pool; + static CDExtraProtocolHandler* m_incoming; + + static bool m_stateChange; + + static CHeaderLogger* m_headerLogger; + + static CCallsignList* m_whiteList; + static CCallsignList* m_blackList; + + wxString m_reflector; + wxString m_repeater; + CDExtraProtocolHandler* m_handler; + in_addr m_yourAddress; + unsigned int m_yourPort; + DIRECTION m_direction; + DEXTRA_STATE m_linkState; + IReflectorCallback* m_destination; + time_t m_time; + CTimer m_pollTimer; + CTimer m_pollInactivityTimer; + CTimer m_tryTimer; + unsigned int m_tryCount; + unsigned int m_dExtraId; + unsigned int m_dExtraSeq; + CTimer m_inactivityTimer; + CHeaderData* m_header; + + unsigned int calcBackoff(); +}; + +#endif diff --git a/Common/DExtraProtocolHandler.cpp b/Common/DExtraProtocolHandler.cpp new file mode 100644 index 0000000..7d59bf0 --- /dev/null +++ b/Common/DExtraProtocolHandler.cpp @@ -0,0 +1,228 @@ +/* + * 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 + * 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 "DExtraProtocolHandler.h" + +#include "DStarDefines.h" +#include "Utils.h" + +// #define DUMP_TX + +const unsigned int BUFFER_LENGTH = 1000U; + +CDExtraProtocolHandler::CDExtraProtocolHandler(unsigned int port, const wxString& addr) : +m_socket(addr, port), +m_type(DE_NONE), +m_buffer(NULL), +m_length(0U), +m_yourAddress(), +m_yourPort(0U), +m_myPort(port) +{ + m_buffer = new unsigned char[BUFFER_LENGTH]; +} + +CDExtraProtocolHandler::~CDExtraProtocolHandler() +{ + delete[] m_buffer; +} + +bool CDExtraProtocolHandler::open() +{ + return m_socket.open(); +} + +unsigned int CDExtraProtocolHandler::getPort() const +{ + return m_myPort; +} + +bool CDExtraProtocolHandler::writeHeader(const CHeaderData& header) +{ + unsigned char buffer[60U]; + unsigned int length = header.getDExtraData(buffer, 60U, true); + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Header"), buffer, length); +#endif + + for (unsigned int i = 0U; i < 5U; i++) { + bool res = m_socket.write(buffer, length, header.getYourAddress(), header.getYourPort()); + if (!res) + return false; + } + + return true; +} + +bool CDExtraProtocolHandler::writeAMBE(const CAMBEData& data) +{ + unsigned char buffer[40U]; + unsigned int length = data.getDExtraData(buffer, 40U); + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Data"), buffer, length); +#endif + + return m_socket.write(buffer, length, data.getYourAddress(), data.getYourPort()); +} + +bool CDExtraProtocolHandler::writePoll(const CPollData& poll) +{ + unsigned char buffer[20U]; + unsigned int length = poll.getDExtraData(buffer, 20U); + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Poll"), buffer, length); +#endif + + return m_socket.write(buffer, length, poll.getYourAddress(), poll.getYourPort()); +} + +bool CDExtraProtocolHandler::writeConnect(const CConnectData& connect) +{ + unsigned char buffer[20U]; + unsigned int length = connect.getDExtraData(buffer, 20U); + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Connect"), buffer, length); +#endif + + for (unsigned int i = 0U; i < 2U; i++) { + bool res = m_socket.write(buffer, length, connect.getYourAddress(), connect.getYourPort()); + if (!res) + return false; + } + + return true; +} + +DEXTRA_TYPE CDExtraProtocolHandler::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 CDExtraProtocolHandler::readPackets() +{ + m_type = DE_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] != 'D' || m_buffer[1] != 'S' || m_buffer[2] != 'V' || m_buffer[3] != 'T') { + switch (m_length) { + case 9U: + m_type = DE_POLL; + return false; + case 11U: + case 14U: + m_type = DE_CONNECT; + return false; + default: + return true; + } + } else { + // Header or data packet type? + if (m_buffer[14] == 0x80) + m_type = DE_HEADER; + else + m_type = DE_AMBE; + + return false; + } +} + +CHeaderData* CDExtraProtocolHandler::readHeader() +{ + if (m_type != DE_HEADER) + return NULL; + + CHeaderData* header = new CHeaderData; + + // DExtra checksums are unreliable + bool res = header->setDExtraData(m_buffer, m_length, false, m_yourAddress, m_yourPort, m_myPort); + if (!res) { + delete header; + return NULL; + } + + return header; +} + +CAMBEData* CDExtraProtocolHandler::readAMBE() +{ + if (m_type != DE_AMBE) + return NULL; + + CAMBEData* data = new CAMBEData; + + bool res = data->setDExtraData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort); + if (!res) { + delete data; + return NULL; + } + + return data; +} + +CPollData* CDExtraProtocolHandler::readPoll() +{ + if (m_type != DE_POLL) + return NULL; + + CPollData* poll = new CPollData; + + bool res = poll->setDExtraData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort); + if (!res) { + delete poll; + return NULL; + } + + return poll; +} + +CConnectData* CDExtraProtocolHandler::readConnect() +{ + if (m_type != DE_CONNECT) + return NULL; + + CConnectData* connect = new CConnectData; + + bool res = connect->setDExtraData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort); + if (!res) { + delete connect; + return NULL; + } + + return connect; +} + +void CDExtraProtocolHandler::close() +{ + m_socket.close(); +} diff --git a/Common/DExtraProtocolHandler.h b/Common/DExtraProtocolHandler.h new file mode 100644 index 0000000..5bd9cb8 --- /dev/null +++ b/Common/DExtraProtocolHandler.h @@ -0,0 +1,79 @@ +/* + * 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 + * 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 DExtraProtocolHandler_H +#define DExtraProtocolHandler_H + +#include "UDPReaderWriter.h" +#include "DStarDefines.h" +#include "ConnectData.h" +#include "HeaderData.h" +#include "AMBEData.h" +#include "PollData.h" + +#if defined(__WINDOWS__) +#include "Inaddr.h" +#else +#include +#endif + +#include + +enum DEXTRA_TYPE { + DE_NONE, + DE_HEADER, + DE_AMBE, + DE_POLL, + DE_CONNECT +}; + +class CDExtraProtocolHandler { +public: + CDExtraProtocolHandler(unsigned int port, const wxString& addr = wxEmptyString); + ~CDExtraProtocolHandler(); + + bool open(); + + unsigned int getPort() const; + + bool writeHeader(const CHeaderData& header); + bool writeAMBE(const CAMBEData& data); + bool writeConnect(const CConnectData& connect); + bool writePoll(const CPollData& poll); + + DEXTRA_TYPE read(); + CHeaderData* readHeader(); + CAMBEData* readAMBE(); + CPollData* readPoll(); + CConnectData* readConnect(); + + void close(); + +private: + CUDPReaderWriter m_socket; + DEXTRA_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/DExtraProtocolHandlerPool.cpp b/Common/DExtraProtocolHandlerPool.cpp new file mode 100644 index 0000000..795d3e3 --- /dev/null +++ b/Common/DExtraProtocolHandlerPool.cpp @@ -0,0 +1,137 @@ +/* + * 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 + * 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 "DExtraProtocolHandlerPool.h" + +CDExtraProtocolHandlerPool::CDExtraProtocolHandlerPool(unsigned int n, unsigned int port, const wxString& addr) : +m_pool(NULL), +m_n(n), +m_index(0U) +{ + wxASSERT(port > 0U); + wxASSERT(n > 0U); + + m_pool = new CDExtraProtocolHandlerEntry[n]; + + for (unsigned int i = 0U; i < n; i++) { + m_pool[i].m_handler = new CDExtraProtocolHandler(port + i, addr); + m_pool[i].m_port = port + i; + m_pool[i].m_inUse = false; + } + + wxLogMessage(wxT("Allocated UDP ports %u-%u to DExtra"), port, port + n - 1U); +} + +CDExtraProtocolHandlerPool::~CDExtraProtocolHandlerPool() +{ + for (unsigned int i = 0U; i < m_n; i++) + delete m_pool[i].m_handler; + + delete[] m_pool; +} + +bool CDExtraProtocolHandlerPool::open() +{ + for (unsigned int i = 0U; i < m_n; i++) { + bool ret = m_pool[i].m_handler->open(); + if (!ret) + return false; + } + + return true; +} + +CDExtraProtocolHandler* CDExtraProtocolHandlerPool::getHandler(unsigned int port) +{ + if (port == 0U) { + for (unsigned int i = 0U; i < m_n; i++) { + if (!m_pool[i].m_inUse) { + m_pool[i].m_inUse = true; + return m_pool[i].m_handler; + } + } + } else { + for (unsigned int i = 0U; i < m_n; i++) { + if (m_pool[i].m_port == port) { + m_pool[i].m_inUse = true; + return m_pool[i].m_handler; + } + } + } + + wxLogError(wxT("Cannot find a free DExtra port in the pool")); + + return NULL; +} + +void CDExtraProtocolHandlerPool::release(CDExtraProtocolHandler* handler) +{ + wxASSERT(handler != NULL); + + for (unsigned int i = 0U; i < m_n; i++) { + if (m_pool[i].m_handler == handler && m_pool[i].m_inUse) { + m_pool[i].m_inUse = false; + return; + } + } + + wxLogError(wxT("Trying to release an unused DExtra port")); +} + +DEXTRA_TYPE CDExtraProtocolHandlerPool::read() +{ + while (m_index < m_n) { + if (m_pool[m_index].m_inUse) { + DEXTRA_TYPE type = m_pool[m_index].m_handler->read(); + if (type != DE_NONE) + return type; + } + + m_index++; + } + + m_index = 0U; + + return DE_NONE; +} + +CHeaderData* CDExtraProtocolHandlerPool::readHeader() +{ + return m_pool[m_index].m_handler->readHeader(); +} + +CAMBEData* CDExtraProtocolHandlerPool::readAMBE() +{ + return m_pool[m_index].m_handler->readAMBE(); +} + +CPollData* CDExtraProtocolHandlerPool::readPoll() +{ + return m_pool[m_index].m_handler->readPoll(); +} + +CConnectData* CDExtraProtocolHandlerPool::readConnect() +{ + return m_pool[m_index].m_handler->readConnect(); +} + +void CDExtraProtocolHandlerPool::close() +{ + for (unsigned int i = 0U; i < m_n; i++) + m_pool[i].m_handler->close(); +} diff --git a/Common/DExtraProtocolHandlerPool.h b/Common/DExtraProtocolHandlerPool.h new file mode 100644 index 0000000..23733b4 --- /dev/null +++ b/Common/DExtraProtocolHandlerPool.h @@ -0,0 +1,57 @@ +/* + * 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 + * 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 DExtraProtocolHandlerPool_H +#define DExtraProtocolHandlerPool_H + +#include + +#include "DExtraProtocolHandler.h" + +class CDExtraProtocolHandlerEntry { +public: + CDExtraProtocolHandler* m_handler; + unsigned int m_port; + bool m_inUse; +}; + +class CDExtraProtocolHandlerPool { +public: + CDExtraProtocolHandlerPool(unsigned int n, unsigned int port, const wxString& addr = wxEmptyString); + ~CDExtraProtocolHandlerPool(); + + bool open(); + + CDExtraProtocolHandler* getHandler(unsigned int port = 0U); + void release(CDExtraProtocolHandler* handler); + + DEXTRA_TYPE read(); + CHeaderData* readHeader(); + CAMBEData* readAMBE(); + CPollData* readPoll(); + CConnectData* readConnect(); + + void close(); + +private: + CDExtraProtocolHandlerEntry* m_pool; + unsigned int m_n; + unsigned int m_index; +}; + +#endif diff --git a/Common/DPlusAuthenticator.cpp b/Common/DPlusAuthenticator.cpp new file mode 100644 index 0000000..7c124a4 --- /dev/null +++ b/Common/DPlusAuthenticator.cpp @@ -0,0 +1,286 @@ +/* + * 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 + * 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 "DPlusAuthenticator.h" +#include "UDPReaderWriter.h" +#include "DStarDefines.h" +#include "Utils.h" +#include "Defs.h" + +const wxString OPENDSTAR_HOSTNAME = wxT("opendstar.org"); +const unsigned int OPENDSTAR_PORT = 20001U; + +const wxString DUTCHSTAR_HOSTNAME = wxT("dpns.dutch-star.eu"); +const unsigned int DUTCHSTAR_PORT = 20001U; + +const unsigned int TCP_TIMEOUT = 10U; + +CDPlusAuthenticator::CDPlusAuthenticator(const wxString& loginCallsign, const wxString& gatewayCallsign, const wxString& address, CCacheManager* cache) : +wxThread(wxTHREAD_JOINABLE), +m_loginCallsign(loginCallsign), +m_gatewayCallsign(gatewayCallsign), +m_address(address), +m_cache(cache), +m_timer(1U, 6U * 3600U), // 6 hours +m_pollTimer(1U, 60U), // 1 minute +m_killed(false) +{ + wxASSERT(!loginCallsign.IsEmpty()); + wxASSERT(!gatewayCallsign.IsEmpty()); + wxASSERT(cache != NULL); + + m_gatewayCallsign.Truncate(LONG_CALLSIGN_LENGTH - 1U); + + m_loginCallsign.Trim(); + m_gatewayCallsign.Trim(); + + if (m_loginCallsign.IsEmpty()) + m_loginCallsign = m_gatewayCallsign; +} + +CDPlusAuthenticator::~CDPlusAuthenticator() +{ +} + +void CDPlusAuthenticator::start() +{ + Create(); + Run(); +} + +void* CDPlusAuthenticator::Entry() +{ + wxLogMessage(wxT("Starting the D-Plus Authenticator thread")); + + authenticate(m_loginCallsign, OPENDSTAR_HOSTNAME, OPENDSTAR_PORT, '2', true); + authenticate(m_gatewayCallsign, DUTCHSTAR_HOSTNAME, DUTCHSTAR_PORT, 'K', false); + + m_timer.start(); + m_pollTimer.start(); + + try { + while (!m_killed) { + if (m_pollTimer.hasExpired()) { + poll(m_gatewayCallsign, DUTCHSTAR_HOSTNAME, DUTCHSTAR_PORT, 'K'); + m_pollTimer.start(); + } + + if (m_timer.hasExpired()) { + authenticate(m_loginCallsign, OPENDSTAR_HOSTNAME, OPENDSTAR_PORT, '2', true); + authenticate(m_gatewayCallsign, DUTCHSTAR_HOSTNAME, DUTCHSTAR_PORT, 'K', false); + m_timer.start(); + } + + Sleep(1000UL); + + m_timer.clock(); + m_pollTimer.clock(); + } + } + catch (std::exception& e) { + wxString message(e.what(), wxConvLocal); + wxLogError(wxT("Exception raised in the D-Plus Authenticator thread - \"%s\""), message.c_str()); + } + catch (...) { + wxLogError(wxT("Unknown exception raised in the D-Plus Authenticator thread")); + } + + wxLogMessage(wxT("Stopping the D-Plus Authenticator thread")); + + return NULL; +} + +void CDPlusAuthenticator::stop() +{ + m_killed = true; + + Wait(); +} + +bool CDPlusAuthenticator::authenticate(const wxString& callsign, const wxString& hostname, unsigned int port, unsigned char id, bool writeToCache) +{ + CTCPReaderWriterClient socket(hostname, port, m_address); + + bool ret = socket.open(); + if (!ret) + return false; + + unsigned char* buffer = new unsigned char[4096U]; + ::memset(buffer, ' ', 56U); + + buffer[0U] = 0x38U; + buffer[1U] = 0xC0U; + buffer[2U] = 0x01U; + buffer[3U] = 0x00U; + + for (unsigned int i = 0U; i < callsign.Len(); i++) + buffer[i + 4U] = callsign.GetChar(i); + + buffer[12U] = 'D'; + buffer[13U] = 'V'; + buffer[14U] = '0'; + buffer[15U] = '1'; + buffer[16U] = '9'; + buffer[17U] = '9'; + buffer[18U] = '9'; + buffer[19U] = '9'; + + buffer[28U] = 'W'; + buffer[29U] = '7'; + buffer[30U] = 'I'; + buffer[31U] = 'B'; + buffer[32U] = id; + + buffer[40U] = 'D'; + buffer[41U] = 'H'; + buffer[42U] = 'S'; + buffer[43U] = '0'; + buffer[44U] = '2'; + buffer[45U] = '5'; + buffer[46U] = '7'; + + ret = socket.write(buffer, 56U); + if (!ret) { + socket.close(); + delete[] buffer; + return false; + } + + ret = read(socket, buffer + 0U, 2U); + + while (ret) { + unsigned int len = (buffer[1U] & 0x0FU) * 256U + buffer[0U]; + + // Ensure that we get exactly len - 2U bytes from the TCP stream + ret = read(socket, buffer + 2U, len - 2U); + if (!ret) { + wxLogError(wxT("Short read from %s:%u"), hostname.c_str(), port); + break; + } + + if ((buffer[1U] & 0xC0U) != 0xC0U || buffer[2U] != 0x01U) { + wxLogError(wxT("Invalid packet received from %s:%u"), hostname.c_str(), port); + CUtils::dump(wxT("Details:"), buffer, len); + break; + } + + for (unsigned int i = 8U; (i + 25U) < len; i += 26U) { + wxString address = wxString((char*)(buffer + i + 0U), wxConvLocal, 16U); + wxString name = wxString((char*)(buffer + i + 16U), wxConvLocal, LONG_CALLSIGN_LENGTH); + + address.Trim(); + name.Trim(); + + // Get the active flag + bool active = (buffer[i + 25U] & 0x80U) == 0x80U; + + // An empty name or IP address or an inactive gateway/reflector is not written out + if (address.Len() > 0U && name.Len() > 0U && !name.Left(3U).IsSameAs(wxT("XRF")) && active && writeToCache){ + if (name.Left(3U).IsSameAs(wxT("REF"))) + wxLogMessage(wxT("D-Plus: %s\t%s"), name.c_str(), address.c_str()); + + name.Append(wxT(" ")); + name.Truncate(LONG_CALLSIGN_LENGTH - 1U); + name.Append(wxT("G")); + m_cache->updateGateway(name, address, DP_DPLUS, false, true); + } + } + + ret = read(socket, buffer + 0U, 2U); + } + + wxLogMessage(wxT("Registered with %s using callsign %s"), hostname.c_str(), callsign.c_str()); + + socket.close(); + + delete[] buffer; + + return true; +} + +bool CDPlusAuthenticator::poll(const wxString& callsign, const wxString& hostname, unsigned int port, unsigned char id) +{ + CUDPReaderWriter socket(m_address, 0U); + bool ret = socket.open(); + if (!ret) + return false; + + unsigned char buffer[56U]; + ::memset(buffer, ' ', 56U); + + buffer[0U] = 0x38U; + buffer[1U] = 0x20U; + buffer[2U] = 0x01U; + buffer[3U] = 0x01U; + + for (unsigned int i = 0U; i < callsign.Len(); i++) + buffer[i + 4U] = callsign.GetChar(i); + + buffer[12U] = 'D'; + buffer[13U] = 'V'; + buffer[14U] = '0'; + buffer[15U] = '1'; + buffer[16U] = '9'; + buffer[17U] = '9'; + buffer[18U] = '9'; + buffer[19U] = '9'; + + for (unsigned int i = 0U; i < callsign.Len(); i++) + buffer[i + 20U] = callsign.GetChar(i); + + buffer[28U] = 'W'; + buffer[29U] = '7'; + buffer[30U] = 'I'; + buffer[31U] = 'B'; + buffer[32U] = id; + + buffer[40U] = 'D'; + buffer[41U] = 'H'; + buffer[42U] = 'S'; + buffer[43U] = '0'; + buffer[44U] = '2'; + buffer[45U] = '5'; + buffer[46U] = '7'; + + in_addr address = socket.lookup(hostname); + if (address.s_addr == INADDR_NONE) { + socket.close(); + return false; + } + + ret = socket.write(buffer, 56U, address, port); + + socket.close(); + + return ret; +} + +bool CDPlusAuthenticator::read(CTCPReaderWriterClient &socket, unsigned char *buffer, unsigned int len) const +{ + unsigned int offset = 0U; + + do { + int n = socket.read(buffer + offset, len - offset, TCP_TIMEOUT); + if (n < 0) + return false; + + offset += n; + } while ((len - offset) > 0U); + + return true; +} diff --git a/Common/DPlusAuthenticator.h b/Common/DPlusAuthenticator.h new file mode 100644 index 0000000..606ce12 --- /dev/null +++ b/Common/DPlusAuthenticator.h @@ -0,0 +1,59 @@ +/* + * 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 + * 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 DPlusAuthenticator_H +#define DPlusAuthenticator_H + +#include "TCPReaderWriterClient.h" +#include "CacheManager.h" +#include "Timer.h" + +#include + +#if defined(__WINDOWS__) +#include "Inaddr.h" +#else +#include +#endif + +class CDPlusAuthenticator : public wxThread { +public: + CDPlusAuthenticator(const wxString& loginCallsign, const wxString& gatewayCallsign, const wxString& address, CCacheManager* cache); + virtual ~CDPlusAuthenticator(); + + virtual void start(); + + virtual void* Entry(); + + virtual void stop(); + +private: + wxString m_loginCallsign; + wxString m_gatewayCallsign; + wxString m_address; + CCacheManager* m_cache; + CTimer m_timer; + CTimer m_pollTimer; + bool m_killed; + + bool poll(const wxString& callsign, const wxString& hostname, unsigned int port, unsigned char id); + bool authenticate(const wxString& callsign, const wxString& hostname, unsigned int port, unsigned char id, bool writeToCache); + bool read(CTCPReaderWriterClient& socket, unsigned char* buffer, unsigned int len) const; +}; + +#endif diff --git a/Common/DPlusHandler.cpp b/Common/DPlusHandler.cpp new file mode 100644 index 0000000..f0fb631 --- /dev/null +++ b/Common/DPlusHandler.cpp @@ -0,0 +1,953 @@ +/* + * 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 + * 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 "DPlusHandler.h" +#include "DStarDefines.h" +#include "Utils.h" + +#include + +unsigned int CDPlusHandler::m_maxReflectors = 0U; +unsigned int CDPlusHandler::m_maxDongles = 0U; +CDPlusHandler** CDPlusHandler::m_reflectors = NULL; + +wxString CDPlusHandler::m_gatewayCallsign; +wxString CDPlusHandler::m_dplusLogin; +CDPlusProtocolHandlerPool* CDPlusHandler::m_pool = NULL; +CDPlusProtocolHandler* CDPlusHandler::m_incoming = NULL; + +bool CDPlusHandler::m_stateChange = false; + +CDPlusAuthenticator* CDPlusHandler::m_authenticator = NULL; +CHeaderLogger* CDPlusHandler::m_headerLogger = NULL; + +CCallsignList* CDPlusHandler::m_whiteList = NULL; +CCallsignList* CDPlusHandler::m_blackList = NULL; + + +CDPlusHandler::CDPlusHandler(IReflectorCallback* handler, const wxString& repeater, const wxString& reflector, CDPlusProtocolHandler* protoHandler, const in_addr& address, unsigned int port) : +m_repeater(repeater.Clone()), +m_callsign(m_dplusLogin.Clone()), +m_reflector(reflector.Clone()), +m_handler(protoHandler), +m_yourAddress(address), +m_yourPort(port), +m_myPort(0U), +m_direction(DIR_OUTGOING), +m_linkState(DPLUS_LINKING), +m_destination(handler), +m_time(), +m_pollTimer(1000U, 1U), // 1s +m_pollInactivityTimer(1000U, 30U), +m_tryTimer(1000U, 1U), +m_tryCount(0U), +m_dPlusId(0x00U), +m_dPlusSeq(0x00U), +m_inactivityTimer(1000U, NETWORK_TIMEOUT), +m_header(NULL) +{ + wxASSERT(protoHandler != NULL); + wxASSERT(handler != NULL); + wxASSERT(port > 0U); + + m_myPort = protoHandler->getPort(); + + m_pollInactivityTimer.start(); + m_tryTimer.start(); + + m_time = ::time(NULL); + + m_callsign.resize(LONG_CALLSIGN_LENGTH, ' '); + wxChar band = m_repeater.GetChar(LONG_CALLSIGN_LENGTH - 1U); + m_callsign.SetChar(LONG_CALLSIGN_LENGTH - 1U, band); +} + +CDPlusHandler::CDPlusHandler(CDPlusProtocolHandler* protoHandler, const in_addr& address, unsigned int port) : +m_repeater(), +m_callsign(), +m_reflector(), +m_handler(protoHandler), +m_yourAddress(address), +m_yourPort(port), +m_myPort(0U), +m_direction(DIR_INCOMING), +m_linkState(DPLUS_LINKING), +m_destination(NULL), +m_time(), +m_pollTimer(1000U, 1U), // 1s +m_pollInactivityTimer(1000U, 10U), // 10s +m_tryTimer(1000U), +m_tryCount(0U), +m_dPlusId(0x00U), +m_dPlusSeq(0x00U), +m_inactivityTimer(1000U, NETWORK_TIMEOUT), +m_header(NULL) +{ + wxASSERT(protoHandler != NULL); + wxASSERT(port > 0U); + + m_myPort = protoHandler->getPort(); + + m_pollTimer.start(); + m_pollInactivityTimer.start(); + + m_time = ::time(NULL); +} + +CDPlusHandler::~CDPlusHandler() +{ + if (m_direction == DIR_OUTGOING) + m_pool->release(m_handler); + + delete m_header; +} + +void CDPlusHandler::initialise(unsigned int maxReflectors) +{ + wxASSERT(maxReflectors > 0U); + + m_maxReflectors = maxReflectors; + + m_reflectors = new CDPlusHandler*[m_maxReflectors]; + for (unsigned int i = 0U; i < m_maxReflectors; i++) + m_reflectors[i] = NULL; +} + +void CDPlusHandler::startAuthenticator(const wxString& address, CCacheManager* cache) +{ + wxASSERT(cache != NULL); + + m_authenticator = new CDPlusAuthenticator(m_dplusLogin, m_gatewayCallsign, address, cache); + m_authenticator->start(); +} + +void CDPlusHandler::setCallsign(const wxString& callsign) +{ + m_gatewayCallsign = callsign; +} + +void CDPlusHandler::setDPlusProtocolHandlerPool(CDPlusProtocolHandlerPool* pool) +{ + wxASSERT(pool != NULL); + + m_pool = pool; +} + +void CDPlusHandler::setDPlusProtocolIncoming(CDPlusProtocolHandler* handler) +{ + wxASSERT(handler != NULL); + + m_incoming = handler; +} + +void CDPlusHandler::setDPlusLogin(const wxString& dplusLogin) +{ + m_dplusLogin = dplusLogin; +} + +void CDPlusHandler::setHeaderLogger(CHeaderLogger* logger) +{ + m_headerLogger = logger; +} + +void CDPlusHandler::setMaxDongles(unsigned int maxDongles) +{ + m_maxDongles = maxDongles; +} + +void CDPlusHandler::setWhiteList(CCallsignList* list) +{ + wxASSERT(list != NULL); + + m_whiteList = list; +} + +void CDPlusHandler::setBlackList(CCallsignList* list) +{ + wxASSERT(list != NULL); + + m_blackList = list; +} + +void CDPlusHandler::getInfo(IReflectorCallback* handler, CRemoteRepeaterData& data) +{ + wxASSERT(handler != NULL); + + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDPlusHandler* reflector = m_reflectors[i]; + if (reflector != NULL) { + if (reflector->m_destination == handler && reflector->m_linkState != DPLUS_UNLINKING) + data.addLink(reflector->m_reflector, PROTO_DPLUS, reflector->m_linkState == DPLUS_LINKED, reflector->m_direction, true); + } + } +} + +wxString CDPlusHandler::getDongles() +{ + wxString dongles; + + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDPlusHandler* reflector = m_reflectors[i]; + + if (reflector != NULL && reflector->m_direction == DIR_INCOMING) { + dongles.Append(wxT("P:")); + dongles.Append(reflector->m_reflector); + dongles.Append(wxT(" ")); + } + } + + return dongles; +} + +void CDPlusHandler::process(CHeaderData& header) +{ + in_addr yourAddress = header.getYourAddress(); + unsigned int yourPort = header.getYourPort(); + unsigned int myPort = header.getMyPort(); + + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDPlusHandler* reflector = m_reflectors[i]; + + if (reflector != NULL) { + if (reflector->m_yourAddress.s_addr == yourAddress.s_addr && + reflector->m_yourPort == yourPort && + reflector->m_myPort == myPort) { + reflector->processInt(header); + return; + } + } + } +} + +void CDPlusHandler::process(CAMBEData& data) +{ + in_addr yourAddress = data.getYourAddress(); + unsigned int yourPort = data.getYourPort(); + unsigned int myPort = data.getMyPort(); + + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDPlusHandler* reflector = m_reflectors[i]; + + if (reflector != NULL) { + if (reflector->m_yourAddress.s_addr == yourAddress.s_addr && + reflector->m_yourPort == yourPort && + reflector->m_myPort == myPort) { + reflector->processInt(data); + return; + } + } + } +} + +void CDPlusHandler::process(const CPollData& poll) +{ + in_addr yourAddress = poll.getYourAddress(); + unsigned int yourPort = poll.getYourPort(); + unsigned int myPort = poll.getMyPort(); + + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDPlusHandler* reflector = m_reflectors[i]; + + if (reflector != NULL) { + if (reflector->m_yourAddress.s_addr == yourAddress.s_addr && + reflector->m_yourPort == yourPort && + reflector->m_myPort == myPort) { + reflector->m_pollInactivityTimer.start(); + return; + } + } + } + + // If we cannot find an existing link, we ignore the poll + wxLogMessage(wxT("Incoming poll from unknown D-Plus dongle")); +} + +void CDPlusHandler::process(CConnectData& connect) +{ + CD_TYPE type = connect.getType(); + in_addr yourAddress = connect.getYourAddress(); + unsigned int yourPort = connect.getYourPort(); + unsigned int myPort = connect.getMyPort(); + + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDPlusHandler* reflector = m_reflectors[i]; + + if (reflector != NULL) { + if (reflector->m_yourAddress.s_addr == yourAddress.s_addr && + reflector->m_yourPort == yourPort && + reflector->m_myPort == myPort) { + bool res = m_reflectors[i]->processInt(connect, type); + if (res) { + delete m_reflectors[i]; + m_reflectors[i] = NULL; + } + } + } + } + + // Check that it isn't a duplicate + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDPlusHandler* reflector = m_reflectors[i]; + + if (reflector != NULL) { + if (reflector->m_yourAddress.s_addr == yourAddress.s_addr && + reflector->m_yourPort == yourPort && + reflector->m_myPort == myPort) + return; + } + } + + if (type == CT_UNLINK) + return; + + if (type != CT_LINK1) { + wxLogMessage(wxT("Incoming D-Plus message from unknown source")); + return; + } + + // Check to see if we are allowed to accept it + unsigned int count = 0U; + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + if (m_reflectors[i] != NULL && + m_reflectors[i]->m_direction == DIR_INCOMING) + count++; + } + + if (count >= m_maxDongles) + return; + + CDPlusHandler* dplus = new CDPlusHandler(m_incoming, yourAddress, yourPort); + + bool found = false; + + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + if (m_reflectors[i] == NULL) { + m_reflectors[i] = dplus; + found = true; + break; + } + } + + if (found) { + CConnectData connect(CT_LINK1, yourAddress, yourPort); + m_incoming->writeConnect(connect); + } else { + wxLogError(wxT("No space to add new D-Plus dongle, ignoring")); + delete dplus; + } +} + +void CDPlusHandler::link(IReflectorCallback* handler, const wxString& repeater, const wxString &gateway, const in_addr& address) +{ + CDPlusProtocolHandler* protoHandler = m_pool->getHandler(); + if (protoHandler == NULL) + return; + + CDPlusHandler* dplus = new CDPlusHandler(handler, repeater, gateway, protoHandler, address, DPLUS_PORT); + + bool found = false; + + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + if (m_reflectors[i] == NULL) { + m_reflectors[i] = dplus; + found = true; + break; + } + } + + if (found) { + CConnectData connect(CT_LINK1, address, DPLUS_PORT); + protoHandler->writeConnect(connect); + m_stateChange = true; + } else { + wxLogError(wxT("No space to add new D-Plus reflector, ignoring")); + delete dplus; + } +} + +void CDPlusHandler::relink(IReflectorCallback* handler, const wxString &gateway) +{ + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + if (m_reflectors[i] != NULL && m_reflectors[i]->m_direction == DIR_OUTGOING) { + if (m_reflectors[i]->m_destination == handler) { + m_reflectors[i]->m_reflector = gateway; + m_reflectors[i]->m_dPlusId = 0x00U; + m_reflectors[i]->m_dPlusSeq = 0x00U; + m_stateChange = true; + return; + } + } + } +} + +void CDPlusHandler::unlink(IReflectorCallback* handler, const wxString& callsign, bool exclude) +{ + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDPlusHandler* reflector = m_reflectors[i]; + + if (reflector != NULL) { + bool found = false; + + if (exclude) { + if (reflector->m_direction == DIR_OUTGOING && reflector->m_destination == handler && !reflector->m_reflector.IsSameAs(callsign)) { + wxLogMessage(wxT("Removing outgoing D-Plus link %s, %s"), reflector->m_repeater.c_str(), reflector->m_reflector.c_str()); + + if (reflector->m_linkState == DPLUS_LINKING || reflector->m_linkState == DPLUS_LINKED) { + CConnectData connect(CT_UNLINK, reflector->m_yourAddress, DPLUS_PORT); + reflector->m_handler->writeConnect(connect); + reflector->m_handler->writeConnect(connect); + + reflector->m_linkState = DPLUS_UNLINKING; + reflector->m_tryTimer.start(1U); + reflector->m_pollTimer.stop(); + reflector->m_pollInactivityTimer.stop(); + reflector->m_tryCount = 0U; + } + + found = true; + } + } else { + if (reflector->m_destination == handler && reflector->m_reflector.IsSameAs(callsign)) { + wxLogMessage(wxT("Removing D-Plus link %s, %s"), reflector->m_repeater.c_str(), reflector->m_reflector.c_str()); + + if (reflector->m_linkState == DPLUS_LINKING || reflector->m_linkState == DPLUS_LINKED) { + CConnectData connect(CT_UNLINK, reflector->m_yourAddress, DPLUS_PORT); + reflector->m_handler->writeConnect(connect); + reflector->m_handler->writeConnect(connect); + + reflector->m_linkState = DPLUS_UNLINKING; + reflector->m_tryTimer.start(1U); + reflector->m_pollTimer.stop(); + reflector->m_pollInactivityTimer.stop(); + reflector->m_tryCount = 0U; + } + + found = true; + } + } + + // If an active link with incoming traffic, send an EOT to the repeater + if (found) { + if (reflector->m_dPlusId != 0x00U) { + unsigned int seq = reflector->m_dPlusSeq + 1U; + if (seq == 21U) + seq = 0U; + + CAMBEData data; + data.setData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES); + data.setSeq(seq); + data.setEnd(true); + data.setId(reflector->m_dPlusId); + + reflector->m_destination->process(data, reflector->m_direction, AS_DPLUS); + } + + m_stateChange = true; + } + } + } +} + +void CDPlusHandler::unlink() +{ + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDPlusHandler* reflector = m_reflectors[i]; + if (reflector != NULL) { + if (!reflector->m_reflector.IsEmpty()) + wxLogMessage(wxT("Unlinking from D-Plus reflector or dongle %s"), reflector->m_reflector.c_str()); + + CConnectData connect(CT_UNLINK, reflector->m_yourAddress, reflector->m_yourPort); + reflector->m_handler->writeConnect(connect); + reflector->m_handler->writeConnect(connect); + reflector->m_tryTimer.start(1U); + reflector->m_pollTimer.stop(); + reflector->m_pollInactivityTimer.stop(); + reflector->m_tryCount = 0U; + } + } +} + +void CDPlusHandler::writeHeader(IReflectorCallback* handler, CHeaderData& header, DIRECTION direction) +{ + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + if (m_reflectors[i] != NULL) + m_reflectors[i]->writeHeaderInt(handler, header, direction); + } +} + +void CDPlusHandler::writeAMBE(IReflectorCallback* handler, CAMBEData& data, DIRECTION direction) +{ + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + if (m_reflectors[i] != NULL) + m_reflectors[i]->writeAMBEInt(handler, data, direction); + } +} + +void CDPlusHandler::gatewayUpdate(const wxString& gateway, const wxString& address) +{ + wxString gatewayBase = gateway; + gatewayBase.Truncate(LONG_CALLSIGN_LENGTH - 1U); + + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDPlusHandler* reflector = m_reflectors[i]; + if (reflector != NULL) { + if (!reflector->m_reflector.IsEmpty() && reflector->m_reflector.Left(LONG_CALLSIGN_LENGTH - 1U).IsSameAs(gatewayBase)) { + if (!address.IsEmpty()) { + // A new address, change the value + wxLogMessage(wxT("Changing IP address of D-Plus gateway or reflector %s to %s"), gatewayBase.c_str(), address.c_str()); + reflector->m_yourAddress.s_addr = ::inet_addr(address.mb_str()); + } else { + wxLogMessage(wxT("IP address for D-Plus gateway or reflector %s has been removed"), gatewayBase.c_str()); + + // No address, this probably shouldn't happen.... + if (reflector->m_direction == DIR_OUTGOING && reflector->m_destination != NULL) + reflector->m_destination->linkFailed(DP_DPLUS, reflector->m_reflector, false); + + m_stateChange = true; + + delete m_reflectors[i]; + m_reflectors[i] = NULL; + } + } + } + } +} + +void CDPlusHandler::clock(unsigned int ms) +{ + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + if (m_reflectors[i] != NULL) { + bool ret = m_reflectors[i]->clockInt(ms); + if (ret) { + delete m_reflectors[i]; + m_reflectors[i] = NULL; + } + } + } +} + +void CDPlusHandler::finalise() +{ + if (m_authenticator != NULL) + m_authenticator->stop(); + + for (unsigned int i = 0U; i < m_maxReflectors; i++) + delete m_reflectors[i]; + + delete[] m_reflectors; +} + +void CDPlusHandler::processInt(CHeaderData& header) +{ + wxString my = header.getMyCall1(); + wxString rpt1 = header.getRptCall1(); + wxString rpt2 = header.getRptCall2(); + unsigned int id = header.getId(); + + if (m_whiteList != NULL) { + bool res = m_whiteList->isInList(my); + if (!res) { + wxLogMessage(wxT("%s rejected from D-Plus as not found in the white list"), my.c_str()); + m_dPlusId = 0x00U; + return; + } + } + + if (m_blackList != NULL) { + bool res = m_blackList->isInList(my); + if (res) { + wxLogMessage(wxT("%s rejected from D-Plus as found in the black list"), my.c_str()); + m_dPlusId = 0x00U; + return; + } + } + + if (m_linkState != DPLUS_LINKED) + return; + + switch (m_direction) { + case DIR_OUTGOING: + if (m_reflector.IsSameAs(rpt1) || m_reflector.IsSameAs(rpt2)) { + // If we're already processing, ignore the new header + if (m_dPlusId != 0x00U) + return; + + // Write to Header.log if it's enabled + if (m_headerLogger != NULL) + m_headerLogger->write(wxT("DPlus"), header); + + m_dPlusId = id; + m_dPlusSeq = 0x00U; + m_inactivityTimer.start(); + m_pollInactivityTimer.start(); + + delete m_header; + + m_header = new CHeaderData(header); + m_header->setCQCQCQ(); + m_header->setFlags(0x00U, 0x00U, 0x00U); + + m_destination->process(*m_header, m_direction, AS_DPLUS); + } + break; + + case DIR_INCOMING: { + m_destination = CRepeaterHandler::findDVRepeater(rpt1); + if (m_destination == NULL) { + m_destination = CRepeaterHandler::findDVRepeater(rpt2); + if (m_destination == NULL) + return; + } + + if (m_dPlusId != 0x00U) + return; + + // Write to Header.log if it's enabled + if (m_headerLogger != NULL) + m_headerLogger->write(wxT("DPlus"), header); + + m_dPlusId = id; + m_dPlusSeq = 0x00U; + m_inactivityTimer.start(); + m_pollInactivityTimer.start(); + + delete m_header; + + m_header = new CHeaderData(header); + m_header->setCQCQCQ(); + m_header->setFlags(0x00U, 0x00U, 0x00U); + + m_destination->process(*m_header, m_direction, AS_DPLUS); + } + break; + } +} + +void CDPlusHandler::processInt(CAMBEData& data) +{ + unsigned int id = data.getId(); + + if (m_dPlusId != id) + return; + + m_dPlusSeq = data.getSeq(); + + // Send the header every 21 frames, if we have it + if (m_dPlusSeq == 0U && m_header != NULL) + m_destination->process(*m_header, m_direction, AS_DUP); + + m_inactivityTimer.start(); + m_pollInactivityTimer.start(); + + m_destination->process(data, m_direction, AS_DPLUS); + + if (data.isEnd()) { + m_dPlusId = 0x00U; + m_dPlusSeq = 0x00U; + + delete m_header; + m_header = NULL; + + m_inactivityTimer.stop(); + } +} + +bool CDPlusHandler::processInt(CConnectData& connect, CD_TYPE type) +{ + switch (m_direction) { + case DIR_OUTGOING: + switch (type) { + case CT_ACK: + if (m_linkState == DPLUS_LINKING) { + wxLogMessage(wxT("D-Plus ACK message received from %s"), m_reflector.c_str()); + m_destination->linkUp(DP_DPLUS, m_reflector); + m_stateChange = true; + m_linkState = DPLUS_LINKED; + m_tryTimer.stop(); + m_pollTimer.start(); + m_pollInactivityTimer.start(); + } + return false; + + case CT_NAK: + if (m_linkState == DPLUS_LINKING) { + wxLogMessage(wxT("D-Plus NAK message received from %s"), m_reflector.c_str()); + m_destination->linkRefused(DP_DPLUS, m_reflector); + CConnectData reply(CT_UNLINK, connect.getYourAddress(), connect.getYourPort()); + m_handler->writeConnect(reply); + m_tryTimer.stop(); + } + return true; + + case CT_UNLINK: + if (m_linkState == DPLUS_UNLINKING) { + wxLogMessage(wxT("D-Plus disconnect acknowledgement received from %s"), m_reflector.c_str()); + m_destination->linkFailed(DP_DPLUS, m_reflector, false); + m_stateChange = true; + m_tryTimer.stop(); + } + return true; + + case CT_LINK1: { + CConnectData reply(m_dplusLogin, CT_LINK2, connect.getYourAddress(), connect.getYourPort()); + m_handler->writeConnect(reply); + m_tryTimer.stop(); + } + return false; + + default: + return false; + } + break; + + case DIR_INCOMING: + switch (type) { + case CT_LINK2: { + m_reflector = connect.getRepeater(); + wxLogMessage(wxT("D-Plus dongle link to %s has started"), m_reflector.c_str()); + CConnectData reply(CT_ACK, m_yourAddress, m_yourPort); + m_handler->writeConnect(reply); + m_linkState = DPLUS_LINKED; + m_stateChange = true; + } + return false; + + case CT_UNLINK: + if (m_linkState == DPLUS_LINKED) { + wxLogMessage(wxT("D-Plus dongle link to %s has ended (unlinked)"), m_reflector.c_str()); + m_stateChange = true; + m_handler->writeConnect(connect); + } + return true; + + default: + return false; + } + break; + } + + return false; +} + +bool CDPlusHandler::clockInt(unsigned int ms) +{ + m_tryTimer.clock(ms); + m_pollTimer.clock(ms); + m_inactivityTimer.clock(ms); + m_pollInactivityTimer.clock(ms); + + if (m_pollInactivityTimer.isRunning() && m_pollInactivityTimer.hasExpired()) { + m_pollInactivityTimer.start(); + + delete m_header; + m_header = NULL; + + m_stateChange = true; + m_dPlusId = 0x00U; + m_dPlusSeq = 0x00U; + + if (!m_reflector.IsEmpty()) { + switch (m_linkState) { + case DPLUS_LINKING: + wxLogMessage(wxT("D-Plus link to %s has failed to connect"), m_reflector.c_str()); + break; + case DPLUS_LINKED: + wxLogMessage(wxT("D-Plus link to %s has failed (poll inactivity)"), m_reflector.c_str()); + break; + case DPLUS_UNLINKING: + wxLogMessage(wxT("D-Plus link to %s has failed to disconnect cleanly"), m_reflector.c_str()); + break; + default: + break; + } + } + + if (m_direction == DIR_OUTGOING) { + bool reconnect = m_destination->linkFailed(DP_DPLUS, m_reflector, true); + if (reconnect) { + CConnectData connect(CT_LINK1, m_yourAddress, DPLUS_PORT); + m_handler->writeConnect(connect); + m_linkState = DPLUS_LINKING; + m_tryTimer.start(1U); + m_tryCount = 0U; + return false; + } + } + + return true; + } + + if (m_pollTimer.isRunning() && m_pollTimer.hasExpired()) { + CPollData poll(m_yourAddress, m_yourPort); + m_handler->writePoll(poll); + + m_pollTimer.start(); + } + + if (m_tryTimer.isRunning() && m_tryTimer.hasExpired()) { + switch (m_linkState) { + case DPLUS_LINKING: { + CConnectData connect(CT_LINK1, m_yourAddress, DPLUS_PORT); + m_handler->writeConnect(connect); + } + break; + + case DPLUS_UNLINKING: { + CConnectData connect(CT_UNLINK, m_yourAddress, m_yourPort); + m_handler->writeConnect(connect); + m_handler->writeConnect(connect); + } + break; + + default: + break; + } + + unsigned int timeout = calcBackoff(); + m_tryTimer.start(timeout); + } + + if (m_inactivityTimer.isRunning() && m_inactivityTimer.hasExpired()) { + delete m_header; + m_header = NULL; + + m_dPlusId = 0x00U; + m_dPlusSeq = 0x00U; + + m_inactivityTimer.stop(); + } + + return false; +} + +void CDPlusHandler::writeHeaderInt(IReflectorCallback* handler, CHeaderData& header, DIRECTION direction) +{ + wxASSERT(handler != NULL); + + if (m_linkState != DPLUS_LINKED) + return; + + if (direction != m_direction) + return; + + // Already is use? + if (m_dPlusId != 0x00U) + return; + + switch (m_direction) { + case DIR_OUTGOING: + if (m_destination == handler) { + header.setRepeaters(m_callsign, m_reflector); + header.setDestination(m_yourAddress, m_yourPort); + m_handler->writeHeader(header); + } + break; + + case DIR_INCOMING: + header.setDestination(m_yourAddress, m_yourPort); + m_handler->writeHeader(header); + break; + } +} + +void CDPlusHandler::writeAMBEInt(IReflectorCallback* handler, CAMBEData& data, DIRECTION direction) +{ + if (m_linkState != DPLUS_LINKED) + return; + + if (direction != m_direction) + return; + + // Already in use? + if (m_dPlusId != 0x00U) + return; + + switch (m_direction) { + case DIR_OUTGOING: + if (m_destination == handler) { + data.setDestination(m_yourAddress, m_yourPort); + m_handler->writeAMBE(data); + } + break; + + case DIR_INCOMING: + data.setDestination(m_yourAddress, m_yourPort); + m_handler->writeAMBE(data); + break; + } +} + +bool CDPlusHandler::stateChange() +{ + bool stateChange = m_stateChange; + + m_stateChange = false; + + return stateChange; +} + +void CDPlusHandler::writeStatus(wxFFile& file) +{ + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDPlusHandler* reflector = m_reflectors[i]; + if (reflector != NULL) { + wxString text; + + struct tm* tm = ::gmtime(&reflector->m_time); + + if (reflector->m_linkState == DPLUS_LINKED) { + switch (reflector->m_direction) { + case DIR_OUTGOING: + text.Printf(wxT("%04d-%02d-%02d %02d:%02d:%02d: DPlus link - Type: Dongle Rptr: %s Refl: %s Dir: Outgoing\n"), + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, + reflector->m_repeater.c_str(), reflector->m_reflector.c_str()); + break; + + case DIR_INCOMING: + text.Printf(wxT("%04d-%02d-%02d %02d:%02d:%02d: DPlus link - Type: Dongle User: %s Dir: Incoming\n"), + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, + reflector->m_reflector.c_str()); + break; + } + + file.Write(text); + } + } + } +} + +unsigned int CDPlusHandler::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; +} diff --git a/Common/DPlusHandler.h b/Common/DPlusHandler.h new file mode 100644 index 0000000..37dcf0b --- /dev/null +++ b/Common/DPlusHandler.h @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2010-2013,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 + * 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 DPlusHandler_H +#define DPlusHandler_H + +#include "DPlusProtocolHandlerPool.h" +#include "DPlusAuthenticator.h" +#include "ReflectorCallback.h" +#include "CacheManager.h" +#include "DStarDefines.h" +#include "HeaderLogger.h" +#include "CallsignList.h" +#include "ConnectData.h" +#include "HeaderData.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 DPLUS_STATE { + DPLUS_LINKING, + DPLUS_LINKED, + DPLUS_UNLINKING +}; + +class CDPlusHandler { +public: + static void initialise(unsigned int maxReflectors); + + static void setCallsign(const wxString& callsign); + static void setDPlusProtocolHandlerPool(CDPlusProtocolHandlerPool* pool); + static void setDPlusProtocolIncoming(CDPlusProtocolHandler* handler); + static void setDPlusLogin(const wxString& dplusLogin); + static void setHeaderLogger(CHeaderLogger* logger); + static void setMaxDongles(unsigned int maxDongles); + + static void startAuthenticator(const wxString& address, CCacheManager* cache); + + static void link(IReflectorCallback* handler, const wxString& repeater, const wxString& reflector, const in_addr& address); + static void relink(IReflectorCallback* handler, const wxString& reflector); + static void unlink(IReflectorCallback* handler, const wxString& reflector = wxEmptyString, bool exclude = true); + static void unlink(); + + static void writeHeader(IReflectorCallback* handler, CHeaderData& header, DIRECTION direction); + static void writeAMBE(IReflectorCallback* handler, CAMBEData& data, DIRECTION direction); + + static void process(CHeaderData& header); + static void process(CAMBEData& header); + static void process(const CPollData& header); + static void process(CConnectData& process); + + static void gatewayUpdate(const wxString& gateway, const wxString& address); + static void clock(unsigned int ms); + + static bool stateChange(); + static void writeStatus(wxFFile& file); + + static void setWhiteList(CCallsignList* list); + static void setBlackList(CCallsignList* list); + + static void finalise(); + + static void getInfo(IReflectorCallback* handler, CRemoteRepeaterData& data); + + static wxString getDongles(); + +protected: + CDPlusHandler(IReflectorCallback* handler, const wxString& repeater, const wxString& reflector, CDPlusProtocolHandler* protoHandler, const in_addr& address, unsigned int port); + CDPlusHandler(CDPlusProtocolHandler* protoHandler, const in_addr& address, unsigned int port); + ~CDPlusHandler(); + + void processInt(CHeaderData& header); + void processInt(CAMBEData& data); + bool processInt(CConnectData& connect, CD_TYPE type); + + void writeHeaderInt(IReflectorCallback* handler, CHeaderData& header, DIRECTION direction); + void writeAMBEInt(IReflectorCallback* handler, CAMBEData& data, DIRECTION direction); + + bool clockInt(unsigned int ms); + +private: + static unsigned int m_maxReflectors; + static unsigned int m_maxDongles; + static CDPlusHandler** m_reflectors; + + static wxString m_gatewayCallsign; + static wxString m_dplusLogin; + static CDPlusProtocolHandlerPool* m_pool; + static CDPlusProtocolHandler* m_incoming; + + static bool m_stateChange; + + static CHeaderLogger* m_headerLogger; + static CDPlusAuthenticator* m_authenticator; + + static CCallsignList* m_whiteList; + static CCallsignList* m_blackList; + + wxString m_repeater; + wxString m_callsign; + wxString m_reflector; + CDPlusProtocolHandler* m_handler; + in_addr m_yourAddress; + unsigned int m_yourPort; + unsigned int m_myPort; + DIRECTION m_direction; + DPLUS_STATE m_linkState; + IReflectorCallback* m_destination; + time_t m_time; + CTimer m_pollTimer; + CTimer m_pollInactivityTimer; + CTimer m_tryTimer; + unsigned int m_tryCount; + unsigned int m_dPlusId; + unsigned int m_dPlusSeq; + CTimer m_inactivityTimer; + CHeaderData* m_header; + + unsigned int calcBackoff(); +}; + +#endif diff --git a/Common/DPlusProtocolHandler.cpp b/Common/DPlusProtocolHandler.cpp new file mode 100644 index 0000000..8781ac7 --- /dev/null +++ b/Common/DPlusProtocolHandler.cpp @@ -0,0 +1,233 @@ +/* + * 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 + * 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 "DPlusProtocolHandler.h" + +#include "DStarDefines.h" +#include "Utils.h" + +// #define DUMP_TX + +const unsigned int BUFFER_LENGTH = 1000U; + +CDPlusProtocolHandler::CDPlusProtocolHandler(unsigned int port, const wxString& addr) : +m_socket(addr, port), +m_type(DP_NONE), +m_buffer(NULL), +m_length(0U), +m_yourAddress(), +m_yourPort(0U), +m_myPort(port) +{ + m_buffer = new unsigned char[BUFFER_LENGTH]; +} + +CDPlusProtocolHandler::~CDPlusProtocolHandler() +{ + delete[] m_buffer; +} + +bool CDPlusProtocolHandler::open() +{ + return m_socket.open(); +} + +unsigned int CDPlusProtocolHandler::getPort() const +{ + return m_myPort; +} + +bool CDPlusProtocolHandler::writeHeader(const CHeaderData& header) +{ + unsigned char buffer[60U]; + unsigned int length = header.getDPlusData(buffer, 60U, true); + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Header"), buffer, length); +#endif + + for (unsigned int i = 0U; i < 5U; i++) { + bool res = m_socket.write(buffer, length, header.getYourAddress(), header.getYourPort()); + if (!res) + return false; + } + + return true; +} + +bool CDPlusProtocolHandler::writeAMBE(const CAMBEData& data) +{ + unsigned char buffer[40U]; + unsigned int length = data.getDPlusData(buffer, 40U); + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Data"), buffer, length); +#endif + + return m_socket.write(buffer, length, data.getYourAddress(), data.getYourPort()); +} + +bool CDPlusProtocolHandler::writePoll(const CPollData& poll) +{ + unsigned char buffer[10U]; + unsigned int length = poll.getDPlusData(buffer, 10U); + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Poll"), buffer, length); +#endif + + return m_socket.write(buffer, length, poll.getYourAddress(), poll.getYourPort()); +} + +bool CDPlusProtocolHandler::writeConnect(const CConnectData& connect) +{ + unsigned char buffer[40U]; + unsigned int length = connect.getDPlusData(buffer, 40U); + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Connect"), buffer, length); +#endif + + return m_socket.write(buffer, length, connect.getYourAddress(), connect.getYourPort()); +} + +DPLUS_TYPE CDPlusProtocolHandler::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 CDPlusProtocolHandler::readPackets() +{ + m_type = DP_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[2] != 'D' || m_buffer[3] != 'S' || m_buffer[4] != 'V' || m_buffer[5] != 'T') { + switch (m_length) { + case 3U: + m_type = DP_POLL; + return false; + case 5U: + case 8U: + case 28U: + m_type = DP_CONNECT; + return false; + default: + // An unknown type + // CUtils::dump(wxT("Unknown packet type from D-Plus"), m_buffer, m_length); + return true; + } + } else { + // Header or data packet type? + if (m_buffer[0] == 0x3A && m_buffer[1] == 0x80) { + m_type = DP_HEADER; + return false; + } else if (m_buffer[0] == 0x1D && m_buffer[1] == 0x80) { + m_type = DP_AMBE; + return false; + } else if (m_buffer[0] == 0x20 && m_buffer[1] == 0x80) { + m_type = DP_AMBE; + return false; + } else { + // An unknown type + CUtils::dump(wxT("Unknown packet type from D-Plus"), m_buffer, m_length); + return true; + } + } +} + +CHeaderData* CDPlusProtocolHandler::readHeader() +{ + if (m_type != DP_HEADER) + return NULL; + + CHeaderData* header = new CHeaderData; + + // DPlus checksums are unreliable + bool res = header->setDPlusData(m_buffer, m_length, false, m_yourAddress, m_yourPort, m_myPort); + if (!res) { + delete header; + return NULL; + } + + return header; +} + +CAMBEData* CDPlusProtocolHandler::readAMBE() +{ + if (m_type != DP_AMBE) + return NULL; + + CAMBEData* data = new CAMBEData; + + bool res = data->setDPlusData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort); + if (!res) { + delete data; + return NULL; + } + + return data; +} + +CPollData* CDPlusProtocolHandler::readPoll() +{ + if (m_type != DP_POLL) + return NULL; + + CPollData* poll = new CPollData; + + bool res = poll->setDPlusData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort); + if (!res) { + delete poll; + return NULL; + } + + return poll; +} + +CConnectData* CDPlusProtocolHandler::readConnect() +{ + if (m_type != DP_CONNECT) + return NULL; + + CConnectData* connect = new CConnectData; + + bool res = connect->setDPlusData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort); + if (!res) { + delete connect; + return NULL; + } + + return connect; +} + +void CDPlusProtocolHandler::close() +{ + m_socket.close(); +} diff --git a/Common/DPlusProtocolHandler.h b/Common/DPlusProtocolHandler.h new file mode 100644 index 0000000..3978f78 --- /dev/null +++ b/Common/DPlusProtocolHandler.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2010,2011,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 DPlusProtocolHandler_H +#define DPlusProtocolHandler_H + +#include "UDPReaderWriter.h" +#include "DStarDefines.h" +#include "ConnectData.h" +#include "HeaderData.h" +#include "AMBEData.h" +#include "PollData.h" + +#if defined(__WINDOWS__) +#include "Inaddr.h" +#else +#include +#endif + +#include + +enum DPLUS_TYPE { + DP_NONE, + DP_HEADER, + DP_AMBE, + DP_POLL, + DP_CONNECT +}; + +class CDPlusProtocolHandler { +public: + CDPlusProtocolHandler(unsigned int port, const wxString& addr = wxEmptyString); + ~CDPlusProtocolHandler(); + + bool open(); + + unsigned int getPort() const; + + bool writeHeader(const CHeaderData& header); + bool writeAMBE(const CAMBEData& data); + bool writeConnect(const CConnectData& connect); + bool writePoll(const CPollData& poll); + + DPLUS_TYPE read(); + CHeaderData* readHeader(); + CAMBEData* readAMBE(); + CPollData* readPoll(); + CConnectData* readConnect(); + + void close(); + +private: + CUDPReaderWriter m_socket; + DPLUS_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/DPlusProtocolHandlerPool.cpp b/Common/DPlusProtocolHandlerPool.cpp new file mode 100644 index 0000000..d1d50a2 --- /dev/null +++ b/Common/DPlusProtocolHandlerPool.cpp @@ -0,0 +1,137 @@ +/* + * 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 + * 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 "DPlusProtocolHandlerPool.h" + +CDPlusProtocolHandlerPool::CDPlusProtocolHandlerPool(unsigned int n, unsigned int port, const wxString& addr) : +m_pool(NULL), +m_n(n), +m_index(0U) +{ + wxASSERT(port > 0U); + wxASSERT(n > 0U); + + m_pool = new CDPlusProtocolHandlerEntry[n]; + + for (unsigned int i = 0U; i < n; i++) { + m_pool[i].m_handler = new CDPlusProtocolHandler(port + i, addr); + m_pool[i].m_port = port + i; + m_pool[i].m_inUse = false; + } + + wxLogMessage(wxT("Allocated UDP ports %u-%u to D-Plus"), port, port + n - 1U); +} + +CDPlusProtocolHandlerPool::~CDPlusProtocolHandlerPool() +{ + for (unsigned int i = 0U; i < m_n; i++) + delete m_pool[i].m_handler; + + delete[] m_pool; +} + +bool CDPlusProtocolHandlerPool::open() +{ + for (unsigned int i = 0U; i < m_n; i++) { + bool ret = m_pool[i].m_handler->open(); + if (!ret) + return false; + } + + return true; +} + +CDPlusProtocolHandler* CDPlusProtocolHandlerPool::getHandler(unsigned int port) +{ + if (port == 0U) { + for (unsigned int i = 0U; i < m_n; i++) { + if (!m_pool[i].m_inUse) { + m_pool[i].m_inUse = true; + return m_pool[i].m_handler; + } + } + } else { + for (unsigned int i = 0U; i < m_n; i++) { + if (m_pool[i].m_port == port) { + m_pool[i].m_inUse = true; + return m_pool[i].m_handler; + } + } + } + + wxLogError(wxT("Cannot find a free D-Plus port in the pool")); + + return NULL; +} + +void CDPlusProtocolHandlerPool::release(CDPlusProtocolHandler* handler) +{ + wxASSERT(handler != NULL); + + for (unsigned int i = 0U; i < m_n; i++) { + if (m_pool[i].m_handler == handler && m_pool[i].m_inUse) { + m_pool[i].m_inUse = false; + return; + } + } + + wxLogError(wxT("Trying to release an unused D-Plus port")); +} + +DPLUS_TYPE CDPlusProtocolHandlerPool::read() +{ + while (m_index < m_n) { + if (m_pool[m_index].m_inUse) { + DPLUS_TYPE type = m_pool[m_index].m_handler->read(); + if (type != DP_NONE) + return type; + } + + m_index++; + } + + m_index = 0U; + + return DP_NONE; +} + +CHeaderData* CDPlusProtocolHandlerPool::readHeader() +{ + return m_pool[m_index].m_handler->readHeader(); +} + +CAMBEData* CDPlusProtocolHandlerPool::readAMBE() +{ + return m_pool[m_index].m_handler->readAMBE(); +} + +CPollData* CDPlusProtocolHandlerPool::readPoll() +{ + return m_pool[m_index].m_handler->readPoll(); +} + +CConnectData* CDPlusProtocolHandlerPool::readConnect() +{ + return m_pool[m_index].m_handler->readConnect(); +} + +void CDPlusProtocolHandlerPool::close() +{ + for (unsigned int i = 0U; i < m_n; i++) + m_pool[i].m_handler->close(); +} diff --git a/Common/DPlusProtocolHandlerPool.h b/Common/DPlusProtocolHandlerPool.h new file mode 100644 index 0000000..b6c14fb --- /dev/null +++ b/Common/DPlusProtocolHandlerPool.h @@ -0,0 +1,57 @@ +/* + * 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 + * 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 DPlusProtocolHandlerPool_H +#define DPlusProtocolHandlerPool_H + +#include + +#include "DPlusProtocolHandler.h" + +class CDPlusProtocolHandlerEntry { +public: + CDPlusProtocolHandler* m_handler; + unsigned int m_port; + bool m_inUse; +}; + +class CDPlusProtocolHandlerPool { +public: + CDPlusProtocolHandlerPool(unsigned int n, unsigned int port, const wxString& addr = wxEmptyString); + ~CDPlusProtocolHandlerPool(); + + bool open(); + + CDPlusProtocolHandler* getHandler(unsigned int port = 0U); + void release(CDPlusProtocolHandler* handler); + + DPLUS_TYPE read(); + CHeaderData* readHeader(); + CAMBEData* readAMBE(); + CPollData* readPoll(); + CConnectData* readConnect(); + + void close(); + +private: + CDPlusProtocolHandlerEntry* m_pool; + unsigned int m_n; + unsigned int m_index; +}; + +#endif diff --git a/Common/DRATSServer.cpp b/Common/DRATSServer.cpp new file mode 100644 index 0000000..bfb4b1a --- /dev/null +++ b/Common/DRATSServer.cpp @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2011-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 + * 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 "DRATSServer.h" +#include "Utils.h" + +// #define LOOPBACK + +const unsigned int BUFFER_LENGTH = 30000U; + +CDRATSServer::CDRATSServer(const wxString& address, unsigned int port, const wxString& callsign, IRepeaterCallback* handler) : +wxThread(wxTHREAD_JOINABLE), +m_address(address), +m_port(port), +m_callsign(callsign), +m_handler(handler), +m_socket(NULL), +m_stopped(false), +m_readState(SS_FIRST), +m_readBuffer(NULL), +m_readLength(0U), +m_readPos(0U), +m_readEnd(false), +m_writeText(NULL), +m_writeState(SS_FIRST), +m_writeBuffer(NULL), +m_writeLength(0U) +{ + wxASSERT(handler != NULL); + wxASSERT(port > 0U); + + m_readBuffer = new unsigned char[BUFFER_LENGTH]; + m_writeBuffer = new unsigned char[BUFFER_LENGTH]; + m_writeText = new unsigned char[6U]; +} + +CDRATSServer::~CDRATSServer() +{ + delete[] m_readBuffer; + delete[] m_writeBuffer; + delete[] m_writeText; +} + +bool CDRATSServer::open() +{ + m_socket = new CTCPReaderWriterServer(m_address, m_port); + bool ret = m_socket->start(); + if (!ret) { + delete m_socket; + m_socket = NULL; + return false; + } + + Create(); + Run(); + + return true; +} + +void CDRATSServer::writeHeader(const CHeaderData&) +{ + m_writeState = SS_FIRST; + + if (m_writeLength > 0U && m_socket != NULL) { + CUtils::dump(wxT("From RF"), m_writeBuffer, m_writeLength); + m_socket->write(m_writeBuffer, m_writeLength); + } + + m_writeLength = 0U; +} + +void CDRATSServer::writeData(const CAMBEData& data) +{ + // Sync data isn't sent on + if (data.isSync()) { + m_writeState = SS_FIRST; + return; + } + + if (data.isEnd()) { + if (m_writeLength > 0U && m_socket != NULL) { + CUtils::dump(wxT("From RF"), m_writeBuffer, m_writeLength); + m_socket->write(m_writeBuffer, m_writeLength); + } + + m_writeLength = 0U; + return; + } + + unsigned char buffer[DV_FRAME_MAX_LENGTH_BYTES]; + unsigned int length = data.getData(buffer, DV_FRAME_MAX_LENGTH_BYTES); + + if (length != DV_FRAME_LENGTH_BYTES) + return; + + unsigned char byte1 = buffer[VOICE_FRAME_LENGTH_BYTES + 0U] ^ SCRAMBLER_BYTE1; + unsigned char byte2 = buffer[VOICE_FRAME_LENGTH_BYTES + 1U] ^ SCRAMBLER_BYTE2; + unsigned char byte3 = buffer[VOICE_FRAME_LENGTH_BYTES + 2U] ^ SCRAMBLER_BYTE3; + + switch (m_writeState) { + case SS_FIRST: + m_writeText[0U] = byte1; + m_writeText[1U] = byte2; + m_writeText[2U] = byte3; + m_writeState = SS_SECOND; + return; + + case SS_SECOND: + m_writeText[3U] = byte1; + m_writeText[4U] = byte2; + m_writeText[5U] = byte3; + m_writeState = SS_FIRST; + break; + } + + if ((m_writeText[0U] & SLOW_DATA_TYPE_MASK) != SLOW_DATA_TYPE_GPS) + return; + + length = m_writeText[0U] & 0x07; // Maximum value of 5 + if (length > 5U) + length = 5U; + + for (unsigned int i = 0U; i < length; i++) { + m_writeBuffer[m_writeLength++] = m_writeText[i + 1U]; + + // Check for [EOB] in the buffer to signal the end of the D-RATS data. + // To allow strstr() to run correctly + m_writeBuffer[m_writeLength] = 0x00U; + + if (::strstr((char*)m_writeBuffer, "[EOB]") != NULL) { + if (m_socket != NULL) { + CUtils::dump(wxT("From RF"), m_writeBuffer, m_writeLength); + m_socket->write(m_writeBuffer, m_writeLength); + } + + m_writeLength = 0U; + } + } +} + +void CDRATSServer::writeEnd() +{ + if (m_writeLength > 0U && m_socket != NULL) { + CUtils::dump(wxT("From RF"), m_writeBuffer, m_writeLength); + m_socket->write(m_writeBuffer, m_writeLength); + } + + m_writeLength = 0U; +} + +void* CDRATSServer::Entry() +{ + wxLogMessage(wxT("Starting the D-RATS Server thread for %s"), m_callsign.c_str()); + + bool sending = false; + unsigned int id = 0U; + + unsigned char seqNo = 0U; + unsigned int sent = 0U; + + wxStopWatch time; + + try { + while (!m_stopped) { + serviceSocket(); + + if (m_readEnd && !sending) { + id = CHeaderData::createId(); + + // Write header + CHeaderData header; + header.setMyCall1(m_callsign); + header.setMyCall2(wxT("DATA")); + header.setYourCall(wxT("CQCQCQ ")); + header.setId(id); + +#if defined(LOOPBACK) + writeHeader(header); +#else + m_handler->process(header, DIR_INCOMING, AS_DRATS); +#endif + + m_readState = SS_FIRST; + m_readPos = 0U; + sending = true; + seqNo = 0U; + sent = 0U; + + time.Start(); + } + + if (m_readEnd && sending) { + unsigned int needed = time.Time() / DSTAR_FRAME_TIME_MS; + + while (sent < needed && sending) { + // Write AMBE data + CAMBEData data; + data.setId(id); + + unsigned char buffer[DV_FRAME_LENGTH_BYTES]; + ::memcpy(buffer + 0U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + + // Insert sync bytes when the sequence number is zero, slow data otherwise + if (seqNo == 0U) { + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); + m_readState = SS_FIRST; + } else { + if (m_readState == SS_FIRST) { + unsigned char readText[3U]; + ::memset(readText, 'f', 3U); + + unsigned int length = m_readLength - m_readPos; + unsigned char bytes = 5U; + if (length < 5U) + bytes = length; + + readText[0U] = SLOW_DATA_TYPE_GPS | bytes; + + for (unsigned int i = 0U; i < 2U && m_readPos < m_readLength; i++) + readText[i + 1U] = m_readBuffer[m_readPos++]; + + readText[0U] ^= SCRAMBLER_BYTE1; + readText[1U] ^= SCRAMBLER_BYTE2; + readText[2U] ^= SCRAMBLER_BYTE3; + + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, readText, DATA_FRAME_LENGTH_BYTES); + + m_readState = SS_SECOND; + } else { + unsigned char readText[3U]; + ::memset(readText, 'f', 3U); + + for (unsigned int i = 0U; i < 3U && m_readPos < m_readLength; i++) + readText[i] = m_readBuffer[m_readPos++]; + + readText[0U] ^= SCRAMBLER_BYTE1; + readText[1U] ^= SCRAMBLER_BYTE2; + readText[2U] ^= SCRAMBLER_BYTE3; + + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, readText, DATA_FRAME_LENGTH_BYTES); + + m_readState = SS_FIRST; + } + } + + data.setSeq(seqNo); + data.setData(buffer, DV_FRAME_LENGTH_BYTES); + sent++; + +#if defined(LOOPBACK) + writeData(data); +#else + m_handler->process(data, DIR_INCOMING, AS_DRATS); +#endif + if (m_readPos == m_readLength) { + if (m_readState == SS_SECOND) { + seqNo++; + if (seqNo == 21U) + seqNo = 0U; + + unsigned char readText[3U]; + readText[0U] = 'f' ^ SCRAMBLER_BYTE1; + readText[1U] = 'f' ^ SCRAMBLER_BYTE2; + readText[2U] = 'f' ^ SCRAMBLER_BYTE3; + + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, readText, DATA_FRAME_LENGTH_BYTES); + + data.setSeq(seqNo); + data.setData(buffer, DV_FRAME_LENGTH_BYTES); + sent++; +#if defined(LOOPBACK) + writeData(data); +#else + m_handler->process(data, DIR_INCOMING, AS_DRATS); +#endif + } + + seqNo++; + if (seqNo == 21U) + seqNo = 0U; + + if (seqNo == 0U) + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); + else + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, NULL_SLOW_DATA_BYTES, DATA_FRAME_LENGTH_BYTES); + + data.setData(buffer, DV_FRAME_LENGTH_BYTES); + data.setSeq(seqNo); + data.setEnd(true); + sent++; +#if defined(LOOPBACK) + writeData(data); +#else + m_handler->process(data, DIR_INCOMING, AS_DRATS); +#endif + m_readLength = 0U; + m_readPos = 0U; + m_readEnd = false; + sending = false; + sent = 0U; + } + + seqNo++; + if (seqNo == 21U) + seqNo = 0U; + } + } + + // 50ms + Sleep(50UL); + } + + if (m_socket != NULL) + m_socket->stop(); + } + catch (std::exception& e) { + wxString message(e.what(), wxConvLocal); + wxLogError(wxT("Exception raised in the D-RATS Server thread - \"%s\""), message.c_str()); + } + catch (...) { + wxLogError(wxT("Unknown exception raised in the D-RATS Server thread")); + } + + wxLogMessage(wxT("Stopping the D-RATS Server thread for %s"), m_callsign.c_str()); + + return NULL; +} + +void CDRATSServer::close() +{ + m_stopped = true; + + Wait(); +} + +void CDRATSServer::serviceSocket() +{ + if (m_socket == NULL) { + m_readLength = 0U; + m_readPos = 0U; + m_readEnd = false; + return; + } + + int len = m_socket->read(m_readBuffer + m_readLength, BUFFER_LENGTH - m_readLength, 0U); + if (len > 0) { + m_readLength += len; + + if (!m_readEnd) { + // To allow strstr() to run correctly + m_readBuffer[m_readLength] = 0x00U; + + if (::strstr((char*)m_readBuffer, "[EOB]") != NULL) { + CUtils::dump(wxT("To RF"), m_readBuffer, m_readLength); + m_readEnd = true; + } + } + } +} diff --git a/Common/DRATSServer.h b/Common/DRATSServer.h new file mode 100644 index 0000000..d54166a --- /dev/null +++ b/Common/DRATSServer.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2011,2012 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 DRATSServer_H +#define DRATSServer_H + +#include "TCPReaderWriterServer.h" +#include "RepeaterCallback.h" +#include "HeaderData.h" +#include "AMBEData.h" +#include "Defs.h" + +#include + +class CDRATSServer : public wxThread { +public: + CDRATSServer(const wxString& address, unsigned int port, const wxString& callsign, IRepeaterCallback* handler); + virtual ~CDRATSServer(); + + virtual bool open(); + + virtual void writeHeader(const CHeaderData& header); + virtual void writeData(const CAMBEData& data); + virtual void writeEnd(); + + virtual void close(); + + virtual void* Entry(); + +private: + wxString m_address; + unsigned int m_port; + wxString m_callsign; + IRepeaterCallback* m_handler; + CTCPReaderWriterServer* m_socket; + bool m_stopped; + SLOWDATA_STATE m_readState; + unsigned char* m_readBuffer; + unsigned int m_readLength; + unsigned int m_readPos; + bool m_readEnd; + unsigned char* m_writeText; + SLOWDATA_STATE m_writeState; + unsigned char* m_writeBuffer; + unsigned int m_writeLength; + + void serviceSocket(); +}; + +#endif diff --git a/Common/DStarDefines.h b/Common/DStarDefines.h new file mode 100644 index 0000000..8ea9ae6 --- /dev/null +++ b/Common/DStarDefines.h @@ -0,0 +1,177 @@ +/* + * 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 + * the Free Software Foundation; version 2 of the License. + * + * 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. + */ + +#ifndef DStarDefines_H +#define DStarDefines_H + +#include + +const unsigned int DSTAR_GMSK_SYMBOL_RATE = 4800U; +const float DSTAR_GMSK_BT = 0.5F; + +const bool BIT_SYNC_BITS[] = {true, false, true, false}; +const unsigned int BIT_SYNC_LENGTH_BITS = 4U; + +const bool FRAME_SYNC_BITS[] = {true, true, true, false, true, true, false, false, + true, false, true, false, false, false, false}; +const unsigned int FRAME_SYNC_LENGTH_BITS = 15U; + +const unsigned char DATA_SYNC_BYTES[] = {0x55, 0x2D, 0x16}; +const bool DATA_SYNC_BITS[] = {true, false, true, false, true, false, true, false, + true, false, true, true, false, true, false, false, + false, true, true, false, true, false, false, false}; + +const unsigned char END_PATTERN_BYTES[] = {0x55, 0x55, 0x55, 0x55, 0xC8, 0x7A, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +const bool END_PATTERN_BITS[] = {true, false, true, false, true, false, true, false, + true, false, true, false, true, false, true, false, + true, false, true, false, true, false, true, false, + true, false, true, false, true, false, true, false, + false, false, false, true, false, false, true, true, + false, true, false, true, true, true, true, false}; +const unsigned int END_PATTERN_LENGTH_BITS = 48U; +const unsigned int END_PATTERN_LENGTH_BYTES = END_PATTERN_LENGTH_BITS / 8U; + +const unsigned char NULL_AMBE_DATA_BYTES[] = {0x9E, 0x8D, 0x32, 0x88, 0x26, 0x1A, 0x3F, 0x61, 0xE8}; +const bool NULL_AMBE_DATA_BITS[] = {false, true, true, true, true, false, false, true, + true, false, true, true, false, false, false, true, + false, true, false, false, true, true, false, false, + false, false, false, true, false, false, false, true, + false, true, true, false, false, true, false, false, + false, true, false, true, true, false, false, false, + true, true, true, true, true, true, false, false, + true, false, false, false, false, true, true, false, + false, false, false, true, false, true, true, true}; + +// Note that these are already scrambled, 0x66 0x66 0x66 otherwise +const unsigned char NULL_SLOW_DATA_BYTES[] = {0x16, 0x29, 0xF5}; +const bool NULL_SLOW_DATA_BITS[] = {false, true, true, false, true, false, false, false, + true, false, false, true, false, true, false, false, + true, false, true, false, true, true, true, true}; + +const unsigned int VOICE_FRAME_LENGTH_BITS = 72U; +const unsigned int VOICE_FRAME_LENGTH_BYTES = VOICE_FRAME_LENGTH_BITS / 8U; + +const unsigned int DATA_FRAME_LENGTH_BITS = 24U; +const unsigned int DATA_FRAME_LENGTH_BYTES = DATA_FRAME_LENGTH_BITS / 8U; + +const unsigned int DV_FRAME_LENGTH_BITS = VOICE_FRAME_LENGTH_BITS + DATA_FRAME_LENGTH_BITS; +const unsigned int DV_FRAME_LENGTH_BYTES = VOICE_FRAME_LENGTH_BYTES + DATA_FRAME_LENGTH_BYTES; + +// The length of the end frame, three bytes extra +const unsigned int DV_FRAME_MAX_LENGTH_BITS = DV_FRAME_LENGTH_BITS + 24U; +const unsigned int DV_FRAME_MAX_LENGTH_BYTES = DV_FRAME_MAX_LENGTH_BITS / 8U; + +const unsigned int FEC_SECTION_LENGTH_BITS = 660U; + +const unsigned int RADIO_HEADER_LENGTH_BITS = 330U; +const unsigned int RADIO_HEADER_LENGTH_BYTES = 41U; + +const unsigned int DATA_BLOCK_SIZE_BITS = 21U * DV_FRAME_LENGTH_BITS; +const unsigned int DATA_BLOCK_SIZE_BYTES = 21U * DV_FRAME_LENGTH_BYTES; + +const unsigned int LONG_CALLSIGN_LENGTH = 8U; +const unsigned int SHORT_CALLSIGN_LENGTH = 4U; + +const unsigned char SLOW_DATA_TYPE_MASK = 0xF0U; +const unsigned char SLOW_DATA_TYPE_GPS = 0x30U; +const unsigned char SLOW_DATA_TYPE_TEXT = 0x40U; +const unsigned char SLOW_DATA_TYPE_HEADER = 0x50U; + +const unsigned char DATA_MASK = 0x80U; +const unsigned char REPEATER_MASK = 0x40U; +const unsigned char INTERRUPTED_MASK = 0x20U; +const unsigned char CONTROL_SIGNAL_MASK = 0x10U; +const unsigned char URGENT_MASK = 0x08U; + +const unsigned char REPEATER_CONTROL_MASK = 0x07U; +const unsigned char REPEATER_CONTROL = 0x07U; +const unsigned char AUTO_REPLY = 0x06U; +const unsigned char RESEND_REQUESTED = 0x04U; +const unsigned char ACK_FLAG = 0x03U; +const unsigned char NO_RESPONSE = 0x02U; +const unsigned char RELAY_UNAVAILABLE = 0x01U; + +const unsigned int DSTAR_FRAME_TIME_MS = 20U; +const unsigned int DSTAR_FRAMES_PER_SEC = 50U; + +const unsigned char SCRAMBLER_BYTE1 = 0x70U; +const unsigned char SCRAMBLER_BYTE2 = 0x4FU; +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, 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 + +enum DSTAR_PROTOCOL { + DP_UNKNOWN, + DP_LOOPBACK, + DP_DEXTRA, + DP_DPLUS, + DP_DCS +}; + +enum AUDIO_SOURCE { + AS_G2, + AS_ECHO, + AS_INFO, + AS_XBAND, + AS_DRATS, + AS_DPLUS, + AS_DEXTRA, + AS_DCS, + AS_DUP, + AS_VERSION, + AS_CCS +}; + +enum DSTAR_RX_STATE { + DSRXS_LISTENING, + DSRXS_PROCESS_HEADER, + DSRXS_PROCESS_DATA, + DSRXS_PROCESS_SLOW_DATA +}; + +enum DSTAR_RPT_STATE { + DSRS_SHUTDOWN, + DSRS_LISTENING, + DSRS_VALID, + DSRS_VALID_WAIT, + DSRS_INVALID, + DSRS_INVALID_WAIT, + DSRS_TIMEOUT, + DSRS_TIMEOUT_WAIT, + DSRS_NETWORK +}; + +enum NETWORK_TYPE { + NETWORK_NONE, + NETWORK_HEADER, + NETWORK_DATA, + NETWORK_TEXT +}; + +enum DSTAR_MODE { + MODE_DUPLEX, + MODE_SIMPLEX, + MODE_GATEWAY +}; + +#endif diff --git a/Common/DTMF.cpp b/Common/DTMF.cpp new file mode 100644 index 0000000..a05cbed --- /dev/null +++ b/Common/DTMF.cpp @@ -0,0 +1,317 @@ +/* + * 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 + * 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 "DTMF.h" + +const unsigned char DTMF_MASK[] = {0x82U, 0x08U, 0x20U, 0x82U, 0x00U, 0x00U, 0x82U, 0x00U, 0x00U}; +const unsigned char DTMF_SIG[] = {0x82U, 0x08U, 0x20U, 0x82U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U}; + +const unsigned char DTMF_SYM_MASK[] = {0x10U, 0x40U, 0x08U, 0x20U}; +const unsigned char DTMF_SYM0[] = {0x00U, 0x40U, 0x08U, 0x20U}; +const unsigned char DTMF_SYM1[] = {0x00U, 0x00U, 0x00U, 0x00U}; +const unsigned char DTMF_SYM2[] = {0x00U, 0x40U, 0x00U, 0x00U}; +const unsigned char DTMF_SYM3[] = {0x10U, 0x00U, 0x00U, 0x00U}; +const unsigned char DTMF_SYM4[] = {0x00U, 0x00U, 0x00U, 0x20U}; +const unsigned char DTMF_SYM5[] = {0x00U, 0x40U, 0x00U, 0x20U}; +const unsigned char DTMF_SYM6[] = {0x10U, 0x00U, 0x00U, 0x20U}; +const unsigned char DTMF_SYM7[] = {0x00U, 0x00U, 0x08U, 0x00U}; +const unsigned char DTMF_SYM8[] = {0x00U, 0x40U, 0x08U, 0x00U}; +const unsigned char DTMF_SYM9[] = {0x10U, 0x00U, 0x08U, 0x00U}; +const unsigned char DTMF_SYMA[] = {0x10U, 0x40U, 0x00U, 0x00U}; +const unsigned char DTMF_SYMB[] = {0x10U, 0x40U, 0x00U, 0x20U}; +const unsigned char DTMF_SYMC[] = {0x10U, 0x40U, 0x08U, 0x00U}; +const unsigned char DTMF_SYMD[] = {0x10U, 0x40U, 0x08U, 0x20U}; +const unsigned char DTMF_SYMS[] = {0x00U, 0x00U, 0x08U, 0x20U}; +const unsigned char DTMF_SYMH[] = {0x10U, 0x00U, 0x08U, 0x20U}; + +CDTMF::CDTMF() : +m_data(), +m_command(), +m_pressed(false), +m_releaseCount(0U), +m_pressCount(0U), +m_lastChar(wxT(' ')) +{ +} + +CDTMF::~CDTMF() +{ +} + +bool CDTMF::decode(const unsigned char* ambe, bool end) +{ + // DTMF begins with these byte values + if (!end && (ambe[0] & DTMF_MASK[0]) == DTMF_SIG[0] && (ambe[1] & DTMF_MASK[1]) == DTMF_SIG[1] && + (ambe[2] & DTMF_MASK[2]) == DTMF_SIG[2] && (ambe[3] & DTMF_MASK[3]) == DTMF_SIG[3] && + (ambe[4] & DTMF_MASK[4]) == DTMF_SIG[4] && (ambe[5] & DTMF_MASK[5]) == DTMF_SIG[5] && + (ambe[6] & DTMF_MASK[6]) == DTMF_SIG[6] && (ambe[7] & DTMF_MASK[7]) == DTMF_SIG[7] && + (ambe[8] & DTMF_MASK[8]) == DTMF_SIG[8]) { + unsigned char sym0 = ambe[4] & DTMF_SYM_MASK[0]; + unsigned char sym1 = ambe[5] & DTMF_SYM_MASK[1]; + unsigned char sym2 = ambe[7] & DTMF_SYM_MASK[2]; + unsigned char sym3 = ambe[8] & DTMF_SYM_MASK[3]; + + wxChar c = wxT(' '); + if (sym0 == DTMF_SYM0[0] && sym1 == DTMF_SYM0[1] && sym2 == DTMF_SYM0[2] && sym3 == DTMF_SYM0[3]) + c = wxT('0'); + else if (sym0 == DTMF_SYM1[0] && sym1 == DTMF_SYM1[1] && sym2 == DTMF_SYM1[2] && sym3 == DTMF_SYM1[3]) + c = wxT('1'); + else if (sym0 == DTMF_SYM2[0] && sym1 == DTMF_SYM2[1] && sym2 == DTMF_SYM2[2] && sym3 == DTMF_SYM2[3]) + c = wxT('2'); + else if (sym0 == DTMF_SYM3[0] && sym1 == DTMF_SYM3[1] && sym2 == DTMF_SYM3[2] && sym3 == DTMF_SYM3[3]) + c = wxT('3'); + else if (sym0 == DTMF_SYM4[0] && sym1 == DTMF_SYM4[1] && sym2 == DTMF_SYM4[2] && sym3 == DTMF_SYM4[3]) + c = wxT('4'); + else if (sym0 == DTMF_SYM5[0] && sym1 == DTMF_SYM5[1] && sym2 == DTMF_SYM5[2] && sym3 == DTMF_SYM5[3]) + c = wxT('5'); + else if (sym0 == DTMF_SYM6[0] && sym1 == DTMF_SYM6[1] && sym2 == DTMF_SYM6[2] && sym3 == DTMF_SYM6[3]) + c = wxT('6'); + else if (sym0 == DTMF_SYM7[0] && sym1 == DTMF_SYM7[1] && sym2 == DTMF_SYM7[2] && sym3 == DTMF_SYM7[3]) + c = wxT('7'); + else if (sym0 == DTMF_SYM8[0] && sym1 == DTMF_SYM8[1] && sym2 == DTMF_SYM8[2] && sym3 == DTMF_SYM8[3]) + c = wxT('8'); + else if (sym0 == DTMF_SYM9[0] && sym1 == DTMF_SYM9[1] && sym2 == DTMF_SYM9[2] && sym3 == DTMF_SYM9[3]) + c = wxT('9'); + else if (sym0 == DTMF_SYMA[0] && sym1 == DTMF_SYMA[1] && sym2 == DTMF_SYMA[2] && sym3 == DTMF_SYMA[3]) + c = wxT('A'); + else if (sym0 == DTMF_SYMB[0] && sym1 == DTMF_SYMB[1] && sym2 == DTMF_SYMB[2] && sym3 == DTMF_SYMB[3]) + c = wxT('B'); + else if (sym0 == DTMF_SYMC[0] && sym1 == DTMF_SYMC[1] && sym2 == DTMF_SYMC[2] && sym3 == DTMF_SYMC[3]) + c = wxT('C'); + else if (sym0 == DTMF_SYMD[0] && sym1 == DTMF_SYMD[1] && sym2 == DTMF_SYMD[2] && sym3 == DTMF_SYMD[3]) + c = wxT('D'); + else if (sym0 == DTMF_SYMS[0] && sym1 == DTMF_SYMS[1] && sym2 == DTMF_SYMS[2] && sym3 == DTMF_SYMS[3]) + c = wxT('*'); + else if (sym0 == DTMF_SYMH[0] && sym1 == DTMF_SYMH[1] && sym2 == DTMF_SYMH[2] && sym3 == DTMF_SYMH[3]) + c = wxT('#'); + + if (c == m_lastChar) { + m_pressCount++; + } else { + m_lastChar = c; + m_pressCount = 0U; + } + + if (c != wxT(' ') && !m_pressed && m_pressCount >= 3U) { + m_data.Append(c); + m_releaseCount = 0U; + m_pressed = true; + } + + return c != wxT(' '); + } else { + // If it is not a DTMF Code + if ((end || m_releaseCount >= 100U) && m_data.Len() > 0U) { + m_command = m_data; + m_data.Clear(); + m_releaseCount = 0U; + } + + m_pressed = false; + m_releaseCount++; + m_pressCount = 0U; + m_lastChar = wxT(' '); + + return false; + } +} + +bool CDTMF::hasCommand() const +{ + return !m_command.IsEmpty(); +} + +// DTMF to YOUR call command +wxString CDTMF::translate() +{ + wxString command = m_command; + m_command.Clear(); + + if (command.IsEmpty()) + return wxEmptyString; + + if (command.IsSameAs(wxT("#"))) + return wxT(" U"); + + if (command.IsSameAs(wxT("0"))) + return wxT(" I"); + + if (command.IsSameAs(wxT("A"))) + return wxT("CA "); + + if (command.IsSameAs(wxT("00"))) + return wxT(" I"); + + if (command.IsSameAs(wxT("**"))) + return wxT(" L"); + + if (command.GetChar(0U) == wxT('*')) + return processReflector(wxT("REF"), command.Mid(1U)); + else if (command.GetChar(0U) == wxT('B')) + 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() +{ + m_data.Clear(); + m_command.Clear(); + m_pressed = false; + m_pressCount = 0U; + m_releaseCount = 0U; + m_lastChar = wxT(' '); +} + +wxString CDTMF::processReflector(const wxString& prefix, const wxString& command) const +{ + unsigned int len = command.Len(); + + wxChar c = command.GetChar(len - 1U); + if (c == wxT('A') || c == wxT('B') || c == wxT('C') || c == wxT('D')) { + if (len < 2U || len > 4U) + return wxEmptyString; + + unsigned long n; + command.Left(len - 1U).ToULong(&n); + if (n == 0UL) + return wxEmptyString; + + wxString out; + out.Printf(wxT("%s%03lu%cL"), prefix.c_str(), n, c); + + return out; + } else { + if (len < 3U || len > 5U) + return wxEmptyString; + + unsigned long n1; + command.Left(len - 2U).ToULong(&n1); + if (n1 == 0UL) + return wxEmptyString; + + unsigned long n2; + command.Right(2U).ToULong(&n2); + if (n2 == 0UL || n2 > 26UL) + return wxEmptyString; + + c = wxT('A') + n2 - 1UL; + + wxString out; + out.Printf(wxT("%s%03lu%cL"), prefix.c_str(), n1, c); + + 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 new file mode 100644 index 0000000..71ab364 --- /dev/null +++ b/Common/DTMF.h @@ -0,0 +1,49 @@ +/* + * 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 + * 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 DTMF_H +#define DTMF_H + +#include + +class CDTMF { +public: + CDTMF(); + ~CDTMF(); + + bool decode(const unsigned char* ambe, bool end); + + bool hasCommand() const; + + wxString translate(); + + void reset(); + +private: + wxString m_data; + wxString m_command; + bool m_pressed; + unsigned int m_releaseCount; + unsigned int m_pressCount; + wxChar m_lastChar; + + wxString processReflector(const wxString& prefix, const wxString& command) const; + wxString processCCS(const wxString& command) const; +}; + +#endif diff --git a/Common/DVTOOLFileReader.cpp b/Common/DVTOOLFileReader.cpp new file mode 100644 index 0000000..4090721 --- /dev/null +++ b/Common/DVTOOLFileReader.cpp @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2009,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 "DVTOOLFileReader.h" +#include "DStarDefines.h" + +#include + +static const char DVTOOL_SIGNATURE[] = "DVTOOL"; +static const unsigned int DVTOOL_SIGNATURE_LENGTH = 6U; + +static const char DSVT_SIGNATURE[] = "DSVT"; +static const unsigned int DSVT_SIGNATURE_LENGTH = 4U; + +static const unsigned int FIXED_DATA_LENGTH = 9U; + +static const unsigned char HEADER_FLAG = 0x10; +static const unsigned char DATA_FLAG = 0x20; + +static const unsigned char HEADER_MASK = 0x80; +static const unsigned char TRAILER_MASK = 0x40; + +const unsigned int BUFFER_LENGTH = 255U; + + +CDVTOOLFileReader::CDVTOOLFileReader() : +m_fileName(), +m_file(), +m_records(0U), +m_type(DVTFR_NONE), +m_buffer(NULL), +m_length(0U), +m_seqNo(0U) +{ + m_buffer = new unsigned char[BUFFER_LENGTH]; +} + +CDVTOOLFileReader::~CDVTOOLFileReader() +{ + delete[] m_buffer; +} + +wxString CDVTOOLFileReader::getFileName() const +{ + return m_fileName; +} + +unsigned int CDVTOOLFileReader::getRecords() const +{ + return m_records; +} + +bool CDVTOOLFileReader::open(const wxString& fileName) +{ + m_fileName = fileName; + + bool res = m_file.Open(fileName, wxT("rb")); + if (!res) + return false; + + unsigned char buffer[DVTOOL_SIGNATURE_LENGTH]; + size_t n = m_file.Read(buffer, DVTOOL_SIGNATURE_LENGTH); + if (n != DVTOOL_SIGNATURE_LENGTH) { + m_file.Close(); + return false; + } + + if (::memcmp(buffer, DVTOOL_SIGNATURE, DVTOOL_SIGNATURE_LENGTH) != 0) { + m_file.Close(); + return false; + } + + wxUint32 uint32; + n = m_file.Read(&uint32, sizeof(wxUint32)); + if (n != sizeof(wxUint32)) { + m_file.Close(); + return false; + } + + m_records = wxUINT32_SWAP_ON_LE(uint32); + m_seqNo = 0U; + + return true; +} + +DVTFR_TYPE CDVTOOLFileReader::read() +{ + wxUint16 uint16; + size_t n = m_file.Read(&uint16, sizeof(wxUint16)); + if (n != sizeof(wxUint16)) + return DVTFR_NONE; + + m_length = wxUINT16_SWAP_ON_BE(uint16) - 15U; + + unsigned char bytes[FIXED_DATA_LENGTH]; + n = m_file.Read(bytes, DSVT_SIGNATURE_LENGTH); + if (n != DSVT_SIGNATURE_LENGTH) + return DVTFR_NONE; + + if (::memcmp(bytes, DSVT_SIGNATURE, DSVT_SIGNATURE_LENGTH) != 0) + return DVTFR_NONE; + + char flag; + n = m_file.Read(&flag, 1U); + if (n != 1U) + return DVTFR_NONE; + + m_type = (flag == HEADER_FLAG) ? DVTFR_HEADER : DVTFR_DATA; + + n = m_file.Read(bytes, FIXED_DATA_LENGTH); + if (n != FIXED_DATA_LENGTH) + return DVTFR_NONE; + + n = m_file.Read(&flag, 1U); + if (n != 1U) + return DVTFR_NONE; + + if (m_type == DVTFR_DATA) + m_seqNo = flag; + + n = m_file.Read(m_buffer, m_length); + if (n != m_length) + return DVTFR_NONE; + + return m_type; +} + +CHeaderData* CDVTOOLFileReader::readHeader() +{ + if (m_type != DVTFR_HEADER) + return NULL; + + CHeaderData* header = new CHeaderData; + + if (m_buffer[39U] == 0xFFU && m_buffer[40U] == 0xFFU) { + header->setDVTOOLData(m_buffer, RADIO_HEADER_LENGTH_BYTES, false); + return header; + } + + // Header checksum testing is enabled + bool valid = header->setDVTOOLData(m_buffer, RADIO_HEADER_LENGTH_BYTES, true); + if (!valid) { + delete header; + return NULL; + } + + return header; +} + +CAMBEData* CDVTOOLFileReader::readData() +{ + if (m_type != DVTFR_DATA) + return NULL; + + CAMBEData* data = new CAMBEData; + + data->setData(m_buffer, m_length); + data->setSeq(m_seqNo); + + return data; +} + +void CDVTOOLFileReader::close() +{ + m_file.Close(); +} diff --git a/Common/DVTOOLFileReader.h b/Common/DVTOOLFileReader.h new file mode 100644 index 0000000..e8b9adc --- /dev/null +++ b/Common/DVTOOLFileReader.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2009,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. + */ + +#ifndef DVTOOLFileReader_H +#define DVTOOLFileReader_H + +#include "HeaderData.h" +#include "AMBEData.h" + +enum DVTFR_TYPE { + DVTFR_NONE, + DVTFR_HEADER, + DVTFR_DATA +}; + +#include +#include + +class CDVTOOLFileReader { +public: + CDVTOOLFileReader(); + ~CDVTOOLFileReader(); + + wxString getFileName() const; + unsigned int getRecords() const; + + bool open(const wxString& fileName); + + DVTFR_TYPE read(); + + CHeaderData* readHeader(); + CAMBEData* readData(); + + void close(); + +private: + wxString m_fileName; + wxFFile m_file; + wxUint32 m_records; + DVTFR_TYPE m_type; + unsigned char* m_buffer; + unsigned int m_length; + unsigned char m_seqNo; +}; + +#endif diff --git a/Common/Defs.h b/Common/Defs.h new file mode 100644 index 0000000..f2c5c31 --- /dev/null +++ b/Common/Defs.h @@ -0,0 +1,143 @@ +/* + * 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 + * 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 Defs_H +#define Defs_H + +#include + +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"); +const wxString TEXT_BASE_NAME = wxT("Text"); +const wxString USERS_BASE_NAME = wxT("Users"); +const wxString STARNET_BASE_NAME = wxT("STARnet"); +const wxString HEADERS_BASE_NAME = wxT("Headers"); +const wxString DDMODE_BASE_NAME = wxT("DDMode"); + +enum RECONNECT { + RECONNECT_NEVER, + RECONNECT_FIXED, + RECONNECT_5MINS, + RECONNECT_10MINS, + RECONNECT_15MINS, + RECONNECT_20MINS, + RECONNECT_25MINS, + RECONNECT_30MINS, + RECONNECT_60MINS, + RECONNECT_90MINS, + RECONNECT_120MINS, + RECONNECT_180MINS +}; + +enum DIRECTION { + DIR_INCOMING, + DIR_OUTGOING +}; + +enum PROTOCOL { + PROTO_DEXTRA, + PROTO_DPLUS, + PROTO_DCS, + PROTO_CCS +}; + +enum HW_TYPE { + HW_HOMEBREW, + HW_ICOM, + HW_DUMMY +}; + +enum TEXT_LANG { + TL_ENGLISH_UK, + TL_DEUTSCH, + TL_DANSK, + TL_FRANCAIS, + TL_ITALIANO, + TL_POLSKI, + TL_ENGLISH_US, + TL_ESPANOL, + TL_SVENSKA, + TL_NEDERLANDS_NL, + TL_NEDERLANDS_BE, + TL_NORSK, + TL_PORTUGUES +}; + +enum IRCDDB_STATUS { + IS_DISABLED, + IS_DISCONNECTED, + IS_CONNECTING, + IS_CONNECTED +}; + +enum G2_STATUS { + G2_NONE, + G2_LOCAL, + G2_USER, + G2_REPEATER, + G2_OK, + G2_XBAND, + G2_ECHO, + G2_VERSION, + G2_STARNET +}; + +enum LINK_STATUS { + LS_NONE, + LS_PENDING_IRCDDB, + LS_LINKING_LOOPBACK, + 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_CCS +}; + +enum SLOWDATA_STATE { + SS_FIRST, + SS_SECOND +}; + +enum STARNET_CALLSIGN_SWITCH { + SCS_GROUP_CALLSIGN, + SCS_USER_CALLSIGN +}; + +enum GATEWAY_TYPE { + GT_REPEATER, + GT_HOTSPOT, + GT_DONGLE, + GT_STARNET +}; + +const unsigned int TIME_PER_TIC_MS = 5U; + +#if defined(__WINDOWS__) +typedef unsigned long in_addr_t; +#endif + +#endif diff --git a/Common/DummyRepeaterProtocolHandler.cpp b/Common/DummyRepeaterProtocolHandler.cpp new file mode 100644 index 0000000..44ae8cb --- /dev/null +++ b/Common/DummyRepeaterProtocolHandler.cpp @@ -0,0 +1,133 @@ +/* + * 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 "DummyRepeaterProtocolHandler.h" + +#include "DStarDefines.h" +#include "Utils.h" + +CDummyRepeaterProtocolHandler::CDummyRepeaterProtocolHandler() +{ +} + +CDummyRepeaterProtocolHandler::~CDummyRepeaterProtocolHandler() +{ +} + +bool CDummyRepeaterProtocolHandler::open() +{ + return true; +} + +bool CDummyRepeaterProtocolHandler::writeHeader(CHeaderData& header) +{ + unsigned char buffer[50U]; + unsigned int length = header.getHBRepeaterData(buffer, 50U, true); + + wxLogMessage(wxT("Sending Header to port: %u, id: %04X"), header.getYourPort(), header.getId()); + + CUtils::dump(wxT("Data"), buffer + 8U, length - 8U); + + return true; +} + +bool CDummyRepeaterProtocolHandler::writeAMBE(CAMBEData& data) +{ + unsigned char buffer[30U]; + unsigned int length = data.getHBRepeaterData(buffer, 30U); + + wxLogMessage(wxT("Sending AMBE to port: %u, seq: %02X, id: %04X"), data.getYourPort(), data.getSeq(), data.getId()); + + CUtils::dump(wxT("Data"), buffer + 9U, length - 9U); + + return true; +} + +bool CDummyRepeaterProtocolHandler::writeDD(CDDData& data) +{ + unsigned char buffer[2000U]; + unsigned int length = data.getHBRepeaterData(buffer, 2000U); + + CUtils::dump(wxT("DD Data"), buffer, length); + + return true; +} + +bool CDummyRepeaterProtocolHandler::writeText(CTextData& text) +{ + unsigned char buffer[40U]; + unsigned int length = text.getHBRepeaterData(buffer, 40U); + + CUtils::dump(wxT("Sending Text"), buffer, length); + + return true; +} + +bool CDummyRepeaterProtocolHandler::writeStatus(CStatusData& status) +{ + unsigned char buffer[30U]; + unsigned int length = status.getHBRepeaterData(buffer, 30U); + + CUtils::dump(wxT("Sending Status"), buffer, length); + + return true; +} + +REPEATER_TYPE CDummyRepeaterProtocolHandler::read() +{ + return RT_NONE; +} + +CPollData* CDummyRepeaterProtocolHandler::readPoll() +{ + return NULL; +} + +CHeaderData* CDummyRepeaterProtocolHandler::readHeader() +{ + return NULL; +} + +CAMBEData* CDummyRepeaterProtocolHandler::readAMBE() +{ + return NULL; +} + +CHeaderData* CDummyRepeaterProtocolHandler::readBusyHeader() +{ + return NULL; +} + +CAMBEData* CDummyRepeaterProtocolHandler::readBusyAMBE() +{ + return NULL; +} + +CHeardData* CDummyRepeaterProtocolHandler::readHeard() +{ + return NULL; +} + +CDDData* CDummyRepeaterProtocolHandler::readDD() +{ + return NULL; +} + +void CDummyRepeaterProtocolHandler::close() +{ +} diff --git a/Common/DummyRepeaterProtocolHandler.h b/Common/DummyRepeaterProtocolHandler.h new file mode 100644 index 0000000..5fc2a37 --- /dev/null +++ b/Common/DummyRepeaterProtocolHandler.h @@ -0,0 +1,67 @@ +/* + * 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 DummyRepeaterProtocolHandler_H +#define DummyRepeaterProtocolHandler_H + +#include "RepeaterProtocolHandler.h" +#include "DStarDefines.h" +#include "HeaderData.h" +#include "StatusData.h" +#include "HeardData.h" +#include "AMBEData.h" +#include "TextData.h" +#include "PollData.h" +#include "DDData.h" + +#if defined(__WINDOWS__) +#include "Inaddr.h" +#else +#include +#endif + +#include + +class CDummyRepeaterProtocolHandler : public IRepeaterProtocolHandler { +public: + CDummyRepeaterProtocolHandler(); + virtual ~CDummyRepeaterProtocolHandler(); + + virtual bool open(); + + virtual bool writeHeader(CHeaderData& header); + virtual bool writeAMBE(CAMBEData& data); + virtual bool writeDD(CDDData& data); + virtual bool writeText(CTextData& text); + virtual bool writeStatus(CStatusData& status); + + virtual REPEATER_TYPE read(); + virtual CPollData* readPoll(); + virtual CHeardData* readHeard(); + virtual CHeaderData* readHeader(); + virtual CAMBEData* readAMBE(); + virtual CDDData* readDD(); + virtual CHeaderData* readBusyHeader(); + virtual CAMBEData* readBusyAMBE(); + + virtual void close(); + +private: +}; + +#endif diff --git a/Common/EchoUnit.cpp b/Common/EchoUnit.cpp new file mode 100644 index 0000000..0e82e46 --- /dev/null +++ b/Common/EchoUnit.cpp @@ -0,0 +1,154 @@ +/* + * 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 + * 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 "EchoUnit.h" +#include "Defs.h" + +const unsigned int MAX_FRAMES = 60U * DSTAR_FRAMES_PER_SEC; + +CEchoUnit::CEchoUnit(IRepeaterCallback* handler, const wxString& callsign) : +m_handler(handler), +m_callsign(callsign), +m_status(ES_IDLE), +m_timer(1000U, REPLY_TIME), +m_header(NULL), +m_data(NULL), +m_in(0U), +m_out(0U), +m_time() +{ + wxASSERT(handler != NULL); + + m_data = new CAMBEData*[MAX_FRAMES]; + + for (unsigned int i = 0U; i < MAX_FRAMES; i++) + m_data[i] = NULL; +} + +CEchoUnit::~CEchoUnit() +{ + delete[] m_data; +} + +void CEchoUnit::writeHeader(const CHeaderData& header) +{ + if (m_status != ES_IDLE) + return; + + m_header = new CHeaderData(header); + + m_in = 0U; + m_status = ES_RECEIVE; +} + +void CEchoUnit::writeData(const CAMBEData& data) +{ + if (m_status != ES_RECEIVE) + return; + + if (m_in < MAX_FRAMES) { + m_data[m_in] = new CAMBEData(data); + m_in++; + } + + if (data.isEnd()) { + wxLogMessage(wxT("Received %.1f secs of audio from %s for echoing"), float(m_in) / float(DSTAR_FRAMES_PER_SEC), m_header->getMyCall1().c_str()); + + m_timer.start(); + m_status = ES_WAIT; + } +} + +void CEchoUnit::end() +{ + if (m_status != ES_RECEIVE) + return; + + wxLogMessage(wxT("Received %.1f secs of audio from %s for echoing"), float(m_in) / float(DSTAR_FRAMES_PER_SEC), m_header->getMyCall1().c_str()); + + m_timer.start(); + m_status = ES_WAIT; +} + +void CEchoUnit::clock(unsigned int ms) +{ + m_timer.clock(ms); + + if (m_status == ES_WAIT && m_timer.hasExpired()) { + m_timer.stop(); + + // RPT1 and RPT2 will be filled in later + m_header->setMyCall1(m_callsign); + m_header->setMyCall2(wxT("ECHO")); + m_header->setYourCall(wxT("CQCQCQ ")); + + m_handler->process(*m_header, DIR_INCOMING, AS_ECHO); + + delete m_header; + + m_out = 0U; + m_status = ES_TRANSMIT; + + m_time.Start(); + + return; + } + + if (m_status == ES_TRANSMIT) { + unsigned int needed = m_time.Time() / DSTAR_FRAME_TIME_MS; + + while (m_out < needed) { + CAMBEData* data = m_data[m_out]; + m_data[m_out] = NULL; + m_out++; + + if (m_in == m_out) + data->setEnd(true); + + m_handler->process(*data, DIR_INCOMING, AS_ECHO); + + delete data; + + if (m_in == m_out) { + m_in = 0U; + m_out = 0U; + m_status = ES_IDLE; + return; + } + } + + return; + } +} + +void CEchoUnit::cancel() +{ + for (unsigned int i = 0U; i < MAX_FRAMES; i++) { + if (m_data[i] != NULL) { + delete m_data[i]; + m_data[i] = NULL; + } + } + + m_status = ES_IDLE; + m_out = 0U; + m_in = 0U; + + m_timer.stop(); +} diff --git a/Common/EchoUnit.h b/Common/EchoUnit.h new file mode 100644 index 0000000..1786c2f --- /dev/null +++ b/Common/EchoUnit.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2011,2012 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 EchoUnit_H +#define EchoUnit_H + +#include "RepeaterCallback.h" +#include "HeaderData.h" +#include "AMBEData.h" +#include "Timer.h" + +#include + +enum ECHO_STATUS { + ES_IDLE, + ES_RECEIVE, + ES_WAIT, + ES_TRANSMIT +}; + +class CEchoUnit { +public: + CEchoUnit(IRepeaterCallback* handler, const wxString& callsign); + ~CEchoUnit(); + + void writeHeader(const CHeaderData& header); + + void writeData(const CAMBEData& data); + + void end(); + + void cancel(); + + void clock(unsigned int ms); + +private: + IRepeaterCallback* m_handler; + wxString m_callsign; + ECHO_STATUS m_status; + CTimer m_timer; + CHeaderData* m_header; + CAMBEData** m_data; + unsigned int m_in; + unsigned int m_out; + wxStopWatch m_time; +}; + +#endif diff --git a/Common/G2Handler.cpp b/Common/G2Handler.cpp new file mode 100644 index 0000000..3604a61 --- /dev/null +++ b/Common/G2Handler.cpp @@ -0,0 +1,198 @@ +/* + * 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 + * 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 "StarNetHandler.h" +#include "DStarDefines.h" +#include "G2Handler.h" +#include "Utils.h" +#include "Defs.h" + +unsigned int CG2Handler::m_maxRoutes = 0U; +CG2Handler** CG2Handler::m_routes = NULL; + +CG2ProtocolHandler* CG2Handler::m_handler = NULL; + +CHeaderLogger* CG2Handler::m_headerLogger = NULL; + +CG2Handler::CG2Handler(CRepeaterHandler* repeater, const in_addr& address, unsigned int id) : +m_repeater(repeater), +m_address(address), +m_id(id), +m_inactivityTimer(1000U, NETWORK_TIMEOUT) +{ + m_inactivityTimer.start(); +} + +CG2Handler::~CG2Handler() +{ +} + +void CG2Handler::initialise(unsigned int maxRoutes) +{ + m_maxRoutes = maxRoutes; + + if (maxRoutes == 0U) + return; + + m_routes = new CG2Handler*[m_maxRoutes]; + for (unsigned int i = 0U; i < m_maxRoutes; i++) + m_routes[i] = NULL; +} + +void CG2Handler::setG2ProtocolHandler(CG2ProtocolHandler* handler) +{ + wxASSERT(handler != NULL); + + m_handler = handler; +} + +void CG2Handler::setHeaderLogger(CHeaderLogger* logger) +{ + m_headerLogger = logger; +} + +void CG2Handler::process(CHeaderData& header) +{ + // Is this a busy reply? + unsigned char flag1 = header.getFlag1(); + if (flag1 == 0x01) { + // Don't check the incoming stream + // wxLogMessage(wxT("G2 busy message received")); + return; + } + + // Check to see if this is for StarNet + CStarNetHandler* handler = CStarNetHandler::findStarNet(header); + if (handler != NULL) { + // Write to Header.log if it's enabled + if (m_headerLogger != NULL) + m_headerLogger->write(wxT("StarNet"), header); + + handler->process(header); + return; + } + + // No need to go any further + if (m_maxRoutes == 0U) + return; + + in_addr address = header.getYourAddress(); + unsigned int id = header.getId(); + + for (unsigned int i = 0U; i < m_maxRoutes; i++) { + CG2Handler* route = m_routes[i]; + if (route != NULL) { + // Is this a duplicate header, ignore it + if (route->m_id == id) + return; + } + } + + // Find the destination repeater + CRepeaterHandler* repeater = CRepeaterHandler::findDVRepeater(header.getRptCall2()); + if (repeater == NULL) { + wxLogMessage(wxT("Incoming G2 header from %s to unknown repeater - %s"), header.getMyCall1().c_str(), header.getRptCall2().c_str()); + return; // Not found, ignore + } + + CG2Handler* route = new CG2Handler(repeater, address, id); + + for (unsigned int i = 0U; i < m_maxRoutes; i++) { + if (m_routes[i] == NULL) { + m_routes[i] = route; + + // Write to Header.log if it's enabled + if (m_headerLogger != NULL) + m_headerLogger->write(wxT("G2"), header); + + repeater->process(header, DIR_INCOMING, AS_G2); + return; + } + } + + wxLogMessage(wxT("No space to add new G2 route, ignoring")); + + delete route; +} + +void CG2Handler::process(CAMBEData& data) +{ + // Check to see if this is for StarNet + CStarNetHandler* handler = CStarNetHandler::findStarNet(data); + if (handler != NULL) { + handler->process(data); + return; + } + + // No need to go any further + if (m_maxRoutes == 0U) + return; + + unsigned int id = data.getId(); + + for (unsigned int i = 0U; i < m_maxRoutes; i++) { + CG2Handler* route = m_routes[i]; + if (route != NULL) { + if (route->m_id == id) { + route->m_inactivityTimer.start(); + route->m_repeater->process(data, DIR_INCOMING, AS_G2); + + if (data.isEnd()) { + delete route; + m_routes[i] = NULL; + } + + return; + } + } + } +} + +void CG2Handler::clock(unsigned int ms) +{ + for (unsigned int i = 0U; i < m_maxRoutes; i++) { + CG2Handler* route = m_routes[i]; + if (route != NULL) { + bool ret = route->clockInt(ms); + if (ret) { + delete route; + m_routes[i] = NULL; + } + } + } +} + +void CG2Handler::finalise() +{ + for (unsigned int i = 0U; i < m_maxRoutes; i++) + delete m_routes[i]; + + delete[] m_routes; +} + +bool CG2Handler::clockInt(unsigned int ms) +{ + m_inactivityTimer.clock(ms); + + if (m_inactivityTimer.isRunning() && m_inactivityTimer.hasExpired()) { + wxLogMessage(wxT("Inactivity timeout for a G2 route has expired")); + return true; + } + + return false; +} diff --git a/Common/G2Handler.h b/Common/G2Handler.h new file mode 100644 index 0000000..d9e1402 --- /dev/null +++ b/Common/G2Handler.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2010,2012 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 G2Handler_H +#define G2Handler_H + +#include "G2ProtocolHandler.h" +#include "RepeaterHandler.h" +#include "DStarDefines.h" +#include "HeaderLogger.h" +#include "HeaderData.h" +#include "AMBEData.h" +#include "Timer.h" + +#if defined(__WINDOWS__) +#include "Inaddr.h" +#else +#include +#endif + +#include + +class CG2Handler { +public: + static void initialise(unsigned int maxRoutes); + + static void setG2ProtocolHandler(CG2ProtocolHandler* handler); + static void setHeaderLogger(CHeaderLogger* logger); + + static void process(CHeaderData& header); + static void process(CAMBEData& header); + + static void clock(unsigned int ms); + + static void finalise(); + +protected: + CG2Handler(CRepeaterHandler* repeater, const in_addr& address, unsigned int id); + ~CG2Handler(); + + bool clockInt(unsigned int ms); + +private: + static unsigned int m_maxRoutes; + static CG2Handler** m_routes; + + static CG2ProtocolHandler* m_handler; + + static CHeaderLogger* m_headerLogger; + + CRepeaterHandler* m_repeater; + in_addr m_address; + unsigned int m_id; + CTimer m_inactivityTimer; +}; + +#endif diff --git a/Common/G2ProtocolHandler.cpp b/Common/G2ProtocolHandler.cpp new file mode 100644 index 0000000..8f68b98 --- /dev/null +++ b/Common/G2ProtocolHandler.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2010,2011,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 "G2ProtocolHandler.h" + +#include "DStarDefines.h" +#include "Utils.h" + +// #define DUMP_TX + +const unsigned int BUFFER_LENGTH = 255U; + +CG2ProtocolHandler::CG2ProtocolHandler(unsigned int port, const wxString& addr) : +m_socket(addr, port), +m_type(GT_NONE), +m_buffer(NULL), +m_length(0U), +m_address(), +m_port(0U) +{ + m_buffer = new unsigned char[BUFFER_LENGTH]; +} + +CG2ProtocolHandler::~CG2ProtocolHandler() +{ + delete[] m_buffer; +} + +bool CG2ProtocolHandler::open() +{ + return m_socket.open(); +} + +bool CG2ProtocolHandler::writeHeader(const CHeaderData& header) +{ + unsigned char buffer[60U]; + unsigned int length = header.getG2Data(buffer, 60U, true); + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Header"), buffer, length); +#endif + + for (unsigned int i = 0U; i < 5U; i++) { + bool res = m_socket.write(buffer, length, header.getYourAddress(), header.getYourPort()); + if (!res) + return false; + } + + return true; +} + +bool CG2ProtocolHandler::writeAMBE(const CAMBEData& data) +{ + unsigned char buffer[40U]; + unsigned int length = data.getG2Data(buffer, 40U); + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Data"), buffer, length); +#endif + + return m_socket.write(buffer, length, data.getYourAddress(), data.getYourPort()); +} + +G2_TYPE CG2ProtocolHandler::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 CG2ProtocolHandler::readPackets() +{ + m_type = GT_NONE; + + // No more data? + int length = m_socket.read(m_buffer, BUFFER_LENGTH, m_address, m_port); + if (length <= 0) + return false; + + m_length = length; + + if (m_buffer[0] != 'D' || m_buffer[1] != 'S' || m_buffer[2] != 'V' || m_buffer[3] != 'T') { + return true; + } else { + // Header or data packet type? + if ((m_buffer[14] & 0x80) == 0x80) + m_type = GT_HEADER; + else + m_type = GT_AMBE; + + return false; + } +} + +CHeaderData* CG2ProtocolHandler::readHeader() +{ + if (m_type != GT_HEADER) + return NULL; + + CHeaderData* header = new CHeaderData; + + // G2 checksums are unreliable + bool res = header->setG2Data(m_buffer, m_length, false, m_address, m_port); + if (!res) { + delete header; + return NULL; + } + + return header; +} + +CAMBEData* CG2ProtocolHandler::readAMBE() +{ + if (m_type != GT_AMBE) + return NULL; + + CAMBEData* data = new CAMBEData; + + bool res = data->setG2Data(m_buffer, m_length, m_address, m_port); + if (!res) { + delete data; + return NULL; + } + + return data; +} + +void CG2ProtocolHandler::close() +{ + m_socket.close(); +} diff --git a/Common/G2ProtocolHandler.h b/Common/G2ProtocolHandler.h new file mode 100644 index 0000000..3a87834 --- /dev/null +++ b/Common/G2ProtocolHandler.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2010,2011 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 G2ProtocolHandler_H +#define G2ProtocolHandler_H + +#include "UDPReaderWriter.h" +#include "DStarDefines.h" +#include "HeaderData.h" +#include "AMBEData.h" + +#if defined(__WINDOWS__) +#include "Inaddr.h" +#else +#include +#endif + +#include + +enum G2_TYPE { + GT_NONE, + GT_HEADER, + GT_AMBE +}; + +class CG2ProtocolHandler { +public: + CG2ProtocolHandler(unsigned int port, const wxString& addr = wxEmptyString); + ~CG2ProtocolHandler(); + + bool open(); + + bool writeHeader(const CHeaderData& header); + bool writeAMBE(const CAMBEData& data); + + G2_TYPE read(); + CHeaderData* readHeader(); + CAMBEData* readAMBE(); + + void close(); + +private: + CUDPReaderWriter m_socket; + G2_TYPE m_type; + unsigned char* m_buffer; + unsigned int m_length; + in_addr m_address; + unsigned int m_port; + + bool readPackets(); +}; + +#endif diff --git a/Common/GatewayCache.cpp b/Common/GatewayCache.cpp new file mode 100644 index 0000000..b029d7c --- /dev/null +++ b/Common/GatewayCache.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2010,2011,2012 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 "GatewayCache.h" + +const unsigned int CACHE_SIZE = 500U; + +CGatewayCache::CGatewayCache() : +m_cache(CACHE_SIZE) +{ +} + +CGatewayCache::~CGatewayCache() +{ + for (CGatewayCache_t::iterator it = m_cache.begin(); it != m_cache.end(); ++it) + delete it->second; +} + +CGatewayRecord* CGatewayCache::find(const wxString& gateway) +{ + return m_cache[gateway]; +} + +void CGatewayCache::update(const wxString& gateway, const wxString& address, DSTAR_PROTOCOL protocol, bool addrLock, bool protoLock) +{ + CGatewayRecord* rec = m_cache[gateway]; + + in_addr addr_in; + addr_in.s_addr = ::inet_addr(address.mb_str()); + + if (rec == NULL) + // A brand new record is needed + m_cache[gateway] = new CGatewayRecord(gateway, addr_in, protocol, addrLock, protoLock); + else + // Update an existing record + rec->setData(addr_in, protocol, addrLock, protoLock); +} + +unsigned int CGatewayCache::getCount() const +{ + return m_cache.size(); +} diff --git a/Common/GatewayCache.h b/Common/GatewayCache.h new file mode 100644 index 0000000..561743c --- /dev/null +++ b/Common/GatewayCache.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2010,2011,2012 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 GatewayCache_H +#define GatewayCache_H + +#include "DStarDefines.h" +#include "Defs.h" + +#if defined(__WINDOWS__) +#include "Inaddr.h" +#else +#include +#include +#include +#endif + +#include +#include + +class CGatewayRecord { +public: + CGatewayRecord(const wxString& gateway, in_addr address, DSTAR_PROTOCOL protocol, bool addrLock, bool protoLock) : + m_gateway(gateway), + m_address(address), + m_protocol(DP_UNKNOWN), + m_addrLock(addrLock), + m_protoLock(false) + { + if (protocol != DP_UNKNOWN) { + m_protocol = protocol; + m_protoLock = protoLock; + } + } + + wxString getGateway() const + { + return m_gateway; + } + + in_addr getAddress() const + { + return m_address; + } + + DSTAR_PROTOCOL getProtocol() const + { + return m_protocol; + } + + void setData(in_addr address, DSTAR_PROTOCOL protocol, bool addrLock, bool protoLock) + { + if (!m_addrLock) { + m_address = address; + m_addrLock = addrLock; + } + + if (!m_protoLock) { + if (protocol != DP_UNKNOWN) { + m_protocol = protocol; + m_protoLock = protoLock; + } + } + } + +private: + wxString m_gateway; + in_addr m_address; + DSTAR_PROTOCOL m_protocol; + bool m_addrLock; + bool m_protoLock; +}; + +WX_DECLARE_STRING_HASH_MAP(CGatewayRecord*, CGatewayCache_t); + +class CGatewayCache { +public: + CGatewayCache(); + ~CGatewayCache(); + + CGatewayRecord* find(const wxString& gateway); + + void update(const wxString& gateway, const wxString& address, DSTAR_PROTOCOL protocol, bool addrLock, bool protoLock); + + unsigned int getCount() const; + +private: + CGatewayCache_t m_cache; +}; + +#endif diff --git a/Common/HBRepeaterProtocolHandler.cpp b/Common/HBRepeaterProtocolHandler.cpp new file mode 100644 index 0000000..0ee7c45 --- /dev/null +++ b/Common/HBRepeaterProtocolHandler.cpp @@ -0,0 +1,285 @@ +/* + * 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 + * 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 "HBRepeaterProtocolHandler.h" + +#include "CCITTChecksum.h" +#include "DStarDefines.h" +#include "Utils.h" + +const unsigned int BUFFER_LENGTH = 255U; + +CHBRepeaterProtocolHandler::CHBRepeaterProtocolHandler(const wxString& address, unsigned int port) : +m_socket(address, port), +m_type(RT_NONE), +m_buffer(NULL), +m_length(0U), +m_address(), +m_port(0U) +{ + wxASSERT(!address.IsEmpty()); + wxASSERT(port > 0U); + + m_buffer = new unsigned char[BUFFER_LENGTH]; +} + +CHBRepeaterProtocolHandler::~CHBRepeaterProtocolHandler() +{ + delete[] m_buffer; +} + +bool CHBRepeaterProtocolHandler::open() +{ + return m_socket.open(); +} + +bool CHBRepeaterProtocolHandler::writeHeader(CHeaderData& header) +{ + unsigned char buffer[50U]; + unsigned int length = header.getHBRepeaterData(buffer, 50U, true); + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Header"), buffer, length); + return true; +#else + return m_socket.write(buffer, length, header.getYourAddress(), header.getYourPort()); +#endif +} + +bool CHBRepeaterProtocolHandler::writeAMBE(CAMBEData& data) +{ + unsigned char buffer[30U]; + unsigned int length = data.getHBRepeaterData(buffer, 30U); + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Data"), buffer, length); + return true; +#else + return m_socket.write(buffer, length, data.getYourAddress(), data.getYourPort()); +#endif +} + +bool CHBRepeaterProtocolHandler::writeDD(CDDData& data) +{ + unsigned char buffer[2000U]; + unsigned int length = data.getHBRepeaterData(buffer, 2000U); + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending DD Data"), buffer, length); + return true; +#else + return m_socket.write(buffer, length, data.getYourAddress(), data.getYourPort()); +#endif +} + +bool CHBRepeaterProtocolHandler::writeText(CTextData& text) +{ + unsigned char buffer[40U]; + unsigned int length = text.getHBRepeaterData(buffer, 40U); + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Text"), buffer, length); + return true; +#else + return m_socket.write(buffer, length, text.getAddress(), text.getPort()); +#endif +} + +bool CHBRepeaterProtocolHandler::writeStatus(CStatusData& status) +{ + unsigned char buffer[30U]; + unsigned int length = status.getHBRepeaterData(buffer, 30U); + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Status"), buffer, length); + return true; +#else + return m_socket.write(buffer, length, status.getAddress(), status.getPort()); +#endif +} + +REPEATER_TYPE CHBRepeaterProtocolHandler::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 CHBRepeaterProtocolHandler::readPackets() +{ + m_type = RT_NONE; + + // No more data? + int length = m_socket.read(m_buffer, BUFFER_LENGTH, m_address, m_port); + if (length <= 0) + return false; + + m_length = length; + + // Invalid packet type? + if (m_buffer[0] == 'D' && m_buffer[1] == 'S' && m_buffer[2] == 'R' && m_buffer[3] == 'P') { + // Poll data + if (m_buffer[4] == 0x0AU) { + m_type = RT_POLL; + return false; + } + + // Header data + else if (m_buffer[4] == 0x20U) { + m_type = RT_HEADER; + return false; + } + + // User data + else if (m_buffer[4] == 0x21U) { + m_type = RT_AMBE; + return false; + } + + // DD data + else if (m_buffer[4] == 0x24U) { + m_type = RT_DD; + return false; + } + + // Busy header data + else if (m_buffer[4] == 0x22U) { + m_type = RT_BUSY_HEADER; + return false; + } + + // Busy user data + else if (m_buffer[4] == 0x23U) { + m_type = RT_BUSY_AMBE; + return false; + } + } + + CUtils::dump(wxT("Unknown packet from the Repeater"), m_buffer, m_length); + + return true; +} + +CPollData* CHBRepeaterProtocolHandler::readPoll() +{ + if (m_type != RT_POLL) + return NULL; + + wxString text = wxString((char*)(m_buffer + 5U), wxConvLocal); + + return new CPollData(text, m_address, m_port, m_socket.getPort()); +} + +CHeaderData* CHBRepeaterProtocolHandler::readHeader() +{ + if (m_type != RT_HEADER) + return NULL; + + CHeaderData* header = new CHeaderData; + + bool res = header->setHBRepeaterData(m_buffer, m_length, true, m_address, m_port); + if (!res) { + wxLogError(wxT("Invalid checksum from the repeater")); + delete header; + return NULL; + } + + return header; +} + +CAMBEData* CHBRepeaterProtocolHandler::readAMBE() +{ + if (m_type != RT_AMBE) + return NULL; + + CAMBEData* data = new CAMBEData; + + bool res = data->setHBRepeaterData(m_buffer, m_length, m_address, m_port); + if (!res) { + wxLogError(wxT("Invalid AMBE data from the repeater")); + delete data; + return NULL; + } + + return data; +} + +CHeaderData* CHBRepeaterProtocolHandler::readBusyHeader() +{ + if (m_type != RT_BUSY_HEADER) + return NULL; + + CHeaderData* header = new CHeaderData; + + bool res = header->setHBRepeaterData(m_buffer, m_length, true, m_address, m_port); + if (!res) { + wxLogError(wxT("Invalid checksum from the repeater")); + delete header; + return NULL; + } + + return header; +} + +CAMBEData* CHBRepeaterProtocolHandler::readBusyAMBE() +{ + if (m_type != RT_BUSY_AMBE) + return NULL; + + CAMBEData* data = new CAMBEData; + + bool res = data->setHBRepeaterData(m_buffer, m_length, m_address, m_port); + if (!res) { + wxLogError(wxT("Invalid AMBE data from the repeater")); + delete data; + return NULL; + } + + return data; +} + +CHeardData* CHBRepeaterProtocolHandler::readHeard() +{ + return NULL; +} + +CDDData* CHBRepeaterProtocolHandler::readDD() +{ + if (m_type != RT_DD) + return NULL; + + CDDData* data = new CDDData; + + bool res = data->setHBRepeaterData(m_buffer, m_length, m_address, m_port); + if (!res) { + wxLogError(wxT("Invalid DD data from the repeater")); + delete data; + return NULL; + } + + return data; +} + +void CHBRepeaterProtocolHandler::close() +{ + m_socket.close(); +} diff --git a/Common/HBRepeaterProtocolHandler.h b/Common/HBRepeaterProtocolHandler.h new file mode 100644 index 0000000..f2de49e --- /dev/null +++ b/Common/HBRepeaterProtocolHandler.h @@ -0,0 +1,76 @@ +/* + * 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 + * 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 HBRepeaterProtocolHandler_H +#define HBRepeaterProtocolHandler_H + +#include "RepeaterProtocolHandler.h" +#include "UDPReaderWriter.h" +#include "DStarDefines.h" +#include "HeaderData.h" +#include "StatusData.h" +#include "HeardData.h" +#include "AMBEData.h" +#include "TextData.h" +#include "PollData.h" +#include "DDData.h" + +#if defined(__WINDOWS__) +#include "Inaddr.h" +#else +#include +#endif + +#include + +class CHBRepeaterProtocolHandler : public IRepeaterProtocolHandler { +public: + CHBRepeaterProtocolHandler(const wxString& address, unsigned int port); + virtual ~CHBRepeaterProtocolHandler(); + + virtual bool open(); + + virtual bool writeHeader(CHeaderData& header); + virtual bool writeAMBE(CAMBEData& data); + virtual bool writeDD(CDDData& data); + virtual bool writeText(CTextData& text); + virtual bool writeStatus(CStatusData& status); + + virtual REPEATER_TYPE read(); + virtual CPollData* readPoll(); + virtual CHeardData* readHeard(); + virtual CHeaderData* readHeader(); + virtual CAMBEData* readAMBE(); + virtual CDDData* readDD(); + virtual CHeaderData* readBusyHeader(); + virtual CAMBEData* readBusyAMBE(); + + virtual void close(); + +private: + CUDPReaderWriter m_socket; + REPEATER_TYPE m_type; + unsigned char* m_buffer; + unsigned int m_length; + in_addr m_address; + unsigned int m_port; + + bool readPackets(); +}; + +#endif diff --git a/Common/HeaderData.cpp b/Common/HeaderData.cpp new file mode 100644 index 0000000..fd518bd --- /dev/null +++ b/Common/HeaderData.cpp @@ -0,0 +1,951 @@ +/* + * 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 + * 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 "HeaderData.h" + +#include + +#include "CCITTChecksum.h" +#include "DStarDefines.h" +#include "Utils.h" + +void CHeaderData::initialise() +{ + wxDateTime now = wxDateTime::UNow(); + ::srand(now.GetMillisecond()); +} + +void CHeaderData::finalise() +{ +} + +unsigned int CHeaderData::createId() +{ + return (::rand() % 65535U) + 1U; +} + +CHeaderData::CHeaderData() : +m_rptSeq(0U), +m_id(0U), +m_band1(0x00U), +m_band2(0x02U), +m_band3(0x01U), +m_flag1(0U), +m_flag2(0U), +m_flag3(0U), +m_myCall1(NULL), +m_myCall2(NULL), +m_yourCall(NULL), +m_rptCall1(NULL), +m_rptCall2(NULL), +m_yourAddress(), +m_yourPort(0U), +m_myPort(0U), +m_errors(0U) +{ + m_myCall1 = new unsigned char[LONG_CALLSIGN_LENGTH]; + m_myCall2 = new unsigned char[SHORT_CALLSIGN_LENGTH]; + m_yourCall = new unsigned char[LONG_CALLSIGN_LENGTH]; + m_rptCall1 = new unsigned char[LONG_CALLSIGN_LENGTH]; + m_rptCall2 = new unsigned char[LONG_CALLSIGN_LENGTH]; + + ::memset(m_rptCall1, ' ', LONG_CALLSIGN_LENGTH); + ::memset(m_rptCall2, ' ', LONG_CALLSIGN_LENGTH); + ::memset(m_yourCall, ' ', LONG_CALLSIGN_LENGTH); + ::memset(m_myCall1, ' ', LONG_CALLSIGN_LENGTH); + ::memset(m_myCall2, ' ', SHORT_CALLSIGN_LENGTH); +} + +CHeaderData::CHeaderData(const CHeaderData& header) : +m_rptSeq(header.m_rptSeq), +m_id(header.m_id), +m_band1(header.m_band1), +m_band2(header.m_band2), +m_band3(header.m_band3), +m_flag1(header.m_flag1), +m_flag2(header.m_flag2), +m_flag3(header.m_flag3), +m_myCall1(NULL), +m_myCall2(NULL), +m_yourCall(NULL), +m_rptCall1(NULL), +m_rptCall2(NULL), +m_yourAddress(header.m_yourAddress), +m_yourPort(header.m_yourPort), +m_myPort(header.m_myPort), +m_errors(header.m_errors) +{ + m_myCall1 = new unsigned char[LONG_CALLSIGN_LENGTH]; + m_myCall2 = new unsigned char[SHORT_CALLSIGN_LENGTH]; + m_yourCall = new unsigned char[LONG_CALLSIGN_LENGTH]; + m_rptCall1 = new unsigned char[LONG_CALLSIGN_LENGTH]; + m_rptCall2 = new unsigned char[LONG_CALLSIGN_LENGTH]; + + ::memcpy(m_myCall1, header.m_myCall1, LONG_CALLSIGN_LENGTH); + ::memcpy(m_myCall2, header.m_myCall2, SHORT_CALLSIGN_LENGTH); + ::memcpy(m_yourCall, header.m_yourCall, LONG_CALLSIGN_LENGTH); + ::memcpy(m_rptCall1, header.m_rptCall1, LONG_CALLSIGN_LENGTH); + ::memcpy(m_rptCall2, header.m_rptCall2, LONG_CALLSIGN_LENGTH); +} + +CHeaderData::CHeaderData(const wxString& myCall1, const wxString& myCall2, const wxString& yourCall, + const wxString& rptCall1, const wxString& rptCall2, unsigned char flag1, + unsigned char flag2, unsigned char flag3) : +m_rptSeq(0U), +m_id(0U), +m_band1(0U), +m_band2(0U), +m_band3(0U), +m_flag1(flag1), +m_flag2(flag2), +m_flag3(flag3), +m_myCall1(NULL), +m_myCall2(NULL), +m_yourCall(NULL), +m_rptCall1(NULL), +m_rptCall2(NULL), +m_yourAddress(), +m_yourPort(0U), +m_myPort(0U), +m_errors(0U) +{ + m_myCall1 = new unsigned char[LONG_CALLSIGN_LENGTH]; + m_myCall2 = new unsigned char[SHORT_CALLSIGN_LENGTH]; + m_yourCall = new unsigned char[LONG_CALLSIGN_LENGTH]; + m_rptCall1 = new unsigned char[LONG_CALLSIGN_LENGTH]; + m_rptCall2 = new unsigned char[LONG_CALLSIGN_LENGTH]; + + ::memset(m_myCall1, ' ', LONG_CALLSIGN_LENGTH); + ::memset(m_myCall2, ' ', SHORT_CALLSIGN_LENGTH); + ::memset(m_yourCall, ' ', LONG_CALLSIGN_LENGTH); + ::memset(m_rptCall1, ' ', LONG_CALLSIGN_LENGTH); + ::memset(m_rptCall2, ' ', LONG_CALLSIGN_LENGTH); + + for (unsigned int i = 0U; i < myCall1.Len() && i < LONG_CALLSIGN_LENGTH; i++) + m_myCall1[i] = myCall1.GetChar(i); + + for (unsigned int i = 0U; i < myCall2.Len() && i < SHORT_CALLSIGN_LENGTH; i++) + m_myCall2[i] = myCall2.GetChar(i); + + for (unsigned int i = 0U; i < yourCall.Len() && i < LONG_CALLSIGN_LENGTH; i++) + m_yourCall[i] = yourCall.GetChar(i); + + for (unsigned int i = 0U; i < rptCall1.Len() && i < LONG_CALLSIGN_LENGTH; i++) + m_rptCall1[i] = rptCall1.GetChar(i); + + for (unsigned int i = 0U; i < rptCall2.Len() && i < LONG_CALLSIGN_LENGTH; i++) + m_rptCall2[i] = rptCall2.GetChar(i); +} + +CHeaderData::~CHeaderData() +{ + delete[] m_myCall1; + delete[] m_myCall2; + delete[] m_yourCall; + delete[] m_rptCall1; + delete[] m_rptCall2; +} + +bool CHeaderData::setIcomRepeaterData(const unsigned char *data, unsigned int length, bool check, const in_addr& yourAddress, unsigned int yourPort) +{ + wxASSERT(data != NULL); + wxASSERT(length >= 58U); + + m_rptSeq = data[4] * 256U + data[5]; + m_band1 = data[11]; + m_band2 = data[12]; + m_band3 = data[13]; + m_id = data[14] * 256U + data[15]; + + m_flag1 = data[17U]; + m_flag2 = data[18U]; + m_flag3 = data[19U]; + + ::memcpy(m_rptCall2, data + 20U, LONG_CALLSIGN_LENGTH); + ::memcpy(m_rptCall1, data + 28U, LONG_CALLSIGN_LENGTH); + ::memcpy(m_yourCall, data + 36U, LONG_CALLSIGN_LENGTH); + ::memcpy(m_myCall1, data + 44U, LONG_CALLSIGN_LENGTH); + ::memcpy(m_myCall2, data + 52U, SHORT_CALLSIGN_LENGTH); + + m_yourAddress = yourAddress; + m_yourPort = yourPort; + + if (check) { + CCCITTChecksum cksum; + cksum.update(data + 17U, RADIO_HEADER_LENGTH_BYTES - 2U); + bool valid = cksum.check(data + 17U + RADIO_HEADER_LENGTH_BYTES - 2U); + + if (!valid) + CUtils::dump(wxT("Header checksum failure from the repeater"), data + 17U, RADIO_HEADER_LENGTH_BYTES); + + return valid; + } else { + return true; + } +} + +bool CHeaderData::setHBRepeaterData(const unsigned char *data, unsigned int length, bool check, const in_addr& yourAddress, unsigned int yourPort) +{ + wxASSERT(data != NULL); + wxASSERT(length >= 49U); + + m_id = data[5U] * 256U + data[6U]; + m_errors = data[7U]; + + m_flag1 = data[8U]; + m_flag2 = data[9U]; + m_flag3 = data[10U]; + + ::memcpy(m_rptCall2, data + 11U, LONG_CALLSIGN_LENGTH); + ::memcpy(m_rptCall1, data + 19U, LONG_CALLSIGN_LENGTH); + ::memcpy(m_yourCall, data + 27U, LONG_CALLSIGN_LENGTH); + ::memcpy(m_myCall1, data + 35U, LONG_CALLSIGN_LENGTH); + ::memcpy(m_myCall2, data + 43U, SHORT_CALLSIGN_LENGTH); + + m_yourAddress = yourAddress; + m_yourPort = yourPort; + + if (check) { + CCCITTChecksum cksum; + cksum.update(data + 8U, RADIO_HEADER_LENGTH_BYTES - 2U); + bool valid = cksum.check(data + 8U + RADIO_HEADER_LENGTH_BYTES - 2U); + + if (!valid) + CUtils::dump(wxT("Header checksum failure from the repeater"), data + 8U, RADIO_HEADER_LENGTH_BYTES); + + return valid; + } else { + return true; + } +} + +void CHeaderData::setDCSData(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]; + + m_flag1 = data[4U]; + m_flag2 = data[5U]; + m_flag3 = data[6U]; + + ::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; +} + +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); + wxASSERT(length >= 56U); + + m_band1 = data[9]; + m_band2 = data[10]; + m_band3 = data[11]; + m_id = data[12] * 256U + data[13]; + + m_flag1 = data[15U]; + m_flag2 = data[16U]; + m_flag3 = data[17U]; + + ::memcpy(m_rptCall2, data + 18U, LONG_CALLSIGN_LENGTH); + ::memcpy(m_rptCall1, data + 26U, LONG_CALLSIGN_LENGTH); + ::memcpy(m_yourCall, data + 34U, LONG_CALLSIGN_LENGTH); + ::memcpy(m_myCall1, data + 42U, LONG_CALLSIGN_LENGTH); + ::memcpy(m_myCall2, data + 50U, SHORT_CALLSIGN_LENGTH); + + m_yourAddress = yourAddress; + m_yourPort = yourPort; + + if (check) { + CCCITTChecksum cksum; + cksum.update(data + 15U, RADIO_HEADER_LENGTH_BYTES - 2U); + bool valid = cksum.check(data + 15U + RADIO_HEADER_LENGTH_BYTES - 2U); + + if (!valid) + CUtils::dump(wxT("Header checksum failure from G2"), data + 15U, RADIO_HEADER_LENGTH_BYTES); + + return valid; + } else { + return true; + } +} + +bool CHeaderData::setDExtraData(const unsigned char *data, unsigned int length, bool check, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) +{ + wxASSERT(data != NULL); + wxASSERT(length >= 56U); + + m_band1 = data[9]; + m_band2 = data[10]; + m_band3 = data[11]; + m_id = data[12] * 256U + data[13]; + + m_flag1 = data[15U]; + m_flag2 = data[16U]; + m_flag3 = data[17U]; + + ::memcpy(m_rptCall2, data + 18U, LONG_CALLSIGN_LENGTH); + ::memcpy(m_rptCall1, data + 26U, LONG_CALLSIGN_LENGTH); + ::memcpy(m_yourCall, data + 34U, LONG_CALLSIGN_LENGTH); + ::memcpy(m_myCall1, data + 42U, LONG_CALLSIGN_LENGTH); + ::memcpy(m_myCall2, data + 50U, SHORT_CALLSIGN_LENGTH); + + m_yourAddress = yourAddress; + m_yourPort = yourPort; + m_myPort = myPort; + + if (check) { + CCCITTChecksum cksum; + cksum.update(data + 15U, RADIO_HEADER_LENGTH_BYTES - 2U); + bool valid = cksum.check(data + 15U + RADIO_HEADER_LENGTH_BYTES - 2U); + + if (!valid) + CUtils::dump(wxT("Header checksum failure from DExtra"), data + 15U, RADIO_HEADER_LENGTH_BYTES); + + return valid; + } else { + return true; + } +} + +bool CHeaderData::setDPlusData(const unsigned char *data, unsigned int length, bool check, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) +{ + wxASSERT(data != NULL); + wxASSERT(length >= 58U); + + if (data[0] != 0x3A || data[1] != 0x80) { + CUtils::dump(wxT("Invalid header length from D-Plus"), data, length); + return false; + } + + m_band1 = data[11]; + m_band2 = data[12]; + m_band3 = data[13]; + m_id = data[14] * 256U + data[15]; + + m_flag1 = data[17U]; + m_flag2 = data[18U]; + m_flag3 = data[19U]; + + ::memcpy(m_rptCall2, data + 20U, LONG_CALLSIGN_LENGTH); + ::memcpy(m_rptCall1, data + 28U, LONG_CALLSIGN_LENGTH); + ::memcpy(m_yourCall, data + 36U, LONG_CALLSIGN_LENGTH); + ::memcpy(m_myCall1, data + 44U, LONG_CALLSIGN_LENGTH); + ::memcpy(m_myCall2, data + 52U, SHORT_CALLSIGN_LENGTH); + + m_yourAddress = yourAddress; + m_yourPort = yourPort; + m_myPort = myPort; + + if (check) { + CCCITTChecksum cksum; + cksum.update(data + 17U, RADIO_HEADER_LENGTH_BYTES - 2U); + bool valid = cksum.check(data + 17U + RADIO_HEADER_LENGTH_BYTES - 2U); + + if (!valid) + CUtils::dump(wxT("Header checksum failure from D-Plus"), data + 17U, RADIO_HEADER_LENGTH_BYTES); + + return valid; + } else { + return true; + } +} + +bool CHeaderData::setDVTOOLData(const unsigned char* data, unsigned int length, bool check) +{ + wxASSERT(data != NULL); + wxASSERT(length >= RADIO_HEADER_LENGTH_BYTES); + + m_flag1 = data[0U]; + m_flag2 = data[1U]; + m_flag3 = data[2U]; + + ::memcpy(m_rptCall2, data + 3U, LONG_CALLSIGN_LENGTH); + ::memcpy(m_rptCall1, data + 11U, LONG_CALLSIGN_LENGTH); + ::memcpy(m_yourCall, data + 19U, LONG_CALLSIGN_LENGTH); + ::memcpy(m_myCall1, data + 27U, LONG_CALLSIGN_LENGTH); + ::memcpy(m_myCall2, data + 35U, SHORT_CALLSIGN_LENGTH); + + if (check) { + CCCITTChecksum cksum; + cksum.update(data, RADIO_HEADER_LENGTH_BYTES - 2U); + bool valid = cksum.check(data + RADIO_HEADER_LENGTH_BYTES - 2U); + + if (!valid) + CUtils::dump(wxT("Header checksum failure from DVTOOL"), data, RADIO_HEADER_LENGTH_BYTES); + + return valid; + } else { + return true; + } +} + +unsigned int CHeaderData::getIcomRepeaterData(unsigned char *data, unsigned int length, bool check) const +{ + wxASSERT(data != NULL); + wxASSERT(length >= 58U); + + data[0] = 'D'; + data[1] = 'S'; + data[2] = 'T'; + data[3] = 'R'; + + data[4] = m_rptSeq / 256U; // Packet sequence number + data[5] = m_rptSeq % 256U; + + data[6] = 0x73; // Not a response + data[7] = 0x12; // Data type + + data[8] = 0x00; // Length of 48 bytes following + data[9] = 0x30; + + data[10] = 0x20; // AMBE plus Slow Data following + + data[11] = m_band1; + data[12] = m_band2; + data[13] = m_band3; + + data[14] = m_id / 256U; // Unique session id + data[15] = m_id % 256U; + + data[16] = 0x80; + + data[17] = m_flag1; // Flags 1, 2, and 3 + data[18] = m_flag2; + data[19] = m_flag3; + + ::memcpy(data + 20U, m_rptCall2, LONG_CALLSIGN_LENGTH); + ::memcpy(data + 28U, m_rptCall1, LONG_CALLSIGN_LENGTH); + ::memcpy(data + 36U, m_yourCall, LONG_CALLSIGN_LENGTH); + ::memcpy(data + 44U, m_myCall1, LONG_CALLSIGN_LENGTH); + ::memcpy(data + 52U, m_myCall2, SHORT_CALLSIGN_LENGTH); + + if (check) { + CCCITTChecksum csum; + csum.update(data + 17, 4U * LONG_CALLSIGN_LENGTH + SHORT_CALLSIGN_LENGTH + 3U); + csum.result(data + 56); + } else { + data[56] = 0xFF; + data[57] = 0xFF; + } + + return 58U; +} + +unsigned int CHeaderData::getHBRepeaterData(unsigned char *data, unsigned int length, bool check) const +{ + wxASSERT(data != NULL); + wxASSERT(length >= 49U); + + data[0] = 'D'; + data[1] = 'S'; + data[2] = 'R'; + data[3] = 'P'; + + data[4] = 0x20U; + + data[5] = m_id / 256U; // Unique session id + data[6] = m_id % 256U; + + data[7] = 0U; + + data[8] = m_flag1; // Flags 1, 2, and 3 + data[9] = m_flag2; + data[10] = m_flag3; + + ::memcpy(data + 11U, m_rptCall2, LONG_CALLSIGN_LENGTH); + ::memcpy(data + 19U, m_rptCall1, LONG_CALLSIGN_LENGTH); + ::memcpy(data + 27U, m_yourCall, LONG_CALLSIGN_LENGTH); + ::memcpy(data + 35U, m_myCall1, LONG_CALLSIGN_LENGTH); + ::memcpy(data + 43U, m_myCall2, SHORT_CALLSIGN_LENGTH); + + if (check) { + CCCITTChecksum csum; + csum.update(data + 8U, 4U * LONG_CALLSIGN_LENGTH + SHORT_CALLSIGN_LENGTH + 3U); + csum.result(data + 47U); + } else { + data[47] = 0xFF; + data[48] = 0xFF; + } + + return 49U; +} + +void CHeaderData::getDCSData(unsigned char *data, unsigned int length) const +{ + wxASSERT(data != NULL); + wxASSERT(length >= 100U); + + data[4] = m_flag1; // Flags 1, 2, and 3 + data[5] = m_flag2; + data[6] = m_flag3; + + ::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); +} + +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); + wxASSERT(length >= 56U); + + data[0] = 'D'; + data[1] = 'S'; + data[2] = 'V'; + data[3] = 'T'; + + data[4] = 0x10; + data[5] = 0x00; + data[6] = 0x15; + data[7] = 0x09; + data[8] = 0x20; + + data[9] = m_band1; + data[10] = m_band2; + data[11] = m_band3; + + data[12] = m_id / 256U; // Unique session id + data[13] = m_id % 256U; + + data[14] = 0x80; + + data[15] = m_flag1; // Flags 1, 2, and 3 + data[16] = m_flag2; + data[17] = m_flag3; + + ::memcpy(data + 18U, m_rptCall2, LONG_CALLSIGN_LENGTH); + ::memcpy(data + 26U, m_rptCall1, LONG_CALLSIGN_LENGTH); + ::memcpy(data + 34U, m_yourCall, LONG_CALLSIGN_LENGTH); + ::memcpy(data + 42U, m_myCall1, LONG_CALLSIGN_LENGTH); + ::memcpy(data + 50U, m_myCall2, SHORT_CALLSIGN_LENGTH); + + if (check) { + CCCITTChecksum csum; + csum.update(data + 15, 4U * LONG_CALLSIGN_LENGTH + SHORT_CALLSIGN_LENGTH + 3U); + csum.result(data + 54); + } else { + data[54] = 0xFF; + data[55] = 0xFF; + } + + return 56U; +} + +unsigned int CHeaderData::getDExtraData(unsigned char* data, unsigned int length, bool check) const +{ + wxASSERT(data != NULL); + wxASSERT(length >= 56U); + + data[0] = 'D'; + data[1] = 'S'; + data[2] = 'V'; + data[3] = 'T'; + + data[4] = 0x10; + data[5] = 0x00; + data[6] = 0x00; + data[7] = 0x00; + data[8] = 0x20; + + data[9] = m_band1; + data[10] = m_band2; + data[11] = m_band3; + + data[12] = m_id % 256U; // Unique session id + data[13] = m_id / 256U; + + data[14] = 0x80; + + data[15] = 0x00; // Flags 1, 2, and 3 + data[16] = 0x00; + data[17] = 0x00; + + ::memcpy(data + 18U, m_rptCall2, LONG_CALLSIGN_LENGTH); + ::memcpy(data + 26U, m_rptCall1, LONG_CALLSIGN_LENGTH); + ::memcpy(data + 34U, m_yourCall, LONG_CALLSIGN_LENGTH); + ::memcpy(data + 42U, m_myCall1, LONG_CALLSIGN_LENGTH); + ::memcpy(data + 50U, m_myCall2, SHORT_CALLSIGN_LENGTH); + + if (check) { + CCCITTChecksum csum; + csum.update(data + 15, 4U * LONG_CALLSIGN_LENGTH + SHORT_CALLSIGN_LENGTH + 3U); + csum.result(data + 54); + } else { + data[54] = 0xFF; + data[55] = 0xFF; + } + + return 56U; +} + +unsigned int CHeaderData::getDPlusData(unsigned char* data, unsigned int length, bool check) const +{ + wxASSERT(data != NULL); + wxASSERT(length >= 58U); + + data[0] = 0x3A; + data[1] = 0x80; + + data[2] = 'D'; + data[3] = 'S'; + data[4] = 'V'; + data[5] = 'T'; + + data[6] = 0x10; + data[7] = 0x00; + data[8] = 0x00; + data[9] = 0x00; + data[10] = 0x20; + + data[11] = m_band1; + data[12] = m_band2; + data[13] = m_band3; + + data[14] = m_id % 256U; // Unique session id + data[15] = m_id / 256U; + + data[16] = 0x80; + + data[17] = 0x00; // Flags 1, 2, and 3 + data[18] = 0x00; + data[19] = 0x00; + + ::memcpy(data + 20U, m_rptCall2, LONG_CALLSIGN_LENGTH); + ::memcpy(data + 28U, m_rptCall1, LONG_CALLSIGN_LENGTH); + ::memcpy(data + 36U, m_yourCall, LONG_CALLSIGN_LENGTH); + ::memcpy(data + 44U, m_myCall1, LONG_CALLSIGN_LENGTH); + ::memcpy(data + 52U, m_myCall2, SHORT_CALLSIGN_LENGTH); + + if (check) { + CCCITTChecksum csum; + csum.update(data + 17, 4U * LONG_CALLSIGN_LENGTH + SHORT_CALLSIGN_LENGTH + 3U); + csum.result(data + 56); + } else { + data[56] = 0xFF; + data[57] = 0xFF; + } + + return 58U; +} + +unsigned int CHeaderData::getId() const +{ + return m_id; +} + +void CHeaderData::setId(unsigned int id) +{ + m_id = id; +} + +unsigned char CHeaderData::getBand1() const +{ + return m_band1; +} + +unsigned char CHeaderData::getBand2() const +{ + return m_band2; +} + +unsigned char CHeaderData::getBand3() const +{ + return m_band3; +} + +void CHeaderData::setBand1(unsigned char band) +{ + m_band1 = band; +} + +void CHeaderData::setBand2(unsigned char band) +{ + m_band2 = band; +} + +void CHeaderData::setBand3(unsigned char band) +{ + m_band3 = band; +} + +unsigned int CHeaderData::getRptSeq() const +{ + return m_rptSeq; +} + +void CHeaderData::setRptSeq(unsigned int seqNo) +{ + m_rptSeq = seqNo; +} + +unsigned char CHeaderData::getFlag1() const +{ + return m_flag1; +} + +unsigned char CHeaderData::getFlag2() const +{ + return m_flag2; +} + +unsigned char CHeaderData::getFlag3() const +{ + return m_flag3; +} + +void CHeaderData::setFlags(unsigned char flag1, unsigned char flag2, unsigned char flag3) +{ + m_flag1 = flag1; + m_flag2 = flag2; + m_flag3 = flag3; +} + +wxString CHeaderData::getMyCall1() const +{ + return wxString((const char*)m_myCall1, wxConvLocal, LONG_CALLSIGN_LENGTH); +} + +wxString CHeaderData::getMyCall2() const +{ + return wxString((const char*)m_myCall2, wxConvLocal, SHORT_CALLSIGN_LENGTH); +} + +wxString CHeaderData::getYourCall() const +{ + return wxString((const char*)m_yourCall, wxConvLocal, LONG_CALLSIGN_LENGTH); +} + +wxString CHeaderData::getRptCall1() const +{ + return wxString((const char*)m_rptCall1, wxConvLocal, LONG_CALLSIGN_LENGTH); +} + +wxString CHeaderData::getRptCall2() const +{ + return wxString((const char*)m_rptCall2, wxConvLocal, LONG_CALLSIGN_LENGTH); +} + +void CHeaderData::setFlag1(unsigned char flag) +{ + m_flag1 = flag; +} + +void CHeaderData::setFlag2(unsigned char flag) +{ + m_flag2 = flag; +} + +void CHeaderData::setFlag3(unsigned char flag) +{ + m_flag3 = flag; +} + +void CHeaderData::setMyCall1(const wxString& my1) +{ + ::memset(m_myCall1, ' ', LONG_CALLSIGN_LENGTH); + + for (unsigned int i = 0U; i < my1.Len(); i++) + m_myCall1[i] = my1.GetChar(i); +} + +void CHeaderData::setMyCall2(const wxString& my2) +{ + ::memset(m_myCall2, ' ', SHORT_CALLSIGN_LENGTH); + + for (unsigned int i = 0U; i < my2.Len(); i++) + m_myCall2[i] = my2.GetChar(i); +} + +void CHeaderData::setYourCall(const wxString& your) +{ + ::memset(m_yourCall, ' ', LONG_CALLSIGN_LENGTH); + + for (unsigned int i = 0U; i < your.Len(); i++) + m_yourCall[i] = your.GetChar(i); +} + +void CHeaderData::setRptCall1(const wxString& rpt1) +{ + ::memset(m_rptCall1, ' ', LONG_CALLSIGN_LENGTH); + + for (unsigned int i = 0U; i < rpt1.Len(); i++) + m_rptCall1[i] = rpt1.GetChar(i); +} + +void CHeaderData::setRptCall2(const wxString& rpt2) +{ + ::memset(m_rptCall2, ' ', LONG_CALLSIGN_LENGTH); + + for (unsigned int i = 0U; i < rpt2.Len(); i++) + m_rptCall2[i] = rpt2.GetChar(i); +} + +void CHeaderData::setRepeaters(const wxString& rpt1, const wxString& rpt2) +{ + ::memset(m_rptCall1, ' ', LONG_CALLSIGN_LENGTH); + ::memset(m_rptCall2, ' ', LONG_CALLSIGN_LENGTH); + + for (unsigned int i = 0U; i < rpt1.Len(); i++) + m_rptCall1[i] = rpt1.GetChar(i); + + for (unsigned int i = 0U; i < rpt2.Len(); i++) + m_rptCall2[i] = rpt2.GetChar(i); +} + +void CHeaderData::setCQCQCQ() +{ + ::memcpy(m_yourCall, "CQCQCQ ", LONG_CALLSIGN_LENGTH); +} + +bool CHeaderData::setData(const unsigned char *data, unsigned int length, bool check) +{ + wxASSERT(data != NULL); + wxASSERT(length >= RADIO_HEADER_LENGTH_BYTES); + + m_flag1 = data[0U]; + m_flag2 = data[1U]; + m_flag3 = data[2U]; + + ::memcpy(m_rptCall2, data + 3U, LONG_CALLSIGN_LENGTH); + ::memcpy(m_rptCall1, data + 11U, LONG_CALLSIGN_LENGTH); + ::memcpy(m_yourCall, data + 19U, LONG_CALLSIGN_LENGTH); + ::memcpy(m_myCall1, data + 27U, LONG_CALLSIGN_LENGTH); + ::memcpy(m_myCall2, data + 35U, SHORT_CALLSIGN_LENGTH); + + if (check) { + CCCITTChecksum cksum; + cksum.update(data, RADIO_HEADER_LENGTH_BYTES - 2U); + return cksum.check(data + RADIO_HEADER_LENGTH_BYTES - 2U); + } else { + return true; + } +} + +unsigned int CHeaderData::getData(unsigned char *data, unsigned int length, bool check) const +{ + wxASSERT(data != NULL); + wxASSERT(length >= RADIO_HEADER_LENGTH_BYTES); + + data[0] = m_flag1; // Flags 1, 2, and 3 + data[1] = m_flag2; + data[2] = m_flag3; + + ::memcpy(data + 3U, m_rptCall2, LONG_CALLSIGN_LENGTH); + ::memcpy(data + 11U, m_rptCall1, LONG_CALLSIGN_LENGTH); + ::memcpy(data + 19U, m_yourCall, LONG_CALLSIGN_LENGTH); + ::memcpy(data + 27U, m_myCall1, LONG_CALLSIGN_LENGTH); + ::memcpy(data + 35U, m_myCall2, SHORT_CALLSIGN_LENGTH); + + if (check) { + CCCITTChecksum csum; + csum.update(data, RADIO_HEADER_LENGTH_BYTES - 2U); + csum.result(data + RADIO_HEADER_LENGTH_BYTES - 2U); + + return RADIO_HEADER_LENGTH_BYTES; + } else { + return RADIO_HEADER_LENGTH_BYTES - 2U; + } +} + +void CHeaderData::setDestination(const in_addr& address, unsigned int port) +{ + m_yourAddress = address; + m_yourPort = port; +} + +in_addr CHeaderData::getYourAddress() const +{ + return m_yourAddress; +} + +unsigned int CHeaderData::getYourPort() const +{ + return m_yourPort; +} + +unsigned int CHeaderData::getMyPort() const +{ + return m_myPort; +} + +CHeaderData& CHeaderData::operator =(const CHeaderData& header) +{ + if (&header != this) { + m_rptSeq = header.m_rptSeq; + m_id = header.m_id; + m_band1 = header.m_band1; + m_band2 = header.m_band2; + m_band3 = header.m_band3; + m_flag1 = header.m_flag1; + m_flag2 = header.m_flag2; + m_flag3 = header.m_flag3; + m_yourAddress = header.m_yourAddress; + m_yourPort = header.m_yourPort; + m_myPort = header.m_myPort; + m_errors = header.m_errors; + + ::memcpy(m_myCall1, header.m_myCall1, LONG_CALLSIGN_LENGTH); + ::memcpy(m_myCall2, header.m_myCall2, SHORT_CALLSIGN_LENGTH); + ::memcpy(m_yourCall, header.m_yourCall, LONG_CALLSIGN_LENGTH); + ::memcpy(m_rptCall1, header.m_rptCall1, LONG_CALLSIGN_LENGTH); + ::memcpy(m_rptCall2, header.m_rptCall2, LONG_CALLSIGN_LENGTH); + } + + return *this; +} diff --git a/Common/HeaderData.h b/Common/HeaderData.h new file mode 100644 index 0000000..791ec36 --- /dev/null +++ b/Common/HeaderData.h @@ -0,0 +1,130 @@ +/* + * 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 + * 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 HeaderData_H +#define HeaderData_H + +#include + +#if defined(__WINDOWS__) +#include "Inaddr.h" +#else +#include +#endif + +class CHeaderData { +public: + CHeaderData(); + CHeaderData(const CHeaderData& header); + CHeaderData(const wxString& myCall1, const wxString& myCall2, const wxString& yourCall, + const wxString& rptCall1, const wxString& rptCall2, unsigned char flag1 = 0x00, + unsigned char flag2 = 0x00, unsigned char flag3 = 0x00); + ~CHeaderData(); + + bool setIcomRepeaterData(const unsigned char* data, unsigned int length, bool check, const in_addr& yourAddress, unsigned int yourPort); + bool setHBRepeaterData(const unsigned char* data, unsigned int length, bool check, const in_addr& yourAddress, unsigned int yourPort); + bool setG2Data(const unsigned char* data, unsigned int length, bool check, const in_addr& yourAddress, unsigned int yourPort); + 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; + unsigned int getDExtraData(unsigned char* data, unsigned int length, bool check) const; + 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); + + unsigned int getId() const; + void setId(unsigned int id); + + unsigned char getBand1() const; + unsigned char getBand2() const; + unsigned char getBand3() const; + void setBand1(unsigned char band); + void setBand2(unsigned char band); + void setBand3(unsigned char band); + + unsigned int getRptSeq() const; + void setRptSeq(unsigned int seqNo); + + unsigned char getFlag1() const; + unsigned char getFlag2() const; + unsigned char getFlag3() const; + + wxString getMyCall1() const; + wxString getMyCall2() const; + wxString getYourCall() const; + wxString getRptCall1() const; + wxString getRptCall2() const; + + void setFlag1(unsigned char flag); + void setFlag2(unsigned char flag); + void setFlag3(unsigned char flag); + void setFlags(unsigned char flag1, unsigned char flag2, unsigned char flag3); + + void setMyCall1(const wxString& callsign); + void setMyCall2(const wxString& callsign); + void setYourCall(const wxString& callsign); + void setRptCall1(const wxString& callsign); + void setRptCall2(const wxString& callsign); + void setCQCQCQ(); + + void setRepeaters(const wxString& rpt1, const wxString& rpt2); + void setDestination(const in_addr& address, unsigned int port); + + bool setData(const unsigned char* data, unsigned int length, bool check); + unsigned int getData(unsigned char* data, unsigned int length, bool check) const; + + in_addr getYourAddress() const; + unsigned int getYourPort() const; + unsigned int getMyPort() const; + + unsigned int getErrors() const; + + static void initialise(); + static void finalise(); + static unsigned int createId(); + + CHeaderData& operator=(const CHeaderData& header); + +private: + unsigned int m_rptSeq; + unsigned int m_id; + unsigned char m_band1; + unsigned char m_band2; + unsigned char m_band3; + unsigned char m_flag1; + unsigned char m_flag2; + unsigned char m_flag3; + unsigned char* m_myCall1; + unsigned char* m_myCall2; + unsigned char* m_yourCall; + unsigned char* m_rptCall1; + unsigned char* m_rptCall2; + in_addr m_yourAddress; + unsigned int m_yourPort; + unsigned int m_myPort; + unsigned int m_errors; +}; + +#endif diff --git a/Common/HeaderLogger.cpp b/Common/HeaderLogger.cpp new file mode 100644 index 0000000..26eea68 --- /dev/null +++ b/Common/HeaderLogger.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2010,2011,2012,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 "HeaderLogger.h" +#include "Defs.h" + +#if !defined(__WXMSW__) +#include +#include +#include +#endif + +#include + +CHeaderLogger::CHeaderLogger(const wxString& dir, const wxString& name) : +m_dir(dir), +m_name(name), +m_file() +{ +} + +CHeaderLogger::~CHeaderLogger() +{ +} + +bool CHeaderLogger::open() +{ + wxString fullName = HEADERS_BASE_NAME; + + if (!m_name.IsEmpty()) { + fullName.Append(wxT("_")); + fullName.Append(m_name); + } + + wxFileName fileName(m_dir, fullName, wxT("log")); + + bool ret = m_file.Open(fileName.GetFullPath(), wxT("a+t")); + if (!ret) { + wxLogError(wxT("Cannot open %s file for appending"), fileName.GetFullPath().c_str()); + return false; + } + + return true; +} + +void CHeaderLogger::write(const wxChar* type, const CHeaderData& header) +{ + wxASSERT(type != NULL); + + time_t timeNow = ::time(NULL); + struct tm* tm = ::gmtime(&timeNow); + + char* t = ::inet_ntoa(header.getYourAddress()); + wxString address(t, wxConvLocal); + + wxString text; + text.Printf(wxT("%04d-%02d-%02d %02d:%02d:%02d: %s header - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X (%s:%u)\n"), + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, type, + header.getMyCall1().c_str(), header.getMyCall2().c_str(), header.getYourCall().c_str(), + header.getRptCall1().c_str(), header.getRptCall2().c_str(), header.getFlag1(), header.getFlag2(), + header.getFlag3(), address.c_str(), header.getYourPort()); + + m_file.Write(text); + m_file.Flush(); +} + +void CHeaderLogger::write(const wxChar* type, const CDDData& data) +{ + wxASSERT(type != NULL); + + time_t timeNow = ::time(NULL); + struct tm* tm = ::gmtime(&timeNow); + + wxString text; + text.Printf(wxT("%04d-%02d-%02d %02d:%02d:%02d: %s header - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X\n"), + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, type, + data.getMyCall1().c_str(), data.getMyCall2().c_str(), data.getYourCall().c_str(), + data.getRptCall1().c_str(), data.getRptCall2().c_str(), data.getFlag1(), data.getFlag2(), + data.getFlag3()); + + m_file.Write(text); + m_file.Flush(); +} + +void CHeaderLogger::close() +{ + m_file.Close(); +} diff --git a/Common/HeaderLogger.h b/Common/HeaderLogger.h new file mode 100644 index 0000000..8d1693c --- /dev/null +++ b/Common/HeaderLogger.h @@ -0,0 +1,46 @@ +/* + * 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 + * 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 HeaderLogger_H +#define HeaderLogger_H + +#include "HeaderData.h" +#include "DDData.h" + +#include +#include + +class CHeaderLogger { +public: + CHeaderLogger(const wxString& dir, const wxString& name = wxEmptyString); + ~CHeaderLogger(); + + bool open(); + + void write(const wxChar* type, const CHeaderData& header); + void write(const wxChar* type, const CDDData& header); + + void close(); + +private: + wxString m_dir; + wxString m_name; + wxFFile m_file; +}; + +#endif diff --git a/Common/HeardData.cpp b/Common/HeardData.cpp new file mode 100644 index 0000000..e2d8f2e --- /dev/null +++ b/Common/HeardData.cpp @@ -0,0 +1,134 @@ +/* + * 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 + * 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 "HeardData.h" + +CHeardData::CHeardData() : +m_reflector(), +m_repeater(), +m_user(), +m_ext(), +m_address(), +m_port(0U) +{ +} + +CHeardData::CHeardData(const CHeardData& data) : +m_reflector(data.m_reflector), +m_repeater(data.m_repeater), +m_user(data.m_user), +m_ext(data.m_ext), +m_address(data.m_address), +m_port(data.m_port) +{ +} + +CHeardData::CHeardData(const CHeaderData& data, const wxString& repeater, const wxString& reflector) : +m_reflector(reflector), +m_repeater(repeater), +m_user(), +m_ext(), +m_address(), +m_port() +{ + m_user = data.getMyCall1(); + m_ext = data.getMyCall2(); +} + +CHeardData::~CHeardData() +{ +} + +bool CHeardData::setIcomRepeaterData(const unsigned char *data, unsigned int length, const in_addr& address, unsigned int port) +{ + wxASSERT(data != NULL); + wxASSERT(length >= 26U); + + m_user = wxString((char*)(data + 10U), wxConvLocal, LONG_CALLSIGN_LENGTH); + m_repeater = wxString((char*)(data + 18U), wxConvLocal, LONG_CALLSIGN_LENGTH); + + m_address = address; + m_port = port; + + 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; +} + +wxString CHeardData::getUser() const +{ + return m_user; +} + +void CHeardData::setDestination(const in_addr& address, unsigned int port) +{ + m_address = address; + m_port = port; +} + +in_addr CHeardData::getAddress() const +{ + return m_address; +} + +unsigned int CHeardData::getPort() const +{ + return m_port; +} diff --git a/Common/HeardData.h b/Common/HeardData.h new file mode 100644 index 0000000..61010fa --- /dev/null +++ b/Common/HeardData.h @@ -0,0 +1,61 @@ +/* + * 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 + * 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 HeardData_H +#define HeardData_H + +#include + +#include "DStarDefines.h" +#include "HeaderData.h" + +#if defined(__WINDOWS__) +#include "Inaddr.h" +#else +#include +#endif + +class CHeardData { +public: + CHeardData(); + CHeardData(const CHeardData& data); + CHeardData(const CHeaderData& data, const wxString& repeater, const wxString& reflector); + ~CHeardData(); + + 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; + + void setDestination(const in_addr& address, unsigned int port); + + in_addr getAddress() const; + unsigned int getPort() const; + +private: + wxString m_reflector; + wxString m_repeater; + wxString m_user; + wxString m_ext; + in_addr m_address; + unsigned int m_port; +}; + +#endif diff --git a/Common/HostFile.cpp b/Common/HostFile.cpp new file mode 100644 index 0000000..13e174c --- /dev/null +++ b/Common/HostFile.cpp @@ -0,0 +1,107 @@ +/* + * 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 + * 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 "HostFile.h" + +#include +#include +#include + +CHostFile::CHostFile(const wxString& fileName, bool logging) : +m_names(), +m_addresses(), +m_locks() +{ + if (!wxFile::Exists(fileName)) + return; + + wxTextFile file; + + bool ret = file.Open(fileName); + if (!ret) + return; + + if (logging) + wxLogMessage(wxT("Reading %s"), fileName.c_str()); + + unsigned int nLines = file.GetLineCount(); + + for (unsigned int i = 0; i < nLines; i++) { + wxString line = file.GetLine(i); + + if (line.length() > 0 && line.GetChar(0) != wxT('#')) { + wxStringTokenizer t(line, wxT(" \t\r\n"), wxTOKEN_STRTOK); + wxString name = t.GetNextToken(); + wxString address = t.GetNextToken(); + wxString lock = t.GetNextToken(); + + name.Append(wxT(" ")); + name.Truncate(LONG_CALLSIGN_LENGTH); + + if (!name.IsEmpty() && !address.IsEmpty()) { + m_names.Add(name); + m_addresses.Add(address); + m_locks.Add(lock.IsEmpty() ? 0 : 1); + } + } + } + + file.Close(); +} + +CHostFile::~CHostFile() +{ +} + +unsigned int CHostFile::getCount() const +{ + return m_names.GetCount(); +} + +wxString CHostFile::getName(unsigned int n) const +{ + return m_names.Item(n); +} + +wxString CHostFile::getAddress(unsigned int n) const +{ + return m_addresses.Item(n); +} + +wxString CHostFile::getAddress(const wxString& host) const +{ + wxString name = host; + name.resize(LONG_CALLSIGN_LENGTH, wxT(' ')); + + int n = m_names.Index(name); + if (n == wxNOT_FOUND) + return wxEmptyString; + + return m_addresses.Item(n); +} + +bool CHostFile::getLock(unsigned int n) const +{ + return m_locks.Item(n) == 1; +} + +wxArrayString CHostFile::getNames() const +{ + return m_names; +} diff --git a/Common/HostFile.h b/Common/HostFile.h new file mode 100644 index 0000000..c8b7a49 --- /dev/null +++ b/Common/HostFile.h @@ -0,0 +1,45 @@ +/* + * 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 + * 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 HostFile_H +#define HostFile_H + +#include + +WX_DEFINE_ARRAY_INT(int, CArrayInt); + +class CHostFile { +public: + CHostFile(const wxString& fileName, bool logging); + ~CHostFile(); + + unsigned int getCount() const; + wxString getName(unsigned int n) const; + wxString getAddress(unsigned int n) const; + wxString getAddress(const wxString& host) const; + bool getLock(unsigned int n) const; + + wxArrayString getNames() const; + +private: + wxArrayString m_names; + wxArrayString m_addresses; + CArrayInt m_locks; +}; + +#endif diff --git a/Common/IRCDDBGatewayConfig.cpp b/Common/IRCDDBGatewayConfig.cpp new file mode 100644 index 0000000..9e97d14 --- /dev/null +++ b/Common/IRCDDBGatewayConfig.cpp @@ -0,0 +1,2651 @@ +/* + * 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 + * 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 "IRCDDBGatewayConfig.h" + +#include + +const wxString KEY_GATEWAY_TYPE = wxT("gatewayType"); +const wxString KEY_GATEWAY_CALLSIGN = wxT("gatewayCallsign"); +const wxString KEY_GATEWAY_ADDRESS = wxT("gatewayAddress"); +const wxString KEY_ICOM_ADDRESS = wxT("icomAddress"); +const wxString KEY_ICOM_PORT = wxT("icomPort"); +const wxString KEY_HB_ADDRESS = wxT("hbAddress"); +const wxString KEY_HB_PORT = wxT("hbPort"); +const wxString KEY_LATITUDE = wxT("latitude"); +const wxString KEY_LONGITUDE = wxT("longitude"); +const wxString KEY_DESCRIPTION1 = wxT("description1"); +const wxString KEY_DESCRIPTION2 = wxT("description2"); +const wxString KEY_URL = wxT("url"); +const wxString KEY_REPEATER_CALL1 = wxT("repeaterCall1"); +const wxString KEY_REPEATER_BAND1 = wxT("repeaterBand1"); +const wxString KEY_REPEATER_TYPE1 = wxT("repeaterType1"); +const wxString KEY_REPEATER_ADDRESS1 = wxT("repeaterAddress1"); +const wxString KEY_REPEATER_PORT1 = wxT("repeaterPort1"); +const wxString KEY_REFLECTOR1 = wxT("reflector1"); +const wxString KEY_ATSTARTUP1 = wxT("atStartup1"); +const wxString KEY_RECONNECT1 = wxT("reconnect1"); +const wxString KEY_FREQUENCY1 = wxT("frequency1"); +const wxString KEY_OFFSET1 = wxT("offset1"); +const wxString KEY_RANGE1 = wxT("rangeKms1"); +const wxString KEY_LATITUDE1 = wxT("latitude1"); +const wxString KEY_LONGITUDE1 = wxT("longitude1"); +const wxString KEY_AGL1 = wxT("agl1"); +const wxString KEY_DESCRIPTION11 = wxT("description1_1"); +const wxString KEY_DESCRIPTION12 = wxT("description1_2"); +const wxString KEY_URL1 = wxT("url1"); +const wxString KEY_BAND11 = wxT("band1_1"); +const wxString KEY_BAND12 = wxT("band1_2"); +const wxString KEY_BAND13 = wxT("band1_3"); +const wxString KEY_REPEATER_CALL2 = wxT("repeaterCall2"); +const wxString KEY_REPEATER_BAND2 = wxT("repeaterBand2"); +const wxString KEY_REPEATER_TYPE2 = wxT("repeaterType2"); +const wxString KEY_REPEATER_ADDRESS2 = wxT("repeaterAddress2"); +const wxString KEY_REPEATER_PORT2 = wxT("repeaterPort2"); +const wxString KEY_REFLECTOR2 = wxT("reflector2"); +const wxString KEY_ATSTARTUP2 = wxT("atStartup2"); +const wxString KEY_RECONNECT2 = wxT("reconnect2"); +const wxString KEY_FREQUENCY2 = wxT("frequency2"); +const wxString KEY_OFFSET2 = wxT("offset2"); +const wxString KEY_RANGE2 = wxT("rangeKms2"); +const wxString KEY_LATITUDE2 = wxT("latitude2"); +const wxString KEY_LONGITUDE2 = wxT("longitude2"); +const wxString KEY_AGL2 = wxT("agl2"); +const wxString KEY_DESCRIPTION21 = wxT("description2_1"); +const wxString KEY_DESCRIPTION22 = wxT("description2_2"); +const wxString KEY_URL2 = wxT("url2"); +const wxString KEY_BAND21 = wxT("band2_1"); +const wxString KEY_BAND22 = wxT("band2_2"); +const wxString KEY_BAND23 = wxT("band2_3"); +const wxString KEY_REPEATER_CALL3 = wxT("repeaterCall3"); +const wxString KEY_REPEATER_BAND3 = wxT("repeaterBand3"); +const wxString KEY_REPEATER_TYPE3 = wxT("repeaterType3"); +const wxString KEY_REPEATER_ADDRESS3 = wxT("repeaterAddress3"); +const wxString KEY_REPEATER_PORT3 = wxT("repeaterPort3"); +const wxString KEY_REFLECTOR3 = wxT("reflector3"); +const wxString KEY_ATSTARTUP3 = wxT("atStartup3"); +const wxString KEY_RECONNECT3 = wxT("reconnect3"); +const wxString KEY_FREQUENCY3 = wxT("frequency3"); +const wxString KEY_OFFSET3 = wxT("offset3"); +const wxString KEY_RANGE3 = wxT("rangeKms3"); +const wxString KEY_LATITUDE3 = wxT("latitude3"); +const wxString KEY_LONGITUDE3 = wxT("longitude3"); +const wxString KEY_AGL3 = wxT("agl3"); +const wxString KEY_DESCRIPTION31 = wxT("description3_1"); +const wxString KEY_DESCRIPTION32 = wxT("description3_2"); +const wxString KEY_URL3 = wxT("url3"); +const wxString KEY_BAND31 = wxT("band3_1"); +const wxString KEY_BAND32 = wxT("band3_2"); +const wxString KEY_BAND33 = wxT("band3_3"); +const wxString KEY_REPEATER_CALL4 = wxT("repeaterCall4"); +const wxString KEY_REPEATER_BAND4 = wxT("repeaterBand4"); +const wxString KEY_REPEATER_TYPE4 = wxT("repeaterType4"); +const wxString KEY_REPEATER_ADDRESS4 = wxT("repeaterAddress4"); +const wxString KEY_REPEATER_PORT4 = wxT("repeaterPort4"); +const wxString KEY_REFLECTOR4 = wxT("reflector4"); +const wxString KEY_ATSTARTUP4 = wxT("atStartup4"); +const wxString KEY_RECONNECT4 = wxT("reconnect4"); +const wxString KEY_FREQUENCY4 = wxT("frequency4"); +const wxString KEY_OFFSET4 = wxT("offset4"); +const wxString KEY_RANGE4 = wxT("rangeKms4"); +const wxString KEY_LATITUDE4 = wxT("latitude4"); +const wxString KEY_LONGITUDE4 = wxT("longitude4"); +const wxString KEY_AGL4 = wxT("agl4"); +const wxString KEY_DESCRIPTION41 = wxT("description4_1"); +const wxString KEY_DESCRIPTION42 = wxT("description4_2"); +const wxString KEY_URL4 = wxT("url4"); +const wxString KEY_BAND41 = wxT("band4_1"); +const wxString KEY_BAND42 = wxT("band4_2"); +const wxString KEY_BAND43 = wxT("band4_3"); +const wxString KEY_IRCDDB_ENABLED = wxT("ircddbEnabled"); +const wxString KEY_IRCDDB_HOSTNAME = wxT("ircddbHostname"); +const wxString KEY_IRCDDB_USERNAME = wxT("ircddbUsername"); +const wxString KEY_IRCDDB_PASSWORD = wxT("ircddbPassword"); +const wxString KEY_IRCDDB_ENABLED2 = wxT("ircddbEnabled2"); +const wxString KEY_IRCDDB_HOSTNAME2 = wxT("ircddbHostname2"); +const wxString KEY_IRCDDB_USERNAME2 = wxT("ircddbUsername2"); +const wxString KEY_IRCDDB_PASSWORD2 = wxT("ircddbPassword2"); +const wxString KEY_IRCDDB_ENABLED3 = wxT("ircddbEnabled3"); +const wxString KEY_IRCDDB_HOSTNAME3 = wxT("ircddbHostname3"); +const wxString KEY_IRCDDB_USERNAME3 = wxT("ircddbUsername3"); +const wxString KEY_IRCDDB_PASSWORD3 = wxT("ircddbPassword3"); +const wxString KEY_IRCDDB_ENABLED4 = wxT("ircddbEnabled4"); +const wxString KEY_IRCDDB_HOSTNAME4 = wxT("ircddbHostname4"); +const wxString KEY_IRCDDB_USERNAME4 = wxT("ircddbUsername4"); +const wxString KEY_IRCDDB_PASSWORD4 = wxT("ircddbPassword4"); +const wxString KEY_APRS_ENABLED = wxT("aprsEnabled"); +const wxString KEY_APRS_HOSTNAME = wxT("aprsHostname"); +const wxString KEY_APRS_PORT = wxT("aprsPort"); +const wxString KEY_DEXTRA_ENABLED = wxT("dextraEnabled"); +const wxString KEY_DEXTRA_MAXDONGLES = wxT("dextraMaxDongles"); +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"); +const wxString KEY_STARNET_BAND1 = wxT("starNetBand1"); +const wxString KEY_STARNET_CALLSIGN1 = wxT("starNetCallsign1"); +const wxString KEY_STARNET_LOGOFF1 = wxT("starNetLogoff1"); +const wxString KEY_STARNET_INFO1 = wxT("starNetInfo1"); +const wxString KEY_STARNET_PERMANENT1 = wxT("starNetPermanent1"); +const wxString KEY_STARNET_USER_TIMEOUT1 = wxT("starNetUserTimeout1"); +const wxString KEY_STARNET_GROUP_TIMEOUT1 = wxT("starNetGroupTimeout1"); +const wxString KEY_STARNET_CALLSIGN_SWITCH1 = wxT("starNetCallsignSwitch1"); +const wxString KEY_STARNET_TXMSG_SWITCH1 = wxT("starNetTXMsgSwitch1"); +const wxString KEY_STARNET_REFLECTOR1 = wxT("starNetReflector1"); // DEXTRA_LINK +const wxString KEY_STARNET_BAND2 = wxT("starNetBand2"); +const wxString KEY_STARNET_CALLSIGN2 = wxT("starNetCallsign2"); +const wxString KEY_STARNET_LOGOFF2 = wxT("starNetLogoff2"); +const wxString KEY_STARNET_INFO2 = wxT("starNetInfo2"); +const wxString KEY_STARNET_PERMANENT2 = wxT("starNetPermanent2"); +const wxString KEY_STARNET_USER_TIMEOUT2 = wxT("starNetUserTimeout2"); +const wxString KEY_STARNET_GROUP_TIMEOUT2 = wxT("starNetGroupTimeout2"); +const wxString KEY_STARNET_CALLSIGN_SWITCH2 = wxT("starNetCallsignSwitch2"); +const wxString KEY_STARNET_TXMSG_SWITCH2 = wxT("starNetTXMsgSwitch2"); +const wxString KEY_STARNET_REFLECTOR2 = wxT("starNetReflector2"); // DEXTRA_LINK +const wxString KEY_STARNET_BAND3 = wxT("starNetBand3"); +const wxString KEY_STARNET_CALLSIGN3 = wxT("starNetCallsign3"); +const wxString KEY_STARNET_LOGOFF3 = wxT("starNetLogoff3"); +const wxString KEY_STARNET_INFO3 = wxT("starNetInfo3"); +const wxString KEY_STARNET_PERMANENT3 = wxT("starNetPermanent3"); +const wxString KEY_STARNET_USER_TIMEOUT3 = wxT("starNetUserTimeout3"); +const wxString KEY_STARNET_GROUP_TIMEOUT3 = wxT("starNetGroupTimeout3"); +const wxString KEY_STARNET_CALLSIGN_SWITCH3 = wxT("starNetCallsignSwitch3"); +const wxString KEY_STARNET_TXMSG_SWITCH3 = wxT("starNetTXMsgSwitch3"); +const wxString KEY_STARNET_REFLECTOR3 = wxT("starNetReflector3"); // DEXTRA_LINK +const wxString KEY_STARNET_BAND4 = wxT("starNetBand4"); +const wxString KEY_STARNET_CALLSIGN4 = wxT("starNetCallsign4"); +const wxString KEY_STARNET_LOGOFF4 = wxT("starNetLogoff4"); +const wxString KEY_STARNET_INFO4 = wxT("starNetInfo4"); +const wxString KEY_STARNET_PERMANENT4 = wxT("starNetPermanent4"); +const wxString KEY_STARNET_USER_TIMEOUT4 = wxT("starNetUserTimeout4"); +const wxString KEY_STARNET_GROUP_TIMEOUT4 = wxT("starNetGroupTimeout4"); +const wxString KEY_STARNET_CALLSIGN_SWITCH4 = wxT("starNetCallsignSwitch4"); +const wxString KEY_STARNET_TXMSG_SWITCH4 = wxT("starNetTXMsgSwitch4"); +const wxString KEY_STARNET_REFLECTOR4 = wxT("starNetReflector4"); // DEXTRA_LINK +const wxString KEY_STARNET_BAND5 = wxT("starNetBand5"); +const wxString KEY_STARNET_CALLSIGN5 = wxT("starNetCallsign5"); +const wxString KEY_STARNET_LOGOFF5 = wxT("starNetLogoff5"); +const wxString KEY_STARNET_INFO5 = wxT("starNetInfo5"); +const wxString KEY_STARNET_PERMANENT5 = wxT("starNetPermanent5"); +const wxString KEY_STARNET_USER_TIMEOUT5 = wxT("starNetUserTimeout5"); +const wxString KEY_STARNET_GROUP_TIMEOUT5 = wxT("starNetGroupTimeout5"); +const wxString KEY_STARNET_CALLSIGN_SWITCH5 = wxT("starNetCallsignSwitch5"); +const wxString KEY_STARNET_TXMSG_SWITCH5 = wxT("starNetTXMsgSwitch5"); +const wxString KEY_STARNET_REFLECTOR5 = wxT("starNetReflector5"); // DEXTRA_LINK +const wxString KEY_REMOTE_ENABLED = wxT("remoteEnabled"); +const wxString KEY_REMOTE_PASSWORD = wxT("remotePassword"); +const wxString KEY_REMOTE_PORT = wxT("remotePort"); +const wxString KEY_LANGUAGE = wxT("language"); +const wxString KEY_INFO_ENABLED = wxT("infoEnabled"); +const wxString KEY_ECHO_ENABLED = wxT("echoEnabled"); +const wxString KEY_LOG_ENABLED = wxT("logEnabled"); +const wxString KEY_DRATS_ENABLED = wxT("dratsEnabled"); +const wxString KEY_DTMF_ENABLED = wxT("dtmfEnabled"); +const wxString KEY_WINDOW_X = wxT("windowX"); +const wxString KEY_WINDOW_Y = wxT("windowY"); + +const GATEWAY_TYPE DEFAULT_GATEWAY_TYPE = GT_REPEATER; +const wxString DEFAULT_GATEWAY_CALLSIGN = wxEmptyString; +const wxString DEFAULT_GATEWAY_ADDRESS = wxEmptyString; +const wxString DEFAULT_ICOM_ADDRESS = wxT("172.16.0.20"); +const unsigned int DEFAULT_ICOM_PORT = 20000U; +const wxString DEFAULT_HB_ADDRESS = wxT("127.0.0.1"); +const unsigned int DEFAULT_HB_PORT = 20010U; +const double DEFAULT_LATITUDE = 0.0; +const double DEFAULT_LONGITUDE = 0.0; +const wxString DEFAULT_DESCRIPTION1 = wxEmptyString; +const wxString DEFAULT_DESCRIPTION2 = wxEmptyString; +const wxString DEFAULT_URL = wxEmptyString; +const wxString DEFAULT_REPEATER_CALL = wxEmptyString; +const wxString DEFAULT_REPEATER_BAND = wxT(" "); +const HW_TYPE DEFAULT_REPEATER_TYPE = HW_HOMEBREW; +const wxString DEFAULT_REPEATER_ADDRESS = wxT("127.0.0.1"); +const wxString DEFAULT_REFLECTOR = wxEmptyString; +const bool DEFAULT_ATSTARTUP = false; +const RECONNECT DEFAULT_RECONNECT = RECONNECT_NEVER; +const double DEFAULT_FREQUENCY = 0.0; +const double DEFAULT_OFFSET = 0.0; +const double DEFAULT_RANGE = 0.0; +const double DEFAULT_AGL = 0.0; +const unsigned char DEFAULT_BAND1 = 0x00U; +const unsigned char DEFAULT_BAND2 = 0x00U; +const unsigned char DEFAULT_BAND3 = 0x00U; +const unsigned int DEFAULT_REPEATER_PORT1 = 20011U; +const unsigned int DEFAULT_REPEATER_PORT2 = 20012U; +const unsigned int DEFAULT_REPEATER_PORT3 = 20013U; +const unsigned int DEFAULT_REPEATER_PORT4 = 20014U; +const bool DEFAULT_IRCDDB_ENABLED = true; +const wxString DEFAULT_IRCDDB_HOSTNAME = wxT("group1-irc.ircddb.net"); +const wxString DEFAULT_IRCDDB_USERNAME = wxEmptyString; +const wxString DEFAULT_IRCDDB_PASSWORD = wxEmptyString; +const bool DEFAULT_IRCDDB_ENABLED2 = true; +const wxString DEFAULT_IRCDDB_HOSTNAME2 = wxT("rr.openquad.net"); +const wxString DEFAULT_IRCDDB_USERNAME2 = wxEmptyString; +const wxString DEFAULT_IRCDDB_PASSWORD2 = wxEmptyString; +const bool DEFAULT_IRCDDB_ENABLED3 = false; +const wxString DEFAULT_IRCDDB_HOSTNAME3 = wxEmptyString; +const wxString DEFAULT_IRCDDB_USERNAME3 = wxEmptyString; +const wxString DEFAULT_IRCDDB_PASSWORD3 = wxEmptyString; +const bool DEFAULT_IRCDDB_ENABLED4 = false; +const wxString DEFAULT_IRCDDB_HOSTNAME4 = wxEmptyString; +const wxString DEFAULT_IRCDDB_USERNAME4 = wxEmptyString; +const wxString DEFAULT_IRCDDB_PASSWORD4 = wxEmptyString; +const bool DEFAULT_APRS_ENABLED = true; +const wxString DEFAULT_APRS_HOSTNAME = wxT("rotate.aprs2.net"); +const unsigned int DEFAULT_APRS_PORT = 14580U; +const bool DEFAULT_DEXTRA_ENABLED = true; +const unsigned int DEFAULT_DEXTRA_MAXDONGLES = 5U; +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"); +const wxString DEFAULT_STARNET_BAND = wxEmptyString; +const wxString DEFAULT_STARNET_CALLSIGN = wxEmptyString; +const wxString DEFAULT_STARNET_LOGOFF = wxEmptyString; +const wxString DEFAULT_STARNET_INFO = wxEmptyString; +const wxString DEFAULT_STARNET_PERMANENT = wxEmptyString; +const unsigned int DEFAULT_STARNET_USER_TIMEOUT = 300U; +const unsigned int DEFAULT_STARNET_GROUP_TIMEOUT = 300U; +const STARNET_CALLSIGN_SWITCH DEFAULT_STARNET_CALLSIGN_SWITCH = SCS_GROUP_CALLSIGN; +const bool DEFAULT_STARNET_TXMSG_SWITCH = true; +const wxString DEFAULT_STARNET_REFLECTOR = wxEmptyString; +const bool DEFAULT_REMOTE_ENABLED = false; +const wxString DEFAULT_REMOTE_PASSWORD = wxEmptyString; +const unsigned int DEFAULT_REMOTE_PORT = 0U; +const TEXT_LANG DEFAULT_LANGUAGE = TL_ENGLISH_UK; +const bool DEFAULT_LOG_ENABLED = false; +const bool DEFAULT_INFO_ENABLED = true; +const bool DEFAULT_ECHO_ENABLED = true; +const bool DEFAULT_DRATS_ENABLED = false; +const bool DEFAULT_DTMF_ENABLED = true; +const int DEFAULT_WINDOW_X = -1; +const int DEFAULT_WINDOW_Y = -1; + + +#if defined(__WINDOWS__) + +CIRCDDBGatewayConfig::CIRCDDBGatewayConfig(wxConfigBase* config, const wxString& dir, const wxString& configName, const wxString& name) : +m_config(config), +m_name(wxT("/")), +m_fileName(), +m_type(DEFAULT_GATEWAY_TYPE), +m_callsign(DEFAULT_GATEWAY_CALLSIGN), +m_address(DEFAULT_GATEWAY_ADDRESS), +m_icomAddress(DEFAULT_ICOM_ADDRESS), +m_icomPort(DEFAULT_ICOM_PORT), +m_hbAddress(DEFAULT_HB_ADDRESS), +m_hbPort(DEFAULT_HB_PORT), +m_latitude(DEFAULT_LATITUDE), +m_longitude(DEFAULT_LONGITUDE), +m_description1(DEFAULT_DESCRIPTION1), +m_description2(DEFAULT_DESCRIPTION2), +m_url(DEFAULT_URL), +m_repeater1Callsign(DEFAULT_REPEATER_CALL), +m_repeater1Band(DEFAULT_REPEATER_BAND), +m_repeater1Type(DEFAULT_REPEATER_TYPE), +m_repeater1Address(DEFAULT_REPEATER_ADDRESS), +m_repeater1Port(DEFAULT_REPEATER_PORT1), +m_repeater1Reflector(DEFAULT_REFLECTOR), +m_repeater1AtStartup(DEFAULT_ATSTARTUP), +m_repeater1Reconnect(DEFAULT_RECONNECT), +m_repeater1Frequency(DEFAULT_FREQUENCY), +m_repeater1Offset(DEFAULT_OFFSET), +m_repeater1Range(DEFAULT_RANGE), +m_repeater1Latitude(DEFAULT_LATITUDE), +m_repeater1Longitude(DEFAULT_LONGITUDE), +m_repeater1Agl(DEFAULT_AGL), +m_repeater1Description1(DEFAULT_DESCRIPTION1), +m_repeater1Description2(DEFAULT_DESCRIPTION2), +m_repeater1URL(DEFAULT_URL), +m_repeater1Band1(DEFAULT_BAND1), +m_repeater1Band2(DEFAULT_BAND2), +m_repeater1Band3(DEFAULT_BAND2), +m_repeater2Callsign(DEFAULT_REPEATER_CALL), +m_repeater2Band(DEFAULT_REPEATER_BAND), +m_repeater2Type(DEFAULT_REPEATER_TYPE), +m_repeater2Address(DEFAULT_REPEATER_ADDRESS), +m_repeater2Port(DEFAULT_REPEATER_PORT2), +m_repeater2Reflector(DEFAULT_REFLECTOR), +m_repeater2AtStartup(DEFAULT_ATSTARTUP), +m_repeater2Reconnect(DEFAULT_RECONNECT), +m_repeater2Frequency(DEFAULT_FREQUENCY), +m_repeater2Offset(DEFAULT_OFFSET), +m_repeater2Range(DEFAULT_RANGE), +m_repeater2Latitude(DEFAULT_LATITUDE), +m_repeater2Longitude(DEFAULT_LONGITUDE), +m_repeater2Agl(DEFAULT_AGL), +m_repeater2Description1(DEFAULT_DESCRIPTION1), +m_repeater2Description2(DEFAULT_DESCRIPTION2), +m_repeater2URL(DEFAULT_URL), +m_repeater2Band1(DEFAULT_BAND1), +m_repeater2Band2(DEFAULT_BAND2), +m_repeater2Band3(DEFAULT_BAND3), +m_repeater3Callsign(DEFAULT_REPEATER_CALL), +m_repeater3Band(DEFAULT_REPEATER_BAND), +m_repeater3Type(DEFAULT_REPEATER_TYPE), +m_repeater3Address(DEFAULT_REPEATER_ADDRESS), +m_repeater3Port(DEFAULT_REPEATER_PORT3), +m_repeater3Reflector(DEFAULT_REFLECTOR), +m_repeater3AtStartup(DEFAULT_ATSTARTUP), +m_repeater3Reconnect(DEFAULT_RECONNECT), +m_repeater3Frequency(DEFAULT_FREQUENCY), +m_repeater3Offset(DEFAULT_OFFSET), +m_repeater3Range(DEFAULT_RANGE), +m_repeater3Latitude(DEFAULT_LATITUDE), +m_repeater3Longitude(DEFAULT_LONGITUDE), +m_repeater3Agl(DEFAULT_AGL), +m_repeater3Description1(DEFAULT_DESCRIPTION1), +m_repeater3Description2(DEFAULT_DESCRIPTION2), +m_repeater3URL(DEFAULT_URL), +m_repeater3Band1(DEFAULT_BAND1), +m_repeater3Band2(DEFAULT_BAND2), +m_repeater3Band3(DEFAULT_BAND3), +m_repeater4Callsign(DEFAULT_REPEATER_CALL), +m_repeater4Band(DEFAULT_REPEATER_BAND), +m_repeater4Type(DEFAULT_REPEATER_TYPE), +m_repeater4Address(DEFAULT_REPEATER_ADDRESS), +m_repeater4Port(DEFAULT_REPEATER_PORT4), +m_repeater4Reflector(DEFAULT_REFLECTOR), +m_repeater4AtStartup(DEFAULT_ATSTARTUP), +m_repeater4Reconnect(DEFAULT_RECONNECT), +m_repeater4Frequency(DEFAULT_FREQUENCY), +m_repeater4Offset(DEFAULT_OFFSET), +m_repeater4Range(DEFAULT_RANGE), +m_repeater4Latitude(DEFAULT_LATITUDE), +m_repeater4Longitude(DEFAULT_LONGITUDE), +m_repeater4Agl(DEFAULT_AGL), +m_repeater4Description1(DEFAULT_DESCRIPTION1), +m_repeater4Description2(DEFAULT_DESCRIPTION2), +m_repeater4URL(DEFAULT_URL), +m_repeater4Band1(DEFAULT_BAND1), +m_repeater4Band2(DEFAULT_BAND2), +m_repeater4Band3(DEFAULT_BAND3), +m_ircddbEnabled(DEFAULT_IRCDDB_ENABLED), +m_ircddbHostname(DEFAULT_IRCDDB_HOSTNAME), +m_ircddbUsername(DEFAULT_IRCDDB_USERNAME), +m_ircddbPassword(DEFAULT_IRCDDB_PASSWORD), +m_ircddbEnabled2(DEFAULT_IRCDDB_ENABLED2), +m_ircddbHostname2(DEFAULT_IRCDDB_HOSTNAME2), +m_ircddbUsername2(DEFAULT_IRCDDB_USERNAME2), +m_ircddbPassword2(DEFAULT_IRCDDB_PASSWORD2), +m_ircddbEnabled3(DEFAULT_IRCDDB_ENABLED3), +m_ircddbHostname3(DEFAULT_IRCDDB_HOSTNAME3), +m_ircddbUsername3(DEFAULT_IRCDDB_USERNAME3), +m_ircddbPassword3(DEFAULT_IRCDDB_PASSWORD3), +m_ircddbEnabled4(DEFAULT_IRCDDB_ENABLED4), +m_ircddbHostname4(DEFAULT_IRCDDB_HOSTNAME4), +m_ircddbUsername4(DEFAULT_IRCDDB_USERNAME4), +m_ircddbPassword4(DEFAULT_IRCDDB_PASSWORD4), +m_aprsEnabled(DEFAULT_APRS_ENABLED), +m_aprsHostname(DEFAULT_APRS_HOSTNAME), +m_aprsPort(DEFAULT_APRS_PORT), +m_dextraEnabled(DEFAULT_DEXTRA_ENABLED), +m_dextraMaxDongles(DEFAULT_DEXTRA_MAXDONGLES), +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), +m_starNet1Band(DEFAULT_STARNET_BAND), +m_starNet1Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet1Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet1Info(DEFAULT_STARNET_INFO), +m_starNet1Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet1UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet1GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet1CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet1TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet1Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet2Band(DEFAULT_STARNET_BAND), +m_starNet2Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet2Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet2Info(DEFAULT_STARNET_INFO), +m_starNet2Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet2UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet2GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet2CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet2TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet2Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet3Band(DEFAULT_STARNET_BAND), +m_starNet3Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet3Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet3Info(DEFAULT_STARNET_INFO), +m_starNet3Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet3UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet3GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet3CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet3TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet3Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet4Band(DEFAULT_STARNET_BAND), +m_starNet4Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet4Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet4Info(DEFAULT_STARNET_INFO), +m_starNet4Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet4UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet4GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet4CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet4TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet4Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet5Band(DEFAULT_STARNET_BAND), +m_starNet5Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet5Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet5Info(DEFAULT_STARNET_INFO), +m_starNet5Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet5UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet5GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet5CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet5TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet5Reflector(DEFAULT_STARNET_REFLECTOR), +m_remoteEnabled(DEFAULT_REMOTE_ENABLED), +m_remotePassword(DEFAULT_REMOTE_PASSWORD), +m_remotePort(DEFAULT_REMOTE_PORT), +m_language(DEFAULT_LANGUAGE), +m_infoEnabled(DEFAULT_INFO_ENABLED), +m_echoEnabled(DEFAULT_ECHO_ENABLED), +m_logEnabled(DEFAULT_LOG_ENABLED), +m_dratsEnabled(DEFAULT_DRATS_ENABLED), +m_dtmfEnabled(DEFAULT_DTMF_ENABLED), +m_x(DEFAULT_WINDOW_X), +m_y(DEFAULT_WINDOW_Y) +{ + wxASSERT(config != NULL); + wxASSERT(!dir.IsEmpty()); + + wxString fileName = configName; + if (!name.IsEmpty()) { + fileName = configName + wxT("_") + name; + m_name = wxT("/") + name + wxT("/"); + } + + m_fileName.Assign(dir, fileName); + + long temp; + + m_config->Read(m_name + KEY_GATEWAY_TYPE, &temp, long(DEFAULT_GATEWAY_TYPE)); + m_type = GATEWAY_TYPE(temp); + + m_config->Read(m_name + KEY_GATEWAY_CALLSIGN, &m_callsign, DEFAULT_GATEWAY_CALLSIGN); + + m_config->Read(m_name + KEY_GATEWAY_ADDRESS, &m_address, DEFAULT_GATEWAY_ADDRESS); + + m_config->Read(m_name + KEY_ICOM_ADDRESS, &m_icomAddress, DEFAULT_ICOM_ADDRESS); + + m_config->Read(m_name + KEY_ICOM_PORT, &temp, long(DEFAULT_ICOM_PORT)); + m_icomPort = (unsigned int)temp; + + m_config->Read(m_name + KEY_HB_ADDRESS, &m_hbAddress, DEFAULT_HB_ADDRESS); + + m_config->Read(m_name + KEY_HB_PORT, &temp, long(DEFAULT_HB_PORT)); + m_hbPort = (unsigned int)temp; + + m_config->Read(m_name + KEY_LATITUDE, &m_latitude, DEFAULT_LATITUDE); + + m_config->Read(m_name + KEY_LONGITUDE, &m_longitude, DEFAULT_LONGITUDE); + + m_config->Read(m_name + KEY_DESCRIPTION1, &m_description1, DEFAULT_DESCRIPTION1); + + m_config->Read(m_name + KEY_DESCRIPTION2, &m_description2, DEFAULT_DESCRIPTION2); + + m_config->Read(m_name + KEY_URL, &m_url, DEFAULT_URL); + + m_config->Read(m_name + KEY_REPEATER_CALL1, &m_repeater1Callsign, DEFAULT_REPEATER_CALL); + + m_config->Read(m_name + KEY_REPEATER_BAND1, &m_repeater1Band, DEFAULT_REPEATER_BAND); + + m_config->Read(m_name + KEY_REPEATER_TYPE1, &temp, long(DEFAULT_REPEATER_TYPE)); + m_repeater1Type = HW_TYPE(temp); + + m_config->Read(m_name + KEY_REPEATER_ADDRESS1, &m_repeater1Address, DEFAULT_REPEATER_ADDRESS); + + m_config->Read(m_name + KEY_REPEATER_PORT1, &temp, long(DEFAULT_REPEATER_PORT1)); + m_repeater1Port = (unsigned int)temp; + + m_config->Read(m_name + KEY_REFLECTOR1, &m_repeater1Reflector, DEFAULT_REFLECTOR); + + m_config->Read(m_name + KEY_ATSTARTUP1, &m_repeater1AtStartup, DEFAULT_ATSTARTUP); + + m_config->Read(m_name + KEY_RECONNECT1, &temp, long(DEFAULT_RECONNECT)); + m_repeater1Reconnect = RECONNECT(temp); + + m_config->Read(m_name + KEY_FREQUENCY1, &m_repeater1Frequency, DEFAULT_FREQUENCY); + + m_config->Read(m_name + KEY_OFFSET1, &m_repeater1Offset, DEFAULT_OFFSET); + + m_config->Read(m_name + KEY_RANGE1, &m_repeater1Range, DEFAULT_RANGE); + + m_config->Read(m_name + KEY_LATITUDE1, &m_repeater1Latitude, DEFAULT_LATITUDE); + + m_config->Read(m_name + KEY_LONGITUDE1, &m_repeater1Longitude, DEFAULT_LONGITUDE); + + m_config->Read(m_name + KEY_AGL1, &m_repeater1Agl, DEFAULT_AGL); + + m_config->Read(m_name + KEY_DESCRIPTION11, &m_repeater1Description1, DEFAULT_DESCRIPTION1); + + m_config->Read(m_name + KEY_DESCRIPTION12, &m_repeater1Description2, DEFAULT_DESCRIPTION2); + + m_config->Read(m_name + KEY_URL1, &m_repeater1URL, DEFAULT_URL); + + m_config->Read(m_name + KEY_BAND11, &temp, long(DEFAULT_BAND1)); + m_repeater1Band1 = (unsigned char)temp; + + m_config->Read(m_name + KEY_BAND12, &temp, long(DEFAULT_BAND2)); + m_repeater1Band2 = (unsigned char)temp; + + m_config->Read(m_name + KEY_BAND13, &temp, long(DEFAULT_BAND3)); + m_repeater1Band3 = (unsigned char)temp; + + m_config->Read(m_name + KEY_REPEATER_CALL2, &m_repeater2Callsign, DEFAULT_REPEATER_CALL); + + m_config->Read(m_name + KEY_REPEATER_BAND2, &m_repeater2Band, DEFAULT_REPEATER_BAND); + + m_config->Read(m_name + KEY_REPEATER_TYPE2, &temp, long(DEFAULT_REPEATER_TYPE)); + m_repeater2Type = HW_TYPE(temp); + + m_config->Read(m_name + KEY_REPEATER_ADDRESS2, &m_repeater2Address, DEFAULT_REPEATER_ADDRESS); + + m_config->Read(m_name + KEY_REPEATER_PORT2, &temp, long(DEFAULT_REPEATER_PORT2)); + m_repeater2Port = (unsigned int)temp; + + m_config->Read(m_name + KEY_REFLECTOR2, &m_repeater2Reflector, DEFAULT_REFLECTOR); + + m_config->Read(m_name + KEY_ATSTARTUP2, &m_repeater2AtStartup, DEFAULT_ATSTARTUP); + + m_config->Read(m_name + KEY_RECONNECT2, &temp, long(DEFAULT_RECONNECT)); + m_repeater2Reconnect = RECONNECT(temp); + + m_config->Read(m_name + KEY_FREQUENCY2, &m_repeater2Frequency, DEFAULT_FREQUENCY); + + m_config->Read(m_name + KEY_OFFSET2, &m_repeater2Offset, DEFAULT_OFFSET); + + m_config->Read(m_name + KEY_RANGE2, &m_repeater2Range, DEFAULT_RANGE); + + m_config->Read(m_name + KEY_LATITUDE2, &m_repeater2Latitude, DEFAULT_LATITUDE); + + m_config->Read(m_name + KEY_LONGITUDE2, &m_repeater2Longitude, DEFAULT_LONGITUDE); + + m_config->Read(m_name + KEY_AGL2, &m_repeater2Agl, DEFAULT_AGL); + + m_config->Read(m_name + KEY_DESCRIPTION21, &m_repeater2Description1, DEFAULT_DESCRIPTION1); + + m_config->Read(m_name + KEY_DESCRIPTION22, &m_repeater2Description2, DEFAULT_DESCRIPTION2); + + m_config->Read(m_name + KEY_URL2, &m_repeater2URL, DEFAULT_URL); + + m_config->Read(m_name + KEY_BAND21, &temp, long(DEFAULT_BAND1)); + m_repeater2Band1 = (unsigned char)temp; + + m_config->Read(m_name + KEY_BAND22, &temp, long(DEFAULT_BAND2)); + m_repeater2Band2 = (unsigned char)temp; + + m_config->Read(m_name + KEY_BAND23, &temp, long(DEFAULT_BAND3)); + m_repeater2Band3 = (unsigned char)temp; + + m_config->Read(m_name + KEY_REPEATER_CALL3, &m_repeater3Callsign, DEFAULT_REPEATER_CALL); + + m_config->Read(m_name + KEY_REPEATER_BAND3, &m_repeater3Band, DEFAULT_REPEATER_BAND); + + m_config->Read(m_name + KEY_REPEATER_TYPE3, &temp, long(DEFAULT_REPEATER_TYPE)); + m_repeater3Type = HW_TYPE(temp); + + m_config->Read(m_name + KEY_REPEATER_ADDRESS3, &m_repeater3Address, DEFAULT_REPEATER_ADDRESS); + + m_config->Read(m_name + KEY_REPEATER_PORT3, &temp, long(DEFAULT_REPEATER_PORT3)); + m_repeater3Port = (unsigned int)temp; + + m_config->Read(m_name + KEY_REFLECTOR3, &m_repeater3Reflector, DEFAULT_REFLECTOR); + + m_config->Read(m_name + KEY_ATSTARTUP3, &m_repeater3AtStartup, DEFAULT_ATSTARTUP); + + m_config->Read(m_name + KEY_RECONNECT3, &temp, long(DEFAULT_RECONNECT)); + m_repeater3Reconnect = RECONNECT(temp); + + m_config->Read(m_name + KEY_FREQUENCY3, &m_repeater3Frequency, DEFAULT_FREQUENCY); + + m_config->Read(m_name + KEY_OFFSET3, &m_repeater3Offset, DEFAULT_OFFSET); + + m_config->Read(m_name + KEY_RANGE3, &m_repeater3Range, DEFAULT_RANGE); + + m_config->Read(m_name + KEY_LATITUDE3, &m_repeater3Latitude, DEFAULT_LATITUDE); + + m_config->Read(m_name + KEY_LONGITUDE3, &m_repeater3Longitude, DEFAULT_LONGITUDE); + + m_config->Read(m_name + KEY_AGL3, &m_repeater3Agl, DEFAULT_AGL); + + m_config->Read(m_name + KEY_DESCRIPTION31, &m_repeater3Description1, DEFAULT_DESCRIPTION1); + + m_config->Read(m_name + KEY_DESCRIPTION32, &m_repeater3Description2, DEFAULT_DESCRIPTION2); + + m_config->Read(m_name + KEY_URL3, &m_repeater3URL, DEFAULT_URL); + + m_config->Read(m_name + KEY_BAND31, &temp, long(DEFAULT_BAND1)); + m_repeater3Band1 = (unsigned char)temp; + + m_config->Read(m_name + KEY_BAND32, &temp, long(DEFAULT_BAND2)); + m_repeater3Band2 = (unsigned char)temp; + + m_config->Read(m_name + KEY_BAND33, &temp, long(DEFAULT_BAND3)); + m_repeater3Band3 = (unsigned char)temp; + + m_config->Read(m_name + KEY_REPEATER_CALL4, &m_repeater4Callsign, DEFAULT_REPEATER_CALL); + + m_config->Read(m_name + KEY_REPEATER_BAND4, &m_repeater4Band, DEFAULT_REPEATER_BAND); + + m_config->Read(m_name + KEY_REPEATER_TYPE4, &temp, long(DEFAULT_REPEATER_TYPE)); + m_repeater4Type = HW_TYPE(temp); + + m_config->Read(m_name + KEY_REPEATER_ADDRESS4, &m_repeater4Address, DEFAULT_REPEATER_ADDRESS); + + m_config->Read(m_name + KEY_REPEATER_PORT4, &temp, long(DEFAULT_REPEATER_PORT4)); + m_repeater4Port = (unsigned int)temp; + + m_config->Read(m_name + KEY_REFLECTOR4, &m_repeater4Reflector, DEFAULT_REFLECTOR); + + m_config->Read(m_name + KEY_ATSTARTUP4, &m_repeater4AtStartup, DEFAULT_ATSTARTUP); + + m_config->Read(m_name + KEY_RECONNECT4, &temp, long(DEFAULT_RECONNECT)); + m_repeater4Reconnect = RECONNECT(temp); + + m_config->Read(m_name + KEY_FREQUENCY4, &m_repeater4Frequency, DEFAULT_FREQUENCY); + + m_config->Read(m_name + KEY_OFFSET4, &m_repeater4Offset, DEFAULT_OFFSET); + + m_config->Read(m_name + KEY_RANGE4, &m_repeater4Range, DEFAULT_RANGE); + + m_config->Read(m_name + KEY_LATITUDE4, &m_repeater4Latitude, DEFAULT_LATITUDE); + + m_config->Read(m_name + KEY_LONGITUDE4, &m_repeater4Longitude, DEFAULT_LONGITUDE); + + m_config->Read(m_name + KEY_AGL4, &m_repeater4Agl, DEFAULT_AGL); + + m_config->Read(m_name + KEY_DESCRIPTION41, &m_repeater4Description1, DEFAULT_DESCRIPTION1); + + m_config->Read(m_name + KEY_DESCRIPTION42, &m_repeater4Description2, DEFAULT_DESCRIPTION2); + + m_config->Read(m_name + KEY_URL4, &m_repeater4URL, DEFAULT_URL); + + m_config->Read(m_name + KEY_BAND41, &temp, long(DEFAULT_BAND1)); + m_repeater4Band1 = (unsigned char)temp; + + m_config->Read(m_name + KEY_BAND42, &temp, long(DEFAULT_BAND2)); + m_repeater4Band2 = (unsigned char)temp; + + m_config->Read(m_name + KEY_BAND43, &temp, long(DEFAULT_BAND3)); + m_repeater4Band3 = (unsigned char)temp; + + m_config->Read(m_name + KEY_IRCDDB_ENABLED, &m_ircddbEnabled, DEFAULT_IRCDDB_ENABLED); + m_config->Read(m_name + KEY_IRCDDB_HOSTNAME, &m_ircddbHostname, DEFAULT_IRCDDB_HOSTNAME); + m_config->Read(m_name + KEY_IRCDDB_USERNAME, &m_ircddbUsername, DEFAULT_IRCDDB_USERNAME); + m_config->Read(m_name + KEY_IRCDDB_PASSWORD, &m_ircddbPassword, DEFAULT_IRCDDB_PASSWORD); + + m_config->Read(m_name + KEY_IRCDDB_ENABLED2, &m_ircddbEnabled, DEFAULT_IRCDDB_ENABLED2); + m_config->Read(m_name + KEY_IRCDDB_HOSTNAME2, &m_ircddbHostname, DEFAULT_IRCDDB_HOSTNAME2); + m_config->Read(m_name + KEY_IRCDDB_USERNAME2, &m_ircddbUsername, DEFAULT_IRCDDB_USERNAME2); + m_config->Read(m_name + KEY_IRCDDB_PASSWORD2, &m_ircddbPassword, DEFAULT_IRCDDB_PASSWORD2); + + m_config->Read(m_name + KEY_IRCDDB_ENABLED3, &m_ircddbEnabled, DEFAULT_IRCDDB_ENABLED3); + m_config->Read(m_name + KEY_IRCDDB_HOSTNAME3, &m_ircddbHostname, DEFAULT_IRCDDB_HOSTNAME3); + m_config->Read(m_name + KEY_IRCDDB_USERNAME3, &m_ircddbUsername, DEFAULT_IRCDDB_USERNAME3); + m_config->Read(m_name + KEY_IRCDDB_PASSWORD3, &m_ircddbPassword, DEFAULT_IRCDDB_PASSWORD3); + + m_config->Read(m_name + KEY_IRCDDB_ENABLED4, &m_ircddbEnabled, DEFAULT_IRCDDB_ENABLED4); + m_config->Read(m_name + KEY_IRCDDB_HOSTNAME4, &m_ircddbHostname, DEFAULT_IRCDDB_HOSTNAME4); + m_config->Read(m_name + KEY_IRCDDB_USERNAME4, &m_ircddbUsername, DEFAULT_IRCDDB_USERNAME4); + m_config->Read(m_name + KEY_IRCDDB_PASSWORD4, &m_ircddbPassword, DEFAULT_IRCDDB_PASSWORD4); + + m_config->Read(m_name + KEY_APRS_ENABLED, &m_aprsEnabled, DEFAULT_APRS_ENABLED); + + m_config->Read(m_name + KEY_APRS_HOSTNAME, &m_aprsHostname, DEFAULT_APRS_HOSTNAME); + + m_config->Read(m_name + KEY_APRS_PORT, &temp, long(DEFAULT_APRS_PORT)); + m_aprsPort = (unsigned int)temp; + + m_config->Read(m_name + KEY_DEXTRA_ENABLED, &m_dextraEnabled, DEFAULT_DEXTRA_ENABLED); + + m_config->Read(m_name + KEY_DEXTRA_MAXDONGLES, &temp, long(DEFAULT_DEXTRA_MAXDONGLES)); + m_dextraMaxDongles = (unsigned int)temp; + + m_config->Read(m_name + KEY_DPLUS_ENABLED, &m_dplusEnabled, DEFAULT_DPLUS_ENABLED); + + m_config->Read(m_name + KEY_DPLUS_MAXDONGLES, &temp, long(DEFAULT_DPLUS_MAXDONGLES)); + m_dplusMaxDongles = (unsigned int)temp; + + m_config->Read(m_name + KEY_DPLUS_LOGIN, &m_dplusLogin, DEFAULT_DPLUS_LOGIN); + + 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); + + m_config->Read(m_name + KEY_XLX_HOSTS_FILE_URL, &m_xlxHostsFileUrl, DEFAULT_XLX_HOSTS_FILE_URL); + + m_config->Read(m_name + KEY_STARNET_BAND1, &m_starNet1Band, DEFAULT_STARNET_BAND); + + m_config->Read(m_name + KEY_STARNET_CALLSIGN1, &m_starNet1Callsign, DEFAULT_STARNET_CALLSIGN); + + m_config->Read(m_name + KEY_STARNET_LOGOFF1, &m_starNet1Logoff, DEFAULT_STARNET_LOGOFF); + + m_config->Read(m_name + KEY_STARNET_INFO1, &m_starNet1Info, DEFAULT_STARNET_INFO); + + m_config->Read(m_name + KEY_STARNET_PERMANENT1, &m_starNet1Permanent, DEFAULT_STARNET_PERMANENT); + + m_config->Read(m_name + KEY_STARNET_USER_TIMEOUT1, &temp, long(DEFAULT_STARNET_USER_TIMEOUT)); + m_starNet1UserTimeout = (unsigned int)temp; + + m_config->Read(m_name + KEY_STARNET_GROUP_TIMEOUT1, &temp, long(DEFAULT_STARNET_GROUP_TIMEOUT)); + m_starNet1GroupTimeout = (unsigned int)temp; + + m_config->Read(m_name + KEY_STARNET_CALLSIGN_SWITCH1, &temp, long(DEFAULT_STARNET_CALLSIGN_SWITCH)); + m_starNet1CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp); + + m_config->Read(m_name + KEY_STARNET_TXMSG_SWITCH1, &m_starNet1TxMsgSwitch, DEFAULT_STARNET_TXMSG_SWITCH); + + m_config->Read(m_name + KEY_STARNET_REFLECTOR1, &m_starNet1Reflector, DEFAULT_STARNET_REFLECTOR); + + m_config->Read(m_name + KEY_STARNET_BAND2, &m_starNet2Band, DEFAULT_STARNET_BAND); + + m_config->Read(m_name + KEY_STARNET_CALLSIGN2, &m_starNet2Callsign, DEFAULT_STARNET_CALLSIGN); + + m_config->Read(m_name + KEY_STARNET_LOGOFF2, &m_starNet2Logoff, DEFAULT_STARNET_LOGOFF); + + m_config->Read(m_name + KEY_STARNET_INFO2, &m_starNet2Info, DEFAULT_STARNET_INFO); + + m_config->Read(m_name + KEY_STARNET_PERMANENT2, &m_starNet2Permanent, DEFAULT_STARNET_PERMANENT); + + m_config->Read(m_name + KEY_STARNET_USER_TIMEOUT2, &temp, long(DEFAULT_STARNET_USER_TIMEOUT)); + m_starNet2UserTimeout = (unsigned int)temp; + + m_config->Read(m_name + KEY_STARNET_GROUP_TIMEOUT2, &temp, long(DEFAULT_STARNET_GROUP_TIMEOUT)); + m_starNet2GroupTimeout = (unsigned int)temp; + + m_config->Read(m_name + KEY_STARNET_CALLSIGN_SWITCH2, &temp, long(DEFAULT_STARNET_CALLSIGN_SWITCH)); + m_starNet2CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp); + + m_config->Read(m_name + KEY_STARNET_TXMSG_SWITCH2, &m_starNet2TxMsgSwitch, DEFAULT_STARNET_TXMSG_SWITCH); + + m_config->Read(m_name + KEY_STARNET_REFLECTOR2, &m_starNet2Reflector, DEFAULT_STARNET_REFLECTOR); + + m_config->Read(m_name + KEY_STARNET_BAND3, &m_starNet3Band, DEFAULT_STARNET_BAND); + + m_config->Read(m_name + KEY_STARNET_CALLSIGN3, &m_starNet3Callsign, DEFAULT_STARNET_CALLSIGN); + + m_config->Read(m_name + KEY_STARNET_LOGOFF3, &m_starNet3Logoff, DEFAULT_STARNET_LOGOFF); + + m_config->Read(m_name + KEY_STARNET_INFO3, &m_starNet3Info, DEFAULT_STARNET_INFO); + + m_config->Read(m_name + KEY_STARNET_PERMANENT3, &m_starNet3Permanent, DEFAULT_STARNET_PERMANENT); + + m_config->Read(m_name + KEY_STARNET_USER_TIMEOUT3, &temp, long(DEFAULT_STARNET_USER_TIMEOUT)); + m_starNet3UserTimeout = (unsigned int)temp; + + m_config->Read(m_name + KEY_STARNET_GROUP_TIMEOUT3, &temp, long(DEFAULT_STARNET_GROUP_TIMEOUT)); + m_starNet3GroupTimeout = (unsigned int)temp; + + m_config->Read(m_name + KEY_STARNET_CALLSIGN_SWITCH3, &temp, long(DEFAULT_STARNET_CALLSIGN_SWITCH)); + m_starNet3CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp); + + m_config->Read(m_name + KEY_STARNET_TXMSG_SWITCH3, &m_starNet3TxMsgSwitch, DEFAULT_STARNET_TXMSG_SWITCH); + + m_config->Read(m_name + KEY_STARNET_REFLECTOR3, &m_starNet3Reflector, DEFAULT_STARNET_REFLECTOR); + + m_config->Read(m_name + KEY_STARNET_BAND4, &m_starNet4Band, DEFAULT_STARNET_BAND); + + m_config->Read(m_name + KEY_STARNET_CALLSIGN4, &m_starNet4Callsign, DEFAULT_STARNET_CALLSIGN); + + m_config->Read(m_name + KEY_STARNET_LOGOFF4, &m_starNet4Logoff, DEFAULT_STARNET_LOGOFF); + + m_config->Read(m_name + KEY_STARNET_INFO4, &m_starNet4Info, DEFAULT_STARNET_INFO); + + m_config->Read(m_name + KEY_STARNET_PERMANENT4, &m_starNet4Permanent, DEFAULT_STARNET_PERMANENT); + + m_config->Read(m_name + KEY_STARNET_USER_TIMEOUT4, &temp, long(DEFAULT_STARNET_USER_TIMEOUT)); + m_starNet4UserTimeout = (unsigned int)temp; + + m_config->Read(m_name + KEY_STARNET_GROUP_TIMEOUT4, &temp, long(DEFAULT_STARNET_GROUP_TIMEOUT)); + m_starNet4GroupTimeout = (unsigned int)temp; + + m_config->Read(m_name + KEY_STARNET_CALLSIGN_SWITCH4, &temp, long(DEFAULT_STARNET_CALLSIGN_SWITCH)); + m_starNet4CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp); + + m_config->Read(m_name + KEY_STARNET_TXMSG_SWITCH4, &m_starNet4TxMsgSwitch, DEFAULT_STARNET_TXMSG_SWITCH); + + m_config->Read(m_name + KEY_STARNET_REFLECTOR4, &m_starNet4Reflector, DEFAULT_STARNET_REFLECTOR); + + m_config->Read(m_name + KEY_STARNET_BAND5, &m_starNet5Band, DEFAULT_STARNET_BAND); + + m_config->Read(m_name + KEY_STARNET_CALLSIGN5, &m_starNet5Callsign, DEFAULT_STARNET_CALLSIGN); + + m_config->Read(m_name + KEY_STARNET_LOGOFF5, &m_starNet5Logoff, DEFAULT_STARNET_LOGOFF); + + m_config->Read(m_name + KEY_STARNET_INFO5, &m_starNet5Info, DEFAULT_STARNET_INFO); + + m_config->Read(m_name + KEY_STARNET_PERMANENT5, &m_starNet5Permanent, DEFAULT_STARNET_PERMANENT); + + m_config->Read(m_name + KEY_STARNET_USER_TIMEOUT5, &temp, long(DEFAULT_STARNET_USER_TIMEOUT)); + m_starNet5UserTimeout = (unsigned int)temp; + + m_config->Read(m_name + KEY_STARNET_GROUP_TIMEOUT5, &temp, long(DEFAULT_STARNET_GROUP_TIMEOUT)); + m_starNet5GroupTimeout = (unsigned int)temp; + + m_config->Read(m_name + KEY_STARNET_CALLSIGN_SWITCH5, &temp, long(DEFAULT_STARNET_CALLSIGN_SWITCH)); + m_starNet5CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp); + + m_config->Read(m_name + KEY_STARNET_TXMSG_SWITCH5, &m_starNet5TxMsgSwitch, DEFAULT_STARNET_TXMSG_SWITCH); + + m_config->Read(m_name + KEY_STARNET_REFLECTOR5, &m_starNet5Reflector, DEFAULT_STARNET_REFLECTOR); + + m_config->Read(m_name + KEY_REMOTE_ENABLED, &m_remoteEnabled, DEFAULT_REMOTE_ENABLED); + + m_config->Read(m_name + KEY_REMOTE_PASSWORD, &m_remotePassword, DEFAULT_REMOTE_PASSWORD); + + m_config->Read(m_name + KEY_REMOTE_PORT, &temp, long(DEFAULT_REMOTE_PORT)); + m_remotePort = (unsigned int)temp; + + m_config->Read(m_name + KEY_LANGUAGE, &temp, long(DEFAULT_LANGUAGE)); + m_language = TEXT_LANG(temp); + + m_config->Read(m_name + KEY_INFO_ENABLED, &m_infoEnabled, DEFAULT_INFO_ENABLED); + + m_config->Read(m_name + KEY_ECHO_ENABLED, &m_echoEnabled, DEFAULT_ECHO_ENABLED); + + m_config->Read(m_name + KEY_LOG_ENABLED, &m_logEnabled, DEFAULT_LOG_ENABLED); + + m_config->Read(m_name + KEY_DRATS_ENABLED, &m_dratsEnabled, DEFAULT_DRATS_ENABLED); + + m_config->Read(m_name + KEY_DTMF_ENABLED, &m_dtmfEnabled, DEFAULT_DTMF_ENABLED); + + m_config->Read(m_name + KEY_WINDOW_X, &temp, long(DEFAULT_WINDOW_X)); + m_x = int(temp); + + m_config->Read(m_name + KEY_WINDOW_Y, &temp, long(DEFAULT_WINDOW_Y)); + m_y = int(temp); +} + +CIRCDDBGatewayConfig::~CIRCDDBGatewayConfig() +{ + delete m_config; +} + +#else + +CIRCDDBGatewayConfig::CIRCDDBGatewayConfig(const wxString& dir, const wxString& configName, const wxString& name) : +m_fileName(), +m_type(DEFAULT_GATEWAY_TYPE), +m_callsign(DEFAULT_GATEWAY_CALLSIGN), +m_address(DEFAULT_GATEWAY_ADDRESS), +m_icomAddress(DEFAULT_ICOM_ADDRESS), +m_icomPort(DEFAULT_ICOM_PORT), +m_hbAddress(DEFAULT_HB_ADDRESS), +m_hbPort(DEFAULT_HB_PORT), +m_latitude(DEFAULT_LATITUDE), +m_longitude(DEFAULT_LONGITUDE), +m_description1(DEFAULT_DESCRIPTION1), +m_description2(DEFAULT_DESCRIPTION2), +m_url(DEFAULT_URL), +m_repeater1Callsign(DEFAULT_REPEATER_CALL), +m_repeater1Band(DEFAULT_REPEATER_BAND), +m_repeater1Type(DEFAULT_REPEATER_TYPE), +m_repeater1Address(DEFAULT_REPEATER_ADDRESS), +m_repeater1Port(DEFAULT_REPEATER_PORT1), +m_repeater1Reflector(DEFAULT_REFLECTOR), +m_repeater1AtStartup(DEFAULT_ATSTARTUP), +m_repeater1Reconnect(DEFAULT_RECONNECT), +m_repeater1Frequency(DEFAULT_FREQUENCY), +m_repeater1Offset(DEFAULT_OFFSET), +m_repeater1Range(DEFAULT_RANGE), +m_repeater1Latitude(DEFAULT_LATITUDE), +m_repeater1Longitude(DEFAULT_LONGITUDE), +m_repeater1Agl(DEFAULT_AGL), +m_repeater1Description1(DEFAULT_DESCRIPTION1), +m_repeater1Description2(DEFAULT_DESCRIPTION2), +m_repeater1URL(DEFAULT_URL), +m_repeater1Band1(DEFAULT_BAND1), +m_repeater1Band2(DEFAULT_BAND2), +m_repeater1Band3(DEFAULT_BAND3), +m_repeater2Callsign(DEFAULT_REPEATER_CALL), +m_repeater2Band(DEFAULT_REPEATER_BAND), +m_repeater2Type(DEFAULT_REPEATER_TYPE), +m_repeater2Address(DEFAULT_REPEATER_ADDRESS), +m_repeater2Port(DEFAULT_REPEATER_PORT2), +m_repeater2Reflector(DEFAULT_REFLECTOR), +m_repeater2AtStartup(DEFAULT_ATSTARTUP), +m_repeater2Reconnect(DEFAULT_RECONNECT), +m_repeater2Frequency(DEFAULT_FREQUENCY), +m_repeater2Offset(DEFAULT_OFFSET), +m_repeater2Range(DEFAULT_RANGE), +m_repeater2Latitude(DEFAULT_LATITUDE), +m_repeater2Longitude(DEFAULT_LONGITUDE), +m_repeater2Agl(DEFAULT_AGL), +m_repeater2Description1(DEFAULT_DESCRIPTION1), +m_repeater2Description2(DEFAULT_DESCRIPTION2), +m_repeater2URL(DEFAULT_URL), +m_repeater2Band1(DEFAULT_BAND1), +m_repeater2Band2(DEFAULT_BAND2), +m_repeater2Band3(DEFAULT_BAND3), +m_repeater3Callsign(DEFAULT_REPEATER_CALL), +m_repeater3Band(DEFAULT_REPEATER_BAND), +m_repeater3Type(DEFAULT_REPEATER_TYPE), +m_repeater3Address(DEFAULT_REPEATER_ADDRESS), +m_repeater3Port(DEFAULT_REPEATER_PORT3), +m_repeater3Reflector(DEFAULT_REFLECTOR), +m_repeater3AtStartup(DEFAULT_ATSTARTUP), +m_repeater3Reconnect(DEFAULT_RECONNECT), +m_repeater3Frequency(DEFAULT_FREQUENCY), +m_repeater3Offset(DEFAULT_OFFSET), +m_repeater3Range(DEFAULT_RANGE), +m_repeater3Latitude(DEFAULT_LATITUDE), +m_repeater3Longitude(DEFAULT_LONGITUDE), +m_repeater3Agl(DEFAULT_AGL), +m_repeater3Description1(DEFAULT_DESCRIPTION1), +m_repeater3Description2(DEFAULT_DESCRIPTION2), +m_repeater3URL(DEFAULT_URL), +m_repeater3Band1(DEFAULT_BAND1), +m_repeater3Band2(DEFAULT_BAND2), +m_repeater3Band3(DEFAULT_BAND3), +m_repeater4Callsign(DEFAULT_REPEATER_CALL), +m_repeater4Band(DEFAULT_REPEATER_BAND), +m_repeater4Type(DEFAULT_REPEATER_TYPE), +m_repeater4Address(DEFAULT_REPEATER_ADDRESS), +m_repeater4Port(DEFAULT_REPEATER_PORT4), +m_repeater4Reflector(DEFAULT_REFLECTOR), +m_repeater4AtStartup(DEFAULT_ATSTARTUP), +m_repeater4Reconnect(DEFAULT_RECONNECT), +m_repeater4Frequency(DEFAULT_FREQUENCY), +m_repeater4Offset(DEFAULT_OFFSET), +m_repeater4Range(DEFAULT_RANGE), +m_repeater4Latitude(DEFAULT_LATITUDE), +m_repeater4Longitude(DEFAULT_LONGITUDE), +m_repeater4Agl(DEFAULT_AGL), +m_repeater4Description1(DEFAULT_DESCRIPTION1), +m_repeater4Description2(DEFAULT_DESCRIPTION2), +m_repeater4URL(DEFAULT_URL), +m_repeater4Band1(DEFAULT_BAND1), +m_repeater4Band2(DEFAULT_BAND2), +m_repeater4Band3(DEFAULT_BAND3), +m_ircddbEnabled(DEFAULT_IRCDDB_ENABLED), +m_ircddbHostname(DEFAULT_IRCDDB_HOSTNAME), +m_ircddbUsername(DEFAULT_IRCDDB_USERNAME), +m_ircddbPassword(DEFAULT_IRCDDB_PASSWORD), +m_ircddbEnabled2(DEFAULT_IRCDDB_ENABLED2), +m_ircddbHostname2(DEFAULT_IRCDDB_HOSTNAME2), +m_ircddbUsername2(DEFAULT_IRCDDB_USERNAME2), +m_ircddbPassword2(DEFAULT_IRCDDB_PASSWORD2), +m_ircddbEnabled3(DEFAULT_IRCDDB_ENABLED3), +m_ircddbHostname3(DEFAULT_IRCDDB_HOSTNAME3), +m_ircddbUsername3(DEFAULT_IRCDDB_USERNAME3), +m_ircddbPassword3(DEFAULT_IRCDDB_PASSWORD3), +m_ircddbEnabled4(DEFAULT_IRCDDB_ENABLED4), +m_ircddbHostname4(DEFAULT_IRCDDB_HOSTNAME4), +m_ircddbUsername4(DEFAULT_IRCDDB_USERNAME4), +m_ircddbPassword4(DEFAULT_IRCDDB_PASSWORD4), +m_aprsEnabled(DEFAULT_APRS_ENABLED), +m_aprsHostname(DEFAULT_APRS_HOSTNAME), +m_aprsPort(DEFAULT_APRS_PORT), +m_dextraEnabled(DEFAULT_DEXTRA_ENABLED), +m_dextraMaxDongles(DEFAULT_DEXTRA_MAXDONGLES), +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), +m_starNet1Band(DEFAULT_STARNET_BAND), +m_starNet1Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet1Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet1Info(DEFAULT_STARNET_INFO), +m_starNet1Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet1UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet1GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet1CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet1TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet1Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet2Band(DEFAULT_STARNET_BAND), +m_starNet2Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet2Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet2Info(DEFAULT_STARNET_INFO), +m_starNet2Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet2UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet2GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet2CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet2TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet2Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet3Band(DEFAULT_STARNET_BAND), +m_starNet3Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet3Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet3Info(DEFAULT_STARNET_INFO), +m_starNet3Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet3UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet3GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet3CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet3TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet3Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet4Band(DEFAULT_STARNET_BAND), +m_starNet4Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet4Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet4Info(DEFAULT_STARNET_INFO), +m_starNet4Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet4UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet4GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet4CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet4TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet4Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet5Band(DEFAULT_STARNET_BAND), +m_starNet5Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet5Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet5Info(DEFAULT_STARNET_INFO), +m_starNet5Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet5UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet5GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet5CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet5TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet5Reflector(DEFAULT_STARNET_REFLECTOR), +m_remoteEnabled(DEFAULT_REMOTE_ENABLED), +m_remotePassword(DEFAULT_REMOTE_PASSWORD), +m_remotePort(DEFAULT_REMOTE_PORT), +m_language(DEFAULT_LANGUAGE), +m_infoEnabled(DEFAULT_INFO_ENABLED), +m_echoEnabled(DEFAULT_ECHO_ENABLED), +m_logEnabled(DEFAULT_LOG_ENABLED), +m_dratsEnabled(DEFAULT_DRATS_ENABLED), +m_dtmfEnabled(DEFAULT_DTMF_ENABLED), +m_x(DEFAULT_WINDOW_X), +m_y(DEFAULT_WINDOW_Y) +{ + wxASSERT(!dir.IsEmpty()); + + wxString fileName = configName; + if (!name.IsEmpty()) + fileName = configName + wxT("_") + name; + + m_fileName.Assign(dir, fileName); + + wxTextFile file(m_fileName.GetFullPath()); + + bool exists = file.Exists(); + if (!exists) + return; + + bool ret = file.Open(); + if (!ret) { + wxLogError(wxT("Cannot open the config file - %s"), m_fileName.GetFullPath().c_str()); + return; + } + + long temp1; + unsigned long temp2; + + wxString str = file.GetFirstLine(); + + while (!file.Eof()) { + if (str.GetChar(0U) == wxT('#')) { + str = file.GetNextLine(); + continue; + } + + int n = str.Find(wxT('=')); + if (n == wxNOT_FOUND) { + str = file.GetNextLine(); + continue; + } + + wxString key = str.Left(n); + wxString val = str.Mid(n + 1U); + + if (key.IsSameAs(KEY_GATEWAY_TYPE)) { + val.ToLong(&temp1); + m_type = GATEWAY_TYPE(temp1); + } else if (key.IsSameAs(KEY_GATEWAY_CALLSIGN)) { + m_callsign = val; + } else if (key.IsSameAs(KEY_GATEWAY_ADDRESS)) { + m_address = val; + } else if (key.IsSameAs(KEY_ICOM_ADDRESS)) { + m_icomAddress = val; + } else if (key.IsSameAs(KEY_ICOM_PORT)) { + val.ToULong(&temp2); + m_icomPort = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_HB_ADDRESS)) { + m_hbAddress = val; + } else if (key.IsSameAs(KEY_HB_PORT)) { + val.ToULong(&temp2); + m_hbPort = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_LATITUDE)) { + val.ToDouble(&m_latitude); + } else if (key.IsSameAs(KEY_LONGITUDE)) { + val.ToDouble(&m_longitude); + } else if (key.IsSameAs(KEY_DESCRIPTION1)) { + m_description1 = val; + } else if (key.IsSameAs(KEY_DESCRIPTION2)) { + m_description2 = val; + } else if (key.IsSameAs(KEY_URL)) { + m_url = val; + } else if (key.IsSameAs(KEY_REPEATER_CALL1)) { + m_repeater1Callsign = val; + } else if (key.IsSameAs(KEY_REPEATER_BAND1)) { + m_repeater1Band = val; + } else if (key.IsSameAs(KEY_REPEATER_TYPE1)) { + val.ToLong(&temp1); + m_repeater1Type = HW_TYPE(temp1); + } else if (key.IsSameAs(KEY_REPEATER_ADDRESS1)) { + m_repeater1Address = val; + } else if (key.IsSameAs(KEY_REPEATER_PORT1)) { + val.ToULong(&temp2); + m_repeater1Port = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_REFLECTOR1)) { + m_repeater1Reflector = val; + } else if (key.IsSameAs(KEY_ATSTARTUP1)) { + val.ToLong(&temp1); + m_repeater1AtStartup = temp1 == 1L; + } else if (key.IsSameAs(KEY_RECONNECT1)) { + val.ToLong(&temp1); + m_repeater1Reconnect = RECONNECT(temp1); + } else if (key.IsSameAs(KEY_FREQUENCY1)) { + val.ToDouble(&m_repeater1Frequency); + } else if (key.IsSameAs(KEY_OFFSET1)) { + val.ToDouble(&m_repeater1Offset); + } else if (key.IsSameAs(KEY_RANGE1)) { + val.ToDouble(&m_repeater1Range); + } else if (key.IsSameAs(KEY_LATITUDE1)) { + val.ToDouble(&m_repeater1Latitude); + } else if (key.IsSameAs(KEY_LONGITUDE1)) { + val.ToDouble(&m_repeater1Longitude); + } else if (key.IsSameAs(KEY_AGL1)) { + val.ToDouble(&m_repeater1Agl); + } else if (key.IsSameAs(KEY_DESCRIPTION11)) { + m_repeater1Description1 = val; + } else if (key.IsSameAs(KEY_DESCRIPTION12)) { + m_repeater1Description2 = val; + } else if (key.IsSameAs(KEY_URL1)) { + m_repeater1URL = val; + } else if (key.IsSameAs(KEY_BAND11)) { + val.ToULong(&temp2); + m_repeater1Band1 = (unsigned char)temp2; + } else if (key.IsSameAs(KEY_BAND12)) { + val.ToULong(&temp2); + m_repeater1Band2 = (unsigned char)temp2; + } else if (key.IsSameAs(KEY_BAND13)) { + val.ToULong(&temp2); + m_repeater1Band3 = (unsigned char)temp2; + } else if (key.IsSameAs(KEY_REPEATER_CALL2)) { + m_repeater2Callsign = val; + } else if (key.IsSameAs(KEY_REPEATER_BAND2)) { + m_repeater2Band = val; + } else if (key.IsSameAs(KEY_REPEATER_TYPE2)) { + val.ToLong(&temp1); + m_repeater2Type = HW_TYPE(temp1); + } else if (key.IsSameAs(KEY_REPEATER_ADDRESS2)) { + m_repeater2Address = val; + } else if (key.IsSameAs(KEY_REPEATER_PORT2)) { + val.ToULong(&temp2); + m_repeater2Port = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_REFLECTOR2)) { + m_repeater2Reflector = val; + } else if (key.IsSameAs(KEY_ATSTARTUP2)) { + val.ToLong(&temp1); + m_repeater2AtStartup = temp1 == 1L; + } else if (key.IsSameAs(KEY_RECONNECT2)) { + val.ToLong(&temp1); + m_repeater2Reconnect = RECONNECT(temp1); + } else if (key.IsSameAs(KEY_FREQUENCY2)) { + val.ToDouble(&m_repeater2Frequency); + } else if (key.IsSameAs(KEY_OFFSET2)) { + val.ToDouble(&m_repeater2Offset); + } else if (key.IsSameAs(KEY_RANGE2)) { + val.ToDouble(&m_repeater2Range); + } else if (key.IsSameAs(KEY_LATITUDE2)) { + val.ToDouble(&m_repeater2Latitude); + } else if (key.IsSameAs(KEY_LONGITUDE2)) { + val.ToDouble(&m_repeater2Longitude); + } else if (key.IsSameAs(KEY_AGL2)) { + val.ToDouble(&m_repeater2Agl); + } else if (key.IsSameAs(KEY_DESCRIPTION21)) { + m_repeater2Description1 = val; + } else if (key.IsSameAs(KEY_DESCRIPTION22)) { + m_repeater2Description2 = val; + } else if (key.IsSameAs(KEY_URL2)) { + m_repeater2URL = val; + } else if (key.IsSameAs(KEY_BAND21)) { + val.ToULong(&temp2); + m_repeater2Band1 = (unsigned char)temp2; + } else if (key.IsSameAs(KEY_BAND22)) { + val.ToULong(&temp2); + m_repeater2Band2 = (unsigned char)temp2; + } else if (key.IsSameAs(KEY_BAND23)) { + val.ToULong(&temp2); + m_repeater2Band3 = (unsigned char)temp2; + } else if (key.IsSameAs(KEY_REPEATER_CALL3)) { + m_repeater3Callsign = val; + } else if (key.IsSameAs(KEY_REPEATER_BAND3)) { + m_repeater3Band = val; + } else if (key.IsSameAs(KEY_REPEATER_TYPE3)) { + val.ToLong(&temp1); + m_repeater3Type = HW_TYPE(temp1); + } else if (key.IsSameAs(KEY_REPEATER_ADDRESS3)) { + m_repeater3Address = val; + } else if (key.IsSameAs(KEY_REPEATER_PORT3)) { + val.ToULong(&temp2); + m_repeater3Port = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_REFLECTOR3)) { + m_repeater3Reflector = val; + } else if (key.IsSameAs(KEY_ATSTARTUP3)) { + val.ToLong(&temp1); + m_repeater3AtStartup = temp1 == 1L; + } else if (key.IsSameAs(KEY_RECONNECT3)) { + val.ToLong(&temp1); + m_repeater3Reconnect = RECONNECT(temp1); + } else if (key.IsSameAs(KEY_FREQUENCY3)) { + val.ToDouble(&m_repeater3Frequency); + } else if (key.IsSameAs(KEY_OFFSET3)) { + val.ToDouble(&m_repeater3Offset); + } else if (key.IsSameAs(KEY_RANGE3)) { + val.ToDouble(&m_repeater3Range); + } else if (key.IsSameAs(KEY_LATITUDE3)) { + val.ToDouble(&m_repeater3Latitude); + } else if (key.IsSameAs(KEY_LONGITUDE3)) { + val.ToDouble(&m_repeater3Longitude); + } else if (key.IsSameAs(KEY_AGL3)) { + val.ToDouble(&m_repeater3Agl); + } else if (key.IsSameAs(KEY_DESCRIPTION31)) { + m_repeater3Description1 = val; + } else if (key.IsSameAs(KEY_DESCRIPTION32)) { + m_repeater3Description2 = val; + } else if (key.IsSameAs(KEY_URL3)) { + m_repeater3URL = val; + } else if (key.IsSameAs(KEY_BAND31)) { + val.ToULong(&temp2); + m_repeater3Band1 = (unsigned char)temp2; + } else if (key.IsSameAs(KEY_BAND32)) { + val.ToULong(&temp2); + m_repeater3Band2 = (unsigned char)temp2; + } else if (key.IsSameAs(KEY_BAND33)) { + val.ToULong(&temp2); + m_repeater3Band3 = (unsigned char)temp2; + } else if (key.IsSameAs(KEY_REPEATER_CALL4)) { + m_repeater4Callsign = val; + } else if (key.IsSameAs(KEY_REPEATER_BAND4)) { + m_repeater4Band = val; + } else if (key.IsSameAs(KEY_REPEATER_TYPE4)) { + val.ToLong(&temp1); + m_repeater4Type = HW_TYPE(temp1); + } else if (key.IsSameAs(KEY_REPEATER_ADDRESS4)) { + m_repeater4Address = val; + } else if (key.IsSameAs(KEY_REPEATER_PORT4)) { + val.ToULong(&temp2); + m_repeater4Port = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_REFLECTOR4)) { + m_repeater4Reflector = val; + } else if (key.IsSameAs(KEY_ATSTARTUP4)) { + val.ToLong(&temp1); + m_repeater4AtStartup = temp1 == 1L; + } else if (key.IsSameAs(KEY_RECONNECT4)) { + val.ToLong(&temp1); + m_repeater4Reconnect = RECONNECT(temp1); + } else if (key.IsSameAs(KEY_FREQUENCY4)) { + val.ToDouble(&m_repeater4Frequency); + } else if (key.IsSameAs(KEY_OFFSET4)) { + val.ToDouble(&m_repeater4Offset); + } else if (key.IsSameAs(KEY_RANGE4)) { + val.ToDouble(&m_repeater4Range); + } else if (key.IsSameAs(KEY_LATITUDE4)) { + val.ToDouble(&m_repeater4Latitude); + } else if (key.IsSameAs(KEY_LONGITUDE4)) { + val.ToDouble(&m_repeater4Longitude); + } else if (key.IsSameAs(KEY_AGL4)) { + val.ToDouble(&m_repeater4Agl); + } else if (key.IsSameAs(KEY_DESCRIPTION41)) { + m_repeater4Description1 = val; + } else if (key.IsSameAs(KEY_DESCRIPTION42)) { + m_repeater4Description2 = val; + } else if (key.IsSameAs(KEY_URL4)) { + m_repeater4URL = val; + } else if (key.IsSameAs(KEY_BAND41)) { + val.ToULong(&temp2); + m_repeater4Band1 = (unsigned char)temp2; + } else if (key.IsSameAs(KEY_BAND42)) { + val.ToULong(&temp2); + m_repeater4Band2 = (unsigned char)temp2; + } else if (key.IsSameAs(KEY_BAND43)) { + val.ToULong(&temp2); + m_repeater4Band3 = (unsigned char)temp2; + } else if (key.IsSameAs(KEY_IRCDDB_ENABLED)) { + val.ToLong(&temp1); + m_ircddbEnabled = temp1 == 1L; + } else if (key.IsSameAs(KEY_IRCDDB_HOSTNAME)) { + m_ircddbHostname = val; + } else if (key.IsSameAs(KEY_IRCDDB_USERNAME)) { + m_ircddbUsername = val; + } else if (key.IsSameAs(KEY_IRCDDB_PASSWORD)) { + m_ircddbPassword = val; + } else if (key.IsSameAs(KEY_IRCDDB_ENABLED2)) { + val.ToLong(&temp1); + m_ircddbEnabled2 = temp1 == 1L; + } else if (key.IsSameAs(KEY_IRCDDB_HOSTNAME2)) { + m_ircddbHostname2 = val; + } else if (key.IsSameAs(KEY_IRCDDB_USERNAME2)) { + m_ircddbUsername2 = val; + } else if (key.IsSameAs(KEY_IRCDDB_PASSWORD2)) { + m_ircddbPassword2 = val; + } else if (key.IsSameAs(KEY_IRCDDB_ENABLED3)) { + val.ToLong(&temp1); + m_ircddbEnabled3 = temp1 == 1L; + } else if (key.IsSameAs(KEY_IRCDDB_HOSTNAME3)) { + m_ircddbHostname3 = val; + } else if (key.IsSameAs(KEY_IRCDDB_USERNAME3)) { + m_ircddbUsername3 = val; + } else if (key.IsSameAs(KEY_IRCDDB_PASSWORD3)) { + m_ircddbPassword3 = val; + } else if (key.IsSameAs(KEY_IRCDDB_ENABLED4)) { + val.ToLong(&temp1); + m_ircddbEnabled4 = temp1 == 1L; + } else if (key.IsSameAs(KEY_IRCDDB_HOSTNAME4)) { + m_ircddbHostname4 = val; + } else if (key.IsSameAs(KEY_IRCDDB_USERNAME4)) { + m_ircddbUsername4 = val; + } else if (key.IsSameAs(KEY_IRCDDB_PASSWORD4)) { + m_ircddbPassword4 = val; + } else if (key.IsSameAs(KEY_APRS_ENABLED)) { + val.ToLong(&temp1); + m_aprsEnabled = temp1 == 1L; + } else if (key.IsSameAs(KEY_APRS_HOSTNAME)) { + m_aprsHostname = val; + } else if (key.IsSameAs(KEY_APRS_PORT)) { + val.ToULong(&temp2); + m_aprsPort = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_DEXTRA_ENABLED)) { + val.ToLong(&temp1); + m_dextraEnabled = temp1 == 1L; + } else if (key.IsSameAs(KEY_DEXTRA_MAXDONGLES)) { + val.ToULong(&temp2); + m_dextraMaxDongles = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_DPLUS_ENABLED)) { + val.ToLong(&temp1); + m_dplusEnabled = temp1 == 1L; + } else if (key.IsSameAs(KEY_DPLUS_MAXDONGLES)) { + val.ToULong(&temp2); + m_dplusMaxDongles = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_DPLUS_LOGIN)) { + m_dplusLogin = val; + } 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; + } else if (key.IsSameAs(KEY_XLX_OVERRIDE_LOCAL)) { + val.ToLong(&temp1); + m_xlxOverrideLocal = temp1 == 1L; + } else if (key.IsSameAs(KEY_XLX_HOSTS_FILE_URL)) { + m_xlxHostsFileUrl = val; + } else if (key.IsSameAs(KEY_STARNET_BAND1)) { + m_starNet1Band = val; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN1)) { + m_starNet1Callsign = val; + } else if (key.IsSameAs(KEY_STARNET_LOGOFF1)) { + m_starNet1Logoff = val; + } else if (key.IsSameAs(KEY_STARNET_INFO1)) { + m_starNet1Info = val; + } else if (key.IsSameAs(KEY_STARNET_PERMANENT1)) { + m_starNet1Permanent = val; + } else if (key.IsSameAs(KEY_STARNET_USER_TIMEOUT1)) { + val.ToULong(&temp2); + m_starNet1UserTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_GROUP_TIMEOUT1)) { + val.ToULong(&temp2); + m_starNet1GroupTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN_SWITCH1)) { + val.ToLong(&temp1); + m_starNet1CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp1); + } else if (key.IsSameAs(KEY_STARNET_TXMSG_SWITCH1)) { + val.ToLong(&temp1); + m_starNet1TxMsgSwitch = temp1 == 1L; + } else if (key.IsSameAs(KEY_STARNET_REFLECTOR1)) { + m_starNet1Reflector = val; + } else if (key.IsSameAs(KEY_STARNET_BAND2)) { + m_starNet2Band = val; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN2)) { + m_starNet2Callsign = val; + } else if (key.IsSameAs(KEY_STARNET_LOGOFF2)) { + m_starNet2Logoff = val; + } else if (key.IsSameAs(KEY_STARNET_INFO2)) { + m_starNet2Info = val; + } else if (key.IsSameAs(KEY_STARNET_PERMANENT2)) { + m_starNet2Permanent = val; + } else if (key.IsSameAs(KEY_STARNET_USER_TIMEOUT2)) { + val.ToULong(&temp2); + m_starNet2UserTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_GROUP_TIMEOUT2)) { + val.ToULong(&temp2); + m_starNet2GroupTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN_SWITCH2)) { + val.ToLong(&temp1); + m_starNet2CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp1); + } else if (key.IsSameAs(KEY_STARNET_TXMSG_SWITCH2)) { + val.ToLong(&temp1); + m_starNet2TxMsgSwitch = temp1 == 1L; + } else if (key.IsSameAs(KEY_STARNET_REFLECTOR2)) { + m_starNet2Reflector = val; + } else if (key.IsSameAs(KEY_STARNET_BAND3)) { + m_starNet3Band = val; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN3)) { + m_starNet3Callsign = val; + } else if (key.IsSameAs(KEY_STARNET_LOGOFF3)) { + m_starNet3Logoff = val; + } else if (key.IsSameAs(KEY_STARNET_INFO3)) { + m_starNet3Info = val; + } else if (key.IsSameAs(KEY_STARNET_PERMANENT3)) { + m_starNet3Permanent = val; + } else if (key.IsSameAs(KEY_STARNET_USER_TIMEOUT3)) { + val.ToULong(&temp2); + m_starNet3UserTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_GROUP_TIMEOUT3)) { + val.ToULong(&temp2); + m_starNet3GroupTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN_SWITCH3)) { + val.ToLong(&temp1); + m_starNet3CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp1); + } else if (key.IsSameAs(KEY_STARNET_TXMSG_SWITCH3)) { + val.ToLong(&temp1); + m_starNet3TxMsgSwitch = temp1 == 1L; + } else if (key.IsSameAs(KEY_STARNET_REFLECTOR3)) { + m_starNet3Reflector = val; + } else if (key.IsSameAs(KEY_STARNET_BAND4)) { + m_starNet4Band = val; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN4)) { + m_starNet4Callsign = val; + } else if (key.IsSameAs(KEY_STARNET_LOGOFF4)) { + m_starNet4Logoff = val; + } else if (key.IsSameAs(KEY_STARNET_INFO4)) { + m_starNet4Info = val; + } else if (key.IsSameAs(KEY_STARNET_PERMANENT4)) { + m_starNet4Permanent = val; + } else if (key.IsSameAs(KEY_STARNET_USER_TIMEOUT4)) { + val.ToULong(&temp2); + m_starNet4UserTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_GROUP_TIMEOUT4)) { + val.ToULong(&temp2); + m_starNet4GroupTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN_SWITCH4)) { + val.ToLong(&temp1); + m_starNet4CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp1); + } else if (key.IsSameAs(KEY_STARNET_TXMSG_SWITCH4)) { + val.ToLong(&temp1); + m_starNet4TxMsgSwitch = temp1 == 1L; + } else if (key.IsSameAs(KEY_STARNET_REFLECTOR4)) { + m_starNet4Reflector = val; + } else if (key.IsSameAs(KEY_STARNET_BAND5)) { + m_starNet5Band = val; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN5)) { + m_starNet5Callsign = val; + } else if (key.IsSameAs(KEY_STARNET_LOGOFF5)) { + m_starNet5Logoff = val; + } else if (key.IsSameAs(KEY_STARNET_INFO5)) { + m_starNet5Info = val; + } else if (key.IsSameAs(KEY_STARNET_PERMANENT5)) { + m_starNet5Permanent = val; + } else if (key.IsSameAs(KEY_STARNET_USER_TIMEOUT5)) { + val.ToULong(&temp2); + m_starNet5UserTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_GROUP_TIMEOUT5)) { + val.ToULong(&temp2); + m_starNet5GroupTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN_SWITCH5)) { + val.ToLong(&temp1); + m_starNet5CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp1); + } else if (key.IsSameAs(KEY_STARNET_TXMSG_SWITCH5)) { + val.ToLong(&temp1); + m_starNet5TxMsgSwitch = temp1 == 1L; + } else if (key.IsSameAs(KEY_STARNET_REFLECTOR5)) { + m_starNet5Reflector = val; + } else if (key.IsSameAs(KEY_REMOTE_ENABLED)) { + val.ToLong(&temp1); + m_remoteEnabled = temp1 == 1L; + } else if (key.IsSameAs(KEY_REMOTE_PASSWORD)) { + m_remotePassword = val; + } else if (key.IsSameAs(KEY_REMOTE_PORT)) { + val.ToULong(&temp2); + m_remotePort = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_LANGUAGE)) { + val.ToLong(&temp1); + m_language = TEXT_LANG(temp1); + } else if (key.IsSameAs(KEY_INFO_ENABLED)) { + val.ToLong(&temp1); + m_infoEnabled = temp1 == 1L; + } else if (key.IsSameAs(KEY_ECHO_ENABLED)) { + val.ToLong(&temp1); + m_echoEnabled = temp1 == 1L; + } else if (key.IsSameAs(KEY_LOG_ENABLED)) { + val.ToLong(&temp1); + m_logEnabled = temp1 == 1L; + } else if (key.IsSameAs(KEY_DRATS_ENABLED)) { + val.ToLong(&temp1); + m_dratsEnabled = temp1 == 1L; + } else if (key.IsSameAs(KEY_DTMF_ENABLED)) { + val.ToLong(&temp1); + m_dtmfEnabled = temp1 == 1L; + } else if (key.IsSameAs(KEY_WINDOW_X)) { + val.ToLong(&temp1); + m_x = int(temp1); + } else if (key.IsSameAs(KEY_WINDOW_Y)) { + val.ToLong(&temp1); + m_y = int(temp1); + } + + str = file.GetNextLine(); + } + + file.Close(); +} + +CIRCDDBGatewayConfig::~CIRCDDBGatewayConfig() +{ +} + +#endif + +void CIRCDDBGatewayConfig::getGateway(GATEWAY_TYPE& type, wxString& callsign, wxString& address, wxString& icomAddress, unsigned int& icomPort, wxString& hbAddress, unsigned int& hbPort, double& latitude, double& longitude, wxString& description1, wxString& description2, wxString& url) const +{ + type = m_type; + callsign = m_callsign; + address = m_address; + icomAddress = m_icomAddress; + icomPort = m_icomPort; + hbAddress = m_hbAddress; + hbPort = m_hbPort; + latitude = m_latitude; + longitude = m_longitude; + description1 = m_description1; + description2 = m_description2; + url = m_url; + + if (address.IsSameAs(wxT("127.0.0.1"))) + address.Clear(); +} + +void CIRCDDBGatewayConfig::setGateway(GATEWAY_TYPE type, const wxString& callsign, const wxString& address, const wxString& icomAddress, unsigned int icomPort, const wxString& hbAddress, unsigned int hbPort, double latitude, double longitude, const wxString& description1, const wxString& description2, const wxString& url) +{ + m_type = type; + m_callsign = callsign; + m_address = address; + m_icomAddress = icomAddress; + m_icomPort = icomPort; + m_hbAddress = hbAddress; + m_hbPort = hbPort; + m_latitude = latitude; + m_longitude = longitude; + m_description1 = description1; + m_description2 = description2; + m_url = url; +} + +void CIRCDDBGatewayConfig::getRepeater1(wxString& callsign, wxString& band, HW_TYPE& type, wxString& address, unsigned int& port, unsigned char& band1, unsigned char& band2, unsigned char& band3, wxString& reflector, bool& atStartup, RECONNECT& reconnect, double& frequency, double& offset, double& range, double& latitude, double& longitude, double& agl, wxString& description1, wxString& description2, wxString& url) const +{ + callsign = m_repeater1Callsign; + band = m_repeater1Band; + type = m_repeater1Type; + address = m_repeater1Address; + port = m_repeater1Port; + band1 = m_repeater1Band1; + band2 = m_repeater1Band2; + band3 = m_repeater1Band3; + reflector = m_repeater1Reflector; + atStartup = m_repeater1AtStartup; + reconnect = m_repeater1Reconnect; + frequency = m_repeater1Frequency; + offset = m_repeater1Offset; + range = m_repeater1Range; + latitude = m_repeater1Latitude; + longitude = m_repeater1Longitude; + agl = m_repeater1Agl; + description1 = m_repeater1Description1; + description2 = m_repeater1Description2; + url = m_repeater1URL; +} + +void CIRCDDBGatewayConfig::setRepeater1(const wxString& band, HW_TYPE type, const wxString& address, unsigned int port, unsigned char band1, unsigned char band2, unsigned char band3, const wxString& reflector, bool atStartup, RECONNECT reconnect, double frequency, double offset, double range, double latitude, double longitude, double agl, const wxString& description1, const wxString& description2, const wxString& url) +{ + m_repeater1Band = band; + m_repeater1Type = type; + m_repeater1Address = address; + m_repeater1Port = port; + m_repeater1Band1 = band1; + m_repeater1Band2 = band2; + m_repeater1Band3 = band3; + m_repeater1Reflector = reflector; + m_repeater1AtStartup = atStartup; + m_repeater1Reconnect = reconnect; + m_repeater1Frequency = frequency; + m_repeater1Offset = offset; + m_repeater1Range = range; + m_repeater1Latitude = latitude; + m_repeater1Longitude = longitude; + m_repeater1Agl = agl; + m_repeater1Description1 = description1; + m_repeater1Description2 = description2; + m_repeater1URL = url; +} + +void CIRCDDBGatewayConfig::getRepeater2(wxString& callsign, wxString& band, HW_TYPE& type, wxString& address, unsigned int& port, unsigned char& band1, unsigned char& band2, unsigned char& band3, wxString& reflector, bool& atStartup, RECONNECT& reconnect, double& frequency, double& offset, double& range, double& latitude, double& longitude, double& agl, wxString& description1, wxString& description2, wxString& url) const +{ + callsign = m_repeater2Callsign; + band = m_repeater2Band; + type = m_repeater2Type; + address = m_repeater2Address; + port = m_repeater2Port; + band1 = m_repeater2Band1; + band2 = m_repeater2Band2; + band3 = m_repeater2Band3; + reflector = m_repeater2Reflector; + atStartup = m_repeater2AtStartup; + reconnect = m_repeater2Reconnect; + frequency = m_repeater2Frequency; + offset = m_repeater2Offset; + range = m_repeater2Range; + latitude = m_repeater2Latitude; + longitude = m_repeater2Longitude; + agl = m_repeater2Agl; + description1 = m_repeater2Description1; + description2 = m_repeater2Description2; + url = m_repeater2URL; +} + +void CIRCDDBGatewayConfig::setRepeater2(const wxString& band, HW_TYPE type, const wxString& address, unsigned int port, unsigned char band1, unsigned char band2, unsigned char band3, const wxString& reflector, bool atStartup, RECONNECT reconnect, double frequency, double offset, double range, double latitude, double longitude, double agl, const wxString& description1, const wxString& description2, const wxString& url) +{ + m_repeater2Band = band; + m_repeater2Type = type; + m_repeater2Address = address; + m_repeater2Port = port; + m_repeater2Band1 = band1; + m_repeater2Band2 = band2; + m_repeater2Band3 = band3; + m_repeater2Reflector = reflector; + m_repeater2AtStartup = atStartup; + m_repeater2Reconnect = reconnect; + m_repeater2Frequency = frequency; + m_repeater2Offset = offset; + m_repeater2Range = range; + m_repeater2Latitude = latitude; + m_repeater2Longitude = longitude; + m_repeater2Agl = agl; + m_repeater2Description1 = description1; + m_repeater2Description2 = description2; + m_repeater2URL = url; +} + +void CIRCDDBGatewayConfig::getRepeater3(wxString& callsign, wxString& band, HW_TYPE& type, wxString& address, unsigned int& port, unsigned char& band1, unsigned char& band2, unsigned char& band3, wxString& reflector, bool& atStartup, RECONNECT& reconnect, double& frequency, double& offset, double& range, double& latitude, double& longitude, double& agl, wxString& description1, wxString& description2, wxString& url) const +{ + callsign = m_repeater3Callsign; + band = m_repeater3Band; + type = m_repeater3Type; + address = m_repeater3Address; + port = m_repeater3Port; + band1 = m_repeater3Band1; + band2 = m_repeater3Band2; + band3 = m_repeater3Band3; + reflector = m_repeater3Reflector; + atStartup = m_repeater3AtStartup; + reconnect = m_repeater3Reconnect; + frequency = m_repeater3Frequency; + offset = m_repeater3Offset; + range = m_repeater3Range; + latitude = m_repeater3Latitude; + longitude = m_repeater3Longitude; + agl = m_repeater3Agl; + description1 = m_repeater3Description1; + description2 = m_repeater3Description2; + url = m_repeater3URL; +} + +void CIRCDDBGatewayConfig::setRepeater3(const wxString& band, HW_TYPE type, const wxString& address, unsigned int port, unsigned char band1, unsigned char band2, unsigned char band3, const wxString& reflector, bool atStartup, RECONNECT reconnect, double frequency, double offset, double range, double latitude, double longitude, double agl, const wxString& description1, const wxString& description2, const wxString& url) +{ + m_repeater3Band = band; + m_repeater3Type = type; + m_repeater3Address = address; + m_repeater3Port = port; + m_repeater3Band1 = band1; + m_repeater3Band2 = band2; + m_repeater3Band3 = band3; + m_repeater3Reflector = reflector; + m_repeater3AtStartup = atStartup; + m_repeater3Reconnect = reconnect; + m_repeater3Frequency = frequency; + m_repeater3Offset = offset; + m_repeater3Range = range; + m_repeater3Latitude = latitude; + m_repeater3Longitude = longitude; + m_repeater3Agl = agl; + m_repeater3Description1 = description1; + m_repeater3Description2 = description2; + m_repeater3URL = url; +} + +void CIRCDDBGatewayConfig::getRepeater4(wxString& callsign, wxString& band, HW_TYPE& type, wxString& address, unsigned int& port, unsigned char& band1, unsigned char& band2, unsigned char& band3, wxString& reflector, bool& atStartup, RECONNECT& reconnect, double& frequency, double& offset, double& range, double& latitude, double& longitude, double& agl, wxString& description1, wxString& description2, wxString& url) const +{ + callsign = m_repeater4Callsign; + band = m_repeater4Band; + type = m_repeater4Type; + address = m_repeater4Address; + port = m_repeater4Port; + band1 = m_repeater4Band1; + band2 = m_repeater4Band2; + band3 = m_repeater4Band3; + reflector = m_repeater4Reflector; + atStartup = m_repeater4AtStartup; + reconnect = m_repeater4Reconnect; + frequency = m_repeater4Frequency; + offset = m_repeater4Offset; + range = m_repeater4Range; + latitude = m_repeater4Latitude; + longitude = m_repeater4Longitude; + agl = m_repeater4Agl; + description1 = m_repeater4Description1; + description2 = m_repeater4Description2; + url = m_repeater4URL; +} + +void CIRCDDBGatewayConfig::setRepeater4(const wxString& band, HW_TYPE type, const wxString& address, unsigned int port, unsigned char band1, unsigned char band2, unsigned char band3, const wxString& reflector, bool atStartup, RECONNECT reconnect, double frequency, double offset, double range, double latitude, double longitude, double agl, const wxString& description1, const wxString& description2, const wxString& url) +{ + m_repeater4Band = band; + m_repeater4Type = type; + m_repeater4Address = address; + m_repeater4Port = port; + m_repeater4Band1 = band1; + m_repeater4Band2 = band2; + m_repeater4Band3 = band3; + m_repeater4Reflector = reflector; + m_repeater4AtStartup = atStartup; + m_repeater4Reconnect = reconnect; + m_repeater4Frequency = frequency; + m_repeater4Offset = offset; + m_repeater4Range = range; + m_repeater4Latitude = latitude; + m_repeater4Longitude = longitude; + m_repeater4Agl = agl; + m_repeater4Description1 = description1; + m_repeater4Description2 = description2; + m_repeater4URL = url; +} + +void CIRCDDBGatewayConfig::getIrcDDB(bool& enabled, wxString& hostname, wxString& username, wxString& password) const +{ + enabled = m_ircddbEnabled; + hostname = m_ircddbHostname; + username = m_ircddbUsername; + password = m_ircddbPassword; +} + +void CIRCDDBGatewayConfig::setIrcDDB(bool enabled, const wxString& hostname, const wxString& username, const wxString& password) +{ + m_ircddbEnabled = enabled; + m_ircddbHostname = hostname; + m_ircddbUsername = username; + m_ircddbPassword = password; +} + +void CIRCDDBGatewayConfig::getIrcDDB2(bool& enabled, wxString& hostname, wxString& username, wxString& password) const +{ + enabled = m_ircddbEnabled2; + hostname = m_ircddbHostname2; + username = m_ircddbUsername2; + + /*if(username.IsEmpty()){ + //no user specified for openquad? use the one from the default network ! + username = m_ircddbUsername; + if(username[0] >= '0' && username[0] <= '9') + username = wxT("r") + username; + }*/ + + password = m_ircddbPassword2; +} + +void CIRCDDBGatewayConfig::setIrcDDB2(bool enabled, const wxString& hostname, const wxString& username, const wxString& password) +{ + m_ircddbEnabled2 = enabled; + m_ircddbHostname2 = hostname; + m_ircddbUsername2 = username; + m_ircddbPassword2 = password; +} + +void CIRCDDBGatewayConfig::getIrcDDB3(bool& enabled, wxString& hostname, wxString& username, wxString& password) const +{ + enabled = m_ircddbEnabled3; + hostname = m_ircddbHostname3; + username = m_ircddbUsername3; + password = m_ircddbPassword3; +} + +void CIRCDDBGatewayConfig::setIrcDDB3(bool enabled, const wxString& hostname, const wxString& username, const wxString& password) +{ + m_ircddbEnabled3 = enabled; + m_ircddbHostname3 = hostname; + m_ircddbUsername3 = username; + m_ircddbPassword3 = password; +} + +void CIRCDDBGatewayConfig::getIrcDDB4(bool& enabled, wxString& hostname, wxString& username, wxString& password) const +{ + enabled = m_ircddbEnabled4; + hostname = m_ircddbHostname4; + username = m_ircddbUsername4; + password = m_ircddbPassword4; +} + +void CIRCDDBGatewayConfig::setIrcDDB4(bool enabled, const wxString& hostname, const wxString& username, const wxString& password) +{ + m_ircddbEnabled4 = enabled; + m_ircddbHostname4 = hostname; + m_ircddbUsername4 = username; + m_ircddbPassword4 = password; +} + +void CIRCDDBGatewayConfig::getDPRS(bool& enabled, wxString& hostname, unsigned int& port) const +{ + enabled = m_aprsEnabled; + hostname = m_aprsHostname; + port = m_aprsPort; +} + +void CIRCDDBGatewayConfig::setDPRS(bool enabled, const wxString& hostname, unsigned int port) +{ + m_aprsEnabled = enabled; + m_aprsHostname = hostname; + m_aprsPort = port; +} + +void CIRCDDBGatewayConfig::getDExtra(bool& enabled, unsigned int& maxDongles) const +{ + enabled = m_dextraEnabled; + maxDongles = m_dextraMaxDongles; +} + +void CIRCDDBGatewayConfig::setDExtra(bool enabled, unsigned int maxDongles) +{ + m_dextraEnabled = enabled; + m_dextraMaxDongles = maxDongles; +} + +void CIRCDDBGatewayConfig::getDPlus(bool& enabled, unsigned int& maxDongles, wxString& login) const +{ + enabled = m_dplusEnabled; + maxDongles = m_dplusMaxDongles; + login = m_dplusLogin; +} + +void CIRCDDBGatewayConfig::setDPlus(bool enabled, unsigned int maxDongles, const wxString& login) +{ + m_dplusEnabled = enabled; + m_dplusMaxDongles = maxDongles; + m_dplusLogin = login; +} + +void CIRCDDBGatewayConfig::getDCS(bool& dcsEnabled, bool& ccsEnabled, wxString& ccsHost) const +{ + dcsEnabled = m_dcsEnabled; + ccsEnabled = m_ccsEnabled; + ccsHost = m_ccsHost; +} + +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) +{ + xlxEnabled = m_xlxEnabled; + xlxOverrideLocal = m_xlxOverrideLocal; + xlxHostsFileUrl = m_xlxHostsFileUrl; +} + +void CIRCDDBGatewayConfig::setXLX(bool xlxEnabled, bool xlxOverrideLocal, wxString xlxHostsFileUrl) +{ + m_xlxEnabled = xlxEnabled; + m_xlxOverrideLocal = xlxOverrideLocal; + m_xlxHostsFileUrl = xlxHostsFileUrl; +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CIRCDDBGatewayConfig::getStarNet1(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +#else +void CIRCDDBGatewayConfig::getStarNet1(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +#endif +{ + band = m_starNet1Band; + callsign = m_starNet1Callsign; + logoff = m_starNet1Logoff; + info = m_starNet1Info; + permanent = m_starNet1Permanent; + userTimeout = m_starNet1UserTimeout; + groupTimeout = m_starNet1GroupTimeout; + callsignSwitch = m_starNet1CallsignSwitch; + txMsgSwitch = m_starNet1TxMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + reflector = m_starNet1Reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CIRCDDBGatewayConfig::setStarNet1(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +#else +void CIRCDDBGatewayConfig::setStarNet1(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +#endif +{ + m_starNet1Band = band; + m_starNet1Callsign = callsign; + m_starNet1Logoff = logoff; + m_starNet1Info = info; + m_starNet1Permanent = permanent; + m_starNet1UserTimeout = userTimeout; + m_starNet1GroupTimeout = groupTimeout; + m_starNet1CallsignSwitch = callsignSwitch; + m_starNet1TxMsgSwitch = txMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_starNet1Reflector = reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CIRCDDBGatewayConfig::getStarNet2(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +#else +void CIRCDDBGatewayConfig::getStarNet2(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +#endif +{ + band = m_starNet2Band; + callsign = m_starNet2Callsign; + logoff = m_starNet2Logoff; + info = m_starNet2Info; + permanent = m_starNet2Permanent; + userTimeout = m_starNet2UserTimeout; + groupTimeout = m_starNet2GroupTimeout; + callsignSwitch = m_starNet2CallsignSwitch; + txMsgSwitch = m_starNet2TxMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + reflector = m_starNet2Reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CIRCDDBGatewayConfig::setStarNet2(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +#else +void CIRCDDBGatewayConfig::setStarNet2(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +#endif +{ + m_starNet2Band = band; + m_starNet2Callsign = callsign; + m_starNet2Logoff = logoff; + m_starNet2Info = info; + m_starNet2Permanent = permanent; + m_starNet2UserTimeout = userTimeout; + m_starNet2GroupTimeout = groupTimeout; + m_starNet2CallsignSwitch = callsignSwitch; + m_starNet2TxMsgSwitch = txMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_starNet2Reflector = reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CIRCDDBGatewayConfig::getStarNet3(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +#else +void CIRCDDBGatewayConfig::getStarNet3(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +#endif +{ + band = m_starNet3Band; + callsign = m_starNet3Callsign; + logoff = m_starNet3Logoff; + info = m_starNet3Info; + permanent = m_starNet3Permanent; + userTimeout = m_starNet3UserTimeout; + groupTimeout = m_starNet3GroupTimeout; + callsignSwitch = m_starNet3CallsignSwitch; + txMsgSwitch = m_starNet3TxMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + reflector = m_starNet3Reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CIRCDDBGatewayConfig::setStarNet3(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +#else +void CIRCDDBGatewayConfig::setStarNet3(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +#endif +{ + m_starNet3Band = band; + m_starNet3Callsign = callsign; + m_starNet3Logoff = logoff; + m_starNet3Info = info; + m_starNet3Permanent = permanent; + m_starNet3UserTimeout = userTimeout; + m_starNet3GroupTimeout = groupTimeout; + m_starNet3CallsignSwitch = callsignSwitch; + m_starNet3TxMsgSwitch = txMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_starNet3Reflector = reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CIRCDDBGatewayConfig::getStarNet4(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +#else +void CIRCDDBGatewayConfig::getStarNet4(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +#endif +{ + band = m_starNet4Band; + callsign = m_starNet4Callsign; + logoff = m_starNet4Logoff; + info = m_starNet4Info; + permanent = m_starNet4Permanent; + userTimeout = m_starNet4UserTimeout; + groupTimeout = m_starNet4GroupTimeout; + callsignSwitch = m_starNet4CallsignSwitch; + txMsgSwitch = m_starNet4TxMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + reflector = m_starNet4Reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CIRCDDBGatewayConfig::setStarNet4(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +#else +void CIRCDDBGatewayConfig::setStarNet4(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +#endif +{ + m_starNet4Band = band; + m_starNet4Callsign = callsign; + m_starNet4Logoff = logoff; + m_starNet4Info = info; + m_starNet4Permanent = permanent; + m_starNet4UserTimeout = userTimeout; + m_starNet4GroupTimeout = groupTimeout; + m_starNet4CallsignSwitch = callsignSwitch; + m_starNet4TxMsgSwitch = txMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_starNet4Reflector = reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CIRCDDBGatewayConfig::getStarNet5(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +#else +void CIRCDDBGatewayConfig::getStarNet5(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +#endif +{ + band = m_starNet5Band; + callsign = m_starNet5Callsign; + logoff = m_starNet5Logoff; + info = m_starNet5Info; + permanent = m_starNet5Permanent; + userTimeout = m_starNet5UserTimeout; + groupTimeout = m_starNet5GroupTimeout; + callsignSwitch = m_starNet5CallsignSwitch; + txMsgSwitch = m_starNet5TxMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + reflector = m_starNet5Reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CIRCDDBGatewayConfig::setStarNet5(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +#else +void CIRCDDBGatewayConfig::setStarNet5(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +#endif +{ + m_starNet5Band = band; + m_starNet5Callsign = callsign; + m_starNet5Logoff = logoff; + m_starNet5Info = info; + m_starNet5Permanent = permanent; + m_starNet5UserTimeout = userTimeout; + m_starNet5GroupTimeout = groupTimeout; + m_starNet5CallsignSwitch = callsignSwitch; + m_starNet5TxMsgSwitch = txMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_starNet5Reflector = reflector; +#endif +} + +void CIRCDDBGatewayConfig::getRemote(bool& enabled, wxString& password, unsigned int& port) const +{ + enabled = m_remoteEnabled; + password = m_remotePassword; + port = m_remotePort; +} + +void CIRCDDBGatewayConfig::setRemote(bool enabled, const wxString& password, unsigned int port) +{ + m_remoteEnabled = enabled; + m_remotePassword = password; + m_remotePort = port; +} + +void CIRCDDBGatewayConfig::getMiscellaneous(TEXT_LANG& language, bool& infoEnabled, bool& echoEnabled, bool& logEnabled, bool& dratsEnabled, bool& dtmfEnabled) const +{ + language = m_language; + infoEnabled = m_infoEnabled; + echoEnabled = m_echoEnabled; + logEnabled = m_logEnabled; + dratsEnabled = m_dratsEnabled; + dtmfEnabled = m_dtmfEnabled; +} + +void CIRCDDBGatewayConfig::setMiscellaneous(TEXT_LANG language, bool infoEnabled, bool echoEnabled, bool logEnabled, bool dratsEnabled, bool dtmfEnabled) +{ + m_language = language; + m_infoEnabled = infoEnabled; + m_echoEnabled = echoEnabled; + m_logEnabled = logEnabled; + m_dratsEnabled = dratsEnabled; + m_dtmfEnabled = dtmfEnabled; +} + +void CIRCDDBGatewayConfig::getPosition(int& x, int& y) const +{ + x = m_x; + y = m_y; +} + +void CIRCDDBGatewayConfig::setPosition(int x, int y) +{ + m_x = x; + m_y = y; +} + +bool CIRCDDBGatewayConfig::write() +{ +#if defined(__WINDOWS__) + wxString text; + + m_config->Write(m_name + KEY_GATEWAY_TYPE, long(m_type)); + m_config->Write(m_name + KEY_GATEWAY_CALLSIGN, m_callsign); + m_config->Write(m_name + KEY_GATEWAY_ADDRESS, m_address); + m_config->Write(m_name + KEY_ICOM_ADDRESS, m_icomAddress); + m_config->Write(m_name + KEY_ICOM_PORT, long(m_icomPort)); + m_config->Write(m_name + KEY_HB_ADDRESS, m_hbAddress); + m_config->Write(m_name + KEY_HB_PORT, long(m_hbPort)); + + text.Printf(wxT("%.6lf"), m_latitude); + m_config->Write(m_name + KEY_LATITUDE, text); + + text.Printf(wxT("%.6lf"), m_longitude); + m_config->Write(m_name + KEY_LONGITUDE, text); + + m_config->Write(m_name + KEY_DESCRIPTION1, m_description1); + m_config->Write(m_name + KEY_DESCRIPTION2, m_description2); + m_config->Write(m_name + KEY_URL, m_url); + m_config->Write(m_name + KEY_REPEATER_BAND1, m_repeater1Band); + m_config->Write(m_name + KEY_REPEATER_TYPE1, long(m_repeater1Type)); + m_config->Write(m_name + KEY_REPEATER_ADDRESS1, m_repeater1Address); + m_config->Write(m_name + KEY_REPEATER_PORT1, long(m_repeater1Port)); + m_config->Write(m_name + KEY_REFLECTOR1, m_repeater1Reflector); + m_config->Write(m_name + KEY_ATSTARTUP1, m_repeater1AtStartup); + m_config->Write(m_name + KEY_RECONNECT1, long(m_repeater1Reconnect)); + + text.Printf(wxT("%.5lf"), m_repeater1Frequency); + m_config->Write(m_name + KEY_FREQUENCY1, text); + + text.Printf(wxT("%.4lf"), m_repeater1Offset); + m_config->Write(m_name + KEY_OFFSET1, text); + + m_config->Write(m_name + KEY_RANGE1, m_repeater1Range); + + text.Printf(wxT("%.6lf"), m_repeater1Latitude); + m_config->Write(m_name + KEY_LATITUDE1, text); + + text.Printf(wxT("%.6lf"), m_repeater1Longitude); + m_config->Write(m_name + KEY_LONGITUDE1, text); + + m_config->Write(m_name + KEY_AGL1, m_repeater1Agl); + m_config->Write(m_name + KEY_DESCRIPTION11, m_repeater1Description1); + m_config->Write(m_name + KEY_DESCRIPTION12, m_repeater1Description2); + m_config->Write(m_name + KEY_URL1, m_repeater1URL); + m_config->Write(m_name + KEY_BAND11, long(m_repeater1Band1)); + m_config->Write(m_name + KEY_BAND12, long(m_repeater1Band2)); + m_config->Write(m_name + KEY_BAND13, long(m_repeater1Band3)); + m_config->Write(m_name + KEY_REPEATER_BAND2, m_repeater2Band); + m_config->Write(m_name + KEY_REPEATER_TYPE2, long(m_repeater2Type)); + m_config->Write(m_name + KEY_REPEATER_ADDRESS2, m_repeater2Address); + m_config->Write(m_name + KEY_REPEATER_PORT2, long(m_repeater2Port)); + m_config->Write(m_name + KEY_REFLECTOR2, m_repeater2Reflector); + m_config->Write(m_name + KEY_ATSTARTUP2, m_repeater2AtStartup); + m_config->Write(m_name + KEY_RECONNECT2, long(m_repeater2Reconnect)); + + text.Printf(wxT("%.5lf"), m_repeater2Frequency); + m_config->Write(m_name + KEY_FREQUENCY2, text); + + text.Printf(wxT("%.4lf"), m_repeater2Offset); + m_config->Write(m_name + KEY_OFFSET2, text); + + m_config->Write(m_name + KEY_RANGE2, m_repeater2Range); + + text.Printf(wxT("%.6lf"), m_repeater2Latitude); + m_config->Write(m_name + KEY_LATITUDE2, text); + + text.Printf(wxT("%.6lf"), m_repeater2Longitude); + m_config->Write(m_name + KEY_LONGITUDE2, text); + + m_config->Write(m_name + KEY_AGL2, m_repeater2Agl); + m_config->Write(m_name + KEY_DESCRIPTION21, m_repeater2Description1); + m_config->Write(m_name + KEY_DESCRIPTION22, m_repeater2Description2); + m_config->Write(m_name + KEY_URL2, m_repeater2URL); + m_config->Write(m_name + KEY_BAND21, long(m_repeater2Band1)); + m_config->Write(m_name + KEY_BAND22, long(m_repeater2Band2)); + m_config->Write(m_name + KEY_BAND23, long(m_repeater2Band3)); + m_config->Write(m_name + KEY_REPEATER_BAND3, m_repeater3Band); + m_config->Write(m_name + KEY_REPEATER_TYPE3, long(m_repeater3Type)); + m_config->Write(m_name + KEY_REPEATER_ADDRESS3, m_repeater3Address); + m_config->Write(m_name + KEY_REPEATER_PORT3, long(m_repeater3Port)); + m_config->Write(m_name + KEY_REFLECTOR3, m_repeater3Reflector); + m_config->Write(m_name + KEY_ATSTARTUP3, m_repeater3AtStartup); + m_config->Write(m_name + KEY_RECONNECT3, long(m_repeater3Reconnect)); + + text.Printf(wxT("%.5lf"), m_repeater3Frequency); + m_config->Write(m_name + KEY_FREQUENCY3, text); + + text.Printf(wxT("%.4lf"), m_repeater3Offset); + m_config->Write(m_name + KEY_OFFSET3, text); + + m_config->Write(m_name + KEY_RANGE3, m_repeater3Range); + + text.Printf(wxT("%.6lf"), m_repeater3Latitude); + m_config->Write(m_name + KEY_LATITUDE3, text); + + text.Printf(wxT("%.6lf"), m_repeater3Longitude); + m_config->Write(m_name + KEY_LONGITUDE3, text); + + m_config->Write(m_name + KEY_AGL3, m_repeater3Agl); + m_config->Write(m_name + KEY_DESCRIPTION31, m_repeater3Description1); + m_config->Write(m_name + KEY_DESCRIPTION32, m_repeater3Description2); + m_config->Write(m_name + KEY_URL3, m_repeater3URL); + m_config->Write(m_name + KEY_BAND31, long(m_repeater3Band1)); + m_config->Write(m_name + KEY_BAND32, long(m_repeater3Band2)); + m_config->Write(m_name + KEY_BAND33, long(m_repeater3Band3)); + m_config->Write(m_name + KEY_REPEATER_BAND4, m_repeater4Band); + m_config->Write(m_name + KEY_REPEATER_TYPE4, long(m_repeater4Type)); + m_config->Write(m_name + KEY_REPEATER_ADDRESS4, m_repeater4Address); + m_config->Write(m_name + KEY_REPEATER_PORT4, long(m_repeater4Port)); + m_config->Write(m_name + KEY_REFLECTOR4, m_repeater4Reflector); + m_config->Write(m_name + KEY_ATSTARTUP4, m_repeater4AtStartup); + m_config->Write(m_name + KEY_RECONNECT4, long(m_repeater4Reconnect)); + + text.Printf(wxT("%.5lf"), m_repeater4Frequency); + m_config->Write(m_name + KEY_FREQUENCY4, text); + + text.Printf(wxT("%.4lf"), m_repeater4Offset); + m_config->Write(m_name + KEY_OFFSET4, text); + + m_config->Write(m_name + KEY_RANGE4, m_repeater4Range); + + text.Printf(wxT("%.6lf"), m_repeater4Latitude); + m_config->Write(m_name + KEY_LATITUDE4, text); + + text.Printf(wxT("%.6lf"), m_repeater4Longitude); + m_config->Write(m_name + KEY_LONGITUDE4, text); + + m_config->Write(m_name + KEY_AGL4, m_repeater4Agl); + m_config->Write(m_name + KEY_DESCRIPTION41, m_repeater4Description1); + m_config->Write(m_name + KEY_DESCRIPTION42, m_repeater4Description2); + m_config->Write(m_name + KEY_URL4, m_repeater4URL); + m_config->Write(m_name + KEY_BAND41, long(m_repeater4Band1)); + m_config->Write(m_name + KEY_BAND42, long(m_repeater4Band2)); + m_config->Write(m_name + KEY_BAND43, long(m_repeater4Band3)); + m_config->Write(m_name + KEY_IRCDDB_ENABLED, m_ircddbEnabled); + m_config->Write(m_name + KEY_IRCDDB_HOSTNAME, m_ircddbHostname); + m_config->Write(m_name + KEY_IRCDDB_USERNAME, m_ircddbUsername); + m_config->Write(m_name + KEY_IRCDDB_PASSWORD, m_ircddbPassword); + m_config->Write(m_name + KEY_IRCDDB_ENABLED2, m_ircddbEnabled2); + m_config->Write(m_name + KEY_IRCDDB_HOSTNAME2, m_ircddbHostname2); + m_config->Write(m_name + KEY_IRCDDB_USERNAME2, m_ircddbUsername2); + m_config->Write(m_name + KEY_IRCDDB_PASSWORD2, m_ircddbPassword2); + m_config->Write(m_name + KEY_IRCDDB_ENABLED3, m_ircddbEnabled3); + m_config->Write(m_name + KEY_IRCDDB_HOSTNAME3, m_ircddbHostname3); + m_config->Write(m_name + KEY_IRCDDB_USERNAME3, m_ircddbUsername3); + m_config->Write(m_name + KEY_IRCDDB_PASSWORD3, m_ircddbPassword3); + m_config->Write(m_name + KEY_IRCDDB_ENABLED4, m_ircddbEnabled4); + m_config->Write(m_name + KEY_IRCDDB_HOSTNAME4, m_ircddbHostname4); + m_config->Write(m_name + KEY_IRCDDB_USERNAME4, m_ircddbUsername4); + m_config->Write(m_name + KEY_IRCDDB_PASSWORD4, m_ircddbPassword4); + m_config->Write(m_name + KEY_APRS_ENABLED, m_aprsEnabled); + m_config->Write(m_name + KEY_APRS_HOSTNAME, m_aprsHostname); + m_config->Write(m_name + KEY_APRS_PORT, long(m_aprsPort)); + m_config->Write(m_name + KEY_DEXTRA_ENABLED, m_dextraEnabled); + m_config->Write(m_name + KEY_DEXTRA_MAXDONGLES, long(m_dextraMaxDongles)); + m_config->Write(m_name + KEY_DPLUS_ENABLED, m_dplusEnabled); + 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); + m_config->Write(m_name + KEY_STARNET_BAND1, m_starNet1Band); + m_config->Write(m_name + KEY_STARNET_CALLSIGN1, m_starNet1Callsign); + m_config->Write(m_name + KEY_STARNET_LOGOFF1, m_starNet1Logoff); + m_config->Write(m_name + KEY_STARNET_INFO1, m_starNet1Info); + m_config->Write(m_name + KEY_STARNET_PERMANENT1, m_starNet1Permanent); + m_config->Write(m_name + KEY_STARNET_USER_TIMEOUT1, long(m_starNet1UserTimeout)); + m_config->Write(m_name + KEY_STARNET_GROUP_TIMEOUT1, long(m_starNet1GroupTimeout)); + m_config->Write(m_name + KEY_STARNET_CALLSIGN_SWITCH1, long(m_starNet1CallsignSwitch)); + m_config->Write(m_name + KEY_STARNET_TXMSG_SWITCH1, long(m_starNet1TxMsgSwitch)); + m_config->Write(m_name + KEY_STARNET_REFLECTOR1, m_starNet1Reflector); + m_config->Write(m_name + KEY_STARNET_BAND2, m_starNet2Band); + m_config->Write(m_name + KEY_STARNET_CALLSIGN2, m_starNet2Callsign); + m_config->Write(m_name + KEY_STARNET_LOGOFF2, m_starNet2Logoff); + m_config->Write(m_name + KEY_STARNET_INFO2, m_starNet2Info); + m_config->Write(m_name + KEY_STARNET_PERMANENT2, m_starNet2Permanent); + m_config->Write(m_name + KEY_STARNET_USER_TIMEOUT2, long(m_starNet2UserTimeout)); + m_config->Write(m_name + KEY_STARNET_GROUP_TIMEOUT2, long(m_starNet2GroupTimeout)); + m_config->Write(m_name + KEY_STARNET_CALLSIGN_SWITCH2, long(m_starNet2CallsignSwitch)); + m_config->Write(m_name + KEY_STARNET_TXMSG_SWITCH2, long(m_starNet2TxMsgSwitch)); + m_config->Write(m_name + KEY_STARNET_REFLECTOR2, m_starNet2Reflector); + m_config->Write(m_name + KEY_STARNET_BAND3, m_starNet3Band); + m_config->Write(m_name + KEY_STARNET_CALLSIGN3, m_starNet3Callsign); + m_config->Write(m_name + KEY_STARNET_LOGOFF3, m_starNet3Logoff); + m_config->Write(m_name + KEY_STARNET_INFO3, m_starNet3Info); + m_config->Write(m_name + KEY_STARNET_PERMANENT3, m_starNet3Permanent); + m_config->Write(m_name + KEY_STARNET_USER_TIMEOUT3, long(m_starNet3UserTimeout)); + m_config->Write(m_name + KEY_STARNET_GROUP_TIMEOUT3, long(m_starNet3GroupTimeout)); + m_config->Write(m_name + KEY_STARNET_CALLSIGN_SWITCH3, long(m_starNet3CallsignSwitch)); + m_config->Write(m_name + KEY_STARNET_TXMSG_SWITCH3, long(m_starNet3TxMsgSwitch)); + m_config->Write(m_name + KEY_STARNET_REFLECTOR3, m_starNet3Reflector); + m_config->Write(m_name + KEY_STARNET_BAND4, m_starNet4Band); + m_config->Write(m_name + KEY_STARNET_CALLSIGN4, m_starNet4Callsign); + m_config->Write(m_name + KEY_STARNET_LOGOFF4, m_starNet4Logoff); + m_config->Write(m_name + KEY_STARNET_INFO4, m_starNet4Info); + m_config->Write(m_name + KEY_STARNET_PERMANENT4, m_starNet4Permanent); + m_config->Write(m_name + KEY_STARNET_USER_TIMEOUT4, long(m_starNet4UserTimeout)); + m_config->Write(m_name + KEY_STARNET_GROUP_TIMEOUT4, long(m_starNet4GroupTimeout)); + m_config->Write(m_name + KEY_STARNET_CALLSIGN_SWITCH4, long(m_starNet4CallsignSwitch)); + m_config->Write(m_name + KEY_STARNET_TXMSG_SWITCH4, long(m_starNet4TxMsgSwitch)); + m_config->Write(m_name + KEY_STARNET_REFLECTOR4, m_starNet4Reflector); + m_config->Write(m_name + KEY_STARNET_BAND5, m_starNet5Band); + m_config->Write(m_name + KEY_STARNET_CALLSIGN5, m_starNet5Callsign); + m_config->Write(m_name + KEY_STARNET_LOGOFF5, m_starNet5Logoff); + m_config->Write(m_name + KEY_STARNET_INFO5, m_starNet5Info); + m_config->Write(m_name + KEY_STARNET_PERMANENT5, m_starNet5Permanent); + m_config->Write(m_name + KEY_STARNET_USER_TIMEOUT5, long(m_starNet5UserTimeout)); + m_config->Write(m_name + KEY_STARNET_GROUP_TIMEOUT5, long(m_starNet5GroupTimeout)); + m_config->Write(m_name + KEY_STARNET_CALLSIGN_SWITCH5, long(m_starNet5CallsignSwitch)); + m_config->Write(m_name + KEY_STARNET_TXMSG_SWITCH5, long(m_starNet5TxMsgSwitch)); + m_config->Write(m_name + KEY_STARNET_REFLECTOR5, m_starNet5Reflector); + m_config->Write(m_name + KEY_REMOTE_ENABLED, m_remoteEnabled); + m_config->Write(m_name + KEY_REMOTE_PASSWORD, m_remotePassword); + m_config->Write(m_name + KEY_REMOTE_PORT, long(m_remotePort)); + m_config->Write(m_name + KEY_LANGUAGE, long(m_language)); + m_config->Write(m_name + KEY_INFO_ENABLED, m_infoEnabled); + m_config->Write(m_name + KEY_ECHO_ENABLED, m_echoEnabled); + m_config->Write(m_name + KEY_LOG_ENABLED, m_logEnabled); + m_config->Write(m_name + KEY_DRATS_ENABLED, m_dratsEnabled); + m_config->Write(m_name + KEY_DTMF_ENABLED, m_dtmfEnabled); + m_config->Write(m_name + KEY_WINDOW_X, long(m_x)); + m_config->Write(m_name + KEY_WINDOW_Y, long(m_y)); + m_config->Flush(); +#endif + + wxTextFile file(m_fileName.GetFullPath()); + + bool exists = file.Exists(); + if (exists) { + bool ret = file.Open(); + if (!ret) { + wxLogError(wxT("Cannot open the config file - %s"), m_fileName.GetFullPath().c_str()); + return false; + } + + // Remove the existing file entries + file.Clear(); + } else { + bool ret = file.Create(); + if (!ret) { + wxLogError(wxT("Cannot create the config file - %s"), m_fileName.GetFullPath().c_str()); + return false; + } + } + + wxString buffer; + buffer.Printf(wxT("%s=%d"), KEY_GATEWAY_TYPE.c_str(), int(m_type)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_GATEWAY_CALLSIGN.c_str(), m_callsign.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_GATEWAY_ADDRESS.c_str(), m_address.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_ICOM_ADDRESS.c_str(), m_icomAddress.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_ICOM_PORT.c_str(), m_icomPort); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_HB_ADDRESS.c_str(), m_hbAddress.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_HB_PORT.c_str(), m_hbPort); file.AddLine(buffer); + buffer.Printf(wxT("%s=%.6lf"), KEY_LATITUDE.c_str(), m_latitude); file.AddLine(buffer); + buffer.Printf(wxT("%s=%.6lf"), KEY_LONGITUDE.c_str(), m_longitude); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_DESCRIPTION1.c_str(), m_description1.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_DESCRIPTION2.c_str(), m_description2.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_URL.c_str(), m_url.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_REPEATER_CALL1.c_str(), m_repeater1Callsign.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_REPEATER_BAND1.c_str(), m_repeater1Band.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_REPEATER_TYPE1.c_str(), int(m_repeater1Type)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_REPEATER_ADDRESS1.c_str(), m_repeater1Address.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_REPEATER_PORT1.c_str(), m_repeater1Port); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_REFLECTOR1.c_str(), m_repeater1Reflector.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_ATSTARTUP1.c_str(), m_repeater1AtStartup ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_RECONNECT1.c_str(), int(m_repeater1Reconnect)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%.5lf"), KEY_FREQUENCY1.c_str(), m_repeater1Frequency); file.AddLine(buffer); + buffer.Printf(wxT("%s=%.4lf"), KEY_OFFSET1.c_str(), m_repeater1Offset); file.AddLine(buffer); + buffer.Printf(wxT("%s=%.3lf"), KEY_RANGE1.c_str(), m_repeater1Range); file.AddLine(buffer); + buffer.Printf(wxT("%s=%.6lf"), KEY_LATITUDE1.c_str(), m_repeater1Latitude); file.AddLine(buffer); + buffer.Printf(wxT("%s=%.6lf"), KEY_LONGITUDE1.c_str(), m_repeater1Longitude); file.AddLine(buffer); + buffer.Printf(wxT("%s=%.3lf"), KEY_AGL1.c_str(), m_repeater1Agl); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_DESCRIPTION11.c_str(), m_repeater1Description1.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_DESCRIPTION12.c_str(), m_repeater1Description2.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_URL1.c_str(), m_repeater1URL.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_BAND11.c_str(), m_repeater1Band1); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_BAND12.c_str(), m_repeater1Band2); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_BAND13.c_str(), m_repeater1Band3); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_REPEATER_CALL2.c_str(), m_repeater2Callsign.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_REPEATER_BAND2.c_str(), m_repeater2Band.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_REPEATER_TYPE2.c_str(), int(m_repeater2Type)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_REPEATER_ADDRESS2.c_str(), m_repeater2Address.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_REPEATER_PORT2.c_str(), m_repeater2Port); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_REFLECTOR2.c_str(), m_repeater2Reflector.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_ATSTARTUP2.c_str(), m_repeater2AtStartup ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_RECONNECT2.c_str(), int(m_repeater2Reconnect)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%.5lf"), KEY_FREQUENCY2.c_str(), m_repeater2Frequency); file.AddLine(buffer); + buffer.Printf(wxT("%s=%.4lf"), KEY_OFFSET2.c_str(), m_repeater2Offset); file.AddLine(buffer); + buffer.Printf(wxT("%s=%.3lf"), KEY_RANGE2.c_str(), m_repeater2Range); file.AddLine(buffer); + buffer.Printf(wxT("%s=%.6lf"), KEY_LATITUDE2.c_str(), m_repeater2Latitude); file.AddLine(buffer); + buffer.Printf(wxT("%s=%.6lf"), KEY_LONGITUDE2.c_str(), m_repeater2Longitude); file.AddLine(buffer); + buffer.Printf(wxT("%s=%.3lf"), KEY_AGL2.c_str(), m_repeater2Agl); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_DESCRIPTION21.c_str(), m_repeater2Description1.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_DESCRIPTION22.c_str(), m_repeater2Description2.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_URL2.c_str(), m_repeater2URL.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_BAND21.c_str(), m_repeater2Band1); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_BAND22.c_str(), m_repeater2Band2); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_BAND23.c_str(), m_repeater2Band3); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_REPEATER_CALL3.c_str(), m_repeater3Callsign.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_REPEATER_BAND3.c_str(), m_repeater3Band.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_REPEATER_TYPE3.c_str(), int(m_repeater3Type)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_REPEATER_ADDRESS3.c_str(), m_repeater3Address.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_REPEATER_PORT3.c_str(), m_repeater3Port); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_REFLECTOR3.c_str(), m_repeater3Reflector.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_ATSTARTUP3.c_str(), m_repeater3AtStartup ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_RECONNECT3.c_str(), int(m_repeater3Reconnect)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%.5lf"), KEY_FREQUENCY3.c_str(), m_repeater3Frequency); file.AddLine(buffer); + buffer.Printf(wxT("%s=%.4lf"), KEY_OFFSET3.c_str(), m_repeater3Offset); file.AddLine(buffer); + buffer.Printf(wxT("%s=%.3lf"), KEY_RANGE3.c_str(), m_repeater3Range); file.AddLine(buffer); + buffer.Printf(wxT("%s=%.6lf"), KEY_LATITUDE3.c_str(), m_repeater3Latitude); file.AddLine(buffer); + buffer.Printf(wxT("%s=%.6lf"), KEY_LONGITUDE3.c_str(), m_repeater3Longitude); file.AddLine(buffer); + buffer.Printf(wxT("%s=%.3lf"), KEY_AGL3.c_str(), m_repeater3Agl); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_DESCRIPTION31.c_str(), m_repeater3Description1.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_DESCRIPTION32.c_str(), m_repeater3Description2.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_URL3.c_str(), m_repeater3URL.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_BAND31.c_str(), m_repeater3Band1); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_BAND32.c_str(), m_repeater3Band2); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_BAND33.c_str(), m_repeater3Band3); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_REPEATER_CALL4.c_str(), m_repeater4Callsign.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_REPEATER_BAND4.c_str(), m_repeater4Band.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_REPEATER_TYPE4.c_str(), int(m_repeater4Type)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_REPEATER_ADDRESS4.c_str(), m_repeater4Address.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_REPEATER_PORT4.c_str(), m_repeater4Port); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_REFLECTOR4.c_str(), m_repeater4Reflector.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_ATSTARTUP4.c_str(), m_repeater4AtStartup ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_RECONNECT4.c_str(), int(m_repeater4Reconnect)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%.5lf"), KEY_FREQUENCY4.c_str(), m_repeater4Frequency); file.AddLine(buffer); + buffer.Printf(wxT("%s=%.4lf"), KEY_OFFSET4.c_str(), m_repeater4Offset); file.AddLine(buffer); + buffer.Printf(wxT("%s=%.3lf"), KEY_RANGE4.c_str(), m_repeater4Range); file.AddLine(buffer); + buffer.Printf(wxT("%s=%.6lf"), KEY_LATITUDE4.c_str(), m_repeater4Latitude); file.AddLine(buffer); + buffer.Printf(wxT("%s=%.6lf"), KEY_LONGITUDE4.c_str(), m_repeater4Longitude); file.AddLine(buffer); + buffer.Printf(wxT("%s=%.3lf"), KEY_AGL4.c_str(), m_repeater4Agl); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_DESCRIPTION41.c_str(), m_repeater4Description1.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_DESCRIPTION42.c_str(), m_repeater4Description2.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_URL4.c_str(), m_repeater4URL.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_BAND41.c_str(), m_repeater4Band1); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_BAND42.c_str(), m_repeater4Band2); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_BAND43.c_str(), m_repeater4Band3); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_IRCDDB_ENABLED.c_str(), m_ircddbEnabled ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_IRCDDB_HOSTNAME.c_str(), m_ircddbHostname.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_IRCDDB_USERNAME.c_str(), m_ircddbUsername.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_IRCDDB_PASSWORD.c_str(), m_ircddbPassword.c_str()); file.AddLine(buffer); + buffer.Printf("%s=%d", KEY_IRCDDB_ENABLED2.c_str(), m_ircddbEnabled2 ? 1 : 0); file.AddLine(buffer); + buffer.Printf("%s=%s", KEY_IRCDDB_HOSTNAME2.c_str(), m_ircddbHostname2.c_str()); file.AddLine(buffer); + buffer.Printf("%s=%s", KEY_IRCDDB_USERNAME2.c_str(), m_ircddbUsername2.c_str()); file.AddLine(buffer); + buffer.Printf("%s=%s", KEY_IRCDDB_PASSWORD2.c_str(), m_ircddbPassword2.c_str()); file.AddLine(buffer); + buffer.Printf("%s=%d", KEY_IRCDDB_ENABLED3.c_str(), m_ircddbEnabled3 ? 1 : 0); file.AddLine(buffer); + buffer.Printf("%s=%s", KEY_IRCDDB_HOSTNAME3.c_str(), m_ircddbHostname3.c_str()); file.AddLine(buffer); + buffer.Printf("%s=%s", KEY_IRCDDB_USERNAME3.c_str(), m_ircddbUsername3.c_str()); file.AddLine(buffer); + buffer.Printf("%s=%s", KEY_IRCDDB_PASSWORD3.c_str(), m_ircddbPassword3.c_str()); file.AddLine(buffer); + buffer.Printf("%s=%d", KEY_IRCDDB_ENABLED4.c_str(), m_ircddbEnabled4 ? 1 : 0); file.AddLine(buffer); + buffer.Printf("%s=%s", KEY_IRCDDB_HOSTNAME4.c_str(), m_ircddbHostname4.c_str()); file.AddLine(buffer); + buffer.Printf("%s=%s", KEY_IRCDDB_USERNAME4.c_str(), m_ircddbUsername4.c_str()); file.AddLine(buffer); + buffer.Printf("%s=%s", KEY_IRCDDB_PASSWORD4.c_str(), m_ircddbPassword4.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_APRS_ENABLED.c_str(), m_aprsEnabled ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_APRS_HOSTNAME.c_str(), m_aprsHostname.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_APRS_PORT.c_str(), m_aprsPort); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_DEXTRA_ENABLED.c_str(), m_dextraEnabled ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_DEXTRA_MAXDONGLES.c_str(), m_dextraMaxDongles); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_DPLUS_ENABLED.c_str(), m_dplusEnabled ? 1 : 0); file.AddLine(buffer); + 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); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_BAND1.c_str(), m_starNet1Band.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_CALLSIGN1.c_str(), m_starNet1Callsign.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_LOGOFF1.c_str(), m_starNet1Logoff.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_INFO1.c_str(), m_starNet1Info.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_PERMANENT1.c_str(), m_starNet1Permanent.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_USER_TIMEOUT1.c_str(), m_starNet1UserTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_GROUP_TIMEOUT1.c_str(), m_starNet1GroupTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_CALLSIGN_SWITCH1.c_str(), int(m_starNet1CallsignSwitch)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_TXMSG_SWITCH1.c_str(), m_starNet1TxMsgSwitch ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_REFLECTOR1.c_str(), m_starNet1Reflector.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_BAND2.c_str(), m_starNet2Band.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_CALLSIGN2.c_str(), m_starNet2Callsign.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_LOGOFF2.c_str(), m_starNet2Logoff.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_INFO2.c_str(), m_starNet2Info.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_PERMANENT2.c_str(), m_starNet2Permanent.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_USER_TIMEOUT2.c_str(), m_starNet2UserTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_GROUP_TIMEOUT2.c_str(), m_starNet2GroupTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_CALLSIGN_SWITCH2.c_str(), int(m_starNet2CallsignSwitch)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_TXMSG_SWITCH2.c_str(), m_starNet2TxMsgSwitch ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_REFLECTOR2.c_str(), m_starNet2Reflector.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_BAND3.c_str(), m_starNet3Band.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_CALLSIGN3.c_str(), m_starNet3Callsign.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_LOGOFF3.c_str(), m_starNet3Logoff.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_INFO3.c_str(), m_starNet3Info.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_PERMANENT3.c_str(), m_starNet3Permanent.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_USER_TIMEOUT3.c_str(), m_starNet3UserTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_GROUP_TIMEOUT3.c_str(), m_starNet3GroupTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_CALLSIGN_SWITCH3.c_str(), int(m_starNet3CallsignSwitch)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_TXMSG_SWITCH3.c_str(), m_starNet3TxMsgSwitch ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_REFLECTOR3.c_str(), m_starNet3Reflector.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_BAND4.c_str(), m_starNet4Band.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_CALLSIGN4.c_str(), m_starNet4Callsign.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_LOGOFF4.c_str(), m_starNet4Logoff.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_INFO4.c_str(), m_starNet4Info.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_PERMANENT4.c_str(), m_starNet4Permanent.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_USER_TIMEOUT4.c_str(), m_starNet4UserTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_GROUP_TIMEOUT4.c_str(), m_starNet4GroupTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_CALLSIGN_SWITCH4.c_str(), int(m_starNet4CallsignSwitch)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_TXMSG_SWITCH4.c_str(), m_starNet4TxMsgSwitch ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_REFLECTOR4.c_str(), m_starNet4Reflector.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_BAND5.c_str(), m_starNet5Band.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_CALLSIGN5.c_str(), m_starNet5Callsign.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_LOGOFF5.c_str(), m_starNet5Logoff.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_INFO5.c_str(), m_starNet5Info.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_PERMANENT5.c_str(), m_starNet5Permanent.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_USER_TIMEOUT5.c_str(), m_starNet5UserTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_GROUP_TIMEOUT5.c_str(), m_starNet5GroupTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_CALLSIGN_SWITCH5.c_str(), int(m_starNet5CallsignSwitch)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_TXMSG_SWITCH5.c_str(), m_starNet5TxMsgSwitch ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_REFLECTOR5.c_str(), m_starNet5Reflector.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_REMOTE_ENABLED.c_str(), m_remoteEnabled ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_REMOTE_PASSWORD.c_str(), m_remotePassword.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_REMOTE_PORT.c_str(), m_remotePort); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_LANGUAGE.c_str(), int(m_language)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_INFO_ENABLED.c_str(), m_infoEnabled ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_ECHO_ENABLED.c_str(), m_echoEnabled ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_LOG_ENABLED.c_str(), m_logEnabled ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_DRATS_ENABLED.c_str(), m_dratsEnabled ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_DTMF_ENABLED.c_str(), m_dtmfEnabled ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_WINDOW_X.c_str(), m_x); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_WINDOW_Y.c_str(), m_y); file.AddLine(buffer); + + bool ret = file.Write(); + if (!ret) { + file.Close(); + wxLogError(wxT("Cannot write the config file - %s"), m_fileName.GetFullPath().c_str()); + return false; + } + + file.Close(); + + return true; +} diff --git a/Common/IRCDDBGatewayConfig.h b/Common/IRCDDBGatewayConfig.h new file mode 100644 index 0000000..f1c8ca2 --- /dev/null +++ b/Common/IRCDDBGatewayConfig.h @@ -0,0 +1,311 @@ +/* + * 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 + * 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 IRCDDBGatewayConfig_H +#define IRCDDBGatewayConfig_H + +#include "Defs.h" + +#include +#include +#include + +class CIRCDDBGatewayConfig { +public: +#if defined(__WINDOWS__) + CIRCDDBGatewayConfig(wxConfigBase* config, const wxString& dir, const wxString& configName, const wxString& name); +#else + CIRCDDBGatewayConfig(const wxString& dir, const wxString& configName, const wxString& name); +#endif + ~CIRCDDBGatewayConfig(); + + void getGateway(GATEWAY_TYPE& type, wxString& callsign, wxString& address, wxString& icomAddress, unsigned int& icomPort, wxString& hbAddress, unsigned int& hbPort, double& latitude, double& longitude, wxString& description1, wxString& description2, wxString& url) const; + void setGateway(GATEWAY_TYPE type, const wxString& callsign, const wxString& address, const wxString& icomAddress, unsigned int icomPort, const wxString& hbAddress, unsigned int hbPort, double latitude, double longitude, const wxString& description1, const wxString& description2, const wxString& url); + + void getRepeater1(wxString& callsign, wxString& band, HW_TYPE& type, wxString& address, unsigned int& port, unsigned char& band1, unsigned char& band2, unsigned char& band3, wxString& reflector, bool& atStartup, RECONNECT& reconnect, double& frequency, double& offset, double& range, double& latitude, double& longitude, double& agl, wxString& description1, wxString& description2, wxString& url) const; + void setRepeater1(const wxString& band, HW_TYPE type, const wxString& address, unsigned int port, unsigned char band1, unsigned char band2, unsigned char band3, const wxString& reflector, bool atStartup, RECONNECT reconnect, double frequency, double offset, double range, double latitude, double longitude, double agl, const wxString& description1, const wxString& description2, const wxString& url); + + void getRepeater2(wxString& callsign, wxString& band, HW_TYPE& type, wxString& address, unsigned int& port, unsigned char& band1, unsigned char& band2, unsigned char& band3, wxString& reflector, bool& atStartup, RECONNECT& reconnect, double& frequency, double& offset, double& range, double& latitude, double& longitude, double& agl, wxString& description1, wxString& description2, wxString& url) const; + void setRepeater2(const wxString& band, HW_TYPE type, const wxString& address, unsigned int port, unsigned char band1, unsigned char band2, unsigned char band3, const wxString& reflector, bool atStartup, RECONNECT reconnect, double frequency, double offset, double range, double latitude, double longitude, double agl, const wxString& description1, const wxString& description2, const wxString& url); + + void getRepeater3(wxString& callsign, wxString& band, HW_TYPE& type, wxString& address, unsigned int& port, unsigned char& band1, unsigned char& band2, unsigned char& band3, wxString& reflector, bool& atStartup, RECONNECT& reconnect, double& frequency, double& offset, double& range, double& latitude, double& longitude, double& agl, wxString& description1, wxString& description2, wxString& url) const; + void setRepeater3(const wxString& band, HW_TYPE type, const wxString& address, unsigned int port, unsigned char band1, unsigned char band2, unsigned char band3, const wxString& reflector, bool atStartup, RECONNECT reconnect, double frequency, double offset, double range, double latitude, double longitude, double agl, const wxString& description1, const wxString& description2, const wxString& url); + + void getRepeater4(wxString& callsign, wxString& band, HW_TYPE& type, wxString& address, unsigned int& port, unsigned char& band1, unsigned char& band2, unsigned char& band3, wxString& reflector, bool& atStartup, RECONNECT& reconnect, double& frequency, double& offset, double& range, double& latitude, double& longitude, double& agl, wxString& description1, wxString& description2, wxString& url) const; + void setRepeater4(const wxString& band, HW_TYPE type, const wxString& address, unsigned int port, unsigned char band1, unsigned char band2, unsigned char band3, const wxString& reflector, bool atStartup, RECONNECT reconnect, double frequency, double offset, double range, double latitude, double longitude, double agl, const wxString& description1, const wxString& description2, const wxString& url); + + void getIrcDDB(bool& enabled, wxString& hostname, wxString& username, wxString& password) const; + void getIrcDDB2(bool& enabled, wxString& hostname, wxString& username, wxString& password) const; + void getIrcDDB3(bool& enabled, wxString& hostname, wxString& username, wxString& password) const; + void getIrcDDB4(bool& enabled, wxString& hostname, wxString& username, wxString& password) const; + + void setIrcDDB(bool enabled, const wxString& hostname, const wxString& username, const wxString& password); + void setIrcDDB2(bool enabled, const wxString& hostname, const wxString& username, const wxString& password); + void setIrcDDB3(bool enabled, const wxString& hostname, const wxString& username, const wxString& password); + void setIrcDDB4(bool enabled, const wxString& hostname, const wxString& username, const wxString& password); + + void getDPRS(bool& enabled, wxString& hostname, unsigned int& port) const; + void setDPRS(bool enabled, const wxString& hostname, unsigned int port); + + void getDExtra(bool& enabled, unsigned int& maxDongles) const; + void setDExtra(bool enabled, unsigned int maxDongles); + + void getDPlus(bool& enabled, unsigned int& maxDongles, wxString& login) const; + void setDPlus(bool enabled, unsigned int maxDongles, const wxString& login); + + 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); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + void getStarNet1(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + void setStarNet1(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); + + void getStarNet2(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + void setStarNet2(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); + + void getStarNet3(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + void setStarNet3(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); + + void getStarNet4(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + void setStarNet4(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); + + void getStarNet5(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + void setStarNet5(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); +#else + void getStarNet1(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + void setStarNet1(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); + + void getStarNet2(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + void setStarNet2(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); + + void getStarNet3(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + void setStarNet3(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); + + void getStarNet4(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + void setStarNet4(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); + + void getStarNet5(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + void setStarNet5(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); +#endif + + void getRemote(bool& enabled, wxString& password, unsigned int& port) const; + void setRemote(bool enabled, const wxString& password, unsigned int port); + + void getMiscellaneous(TEXT_LANG& language, bool& infoEnabled, bool& echoEnabled, bool& logEnabled, bool& dratsEnabled, bool& dtmfEnabled) const; + void setMiscellaneous(TEXT_LANG language, bool infoEnabled, bool echoEnabled, bool logEnabled, bool dratsEnabled, bool dtmfEnabled); + + void getPosition(int& x, int& y) const; + void setPosition(int x, int y); + + bool write(); + +private: +#if defined(__WINDOWS__) + wxConfigBase* m_config; + wxString m_name; +#endif + wxFileName m_fileName; + GATEWAY_TYPE m_type; + wxString m_callsign; + wxString m_address; + wxString m_icomAddress; + unsigned int m_icomPort; + wxString m_hbAddress; + unsigned int m_hbPort; + double m_latitude; + double m_longitude; + wxString m_description1; + wxString m_description2; + wxString m_url; + wxString m_repeater1Callsign; + wxString m_repeater1Band; + HW_TYPE m_repeater1Type; + wxString m_repeater1Address; + unsigned int m_repeater1Port; + wxString m_repeater1Reflector; + bool m_repeater1AtStartup; + RECONNECT m_repeater1Reconnect; + double m_repeater1Frequency; + double m_repeater1Offset; + double m_repeater1Range; + double m_repeater1Latitude; + double m_repeater1Longitude; + double m_repeater1Agl; + wxString m_repeater1Description1; + wxString m_repeater1Description2; + wxString m_repeater1URL; + unsigned char m_repeater1Band1; + unsigned char m_repeater1Band2; + unsigned char m_repeater1Band3; + wxString m_repeater2Callsign; + wxString m_repeater2Band; + HW_TYPE m_repeater2Type; + wxString m_repeater2Address; + unsigned int m_repeater2Port; + wxString m_repeater2Reflector; + bool m_repeater2AtStartup; + RECONNECT m_repeater2Reconnect; + double m_repeater2Frequency; + double m_repeater2Offset; + double m_repeater2Range; + double m_repeater2Latitude; + double m_repeater2Longitude; + double m_repeater2Agl; + wxString m_repeater2Description1; + wxString m_repeater2Description2; + wxString m_repeater2URL; + unsigned char m_repeater2Band1; + unsigned char m_repeater2Band2; + unsigned char m_repeater2Band3; + wxString m_repeater3Callsign; + wxString m_repeater3Band; + HW_TYPE m_repeater3Type; + wxString m_repeater3Address; + unsigned int m_repeater3Port; + wxString m_repeater3Reflector; + bool m_repeater3AtStartup; + RECONNECT m_repeater3Reconnect; + double m_repeater3Frequency; + double m_repeater3Offset; + double m_repeater3Range; + double m_repeater3Latitude; + double m_repeater3Longitude; + double m_repeater3Agl; + wxString m_repeater3Description1; + wxString m_repeater3Description2; + wxString m_repeater3URL; + unsigned char m_repeater3Band1; + unsigned char m_repeater3Band2; + unsigned char m_repeater3Band3; + wxString m_repeater4Callsign; + wxString m_repeater4Band; + HW_TYPE m_repeater4Type; + wxString m_repeater4Address; + unsigned int m_repeater4Port; + wxString m_repeater4Reflector; + bool m_repeater4AtStartup; + RECONNECT m_repeater4Reconnect; + double m_repeater4Frequency; + double m_repeater4Offset; + double m_repeater4Range; + double m_repeater4Latitude; + double m_repeater4Longitude; + double m_repeater4Agl; + wxString m_repeater4Description1; + wxString m_repeater4Description2; + wxString m_repeater4URL; + unsigned char m_repeater4Band1; + unsigned char m_repeater4Band2; + unsigned char m_repeater4Band3; + bool m_ircddbEnabled; + wxString m_ircddbHostname; + wxString m_ircddbUsername; + wxString m_ircddbPassword; + bool m_ircddbEnabled2; + wxString m_ircddbHostname2; + wxString m_ircddbUsername2; + wxString m_ircddbPassword2; + bool m_ircddbEnabled3; + wxString m_ircddbHostname3; + wxString m_ircddbUsername3; + wxString m_ircddbPassword3; + bool m_ircddbEnabled4; + wxString m_ircddbHostname4; + wxString m_ircddbUsername4; + wxString m_ircddbPassword4; + bool m_aprsEnabled; + wxString m_aprsHostname; + unsigned int m_aprsPort; + bool m_dextraEnabled; + unsigned int m_dextraMaxDongles; + bool m_dplusEnabled; + 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; + wxString m_starNet1Band; + wxString m_starNet1Callsign; + wxString m_starNet1Logoff; + wxString m_starNet1Info; + wxString m_starNet1Permanent; + unsigned int m_starNet1UserTimeout; + unsigned int m_starNet1GroupTimeout; + STARNET_CALLSIGN_SWITCH m_starNet1CallsignSwitch; + bool m_starNet1TxMsgSwitch; + wxString m_starNet1Reflector; + wxString m_starNet2Band; + wxString m_starNet2Callsign; + wxString m_starNet2Logoff; + wxString m_starNet2Info; + wxString m_starNet2Permanent; + unsigned int m_starNet2UserTimeout; + unsigned int m_starNet2GroupTimeout; + STARNET_CALLSIGN_SWITCH m_starNet2CallsignSwitch; + bool m_starNet2TxMsgSwitch; + wxString m_starNet2Reflector; + wxString m_starNet3Band; + wxString m_starNet3Callsign; + wxString m_starNet3Logoff; + wxString m_starNet3Info; + wxString m_starNet3Permanent; + unsigned int m_starNet3UserTimeout; + unsigned int m_starNet3GroupTimeout; + STARNET_CALLSIGN_SWITCH m_starNet3CallsignSwitch; + bool m_starNet3TxMsgSwitch; + wxString m_starNet3Reflector; + wxString m_starNet4Band; + wxString m_starNet4Callsign; + wxString m_starNet4Logoff; + wxString m_starNet4Info; + wxString m_starNet4Permanent; + unsigned int m_starNet4UserTimeout; + unsigned int m_starNet4GroupTimeout; + STARNET_CALLSIGN_SWITCH m_starNet4CallsignSwitch; + bool m_starNet4TxMsgSwitch; + wxString m_starNet4Reflector; + wxString m_starNet5Band; + wxString m_starNet5Callsign; + wxString m_starNet5Logoff; + wxString m_starNet5Info; + wxString m_starNet5Permanent; + unsigned int m_starNet5UserTimeout; + unsigned int m_starNet5GroupTimeout; + STARNET_CALLSIGN_SWITCH m_starNet5CallsignSwitch; + bool m_starNet5TxMsgSwitch; + wxString m_starNet5Reflector; + bool m_remoteEnabled; + wxString m_remotePassword; + unsigned int m_remotePort; + TEXT_LANG m_language; + bool m_infoEnabled; + bool m_echoEnabled; + bool m_logEnabled; + bool m_dratsEnabled; + bool m_dtmfEnabled; + int m_x; + int m_y; +}; + +#endif diff --git a/Common/IcomRepeaterProtocolHandler.cpp b/Common/IcomRepeaterProtocolHandler.cpp new file mode 100644 index 0000000..61e6417 --- /dev/null +++ b/Common/IcomRepeaterProtocolHandler.cpp @@ -0,0 +1,650 @@ +/* + * 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 + * 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 "IcomRepeaterProtocolHandler.h" + +#include + +#include "CCITTChecksum.h" +#include "DStarDefines.h" +#include "Utils.h" + +// Allow space for a big DD packet +const unsigned int BUFFER_LENGTH = 2500U; + +const unsigned int QUEUE_LENGTH = 50U; + +const unsigned int LOOP_DELAY = 5UL; +const unsigned int LOOP_TICKS = 200U; + +CIcomRepeaterProtocolHandler::CIcomRepeaterProtocolHandler(const wxString& address, unsigned int port, const wxString& icomAddress, unsigned int icomPort) : +wxThread(wxTHREAD_JOINABLE), +m_socket(address, port), +m_icomAddress(), +m_icomPort(icomPort), +m_over1(false), +m_seqNo(0U), +m_tries(0U), +m_ackQueue(NULL), +m_killed(false), +m_type(RT_NONE), +m_buffer(NULL), +m_rptrQueue(QUEUE_LENGTH), +m_gwyQueue(QUEUE_LENGTH), +m_retryTimer(LOOP_TICKS, 0U, 200U) // 200ms +{ + wxASSERT(!icomAddress.IsEmpty()); + wxASSERT(!address.IsEmpty()); + wxASSERT(icomPort > 0U); + wxASSERT(port > 0U); + + m_icomAddress.s_addr = ::inet_addr(icomAddress.mb_str()); + + m_buffer = new unsigned char[BUFFER_LENGTH]; + + wxDateTime now = wxDateTime::UNow(); + ::srand(now.GetMillisecond()); +} + +CIcomRepeaterProtocolHandler::~CIcomRepeaterProtocolHandler() +{ + while (!m_gwyQueue.isEmpty()) { + CDataQueue* dq = m_gwyQueue.getData(); + free(dq); + } + + while (!m_rptrQueue.isEmpty()) { + CDataQueue* dq = m_rptrQueue.getData(); + free(dq); + } + + delete[] m_buffer; +} + +void CIcomRepeaterProtocolHandler::setCount(unsigned int count) +{ + m_over1 = count > 1U; +} + +bool CIcomRepeaterProtocolHandler::open() +{ + bool ret = m_socket.open(); + if (!ret) + return false; + + unsigned char buffer[10U]; + + buffer[0U] = 'I'; + buffer[1U] = 'N'; + buffer[2U] = 'I'; + buffer[3U] = 'T'; + buffer[4U] = 0x00; + buffer[5U] = 0x00; + buffer[6U] = 0x73; + buffer[7U] = 0x00; + buffer[8U] = 0x00; + buffer[9U] = 0x00; + + ret = m_socket.write(buffer, 10U, m_icomAddress, m_icomPort); + if (!ret) { + m_socket.close(); + return false; + } + + // Wait for a reply from the RP2C + for (unsigned int i = 0U; i < 10U; i++) { + in_addr address; + unsigned int port; + int length = m_socket.read(m_buffer, BUFFER_LENGTH, address, port); + + if (length == 10 && m_buffer[0U] == 'I' && m_buffer[1U] == 'N' && m_buffer[2U] == 'I' && m_buffer[3U] == 'T' && m_buffer[6U] == 0x72 && m_buffer[7U] == 0x00) { + m_seqNo = m_buffer[4U] * 256U + m_buffer[5U] + 1U; + wxLogMessage(wxT("Initial sequence number from the RP2C is %u"), m_seqNo); + + // Start the thread + Create(); + Run(); + + return true; + } + + ::wxSleep(1); + } + + m_socket.close(); + + wxLogError(wxT("No reply from the RP2C for 10 seconds, aborting")); + + return false; +} + +void* CIcomRepeaterProtocolHandler::Entry() +{ + wxLogMessage(wxT("Starting the Icom Controller thread")); + + try { + while (!m_killed) { + sendGwyPackets(); + + Sleep(LOOP_DELAY); + + readIcomPackets(); + + m_retryTimer.clock(); + } + } + catch (std::exception& e) { + wxString message(e.what(), wxConvLocal); + wxLogError(wxT("Exception raised in the Icom Controller thread - \"%s\""), message.c_str()); + } + catch (...) { + wxLogError(wxT("Unknown exception raised in the Icom Controller thread")); + } + + wxLogMessage(wxT("Stopping the Icom Controller thread")); + + m_socket.close(); + + return NULL; +} + +bool CIcomRepeaterProtocolHandler::writeHeader(CHeaderData& header) +{ + CDataQueue* dq = new CDataQueue(new CHeaderData(header)); + + m_gwyQueue.addData(dq); + + return true; +} + +bool CIcomRepeaterProtocolHandler::writeAMBE(CAMBEData& data) +{ + CDataQueue* dq = new CDataQueue(new CAMBEData(data)); + + m_gwyQueue.addData(dq); + + return true; +} + +bool CIcomRepeaterProtocolHandler::writeDD(CDDData& data) +{ + CDataQueue* dq = new CDataQueue(new CDDData(data)); + + m_gwyQueue.addData(dq); + + return true; +} + +bool CIcomRepeaterProtocolHandler::writeText(CTextData&) +{ + return true; +} + +bool CIcomRepeaterProtocolHandler::writeStatus(CStatusData&) +{ + return true; +} + +void CIcomRepeaterProtocolHandler::readIcomPackets() +{ + for (;;) { + // No more data? + in_addr address; + unsigned int port; + int length = m_socket.read(m_buffer, BUFFER_LENGTH, address, port); + if (length <= 0) + return; + + if (address.s_addr != m_icomAddress.s_addr || port != m_icomPort) { + wxLogError(wxT("Incoming Icom data from an unknown source")); + continue; + } + + // Invalid packet type? + if (m_buffer[0] != 'D' || m_buffer[1] != 'S' || m_buffer[2] != 'T' || m_buffer[3] != 'R') { + CUtils::dump(wxT("Missing DSTR from the RP2C"), m_buffer, length); + continue; + } + + // An ack + if (length == 10 && m_buffer[6] == 0x72) { + wxUint16 seqNo = m_buffer[4] * 256U + m_buffer[5]; + + if (seqNo == m_seqNo) + m_seqNo++; + else + m_seqNo = seqNo; + + // Free the ack queue + free(m_ackQueue); + m_ackQueue = NULL; + + m_retryTimer.stop(); + m_tries = 0U; + + continue; + } + + // Send an ack for all packet types except an ack + wxUint16 seqNo = m_buffer[4] * 256U + m_buffer[5]; + sendAck(seqNo); + + // Heard data + if (m_buffer[6] == 0x73 && m_buffer[7] == 0x21) { + CHeardData* heard = new CHeardData; + bool ret = heard->setIcomRepeaterData(m_buffer, length, m_icomAddress, m_icomPort); + if (!ret) { + wxLogError(wxT("Invalid heard data from the RP2C")); + delete heard; + continue; + } + + m_rptrQueue.addData(new CDataQueue(heard)); + continue; + } + + // Poll data + if (m_buffer[6] == 0x73 && m_buffer[7] == 0x00) { + m_rptrQueue.addData(new CDataQueue); + continue; + } + + // DD Data + if (m_buffer[6] == 0x73 && m_buffer[7] == 0x11) { + CDDData* data = new CDDData; + bool ret = data->setIcomRepeaterData(m_buffer, length, m_icomAddress, m_icomPort); + if (!ret) { + wxLogError(wxT("Invalid DD data from the RP2C")); + delete data; + continue; + } + + m_rptrQueue.addData(new CDataQueue(data)); + continue; + } + + // DV data + if (m_buffer[6] == 0x73 && m_buffer[7] == 0x12 && m_buffer[10] == 0x20) { + if ((m_buffer[16] & 0x80) == 0x80) { + CHeaderData* header = new CHeaderData; + bool ret = header->setIcomRepeaterData(m_buffer, length, true, m_icomAddress, m_icomPort); + if (!ret) { + wxLogError(wxT("Invalid header data or checksum from the RP2C")); + delete header; + continue; + } + + if (m_over1) + sendMultiReply(*header); + else + sendSingleReply(*header); + + m_rptrQueue.addData(new CDataQueue(header)); + continue; + } else { + CAMBEData* data = new CAMBEData; + bool ret = data->setIcomRepeaterData(m_buffer, length, m_icomAddress, m_icomPort); + if (!ret) { + wxLogError(wxT("Invalid AMBE data from the RP2C")); + delete data; + continue; + } + + m_rptrQueue.addData(new CDataQueue(data)); + continue; + } + } + + // An unknown type + CUtils::dump(wxT("Unknown packet type from the RP2C"), m_buffer, length); + } +} + +void CIcomRepeaterProtocolHandler::sendGwyPackets() +{ + // Anything to send + if (m_gwyQueue.isEmpty() && m_ackQueue == NULL) + return; + + if (m_tries > 0U && !m_retryTimer.hasExpired()) + return; + + if (m_ackQueue == NULL) { + m_ackQueue = m_gwyQueue.getData(); + if (m_ackQueue == NULL) { + wxLogError(wxT("getData of a non-empty gateway queue is NULL")); + return; + } + } + + unsigned int length = 0U; + + switch (m_ackQueue->getType()) { + case RT_HEADER: { + CHeaderData* header = m_ackQueue->getHeader(); + header->setRptSeq(m_seqNo); + length = header->getIcomRepeaterData(m_buffer, 60U, true); + } + break; + + case RT_AMBE: { + CAMBEData* data = m_ackQueue->getAMBE(); + data->setRptSeq(m_seqNo); + length = data->getIcomRepeaterData(m_buffer, 60U); + } + break; + + case RT_DD: { + CDDData* data = m_ackQueue->getDD(); + data->setRptSeq(m_seqNo); + length = data->getIcomRepeaterData(m_buffer, BUFFER_LENGTH); + } + break; + + default: + wxLogError(wxT("Invalid type in the gateway queue")); + break; + } + + if (length > 0U) { + m_socket.write(m_buffer, length, m_icomAddress, m_icomPort); + + m_tries++; + m_retryTimer.start(); + + if (m_tries > 0U && (m_tries % 100U) == 0U) + wxLogMessage(wxT("No reply from the RP2C after %u retries"), m_tries); + } +} + +REPEATER_TYPE CIcomRepeaterProtocolHandler::read() +{ + if (m_rptrQueue.isEmpty()) { + m_type = RT_NONE; + } else { + CDataQueue* dq = m_rptrQueue.peek(); + if (dq == NULL) { + wxLogError(wxT("Peek of a non-empty repeater queue is NULL")); + m_type = RT_NONE; + } else { + m_type = dq->getType(); + } + } + + return m_type; +} + +CPollData* CIcomRepeaterProtocolHandler::readPoll() +{ + if (m_type != RT_POLL) + return NULL; + + CDataQueue* dq = m_rptrQueue.getData(); + if (dq == NULL) { + wxLogError(wxT("Missing DataQueue in readPoll")); + return NULL; + } + + if (dq->getType() != RT_POLL) { + wxLogError(wxT("Wrong DataQueue type in readPoll")); + delete dq; + return NULL; + } + + delete dq; + + CPollData* data = new CPollData; + data->setData1(wxT("icom_rp2c")); + + return data; +} + +CHeaderData* CIcomRepeaterProtocolHandler::readHeader() +{ + if (m_type != RT_HEADER) + return NULL; + + CDataQueue* dq = m_rptrQueue.getData(); + if (dq == NULL) { + wxLogError(wxT("Missing DataQueue in readHeader")); + return NULL; + } + + if (dq->getType() != RT_HEADER) { + wxLogError(wxT("Wrong DataQueue type in readHeader")); + delete dq; + return NULL; + } + + CHeaderData* header = dq->getHeader(); + + delete dq; + + return header; +} + +CAMBEData* CIcomRepeaterProtocolHandler::readAMBE() +{ + if (m_type != RT_AMBE) + return NULL; + + CDataQueue* dq = m_rptrQueue.getData(); + if (dq == NULL) { + wxLogError(wxT("Missing DataQueue in readData")); + return NULL; + } + + if (dq->getType() != RT_AMBE) { + wxLogError(wxT("Wrong DataQueue type in readData")); + delete dq; + return NULL; + } + + CAMBEData* data = dq->getAMBE(); + + delete dq; + + return data; +} + +CHeardData* CIcomRepeaterProtocolHandler::readHeard() +{ + if (m_type != RT_HEARD) + return NULL; + + CDataQueue* dq = m_rptrQueue.getData(); + if (dq == NULL) { + wxLogError(wxT("Missing DataQueue in readHeard")); + return NULL; + } + + if (dq->getType() != RT_HEARD) { + wxLogError(wxT("Wrong DataQueue type in readHeard")); + delete dq; + return NULL; + } + + CHeardData* heard = dq->getHeard(); + + delete dq; + + return heard; +} + +CDDData* CIcomRepeaterProtocolHandler::readDD() +{ + if (m_type != RT_DD) + return NULL; + + CDataQueue* dq = m_rptrQueue.getData(); + if (dq == NULL) { + wxLogError(wxT("Missing DataQueue in readData")); + return NULL; + } + + if (dq->getType() != RT_DD) { + wxLogError(wxT("Wrong DataQueue type in readData")); + delete dq; + return NULL; + } + + CDDData* data = dq->getDD(); + + delete dq; + + return data; +} + +CHeaderData* CIcomRepeaterProtocolHandler::readBusyHeader() +{ + return NULL; +} + +CAMBEData* CIcomRepeaterProtocolHandler::readBusyAMBE() +{ + return NULL; +} + +void CIcomRepeaterProtocolHandler::close() +{ + m_killed = true; + + Wait(); +} + +bool CIcomRepeaterProtocolHandler::sendAck(wxUint16 seqNo) +{ + unsigned char buffer[10U]; + + buffer[0U] = 'D'; + buffer[1U] = 'S'; + buffer[2U] = 'T'; + buffer[3U] = 'R'; + + buffer[4U] = seqNo / 256U; + buffer[5U] = seqNo % 256U; + + buffer[6U] = 0x72U; + buffer[7U] = 0x00U; + + buffer[8U] = 0x00U; + buffer[9U] = 0x00U; + + return m_socket.write(buffer, 10U, m_icomAddress, m_icomPort); +} + +void CIcomRepeaterProtocolHandler::sendSingleReply(const CHeaderData& header) +{ + unsigned int id = ::rand() % 0xFFFF; + + CHeaderData replyHdr; + replyHdr.setId(id); + replyHdr.setBand1(0xFFU); + replyHdr.setBand2(0xFFU); + replyHdr.setBand3(0xFFU); + replyHdr.setFlag1(0x01U); + replyHdr.setMyCall1(header.getRptCall2()); + replyHdr.setMyCall2(wxT(" ")); + replyHdr.setYourCall(header.getMyCall1()); + replyHdr.setRptCall1(header.getRptCall2()); + replyHdr.setRptCall2(header.getRptCall1()); + + writeHeader(replyHdr); + + unsigned char buffer[DV_FRAME_MAX_LENGTH_BYTES]; + ::memcpy(buffer + 0U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, END_PATTERN_BYTES, END_PATTERN_LENGTH_BYTES); + + CAMBEData replyData; + replyData.setId(id); + replyData.setBand1(0xFFU); + replyData.setBand2(0xFFU); + replyData.setBand3(0xFFU); + replyData.setSeq(0x40U); // Seq = 0 and end-of-data + replyData.setData(buffer, DV_FRAME_MAX_LENGTH_BYTES); + + writeAMBE(replyData); +} + +void CIcomRepeaterProtocolHandler::sendMultiReply(const CHeaderData& header) +{ + unsigned int id = ::rand() % 0xFFFF; + + CHeaderData replyHdr; + replyHdr.setId(id); + replyHdr.setBand1(0xFFU); + replyHdr.setBand2(0xFFU); + replyHdr.setBand3(0xFFU); + replyHdr.setFlag1(0x41U); + replyHdr.setMyCall1(header.getRptCall2()); + replyHdr.setMyCall2(wxT(" ")); + replyHdr.setYourCall(header.getMyCall1()); + replyHdr.setRptCall1(header.getRptCall2()); + replyHdr.setRptCall2(header.getRptCall1()); + + writeHeader(replyHdr); + + CAMBEData replyData; + unsigned char buffer[DV_FRAME_LENGTH_BYTES]; + + replyData.setId(id); + replyData.setBand1(0xFFU); + replyData.setBand2(0xFFU); + replyData.setBand3(0xFFU); + + ::memcpy(buffer + 0U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, END_PATTERN_BYTES + 0U, 3U); + + replyData.setSeq(0x00U); // Seq = 0 + replyData.setData(buffer, DV_FRAME_LENGTH_BYTES); + + writeAMBE(replyData); + + ::memset(buffer + 0U, 0x00, DV_FRAME_LENGTH_BYTES); + ::memcpy(buffer + 0U, END_PATTERN_BYTES + 3U, 3U); + + replyData.setSeq(0x41U); // Seq = 1 and end-of-data + replyData.setData(buffer, DV_FRAME_LENGTH_BYTES); + + writeAMBE(replyData); +} + +void CIcomRepeaterProtocolHandler::free(CDataQueue* dataQueue) +{ + if (dataQueue == NULL) + return; + + switch (dataQueue->getType()) { + case RT_HEADER: + delete dataQueue->getHeader(); + break; + case RT_HEARD: + delete dataQueue->getHeard(); + break; + case RT_AMBE: + delete dataQueue->getAMBE(); + break; + case RT_DD: + delete dataQueue->getDD(); + break; + default: + break; + } + + delete dataQueue; +} diff --git a/Common/IcomRepeaterProtocolHandler.h b/Common/IcomRepeaterProtocolHandler.h new file mode 100644 index 0000000..95ee151 --- /dev/null +++ b/Common/IcomRepeaterProtocolHandler.h @@ -0,0 +1,176 @@ +/* + * 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 + * 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 IcomRepeaterProtocolHandler_H +#define IcomRepeaterProtocolHandler_H + +#include "RepeaterProtocolHandler.h" +#include "UDPReaderWriter.h" +#include "DStarDefines.h" +#include "RingBuffer.h" +#include "HeaderData.h" +#include "StatusData.h" +#include "HeardData.h" +#include "AMBEData.h" +#include "TextData.h" +#include "PollData.h" +#include "DDData.h" +#include "Timer.h" + +#if defined(__WINDOWS__) +#include "Inaddr.h" +#else +#include +#endif + +#include + +class CDataQueue { +public: + CDataQueue(CHeaderData* header) : + m_type(RT_HEADER), + m_header(header), + m_heard(NULL), + m_ambe(NULL), + m_dd(NULL) + { + } + + CDataQueue(CHeardData* heard) : + m_type(RT_HEARD), + m_header(NULL), + m_heard(heard), + m_ambe(NULL), + m_dd(NULL) + { + } + + CDataQueue(CAMBEData* data) : + m_type(RT_AMBE), + m_header(NULL), + m_heard(NULL), + m_ambe(data), + m_dd(NULL) + { + } + + CDataQueue(CDDData* data) : + m_type(RT_DD), + m_header(NULL), + m_heard(NULL), + m_ambe(NULL), + m_dd(data) + { + } + + CDataQueue() : + m_type(RT_POLL), + m_header(NULL), + m_heard(NULL), + m_ambe(NULL), + m_dd(NULL) + { + } + + REPEATER_TYPE getType() const + { + return m_type; + } + + CHeaderData* getHeader() const + { + return m_header; + } + + CHeardData* getHeard() const + { + return m_heard; + } + + CAMBEData* getAMBE() const + { + return m_ambe; + } + + CDDData* getDD() const + { + return m_dd; + } + +private: + REPEATER_TYPE m_type; + CHeaderData* m_header; + CHeardData* m_heard; + CAMBEData* m_ambe; + CDDData* m_dd; +}; + +class CIcomRepeaterProtocolHandler : public IRepeaterProtocolHandler, public wxThread { +public: + CIcomRepeaterProtocolHandler(const wxString& address, unsigned int port, const wxString& icomAddress, unsigned int icomPort); + virtual ~CIcomRepeaterProtocolHandler(); + + virtual void setCount(unsigned int count); + + virtual bool open(); + + virtual void* Entry(); + + virtual bool writeHeader(CHeaderData& header); + virtual bool writeAMBE(CAMBEData& data); + virtual bool writeDD(CDDData& data); + virtual bool writeText(CTextData& text); + virtual bool writeStatus(CStatusData& status); + + virtual REPEATER_TYPE read(); + virtual CPollData* readPoll(); + virtual CHeardData* readHeard(); + virtual CHeaderData* readHeader(); + virtual CAMBEData* readAMBE(); + virtual CDDData* readDD(); + virtual CHeaderData* readBusyHeader(); + virtual CAMBEData* readBusyAMBE(); + + virtual void close(); + +private: + CUDPReaderWriter m_socket; + in_addr m_icomAddress; + unsigned int m_icomPort; + bool m_over1; + wxUint16 m_seqNo; + unsigned int m_tries; + CDataQueue* m_ackQueue; + bool m_killed; + REPEATER_TYPE m_type; + unsigned char* m_buffer; + CRingBuffer m_rptrQueue; + CRingBuffer m_gwyQueue; + CTimer m_retryTimer; + + void readIcomPackets(); + void sendGwyPackets(); + bool sendAck(wxUint16 seqNo); + + void sendSingleReply(const CHeaderData& header); + void sendMultiReply(const CHeaderData& header); + + void free(CDataQueue* dataQueue); +}; + +#endif diff --git a/Common/LogEvent.cpp b/Common/LogEvent.cpp new file mode 100644 index 0000000..2cd850c --- /dev/null +++ b/Common/LogEvent.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2010 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 "LogEvent.h" + +CLogEvent::CLogEvent(const wxString& text, wxEventType type, int id) : +wxEvent(id, type), +m_text(text) +{ +} + +CLogEvent::CLogEvent(const CLogEvent& event) : +wxEvent(event), +m_text(event.m_text) +{ +} + +CLogEvent::~CLogEvent() +{ +} + +wxString CLogEvent::getText() const +{ + return m_text; +} + +wxEvent* CLogEvent::Clone() const +{ + return new CLogEvent(*this); +} diff --git a/Common/LogEvent.h b/Common/LogEvent.h new file mode 100644 index 0000000..4721989 --- /dev/null +++ b/Common/LogEvent.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010 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 LogEvent_H +#define LogEvent_H + +#include + +class CLogEvent : public wxEvent { +public: + CLogEvent(const wxString& text, wxEventType type, int id = 0); + virtual ~CLogEvent(); + + virtual wxString getText() const; + + virtual wxEvent* Clone() const; + +protected: + CLogEvent(const CLogEvent& event); + +private: + wxString m_text; +}; + +#endif diff --git a/Common/Logger.cpp b/Common/Logger.cpp new file mode 100644 index 0000000..8edd3b1 --- /dev/null +++ b/Common/Logger.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2002,2003,2009,2011,2012,2018 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 "Logger.h" + +CLogger::CLogger(const wxString& directory, const wxString& name) : +#if(defined(__WINDOWS__)) +m_day(0), +#endif +wxLog(), +m_name(name), +m_file(NULL), +m_fileName() +{ + m_file = new wxFFile; + + m_fileName.SetPath(directory); + m_fileName.SetExt(wxT("log")); + +#if(defined(__WINDOWS__)) + time_t timestamp; + ::time(×tamp); + struct tm* tm = ::gmtime(×tamp); + + wxString text; + text.Printf(wxT("%s-%04d-%02d-%02d"), m_name.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); + + m_day = tm->tm_yday; + m_fileName.SetName(text); +#else + m_fileName.SetName(m_name); +#endif + + bool ret = m_file->Open(m_fileName.GetFullPath(), wxT("a+t")); + if (!ret) { + wxLogError(wxT("Cannot open %s file for appending"), m_fileName.GetFullPath().c_str()); + return; + } +} + +CLogger::~CLogger() +{ + wxASSERT(m_file != NULL); + + m_file->Close(); + delete m_file; +} + +void CLogger::DoLogRecord(wxLogLevel level, const wxString& msg, const wxLogRecordInfo& info) +{ + wxASSERT(m_file != NULL); + wxASSERT(m_file->IsOpened()); + + wxString letter; + + switch (level) { + case wxLOG_FatalError: letter = wxT("F"); break; + case wxLOG_Error: letter = wxT("E"); break; + case wxLOG_Warning: letter = wxT("W"); break; + case wxLOG_Info: letter = wxT("I"); break; + case wxLOG_Message: letter = wxT("M"); break; + case wxLOG_Status: letter = wxT("M"); break; + case wxLOG_Trace: letter = wxT("T"); break; + case wxLOG_Debug: letter = wxT("D"); break; + default: letter = wxT("U"); break; + } + + struct tm* tm = ::gmtime(&info.timestamp); + + wxString message; + message.Printf(wxT("%s: %04d-%02d-%02d %02d:%02d:%02d: %s\n"), letter.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, msg.c_str()); + + logString(message, info.timestamp); + + if (level == wxLOG_FatalError) + ::abort(); +} + +void CLogger::logString(const wxString& msg, time_t timestamp) +{ + wxASSERT(m_file != NULL); + wxASSERT(m_file->IsOpened()); + +#if(defined(__WINDOWS__)) + struct tm* tm = ::gmtime(×tamp); + + int day = tm->tm_yday; + if (day != m_day) { + wxString text; + text.Printf(wxT("%s-%04d-%02d-%02d"), m_name.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); + + m_day = day; + m_fileName.SetName(text); + + m_file->Close(); + + bool ret = m_file->Open(m_fileName.GetFullPath(), wxT("a+t")); + if (!ret) { + wxLogError(wxT("Cannot open %s file for appending"), m_fileName.GetFullPath().c_str()); + return; + } + } +#endif + + m_file->Write(msg); + m_file->Flush(); +} diff --git a/Common/Logger.h b/Common/Logger.h new file mode 100644 index 0000000..0d082f0 --- /dev/null +++ b/Common/Logger.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2002,2003,2009,2011,2012,2018 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 Logger_H +#define Logger_H + +#include +#include +#include +#include + +class CLogger : public wxLog { +public: + CLogger(const wxString& directory, const wxString& name); + virtual ~CLogger(); + + virtual void DoLogRecord(wxLogLevel level, const wxString& msg, const wxLogRecordInfo& info); + +private: + wxString m_name; + wxFFile* m_file; + wxFileName m_fileName; +#if(defined(__WINDOWS__)) + int m_day; +#endif + + void logString(const wxString& msg, time_t timestamp); +}; + +#endif diff --git a/Common/PollData.cpp b/Common/PollData.cpp new file mode 100644 index 0000000..04b2569 --- /dev/null +++ b/Common/PollData.cpp @@ -0,0 +1,284 @@ +/* + * 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 + * 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 "PollData.h" + +#include "DStarDefines.h" +#include "Utils.h" + +CPollData::CPollData(const wxString& data1, const wxString& data2, DIRECTION direction, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) : +m_data1(data1), +m_data2(data2), +m_direction(direction), +m_dongle(false), +m_length(0U), +m_yourAddress(yourAddress), +m_yourPort(yourPort), +m_myPort(myPort) +{ + wxASSERT(yourPort > 0U); +} + +CPollData::CPollData(const wxString& data, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) : +m_data1(data), +m_data2(), +m_direction(DIR_OUTGOING), +m_dongle(false), +m_length(0U), +m_yourAddress(yourAddress), +m_yourPort(yourPort), +m_myPort(myPort) +{ + wxASSERT(yourPort > 0U); +} + +CPollData::CPollData(const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) : +m_data1(), +m_data2(), +m_direction(DIR_OUTGOING), +m_dongle(false), +m_length(0U), +m_yourAddress(yourAddress), +m_yourPort(yourPort), +m_myPort(myPort) +{ + wxASSERT(yourPort > 0U); +} + +CPollData::CPollData() : +m_data1(), +m_data2(), +m_direction(DIR_OUTGOING), +m_dongle(false), +m_length(0U), +m_yourAddress(), +m_yourPort(0U), +m_myPort(0U) +{ +} + +CPollData::~CPollData() +{ +} + +bool CPollData::setDExtraData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) +{ + wxASSERT(data != NULL); + wxASSERT(length >= 9U); + wxASSERT(yourPort > 0U); + + m_data1 = wxString((const char*)data, wxConvLocal, LONG_CALLSIGN_LENGTH); + m_dongle = data[LONG_CALLSIGN_LENGTH] != 0x00; + + m_length = length; + m_yourAddress = yourAddress; + m_yourPort = yourPort; + m_myPort = myPort; + + + return true; +} + +bool CPollData::setDCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) +{ + wxASSERT(data != NULL); + wxASSERT(yourPort > 0U); + + switch (length) { + case 17U: + m_data1 = wxString((const char*)(data + 0U), wxConvLocal, LONG_CALLSIGN_LENGTH); + m_data2 = wxString((const char*)(data + 9U), wxConvLocal, LONG_CALLSIGN_LENGTH); + m_length = length; + m_direction = DIR_INCOMING; + m_yourAddress = yourAddress; + m_yourPort = yourPort; + m_myPort = myPort; + break; + + case 22U: + m_data1 = wxString((const char*)(data + 0U), wxConvLocal, LONG_CALLSIGN_LENGTH); + m_data2 = wxString((const char*)(data + 9U), wxConvLocal, LONG_CALLSIGN_LENGTH - 1U); + m_data2.Append(wxString((const char*)(data + 17U), wxConvLocal, 1U)); + m_length = length; + m_direction = DIR_OUTGOING; + m_yourAddress = yourAddress; + m_yourPort = yourPort; + m_myPort = myPort; + break; + } + + 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); + + m_length = length; + m_yourAddress = yourAddress; + m_yourPort = yourPort; + m_myPort = myPort; + + return true; +} + +unsigned int CPollData::getDExtraData(unsigned char *data, unsigned int length) const +{ + wxASSERT(data != NULL); + wxASSERT(length >= 9U); + + ::memset(data, ' ', LONG_CALLSIGN_LENGTH); + + for (unsigned int i = 0U; i < m_data1.Len() && i < LONG_CALLSIGN_LENGTH; i++) + data[i] = m_data1.GetChar(i); + + data[LONG_CALLSIGN_LENGTH] = 0x00; + + return 9U; +} + +unsigned int CPollData::getDCSData(unsigned char *data, unsigned int length) const +{ + wxASSERT(data != NULL); + wxASSERT(length >= 22U); + + if (m_direction == DIR_OUTGOING) { + ::memset(data, ' ', 17U); + + for (unsigned int i = 0U; i < m_data1.Len() && i < LONG_CALLSIGN_LENGTH; i++) + data[i + 0U] = m_data1.GetChar(i); + + data[8U] = 0x00U; + + for (unsigned int i = 0U; i < m_data2.Len() && i < LONG_CALLSIGN_LENGTH; i++) + data[i + 9U] = m_data2.GetChar(i); + + return 17U; + } else { + ::memset(data, ' ', 22U); + + for (unsigned int i = 0U; i < m_data1.Len() && i < LONG_CALLSIGN_LENGTH; i++) + data[i + 0U] = m_data1.GetChar(i); + + for (unsigned int i = 0U; i < m_data2.Len() && i < (LONG_CALLSIGN_LENGTH - 1U); i++) + data[i + 9U] = m_data2.GetChar(i); + + if (m_data2.Len() >= LONG_CALLSIGN_LENGTH) + data[17U] = m_data2.GetChar(LONG_CALLSIGN_LENGTH - 1U); + + data[18U] = 0x0AU; + data[19U] = 0x00U; + + return 22U; + } +} + +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); + wxASSERT(length >= 3U); + + data[0U] = 0x03; + data[1U] = 0x60; + data[2U] = 0x00; + + return 3U; +} + +wxString CPollData::getData1() const +{ + return m_data1; +} + +void CPollData::setData1(const wxString& data) +{ + m_data1 = data; +} + +wxString CPollData::getData2() const +{ + return m_data2; +} + +void CPollData::setData2(const wxString& data) +{ + m_data2 = data; +} + +bool CPollData::isDongle() const +{ + return m_dongle; +} + +in_addr CPollData::getYourAddress() const +{ + return m_yourAddress; +} + +unsigned int CPollData::getYourPort() const +{ + return m_yourPort; +} + +unsigned int CPollData::getMyPort() const +{ + return m_myPort; +} + +DIRECTION CPollData::getDirection() const +{ + return m_direction; +} + +unsigned int CPollData::getLength() const +{ + return m_length; +} diff --git a/Common/PollData.h b/Common/PollData.h new file mode 100644 index 0000000..d85ec78 --- /dev/null +++ b/Common/PollData.h @@ -0,0 +1,76 @@ +/* + * 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 + * 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 PollData_H +#define PollData_H + +#include "Defs.h" + +#include + +#if defined(__WINDOWS__) +#include "Inaddr.h" +#else +#include +#endif + +class CPollData { +public: + CPollData(const wxString& data1, const wxString& data2, DIRECTION direction, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort = 0U); + CPollData(const wxString& data, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort = 0U); + CPollData(const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort = 0U); + CPollData(); + ~CPollData(); + + 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); + + wxString getData2() const; + void setData2(const wxString& data); + + bool isDongle() const; + + in_addr getYourAddress() const; + unsigned int getYourPort() const; + unsigned int getMyPort() const; + + DIRECTION getDirection() const; + unsigned int getLength() const; + +private: + wxString m_data1; + wxString m_data2; + DIRECTION m_direction; + bool m_dongle; + unsigned int m_length; + in_addr m_yourAddress; + unsigned int m_yourPort; + unsigned int m_myPort; +}; + +#endif diff --git a/Common/ReflectorCallback.h b/Common/ReflectorCallback.h new file mode 100644 index 0000000..68f97f1 --- /dev/null +++ b/Common/ReflectorCallback.h @@ -0,0 +1,44 @@ +/* + * 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 + * 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 ReflectorCallback_H +#define ReflectorCallback_H + +#include "DStarDefines.h" +#include "HeaderData.h" +#include "AMBEData.h" +#include "Defs.h" + +#include + +class IReflectorCallback { +public: + virtual bool process(CHeaderData& header, DIRECTION direction, AUDIO_SOURCE source) = 0; + + virtual bool process(CAMBEData& data, DIRECTION direction, AUDIO_SOURCE source) = 0; + + virtual bool linkFailed(DSTAR_PROTOCOL protocol, const wxString& callsign, bool isRecoverable) = 0; + + virtual void linkRefused(DSTAR_PROTOCOL protocol, const wxString& callsign) = 0; + + virtual void linkUp(DSTAR_PROTOCOL protocol, const wxString& callsign) = 0; + +private: +}; + +#endif diff --git a/Common/RemoteHandler.cpp b/Common/RemoteHandler.cpp new file mode 100644 index 0000000..dbbe1fd --- /dev/null +++ b/Common/RemoteHandler.cpp @@ -0,0 +1,214 @@ +/* + * 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 + * 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 "StarNetHandler.h" +#include "RemoteHandler.h" +#include "DExtraHandler.h" +#include "DPlusHandler.h" +#include "DStarDefines.h" +#include "DCSHandler.h" + +CRemoteHandler::CRemoteHandler(const wxString& password, unsigned int port, const wxString& address) : +m_password(password), +m_handler(port, address), +m_random(0U) +{ + wxASSERT(port > 0U); + wxASSERT(!password.IsEmpty()); +} + +CRemoteHandler::~CRemoteHandler() +{ +} + +bool CRemoteHandler::open() +{ + return m_handler.open(); +} + +void CRemoteHandler::process() +{ + RPH_TYPE type = m_handler.readType(); + switch (type) { + case RPHT_LOGOUT: + m_handler.setLoggedIn(false); + wxLogMessage(wxT("Remote control user has logged out")); + break; + case RPHT_LOGIN: + m_random = ::rand(); + m_handler.sendRandom(m_random); + break; + case RPHT_HASH: { + bool valid = m_handler.readHash(m_password, m_random); + if (valid) { + wxLogMessage(wxT("Remote control user has logged in")); + m_handler.setLoggedIn(true); + m_handler.sendACK(); + } else { + wxLogMessage(wxT("Remote control user has failed login authentication")); + m_handler.setLoggedIn(false); + m_handler.sendNAK(wxT("Invalid password")); + } + } + break; + case RPHT_CALLSIGNS: + sendCallsigns(); + break; + case RPHT_REPEATER: { + wxString callsign = m_handler.readRepeater(); + sendRepeater(callsign); + } + break; + case RPHT_STARNET: { + wxString callsign = m_handler.readStarNetGroup(); + sendStarNetGroup(callsign); + } + break; + case RPHT_LINK: { + wxString callsign, reflector; + RECONNECT reconnect; + m_handler.readLink(callsign, reconnect, reflector); + if (reflector.IsEmpty()) + wxLogMessage(wxT("Remote control user has linked \"%s\" to \"None\" with reconnect %d"), callsign.c_str(), int(reconnect)); + else + wxLogMessage(wxT("Remote control user has linked \"%s\" to \"%s\" with reconnect %d"), callsign.c_str(), reflector.c_str(), int(reconnect)); + link(callsign, reconnect, reflector, true); + } + break; + case RPHT_UNLINK: { + wxString callsign, reflector; + PROTOCOL protocol; + m_handler.readUnlink(callsign, protocol, reflector); + wxLogMessage(wxT("Remote control user has unlinked \"%s\" from \"%s\" for protocol %d"), callsign.c_str(), reflector.c_str(), int(protocol)); + unlink(callsign, protocol, reflector); + } + break; + case RPHT_LINKSCR: { + wxString callsign, reflector; + RECONNECT reconnect; + m_handler.readLinkScr(callsign, reconnect, reflector); + if (reflector.IsEmpty()) + wxLogMessage(wxT("Remote control user has linked \"%s\" to \"None\" with reconnect %d from localhost"), callsign.c_str(), reconnect); + else + wxLogMessage(wxT("Remote control user has linked \"%s\" to \"%s\" with reconnect %d from localhost"), callsign.c_str(), reflector.c_str(), reconnect); + link(callsign, reconnect, reflector, false); + } + break; + case RPHT_LOGOFF: { + wxString callsign, user; + m_handler.readLogoff(callsign, user); + wxLogMessage(wxT("Remote control user has logged off \"%s\" from \"%s\""), user.c_str(), callsign.c_str()); + logoff(callsign, user); + } + break; + default: + break; + } +} + +void CRemoteHandler::close() +{ + m_handler.close(); +} + +void CRemoteHandler::sendCallsigns() +{ + wxArrayString repeaters = CRepeaterHandler::listDVRepeaters(); + wxArrayString starNets = CStarNetHandler::listStarNets(); + + m_handler.sendCallsigns(repeaters, starNets); +} + +void CRemoteHandler::sendRepeater(const wxString& callsign) +{ + CRepeaterHandler* repeater = CRepeaterHandler::findDVRepeater(callsign); + if (repeater == NULL) { + m_handler.sendNAK(wxT("Invalid repeater callsign")); + return; + } + + CRemoteRepeaterData* data = repeater->getInfo(); + if (data != NULL) { + CDExtraHandler::getInfo(repeater, *data); + CDPlusHandler::getInfo(repeater, *data); + CDCSHandler::getInfo(repeater, *data); + CCCSHandler::getInfo(repeater, *data); + + m_handler.sendRepeater(*data); + } + + delete data; +} + +void CRemoteHandler::sendStarNetGroup(const wxString& callsign) +{ + CStarNetHandler* starNet = CStarNetHandler::findStarNet(callsign); + if (starNet == NULL) { + m_handler.sendNAK(wxT("Invalid STARnet Group callsign")); + return; + } + + CRemoteStarNetGroup* data = starNet->getInfo(); + if (data != NULL) + m_handler.sendStarNetGroup(*data); + + delete data; +} + +void CRemoteHandler::link(const wxString& callsign, RECONNECT reconnect, const wxString& reflector, bool respond) +{ + CRepeaterHandler* repeater = CRepeaterHandler::findDVRepeater(callsign); + if (repeater == NULL) { + m_handler.sendNAK(wxT("Invalid repeater callsign")); + return; + } + + repeater->link(reconnect, reflector); + + if (respond) + m_handler.sendACK(); +} + +void CRemoteHandler::unlink(const wxString& callsign, PROTOCOL protocol, const wxString& reflector) +{ + CRepeaterHandler* repeater = CRepeaterHandler::findDVRepeater(callsign); + if (repeater == NULL) { + m_handler.sendNAK(wxT("Invalid repeater callsign")); + return; + } + + repeater->unlink(protocol, reflector); + + m_handler.sendACK(); +} + +void CRemoteHandler::logoff(const wxString& callsign, const wxString& user) +{ + CStarNetHandler* starNet = CStarNetHandler::findStarNet(callsign); + if (starNet == NULL) { + m_handler.sendNAK(wxT("Invalid STARnet group callsign")); + return; + } + + bool res = starNet->logoff(user); + if (!res) + m_handler.sendNAK(wxT("Invalid STARnet user callsign")); + else + m_handler.sendACK(); +} diff --git a/Common/RemoteHandler.h b/Common/RemoteHandler.h new file mode 100644 index 0000000..6ad2f94 --- /dev/null +++ b/Common/RemoteHandler.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2011,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 RemoteHandler_H +#define RemoteHandler_H + +#include "RemoteProtocolHandler.h" +#include "Timer.h" + +#include + +class CRemoteHandler { +public: + CRemoteHandler(const wxString& password, unsigned int port, const wxString& address = wxEmptyString); + ~CRemoteHandler(); + + bool open(); + + void process(); + + void close(); + +private: + wxString m_password; + CRemoteProtocolHandler m_handler; + unsigned int m_random; + + void sendCallsigns(); + void sendRepeater(const wxString& callsign); + void sendStarNetGroup(const wxString& callsign); + void link(const wxString& callsign, RECONNECT reconnect, const wxString& reflector, bool respond); + void unlink(const wxString& callsign, PROTOCOL protocol, const wxString& reflector); + void logoff(const wxString& callsign, const wxString& user); +}; + +#endif diff --git a/Common/RemoteLinkData.cpp b/Common/RemoteLinkData.cpp new file mode 100644 index 0000000..1483c40 --- /dev/null +++ b/Common/RemoteLinkData.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2011 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 "RemoteLinkData.h" + +CRemoteLinkData::CRemoteLinkData(const wxString& callsign, PROTOCOL protocol, bool linked, DIRECTION direction, bool dongle) : +m_callsign(callsign), +m_protocol(protocol), +m_linked(linked), +m_direction(direction), +m_dongle(dongle) +{ +} + +CRemoteLinkData::~CRemoteLinkData() +{ +} + +wxString CRemoteLinkData::getCallsign() const +{ + return m_callsign; +} + +wxInt32 CRemoteLinkData::getProtocol() const +{ + return wxInt32(m_protocol); +} + +wxInt32 CRemoteLinkData::isLinked() const +{ + return m_linked ? 1 : 0; +} + +wxInt32 CRemoteLinkData::getDirection() const +{ + return wxInt32(m_direction); +} + +wxInt32 CRemoteLinkData::isDongle() const +{ + return m_dongle ? 1 : 0; +} diff --git a/Common/RemoteLinkData.h b/Common/RemoteLinkData.h new file mode 100644 index 0000000..23df8f8 --- /dev/null +++ b/Common/RemoteLinkData.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2011 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 RemoteLinkData_H +#define RemoteLinkData_H + +#include "Defs.h" + +#include + +class CRemoteLinkData { +public: + CRemoteLinkData(const wxString& callsign, PROTOCOL protocol, bool linked, DIRECTION direction, bool dongle); + ~CRemoteLinkData(); + + wxString getCallsign() const; + wxInt32 getProtocol() const; + wxInt32 isLinked() const; + wxInt32 getDirection() const; + wxInt32 isDongle() const; + +private: + wxString m_callsign; + PROTOCOL m_protocol; + bool m_linked; + DIRECTION m_direction; + bool m_dongle; +}; + +#endif diff --git a/Common/RemoteProtocolHandler.cpp b/Common/RemoteProtocolHandler.cpp new file mode 100644 index 0000000..1c77426 --- /dev/null +++ b/Common/RemoteProtocolHandler.cpp @@ -0,0 +1,441 @@ +/* + * Copyright (C) 2011,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 "RemoteProtocolHandler.h" +#include "DStarDefines.h" +#include "SHA256.h" +#include "Utils.h" + +const unsigned int BUFFER_LENGTH = 2000U; + +CRemoteProtocolHandler::CRemoteProtocolHandler(unsigned int port, const wxString& address) : +m_socket(address, port), +m_address(), +m_port(0U), +m_loggedIn(false), +m_type(RPHT_NONE), +m_inBuffer(NULL), +m_inLength(0U), +m_outBuffer(NULL) +{ + wxASSERT(port > 0U); + + m_inBuffer = new unsigned char[BUFFER_LENGTH]; + m_outBuffer = new unsigned char[BUFFER_LENGTH]; +} + +CRemoteProtocolHandler::~CRemoteProtocolHandler() +{ + delete[] m_outBuffer; + delete[] m_inBuffer; +} + +bool CRemoteProtocolHandler::open() +{ + return m_socket.open(); +} + +RPH_TYPE CRemoteProtocolHandler::readType() +{ + m_type = RPHT_NONE; + + in_addr address; + unsigned int port; + + int length = m_socket.read(m_inBuffer, BUFFER_LENGTH, address, port); + if (length <= 0) + return m_type; + + // CUtils::dump(wxT("Incoming"), m_inBuffer, length); + + if (::memcmp(m_inBuffer, "LIN", 3U) == 0) { + m_loggedIn = false; + m_address = address; + m_port = port; + m_type = RPHT_LOGIN; + return m_type; + } + + if (address.s_addr == inet_addr("127.0.0.1")) { + if (::memcmp(m_inBuffer, "LKS", 3U) == 0) { + m_inLength = length; + m_type = RPHT_LINKSCR; + return m_type; + } + } + + if (m_loggedIn) { + if (address.s_addr != m_address.s_addr || port != m_port) { + sendNAK(wxT("You are not logged in")); + return m_type; + } + } + + m_inLength = length; + + if (::memcmp(m_inBuffer, "SHA", 3U) == 0) { + if (m_loggedIn) { + sendNAK(wxT("Someone is already logged in")); + return m_type; + } + m_type = RPHT_HASH; + return m_type; + } else if (::memcmp(m_inBuffer, "GCS", 3U) == 0) { + if (!m_loggedIn) { + sendNAK(wxT("You are not logged in")); + return m_type; + } + m_type = RPHT_CALLSIGNS; + return m_type; + } else if (::memcmp(m_inBuffer, "GRP", 3U) == 0) { + if (!m_loggedIn) { + sendNAK(wxT("You are not logged in")); + return m_type; + } + m_type = RPHT_REPEATER; + return m_type; + } else if (::memcmp(m_inBuffer, "GSN", 3U) == 0) { + if (!m_loggedIn) { + sendNAK(wxT("You are not logged in")); + return m_type; + } + m_type = RPHT_STARNET; + return m_type; + } else if (::memcmp(m_inBuffer, "LNK", 3U) == 0) { + if (!m_loggedIn) { + sendNAK(wxT("You are not logged in")); + return m_type; + } + m_type = RPHT_LINK; + return m_type; + } else if (::memcmp(m_inBuffer, "UNL", 3U) == 0) { + if (!m_loggedIn) { + sendNAK(wxT("You are not logged in")); + return m_type; + } + m_type = RPHT_UNLINK; + return m_type; + } else if (::memcmp(m_inBuffer, "LGO", 3U) == 0) { + if (!m_loggedIn) { + sendNAK(wxT("You are not logged in")); + return m_type; + } + m_type = RPHT_LOGOFF; + return m_type; + } else if (::memcmp(m_inBuffer, "LOG", 3U) == 0) { + if (!m_loggedIn) + return m_type; + m_type = RPHT_LOGOUT; + return m_type; + } else { + if (!m_loggedIn) { + sendNAK(wxT("You are not logged in")); + return m_type; + } + m_type = RPHT_UNKNOWN; + return m_type; + } +} + +bool CRemoteProtocolHandler::readHash(const wxString& password, wxUint32 random) +{ + if (m_type != RPHT_HASH) + return false; + + unsigned char* hash = m_inBuffer + 3U; + + unsigned int len = password.Len() + sizeof(wxUint32); + unsigned char* in = new unsigned char[len]; + unsigned char* out = new unsigned char[32U]; + + ::memcpy(in, &random, sizeof(wxUint32)); + for (unsigned int i = 0U; i < password.Len(); i++) + in[i + sizeof(unsigned int)] = password.GetChar(i); + + CSHA256 sha256; + sha256.buffer(in, len, out); + + bool res = ::memcmp(out, hash, 32U) == 0; + + delete[] in; + delete[] out; + + return res; +} + +wxString CRemoteProtocolHandler::readRepeater() +{ + if (m_type != RPHT_REPEATER) + return wxEmptyString; + + wxString callsign((char*)(m_inBuffer + 3U), wxConvLocal, LONG_CALLSIGN_LENGTH); + + return callsign; +} + +wxString CRemoteProtocolHandler::readStarNetGroup() +{ + if (m_type != RPHT_STARNET) + return wxEmptyString; + + wxString callsign((char*)(m_inBuffer + 3U), wxConvLocal, LONG_CALLSIGN_LENGTH); + + return callsign; +} + +bool CRemoteProtocolHandler::readLogoff(wxString& callsign, wxString& user) +{ + if (m_type != RPHT_LOGOFF) + return false; + + callsign = wxString((char*)(m_inBuffer + 3U), wxConvLocal, LONG_CALLSIGN_LENGTH); + user = wxString((char*)(m_inBuffer + 3U + LONG_CALLSIGN_LENGTH), wxConvLocal, LONG_CALLSIGN_LENGTH); + + return true; +} + + +bool CRemoteProtocolHandler::readLink(wxString& callsign, RECONNECT& reconnect, wxString& reflector) +{ + if (m_type != RPHT_LINK) + return false; + + callsign = wxString((char*)(m_inBuffer + 3U), wxConvLocal, LONG_CALLSIGN_LENGTH); + + wxInt32 temp; + ::memcpy(&temp, m_inBuffer + 3U + LONG_CALLSIGN_LENGTH, sizeof(wxInt32)); + reconnect = RECONNECT(wxINT32_SWAP_ON_BE(temp)); + + reflector = wxString((char*)(m_inBuffer + 3U + LONG_CALLSIGN_LENGTH + sizeof(wxInt32)), wxConvLocal, LONG_CALLSIGN_LENGTH); + + if (reflector.IsSameAs(wxT(" "))) + reflector.Clear(); + + return true; +} + +bool CRemoteProtocolHandler::readUnlink(wxString& callsign, PROTOCOL& protocol, wxString& reflector) +{ + if (m_type != RPHT_UNLINK) + return false; + + callsign = wxString((char*)(m_inBuffer + 3U), wxConvLocal, LONG_CALLSIGN_LENGTH); + + wxInt32 temp; + ::memcpy(&temp, m_inBuffer + 3U + LONG_CALLSIGN_LENGTH, sizeof(wxInt32)); + protocol = PROTOCOL(wxINT32_SWAP_ON_BE(temp)); + + reflector = wxString((char*)(m_inBuffer + 3U + LONG_CALLSIGN_LENGTH + sizeof(wxInt32)), wxConvLocal, LONG_CALLSIGN_LENGTH); + + return true; +} + +bool CRemoteProtocolHandler::readLinkScr(wxString& callsign, RECONNECT& reconnect, wxString& reflector) +{ + if (m_type != RPHT_LINKSCR) + return false; + + callsign = wxString((char*)(m_inBuffer + 3U), wxConvLocal, LONG_CALLSIGN_LENGTH); + + reflector = wxString((char*)(m_inBuffer + 3U + LONG_CALLSIGN_LENGTH), wxConvLocal, LONG_CALLSIGN_LENGTH); + + wxString rec = wxString((char*)(m_inBuffer + 3U + 2U * LONG_CALLSIGN_LENGTH), wxConvLocal, 1U); + + unsigned long val; + rec.ToULong(&val); + + reconnect = RECONNECT(val); + + if (reflector.IsSameAs(wxT(" "))) + reflector.Clear(); + + return true; +} + +bool CRemoteProtocolHandler::sendCallsigns(const wxArrayString& repeaters, const wxArrayString& starNets) +{ + unsigned char* p = m_outBuffer; + + ::memcpy(p, "CAL", 3U); + p += 3U; + + for (unsigned int n = 0U; n < repeaters.GetCount(); n++) { + *p++ = 'R'; + + ::memset(p, ' ' , LONG_CALLSIGN_LENGTH); + for (unsigned int i = 0U; i < repeaters.Item(n).Len(); i++) + p[i] = repeaters.Item(n).GetChar(i); + p += LONG_CALLSIGN_LENGTH; + } + + for (unsigned int n = 0U; n < starNets.GetCount(); n++) { + *p++ = 'S'; + + ::memset(p, ' ' , LONG_CALLSIGN_LENGTH); + for (unsigned int i = 0U; i < starNets.Item(n).Len(); i++) + p[i] = starNets.Item(n).GetChar(i); + p += LONG_CALLSIGN_LENGTH; + } + + // CUtils::dump(wxT("Outgoing"), m_outBuffer, p - m_outBuffer); + + return m_socket.write(m_outBuffer, p - m_outBuffer, m_address, m_port); +} + +bool CRemoteProtocolHandler::sendRepeater(const CRemoteRepeaterData& data) +{ + unsigned char* p = m_outBuffer; + + ::memcpy(p, "RPT", 3U); + p += 3U; + + ::memset(p, ' ', LONG_CALLSIGN_LENGTH); + for (unsigned int i = 0U; i < data.getCallsign().Len(); i++) + p[i] = data.getCallsign().GetChar(i); + p += LONG_CALLSIGN_LENGTH; + + wxInt32 reconnect = wxINT32_SWAP_ON_BE(data.getReconnect()); + ::memcpy(p, &reconnect, sizeof(wxInt32)); + p += sizeof(wxInt32); + + ::memset(p, ' ', LONG_CALLSIGN_LENGTH); + for (unsigned int i = 0U; i < data.getReflector().Len(); i++) + p[i] = data.getReflector().GetChar(i); + p += LONG_CALLSIGN_LENGTH; + + for (unsigned int n = 0U; n < data.getLinkCount(); n++) { + CRemoteLinkData& link = data.getLink(n); + + ::memset(p, ' ', LONG_CALLSIGN_LENGTH); + for (unsigned int i = 0U; i < link.getCallsign().Len(); i++) + p[i] = link.getCallsign().GetChar(i); + p += LONG_CALLSIGN_LENGTH; + + wxInt32 protocol = wxINT32_SWAP_ON_BE(link.getProtocol()); + ::memcpy(p, &protocol, sizeof(wxInt32)); + p += sizeof(wxInt32); + + wxInt32 linked = wxINT32_SWAP_ON_BE(link.isLinked()); + ::memcpy(p, &linked, sizeof(wxInt32)); + p += sizeof(wxInt32); + + wxInt32 direction = wxINT32_SWAP_ON_BE(link.getDirection()); + ::memcpy(p, &direction, sizeof(wxInt32)); + p += sizeof(wxInt32); + + wxInt32 dongle = wxINT32_SWAP_ON_BE(link.isDongle()); + ::memcpy(p, &dongle, sizeof(wxInt32)); + p += sizeof(wxInt32); + } + + // CUtils::dump(wxT("Outgoing"), m_outBuffer, p - m_outBuffer); + + return m_socket.write(m_outBuffer, p - m_outBuffer, m_address, m_port); +} + +bool CRemoteProtocolHandler::sendStarNetGroup(const CRemoteStarNetGroup& data) +{ + unsigned char* p = m_outBuffer; + + ::memcpy(p, "SNT", 3U); + p += 3U; + + ::memset(p, ' ', LONG_CALLSIGN_LENGTH); + for (unsigned int i = 0U; i < data.getCallsign().Len(); i++) + p[i] = data.getCallsign().GetChar(i); + p += LONG_CALLSIGN_LENGTH; + + ::memset(p, ' ', LONG_CALLSIGN_LENGTH); + for (unsigned int i = 0U; i < data.getLogoff().Len(); i++) + p[i] = data.getLogoff().GetChar(i); + p += LONG_CALLSIGN_LENGTH; + + wxUint32 timer = wxUINT32_SWAP_ON_BE(data.getTimer()); + ::memcpy(p, &timer, sizeof(wxUint32)); + p += sizeof(wxUint32); + + wxUint32 timeout = wxUINT32_SWAP_ON_BE(data.getTimeout()); + ::memcpy(p, &timeout, sizeof(wxUint32)); + p += sizeof(wxUint32); + + for (unsigned int n = 0U; n < data.getUserCount(); n++) { + CRemoteStarNetUser& user = data.getUser(n); + + ::memset(p, ' ', LONG_CALLSIGN_LENGTH); + for (unsigned int i = 0U; i < user.getCallsign().Len(); i++) + p[i] = user.getCallsign().GetChar(i); + p += LONG_CALLSIGN_LENGTH; + + timer = wxUINT32_SWAP_ON_BE(user.getTimer()); + ::memcpy(p, &timer, sizeof(wxUint32)); + p += sizeof(wxUint32); + + timeout = wxUINT32_SWAP_ON_BE(user.getTimeout()); + ::memcpy(p, &timeout, sizeof(wxUint32)); + p += sizeof(wxUint32); + } + + // CUtils::dump(wxT("Outgoing"), m_outBuffer, p - m_outBuffer); + + return m_socket.write(m_outBuffer, p - m_outBuffer, m_address, m_port); +} + +void CRemoteProtocolHandler::setLoggedIn(bool set) +{ + m_loggedIn = set; +} + +void CRemoteProtocolHandler::close() +{ + m_socket.close(); +} + +bool CRemoteProtocolHandler::sendACK() +{ + ::memcpy(m_outBuffer + 0U, "ACK", 3U); + + // CUtils::dump(wxT("Outgoing"), m_outBuffer, 3U); + + return m_socket.write(m_outBuffer, 3U, m_address, m_port); +} + +bool CRemoteProtocolHandler::sendNAK(const wxString& text) +{ + ::memcpy(m_outBuffer + 0U, "NAK", 3U); + + ::memset(m_outBuffer + 3U, 0x00U, text.Len() + 1U); + + for (unsigned int i = 0U; i < text.Len(); i++) + m_outBuffer[i + 3U] = text.GetChar(i); + + // CUtils::dump(wxT("Outgoing"), m_outBuffer, 3U + text.Len() + 1U); + + return m_socket.write(m_outBuffer, 3U + text.Len() + 1U, m_address, m_port); +} + +bool CRemoteProtocolHandler::sendRandom(wxUint32 random) +{ + ::memcpy(m_outBuffer + 0U, "RND", 3U); + + wxUint32 temp = wxUINT32_SWAP_ON_BE(random); + ::memcpy(m_outBuffer + 3U, &temp, sizeof(wxUint32)); + + // CUtils::dump(wxT("Outgoing"), m_outBuffer, 3U + sizeof(wxUint32)); + + return m_socket.write(m_outBuffer, 3U + sizeof(wxUint32), m_address, m_port); +} diff --git a/Common/RemoteProtocolHandler.h b/Common/RemoteProtocolHandler.h new file mode 100644 index 0000000..5355f25 --- /dev/null +++ b/Common/RemoteProtocolHandler.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2011,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 RemoteProtocolHandler_H +#define RemoteProtocolHandler_H + +#include "RemoteStarNetGroup.h" +#include "RemoteRepeaterData.h" +#include "UDPReaderWriter.h" +#include "Defs.h" + +#include + +enum RPH_TYPE { + RPHT_NONE, + RPHT_LOGIN, + RPHT_HASH, + RPHT_CALLSIGNS, + RPHT_REPEATER, + RPHT_STARNET, + RPHT_LINK, + RPHT_UNLINK, + RPHT_LINKSCR, + RPHT_LOGOFF, + RPHT_LOGOUT, + RPHT_UNKNOWN +}; + +class CRemoteProtocolHandler { +public: + CRemoteProtocolHandler(unsigned int port, const wxString& address = wxEmptyString); + ~CRemoteProtocolHandler(); + + bool open(); + + RPH_TYPE readType(); + + bool readHash(const wxString& password, wxUint32 random); + wxString readRepeater(); + wxString readStarNetGroup(); + bool readLink(wxString& callsign, RECONNECT& reconnect, wxString& reflector); + bool readUnlink(wxString& callsign, PROTOCOL& protocol, wxString& reflector); + bool readLinkScr(wxString& callsign, RECONNECT& reconnect, wxString& reflector); + bool readLogoff(wxString& callsign, wxString& user); + + bool sendACK(); + bool sendNAK(const wxString& text); + bool sendRandom(wxUint32 random); + bool sendCallsigns(const wxArrayString& repeaters, const wxArrayString& starNets); + bool sendRepeater(const CRemoteRepeaterData& data); + bool sendStarNetGroup(const CRemoteStarNetGroup& data); + + void setLoggedIn(bool set); + + void close(); + +private: + CUDPReaderWriter m_socket; + in_addr m_address; + unsigned int m_port; + bool m_loggedIn; + RPH_TYPE m_type; + unsigned char* m_inBuffer; + unsigned int m_inLength; + unsigned char* m_outBuffer; +}; + +#endif diff --git a/Common/RemoteRepeaterData.cpp b/Common/RemoteRepeaterData.cpp new file mode 100644 index 0000000..39bc714 --- /dev/null +++ b/Common/RemoteRepeaterData.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2011 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 "RemoteRepeaterData.h" + +#include +WX_DEFINE_OBJARRAY(CLinkData_t); + +CRemoteRepeaterData::CRemoteRepeaterData(const wxString& callsign, RECONNECT reconnect, const wxString& reflector) : +m_callsign(callsign), +m_reconnect(reconnect), +m_reflector(reflector), +m_links() +{ +} + +CRemoteRepeaterData::~CRemoteRepeaterData() +{ + m_links.Clear(); +} + +void CRemoteRepeaterData::addLink(const wxString& callsign, PROTOCOL protocol, bool linked, DIRECTION direction, bool dongle) +{ + CRemoteLinkData data(callsign, protocol, linked, direction, dongle); + + m_links.Add(data); +} + +wxString CRemoteRepeaterData::getCallsign() const +{ + return m_callsign; +} + +wxInt32 CRemoteRepeaterData::getReconnect() const +{ + return wxInt32(m_reconnect); +} + +wxString CRemoteRepeaterData::getReflector() const +{ + return m_reflector; +} + +unsigned int CRemoteRepeaterData::getLinkCount() const +{ + return m_links.GetCount(); +} + +CRemoteLinkData& CRemoteRepeaterData::getLink(unsigned int n) const +{ + return m_links.Item(n); +} diff --git a/Common/RemoteRepeaterData.h b/Common/RemoteRepeaterData.h new file mode 100644 index 0000000..1a6ff6c --- /dev/null +++ b/Common/RemoteRepeaterData.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2011 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 RemoteRepeaterData_H +#define RemoteRepeaterData_H + +#include "RemoteLinkData.h" + +#include +#include + +WX_DECLARE_OBJARRAY(CRemoteLinkData, CLinkData_t); + +class CRemoteRepeaterData { +public: + CRemoteRepeaterData(const wxString& callsign, RECONNECT reconnect, const wxString& reflector); + ~CRemoteRepeaterData(); + + void addLink(const wxString& callsign, PROTOCOL protocol, bool linked, DIRECTION direction, bool dongle); + + wxString getCallsign() const; + wxInt32 getReconnect() const; + wxString getReflector() const; + + unsigned int getLinkCount() const; + CRemoteLinkData& getLink(unsigned int n) const; + +private: + wxString m_callsign; + RECONNECT m_reconnect; + wxString m_reflector; + CLinkData_t m_links; +}; + +#endif diff --git a/Common/RemoteStarNetGroup.cpp b/Common/RemoteStarNetGroup.cpp new file mode 100644 index 0000000..9afdd2d --- /dev/null +++ b/Common/RemoteStarNetGroup.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2011 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 "RemoteStarNetGroup.h" + +#include +WX_DEFINE_OBJARRAY(CUserData_t); + +CRemoteStarNetGroup::CRemoteStarNetGroup(const wxString& callsign, const wxString& logoff, unsigned int timer, unsigned int timeout) : +m_callsign(callsign), +m_logoff(logoff), +m_timer(timer), +m_timeout(timeout), +m_users() +{ + if (m_logoff.IsSameAs(wxT(" "))) + m_logoff.Clear(); +} + +CRemoteStarNetGroup::~CRemoteStarNetGroup() +{ + m_users.Clear(); +} + +void CRemoteStarNetGroup::addUser(const wxString& callsign, unsigned int timer, unsigned int timeout) +{ + CRemoteStarNetUser user(callsign, timer, timeout); + + m_users.Add(user); +} + +wxString CRemoteStarNetGroup::getCallsign() const +{ + return m_callsign; +} + +wxString CRemoteStarNetGroup::getLogoff() const +{ + return m_logoff; +} + +wxUint32 CRemoteStarNetGroup::getTimer() const +{ + return wxUint32(m_timer); +} + +wxUint32 CRemoteStarNetGroup::getTimeout() const +{ + return wxUint32(m_timeout); +} + +unsigned int CRemoteStarNetGroup::getUserCount() const +{ + return m_users.GetCount(); +} + +CRemoteStarNetUser& CRemoteStarNetGroup::getUser(unsigned int n) const +{ + return m_users.Item(n); +} diff --git a/Common/RemoteStarNetGroup.h b/Common/RemoteStarNetGroup.h new file mode 100644 index 0000000..095d4e7 --- /dev/null +++ b/Common/RemoteStarNetGroup.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2011 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 RemoteStarNetGroup_H +#define RemoteStarNetGroup_H + +#include "RemoteStarNetUser.h" + +#include + +#include +WX_DECLARE_OBJARRAY(CRemoteStarNetUser, CUserData_t); + +class CRemoteStarNetGroup { +public: + CRemoteStarNetGroup(const wxString& callsign, const wxString& logoff, unsigned int timer, unsigned int timeout); + ~CRemoteStarNetGroup(); + + void addUser(const wxString& callsign, unsigned int timer, unsigned int timeout); + + wxString getCallsign() const; + wxString getLogoff() const; + wxUint32 getTimer() const; + wxUint32 getTimeout() const; + + unsigned int getUserCount() const; + CRemoteStarNetUser& getUser(unsigned int n) const; + +private: + wxString m_callsign; + wxString m_logoff; + unsigned int m_timer; + unsigned int m_timeout; + CUserData_t m_users; +}; + +#endif diff --git a/Common/RemoteStarNetUser.cpp b/Common/RemoteStarNetUser.cpp new file mode 100644 index 0000000..fc1f2c1 --- /dev/null +++ b/Common/RemoteStarNetUser.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2011 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 "RemoteStarNetUser.h" + +CRemoteStarNetUser::CRemoteStarNetUser(const wxString& callsign, unsigned int timer, unsigned int timeout) : +m_callsign(callsign), +m_timer(timer), +m_timeout(timeout) +{ +} + +CRemoteStarNetUser::~CRemoteStarNetUser() +{ +} + +wxString CRemoteStarNetUser::getCallsign() const +{ + return m_callsign; +} + +wxUint32 CRemoteStarNetUser::getTimer() const +{ + return wxUint32(m_timer); +} + +wxUint32 CRemoteStarNetUser::getTimeout() const +{ + return wxUint32(m_timeout); +} diff --git a/Common/RemoteStarNetUser.h b/Common/RemoteStarNetUser.h new file mode 100644 index 0000000..a23256e --- /dev/null +++ b/Common/RemoteStarNetUser.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2011 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 RemoteStarNetUser_H +#define RemoteStarNetUser_H + +#include + +class CRemoteStarNetUser { +public: + CRemoteStarNetUser(const wxString& callsign, unsigned int timer, unsigned int timeout); + ~CRemoteStarNetUser(); + + wxString getCallsign() const; + wxUint32 getTimer() const; + wxUint32 getTimeout() const; + +private: + wxString m_callsign; + unsigned int m_timer; + unsigned int m_timeout; +}; + +#endif diff --git a/Common/RepeaterCache.cpp b/Common/RepeaterCache.cpp new file mode 100644 index 0000000..e24041e --- /dev/null +++ b/Common/RepeaterCache.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2010 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 "RepeaterCache.h" + +const unsigned int CACHE_SIZE = 500U; + +CRepeaterCache::CRepeaterCache() : +m_cache(CACHE_SIZE) +{ +} + +CRepeaterCache::~CRepeaterCache() +{ + for (CRepeaterCache_t::iterator it = m_cache.begin(); it != m_cache.end(); ++it) + delete it->second; +} + +CRepeaterRecord* CRepeaterCache::find(const wxString& repeater) +{ + return m_cache[repeater]; +} + +void CRepeaterCache::update(const wxString& repeater, const wxString& gateway) +{ + CRepeaterRecord* rec = m_cache[repeater]; + + if (rec == NULL) + // A brand new record is needed + m_cache[repeater] = new CRepeaterRecord(repeater, gateway); + else + // Update an existing record + rec->setGateway(gateway); +} + +unsigned int CRepeaterCache::getCount() const +{ + return m_cache.size(); +} diff --git a/Common/RepeaterCache.h b/Common/RepeaterCache.h new file mode 100644 index 0000000..2fe2b54 --- /dev/null +++ b/Common/RepeaterCache.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2010 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 RepeaterCache_H +#define RepeaterCache_H + +#include +#include + +class CRepeaterRecord { +public: + CRepeaterRecord(const wxString& repeater, const wxString& gateway) : + m_repeater(repeater), + m_gateway(gateway) + { + } + + wxString getRepeater() const + { + return m_repeater; + } + + wxString getGateway() const + { + return m_gateway; + } + + void setGateway(const wxString& gateway) + { + m_gateway = gateway; + } + +private: + wxString m_repeater; + wxString m_gateway; +}; + +WX_DECLARE_STRING_HASH_MAP(CRepeaterRecord*, CRepeaterCache_t); + +class CRepeaterCache { +public: + CRepeaterCache(); + ~CRepeaterCache(); + + CRepeaterRecord* find(const wxString& repeater); + + void update(const wxString& repeater, const wxString& gateway); + + unsigned int getCount() const; + +private: + CRepeaterCache_t m_cache; +}; + +#endif diff --git a/Common/RepeaterCallback.h b/Common/RepeaterCallback.h new file mode 100644 index 0000000..fb6004c --- /dev/null +++ b/Common/RepeaterCallback.h @@ -0,0 +1,38 @@ +/* + * 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 + * 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 RepeaterCallback_H +#define RepeaterCallback_H + +#include "DStarDefines.h" +#include "HeaderData.h" +#include "AMBEData.h" +#include "Defs.h" + +#include + +class IRepeaterCallback { +public: + virtual bool process(CHeaderData& header, DIRECTION direction, AUDIO_SOURCE source) = 0; + + virtual bool process(CAMBEData& data, DIRECTION direction, AUDIO_SOURCE source) = 0; + +private: +}; + +#endif diff --git a/Common/RepeaterHandler.cpp b/Common/RepeaterHandler.cpp new file mode 100644 index 0000000..497c4c7 --- /dev/null +++ b/Common/RepeaterHandler.cpp @@ -0,0 +1,2970 @@ +/* + * 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 + * 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 "DExtraHandler.h" +#include "DPlusHandler.h" +#include "DStarDefines.h" +#include "DCSHandler.h" +#include "CCSHandler.h" +#include "HeaderData.h" +#include "DDHandler.h" +#include "AMBEData.h" +#include "Utils.h" + +#include + +const unsigned int ETHERNET_ADDRESS_LENGTH = 6U; + +const unsigned char ETHERNET_BROADCAST_ADDRESS[] = {0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU}; +// Multicast address '01:00:5E:00:00:01' - IP: '224.0.0.1' (to all) +const unsigned char TOALL_MULTICAST_ADDRESS[] = {0x01U, 0x00U, 0x5EU, 0x00U, 0x00U, 0x01U}; +// Multicast address '01:00:5E:00:00:23' - IP: '224.0.0.35' (DX-Cluster) +const unsigned char DX_MULTICAST_ADDRESS[] = {0x01U, 0x00U, 0x5EU, 0x00U, 0x00U, 0x23U}; + +unsigned int CRepeaterHandler::m_maxRepeaters = 0U; +CRepeaterHandler** CRepeaterHandler::m_repeaters = NULL; + +wxString CRepeaterHandler::m_localAddress; +CG2ProtocolHandler* CRepeaterHandler::m_g2Handler = NULL; +CIRCDDB* CRepeaterHandler::m_irc = NULL; +CCacheManager* CRepeaterHandler::m_cache = NULL; +wxString CRepeaterHandler::m_gateway; +TEXT_LANG CRepeaterHandler::m_language = TL_ENGLISH_UK; +bool CRepeaterHandler::m_dextraEnabled = true; +bool CRepeaterHandler::m_dplusEnabled = false; +bool CRepeaterHandler::m_dcsEnabled = true; +bool CRepeaterHandler::m_infoEnabled = true; +bool CRepeaterHandler::m_echoEnabled = true; +bool CRepeaterHandler::m_dtmfEnabled = true; + +CHeaderLogger* CRepeaterHandler::m_headerLogger = NULL; + +CAPRSWriter* CRepeaterHandler::m_aprsWriter = NULL; + +CCallsignList* CRepeaterHandler::m_restrictList = NULL; + +CRepeaterHandler::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) : +m_index(0x00U), +m_rptCallsign(), +m_gwyCallsign(), +m_band(' '), +m_address(), +m_port(port), +m_hwType(hwType), +m_repeaterHandler(handler), +m_frequency(frequency), +m_offset(offset), +m_range(range), +m_latitude(latitude), +m_longitude(longitude), +m_agl(agl), +m_description1(description1), +m_description2(description2), +m_url(url), +m_band1(band1), +m_band2(band2), +m_band3(band3), +m_repeaterId(0x00U), +m_busyId(0x00U), +m_watchdogTimer(1000U, REPEATER_TIMEOUT), +m_ddMode(false), +m_ddCallsign(), +m_queryTimer(1000U, 5U), // 5 seconds +m_myCall1(), +m_myCall2(), +m_yourCall(), +m_rptCall1(), +m_rptCall2(), +m_flag1(0x00U), +m_flag2(0x00U), +m_flag3(0x00U), +m_restricted(false), +m_frames(0U), +m_silence(0U), +m_errors(0U), +m_textCollector(), +m_text(), +m_xBandRptr(NULL), +m_starNet(NULL), +m_g2Status(G2_NONE), +m_g2User(), +m_g2Repeater(), +m_g2Gateway(), +m_g2Header(NULL), +m_g2Address(), +m_linkStatus(LS_NONE), +m_linkRepeater(), +m_linkGateway(), +m_linkReconnect(reconnect), +m_linkAtStartup(atStartup), +m_linkStartup(reflector), +m_linkReconnectTimer(1000U), +m_linkRelink(false), +m_echo(NULL), +m_infoAudio(NULL), +m_infoNeeded(false), +m_msgAudio(NULL), +m_msgNeeded(false), +m_wxAudio(NULL), +m_wxNeeded(false), +m_version(NULL), +m_drats(NULL), +m_dtmf(), +m_pollTimer(1000U, 900U), // 15 minutes +m_ccsHandler(NULL), +m_lastReflector(), +m_heardUser(), +m_heardRepeater(), +m_heardTimer(1000U, 0U, 100U) // 100ms +{ + wxASSERT(!callsign.IsEmpty()); + wxASSERT(port > 0U); + wxASSERT(handler != NULL); + + m_ddMode = band.Len() > 1U; + + m_band = band.GetChar(0U); + + m_rptCallsign = callsign; + m_rptCallsign.Append(wxT(" ")); + m_rptCallsign.Truncate(LONG_CALLSIGN_LENGTH - 1U); + m_rptCallsign.Append(band); + m_rptCallsign.Truncate(LONG_CALLSIGN_LENGTH); + + m_gwyCallsign = callsign; + m_gwyCallsign.Append(wxT(" ")); + m_gwyCallsign.Truncate(LONG_CALLSIGN_LENGTH - 1U); + m_gwyCallsign.Append(wxT("G")); + + m_address.s_addr = ::inet_addr(address.mb_str()); + + m_pollTimer.start(); + + switch (m_linkReconnect) { + case RECONNECT_5MINS: + m_linkReconnectTimer.start(5U * 60U); + break; + case RECONNECT_10MINS: + m_linkReconnectTimer.start(10U * 60U); + break; + case RECONNECT_15MINS: + m_linkReconnectTimer.start(15U * 60U); + break; + case RECONNECT_20MINS: + m_linkReconnectTimer.start(20U * 60U); + break; + case RECONNECT_25MINS: + m_linkReconnectTimer.start(25U * 60U); + break; + case RECONNECT_30MINS: + m_linkReconnectTimer.start(30U * 60U); + break; + case RECONNECT_60MINS: + m_linkReconnectTimer.start(60U * 60U); + break; + case RECONNECT_90MINS: + m_linkReconnectTimer.start(90U * 60U); + break; + case RECONNECT_120MINS: + m_linkReconnectTimer.start(120U * 60U); + break; + case RECONNECT_180MINS: + m_linkReconnectTimer.start(180U * 60U); + break; + default: + break; + } + + wxFileName messageFile; + messageFile.SetPath(::wxGetHomeDir()); + messageFile.SetName(wxT("message")); + messageFile.SetExt(wxT("dvtool")); + + wxFileName weatherFile; + weatherFile.SetPath(::wxGetHomeDir()); + weatherFile.SetName(wxT("weather")); + weatherFile.SetExt(wxT("dvtool")); + + m_echo = new CEchoUnit(this, callsign); + m_infoAudio = new CAudioUnit(this, callsign); + m_msgAudio = new CAnnouncementUnit(this, callsign, messageFile.GetFullPath(), wxT("MSG")); + m_wxAudio = new CAnnouncementUnit(this, callsign, weatherFile.GetFullPath(), wxT("WX")); + m_version = new CVersionUnit(this, callsign); + + if (dratsEnabled) { + m_drats = new CDRATSServer(m_localAddress, port, callsign, this); + bool ret = m_drats->open(); + if (!ret) { + delete m_drats; + m_drats = NULL; + } + } +} + +CRepeaterHandler::~CRepeaterHandler() +{ + delete m_echo; + delete m_infoAudio; + delete m_msgAudio; + delete m_wxAudio; + delete m_version; + + if (m_drats != NULL) + m_drats->close(); +} + +void CRepeaterHandler::initialise(unsigned int maxRepeaters) +{ + wxASSERT(maxRepeaters > 0U); + + m_maxRepeaters = maxRepeaters; + + m_repeaters = new CRepeaterHandler*[m_maxRepeaters]; + for (unsigned int i = 0U; i < m_maxRepeaters; i++) + m_repeaters[i] = NULL; +} + +void CRepeaterHandler::setIndex(unsigned int index) +{ + m_index = index; +} + +void CRepeaterHandler::add(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) +{ + wxASSERT(!callsign.IsEmpty()); + wxASSERT(port > 0U); + wxASSERT(handler != NULL); + + CRepeaterHandler* repeater = new CRepeaterHandler(callsign, band, address, port, hwType, reflector, atStartup, reconnect, dratsEnabled, frequency, offset, range, latitude, longitude, agl, description1, description2, url, handler, band1, band2, band3); + + for (unsigned int i = 0U; i < m_maxRepeaters; i++) { + if (m_repeaters[i] == NULL) { + repeater->setIndex(i); + m_repeaters[i] = repeater; + return; + } + } + + wxLogError(wxT("Cannot add repeater with callsign %s, no space"), callsign.c_str()); + + delete repeater; +} + +void CRepeaterHandler::setG2Handler(CG2ProtocolHandler* handler) +{ + wxASSERT(handler != NULL); + + m_g2Handler = handler; +} + +void CRepeaterHandler::setCache(CCacheManager* cache) +{ + wxASSERT(cache != NULL); + + m_cache = cache; +} + +void CRepeaterHandler::setIRC(CIRCDDB* irc) +{ + wxASSERT(irc != NULL); + + m_irc = irc; +} + +void CRepeaterHandler::setGateway(const wxString& gateway) +{ + m_gateway = gateway; +} + +void CRepeaterHandler::setLanguage(TEXT_LANG language) +{ + m_language = language; +} + +void CRepeaterHandler::setDExtraEnabled(bool enabled) +{ + m_dextraEnabled = enabled; +} + +void CRepeaterHandler::setDPlusEnabled(bool enabled) +{ + m_dplusEnabled = enabled; +} + +void CRepeaterHandler::setDCSEnabled(bool enabled) +{ + m_dcsEnabled = enabled; +} + +void CRepeaterHandler::setInfoEnabled(bool enabled) +{ + m_infoEnabled = enabled; +} + +void CRepeaterHandler::setEchoEnabled(bool enabled) +{ + m_echoEnabled = enabled; +} + +void CRepeaterHandler::setDTMFEnabled(bool enabled) +{ + m_dtmfEnabled = enabled; +} + +void CRepeaterHandler::setHeaderLogger(CHeaderLogger* logger) +{ + m_headerLogger = logger; +} + +void CRepeaterHandler::setAPRSWriter(CAPRSWriter* writer) +{ + m_aprsWriter = writer; +} + +void CRepeaterHandler::setLocalAddress(const wxString& address) +{ + m_localAddress = address; +} + +void CRepeaterHandler::setRestrictList(CCallsignList* list) +{ + wxASSERT(list != NULL); + + m_restrictList = list; +} + +bool CRepeaterHandler::getRepeater(unsigned int n, wxString& callsign, LINK_STATUS& linkStatus, wxString& linkCallsign) +{ + if (n >= m_maxRepeaters) + return false; + + if (m_repeaters[n] == NULL) + return false; + + callsign = m_repeaters[n]->m_rptCallsign; + linkStatus = m_repeaters[n]->m_linkStatus; + linkCallsign = m_repeaters[n]->m_linkRepeater; + + return true; +} + +void CRepeaterHandler::resolveUser(const wxString &user, const wxString& repeater, const wxString& gateway, const wxString &address) +{ + for (unsigned int i = 0U; i < m_maxRepeaters; i++) { + if (m_repeaters[i] != NULL) + m_repeaters[i]->resolveUserInt(user, repeater, gateway, address); + } +} + +void CRepeaterHandler::resolveRepeater(const wxString& repeater, const wxString& gateway, const wxString &address, DSTAR_PROTOCOL protocol) +{ + for (unsigned int i = 0U; i < m_maxRepeaters; i++) { + if (m_repeaters[i] != NULL) + m_repeaters[i]->resolveRepeaterInt(repeater, gateway, address, protocol); + } +} + +void CRepeaterHandler::startup() +{ + for (unsigned int i = 0U; i < m_maxRepeaters; i++) { + if (m_repeaters[i] != NULL) + m_repeaters[i]->startupInt(); + } +} + +void CRepeaterHandler::clock(unsigned int ms) +{ + for (unsigned int i = 0U; i < m_maxRepeaters; i++) { + if (m_repeaters[i] != NULL) + m_repeaters[i]->clockInt(ms); + } +} + +void CRepeaterHandler::finalise() +{ + for (unsigned int i = 0U; i < m_maxRepeaters; i++) { + delete m_repeaters[i]; + m_repeaters[i] = NULL; + } + + if (m_aprsWriter != NULL) { + m_aprsWriter->close(); + delete m_aprsWriter; + } + + delete[] m_repeaters; +} + +CRepeaterHandler* CRepeaterHandler::findDVRepeater(const CHeaderData& header) +{ + wxString rpt1 = header.getRptCall1(); + in_addr address = header.getYourAddress(); + + for (unsigned int i = 0U; i < m_maxRepeaters; i++) { + CRepeaterHandler* repeater = m_repeaters[i]; + if (repeater != NULL) { + if (!repeater->m_ddMode && repeater->m_address.s_addr == address.s_addr && repeater->m_rptCallsign.IsSameAs(rpt1)) + return repeater; + } + } + + return NULL; +} + +CRepeaterHandler* CRepeaterHandler::findDVRepeater(const CAMBEData& data, bool busy) +{ + unsigned int id = data.getId(); + + for (unsigned int i = 0U; i < m_maxRepeaters; i++) { + CRepeaterHandler* repeater = m_repeaters[i]; + if (repeater != NULL) { + if (!busy && !repeater->m_ddMode && repeater->m_repeaterId == id) + return repeater; + if (busy && !repeater->m_ddMode && repeater->m_busyId == id) + return repeater; + } + } + + return NULL; +} + +CRepeaterHandler* CRepeaterHandler::findDVRepeater(const wxString& callsign) +{ + for (unsigned int i = 0U; i < m_maxRepeaters; i++) { + CRepeaterHandler* repeater = m_repeaters[i]; + if (repeater != NULL) { + if (!repeater->m_ddMode && repeater->m_rptCallsign.IsSameAs(callsign)) + return repeater; + } + } + + return NULL; +} + +CRepeaterHandler* CRepeaterHandler::findRepeater(const CPollData& data) +{ + in_addr address = data.getYourAddress(); + unsigned int port = data.getYourPort(); + + for (unsigned int i = 0U; i < m_maxRepeaters; i++) { + CRepeaterHandler* repeater = m_repeaters[i]; + if (repeater != NULL) { + if (repeater->m_address.s_addr == address.s_addr && repeater->m_port == port) + return repeater; + } + } + + return NULL; +} + +CRepeaterHandler* CRepeaterHandler::findDDRepeater(const CDDData& data) +{ + wxString rpt1 = data.getRptCall1(); + + for (unsigned int i = 0U; i < m_maxRepeaters; i++) { + CRepeaterHandler* repeater = m_repeaters[i]; + if (repeater != NULL) { + if (repeater->m_ddMode && repeater->m_rptCallsign.IsSameAs(rpt1)) + return repeater; + } + } + + return NULL; +} + +CRepeaterHandler* CRepeaterHandler::findDDRepeater() +{ + for (unsigned int i = 0U; i < m_maxRepeaters; i++) { + CRepeaterHandler* repeater = m_repeaters[i]; + if (repeater != NULL) { + if (repeater->m_ddMode) + return repeater; + } + } + + return NULL; +} + +wxArrayString CRepeaterHandler::listDVRepeaters() +{ + wxArrayString repeaters; + + for (unsigned int i = 0U; i < m_maxRepeaters; i++) { + CRepeaterHandler* repeater = m_repeaters[i]; + if (repeater != NULL && !repeater->m_ddMode) + repeaters.Add(repeater->m_rptCallsign); + } + + return repeaters; +} + +void CRepeaterHandler::pollAllIcom(CPollData& data) +{ + for (unsigned int i = 0U; i < m_maxRepeaters; i++) { + CRepeaterHandler* repeater = m_repeaters[i]; + if (repeater != NULL && repeater->m_hwType == HW_ICOM) + repeater->processRepeater(data); + } +} + +CRemoteRepeaterData* CRepeaterHandler::getInfo() const +{ + return new CRemoteRepeaterData(m_rptCallsign, m_linkReconnect, m_linkStartup); +} + +void CRepeaterHandler::processRepeater(CHeaderData& header) +{ + unsigned int id = header.getId(); + + // Stop duplicate headers + if (id == m_repeaterId) + return; + + // Save the header fields + m_myCall1 = header.getMyCall1(); + m_myCall2 = header.getMyCall2(); + m_yourCall = header.getYourCall(); + m_rptCall1 = header.getRptCall1(); + m_rptCall2 = header.getRptCall2(); + m_flag1 = header.getFlag1(); + m_flag2 = header.getFlag2(); + m_flag3 = header.getFlag3(); + + if (m_hwType == HW_ICOM) { + unsigned char band1 = header.getBand1(); + unsigned char band2 = header.getBand2(); + unsigned char band3 = header.getBand3(); + + if (m_band1 != band1 || m_band2 != band2 || m_band3 != band3) { + m_band1 = band1; + m_band2 = band2; + m_band3 = band3; + wxLogMessage(wxT("Repeater %s registered with bands %u %u %u"), m_rptCall1.c_str(), m_band1, m_band2, m_band3); + } + } + + if (m_flag1 == 0x01) { + wxLogMessage(wxT("Received a busy message from repeater %s"), m_rptCall1.c_str()); + return; + } + + 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(); + + if (m_drats != NULL) + m_drats->writeHeader(header); + + // Reset the statistics + m_frames = 0U; + m_silence = 0U; + m_errors = 0U; + + // An RF header resets the reconnect timer + m_linkReconnectTimer.start(); + + // Incoming links get everything + sendToIncoming(header); + + // Reset the slow data text collector + m_textCollector.reset(); + m_text.Clear(); + + // Reset the APRS Writer if it's enabled + if (m_aprsWriter != NULL) + m_aprsWriter->reset(m_rptCallsign); + + // Write to Header.log if it's enabled + if (m_headerLogger != NULL) + m_headerLogger->write(wxT("Repeater"), header); + + // Reset the DTMF decoder + m_dtmf.reset(); + + // Reset the info, echo and version commands if they're running + m_infoAudio->cancel(); + m_msgAudio->cancel(); + m_wxAudio->cancel(); + m_echo->cancel(); + m_version->cancel(); + + // A new header resets fields and G2 routing status + m_repeaterId = id; + m_busyId = 0x00U; + m_watchdogTimer.start(); + + m_xBandRptr = NULL; + m_starNet = NULL; + + // If we're querying for a user or repeater, kill the query timer + if (m_g2Status == G2_USER || m_g2Status == G2_REPEATER) + m_queryTimer.stop(); + + delete m_g2Header; + m_g2Header = NULL; + m_g2Status = G2_NONE; + m_g2User.Clear(); + m_g2Repeater.Clear(); + m_g2Gateway.Clear(); + + // Check if this user is restricted + m_restricted = false; + if (m_restrictList != NULL) { + bool res = m_restrictList->isInList(m_myCall1); + if (res) + m_restricted = true; + } + + // Reject silly RPT2 values + if (m_rptCall2.IsSameAs(m_rptCallsign) || m_rptCall2.IsSameAs(wxT(" "))) + return; + + // Do cross-band routing if RPT2 is not one of the gateway callsigns + if (!m_rptCall2.IsSameAs(m_gwyCallsign) && !m_rptCall2.IsSameAs(m_gateway)) { + CRepeaterHandler* repeater = findDVRepeater(m_rptCall2); + if (repeater != NULL) { + wxLogMessage(wxT("Cross-band routing by %s from %s to %s"), m_myCall1.c_str(), m_rptCallsign.c_str(), m_rptCall2.c_str()); + m_xBandRptr = repeater; + m_xBandRptr->process(header, DIR_INCOMING, AS_XBAND); + m_g2Status = G2_XBAND; + } else { + // Keep the transmission local + wxLogMessage(wxT("Invalid cross-band route by %s from %s to %s"), m_myCall1.c_str(), m_rptCallsign.c_str(), m_rptCall2.c_str()); + m_g2Status = G2_LOCAL; + } + return; + } + + m_starNet = CStarNetHandler::findStarNet(header); + if (m_starNet != NULL && !m_restricted) { + wxLogMessage(wxT("StarNet routing by %s to %s"), m_myCall1.c_str(), m_yourCall.c_str()); + m_starNet->process(header); + m_g2Status = G2_STARNET; + return; + } + + // Reject simple cases + if (m_yourCall.Left(4).IsSameAs(wxT("CQCQ"))) { + sendToOutgoing(header); + return; + } + + // Handle the Echo command + if (m_echoEnabled && m_yourCall.IsSameAs(wxT(" E"))) { + m_g2Status = G2_ECHO; + m_echo->writeHeader(header); + return; + } + + // Handle the Info command + if (m_infoEnabled && m_yourCall.IsSameAs(wxT(" I"))) { + m_g2Status = G2_LOCAL; + m_infoNeeded = true; + return; + } + + // Handle the MSG command + if (m_infoEnabled && m_yourCall.IsSameAs(wxT(" M"))) { + m_g2Status = G2_LOCAL; + m_msgNeeded = true; + return; + } + + // Handle the WX command + if (m_infoEnabled && m_yourCall.IsSameAs(wxT(" W"))) { + m_g2Status = G2_LOCAL; + m_wxNeeded = true; + return; + } + + // Handle the Version command + if (m_infoEnabled && m_yourCall.IsSameAs(wxT(" V"))) { + m_g2Status = G2_VERSION; + sendToOutgoing(header); + return; + } + + if (m_restricted) { + sendToOutgoing(header); + return; + } + + 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); + } + } +} + +void CRepeaterHandler::processRepeater(CAMBEData& data) +{ + // AMBE data via RF resets the reconnect timer + m_linkReconnectTimer.start(); + m_watchdogTimer.start(); + + m_frames++; + m_errors += data.getErrors(); + + unsigned char buffer[DV_FRAME_MAX_LENGTH_BYTES]; + data.getData(buffer, DV_FRAME_MAX_LENGTH_BYTES); + + if (::memcmp(buffer, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES) == 0) + m_silence++; + + // Don't do DTMF decoding or blanking if off and not on crossband either + if (m_dtmfEnabled && m_g2Status != G2_XBAND) { + bool pressed = m_dtmf.decode(buffer, data.isEnd()); + if (pressed) { + // Replace the DTMF with silence + ::memcpy(buffer, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + data.setData(buffer, DV_FRAME_LENGTH_BYTES); + } + + bool dtmfDone = m_dtmf.hasCommand(); + if (dtmfDone) { + wxString command = m_dtmf.translate(); + + // Only process the DTMF command if the your call is CQCQCQ and not a restricted user + 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 { + reflectorCommandHandler(command, m_myCall1, wxT("DTMF")); + } + } + } + } + + // Incoming links get everything + sendToIncoming(data); + + // CCS gets everything + m_ccsHandler->writeAMBE(data); + + if (m_drats != NULL) + m_drats->writeData(data); + + if (m_aprsWriter != NULL) + m_aprsWriter->writeData(m_rptCallsign, data); + + if (m_text.IsEmpty() && !data.isEnd()) { + m_textCollector.writeData(data); + + bool hasText = m_textCollector.hasData(); + if (hasText) { + m_text = m_textCollector.getData(); + sendHeard(m_text); + } + } + + data.setText(m_text); + + // If no slow data text has been received, send a heard with no text when the end of the + // transmission arrives + if (data.isEnd() && m_text.IsEmpty()) + sendHeard(); + + // Send the statistics after the end of the data, any stats from the repeater should have + // been received by now + if (data.isEnd()) { + m_watchdogTimer.stop(); + sendStats(); + } + + switch (m_g2Status) { + case G2_LOCAL: + if (data.isEnd()) { + m_repeaterId = 0x00U; + m_g2Status = G2_NONE; + } + break; + + case G2_OK: + data.setDestination(m_g2Address, G2_DV_PORT); + m_g2Handler->writeAMBE(data); + + if (data.isEnd()) { + m_repeaterId = 0x00U; + m_g2Status = G2_NONE; + } + break; + + case G2_USER: + case G2_REPEATER: + // Data ended before the callsign could be resolved + if (data.isEnd()) { + m_queryTimer.stop(); + delete m_g2Header; + m_repeaterId = 0x0U; + m_g2Status = G2_NONE; + m_g2Header = NULL; + } + break; + + case G2_NONE: + if (data.isEnd()) + m_repeaterId = 0x00U; + + sendToOutgoing(data); + break; + + case G2_XBAND: + m_xBandRptr->process(data, DIR_INCOMING, AS_XBAND); + + if (data.isEnd()) { + m_repeaterId = 0x00U; + m_g2Status = G2_NONE; + m_xBandRptr = NULL; + } + break; + + case G2_STARNET: + m_starNet->process(data); + + if (data.isEnd()) { + m_repeaterId = 0x00U; + m_g2Status = G2_NONE; + m_starNet = NULL; + } + break; + + case G2_ECHO: + m_echo->writeData(data); + + if (data.isEnd()) { + m_repeaterId = 0x00U; + m_g2Status = G2_NONE; + } + break; + + case G2_VERSION: + sendToOutgoing(data); + + if (data.isEnd()) { + m_version->sendVersion(); + + m_repeaterId = 0x00U; + m_g2Status = G2_NONE; + } + break; + } + + if (data.isEnd() && m_infoNeeded) { + m_infoAudio->sendStatus(); + m_infoNeeded = false; + } + + if (data.isEnd() && m_msgNeeded) { + m_msgAudio->sendAnnouncement(); + m_msgNeeded = false; + } + + if (data.isEnd() && m_wxNeeded) { + m_wxAudio->sendAnnouncement(); + m_wxNeeded = false; + } +} + +// Incoming headers when relaying network traffic, as detected by the repeater, will be used as a command +// to the reflector command handler, probably to do an unlink. +void CRepeaterHandler::processBusy(CHeaderData& header) +{ + unsigned int id = header.getId(); + + // Ignore duplicate headers + if (id == m_busyId) + return; + + wxString rptCall1 = header.getRptCall1(); + wxString rptCall2 = header.getRptCall2(); + + if (m_hwType == HW_ICOM) { + unsigned char band1 = header.getBand1(); + unsigned char band2 = header.getBand2(); + unsigned char band3 = header.getBand3(); + + if (m_band1 != band1 || m_band2 != band2 || m_band3 != band3) { + m_band1 = band1; + m_band2 = band2; + m_band3 = band3; + wxLogMessage(wxT("Repeater %s registered with bands %u %u %u"), rptCall1.c_str(), m_band1, m_band2, m_band3); + } + } + + if (header.getFlag1() == 0x01) { + wxLogMessage(wxT("Received a busy message from repeater %s"), rptCall1.c_str()); + return; + } + + // Reject the header if the RPT2 value is not one of the gateway callsigns + if (!rptCall2.IsSameAs(m_gwyCallsign) && !rptCall2.IsSameAs(m_gateway)) + return; + + m_myCall1 = header.getMyCall1(); + m_yourCall = header.getYourCall(); + m_rptCall1 = rptCall1; + m_rptCall2 = rptCall2; + + m_dtmf.reset(); + + m_busyId = id; + m_repeaterId = 0x00U; + m_watchdogTimer.start(); + + // If restricted then don't send to the command handler + m_restricted = false; + if (m_restrictList != NULL) { + bool res = m_restrictList->isInList(m_myCall1); + if (res) { + m_restricted = true; + return; + } + } + + // Reject simple cases + if (m_yourCall.Left(4).IsSameAs(wxT("CQCQ")) || m_yourCall.IsSameAs(wxT(" E")) || m_yourCall.IsSameAs(wxT(" I"))) + return; + + 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) +{ + m_watchdogTimer.start(); + + unsigned char buffer[DV_FRAME_MAX_LENGTH_BYTES]; + data.getData(buffer, DV_FRAME_MAX_LENGTH_BYTES); + + // Don't do DTMF decoding if off + if (m_dtmfEnabled) { + m_dtmf.decode(buffer, data.isEnd()); + + bool dtmfDone = m_dtmf.hasCommand(); + if (dtmfDone) { + wxString command = m_dtmf.translate(); + + // Only process the DTMF command if the your call is CQCQCQ and the user isn't restricted + 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 { + reflectorCommandHandler(command, m_myCall1, wxT("background DTMF")); + } + } + } + } + + if (data.isEnd()) { + if (m_infoNeeded) { + m_infoAudio->sendStatus(); + m_infoNeeded = false; + } + + if (m_msgNeeded) { + m_msgAudio->sendAnnouncement(); + m_msgNeeded = false; + } + + if (m_wxNeeded) { + m_wxAudio->sendAnnouncement(); + m_wxNeeded = false; + } + + if (m_g2Status == G2_VERSION) + m_version->sendVersion(); + + m_g2Status = G2_NONE; + m_busyId = 0x00U; + m_watchdogTimer.stop(); + } +} + +void CRepeaterHandler::processRepeater(CHeardData& heard) +{ + if (m_irc == NULL) + return; + + // A second heard has come in before the original has been sent or cancelled + if (m_heardTimer.isRunning() && !m_heardTimer.hasExpired()) + m_irc->sendHeard(m_heardUser, wxT(" "), wxT(" "), m_heardRepeater, wxT(" "), 0x00U, 0x00U, 0x00U); + + m_heardUser = heard.getUser(); + m_heardRepeater = heard.getRepeater(); + + m_heardTimer.start(); +} + +void CRepeaterHandler::processRepeater(CPollData& data) +{ + if (!m_pollTimer.hasExpired()) + return; + + if (m_irc == NULL) + return; + + wxString callsign = m_rptCallsign; + if (m_ddMode) + callsign.Append(wxT("D")); + + wxString text = data.getData1(); + + m_irc->kickWatchdog(callsign, text); + + m_pollTimer.start(); +} + +void CRepeaterHandler::processRepeater(CDDData& data) +{ + if (!m_ddMode) + return; + + if (m_ddCallsign.IsEmpty()) { + m_ddCallsign = data.getYourCall(); + wxLogMessage(wxT("Added DD callsign %s"), m_ddCallsign.c_str()); + } + + CDDHandler::process(data); +} + +bool CRepeaterHandler::process(CDDData& data) +{ + unsigned char* address = data.getDestinationAddress(); + if (::memcmp(address, ETHERNET_BROADCAST_ADDRESS, ETHERNET_ADDRESS_LENGTH) == 0) + data.setRepeaters(m_gwyCallsign, wxT(" ")); + else if (::memcmp(address, TOALL_MULTICAST_ADDRESS, ETHERNET_ADDRESS_LENGTH) == 0) + data.setRepeaters(m_gwyCallsign, m_rptCallsign); + else if (::memcmp(address, DX_MULTICAST_ADDRESS, ETHERNET_ADDRESS_LENGTH) == 0) + data.setRepeaters(m_gwyCallsign, m_rptCallsign); + else + data.setRepeaters(m_gwyCallsign, m_rptCallsign); + + data.setDestination(m_address, m_port); + data.setFlags(0xC0U, 0x00U, 0x00U); + data.setMyCall1(m_ddCallsign); + data.setMyCall2(wxT(" ")); + + m_repeaterHandler->writeDD(data); + + return true; +} + +bool CRepeaterHandler::process(CHeaderData& header, DIRECTION, AUDIO_SOURCE source) +{ + // If data is coming from the repeater then don't send + if (m_repeaterId != 0x00U) + return false; + + // Rewrite the ID if we're using Icom hardware + if (m_hwType == HW_ICOM) { + unsigned int id1 = header.getId(); + unsigned int id2 = id1 + m_index; + header.setId(id2); + } + + // Send all original headers to all repeater types, and only send duplicate headers to homebrew repeaters + if (source != AS_DUP || (source == AS_DUP && m_hwType == HW_HOMEBREW)) { + header.setBand1(m_band1); + header.setBand2(m_band2); + header.setBand3(m_band3); + header.setDestination(m_address, m_port); + header.setRepeaters(m_gwyCallsign, m_rptCallsign); + + m_repeaterHandler->writeHeader(header); + } + + // Don't send duplicate headers to anyone else + if (source == AS_DUP) + return true; + + 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; + + // Reset the slow data text collector, used for DCS text passing + m_textCollector.reset(); + m_text.Clear(); + + sendToOutgoing(header); + + return true; +} + +bool CRepeaterHandler::process(CAMBEData& data, DIRECTION, AUDIO_SOURCE source) +{ + // If data is coming from the repeater then don't send + if (m_repeaterId != 0x00U) + return false; + + // Rewrite the ID if we're using Icom hardware + if (m_hwType == HW_ICOM) { + unsigned int id = data.getId(); + id += m_index; + data.setId(id); + } + + data.setBand1(m_band1); + data.setBand2(m_band2); + data.setBand3(m_band3); + data.setDestination(m_address, m_port); + + m_repeaterHandler->writeAMBE(data); + + 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; + + // Collect the text from the slow data for DCS + if (m_text.IsEmpty() && !data.isEnd()) { + m_textCollector.writeData(data); + + bool hasText = m_textCollector.hasData(); + if (hasText) + m_text = m_textCollector.getData(); + } + + data.setText(m_text); + + sendToOutgoing(data); + + return true; +} + +void CRepeaterHandler::resolveUserInt(const wxString& user, const wxString& repeater, const wxString& gateway, const wxString &address) +{ + if (m_g2Status == G2_USER && m_g2User.IsSameAs(user)) { + m_queryTimer.stop(); + + if (!address.IsEmpty()) { + // No point routing to self + if (repeater.IsSameAs(m_rptCallsign)) { + m_g2Status = G2_LOCAL; + delete m_g2Header; + m_g2Header = NULL; + return; + } + + // User found, update the settings and send the header to the correct place + m_g2Address.s_addr = ::inet_addr(address.mb_str()); + + m_g2Repeater = repeater; + m_g2Gateway = gateway; + + m_g2Header->setDestination(m_g2Address, G2_DV_PORT); + m_g2Header->setRepeaters(m_g2Gateway, m_g2Repeater); + m_g2Handler->writeHeader(*m_g2Header); + + delete m_g2Header; + m_g2Status = G2_OK; + m_g2Header = NULL; + } else { + // User not found, remove G2 settings + m_g2Status = G2_LOCAL; + m_g2User.Clear(); + m_g2Repeater.Clear(); + m_g2Gateway.Clear(); + + delete m_g2Header; + m_g2Header = NULL; + } + } +} + +void CRepeaterHandler::resolveRepeaterInt(const wxString& repeater, const wxString& gateway, const wxString &address, DSTAR_PROTOCOL protocol) +{ + if (m_g2Status == G2_REPEATER && m_g2Repeater.IsSameAs(repeater)) { + m_queryTimer.stop(); + + if (!address.IsEmpty()) { + // Repeater found, update the settings and send the header to the correct place + m_g2Address.s_addr = ::inet_addr(address.mb_str()); + + m_g2Repeater = repeater; + m_g2Gateway = gateway; + + m_g2Header->setDestination(m_g2Address, G2_DV_PORT); + m_g2Header->setRepeaters(m_g2Gateway, m_g2Repeater); + m_g2Handler->writeHeader(*m_g2Header); + + delete m_g2Header; + m_g2Status = G2_OK; + m_g2Header = NULL; + } else { + // Repeater not found, remove G2 settings + m_g2Status = G2_LOCAL; + m_g2User.Clear(); + m_g2Repeater.Clear(); + m_g2Gateway.Clear(); + + delete m_g2Header; + m_g2Header = NULL; + } + } + + if (m_linkStatus == LS_PENDING_IRCDDB && m_linkRepeater.IsSameAs(repeater)) { + m_queryTimer.stop(); + + if (!address.IsEmpty()) { + // Repeater found + in_addr addr; + switch (protocol) { + case DP_DPLUS: + if (m_dplusEnabled) { + m_linkGateway = gateway; + addr.s_addr = ::inet_addr(address.mb_str()); + CDPlusHandler::link(this, m_rptCallsign, m_linkRepeater, addr); + m_linkStatus = LS_LINKING_DPLUS; + } else { + wxLogMessage(wxT("Require D-Plus for linking to %s, but D-Plus is disabled"), repeater.c_str()); + m_linkStatus = LS_NONE; + m_linkRepeater.Clear(); + m_linkGateway.Clear(); + writeNotLinked(); + triggerInfo(); + } + break; + + case DP_DCS: + if (m_dcsEnabled) { + m_linkGateway = gateway; + addr.s_addr = ::inet_addr(address.mb_str()); + CDCSHandler::link(this, m_rptCallsign, m_linkRepeater, addr); + m_linkStatus = LS_LINKING_DCS; + } else { + wxLogMessage(wxT("Require DCS for linking to %s, but DCS is disabled"), repeater.c_str()); + m_linkStatus = LS_NONE; + m_linkRepeater.Clear(); + m_linkGateway.Clear(); + writeNotLinked(); + triggerInfo(); + } + break; + + case DP_LOOPBACK: + m_linkGateway = gateway; + addr.s_addr = ::inet_addr(address.mb_str()); + CDCSHandler::link(this, m_rptCallsign, m_linkRepeater, addr); + m_linkStatus = LS_LINKING_LOOPBACK; + break; + + default: + if (m_dextraEnabled) { + m_linkGateway = gateway; + addr.s_addr = ::inet_addr(address.mb_str()); + CDExtraHandler::link(this, m_rptCallsign, m_linkRepeater, addr); + m_linkStatus = LS_LINKING_DEXTRA; + } else { + wxLogMessage(wxT("Require DExtra for linking to %s, but DExtra is disabled"), repeater.c_str()); + m_linkStatus = LS_NONE; + m_linkRepeater.Clear(); + m_linkGateway.Clear(); + writeNotLinked(); + triggerInfo(); + } + break; + + } + } else { + // Repeater not found + m_linkStatus = LS_NONE; + m_linkRepeater.Clear(); + m_linkGateway.Clear(); + + writeNotLinked(); + triggerInfo(); + } + } +} + +void CRepeaterHandler::clockInt(unsigned int ms) +{ + m_infoAudio->clock(ms); + m_msgAudio->clock(ms); + m_wxAudio->clock(ms); + m_echo->clock(ms); + m_version->clock(ms); + + m_linkReconnectTimer.clock(ms); + m_watchdogTimer.clock(ms); + m_queryTimer.clock(ms); + m_heardTimer.clock(ms); + m_pollTimer.clock(ms); + + // If the reconnect timer has expired + if (m_linkReconnectTimer.isRunning() && m_linkReconnectTimer.hasExpired()) { + if (m_linkStatus != LS_NONE && (m_linkStartup.IsEmpty() || m_linkStartup.IsSameAs(wxT(" ")))) { + // Unlink if linked to something + wxLogMessage(wxT("Reconnect timer has expired, unlinking %s from %s"), m_rptCallsign.c_str(), m_linkRepeater.c_str()); + + CDExtraHandler::unlink(this); + CDPlusHandler::unlink(this); + CDCSHandler::unlink(this); + + m_linkStatus = LS_NONE; + m_linkRepeater.Clear(); + + // Tell the users + writeNotLinked(); + triggerInfo(); + } else if ((m_linkStatus == LS_NONE && !m_linkStartup.IsEmpty() && !m_linkStartup.IsSameAs(wxT(" "))) || + (m_linkStatus != LS_NONE && !m_linkRepeater.IsSameAs(m_linkStartup))) { + // Relink if not linked or linked to the wrong reflector + wxLogMessage(wxT("Reconnect timer has expired, relinking %s to %s"), m_rptCallsign.c_str(), m_linkStartup.c_str()); + + // Check for just a change of letter + if (m_linkStatus != LS_NONE) { + wxString oldCall = m_linkRepeater.Left(LONG_CALLSIGN_LENGTH - 1U); + wxString newCall = m_linkStartup.Left(LONG_CALLSIGN_LENGTH - 1U); + + // Just a change of port? + if (oldCall.IsSameAs(newCall)) { + switch (m_linkStatus) { + case LS_LINKING_DEXTRA: + case LS_LINKED_DEXTRA: + m_linkRelink = true; + m_linkRepeater = m_linkStartup; + CDExtraHandler::unlink(this, m_linkRepeater); + + m_linkStatus = LS_LINKING_DEXTRA; + writeLinkingTo(m_linkRepeater); + triggerInfo(); + break; + + case LS_LINKING_DCS: + case LS_LINKED_DCS: + m_linkRelink = true; + m_linkRepeater = m_linkStartup; + CDCSHandler::unlink(this, m_linkRepeater); + + m_linkStatus = LS_LINKING_DCS; + writeLinkingTo(m_linkRepeater); + triggerInfo(); + break; + + case LS_LINKING_LOOPBACK: + case LS_LINKED_LOOPBACK: + m_linkRelink = true; + m_linkRepeater = m_linkStartup; + CDCSHandler::unlink(this, m_linkRepeater); + + m_linkStatus = LS_LINKING_LOOPBACK; + writeLinkingTo(m_linkRepeater); + triggerInfo(); + break; + + case LS_LINKING_DPLUS: + m_linkRepeater = m_linkStartup; + CDPlusHandler::relink(this, m_linkRepeater); + writeLinkingTo(m_linkRepeater); + triggerInfo(); + break; + + case LS_LINKED_DPLUS: + m_linkRepeater = m_linkStartup; + CDPlusHandler::relink(this, m_linkRepeater); + writeLinkedTo(m_linkRepeater); + triggerInfo(); + break; + + default: + break; + } + + return; + } + } + + CDExtraHandler::unlink(this); + CDPlusHandler::unlink(this); + CDCSHandler::unlink(this); + + linkInt(m_linkStartup); + } + + m_linkReconnectTimer.start(); + } + + // If the ircDDB query timer has expired + if (m_queryTimer.isRunning() && m_queryTimer.hasExpired()) { + m_queryTimer.stop(); + + if (m_g2Status == G2_USER || m_g2Status == G2_REPEATER) { + // User or repeater not found in time, remove G2 settings + wxLogMessage(wxT("ircDDB did not reply within five seconds")); + + m_g2Status = G2_LOCAL; + m_g2User.Clear(); + m_g2Repeater.Clear(); + m_g2Gateway.Clear(); + + delete m_g2Header; + m_g2Header = NULL; + } else if (m_linkStatus == LS_PENDING_IRCDDB) { + // Repeater not found in time + wxLogMessage(wxT("ircDDB did not reply within five seconds")); + + m_linkStatus = LS_NONE; + m_linkRepeater.Clear(); + m_linkGateway.Clear(); + + 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(); + } + } + + // Icom heard timer has expired + if (m_heardTimer.isRunning() && m_heardTimer.hasExpired() && m_irc != NULL) { + m_irc->sendHeard(m_heardUser, wxT(" "), wxT(" "), m_heardRepeater, wxT(" "), 0x00U, 0x00U, 0x00U); + m_heardTimer.stop(); + } + + // If the watchdog timer has expired, clean up + if (m_watchdogTimer.isRunning() && m_watchdogTimer.hasExpired()) { + wxLogMessage(wxT("Radio watchdog timer for %s has expired"), m_rptCallsign.c_str()); + m_watchdogTimer.stop(); + + if (m_repeaterId != 0x00U) { + if (m_text.IsEmpty()) + sendHeard(); + + if (m_drats != NULL) + m_drats->writeEnd(); + + sendStats(); + + switch (m_g2Status) { + case G2_USER: + case G2_REPEATER: + m_queryTimer.stop(); + delete m_g2Header; + m_g2Header = NULL; + break; + + case G2_XBAND: + m_xBandRptr = NULL; + break; + + case G2_STARNET: + m_starNet = NULL; + break; + + case G2_ECHO: + m_echo->end(); + break; + + case G2_VERSION: + m_version->sendVersion(); + break; + + default: + break; + } + + if (m_infoNeeded) { + m_infoAudio->sendStatus(); + m_infoNeeded = false; + } + + if (m_msgNeeded) { + m_msgAudio->sendAnnouncement(); + m_msgNeeded = false; + } + + if (m_wxNeeded) { + m_wxAudio->sendAnnouncement(); + m_wxNeeded = false; + } + + m_repeaterId = 0x00U; + m_g2Status = G2_NONE; + } + + if (m_busyId != 0x00U) { + if (m_infoNeeded) { + m_infoAudio->sendStatus(); + m_infoNeeded = false; + } + + if (m_msgNeeded) { + m_msgAudio->sendAnnouncement(); + m_msgNeeded = false; + } + + if (m_wxNeeded) { + m_wxAudio->sendAnnouncement(); + m_wxNeeded = false; + } + + if (m_g2Status == G2_VERSION) + m_version->sendVersion(); + + m_g2Status = G2_NONE; + m_busyId = 0x00U; + } + } +} + +void CRepeaterHandler::linkUp(DSTAR_PROTOCOL protocol, const wxString& callsign) +{ + if (protocol == DP_DEXTRA && m_linkStatus == LS_LINKING_DEXTRA) { + wxLogMessage(wxT("DExtra link to %s established"), callsign.c_str()); + m_linkStatus = LS_LINKED_DEXTRA; + writeLinkedTo(callsign); + triggerInfo(); + } + + if (protocol == DP_DPLUS && m_linkStatus == LS_LINKING_DPLUS) { + wxLogMessage(wxT("D-Plus link to %s established"), callsign.c_str()); + m_linkStatus = LS_LINKED_DPLUS; + writeLinkedTo(callsign); + triggerInfo(); + } + + if (protocol == DP_DCS && m_linkStatus == LS_LINKING_DCS) { + wxLogMessage(wxT("DCS link to %s established"), callsign.c_str()); + m_linkStatus = LS_LINKED_DCS; + writeLinkedTo(callsign); + triggerInfo(); + } + + if (protocol == DP_DCS && m_linkStatus == LS_LINKING_LOOPBACK) { + wxLogMessage(wxT("Loopback link to %s established"), callsign.c_str()); + m_linkStatus = LS_LINKED_LOOPBACK; + writeLinkedTo(callsign); + triggerInfo(); + } +} + +bool CRepeaterHandler::linkFailed(DSTAR_PROTOCOL protocol, const wxString& callsign, bool isRecoverable) +{ + // Is relink to another module required? + if (!isRecoverable && m_linkRelink) { + m_linkRelink = false; + wxLogMessage(wxT("Relinking %s from %s to %s"), m_rptCallsign.c_str(), callsign.c_str(), m_linkRepeater.c_str()); + linkInt(m_linkRepeater); + return false; + } + + // Have we linked to something else in the meantime? + if (m_linkStatus == LS_NONE || !m_linkRepeater.IsSameAs(callsign)) { + switch (protocol) { + case DP_DCS: + wxLogMessage(wxT("DCS link to %s has failed"), callsign.c_str()); + break; + case DP_DEXTRA: + wxLogMessage(wxT("DExtra link to %s has failed"), callsign.c_str()); + break; + case DP_DPLUS: + wxLogMessage(wxT("D-Plus link to %s has failed"), callsign.c_str()); + break; + default: + break; + } + + return false; + } + + if (!isRecoverable) { + if (protocol == DP_DEXTRA && callsign.IsSameAs(m_linkRepeater)) { + wxLogMessage(wxT("DExtra link to %s has failed"), m_linkRepeater.c_str()); + m_linkRepeater.Clear(); + m_linkStatus = LS_NONE; + writeNotLinked(); + triggerInfo(); + } + + if (protocol == DP_DPLUS && callsign.IsSameAs(m_linkRepeater)) { + wxLogMessage(wxT("D-Plus link to %s has failed"), m_linkRepeater.c_str()); + m_linkRepeater.Clear(); + m_linkStatus = LS_NONE; + writeNotLinked(); + triggerInfo(); + } + + if (protocol == DP_DCS && callsign.IsSameAs(m_linkRepeater)) { + if (m_linkStatus == LS_LINKED_DCS || m_linkStatus == LS_LINKING_DCS) + wxLogMessage(wxT("DCS link to %s has failed"), m_linkRepeater.c_str()); + else + wxLogMessage(wxT("Loopback link to %s has failed"), m_linkRepeater.c_str()); + m_linkRepeater.Clear(); + m_linkStatus = LS_NONE; + writeNotLinked(); + triggerInfo(); + } + + return false; + } + + if (protocol == DP_DEXTRA) { + switch (m_linkStatus) { + case LS_LINKED_DEXTRA: + wxLogMessage(wxT("DExtra link to %s has failed, relinking"), m_linkRepeater.c_str()); + m_linkStatus = LS_LINKING_DEXTRA; + writeLinkingTo(m_linkRepeater); + triggerInfo(); + return true; + + case LS_LINKING_DEXTRA: + return true; + + default: + return false; + } + } + + if (protocol == DP_DPLUS) { + switch (m_linkStatus) { + case LS_LINKED_DPLUS: + wxLogMessage(wxT("D-Plus link to %s has failed, relinking"), m_linkRepeater.c_str()); + m_linkStatus = LS_LINKING_DPLUS; + writeLinkingTo(m_linkRepeater); + triggerInfo(); + return true; + + case LS_LINKING_DPLUS: + return true; + + default: + return false; + } + } + + if (protocol == DP_DCS) { + switch (m_linkStatus) { + case LS_LINKED_DCS: + wxLogMessage(wxT("DCS link to %s has failed, relinking"), m_linkRepeater.c_str()); + m_linkStatus = LS_LINKING_DCS; + writeLinkingTo(m_linkRepeater); + triggerInfo(); + return true; + + case LS_LINKED_LOOPBACK: + wxLogMessage(wxT("Loopback link to %s has failed, relinking"), m_linkRepeater.c_str()); + m_linkStatus = LS_LINKING_LOOPBACK; + writeLinkingTo(m_linkRepeater); + triggerInfo(); + return true; + + case LS_LINKING_DCS: + case LS_LINKING_LOOPBACK: + return true; + + default: + return false; + } + } + + return false; +} + +void CRepeaterHandler::linkRefused(DSTAR_PROTOCOL protocol, const wxString& callsign) +{ + if (protocol == DP_DEXTRA && callsign.IsSameAs(m_linkRepeater)) { + wxLogMessage(wxT("DExtra link to %s was refused"), m_linkRepeater.c_str()); + m_linkRepeater.Clear(); + m_linkStatus = LS_NONE; + writeIsBusy(callsign); + triggerInfo(); + } + + if (protocol == DP_DPLUS && callsign.IsSameAs(m_linkRepeater)) { + wxLogMessage(wxT("D-Plus link to %s was refused"), m_linkRepeater.c_str()); + m_linkRepeater.Clear(); + m_linkStatus = LS_NONE; + writeIsBusy(callsign); + triggerInfo(); + } + + if (protocol == DP_DCS && callsign.IsSameAs(m_linkRepeater)) { + if (m_linkStatus == LS_LINKED_DCS || m_linkStatus == LS_LINKING_DCS) + wxLogMessage(wxT("DCS link to %s was refused"), m_linkRepeater.c_str()); + else + wxLogMessage(wxT("Loopback link to %s was refused"), m_linkRepeater.c_str()); + m_linkRepeater.Clear(); + m_linkStatus = LS_NONE; + writeIsBusy(callsign); + triggerInfo(); + } +} + +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; + + m_linkReconnectTimer.stop(); + + switch (m_linkReconnect) { + case RECONNECT_5MINS: + m_linkReconnectTimer.start(5U * 60U); + break; + case RECONNECT_10MINS: + m_linkReconnectTimer.start(10U * 60U); + break; + case RECONNECT_15MINS: + m_linkReconnectTimer.start(15U * 60U); + break; + case RECONNECT_20MINS: + m_linkReconnectTimer.start(20U * 60U); + break; + case RECONNECT_25MINS: + m_linkReconnectTimer.start(25U * 60U); + break; + case RECONNECT_30MINS: + m_linkReconnectTimer.start(30U * 60U); + break; + case RECONNECT_60MINS: + m_linkReconnectTimer.start(60U * 60U); + break; + case RECONNECT_90MINS: + m_linkReconnectTimer.start(90U * 60U); + break; + case RECONNECT_120MINS: + m_linkReconnectTimer.start(120U * 60U); + break; + case RECONNECT_180MINS: + m_linkReconnectTimer.start(180U * 60U); + break; + default: + break; + } + + // Nothing to do + if ((m_linkStatus != LS_NONE && m_linkRepeater.IsSameAs(reflector)) || + (m_linkStatus == LS_NONE && (reflector.IsEmpty() || reflector.IsSameAs(wxT(" "))))) + return; + + // Handle unlinking + if (m_linkStatus != LS_NONE && (reflector.IsEmpty() || reflector.IsSameAs(wxT(" ")))) { + wxLogMessage(wxT("Unlinking %s from %s"), m_rptCallsign.c_str(), m_linkRepeater.c_str()); + + CDExtraHandler::unlink(this); + CDPlusHandler::unlink(this); + CDCSHandler::unlink(this); + + m_linkStatus = LS_NONE; + m_linkRepeater.Clear(); + + writeNotLinked(); + triggerInfo(); + + return; + } + + wxLogMessage(wxT("Linking %s to %s"), m_rptCallsign.c_str(), reflector.c_str()); + + // Check for just a change of letter + if (m_linkStatus != LS_NONE) { + wxString oldCall = m_linkRepeater.Left(LONG_CALLSIGN_LENGTH - 1U); + wxString newCall = reflector.Left(LONG_CALLSIGN_LENGTH - 1U); + + // Just a change of port? + if (oldCall.IsSameAs(newCall)) { + switch (m_linkStatus) { + case LS_LINKING_DEXTRA: + case LS_LINKED_DEXTRA: + m_linkRelink = true; + m_linkRepeater = reflector; + CDExtraHandler::unlink(this, m_linkRepeater); + + m_linkStatus = LS_LINKING_DEXTRA; + writeLinkingTo(m_linkRepeater); + triggerInfo(); + break; + + case LS_LINKING_DCS: + case LS_LINKED_DCS: + m_linkRelink = true; + m_linkRepeater = reflector; + CDCSHandler::unlink(this, m_linkRepeater); + + m_linkStatus = LS_LINKING_DCS; + writeLinkingTo(m_linkRepeater); + triggerInfo(); + break; + + case LS_LINKING_LOOPBACK: + case LS_LINKED_LOOPBACK: + m_linkRelink = true; + m_linkRepeater = reflector; + CDCSHandler::unlink(this, m_linkRepeater); + + m_linkStatus = LS_LINKING_LOOPBACK; + writeLinkingTo(m_linkRepeater); + triggerInfo(); + break; + + case LS_LINKING_DPLUS: + m_linkRepeater = reflector; + CDPlusHandler::relink(this, m_linkRepeater); + writeLinkingTo(m_linkRepeater); + triggerInfo(); + break; + + case LS_LINKED_DPLUS: + m_linkRepeater = reflector; + CDPlusHandler::relink(this, m_linkRepeater); + writeLinkedTo(m_linkRepeater); + triggerInfo(); + break; + + default: + break; + } + + return; + } + } + + CDExtraHandler::unlink(this); + CDPlusHandler::unlink(this); + CDCSHandler::unlink(this); + + linkInt(m_linkStartup); +} + +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; + } + + switch (protocol) { + case PROTO_DPLUS: + CDPlusHandler::unlink(this, reflector, false); + break; + + case PROTO_DEXTRA: + CDExtraHandler::unlink(this, reflector, false); + break; + + case PROTO_DCS: + CDCSHandler::unlink(this, reflector, false); + break; + + default: + break; + } +} + +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()); + m_g2Status = G2_LOCAL; + return; + } + + // This a repeater route + // Convert "/1234567" to "123456 7" + wxString repeater = callsign.Mid(1, LONG_CALLSIGN_LENGTH - 2U); + repeater.Append(wxT(" ")); + repeater.Append(callsign.Right(1)); + + if (repeater.IsSameAs(m_rptCallsign)) { + wxLogMessage(wxT("%s is trying to G2 route to self, ignoring"), user.c_str()); + m_g2Status = G2_LOCAL; + return; + } + + if (repeater.Left(3U).IsSameAs(wxT("REF")) || repeater.Left(3U).IsSameAs(wxT("XRF")) || repeater.Left(3U).IsSameAs(wxT("DCS"))) { + wxLogMessage(wxT("%s is trying to G2 route to reflector %s, ignoring"), user.c_str(), repeater.c_str()); + m_g2Status = G2_LOCAL; + return; + } + + wxLogMessage(wxT("%s is trying to G2 route to repeater %s"), user.c_str(), repeater.c_str()); + + m_g2Repeater = repeater; + m_g2User = wxT("CQCQCQ "); + + CRepeaterData* data = m_cache->findRepeater(m_g2Repeater); + + if (data == NULL) { + m_g2Status = G2_REPEATER; + m_irc->findRepeater(m_g2Repeater); + m_g2Header = new CHeaderData(header); + m_queryTimer.start(); + } else { + m_g2Status = G2_OK; + m_g2Address = data->getAddress(); + m_g2Gateway = data->getGateway(); + header.setDestination(m_g2Address, G2_DV_PORT); + header.setRepeaters(m_g2Gateway, m_g2Repeater); + m_g2Handler->writeHeader(header); + delete data; + } + } else if (!callsign.Right(1).IsSameAs(wxT("L")) && !callsign.Right(1).IsSameAs(wxT("U"))) { + if (m_irc == NULL) { + wxLogMessage(wxT("%s is trying to G2 route with ircDDB disabled"), user.c_str()); + m_g2Status = G2_LOCAL; + return; + } + + // This a callsign route + if (callsign.Left(3U).IsSameAs(wxT("REF")) || callsign.Left(3U).IsSameAs(wxT("XRF")) || callsign.Left(3U).IsSameAs(wxT("DCS"))) { + wxLogMessage(wxT("%s is trying to G2 route to reflector %s, ignoring"), user.c_str(), callsign.c_str()); + m_g2Status = G2_LOCAL; + return; + } + + wxLogMessage(wxT("%s is trying to G2 route to callsign %s"), user.c_str(), callsign.c_str()); + + CUserData* data = m_cache->findUser(callsign); + + if (data == NULL) { + m_g2User = callsign; + m_g2Status = G2_USER; + m_irc->findUser(m_g2User); + m_g2Header = new CHeaderData(header); + m_queryTimer.start(); + } else { + // No point G2 routing to yourself + if (data->getRepeater().IsSameAs(m_rptCallsign)) { + m_g2Status = G2_LOCAL; + delete data; + return; + } + + m_g2Status = G2_OK; + m_g2User = callsign; + m_g2Address = data->getAddress(); + m_g2Repeater = data->getRepeater(); + m_g2Gateway = data->getGateway(); + header.setDestination(m_g2Address, G2_DV_PORT); + header.setRepeaters(m_g2Gateway, m_g2Repeater); + m_g2Handler->writeHeader(header); + + delete data; + } + } +} + +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; + + m_queryTimer.stop(); + + wxString letter = callsign.Right(1); + + if (letter.IsSameAs(wxT("U"))) { + // Ignore duplicate unlink requests + if (m_linkStatus == LS_NONE) + return; + + wxLogMessage(wxT("Unlink command issued via %s by %s"), type.c_str(), user.c_str()); + + CDExtraHandler::unlink(this); + CDPlusHandler::unlink(this); + CDCSHandler::unlink(this); + + m_linkStatus = LS_NONE; + m_linkRepeater.Clear(); + + writeNotLinked(); + triggerInfo(); + } else if (letter.IsSameAs(wxT("L"))) { + wxString reflector; + + // Handle the special case of " L" + if (callsign.IsSameAs(wxT(" L"))) { + if (m_linkStartup.IsEmpty()) + return; + + reflector = m_linkStartup; + } else { + // Extract the callsign "1234567L" -> "123456 7" + reflector = callsign.Left(LONG_CALLSIGN_LENGTH - 2U); + reflector.Append(wxT(" ")); + reflector.Append(callsign.Mid(LONG_CALLSIGN_LENGTH - 2U, 1)); + } + + // Ensure duplicate link requests aren't acted on + if (m_linkStatus != LS_NONE && reflector.IsSameAs(m_linkRepeater)) + return; + + // We can't link to ourself + if (reflector.IsSameAs(m_rptCallsign)) { + wxLogMessage(wxT("%s is trying to link with self via %s, ignoring"), user.c_str(), type.c_str()); + triggerInfo(); + return; + } + + wxLogMessage(wxT("Link command from %s to %s issued via %s by %s"), m_rptCallsign.c_str(), reflector.c_str(), type.c_str(), user.c_str()); + + // Check for just a change of letter + if (m_linkStatus != LS_NONE) { + wxString oldCall = m_linkRepeater.Left(LONG_CALLSIGN_LENGTH - 1U); + wxString newCall = reflector.Left(LONG_CALLSIGN_LENGTH - 1U); + + // Just a change of port? + if (oldCall.IsSameAs(newCall)) { + switch (m_linkStatus) { + case LS_LINKING_DEXTRA: + case LS_LINKED_DEXTRA: + m_linkRelink = true; + m_linkRepeater = reflector; + CDExtraHandler::unlink(this, m_linkRepeater); + + m_linkStatus = LS_LINKING_DEXTRA; + writeLinkingTo(m_linkRepeater); + triggerInfo(); + break; + + case LS_LINKING_DCS: + case LS_LINKED_DCS: + m_linkRelink = true; + m_linkRepeater = reflector; + CDCSHandler::unlink(this, m_linkRepeater); + + m_linkStatus = LS_LINKING_DCS; + writeLinkingTo(m_linkRepeater); + triggerInfo(); + break; + + case LS_LINKING_LOOPBACK: + case LS_LINKED_LOOPBACK: + m_linkRelink = true; + m_linkRepeater = reflector; + CDCSHandler::unlink(this, m_linkRepeater); + + m_linkStatus = LS_LINKING_LOOPBACK; + writeLinkingTo(m_linkRepeater); + triggerInfo(); + break; + + case LS_LINKING_DPLUS: + m_linkRepeater = reflector; + CDPlusHandler::relink(this, m_linkRepeater); + writeLinkingTo(m_linkRepeater); + triggerInfo(); + break; + + case LS_LINKED_DPLUS: + m_linkRepeater = reflector; + CDPlusHandler::relink(this, m_linkRepeater); + writeLinkedTo(m_linkRepeater); + triggerInfo(); + break; + + default: + break; + } + + return; + } + } + + CDExtraHandler::unlink(this); + CDPlusHandler::unlink(this); + CDCSHandler::unlink(this); + + linkInt(reflector); + } +} + +void CRepeaterHandler::linkInt(const wxString& callsign) +{ + // Find the repeater to link to + CRepeaterData* data = m_cache->findRepeater(callsign); + + // Are we trying to link to an unknown DExtra, D-Plus, or DCS reflector? + if (data == NULL && (callsign.Left(3U).IsSameAs(wxT("REF")) || callsign.Left(3U).IsSameAs(wxT("XRF")) || callsign.Left(3U).IsSameAs(wxT("DCS")))) { + wxLogMessage(wxT("%s is unknown, ignoring link request"), callsign.c_str()); + triggerInfo(); + return; + } + + m_linkRepeater = callsign; + + if (data != NULL) { + m_linkGateway = data->getGateway(); + + switch (data->getProtocol()) { + case DP_DPLUS: + if (m_dplusEnabled) { + m_linkStatus = LS_LINKING_DPLUS; + CDPlusHandler::link(this, m_rptCallsign, m_linkRepeater, data->getAddress()); + writeLinkingTo(m_linkRepeater); + triggerInfo(); + } else { + wxLogMessage(wxT("Require D-Plus for linking to %s, but D-Plus is disabled"), callsign.c_str()); + m_linkStatus = LS_NONE; + writeNotLinked(); + triggerInfo(); + } + break; + + case DP_DCS: + if (m_dcsEnabled) { + m_linkStatus = LS_LINKING_DCS; + CDCSHandler::link(this, m_rptCallsign, m_linkRepeater, data->getAddress()); + writeLinkingTo(m_linkRepeater); + triggerInfo(); + } else { + wxLogMessage(wxT("Require DCS for linking to %s, but DCS is disabled"), callsign.c_str()); + m_linkStatus = LS_NONE; + writeNotLinked(); + triggerInfo(); + } + break; + + case DP_LOOPBACK: + m_linkStatus = LS_LINKING_LOOPBACK; + CDCSHandler::link(this, m_rptCallsign, m_linkRepeater, data->getAddress()); + writeLinkingTo(m_linkRepeater); + triggerInfo(); + break; + + default: + if (m_dextraEnabled) { + m_linkStatus = LS_LINKING_DEXTRA; + CDExtraHandler::link(this, m_rptCallsign, m_linkRepeater, data->getAddress()); + writeLinkingTo(m_linkRepeater); + triggerInfo(); + } else { + wxLogMessage(wxT("Require DExtra for linking to %s, but DExtra is disabled"), callsign.c_str()); + m_linkStatus = LS_NONE; + writeNotLinked(); + triggerInfo(); + } + break; + } + + delete data; + } else { + if (m_irc != NULL) { + m_linkStatus = LS_PENDING_IRCDDB; + m_irc->findRepeater(callsign); + m_queryTimer.start(); + writeLinkingTo(callsign); + triggerInfo(); + } else { + m_linkStatus = LS_NONE; + writeNotLinked(); + triggerInfo(); + } + } +} + +void CRepeaterHandler::sendToOutgoing(const CHeaderData& header) +{ + CHeaderData temp(header); + + temp.setCQCQCQ(); + temp.setFlags(0x00U, 0x00U, 0x00U); + + // Outgoing DPlus links change the RPT1 and RPT2 values in the DPlus handler + CDPlusHandler::writeHeader(this, temp, DIR_OUTGOING); + + // Outgoing DExtra links have the currently linked repeater/gateway + // as the RPT1 and RPT2 values + temp.setRepeaters(m_linkGateway, m_linkRepeater); + CDExtraHandler::writeHeader(this, temp, DIR_OUTGOING); + + // Outgoing DCS links have the currently linked repeater and repeater callsign + // as the RPT1 and RPT2 values + temp.setRepeaters(m_rptCallsign, m_linkRepeater); + CDCSHandler::writeHeader(this, temp, DIR_OUTGOING); +} + +void CRepeaterHandler::sendToOutgoing(const CAMBEData& data) +{ + CAMBEData temp(data); + + CDExtraHandler::writeAMBE(this, temp, DIR_OUTGOING); + + CDPlusHandler::writeAMBE(this, temp, DIR_OUTGOING); + + CDCSHandler::writeAMBE(this, temp, DIR_OUTGOING); +} + +void CRepeaterHandler::sendToIncoming(const CHeaderData& header) +{ + CHeaderData temp(header); + + temp.setCQCQCQ(); + temp.setFlags(0x00U, 0x00U, 0x00U); + + // Incoming DPlus links + temp.setRepeaters(m_rptCallsign, m_gateway); + CDPlusHandler::writeHeader(this, temp, DIR_INCOMING); + + // Incoming DExtra links have RPT1 and RPT2 swapped + temp.setRepeaters(m_gwyCallsign, m_rptCallsign); + CDExtraHandler::writeHeader(this, temp, DIR_INCOMING); + + // Incoming DCS links have RPT1 and RPT2 swapped + temp.setRepeaters(m_gwyCallsign, m_rptCallsign); + CDCSHandler::writeHeader(this, temp, DIR_INCOMING); +} + +void CRepeaterHandler::sendToIncoming(const CAMBEData& data) +{ + CAMBEData temp(data); + + CDExtraHandler::writeAMBE(this, temp, DIR_INCOMING); + + CDPlusHandler::writeAMBE(this, temp, DIR_INCOMING); + + CDCSHandler::writeAMBE(this, temp, DIR_INCOMING); +} + +void CRepeaterHandler::startupInt() +{ + // Report our existence to ircDDB + if (m_irc != NULL) { + wxString callsign = m_rptCallsign; + if (m_ddMode) + callsign.Append(wxT("D")); + + if (m_frequency > 0.0) + m_irc->rptrQRG(callsign, m_frequency, m_offset, m_range * 1000.0, m_agl); + + if (m_latitude != 0.0 && m_longitude != 0.0) + m_irc->rptrQTH(callsign, m_latitude, m_longitude, m_description1, m_description2, m_url); + } + + + 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()); + + // Find the repeater to link to + CRepeaterData* data = m_cache->findRepeater(m_linkStartup); + + m_linkRepeater = m_linkStartup; + + if (data != NULL) { + m_linkGateway = data->getGateway(); + + DSTAR_PROTOCOL protocol = data->getProtocol(); + switch (protocol) { + case DP_DPLUS: + if (m_dplusEnabled) { + m_linkStatus = LS_LINKING_DPLUS; + CDPlusHandler::link(this, m_rptCallsign, m_linkRepeater, data->getAddress()); + writeLinkingTo(m_linkRepeater); + triggerInfo(); + } else { + wxLogMessage(wxT("Require D-Plus for linking to %s, but D-Plus is disabled"), m_linkRepeater.c_str()); + m_linkStatus = LS_NONE; + writeNotLinked(); + triggerInfo(); + } + break; + + case DP_DCS: + if (m_dcsEnabled) { + m_linkStatus = LS_LINKING_DCS; + CDCSHandler::link(this, m_rptCallsign, m_linkRepeater, data->getAddress()); + writeLinkingTo(m_linkRepeater); + triggerInfo(); + } else { + wxLogMessage(wxT("Require DCS for linking to %s, but DCS is disabled"), m_linkRepeater.c_str()); + m_linkStatus = LS_NONE; + writeNotLinked(); + triggerInfo(); + } + break; + + case DP_LOOPBACK: + m_linkStatus = LS_LINKING_LOOPBACK; + CDCSHandler::link(this, m_rptCallsign, m_linkRepeater, data->getAddress()); + writeLinkingTo(m_linkRepeater); + triggerInfo(); + break; + + default: + if (m_dextraEnabled) { + m_linkStatus = LS_LINKING_DEXTRA; + CDExtraHandler::link(this, m_rptCallsign, m_linkRepeater, data->getAddress()); + writeLinkingTo(m_linkRepeater); + triggerInfo(); + } else { + wxLogMessage(wxT("Require DExtra for linking to %s, but DExtra is disabled"), m_linkRepeater.c_str()); + m_linkStatus = LS_NONE; + writeNotLinked(); + triggerInfo(); + } + break; + } + + delete data; + } else { + if (m_irc != NULL) { + m_linkStatus = LS_PENDING_IRCDDB; + m_irc->findRepeater(m_linkStartup); + m_queryTimer.start(); + writeLinkingTo(m_linkStartup); + triggerInfo(); + } else { + m_linkStatus = LS_NONE; + writeNotLinked(); + triggerInfo(); + } + } + } else { + writeNotLinked(); + triggerInfo(); + } +} + +void CRepeaterHandler::writeLinkingTo(const wxString &callsign) +{ + wxString text; + + switch (m_language) { + case TL_DEUTSCH: + text.Printf(wxT("Verbinde mit %s"), callsign.c_str()); + break; + case TL_DANSK: + text.Printf(wxT("Linker til %s"), callsign.c_str()); + break; + case TL_FRANCAIS: + text.Printf(wxT("Connexion a %s"), callsign.c_str()); + break; + case TL_ITALIANO: + text.Printf(wxT("In conn con %s"), callsign.c_str()); + break; + case TL_POLSKI: + text.Printf(wxT("Linkuje do %s"), callsign.c_str()); + break; + case TL_ESPANOL: + text.Printf(wxT("Enlazando %s"), callsign.c_str()); + break; + case TL_SVENSKA: + text.Printf(wxT("Lankar till %s"), callsign.c_str()); + break; + case TL_NEDERLANDS_NL: + case TL_NEDERLANDS_BE: + text.Printf(wxT("Linken naar %s"), callsign.c_str()); + break; + case TL_NORSK: + text.Printf(wxT("Kobler til %s"), callsign.c_str()); + break; + case TL_PORTUGUES: + text.Printf(wxT("Conectando, %s"), callsign.c_str()); + break; + default: + text.Printf(wxT("Linking to %s"), callsign.c_str()); + break; + } + + CTextData textData(m_linkStatus, callsign, text, m_address, m_port); + m_repeaterHandler->writeText(textData); + + m_infoAudio->setStatus(m_linkStatus, m_linkRepeater, text); + triggerInfo(); + + m_ccsHandler->setReflector(); +} + +void CRepeaterHandler::writeLinkedTo(const wxString &callsign) +{ + 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; + } + + CTextData textData(m_linkStatus, callsign, text, m_address, m_port); + m_repeaterHandler->writeText(textData); + + m_infoAudio->setStatus(m_linkStatus, m_linkRepeater, text); + triggerInfo(); + + m_ccsHandler->setReflector(callsign); +} + +void CRepeaterHandler::writeNotLinked() +{ + wxString text; + + switch (m_language) { + case TL_DEUTSCH: + text = wxT("Nicht verbunden"); + break; + case TL_DANSK: + text = wxT("Ikke forbundet"); + break; + case TL_FRANCAIS: + text = wxT("Non connecte"); + break; + case TL_ITALIANO: + text = wxT("Non connesso"); + break; + case TL_POLSKI: + text = wxT("Nie polaczony"); + break; + case TL_ESPANOL: + text = wxT("No enlazado"); + break; + case TL_SVENSKA: + text = wxT("Ej lankad"); + break; + case TL_NEDERLANDS_NL: + case TL_NEDERLANDS_BE: + text = wxT("Niet gelinkt"); + break; + case TL_NORSK: + text = wxT("Ikke linket"); + break; + case TL_PORTUGUES: + text = wxT("Desconectado"); + break; + default: + text = wxT("Not linked"); + break; + } + + CTextData textData(LS_NONE, wxEmptyString, text, m_address, m_port); + m_repeaterHandler->writeText(textData); + + m_infoAudio->setStatus(m_linkStatus, m_linkRepeater, text); + triggerInfo(); + + m_ccsHandler->setReflector(); +} + +void CRepeaterHandler::writeIsBusy(const wxString& callsign) +{ + wxString tempText; + wxString text; + + switch (m_language) { + case TL_DEUTSCH: + text = wxT("Nicht verbunden"); + tempText.Printf(wxT("%s ist belegt"), callsign.c_str()); + break; + case TL_DANSK: + text = wxT("Ikke forbundet"); + tempText.Printf(wxT("Optaget fra %s"), callsign.c_str()); + break; + case TL_FRANCAIS: + text = wxT("Non connecte"); + tempText.Printf(wxT("Occupe par %s"), callsign.c_str()); + break; + case TL_ITALIANO: + text = wxT("Non connesso"); + tempText.Printf(wxT("Occupado da%s"), callsign.c_str()); + break; + case TL_POLSKI: + text = wxT("Nie polaczony"); + tempText.Printf(wxT("%s jest zajety"), callsign.c_str()); + break; + case TL_ESPANOL: + text = wxT("No enlazado"); + tempText.Printf(wxT("%s ocupado"), callsign.c_str()); + break; + case TL_SVENSKA: + text = wxT("Ej lankad"); + tempText.Printf(wxT("%s ar upptagen"), callsign.c_str()); + break; + case TL_NEDERLANDS_NL: + case TL_NEDERLANDS_BE: + text = wxT("Niet gelinkt"); + tempText.Printf(wxT("%s is bezet"), callsign.c_str()); + break; + case TL_NORSK: + text = wxT("Ikke linket"); + tempText.Printf(wxT("%s er opptatt"), callsign.c_str()); + break; + case TL_PORTUGUES: + text = wxT("Desconectado"); + tempText.Printf(wxT("%s, ocupado"), callsign.c_str()); + break; + default: + text = wxT("Not linked"); + tempText.Printf(wxT("%s is busy"), callsign.c_str()); + break; + } + + 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(); + + 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) +{ + for (unsigned int i = 0U; i < m_maxRepeaters; i++) { + if (m_repeaters[i] != NULL) { + statusData.setDestination(m_repeaters[i]->m_address, m_repeaters[i]->m_port); + m_repeaters[i]->m_repeaterHandler->writeStatus(statusData); + } + } +} + +void CRepeaterHandler::sendHeard(const wxString& text) +{ + if (m_irc == NULL) + return; + + wxString destination; + + if (m_g2Status == G2_OK) { + destination = m_g2Repeater; + } else if (m_g2Status == G2_NONE && (m_linkStatus == LS_LINKED_DPLUS || m_linkStatus == LS_LINKED_DEXTRA || m_linkStatus == LS_LINKED_DCS)) { + if (m_linkRepeater.Left(3U).IsSameAs(wxT("REF")) || m_linkRepeater.Left(3U).IsSameAs(wxT("XRF")) || m_linkRepeater.Left(3U).IsSameAs(wxT("DCS"))) + destination = m_linkRepeater; + } + + m_irc->sendHeardWithTXMsg(m_myCall1, m_myCall2, m_yourCall, m_rptCall1, m_rptCall2, m_flag1, m_flag2, m_flag3, destination, text); +} + +void CRepeaterHandler::sendStats() +{ + if (m_irc != NULL) + m_irc->sendHeardWithTXStats(m_myCall1, m_myCall2, m_yourCall, m_rptCall1, m_rptCall2, m_flag1, m_flag2, m_flag3, m_frames, m_silence, m_errors / 2U); +} + +void CRepeaterHandler::suspendLinks() +{ + if (m_linkStatus == LS_LINKING_DCS || m_linkStatus == LS_LINKED_DCS || + m_linkStatus == LS_LINKING_DEXTRA || m_linkStatus == LS_LINKED_DEXTRA || + m_linkStatus == LS_LINKING_DPLUS || m_linkStatus == LS_LINKED_DPLUS || + m_linkStatus == LS_LINKING_LOOPBACK || m_linkStatus == LS_LINKED_LOOPBACK) { + m_lastReflector = m_linkRepeater; + m_lastReflector.Trim(); + } + + CDPlusHandler::unlink(this); + CDExtraHandler::unlink(this); + CDCSHandler::unlink(this); + + m_linkStatus = LS_NONE; + m_linkRepeater.Clear(); + m_linkReconnectTimer.stop(); + + m_ccsHandler->setReflector(); +} + +bool CRepeaterHandler::restoreLinks() +{ + if (m_linkReconnect == RECONNECT_FIXED) { + if (!m_lastReflector.IsEmpty()) { + linkInt(m_linkStartup); + m_lastReflector.Clear(); + return true; + } + } else if (m_linkReconnect == RECONNECT_NEVER) { + if (!m_lastReflector.IsEmpty()) { + linkInt(m_lastReflector); + m_lastReflector.Clear(); + return true; + } + } else { + m_linkReconnectTimer.start(); + if (!m_lastReflector.IsEmpty()) { + linkInt(m_lastReflector); + m_lastReflector.Clear(); + return true; + } + } + + m_lastReflector.Clear(); + return false; +} + +void CRepeaterHandler::triggerInfo() +{ + if (!m_infoEnabled) + return; + + // Either send the audio now, or queue it until the end of the transmission + if (m_repeaterId != 0x00U || m_busyId != 0x00U) { + m_infoNeeded = true; + } else { + m_infoAudio->sendStatus(); + 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 new file mode 100644 index 0000000..5871775 --- /dev/null +++ b/Common/RepeaterHandler.h @@ -0,0 +1,305 @@ +/* + * 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 + * 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 RepeaterHandler_H +#define RepeaterHandler_H + +#include "RepeaterProtocolHandler.h" +#include "DExtraProtocolHandler.h" +#include "DPlusProtocolHandler.h" +#include "RemoteRepeaterData.h" +#include "G2ProtocolHandler.h" +#include "ReflectorCallback.h" +#include "RepeaterCallback.h" +#include "AnnouncementUnit.h" +#include "StarNetHandler.h" +#include "TextCollector.h" +#include "CacheManager.h" +#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" +#include "AudioUnit.h" +#include "EchoUnit.h" +#include "PollData.h" +#include "DDData.h" +#include "IRCDDB.h" +#include "Timer.h" +#include "DTMF.h" +#include "Defs.h" + +#if defined(__WINDOWS__) +#include "Inaddr.h" +#else +#include +#endif + +#include + +class CRepeaterHandler : public IRepeaterCallback, public IReflectorCallback, public ICCSCallback { +public: + static void initialise(unsigned int maxRepeaters); + + static void add(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); + + static void setLocalAddress(const wxString& address); + static void setG2Handler(CG2ProtocolHandler* handler); + static void setIRC(CIRCDDB* irc); + static void setCache(CCacheManager* cache); + static void setGateway(const wxString& gateway); + static void setLanguage(TEXT_LANG language); + static void setDExtraEnabled(bool enabled); + static void setDPlusEnabled(bool enabled); + static void setDCSEnabled(bool enabled); + static void setHeaderLogger(CHeaderLogger* logger); + static void setAPRSWriter(CAPRSWriter* writer); + static void setInfoEnabled(bool enabled); + static void setEchoEnabled(bool enabled); + static void setDTMFEnabled(bool enabled); + static void setWhiteList(CCallsignList* list); + static void setBlackList(CCallsignList* list); + static void setRestrictList(CCallsignList* list); + + static void startup(); + + static bool getRepeater(unsigned int n, wxString& callsign, LINK_STATUS& linkStatus, wxString& linkCallsign); + + static wxArrayString listDVRepeaters(); + + static CRepeaterHandler* findDVRepeater(const CHeaderData& header); + static CRepeaterHandler* findDVRepeater(const CAMBEData& data, bool busy); + static CRepeaterHandler* findDVRepeater(const wxString& callsign); + + static CRepeaterHandler* findRepeater(const CPollData& data); + + static CRepeaterHandler* findDDRepeater(const CDDData& data); + static CRepeaterHandler* findDDRepeater(); + + static void pollAllIcom(CPollData& data); + + static void writeStatus(CStatusData& statusData); + + static void resolveUser(const wxString& user, const wxString& repeater, const wxString& gateway, const wxString& address); + static void resolveRepeater(const wxString& repeater, const wxString& gateway, const wxString& address, DSTAR_PROTOCOL protocol); + + static void finalise(); + + static void clock(unsigned int ms); + + void processRepeater(CHeaderData& header); + void processRepeater(CHeardData& heard); + void processRepeater(CAMBEData& data); + void processRepeater(CPollData& data); + void processRepeater(CDDData& data); + + void processBusy(CHeaderData& header); + void processBusy(CAMBEData& data); + + void link(RECONNECT reconnect, const wxString& reflector); + void unlink(PROTOCOL protocol, const wxString& reflector); + + CRemoteRepeaterData* getInfo() const; + + virtual bool process(CHeaderData& header, DIRECTION direction, AUDIO_SOURCE source); + virtual bool process(CAMBEData& data, DIRECTION direction, AUDIO_SOURCE source); + virtual bool process(CDDData& data); + + virtual void linkUp(DSTAR_PROTOCOL protocol, const wxString& callsign); + 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(); + + void resolveUserInt(const wxString& user, const wxString& repeater, const wxString& gateway, const wxString& address); + void resolveRepeaterInt(const wxString& repeater, const wxString& gateway, const wxString& address, DSTAR_PROTOCOL protocol); + + void startupInt(); + + void setIndex(unsigned int index); + + void clockInt(unsigned int ms); + +private: + static unsigned int m_maxRepeaters; + static CRepeaterHandler** m_repeaters; + + static wxString m_localAddress; + static CG2ProtocolHandler* m_g2Handler; + static CCacheManager* m_cache; + static wxString m_gateway; + static CIRCDDB* m_irc; + static TEXT_LANG m_language; + static bool m_dextraEnabled; + static bool m_dplusEnabled; + static bool m_dcsEnabled; + static bool m_infoEnabled; + static bool m_echoEnabled; + static bool m_dtmfEnabled; + + static CHeaderLogger* m_headerLogger; + + static CAPRSWriter* m_aprsWriter; + + static CCallsignList* m_whiteList; + static CCallsignList* m_blackList; + static CCallsignList* m_restrictList; + + // Repeater info + unsigned int m_index; + wxString m_rptCallsign; + wxString m_gwyCallsign; + unsigned char m_band; + in_addr m_address; + unsigned int m_port; + HW_TYPE m_hwType; + IRepeaterProtocolHandler* m_repeaterHandler; + double m_frequency; + double m_offset; + double m_range; + double m_latitude; + double m_longitude; + double m_agl; + wxString m_description1; + wxString m_description2; + wxString m_url; + unsigned char m_band1; + unsigned char m_band2; + unsigned char m_band3; + unsigned int m_repeaterId; + unsigned int m_busyId; + CTimer m_watchdogTimer; + bool m_ddMode; + wxString m_ddCallsign; + CTimer m_queryTimer; + + // User details + wxString m_myCall1; + wxString m_myCall2; + wxString m_yourCall; + wxString m_rptCall1; + wxString m_rptCall2; + unsigned char m_flag1; + unsigned char m_flag2; + unsigned char m_flag3; + bool m_restricted; + + // Statistics + unsigned int m_frames; + unsigned int m_silence; + unsigned int m_errors; + + // Slow data handling + CTextCollector m_textCollector; + wxString m_text; + + // Cross-band repeating + CRepeaterHandler* m_xBandRptr; + + // StarNet + CStarNetHandler* m_starNet; + + // G2 info + G2_STATUS m_g2Status; + wxString m_g2User; + wxString m_g2Repeater; + wxString m_g2Gateway; + CHeaderData* m_g2Header; + in_addr m_g2Address; + + // Link info + LINK_STATUS m_linkStatus; + wxString m_linkRepeater; + wxString m_linkGateway; + RECONNECT m_linkReconnect; + bool m_linkAtStartup; + wxString m_linkStartup; + CTimer m_linkReconnectTimer; + bool m_linkRelink; + + // Echoing + CEchoUnit* m_echo; + + // Voice messages + CAudioUnit* m_infoAudio; + bool m_infoNeeded; + CAnnouncementUnit* m_msgAudio; + bool m_msgNeeded; + CAnnouncementUnit* m_wxAudio; + bool m_wxNeeded; + + // Version information + CVersionUnit* m_version; + + // D-RATS handler + CDRATSServer* m_drats; + + // DTMF commands + CDTMF m_dtmf; + + // Poll timer + CTimer m_pollTimer; + + // CCS + CCCSHandler* m_ccsHandler; + + // Reflector restoration + wxString m_lastReflector; + + // Icom heard data + wxString m_heardUser; + wxString m_heardRepeater; + 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); + void sendToIncoming(const CHeaderData& header); + void sendToIncoming(const CAMBEData& data); + + void writeIsBusy(const wxString& callsign); + void writeLinkedTo(const wxString& callsign); + void writeLinkingTo(const wxString& callsign); + void writeNotLinked(); + + void sendHeard(const wxString& text = wxEmptyString); + void sendStats(); + + void linkInt(const wxString& callsign); + + void suspendLinks(); + bool restoreLinks(); + + void triggerInfo(); + + bool isCCSCommand(const wxString& command) const; +}; + +#endif diff --git a/Common/RepeaterProtocolHandler.h b/Common/RepeaterProtocolHandler.h new file mode 100644 index 0000000..3486a98 --- /dev/null +++ b/Common/RepeaterProtocolHandler.h @@ -0,0 +1,67 @@ +/* + * 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 + * 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 RepeaterProtocolHandler_H +#define RepeaterProtocolHandler_H + +#include "HeaderData.h" +#include "StatusData.h" +#include "HeardData.h" +#include "AMBEData.h" +#include "TextData.h" +#include "PollData.h" +#include "DDData.h" + +#include + +enum REPEATER_TYPE { + RT_NONE, + RT_POLL, + RT_HEARD, + RT_HEADER, + RT_AMBE, + RT_BUSY_HEADER, + RT_BUSY_AMBE, + RT_DD +}; + +class IRepeaterProtocolHandler { +public: + virtual bool open() = 0; + + virtual bool writeHeader(CHeaderData& header) = 0; + virtual bool writeAMBE(CAMBEData& data) = 0; + virtual bool writeDD(CDDData& data) = 0; + virtual bool writeText(CTextData& text) = 0; + virtual bool writeStatus(CStatusData& status) = 0; + + virtual REPEATER_TYPE read() = 0; + virtual CPollData* readPoll() = 0; + virtual CHeardData* readHeard() = 0; + virtual CHeaderData* readHeader() = 0; + virtual CAMBEData* readAMBE() = 0; + virtual CDDData* readDD() = 0; + virtual CHeaderData* readBusyHeader() = 0; + virtual CAMBEData* readBusyAMBE() = 0; + + virtual void close() = 0; + +private: +}; + +#endif diff --git a/Common/RingBuffer.h b/Common/RingBuffer.h new file mode 100644 index 0000000..4a180dd --- /dev/null +++ b/Common/RingBuffer.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2006-2009,2013,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 + * 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 RingBuffer_H +#define RingBuffer_H + +#include + +template class CRingBuffer { +public: + CRingBuffer(unsigned int length) : + m_length(length), + m_buffer(NULL), + m_iPtr(0U), + m_oPtr(0U), + m_mutex() + { + wxASSERT(length > 0U); + + m_buffer = new T[length]; + + ::memset(m_buffer, 0x00, length * sizeof(T)); + } + + ~CRingBuffer() + { + delete[] m_buffer; + } + + void addData(const T data) + { + wxMutexLocker locker(m_mutex); + + m_buffer[m_iPtr++] = data; + + if (m_iPtr == m_length) + m_iPtr = 0U; + } + + T getData() + { + wxMutexLocker locker(m_mutex); + + if (m_iPtr == m_oPtr) + return NULL; + + T data = m_buffer[m_oPtr++]; + + if (m_oPtr == m_length) + m_oPtr = 0U; + + return data; + } + + void clear() + { + wxMutexLocker locker(m_mutex); + + m_iPtr = 0U; + m_oPtr = 0U; + + ::memset(m_buffer, 0x00, m_length * sizeof(T)); + } + + bool isEmpty() + { + wxMutexLocker locker(m_mutex); + + return m_iPtr == m_oPtr; + } + + T peek() + { + wxMutexLocker locker(m_mutex); + + if (m_iPtr == m_oPtr) + return NULL; + + return m_buffer[m_oPtr]; + } + +private: + unsigned int m_length; + T* m_buffer; + volatile unsigned int m_iPtr; + volatile unsigned int m_oPtr; + wxMutex m_mutex; +}; + +#endif diff --git a/Common/SHA256.cpp b/Common/SHA256.cpp new file mode 100644 index 0000000..3aceee2 --- /dev/null +++ b/Common/SHA256.cpp @@ -0,0 +1,369 @@ +/* + * Copyright (C) 2005, 2006, 2008 Free Software Foundation, Inc. + * Copyright (C) 2011 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 "SHA256.h" + +#ifdef WORDS_BIGENDIAN +# define SWAP(n) (n) +#else +# define SWAP(n) \ + (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) +#endif + +#define BLOCKSIZE 4096 +#if BLOCKSIZE % 64 != 0 +# error "invalid BLOCKSIZE" +#endif + +/* This array contains the bytes used to pad the buffer to the next + 64-byte boundary. */ +static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; + + +/* + Takes a pointer to a 256 bit block of data (eight 32 bit ints) and + intializes it to the start constants of the SHA256 algorithm. This + must be called before using hash in the call to sha256_hash +*/ +CSHA256::CSHA256() : +m_state(NULL), +m_total(NULL), +m_buflen(0U), +m_buffer(NULL) +{ + m_state = new wxUint32[8U]; + m_total = new wxUint32[2U]; + m_buffer = new wxUint32[32U]; + + init(); +} + +CSHA256::~CSHA256() +{ + delete[] m_state; + delete[] m_total; + delete[] m_buffer; +} + +void CSHA256::init() +{ + m_state[0] = 0x6a09e667UL; + m_state[1] = 0xbb67ae85UL; + m_state[2] = 0x3c6ef372UL; + m_state[3] = 0xa54ff53aUL; + m_state[4] = 0x510e527fUL; + m_state[5] = 0x9b05688cUL; + m_state[6] = 0x1f83d9abUL; + m_state[7] = 0x5be0cd19UL; + + m_total[0] = m_total[1] = 0; + m_buflen = 0; +} + +/* Copy the value from v into the memory location pointed to by *cp, + If your architecture allows unaligned access this is equivalent to + * (uint32_t *) cp = v */ +static inline void set_uint32(unsigned char* cp, wxUint32 v) +{ + wxASSERT(cp != NULL); + + ::memcpy(cp, &v, sizeof v); +} + +/* Put result from CTX in first 32 bytes following RESBUF. The result + must be in little endian byte order. */ +unsigned char* CSHA256::read(unsigned char* resbuf) +{ + wxASSERT(resbuf != NULL); + + for (unsigned int i = 0U; i < 8U; i++) + set_uint32(resbuf + i * sizeof(m_state[0]), SWAP(m_state[i])); + + return resbuf; +} + +/* Process the remaining bytes in the internal buffer and the usual + prolog according to the standard and write the result to RESBUF. */ +void CSHA256::conclude() +{ + /* Take yet unprocessed bytes into account. */ + unsigned int bytes = m_buflen; + unsigned int size = (bytes < 56) ? 64 / 4 : 64 * 2 / 4; + + /* Now count remaining bytes. */ + m_total[0] += bytes; + if (m_total[0] < bytes) + ++m_total[1]; + + /* Put the 64-bit file length in *bits* at the end of the buffer. + Use set_uint32 rather than a simple assignment, to avoid risk of + unaligned access. */ + set_uint32((unsigned char*)&m_buffer[size - 2], SWAP((m_total[1] << 3) | (m_total[0] >> 29))); + set_uint32((unsigned char*)&m_buffer[size - 1], SWAP(m_total[0] << 3)); + + ::memcpy(&((char*)m_buffer)[bytes], fillbuf, (size - 2) * 4 - bytes); + + /* Process last bytes. */ + processBlock((unsigned char*)m_buffer, size * 4); +} + +unsigned char* CSHA256::finish(unsigned char* resbuf) +{ + wxASSERT(resbuf != NULL); + + conclude(); + + return read(resbuf); +} + +/* Compute SHA256 message digest for LEN bytes beginning at BUFFER. The + result is always in little endian byte order, so that a byte-wise + output yields to the wanted ASCII representation of the message + digest. */ +unsigned char* CSHA256::buffer(const unsigned char* buffer, unsigned int len, unsigned char* resblock) +{ + wxASSERT(buffer != NULL); + wxASSERT(resblock != NULL); + + /* Initialize the computation context. */ + init(); + + /* Process whole buffer but last len % 64 bytes. */ + processBytes(buffer, len); + + /* Put result in desired memory area. */ + return finish(resblock); +} + +void CSHA256::processBytes(const unsigned char* buffer, unsigned int len) +{ + wxASSERT(buffer != NULL); + + /* When we already have some bits in our internal buffer concatenate + both inputs first. */ + if (m_buflen != 0U) { + unsigned int left_over = m_buflen; + unsigned int add = 128U - left_over > len ? len : 128U - left_over; + + ::memcpy(&((char*)m_buffer)[left_over], buffer, add); + m_buflen += add; + + if (m_buflen > 64U) { + processBlock((unsigned char*)m_buffer, m_buflen & ~63U); + + m_buflen &= 63U; + + /* The regions in the following copy operation cannot overlap. */ + ::memcpy(m_buffer, &((char*)m_buffer)[(left_over + add) & ~63U], m_buflen); + } + + buffer += add; + len -= add; + } + + /* Process available complete blocks. */ + if (len >= 64U) { +//#if !_STRING_ARCH_unaligned +//# define alignof(type) offsetof (struct { char c; type x; }, x) +//# define UNALIGNED_P(p) (((unsigned int) p) % alignof (wxUint32) != 0) +// if (UNALIGNED_P (buffer)) { +// while (len > 64U) { +// ::memcpy(m_buffer, buffer, 64U); +// processBlock((unsigned char*)m_buffer, 64U); +// buffer += 64U; +// len -= 64U; +// } +// } else +//#endif + { + processBlock(buffer, len & ~63U); + buffer += (len & ~63U); + len &= 63U; + } + } + + /* Move remaining bytes in internal buffer. */ + if (len > 0U) { + unsigned int left_over = m_buflen; + + ::memcpy(&((char*)m_buffer)[left_over], buffer, len); + left_over += len; + + if (left_over >= 64U) { + processBlock((unsigned char*)m_buffer, 64U); + left_over -= 64U; + ::memcpy(m_buffer, &m_buffer[16], left_over); + } + + m_buflen = left_over; + } +} + +/* --- Code below is the primary difference between sha1.c and sha256.c --- */ + +/* SHA256 round constants */ +#define K(I) roundConstants[I] +static const wxUint32 roundConstants[64] = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, + 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, + 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL, + 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL, + 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, + 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, + 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, + 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL, + 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL, + 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, + 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, + 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, + 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL, + 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL, + 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL, +}; + +/* Round functions. */ +#define F2(A,B,C) ( ( A & B ) | ( C & ( A | B ) ) ) +#define F1(E,F,G) ( G ^ ( E & ( F ^ G ) ) ) + +/* Process LEN bytes of BUFFER, accumulating context into CTX. + It is assumed that LEN % 64 == 0. + Most of this code comes from GnuPG's cipher/sha1.c. */ + +void CSHA256::processBlock(const unsigned char* buffer, unsigned int len) +{ + wxASSERT(buffer != NULL); + + const wxUint32* words = (wxUint32*)buffer; + unsigned int nwords = len / sizeof(wxUint32); + const wxUint32* endp = words + nwords; + wxUint32 x[16]; + wxUint32 a = m_state[0]; + wxUint32 b = m_state[1]; + wxUint32 c = m_state[2]; + wxUint32 d = m_state[3]; + wxUint32 e = m_state[4]; + wxUint32 f = m_state[5]; + wxUint32 g = m_state[6]; + wxUint32 h = m_state[7]; + + /* First increment the byte count. FIPS PUB 180-2 specifies the possible + length of the file up to 2^64 bits. Here we only compute the + number of bytes. Do a double word increment. */ + m_total[0] += len; + if (m_total[0] < len) + ++m_total[1]; + + #define rol(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + #define S0(x) (rol(x,25)^rol(x,14)^(x>>3)) + #define S1(x) (rol(x,15)^rol(x,13)^(x>>10)) + #define SS0(x) (rol(x,30)^rol(x,19)^rol(x,10)) + #define SS1(x) (rol(x,26)^rol(x,21)^rol(x,7)) + + #define M(I) (tm = S1(x[(I-2)&0x0f]) + x[(I-7)&0x0f] + S0(x[(I-15)&0x0f]) + x[I&0x0f], x[I&0x0f] = tm) + + #define R(A,B,C,D,E,F,G,H,K,M) do { t0 = SS0(A) + F2(A,B,C); \ + t1 = H + SS1(E) + F1(E,F,G) + K + M; \ + D += t1; H = t0 + t1; \ + } while(0) + + while (words < endp) { + wxUint32 tm; + wxUint32 t0, t1; + /* FIXME: see sha1.c for a better implementation. */ + for (unsigned int t = 0U; t < 16U; t++) { + x[t] = SWAP(*words); + words++; + } + + R( a, b, c, d, e, f, g, h, K( 0), x[ 0] ); + R( h, a, b, c, d, e, f, g, K( 1), x[ 1] ); + R( g, h, a, b, c, d, e, f, K( 2), x[ 2] ); + R( f, g, h, a, b, c, d, e, K( 3), x[ 3] ); + R( e, f, g, h, a, b, c, d, K( 4), x[ 4] ); + R( d, e, f, g, h, a, b, c, K( 5), x[ 5] ); + R( c, d, e, f, g, h, a, b, K( 6), x[ 6] ); + R( b, c, d, e, f, g, h, a, K( 7), x[ 7] ); + R( a, b, c, d, e, f, g, h, K( 8), x[ 8] ); + R( h, a, b, c, d, e, f, g, K( 9), x[ 9] ); + R( g, h, a, b, c, d, e, f, K(10), x[10] ); + R( f, g, h, a, b, c, d, e, K(11), x[11] ); + R( e, f, g, h, a, b, c, d, K(12), x[12] ); + R( d, e, f, g, h, a, b, c, K(13), x[13] ); + R( c, d, e, f, g, h, a, b, K(14), x[14] ); + R( b, c, d, e, f, g, h, a, K(15), x[15] ); + R( a, b, c, d, e, f, g, h, K(16), M(16) ); + R( h, a, b, c, d, e, f, g, K(17), M(17) ); + R( g, h, a, b, c, d, e, f, K(18), M(18) ); + R( f, g, h, a, b, c, d, e, K(19), M(19) ); + R( e, f, g, h, a, b, c, d, K(20), M(20) ); + R( d, e, f, g, h, a, b, c, K(21), M(21) ); + R( c, d, e, f, g, h, a, b, K(22), M(22) ); + R( b, c, d, e, f, g, h, a, K(23), M(23) ); + R( a, b, c, d, e, f, g, h, K(24), M(24) ); + R( h, a, b, c, d, e, f, g, K(25), M(25) ); + R( g, h, a, b, c, d, e, f, K(26), M(26) ); + R( f, g, h, a, b, c, d, e, K(27), M(27) ); + R( e, f, g, h, a, b, c, d, K(28), M(28) ); + R( d, e, f, g, h, a, b, c, K(29), M(29) ); + R( c, d, e, f, g, h, a, b, K(30), M(30) ); + R( b, c, d, e, f, g, h, a, K(31), M(31) ); + R( a, b, c, d, e, f, g, h, K(32), M(32) ); + R( h, a, b, c, d, e, f, g, K(33), M(33) ); + R( g, h, a, b, c, d, e, f, K(34), M(34) ); + R( f, g, h, a, b, c, d, e, K(35), M(35) ); + R( e, f, g, h, a, b, c, d, K(36), M(36) ); + R( d, e, f, g, h, a, b, c, K(37), M(37) ); + R( c, d, e, f, g, h, a, b, K(38), M(38) ); + R( b, c, d, e, f, g, h, a, K(39), M(39) ); + R( a, b, c, d, e, f, g, h, K(40), M(40) ); + R( h, a, b, c, d, e, f, g, K(41), M(41) ); + R( g, h, a, b, c, d, e, f, K(42), M(42) ); + R( f, g, h, a, b, c, d, e, K(43), M(43) ); + R( e, f, g, h, a, b, c, d, K(44), M(44) ); + R( d, e, f, g, h, a, b, c, K(45), M(45) ); + R( c, d, e, f, g, h, a, b, K(46), M(46) ); + R( b, c, d, e, f, g, h, a, K(47), M(47) ); + R( a, b, c, d, e, f, g, h, K(48), M(48) ); + R( h, a, b, c, d, e, f, g, K(49), M(49) ); + R( g, h, a, b, c, d, e, f, K(50), M(50) ); + R( f, g, h, a, b, c, d, e, K(51), M(51) ); + R( e, f, g, h, a, b, c, d, K(52), M(52) ); + R( d, e, f, g, h, a, b, c, K(53), M(53) ); + R( c, d, e, f, g, h, a, b, K(54), M(54) ); + R( b, c, d, e, f, g, h, a, K(55), M(55) ); + R( a, b, c, d, e, f, g, h, K(56), M(56) ); + R( h, a, b, c, d, e, f, g, K(57), M(57) ); + R( g, h, a, b, c, d, e, f, K(58), M(58) ); + R( f, g, h, a, b, c, d, e, K(59), M(59) ); + R( e, f, g, h, a, b, c, d, K(60), M(60) ); + R( d, e, f, g, h, a, b, c, K(61), M(61) ); + R( c, d, e, f, g, h, a, b, K(62), M(62) ); + R( b, c, d, e, f, g, h, a, K(63), M(63) ); + + a = m_state[0] += a; + b = m_state[1] += b; + c = m_state[2] += c; + d = m_state[3] += d; + e = m_state[4] += e; + f = m_state[5] += f; + g = m_state[6] += g; + h = m_state[7] += h; + } +} diff --git a/Common/SHA256.h b/Common/SHA256.h new file mode 100644 index 0000000..1a3577a --- /dev/null +++ b/Common/SHA256.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2005, 2006, 2008, 2009 Free Software Foundation, Inc. + * Copyright (C) 2011 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 SHA256_H +#define SHA256_H + +#include + +enum { + SHA256_DIGEST_SIZE = 256 / 8 +}; + +class CSHA256 { +public: + CSHA256(); + ~CSHA256(); + + /* Starting with the result of former calls of this function (or the + initialization function update the context for the next LEN bytes + starting at BUFFER. + It is necessary that LEN is a multiple of 64!!! */ + void processBlock(const unsigned char* buffer, unsigned int len); + + /* Starting with the result of former calls of this function (or the + initialization function update the context for the next LEN bytes + starting at BUFFER. + It is NOT required that LEN is a multiple of 64. */ + void processBytes(const unsigned char* buffer, unsigned int len); + + /* Process the remaining bytes in the buffer and put result from CTX + in first 32 bytes following RESBUF. The result is always in little + endian byte order, so that a byte-wise output yields to the wanted + ASCII representation of the message digest. */ + unsigned char* finish(unsigned char* resbuf); + + /* Put result from CTX in first 32 bytes following RESBUF. The result is + always in little endian byte order, so that a byte-wise output yields + to the wanted ASCII representation of the message digest. */ + unsigned char* read(unsigned char* resbuf); + + /* Compute SHA256 message digest for LEN bytes beginning at BUFFER. The + result is always in little endian byte order, so that a byte-wise + output yields to the wanted ASCII representation of the message + digest. */ + unsigned char* buffer(const unsigned char* buffer, unsigned int len, unsigned char* resblock); + +private: + wxUint32* m_state; + wxUint32* m_total; + unsigned int m_buflen; + wxUint32* m_buffer; + + void init(); + void conclude(); +}; + +#endif diff --git a/Common/SlowDataEncoder.cpp b/Common/SlowDataEncoder.cpp new file mode 100644 index 0000000..c22c500 --- /dev/null +++ b/Common/SlowDataEncoder.cpp @@ -0,0 +1,358 @@ +/* + * Copyright (C) 2010 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; version 2 of the License. + * + * 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. + */ + +#include "SlowDataEncoder.h" +#include "CCITTChecksum.h" +#include "DStarDefines.h" + +const unsigned int SLOW_DATA_BLOCK_SIZE = 6U; + +const unsigned int SLOW_DATA_FULL_BLOCK_SIZE = SLOW_DATA_BLOCK_SIZE * 10U; + +const unsigned char SLOW_DATA_FILLER_BYTES[] = {'f', 'f', 'f' }; + +const unsigned int HEADER_SIZE = 54U; +const unsigned int TEXT_SIZE = 24U; + +CSlowDataEncoder::CSlowDataEncoder() : +m_headerData(NULL), +m_textData(NULL), +m_gpsData(NULL), +m_interleavedData(NULL), +m_headerPtr(0U), +m_textPtr(0U), +m_gpsPtr(0U), +m_interleavedPtr(0U), +m_gpsDataSize(0U), +m_gpsDataFullSize(0U), +m_interleavedDataFullSize(0U) +{ +} + +CSlowDataEncoder::~CSlowDataEncoder() +{ + reset(); +} + +void CSlowDataEncoder::clearHeaderData() +{ + if(m_headerData) + { + delete[] m_headerData; + m_headerData = NULL; + m_headerPtr = 0U; + } +} + +void CSlowDataEncoder::clearTextData() +{ + if(m_textData) + { + delete[] m_textData; + m_textData = NULL; + m_textPtr = 0U; + } +} + +void CSlowDataEncoder::clearGPSData() +{ + if(m_gpsData) + { + delete[] m_gpsData; + m_gpsData = NULL; + m_gpsPtr = 0U; + } +} + +void CSlowDataEncoder::clearInterleavedData() +{ + if(m_interleavedData) + { + delete[] m_interleavedData; + m_interleavedData = NULL; + m_interleavedPtr = 0U; + } +} + +void CSlowDataEncoder::getData(unsigned char* source, unsigned char* data, unsigned int &sourcePtr, unsigned int sourceLength) +{ + wxASSERT(data != NULL); + if(source != NULL){ + data[0U] = source[sourcePtr++] ^ SCRAMBLER_BYTE1; + data[1U] = source[sourcePtr++] ^ SCRAMBLER_BYTE2; + data[2U] = source[sourcePtr++] ^ SCRAMBLER_BYTE3; + } + else{ + data[0U] = SLOW_DATA_FILLER_BYTES[sourcePtr++] ^ SCRAMBLER_BYTE1; + data[1U] = SLOW_DATA_FILLER_BYTES[sourcePtr++] ^ SCRAMBLER_BYTE2; + data[2U] = SLOW_DATA_FILLER_BYTES[sourcePtr++] ^ SCRAMBLER_BYTE3; + } + + if (sourcePtr >= sourceLength) + sourcePtr = 0U; +} + +void CSlowDataEncoder::getHeaderData(unsigned char* data) +{ + getData(m_headerData, data, m_headerPtr, SLOW_DATA_FULL_BLOCK_SIZE); +} + +void CSlowDataEncoder::getTextData(unsigned char* data) +{ + getData(m_textData, data, m_textPtr, SLOW_DATA_FULL_BLOCK_SIZE); +} + +void CSlowDataEncoder::getGPSData(unsigned char* data) +{ + getData(m_gpsData, data, m_gpsPtr, m_gpsDataFullSize); +} + +void CSlowDataEncoder::getInterleavedData(unsigned char* data) +{ + if(m_textData && !m_gpsData && !m_headerData) + getTextData(data); + else if(!m_textData && m_gpsData && !m_headerData) + getGPSData(data); + else if(!m_textData && !m_gpsData && m_headerData) + getHeaderData(data); + else { + buildInterleavedData(); + getData(m_interleavedData, data, m_interleavedPtr, m_gpsDataFullSize); + } +} + +void CSlowDataEncoder::buildInterleavedData() +{ + //first build interleaved data if we do not have it + if(!m_interleavedData) + { + getInterleavedDataLength(); + m_interleavedData = new unsigned char[m_interleavedDataFullSize]; + ::memset(m_interleavedData, 'f', m_interleavedDataFullSize); + + unsigned int textPtr = 0U; + unsigned int gpsPtr = 0U; + unsigned int headerPtr = 0U; + + //now proceed with data copying, according to this document http://www.qsl.net/kb9mwr/projects/dv/dstar/Slow%20Data.pdf + if(m_textData && m_gpsData){ + for(unsigned int interleavedPtr = 0; interleavedPtr < m_interleavedDataFullSize; interleavedPtr += SLOW_DATA_BLOCK_SIZE){ + if(textPtr < TEXT_SIZE + && ((interleavedPtr / SLOW_DATA_BLOCK_SIZE) & 0x01U) == 0) + { + ::memcpy(m_interleavedData + interleavedPtr, m_textData + textPtr, SLOW_DATA_BLOCK_SIZE); + textPtr += SLOW_DATA_BLOCK_SIZE; + } + else if(gpsPtr < m_gpsDataSize){ + ::memcpy(m_interleavedData + interleavedPtr, m_gpsData + gpsPtr, SLOW_DATA_BLOCK_SIZE); + gpsPtr += SLOW_DATA_BLOCK_SIZE; + } + else if(m_headerData && headerPtr < HEADER_SIZE){ + ::memcpy(m_interleavedData + interleavedPtr, m_headerData + headerPtr, SLOW_DATA_BLOCK_SIZE); + headerPtr += SLOW_DATA_BLOCK_SIZE; + } + } + } + else if(m_textData && !m_gpsData && m_headerData){ + //according to above doc, header and text are not interleaved, just on after the other. filler bytes between resync bytes. + ::memcpy(m_interleavedData, m_textData, SLOW_DATA_FULL_BLOCK_SIZE); + ::memcpy(m_interleavedData + SLOW_DATA_FULL_BLOCK_SIZE, m_headerData, SLOW_DATA_FULL_BLOCK_SIZE); + } + else if(!m_textData && m_gpsData && m_headerData){ + //could not find any spec about this particular case, let's put the data one after the other + ::memcpy(m_interleavedData, m_gpsData, SLOW_DATA_FULL_BLOCK_SIZE); + ::memcpy(m_interleavedData + SLOW_DATA_FULL_BLOCK_SIZE, m_headerData, SLOW_DATA_FULL_BLOCK_SIZE); + } + } +} + +unsigned int CSlowDataEncoder::getInterleavedDataLength() +{ + //calculate size (including filler bytes); + m_interleavedDataFullSize = 0U; + if(m_textData) m_interleavedDataFullSize += TEXT_SIZE; + if(m_headerData) m_interleavedDataFullSize += HEADER_SIZE; + if(m_gpsData) m_interleavedDataFullSize += m_gpsDataSize; + m_interleavedDataFullSize = SLOW_DATA_FULL_BLOCK_SIZE * (1U + ((m_interleavedDataFullSize - 1U) / SLOW_DATA_FULL_BLOCK_SIZE)); + return m_interleavedDataFullSize; +} + +void CSlowDataEncoder::sync() +{ + m_headerPtr = 0U; + m_textPtr = 0U; + m_gpsPtr = 0U; + m_interleavedPtr = 0U; +} + +void CSlowDataEncoder::reset() +{ + clearHeaderData(); + clearTextData(); + clearGPSData(); + clearInterleavedData(); +} + +void CSlowDataEncoder::setHeaderData(const CHeaderData& header) +{ + clearInterleavedData(); + if(!m_headerData) m_headerData = new unsigned char[SLOW_DATA_FULL_BLOCK_SIZE]; + ::memset(m_headerData, 'f', SLOW_DATA_FULL_BLOCK_SIZE); + + m_headerData[0U] = SLOW_DATA_TYPE_HEADER | 5U; + m_headerData[1U] = header.getFlag1(); + m_headerData[2U] = header.getFlag2(); + m_headerData[3U] = header.getFlag3(); + m_headerData[4U] = header.getRptCall2().GetChar(0); + m_headerData[5U] = header.getRptCall2().GetChar(1); + + m_headerData[6U] = SLOW_DATA_TYPE_HEADER | 5U; + m_headerData[7U] = header.getRptCall2().GetChar(2); + m_headerData[8U] = header.getRptCall2().GetChar(3); + m_headerData[9U] = header.getRptCall2().GetChar(4); + m_headerData[10U] = header.getRptCall2().GetChar(5); + m_headerData[11U] = header.getRptCall2().GetChar(6); + + m_headerData[12U] = SLOW_DATA_TYPE_HEADER | 5U; + m_headerData[13U] = header.getRptCall2().GetChar(7); + m_headerData[14U] = header.getRptCall1().GetChar(0); + m_headerData[15U] = header.getRptCall1().GetChar(1); + m_headerData[16U] = header.getRptCall1().GetChar(2); + m_headerData[17U] = header.getRptCall1().GetChar(3); + + m_headerData[18U] = SLOW_DATA_TYPE_HEADER | 5U; + m_headerData[19U] = header.getRptCall1().GetChar(4); + m_headerData[20U] = header.getRptCall1().GetChar(5); + m_headerData[21U] = header.getRptCall1().GetChar(6); + m_headerData[22U] = header.getRptCall1().GetChar(7); + m_headerData[23U] = header.getYourCall().GetChar(0); + + m_headerData[24U] = SLOW_DATA_TYPE_HEADER | 5U; + m_headerData[25U] = header.getYourCall().GetChar(1); + m_headerData[26U] = header.getYourCall().GetChar(2); + m_headerData[27U] = header.getYourCall().GetChar(3); + m_headerData[28U] = header.getYourCall().GetChar(4); + m_headerData[29U] = header.getYourCall().GetChar(5); + + m_headerData[30U] = SLOW_DATA_TYPE_HEADER | 5U; + m_headerData[31U] = header.getYourCall().GetChar(6); + m_headerData[32U] = header.getYourCall().GetChar(7); + m_headerData[33U] = header.getMyCall1().GetChar(0); + m_headerData[34U] = header.getMyCall1().GetChar(1); + m_headerData[35U] = header.getMyCall1().GetChar(2); + + m_headerData[36U] = SLOW_DATA_TYPE_HEADER | 5U; + m_headerData[37U] = header.getMyCall1().GetChar(3); + m_headerData[38U] = header.getMyCall1().GetChar(4); + m_headerData[39U] = header.getMyCall1().GetChar(5); + m_headerData[40U] = header.getMyCall1().GetChar(6); + m_headerData[41U] = header.getMyCall1().GetChar(7); + + m_headerData[42U] = SLOW_DATA_TYPE_HEADER | 5U; + m_headerData[43U] = header.getMyCall2().GetChar(0); + m_headerData[44U] = header.getMyCall2().GetChar(1); + m_headerData[45U] = header.getMyCall2().GetChar(2); + m_headerData[46U] = header.getMyCall2().GetChar(3); + + CCCITTChecksum cksum; + cksum.update(m_headerData + 1U, 5U); + cksum.update(m_headerData + 7U, 5U); + cksum.update(m_headerData + 13U, 5U); + cksum.update(m_headerData + 19U, 5U); + cksum.update(m_headerData + 25U, 5U); + cksum.update(m_headerData + 31U, 5U); + cksum.update(m_headerData + 37U, 5U); + cksum.update(m_headerData + 43U, 4U); + + unsigned char checkSum[2U]; + cksum.result(checkSum); + + m_headerData[47U] = checkSum[0]; + + m_headerData[48U] = SLOW_DATA_TYPE_HEADER | 1U; + m_headerData[49U] = checkSum[1]; + + m_headerPtr = 0U; +} + +void CSlowDataEncoder::setTextData(const wxString& text) +{ + clearInterleavedData(); + if(!m_textData) m_textData = new unsigned char[SLOW_DATA_FULL_BLOCK_SIZE]; + ::memset(m_textData, 'f', SLOW_DATA_FULL_BLOCK_SIZE); + + wxString paddedText = text; + paddedText.Append(wxT(" ")); + paddedText.Truncate(20U); + + m_textData[0U] = SLOW_DATA_TYPE_TEXT | 0U; + m_textData[1U] = paddedText.GetChar(0); + m_textData[2U] = paddedText.GetChar(1); + m_textData[3U] = paddedText.GetChar(2); + m_textData[4U] = paddedText.GetChar(3); + m_textData[5U] = paddedText.GetChar(4); + + m_textData[6U] = SLOW_DATA_TYPE_TEXT | 1U; + m_textData[7U] = paddedText.GetChar(5); + m_textData[8U] = paddedText.GetChar(6); + m_textData[9U] = paddedText.GetChar(7); + m_textData[10U] = paddedText.GetChar(8); + m_textData[11U] = paddedText.GetChar(9); + + m_textData[12U] = SLOW_DATA_TYPE_TEXT | 2U; + m_textData[13U] = paddedText.GetChar(10); + m_textData[14U] = paddedText.GetChar(11); + m_textData[15U] = paddedText.GetChar(12); + m_textData[16U] = paddedText.GetChar(13); + m_textData[17U] = paddedText.GetChar(14); + + m_textData[18U] = SLOW_DATA_TYPE_TEXT | 3U; + m_textData[19U] = paddedText.GetChar(15); + m_textData[20U] = paddedText.GetChar(16); + m_textData[21U] = paddedText.GetChar(17); + m_textData[22U] = paddedText.GetChar(18); + m_textData[23U] = paddedText.GetChar(19); + + m_textPtr = 0U; +} + +void CSlowDataEncoder::setGPSData(const wxString& gpsData) +{ + size_t gpsDataStrLen; + clearInterleavedData(); + + if(m_gpsData) delete[] m_gpsData; + m_gpsDataSize = 0U; + m_gpsPtr = 0U; + + if((gpsDataStrLen = gpsData.length()) > 0){ + unsigned int gpsDataPos; + unsigned int strPos = 0; + m_gpsDataSize = 1U + ((gpsDataStrLen - 1U) / 6U);//to make room for the type bytes + m_gpsDataSize += gpsDataStrLen; + m_gpsDataFullSize = SLOW_DATA_FULL_BLOCK_SIZE * (1U + ((m_gpsDataSize - 1U) / SLOW_DATA_FULL_BLOCK_SIZE)); + + m_gpsData = new unsigned char[m_gpsDataFullSize]; + ::memset(m_gpsData, 'f', m_gpsDataFullSize); + + for(gpsDataPos = 0; gpsDataPos < m_gpsDataFullSize;){ + unsigned int dataLen = gpsDataStrLen - strPos < 5U ? gpsDataStrLen - strPos : 5U; + m_gpsData[gpsDataPos++] = SLOW_DATA_TYPE_GPS | dataLen; + + for(unsigned int i = 0U; i < dataLen; i++){ + m_gpsData[gpsDataPos++] = gpsData.GetChar(strPos++); + } + } + } +} + diff --git a/Common/SlowDataEncoder.h b/Common/SlowDataEncoder.h new file mode 100644 index 0000000..5172466 --- /dev/null +++ b/Common/SlowDataEncoder.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2010 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; version 2 of the License. + * + * 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. + */ + +#ifndef SlowDataEncoder_H +#define SlowDataEncoder_H + +#include "HeaderData.h" + +#include + +class CSlowDataEncoder { +public: + CSlowDataEncoder(); + ~CSlowDataEncoder(); + + + void setHeaderData(const CHeaderData& header); + void setTextData(const wxString& text); + void setGPSData(const wxString& gpsData); + + void clearHeaderData(); + void clearTextData(); + void clearGPSData(); + void clearInterleavedData(); + + void getHeaderData(unsigned char* data); + void getTextData(unsigned char* data); + void getGPSData(unsigned char* data); + + void getInterleavedData(unsigned char* data); + + unsigned int getInterleavedDataLength(); + + void reset(); + void sync(); + +private: + void getData(unsigned char* source, unsigned char* data, unsigned int &sourcePtr, unsigned int sourceLength); + void buildInterleavedData(); + + unsigned char* m_headerData; + unsigned char* m_textData; + unsigned char* m_gpsData; + unsigned char* m_interleavedData; + + unsigned int m_headerPtr; + unsigned int m_textPtr; + unsigned int m_gpsPtr; + unsigned int m_interleavedPtr; + + unsigned int m_gpsDataSize; //actual useful data size + unsigned int m_gpsDataFullSize; //size including filler bytes + + unsigned int m_interleavedDataFullSize; //size of interleaved data including filler bytes +}; + +#endif diff --git a/Common/StarNetHandler.cpp b/Common/StarNetHandler.cpp new file mode 100644 index 0000000..3b33a0a --- /dev/null +++ b/Common/StarNetHandler.cpp @@ -0,0 +1,1338 @@ +/* + * 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 + * 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 "SlowDataEncoder.h" +#include "RepeaterHandler.h" +#include "StarNetHandler.h" +#include "DExtraHandler.h" // DEXTRA_LINK +#include "DStarDefines.h" +#include "DCSHandler.h" // DCS_LINK + +#include +#include +#include + +const unsigned int MESSAGE_DELAY = 4U; + +unsigned int CStarNetHandler::m_maxStarNets = 0U; +CStarNetHandler** CStarNetHandler::m_starNets = NULL; + +CG2ProtocolHandler* CStarNetHandler::m_g2Handler = NULL; +CIRCDDB* CStarNetHandler::m_irc = NULL; +CCacheManager* CStarNetHandler::m_cache = NULL; +wxString CStarNetHandler::m_gateway; + +wxString CStarNetHandler::m_name; +wxFFile* CStarNetHandler::m_logFile = NULL; + + +CStarNetUser::CStarNetUser(const wxString &callsign, unsigned int timeout) : +m_callsign(callsign), +m_timer(1000U, timeout) +{ + m_timer.start(); +} + +CStarNetUser::~CStarNetUser() +{ +} + +bool CStarNetUser::clock(unsigned int ms) +{ + m_timer.clock(ms); + + return m_timer.isRunning() && m_timer.hasExpired(); +} + +bool CStarNetUser::hasExpired() +{ + return m_timer.isRunning() && m_timer.hasExpired(); +} + +void CStarNetUser::reset() +{ + m_timer.start(); +} + +wxString CStarNetUser::getCallsign() const +{ + return m_callsign; +} + +CTimer CStarNetUser::getTimer() const +{ + return m_timer; +} + +CStarNetId::CStarNetId(unsigned int id, unsigned int timeout, CStarNetUser* user) : +m_id(id), +m_timer(1000U, timeout), +m_login(false), +m_info(false), +m_logoff(false), +m_end(false), +m_user(user), +m_textCollector() +{ + wxASSERT(user != NULL); + + m_timer.start(); +} + +CStarNetId::~CStarNetId() +{ +} + +unsigned int CStarNetId::getId() const +{ + return m_id; +} + +void CStarNetId::reset() +{ + m_timer.start(); +} + +void CStarNetId::setLogin() +{ + m_login = true; +} + +void CStarNetId::setInfo() +{ + if (!m_login && !m_logoff) + m_info = true; +} + +void CStarNetId::setLogoff() +{ + if (!m_login && !m_info) + m_logoff = true; +} + +void CStarNetId::setEnd() +{ + m_end = true; +} + +bool CStarNetId::clock(unsigned int ms) +{ + m_timer.clock(ms); + + return m_timer.isRunning() && m_timer.hasExpired(); +} + +bool CStarNetId::hasExpired() +{ + return m_timer.isRunning() && m_timer.hasExpired(); +} + +bool CStarNetId::isLogin() const +{ + return m_login; +} + +bool CStarNetId::isInfo() const +{ + return m_info; +} + +bool CStarNetId::isLogoff() const +{ + return m_logoff; +} + +bool CStarNetId::isEnd() const +{ + return m_end; +} + +CStarNetUser* CStarNetId::getUser() const +{ + return m_user; +} + +CTextCollector& CStarNetId::getTextCollector() +{ + return m_textCollector; +} + +void CStarNetHandler::initialise(unsigned int maxStarNets, const wxString& name) +{ + wxASSERT(maxStarNets > 0U); + + m_maxStarNets = maxStarNets; + m_name = name; + + m_starNets = new CStarNetHandler*[maxStarNets]; + + for (unsigned int i = 0U; i < maxStarNets; i++) + m_starNets[i] = NULL; +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetHandler::add(const wxString& callsign, const wxString& logoff, const wxString& repeater, const wxString& infoText, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +{ + CStarNetHandler* starNet = new CStarNetHandler(callsign, logoff, repeater, infoText, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); + + for (unsigned int i = 0U; i < m_maxStarNets; i++) { + if (m_starNets[i] == NULL) { + m_starNets[i] = starNet; + return; + } + } + + wxLogError(wxT("Cannot add StarNet group with callsign %s, no space"), callsign.c_str()); + + delete starNet; +} +#else +void CStarNetHandler::add(const wxString& callsign, const wxString& logoff, const wxString& repeater, const wxString& infoText, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +{ + CStarNetHandler* starNet = new CStarNetHandler(callsign, logoff, repeater, infoText, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); + + for (unsigned int i = 0U; i < m_maxStarNets; i++) { + if (m_starNets[i] == NULL) { + m_starNets[i] = starNet; + return; + } + } + + wxLogError(wxT("Cannot add StarNet group with callsign %s, no space"), callsign.c_str()); + + delete starNet; +} +#endif + +void CStarNetHandler::setG2Handler(CG2ProtocolHandler* handler) +{ + wxASSERT(handler != NULL); + + m_g2Handler = handler; +} + +void CStarNetHandler::setIRC(CIRCDDB* irc) +{ + wxASSERT(irc != NULL); + + m_irc = irc; +} + +void CStarNetHandler::setCache(CCacheManager* cache) +{ + wxASSERT(cache != NULL); + + m_cache = cache; +} + +void CStarNetHandler::setGateway(const wxString& gateway) +{ + m_gateway = gateway; +} + +void CStarNetHandler::setLogging(bool enable, const wxString& dir) +{ + if (!enable) + return; + + wxString fullName = STARNET_BASE_NAME; + + if (!m_name.IsEmpty()) { + fullName.Append(wxT("_")); + fullName.Append(m_name); + } + + wxFileName fileName(dir, fullName, wxT("log")); + + m_logFile = new wxFFile; + bool ret = m_logFile->Open(fileName.GetFullPath(), wxT("wt")); + if (!ret) { + wxLogError(wxT("Unable to open %s for writing"), fileName.GetFullPath().c_str()); + delete m_logFile; + m_logFile = NULL; + } +} + +CStarNetHandler* CStarNetHandler::findStarNet(const wxString& callsign) +{ + for (unsigned int i = 0U; i < m_maxStarNets; i++) { + CStarNetHandler* starNet = m_starNets[i]; + if (starNet != NULL) { + if (starNet->m_groupCallsign.IsSameAs(callsign)) + return starNet; + } + } + + return NULL; +} + +CStarNetHandler* CStarNetHandler::findStarNet(const CHeaderData& header) +{ + wxString your = header.getYourCall(); + + for (unsigned int i = 0U; i < m_maxStarNets; i++) { + CStarNetHandler* starNet = m_starNets[i]; + if (starNet != NULL) { + if (starNet->m_groupCallsign.IsSameAs(your)) + return starNet; + if (starNet->m_offCallsign.IsSameAs(your)) + return starNet; + } + } + + return NULL; +} + +CStarNetHandler* CStarNetHandler::findStarNet(const CAMBEData& data) +{ + unsigned int id = data.getId(); + + for (unsigned int i = 0U; i < m_maxStarNets; i++) { + CStarNetHandler* starNet = m_starNets[i]; + if (starNet != NULL) { + if (starNet->m_id == id) + return starNet; + } + } + + return NULL; +} + +wxArrayString CStarNetHandler::listStarNets() +{ + wxArrayString starNets; + + for (unsigned int i = 0U; i < m_maxStarNets; i++) { + CStarNetHandler* starNet = m_starNets[i]; + if (starNet != NULL) + starNets.Add(starNet->m_groupCallsign); + } + + return starNets; +} + +CRemoteStarNetGroup* CStarNetHandler::getInfo() const +{ + CRemoteStarNetGroup* data = new CRemoteStarNetGroup(m_groupCallsign, m_offCallsign, m_groupTimer.getTimer(), m_groupTimer.getTimeout()); + + for (CStarNetUsersHashMap::const_iterator it = m_users.begin(); it != m_users.end(); ++it) { + CStarNetUser* user = it->second; + data->addUser(user->getCallsign(), user->getTimer().getTimer(), user->getTimer().getTimeout()); + } + + return data; +} + +void CStarNetHandler::finalise() +{ + for (unsigned int i = 0U; i < m_maxStarNets; i++) + delete m_starNets[i]; + + delete[] m_starNets; + + if (m_logFile != NULL) { + m_logFile->Close(); + delete m_logFile; + } +} + +void CStarNetHandler::clock(unsigned int ms) +{ + for (unsigned int i = 0U; i < m_maxStarNets; i++) { + if (m_starNets[i] != NULL) + m_starNets[i]->clockInt(ms); + } +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetHandler::link() +{ + for (unsigned int i = 0U; i < m_maxStarNets; i++) { + if (m_starNets[i] != NULL) + m_starNets[i]->linkInt(); + } +} +#endif + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +CStarNetHandler::CStarNetHandler(const wxString& callsign, const wxString& logoff, const wxString& repeater, const wxString& infoText, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) : +m_groupCallsign(callsign), +m_offCallsign(logoff), +m_shortCallsign(wxT("SNET")), +m_repeater(repeater), +m_infoText(infoText), +m_permanent(), +m_linkReflector(reflector), +m_linkGateway(), +m_linkStatus(LS_NONE), +m_linkTimer(1000U, NETWORK_TIMEOUT), +m_id(0x00U), +m_groupTimer(1000U, groupTimeout * 60U), +m_announceTimer(1000U, 2U * 60U), // 2 minutes +m_userTimeout(userTimeout), +m_callsignSwitch(callsignSwitch), +m_txMsgSwitch(txMsgSwitch), +m_ids(), +m_users(), +m_repeaters() +{ + m_announceTimer.start(); + + // Create the short version of the STARnet Group callsign + wxString rest; + if (m_groupCallsign.StartsWith(wxT("STN"), &rest)) { + wxChar c = m_groupCallsign.GetChar(7U); + if (c == wxT(' ')) + m_shortCallsign.Printf(wxT("S%s"), rest.Left(3U).c_str()); + else + m_shortCallsign.Printf(wxT("%s%c"), rest.Left(3U).c_str(), c); + } + + wxStringTokenizer tkn(permanent, wxT(",")); + while (tkn.HasMoreTokens()) { + wxString callsign = tkn.GetNextToken(); + callsign.resize(LONG_CALLSIGN_LENGTH, wxT(' ')); + m_permanent.Add(callsign); + } +} +#else +CStarNetHandler::CStarNetHandler(const wxString& callsign, const wxString& logoff, const wxString& repeater, const wxString& infoText, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) : +m_groupCallsign(callsign), +m_offCallsign(logoff), +m_shortCallsign(wxT("SNET")), +m_repeater(repeater), +m_infoText(infoText), +m_permanent(), +m_id(0x00U), +m_groupTimer(1000U, groupTimeout * 60U), +m_announceTimer(1000U, 2U * 60U), // 2 minutes +m_userTimeout(userTimeout), +m_callsignSwitch(callsignSwitch), +m_txMsgSwitch(txMsgSwitch), +m_ids(), +m_users(), +m_repeaters() +{ + m_announceTimer.start(); + + // Create the short version of the STARnet Group callsign + wxString rest; + if (m_groupCallsign.StartsWith(wxT("STN"), &rest)) { + wxChar c = m_groupCallsign.GetChar(7U); + if (c == wxT(' ')) + m_shortCallsign.Printf(wxT("S%s"), rest.Left(3U).c_str()); + else + m_shortCallsign.Printf(wxT("%s%c"), rest.Left(3U).c_str(), c); + } + + wxStringTokenizer tkn(permanent, wxT(",")); + while (tkn.HasMoreTokens()) { + wxString callsign = tkn.GetNextToken(); + callsign.resize(LONG_CALLSIGN_LENGTH, wxT(' ')); + m_permanent.Add(callsign); + } +} +#endif + +CStarNetHandler::~CStarNetHandler() +{ + for (CStarNetUsersHashMap::iterator it = m_users.begin(); it != m_users.end(); ++it) + delete it->second; + + for (CStarNetRepeatersHashMap::iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) + delete it->second; + + m_users.clear(); + m_repeaters.clear(); +} + +void CStarNetHandler::process(CHeaderData &header) +{ + wxString my = header.getMyCall1(); + wxString your = header.getYourCall(); + + unsigned int id = header.getId(); + + CStarNetUser* user = m_users[my]; + + // Ensure that this user is in the cache + CUserData* userData = m_cache->findUser(my); + if (userData == NULL) + m_irc->findUser(my); + + if (your.IsSameAs(m_groupCallsign)) { + // This is a normal message for logging in/relaying + if (user == NULL) { + // This is a new user, add them to the list + if (m_logFile != NULL) { + time_t timeNow = ::time(NULL); + struct tm* tm = ::gmtime(&timeNow); + + wxString text; + text.Printf(wxT("%04d-%02d-%02d %02d:%02d:%02d: Adding %s to StarNet group %s\n"), + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, + my.c_str(), m_groupCallsign.c_str()); + + m_logFile->Write(text); + m_logFile->Flush(); + } + + // Start the StarNet group timer if not already running + if (!m_groupTimer.isRunning()) + m_groupTimer.start(); + + user = new CStarNetUser(my, m_userTimeout * 60U); + m_users[my] = user; + + CStarNetId* tx = new CStarNetId(id, MESSAGE_DELAY, user); + tx->setLogin(); + m_ids[id] = tx; + } else { + user->reset(); + + // Check that it isn't a duplicate header + CStarNetId* tx = m_ids[id]; + if (tx != NULL) { + delete userData; + return; + } + + m_ids[id] = new CStarNetId(id, MESSAGE_DELAY, user); + } + } else { + delete userData; + userData = NULL; + + // This is a logoff message + if (user == NULL) // Not a known user, ignore + return; + + if (m_logFile != NULL) { + time_t timeNow = ::time(NULL); + struct tm* tm = ::gmtime(&timeNow); + + wxString text; + text.Printf(wxT("%04d-%02d-%02d %02d:%02d:%02d: Removing %s from StarNet group %s, logged off\n"), + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, + user->getCallsign().c_str(), m_groupCallsign.c_str()); + + m_logFile->Write(text); + m_logFile->Flush(); + } + + // Remove the user from the user list + m_users.erase(my); + + CStarNetId* tx = new CStarNetId(id, MESSAGE_DELAY, user); + tx->setLogoff(); + m_ids[id] = tx; + + return; + } + + m_groupTimer.start(); + + if (m_id != 0x00U) { + delete userData; + return; + } + + m_id = id; + + // Change the Your callsign to CQCQCQ + header.setCQCQCQ(); + + header.setFlag1(0x00); + header.setFlag2(0x00); + header.setFlag3(0x00); + +#if defined(DEXTRA_LINK) + header.setRepeaters(m_linkGateway, m_linkReflector); + CDExtraHandler::writeHeader(this, header, DIR_OUTGOING); +#endif +#if defined(DCS_LINK) + header.setRepeaters(m_linkGateway, m_linkReflector); + CDCSHandler::writeHeader(this, header, DIR_OUTGOING); +#endif + + // Get the home repeater of the user + wxString exclude; + if (userData != NULL) { + exclude = userData->getRepeater(); + delete userData; + userData = NULL; + } + + // Build new repeater list + for (CStarNetUsersHashMap::const_iterator it = m_users.begin(); it != m_users.end(); ++it) { + CStarNetUser* user = it->second; + if (user != NULL) { + // Find the user in the cache + CUserData* userData = m_cache->findUser(user->getCallsign()); + + if (userData != NULL) { + // Check for the excluded repeater + if (!userData->getRepeater().IsSameAs(exclude)) { + // Find the users repeater in the repeater list, add it otherwise + CStarNetRepeater* repeater = m_repeaters[userData->getRepeater()]; + if (repeater == NULL) { + // Add a new repeater entry + repeater = new CStarNetRepeater; + repeater->m_destination = wxT("/") + userData->getRepeater().Left(6U) + userData->getRepeater().Right(1U); + repeater->m_repeater = userData->getRepeater(); + repeater->m_gateway = userData->getGateway(); + repeater->m_address = userData->getAddress(); + repeater->m_local = CRepeaterHandler::findDVRepeater(userData->getRepeater()); + m_repeaters[userData->getRepeater()] = repeater; + } + } + + delete userData; + userData = NULL; + } + } + } + + switch (m_callsignSwitch) { + case SCS_GROUP_CALLSIGN: + // Change the My Callsign 1 to be that of the StarNet group + header.setMyCall1(m_groupCallsign); + header.setMyCall2(wxT("SNET")); + break; + case SCS_USER_CALLSIGN: + // Change the My Callsign 2 to be that of the StarNet group + header.setMyCall1(my); + header.setMyCall2(m_shortCallsign); + break; + default: + break; + } + + sendToRepeaters(header); + + if (m_txMsgSwitch) + sendFromText(my); +} + +void CStarNetHandler::process(CAMBEData &data) +{ + unsigned int id = data.getId(); + + CStarNetId* tx = m_ids[id]; + if (tx == NULL) + return; + + tx->reset(); + + CStarNetUser* user = tx->getUser(); + user->reset(); + + m_groupTimer.start(); + + // If we've just logged in, the LOGOFF and INFO commands are disabled + if (!tx->isLogin()) { + // If we've already found some slow data, then don't look again + if (!tx->isLogoff() && !tx->isInfo()) { + tx->getTextCollector().writeData(data); + bool hasText = tx->getTextCollector().hasData(); + if (hasText) { + wxString text = tx->getTextCollector().getData(); + + if (text.Left(6U).IsSameAs(wxT("LOGOFF"), false)) { + if (m_logFile != NULL) { + time_t timeNow = ::time(NULL); + struct tm* tm = ::gmtime(&timeNow); + + wxString text; + text.Printf(wxT("%04d-%02d-%02d %02d:%02d:%02d: Removing %s from StarNet group %s, logged off\n"), + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, + user->getCallsign().c_str(), m_groupCallsign.c_str()); + + m_logFile->Write(text); + m_logFile->Flush(); + } + + tx->setLogoff(); + + // Ensure that this user is in the cache in time for the logoff ack + CUserData* cacheUser = m_cache->findUser(user->getCallsign()); + if (cacheUser == NULL) + m_irc->findUser(user->getCallsign()); + + delete cacheUser; + cacheUser = NULL; + } + + if (text.Left(4U).IsSameAs(wxT("INFO"), false)) { + tx->setInfo(); + + // Ensure that this user is in the cache in time for the info text + CUserData* cacheUser = m_cache->findUser(user->getCallsign()); + if (cacheUser == NULL) + m_irc->findUser(user->getCallsign()); + + delete cacheUser; + cacheUser = NULL; + } + } + } + } + + if (id == m_id) { +#if defined(DEXTRA_LINK) + CDExtraHandler::writeAMBE(this, data, DIR_OUTGOING); +#endif +#if defined(DCS_LINK) + CDCSHandler::writeAMBE(this, data, DIR_OUTGOING); +#endif + sendToRepeaters(data); + } + + if (data.isEnd()) { + if (id == m_id) { + // Clear the repeater list if we're the relayed id + for (CStarNetRepeatersHashMap::iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) + delete it->second; + m_repeaters.clear(); + m_id = 0x00U; + } + + if (tx->isLogin()) { + tx->reset(); + tx->setEnd(); + } else if (tx->isLogoff()) { + m_users.erase(user->getCallsign()); + tx->reset(); + tx->setEnd(); + } else if (tx->isInfo()) { + tx->reset(); + tx->setEnd(); + } else { + m_ids.erase(tx->getId()); + delete tx; + } + } +} + +bool CStarNetHandler::logoff(const wxString &callsign) +{ + if (callsign.IsSameAs(wxT("ALL "))) { + for (CStarNetUsersHashMap::iterator it = m_users.begin(); it != m_users.end(); ++it) { + CStarNetUser* user = it->second; + + if (user != NULL && m_logFile != NULL) { + time_t timeNow = ::time(NULL); + struct tm* tm = ::gmtime(&timeNow); + + wxString text; + text.Printf(wxT("%04d-%02d-%02d %02d:%02d:%02d: Removing %s from StarNet group %s, logged off by remote control\n"), + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, + user->getCallsign().c_str(), m_groupCallsign.c_str()); + + m_logFile->Write(text); + } + + delete user; + } + + if (m_logFile != NULL) + m_logFile->Flush(); + + for (CStarNetIdsHashMap::iterator it = m_ids.begin(); it != m_ids.end(); ++it) + delete it->second; + + for (CStarNetRepeatersHashMap::iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) + delete it->second; + + m_users.clear(); + m_ids.clear(); + m_repeaters.end(); + + m_groupTimer.stop(); + m_id = 0x00U; + + return true; + } else { + CStarNetUser* user = m_users[callsign]; + if (user == NULL) { + wxLogMessage(wxT("Invalid callsign asked to logoff")); + return false; + } + + if (m_logFile != NULL) { + time_t timeNow = ::time(NULL); + struct tm* tm = ::gmtime(&timeNow); + + wxString text; + text.Printf(wxT("%04d-%02d-%02d %02d:%02d:%02d: Removing %s from StarNet group %s, logged off by remote control\n"), + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, + user->getCallsign().c_str(), m_groupCallsign.c_str()); + + m_logFile->Write(text); + m_logFile->Flush(); + } + + // Find any associated id structure associated with this use, and the logged off user is the + // currently relayed one, remove his id. + for (CStarNetIdsHashMap::iterator it = m_ids.begin(); it != m_ids.end(); ++it) { + CStarNetId* id = it->second; + if (id != NULL && id->getUser() == user) { + if (id->getId() == m_id) + m_id = 0x00U; + + m_ids.erase(it); + delete id; + break; + } + } + + m_users.erase(callsign); + delete user; + + // Check to see if we have any users left + unsigned int count = 0U; + for (CStarNetUsersHashMap::iterator it = m_users.begin(); it != m_users.end(); ++it) { + if (it->second != NULL) + count++; + } + + // If none then clear all the data structures + if (count == 0U) { + for (CStarNetUsersHashMap::iterator it = m_users.begin(); it != m_users.end(); ++it) + delete it->second; + for (CStarNetIdsHashMap::iterator it = m_ids.begin(); it != m_ids.end(); ++it) + delete it->second; + for (CStarNetRepeatersHashMap::iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) + delete it->second; + + m_users.clear(); + m_ids.clear(); + m_repeaters.end(); + + m_groupTimer.stop(); + m_id = 0x00U; + } + + return true; + } +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +bool CStarNetHandler::process(CHeaderData &header, DIRECTION, AUDIO_SOURCE) +{ + if (m_id != 0x00U) + return false; + + wxString my = header.getMyCall1(); + m_id = header.getId(); + + m_linkTimer.start(); + + // Change the Your callsign to CQCQCQ + header.setCQCQCQ(); + + header.setFlag1(0x00); + header.setFlag2(0x00); + header.setFlag3(0x00); + + // Build new repeater list + for (CStarNetUsersHashMap::const_iterator it = m_users.begin(); it != m_users.end(); ++it) { + CStarNetUser* user = it->second; + if (user != NULL) { + // Find the user in the cache + CUserData* userData = m_cache->findUser(user->getCallsign()); + + if (userData != NULL) { + // Find the users repeater in the repeater list, add it otherwise + CStarNetRepeater* repeater = m_repeaters[userData->getRepeater()]; + if (repeater == NULL) { + // Add a new repeater entry + repeater = new CStarNetRepeater; + repeater->m_destination = wxT("/") + userData->getRepeater().Left(6U) + userData->getRepeater().Right(1U); + repeater->m_repeater = userData->getRepeater(); + repeater->m_gateway = userData->getGateway(); + repeater->m_address = userData->getAddress(); + repeater->m_local = CRepeaterHandler::findDVRepeater(userData->getRepeater()); + m_repeaters[userData->getRepeater()] = repeater; + } + + delete userData; + userData = NULL; + } + } + } + + switch (m_callsignSwitch) { + case SCS_GROUP_CALLSIGN: + // Change the My Callsign 1 to be that of the StarNet group + header.setMyCall1(m_groupCallsign); + header.setMyCall2(wxT("SNET")); + break; + case SCS_USER_CALLSIGN: + // Change the My Callsign 2 to be that of the StarNet group + header.setMyCall1(my); + header.setMyCall2(m_shortCallsign); + break; + default: + break; + } + + sendToRepeaters(header); + + if (m_txMsgSwitch) + sendFromText(my); + + return true; +} + +bool CStarNetHandler::process(CAMBEData &data, DIRECTION, AUDIO_SOURCE) +{ + unsigned int id = data.getId(); + if (id != m_id) + return false; + + m_linkTimer.start(); + + sendToRepeaters(data); + + if (data.isEnd()) { + m_linkTimer.stop(); + m_id = 0x00U; + + // Clear the repeater list + for (CStarNetRepeatersHashMap::iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) + delete it->second; + m_repeaters.clear(); + } + + return true; +} +#endif + +#if defined(DEXTRA_LINK) +void CStarNetHandler::linkInt() +{ + if (m_linkReflector.IsEmpty()) + return; + + wxLogMessage(wxT("Linking %s at startup to DExtra reflector %s"), m_repeater.c_str(), m_linkReflector.c_str()); + + // Find the repeater to link to + CRepeaterData* data = m_cache->findRepeater(m_linkReflector); + if (data == NULL) { + wxLogError(wxT("Cannot find the reflector in the cache, not linking")); + return; + } + + m_linkGateway = data->getGateway(); + m_linkStatus = LS_LINKING_DEXTRA; + + CDExtraHandler::link(this, m_repeater, m_linkReflector, data->getAddress()); + + delete data; +} +#endif + +#if defined(DCS_LINK) +void CStarNetHandler::linkInt() +{ + if (m_linkReflector.IsEmpty()) + return; + + wxLogMessage(wxT("Linking %s at startup to DCS reflector %s"), m_repeater.c_str(), m_linkReflector.c_str()); + + // Find the repeater to link to + CRepeaterData* data = m_cache->findRepeater(m_linkReflector); + if (data == NULL) { + wxLogError(wxT("Cannot find the reflector in the cache, not linking")); + return; + } + + m_linkGateway = data->getGateway(); + m_linkStatus = LS_LINKING_DCS; + + CDCSHandler::link(this, m_repeater, m_linkReflector, data->getAddress()); + + delete data; +} +#endif + +void CStarNetHandler::clockInt(unsigned int ms) +{ +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_linkTimer.clock(ms); + if (m_linkTimer.isRunning() && m_linkTimer.hasExpired()) { + m_linkTimer.stop(); + m_id = 0x00U; + + // Clear the repeater list + for (CStarNetRepeatersHashMap::iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) + delete it->second; + m_repeaters.clear(); + } +#endif + + m_announceTimer.clock(ms); + if (m_announceTimer.hasExpired()) { + m_irc->sendHeardWithTXMsg(m_groupCallsign, wxT(" "), wxT("CQCQCQ "), m_repeater, m_gateway, 0x00U, 0x00U, 0x00U, wxEmptyString, m_infoText); + + if (!m_offCallsign.IsEmpty() && !m_offCallsign.IsSameAs(wxT(" "))) + m_irc->sendHeardWithTXMsg(m_offCallsign, wxT(" "), wxT("CQCQCQ "), m_repeater, m_gateway, 0x00U, 0x00U, 0x00U, wxEmptyString, m_infoText); + + m_announceTimer.start(60U * 60U); // 1 hour + } + + // For each incoming id + for (CStarNetIdsHashMap::iterator it = m_ids.begin(); it != m_ids.end(); ++it) { + CStarNetId* tx = it->second; + + if (tx != NULL && tx->clock(ms)) { + wxString callsign = tx->getUser()->getCallsign(); + + if (tx->isEnd()) { + CUserData* user = m_cache->findUser(callsign); + if (user != NULL) { + if (tx->isLogin()) + sendAck(*user, wxT("Logged in")); + else if (tx->isInfo()) + sendAck(*user, m_infoText); + else if (tx->isLogoff()) + sendAck(*user, wxT("Logged off")); + + delete user; + user = NULL; + } else { + wxLogError(wxT("Cannot find %s in the cache"), callsign.c_str()); + } + + delete tx; + m_ids.erase(it); + + // The iterator is now invalid, so we'll find the next expiry on the next clock tick with a + // new iterator + break; + } else { + if (tx->getId() == m_id) { + // Clear the repeater list if we're the relayed id + for (CStarNetRepeatersHashMap::iterator it1 = m_repeaters.begin(); it1 != m_repeaters.end(); ++it1) + delete it1->second; + m_repeaters.clear(); + m_id = 0x00U; + } + + if (tx->isLogin()) { + tx->reset(); + tx->setEnd(); + } else if (tx->isLogoff()) { + m_users.erase(callsign); + tx->reset(); + tx->setEnd(); + } else if (tx->isInfo()) { + tx->reset(); + tx->setEnd(); + } else { + delete tx; + m_ids.erase(it); + // The iterator is now invalid, so we'll find the next expiry on the next clock tick with a + // new iterator + break; + } + } + } + } + + // Individual user expiry, but not for the permanent entries + for (CStarNetUsersHashMap::iterator it = m_users.begin(); it != m_users.end(); ++it) { + CStarNetUser* user = it->second; + if (user != NULL && m_permanent.Index(user->getCallsign()) == wxNOT_FOUND) + user->clock(ms); + } + + // Handle the group expiry timer + m_groupTimer.clock(ms); + + // Don't do timeouts when relaying audio + if (m_id != 0x00U) + return; + + if (m_groupTimer.isRunning() && m_groupTimer.hasExpired()) { + std::vector permanent; + + // Clear all the users, except the permenent one + for (CStarNetUsersHashMap::iterator it = m_users.begin(); it != m_users.end(); ++it) { + CStarNetUser* user = it->second; + + if (user != NULL) { + if (m_permanent.Index(user->getCallsign()) != wxNOT_FOUND) { + permanent.push_back(user); + } else { + if (m_logFile != NULL) { + time_t timeNow = ::time(NULL); + struct tm* tm = ::gmtime(&timeNow); + + wxString text; + text.Printf(wxT("%04d-%02d-%02d %02d:%02d:%02d: Removing %s from StarNet group %s, group timeout\n"), + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, + user->getCallsign().c_str(), m_groupCallsign.c_str()); + + m_logFile->Write(text); + m_logFile->Flush(); + } + + delete user; + } + } + } + + m_users.clear(); + + // Re-insert the permenent users + for (std::vector::const_iterator it = permanent.begin(); it != permanent.end(); ++it) { + CStarNetUser* user = *it; + wxString callsign = user->getCallsign(); + m_users[callsign] = user; + } + + m_groupTimer.stop(); + } + + // Individual user expiry + for (CStarNetUsersHashMap::iterator it = m_users.begin(); it != m_users.end(); ++it) { + CStarNetUser* user = it->second; + if (user != NULL && user->hasExpired()) { + if (m_logFile != NULL) { + time_t timeNow = ::time(NULL); + struct tm* tm = ::gmtime(&timeNow); + + wxString text; + text.Printf(wxT("%04d-%02d-%02d %02d:%02d:%02d: Removing %s from StarNet group %s, user timeout\n"), + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, + user->getCallsign().c_str(), m_groupCallsign.c_str()); + + m_logFile->Write(text); + m_logFile->Flush(); + } + + delete user; + m_users.erase(it); + // The iterator is now invalid, so we'll find the next expiry on the next clock tick with a + // new iterator + break; + } + } +} + +void CStarNetHandler::sendToRepeaters(CHeaderData& header) const +{ + for (CStarNetRepeatersHashMap::const_iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) { + CStarNetRepeater* repeater = it->second; + if (repeater != NULL) { + header.setYourCall(repeater->m_destination); + header.setDestination(repeater->m_address, G2_DV_PORT); + header.setRepeaters(repeater->m_gateway, repeater->m_repeater); + if (repeater->m_local != NULL) + repeater->m_local->process(header, DIR_INCOMING, AS_G2); + else + m_g2Handler->writeHeader(header); + } + } +} + +void CStarNetHandler::sendToRepeaters(CAMBEData& data) const +{ + for (CStarNetRepeatersHashMap::const_iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) { + CStarNetRepeater* repeater = it->second; + if (repeater != NULL) { + data.setDestination(repeater->m_address, G2_DV_PORT); + if (repeater->m_local != NULL) + repeater->m_local->process(data, DIR_INCOMING, AS_G2); + else + m_g2Handler->writeAMBE(data); + } + } +} + +void CStarNetHandler::sendFromText(const wxString& my) const +{ + wxString text; + switch (m_callsignSwitch) { + case SCS_GROUP_CALLSIGN: + text.Printf(wxT("FROM %s"), my.c_str()); + break; + case SCS_USER_CALLSIGN: + text.Printf(wxT("VIA STARnet %s"), m_groupCallsign.c_str()); + break; + default: + break; + } + + CSlowDataEncoder slowData; + slowData.setTextData(text); + + CAMBEData data; + data.setId(m_id); + + unsigned char buffer[DV_FRAME_LENGTH_BYTES]; + ::memcpy(buffer + 0U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + + for (unsigned int i = 0U; i < 21U; i++) { + if (i == 0U) { + // The first AMBE packet is a sync + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); + data.setData(buffer, DV_FRAME_LENGTH_BYTES); + data.setSeq(i); + } else { + // The packets containing the text data + unsigned char slowDataBuffer[DATA_FRAME_LENGTH_BYTES]; + slowData.getTextData(slowDataBuffer); + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, slowDataBuffer, DATA_FRAME_LENGTH_BYTES); + data.setData(buffer, DV_FRAME_LENGTH_BYTES); + data.setSeq(i); + } + + sendToRepeaters(data); + } +} + +void CStarNetHandler::sendAck(const CUserData& user, const wxString& text) const +{ + unsigned int id = CHeaderData::createId(); + + CHeaderData header(m_groupCallsign, wxT(" "), user.getUser(), user.getGateway(), user.getRepeater()); + header.setDestination(user.getAddress(), G2_DV_PORT); + header.setId(id); + m_g2Handler->writeHeader(header); + + CSlowDataEncoder slowData; + slowData.setTextData(text); + + CAMBEData data; + data.setId(id); + data.setDestination(user.getAddress(), G2_DV_PORT); + + unsigned char buffer[DV_FRAME_MAX_LENGTH_BYTES]; + ::memcpy(buffer + 0U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + + for (unsigned int i = 0U; i < 20U; i++) { + if (i == 0U) { + // The first AMBE packet is a sync + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); + data.setData(buffer, DV_FRAME_LENGTH_BYTES); + data.setSeq(i); + } else if (i == 19U) { + // The last packet of the ack + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, END_PATTERN_BYTES, END_PATTERN_LENGTH_BYTES); + data.setData(buffer, DV_FRAME_MAX_LENGTH_BYTES); + data.setSeq(i); + data.setEnd(true); + } else { + // The packets containing the text data + unsigned char slowDataBuffer[DATA_FRAME_LENGTH_BYTES]; + slowData.getTextData(slowDataBuffer); + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, slowDataBuffer, DATA_FRAME_LENGTH_BYTES); + data.setData(buffer, DV_FRAME_LENGTH_BYTES); + data.setSeq(i); + } + + m_g2Handler->writeAMBE(data); + } +} + +#if defined(DEXTRA_LINK) +void CStarNetHandler::linkUp(DSTAR_PROTOCOL, const wxString& callsign) +{ + wxLogMessage(wxT("DExtra link to %s established"), callsign.c_str()); + + m_linkStatus = LS_LINKED_DEXTRA; +} + +bool CStarNetHandler::linkFailed(DSTAR_PROTOCOL, const wxString& callsign, bool isRecoverable) +{ + if (!isRecoverable) { + if (m_linkStatus != LS_NONE) { + wxLogMessage(wxT("DExtra link to %s has failed"), callsign.c_str()); + m_linkStatus = LS_NONE; + } + + return false; + } + + if (m_linkStatus == LS_LINKING_DEXTRA || m_linkStatus == LS_LINKED_DEXTRA) { + wxLogMessage(wxT("DExtra link to %s has failed, relinking"), callsign.c_str()); + m_linkStatus = LS_LINKING_DEXTRA; + return true; + } + + return false; +} + +void CStarNetHandler::linkRefused(DSTAR_PROTOCOL, const wxString& callsign) +{ + if (m_linkStatus != LS_NONE) { + wxLogMessage(wxT("DExtra link to %s was refused"), callsign.c_str()); + m_linkStatus = LS_NONE; + } +} + +bool CStarNetHandler::singleHeader() +{ + return true; +} +#endif + +#if defined(DCS_LINK) +void CStarNetHandler::linkUp(DSTAR_PROTOCOL, const wxString& callsign) +{ + wxLogMessage(wxT("DCS link to %s established"), callsign.c_str()); + + m_linkStatus = LS_LINKED_DCS; +} + +void CStarNetHandler::linkRefused(DSTAR_PROTOCOL, const wxString& callsign) +{ + if (m_linkStatus != LS_NONE) { + wxLogMessage(wxT("DCS link to %s was refused"), callsign.c_str()); + m_linkStatus = LS_NONE; + } +} + +bool CStarNetHandler::linkFailed(DSTAR_PROTOCOL, const wxString& callsign, bool isRecoverable) +{ + if (!isRecoverable) { + if (m_linkStatus != LS_NONE) { + wxLogMessage(wxT("DCS link to %s has failed"), callsign.c_str()); + m_linkStatus = LS_NONE; + } + + return false; + } + + if (m_linkStatus == LS_LINKING_DCS || m_linkStatus == LS_LINKED_DCS) { + wxLogMessage(wxT("DCS link to %s has failed, relinking"), callsign.c_str()); + m_linkStatus = LS_LINKING_DCS; + return true; + } + + return false; +} + +bool CStarNetHandler::singleHeader() +{ + return true; +} +#endif diff --git a/Common/StarNetHandler.h b/Common/StarNetHandler.h new file mode 100644 index 0000000..067d6cd --- /dev/null +++ b/Common/StarNetHandler.h @@ -0,0 +1,221 @@ +/* + * 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 + * 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 StarNetHandler_H +#define StarNetHandler_H + +#include "RemoteStarNetGroup.h" +#include "G2ProtocolHandler.h" +#include "ReflectorCallback.h" // DEXTRA_LINK || DCS_LINK +#include "RepeaterCallback.h" +#include "TextCollector.h" +#include "CacheManager.h" +#include "HeaderData.h" +#include "AMBEData.h" +#include "IRCDDB.h" +#include "Timer.h" + +#if defined(__WINDOWS__) +#include "Inaddr.h" +#else +#include +#endif + +#include + +class CStarNetUser { +public: + CStarNetUser(const wxString& callsign, unsigned int timeout); + ~CStarNetUser(); + + void reset(); + + bool clock(unsigned int ms); + bool hasExpired(); + + wxString getCallsign() const; + CTimer getTimer() const; + +private: + wxString m_callsign; + CTimer m_timer; +}; + +class CStarNetId { +public: + CStarNetId(unsigned int id, unsigned int timeout, CStarNetUser* user); + ~CStarNetId(); + + unsigned int getId() const; + + void reset(); + + void setLogin(); + void setInfo(); + void setLogoff(); + void setEnd(); + + bool clock(unsigned int ms); + bool hasExpired(); + + bool isLogin() const; + bool isInfo() const; + bool isLogoff() const; + bool isEnd() const; + + CStarNetUser* getUser() const; + + CTextCollector& getTextCollector(); + +private: + unsigned int m_id; + CTimer m_timer; + bool m_login; + bool m_info; + bool m_logoff; + bool m_end; + CStarNetUser* m_user; + CTextCollector m_textCollector; +}; + +class CStarNetRepeater { +public: + wxString m_destination; + wxString m_repeater; + wxString m_gateway; + in_addr m_address; + IRepeaterCallback* m_local; +}; + +WX_DECLARE_HASH_MAP(unsigned int, CStarNetId*, wxIntegerHash, wxIntegerEqual, CStarNetIdsHashMap); +WX_DECLARE_STRING_HASH_MAP(CStarNetUser*, CStarNetUsersHashMap); +WX_DECLARE_STRING_HASH_MAP(CStarNetRepeater*, CStarNetRepeatersHashMap); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +class CStarNetHandler : public IReflectorCallback { +#else +class CStarNetHandler { +#endif +public: + static void initialise(unsigned int maxStarNets, const wxString& name = wxEmptyString); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + static void add(const wxString& callsign, const wxString& logoff, const wxString& repeater, const wxString& infoText, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); +#else + static void add(const wxString& callsign, const wxString& logoff, const wxString& repeater, const wxString& infoText, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); +#endif + + static void setG2Handler(CG2ProtocolHandler* handler); + static void setIRC(CIRCDDB* irc); + static void setCache(CCacheManager* cache); + static void setGateway(const wxString& gateway); + static void setLogging(bool enable, const wxString& dir); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + static void link(); +#endif + + static wxArrayString listStarNets(); + + static CStarNetHandler* findStarNet(const wxString& callsign); + static CStarNetHandler* findStarNet(const CHeaderData& header); + static CStarNetHandler* findStarNet(const CAMBEData& data); + + static void finalise(); + + static void clock(unsigned int ms); + + void process(CHeaderData& header); + void process(CAMBEData& data); + + CRemoteStarNetGroup* getInfo() const; + + bool logoff(const wxString& callsign); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + virtual bool process(CHeaderData& header, DIRECTION direction, AUDIO_SOURCE source); + virtual bool process(CAMBEData& data, DIRECTION direction, AUDIO_SOURCE source); + + virtual void linkUp(DSTAR_PROTOCOL protocol, const wxString& callsign); + virtual void linkRefused(DSTAR_PROTOCOL protocol, const wxString& callsign); + virtual bool linkFailed(DSTAR_PROTOCOL protocol, const wxString& callsign, bool isRecoverable); + + virtual bool singleHeader(); +#endif + +protected: +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + CStarNetHandler(const wxString& callsign, const wxString& logoff, const wxString& repeater, const wxString& infoText, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); +#else + CStarNetHandler(const wxString& callsign, const wxString& logoff, const wxString& repeater, const wxString& infoText, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); +#endif + ~CStarNetHandler(); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + void linkInt(); +#endif + + void clockInt(unsigned int ms); + +private: + static unsigned int m_maxStarNets; + static CStarNetHandler** m_starNets; + + static CG2ProtocolHandler* m_g2Handler; + static CIRCDDB* m_irc; + static CCacheManager* m_cache; + static wxString m_gateway; + + static wxString m_name; + static wxFFile* m_logFile; + + // Group info + wxString m_groupCallsign; + wxString m_offCallsign; + wxString m_shortCallsign; + wxString m_repeater; + wxString m_infoText; + wxArrayString m_permanent; +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + wxString m_linkReflector; + wxString m_linkGateway; + LINK_STATUS m_linkStatus; + CTimer m_linkTimer; +#endif + + unsigned int m_id; + + CTimer m_groupTimer; + CTimer m_announceTimer; + + unsigned int m_userTimeout; + + STARNET_CALLSIGN_SWITCH m_callsignSwitch; + bool m_txMsgSwitch; + + CStarNetIdsHashMap m_ids; + CStarNetUsersHashMap m_users; + CStarNetRepeatersHashMap m_repeaters; + + void sendFromText(const wxString& text) const; + void sendToRepeaters(CHeaderData& header) const; + void sendToRepeaters(CAMBEData& data) const; + void sendAck(const CUserData& user, const wxString& text) const; +}; + +#endif diff --git a/Common/StatusData.cpp b/Common/StatusData.cpp new file mode 100644 index 0000000..e83ce8f --- /dev/null +++ b/Common/StatusData.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2011,2012 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 "StatusData.h" + +#include "DStarDefines.h" +#include "Utils.h" + +CStatusData::CStatusData(const wxString& text, unsigned int n) : +m_data(NULL), +m_n(n), +m_address(), +m_port(0U) +{ + m_data = new unsigned char[20U]; + ::memset(m_data, ' ', 20U); + + for (unsigned int i = 0U; i < text.Length() && i < 20U; i++) + m_data[i] = text.GetChar(i); +} + +CStatusData::~CStatusData() +{ + delete[] m_data; +} + +unsigned int CStatusData::getHBRepeaterData(unsigned char *data, unsigned int length) const +{ + wxASSERT(data != NULL); + wxASSERT(length >= 26U); + + data[0] = 'D'; + data[1] = 'S'; + data[2] = 'R'; + data[3] = 'P'; + + data[4] = 0x04; // Status text data + + data[5] = m_n; + + ::memcpy(data + 6U, m_data, 20U); + + return 26U; +} + +void CStatusData::setDestination(const in_addr& address, unsigned int port) +{ + m_address = address; + m_port = port; +} + +in_addr CStatusData::getAddress() const +{ + return m_address; +} + +unsigned int CStatusData::getPort() const +{ + return m_port; +} diff --git a/Common/StatusData.h b/Common/StatusData.h new file mode 100644 index 0000000..c351921 --- /dev/null +++ b/Common/StatusData.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2011,2012 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 StatusData_H +#define StatusData_H + +#include + +#if defined(__WINDOWS__) +#include "Inaddr.h" +#else +#include +#endif + +class CStatusData { +public: + CStatusData(const wxString& text, unsigned int n); + virtual ~CStatusData(); + + unsigned int getHBRepeaterData(unsigned char* data, unsigned int length) const; + + void setDestination(const in_addr& address, unsigned int port); + + in_addr getAddress() const; + unsigned int getPort() const; + +private: + unsigned char* m_data; + unsigned int m_n; + in_addr m_address; + unsigned int m_port; +}; + +#endif diff --git a/Common/TCPReaderWriterClient.cpp b/Common/TCPReaderWriterClient.cpp new file mode 100644 index 0000000..09e2489 --- /dev/null +++ b/Common/TCPReaderWriterClient.cpp @@ -0,0 +1,292 @@ +/* + * 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 + * 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 "TCPReaderWriterClient.h" +#include "UDPReaderWriter.h" + +#include + +#if !defined(__WINDOWS__) +#include +#endif + + +CTCPReaderWriterClient::CTCPReaderWriterClient(const wxString& address, unsigned int port, const wxString& localAddress) : +m_address(address), +m_port(port), +m_localAddress(localAddress), +m_fd(-1) +{ + wxASSERT(!address.IsEmpty()); + wxASSERT(port > 0U); + +#if defined(__WINDOWS__) + WSAData data; + int wsaRet = ::WSAStartup(MAKEWORD(2, 2), &data); + if (wsaRet != 0) + wxLogError(wxT("Error from WSAStartup")); +#endif +} + +CTCPReaderWriterClient::CTCPReaderWriterClient(int fd) : +m_address(), +m_port(0U), +m_localAddress(), +m_fd(fd) +{ + wxASSERT(fd >= 0); + +#if defined(__WINDOWS__) + WSAData data; + int wsaRet = ::WSAStartup(MAKEWORD(2, 2), &data); + if (wsaRet != 0) + wxLogError(wxT("Error from WSAStartup")); +#endif +} + +CTCPReaderWriterClient::CTCPReaderWriterClient() : +m_address(), +m_port(0U), +m_localAddress(), +m_fd(-1) +{ +#if defined(__WINDOWS__) + WSAData data; + int wsaRet = ::WSAStartup(MAKEWORD(2, 2), &data); + if (wsaRet != 0) + wxLogError(wxT("Error from WSAStartup")); +#endif +} + +CTCPReaderWriterClient::~CTCPReaderWriterClient() +{ +#if defined(__WINDOWS__) + ::WSACleanup(); +#endif +} + +bool CTCPReaderWriterClient::open(const wxString& address, unsigned int port, const wxString& localAddress) +{ + m_address = address; + m_port = port; + m_localAddress = localAddress; + + return open(); +} + +bool CTCPReaderWriterClient::open() +{ + if (m_fd != -1) + return true; + + if (m_address.IsEmpty() || m_port == 0U) + return false; + + m_fd = ::socket(PF_INET, SOCK_STREAM, 0); + if (m_fd < 0) { +#if defined(__WINDOWS__) + wxLogError(wxT("Cannot create the TCP client socket, err=%d"), ::GetLastError()); +#else + wxLogError(wxT("Cannot create the TCP client socket, err=%d"), errno); +#endif + return false; + } + + if (!m_localAddress.IsEmpty()) { + sockaddr_in addr; + ::memset(&addr, 0x00, sizeof(struct sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_port = 0U; +#if defined(__WINDOWS__) + addr.sin_addr.s_addr = ::inet_addr(m_localAddress.mb_str()); +#else + addr.sin_addr.s_addr = ::inet_addr(m_localAddress.mb_str()); +#endif + if (addr.sin_addr.s_addr == INADDR_NONE) { + wxLogError(wxT("The address is invalid - %s"), m_localAddress.c_str()); + close(); + return false; + } + + if (::bind(m_fd, (sockaddr*)&addr, sizeof(sockaddr_in)) == -1) { +#if defined(__WINDOWS__) + wxLogError(wxT("Cannot bind the TCP client address, err=%d"), ::GetLastError()); +#else + wxLogError(wxT("Cannot bind the TCP client address, err=%d"), errno); +#endif + close(); + return false; + } + } + + struct sockaddr_in addr; + ::memset(&addr, 0x00, sizeof(struct sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_port = htons(m_port); + addr.sin_addr = CUDPReaderWriter::lookup(m_address); + + if (addr.sin_addr.s_addr == INADDR_NONE) { + close(); + return false; + } + + if (::connect(m_fd, (sockaddr*)&addr, sizeof(struct sockaddr_in)) == -1) { +#if defined(__WINDOWS__) + wxLogError(wxT("Cannot connect the TCP client socket, err=%d"), ::GetLastError()); +#else + wxLogError(wxT("Cannot connect the TCP client socket, err=%d"), errno); +#endif + close(); + return false; + } + + int noDelay = 1; + if (::setsockopt(m_fd, IPPROTO_TCP, TCP_NODELAY, (char *)&noDelay, sizeof(noDelay)) == -1) { +#if defined(__WINDOWS__) + wxLogError(wxT("Cannot set the TCP client socket option, err=%d"), ::GetLastError()); +#else + wxLogError(wxT("Cannot set the TCP client socket option, err=%d"), errno); +#endif + close(); + return false; + } + + return true; +} + +int CTCPReaderWriterClient::read(unsigned char* buffer, unsigned int length, unsigned int secs, unsigned int msecs) +{ + wxASSERT(buffer != NULL); + wxASSERT(length > 0U); + wxASSERT(m_fd != -1); + + // Check that the recv() won't block + fd_set readFds; + FD_ZERO(&readFds); +#if defined(__WINDOWS__) + FD_SET((unsigned int)m_fd, &readFds); +#else + FD_SET(m_fd, &readFds); +#endif + + // Return after timeout + timeval tv; + tv.tv_sec = secs; + tv.tv_usec = msecs * 1000; + + int ret = ::select(m_fd + 1, &readFds, NULL, NULL, &tv); + if (ret < 0) { +#if defined(__WINDOWS__) + wxLogError(wxT("Error returned from TCP client select, err=%d"), ::GetLastError()); +#else + wxLogError(wxT("Error returned from TCP client select, err=%d"), errno); +#endif + return -1; + } + +#if defined(__WINDOWS__) + if (!FD_ISSET((unsigned int)m_fd, &readFds)) + return 0; +#else + if (!FD_ISSET(m_fd, &readFds)) + return 0; +#endif + + ssize_t len = ::recv(m_fd, (char*)buffer, length, 0); + if (len == 0) { + return -2; + } else if (len < 0) { +#if defined(__WINDOWS__) + wxLogError(wxT("Error returned from recv, err=%d"), ::GetLastError()); +#else + wxLogError(wxT("Error returned from recv, err=%d"), errno); +#endif + return -1; + } + + return len; +} + +int CTCPReaderWriterClient::readLine(wxString& line, unsigned int secs) +{ + //maybe there is a better way to do this like reading blocks, pushing them for later calls + //Nevermind, we'll read one char at a time for the time being. + unsigned char c; + int resultCode; + int len = 0; + line = wxT(""); + + do + { + resultCode = read(&c, 1, secs); + if(resultCode == 1){ + line.Append(c); + len++; + } + }while(c != '\n' && resultCode == 1); + + return resultCode <= 0 ? resultCode : len; +} + +bool CTCPReaderWriterClient::write(const unsigned char* buffer, unsigned int length) +{ + wxASSERT(buffer != NULL); + wxASSERT(length > 0U); + wxASSERT(m_fd != -1); + + ssize_t ret = ::send(m_fd, (char *)buffer, length, 0); + if (ret != ssize_t(length)) { +#if defined(__WINDOWS__) + wxLogError(wxT("Error returned from send, err=%d"), ::GetLastError()); +#else + wxLogError(wxT("Error returned from send, err=%d"), errno); +#endif + return false; + } + + return true; +} + +bool CTCPReaderWriterClient::writeLine(const wxString& line) +{ + wxString lineCopy(line); + if(lineCopy.Length() > 0 && lineCopy.GetChar(lineCopy.Length() - 1) != '\n') + lineCopy.Append(wxT("\n")); + + //stupidly write one char after the other + size_t len = lineCopy.Length(); + bool result = true; + for(size_t i = 0; i < len && result; i++){ + unsigned char c = lineCopy.GetChar(i); + result = write(&c , 1); + } + + return result; +} + +void CTCPReaderWriterClient::close() +{ + if (m_fd != -1) { +#if defined(__WINDOWS__) + ::closesocket(m_fd); +#else + ::close(m_fd); +#endif + m_fd = -1; + } +} diff --git a/Common/TCPReaderWriterClient.h b/Common/TCPReaderWriterClient.h new file mode 100644 index 0000000..1f5f31f --- /dev/null +++ b/Common/TCPReaderWriterClient.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2010,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 + * 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 TCPReaderWriterClient_H +#define TCPReaderWriterClient_H + +#include + +#if !defined(__WINDOWS__) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +class CTCPReaderWriterClient { +public: + CTCPReaderWriterClient(const wxString& address, unsigned int port, const wxString& localAddress = wxEmptyString); + CTCPReaderWriterClient(int fd); + CTCPReaderWriterClient(); + ~CTCPReaderWriterClient(); + + bool open(const wxString& address, unsigned int port, const wxString& localAddress = wxEmptyString); + bool open(); + + int read(unsigned char* buffer, unsigned int length, unsigned int secs, unsigned int msecs = 0U); + int readLine(wxString& line, unsigned int secs); + bool write(const unsigned char* buffer, unsigned int length); + bool writeLine(const wxString& line); + + void close(); + +private: + wxString m_address; + unsigned short m_port; + wxString m_localAddress; + int m_fd; +}; + +#endif diff --git a/Common/TCPReaderWriterServer.cpp b/Common/TCPReaderWriterServer.cpp new file mode 100644 index 0000000..ef945ad --- /dev/null +++ b/Common/TCPReaderWriterServer.cpp @@ -0,0 +1,319 @@ +/* + * 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 + * 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 "TCPReaderWriterServer.h" + +#if !defined(__WINDOWS__) +#include +#endif + + +CTCPReaderWriterServer::CTCPReaderWriterServer(const wxString& address, unsigned int port) : +wxThread(wxTHREAD_JOINABLE), +m_address(address), +m_port(port), +m_fd(-1), +m_client(NULL), +m_stopped(false) +{ + wxASSERT(port > 0U); + +#if defined(__WINDOWS__) + WSAData data; + int wsaRet = ::WSAStartup(MAKEWORD(2, 2), &data); + if (wsaRet != 0) + wxLogError(wxT("Error from WSAStartup")); +#endif +} + +CTCPReaderWriterServer::~CTCPReaderWriterServer() +{ +#if defined(__WINDOWS__) + ::WSACleanup(); +#endif +} + +bool CTCPReaderWriterServer::start() +{ + bool ret = open(); + if (!ret) { + close(); + return false; + } + + Create(); + Run(); + + return true; +} + +int CTCPReaderWriterServer::read(unsigned char* buffer, unsigned int length, unsigned int secs) +{ + wxASSERT(buffer != NULL); + wxASSERT(length > 0U); + + if (m_client != NULL) { + int ret = m_client->read(buffer, length, secs); + if (ret < 0) { + wxLogMessage(wxT("Lost TCP connection to port %u"), m_port); + + m_client->close(); + delete m_client; + m_client = NULL; + + open(); + + return 0; + } + + return ret; + } + + return 0; +} + +bool CTCPReaderWriterServer::write(const unsigned char* buffer, unsigned int length) +{ + wxASSERT(buffer != NULL); + wxASSERT(length > 0U); + + if (m_client != NULL) { + bool ret = m_client->write(buffer, length); + if (!ret) { + wxLogMessage(wxT("Lost TCP connection to port %u"), m_port); + + m_client->close(); + delete m_client; + m_client = NULL; + + open(); + + return false; + } + + return true; + } + + return true; +} + +void* CTCPReaderWriterServer::Entry() +{ + try { + while (!m_stopped) { + int ret = accept(); + switch (ret) { + case -2: + break; + case -1: + break; + default: + wxLogMessage(wxT("Incoming TCP connection to port %u"), m_port); + m_client = new CTCPReaderWriterClient(ret); + close(); + break; + } + + Sleep(1000UL); + } + + if (m_client != NULL) { + m_client->close(); + delete m_client; + } + + close(); + } + catch (std::exception& e) { + wxString message(e.what(), wxConvLocal); + wxLogError(wxT("Exception raised in the TCP Reader-Writer Server thread - \"%s\""), message.c_str()); + } + catch (...) { + wxLogError(wxT("Unknown exception raised in the TCP Reader-Writer Server thread")); + } + + return NULL; +} + +void CTCPReaderWriterServer::stop() +{ + m_stopped = true; + + Wait(); +} + +bool CTCPReaderWriterServer::open() +{ + m_fd = ::socket(PF_INET, SOCK_STREAM, 0); + if (m_fd < 0) { +#if defined(__WINDOWS__) + wxLogError(wxT("Cannot create the TCP server socket, err=%d"), ::GetLastError()); +#else + wxLogError(wxT("Cannot create the TCP server socket, err=%d"), errno); +#endif + return false; + } + + struct sockaddr_in addr; + ::memset(&addr, 0x00, sizeof(struct sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_port = htons(m_port); + if (m_address.IsEmpty()) + addr.sin_addr.s_addr = htonl(INADDR_ANY); + else + addr.sin_addr = lookup(m_address); + + if (addr.sin_addr.s_addr == INADDR_NONE) { + wxLogError(wxT("The address is invalid - %s"), m_address.c_str()); + close(); + return false; + } + + int reuse = 1; + if (::setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) == -1) { +#if defined(__WINDOWS__) + wxLogError(wxT("Cannot set the TCP server socket option, err=%d"), ::GetLastError()); +#else + wxLogError(wxT("Cannot set the TCP server socket option, err=%d"), errno); +#endif + close(); + return false; + } + + if (::bind(m_fd, (sockaddr*)&addr, sizeof(struct sockaddr_in)) == -1) { +#if defined(__WINDOWS__) + wxLogError(wxT("Cannot bind the TCP server address, err=%d"), ::GetLastError()); +#else + wxLogError(wxT("Cannot bind the TCP server address, err=%d"), errno); +#endif + close(); + return false; + } + + ::listen(m_fd, 5); + + return true; +} + +int CTCPReaderWriterServer::accept() +{ + if (m_fd == -1) + return -1; + + // Check that the accept() won't block + fd_set readFds; + FD_ZERO(&readFds); +#if defined(__WINDOWS__) + FD_SET((unsigned int)m_fd, &readFds); +#else + FD_SET(m_fd, &readFds); +#endif + + // Return after timeout + timeval tv; + tv.tv_sec = 0L; + tv.tv_usec = 0L; + + int ret = ::select(m_fd + 1, &readFds, NULL, NULL, &tv); + if (ret < 0) { +#if defined(__WINDOWS__) + wxLogError(wxT("Error returned from TCP server select, err=%d"), ::GetLastError()); +#else + wxLogError(wxT("Error returned from TCP server select, err=%d"), errno); +#endif + return -2; + } + +#if defined(__WINDOWS__) + if (!FD_ISSET((unsigned int)m_fd, &readFds)) + return -1; +#else + if (!FD_ISSET(m_fd, &readFds)) + return -1; +#endif + + struct sockaddr_in addr; +#if defined(__WINDOWS__) + int len = sizeof(struct sockaddr_in); +#else + socklen_t len = sizeof(struct sockaddr_in); +#endif + + ret = ::accept(m_fd, (sockaddr*)&addr, &len); + if (ret < 0) { +#if defined(__WINDOWS__) + wxLogError(wxT("Error returned from TCP server accept, err=%d"), ::GetLastError()); +#else + wxLogError(wxT("Error returned from TCP server accept, err=%d"), errno); +#endif + } + + return ret; +} + +void CTCPReaderWriterServer::close() +{ + if (m_fd != -1) { +#if defined(__WINDOWS__) + ::closesocket(m_fd); +#else + ::close(m_fd); +#endif + m_fd = -1; + } +} + +in_addr CTCPReaderWriterServer::lookup(const wxString& hostname) const +{ + in_addr addr; +#if defined(WIN32) + unsigned long address = ::inet_addr(hostname.mb_str()); + if (address != INADDR_NONE && address != INADDR_ANY) { + addr.s_addr = address; + return addr; + } + + struct hostent* hp = ::gethostbyname(hostname.mb_str()); + if (hp != NULL) { + ::memcpy(&addr, hp->h_addr_list[0], hp->h_length); + return addr; + } + + wxLogError(wxT("Cannot find %s"), hostname.c_str()); + + addr.s_addr = INADDR_NONE; + return addr; +#else + in_addr_t address = ::inet_addr(hostname.mb_str()); + if (address != in_addr_t(-1)) { + addr.s_addr = address; + return addr; + } + + struct hostent* hp = ::gethostbyname(hostname.mb_str()); + if (hp != NULL) { + ::memcpy(&addr, hp->h_addr_list[0], hp->h_length); + return addr; + } + + wxLogError(wxT("Cannot find %s"), hostname.c_str()); + + addr.s_addr = INADDR_NONE; + return addr; +#endif +} diff --git a/Common/TCPReaderWriterServer.h b/Common/TCPReaderWriterServer.h new file mode 100644 index 0000000..f3d7c1d --- /dev/null +++ b/Common/TCPReaderWriterServer.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2011 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 TCPReaderWriterServer_H +#define TCPReaderWriterServer_H + +#include "TCPReaderWriterClient.h" + +#include + +#if !defined(__WINDOWS__) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +class CTCPReaderWriterServer : public wxThread { +public: + CTCPReaderWriterServer(const wxString& address, unsigned int port); + virtual ~CTCPReaderWriterServer(); + + virtual bool start(); + + virtual bool write(const unsigned char* buffer, unsigned int length); + virtual int read(unsigned char* buffer, unsigned int length, unsigned int secs); + + virtual void stop(); + + virtual void* Entry(); + +private: + wxString m_address; + unsigned short m_port; + int m_fd; + CTCPReaderWriterClient* m_client; + bool m_stopped; + + bool open(); + int accept(); + void close(); + in_addr lookup(const wxString& hostname) const; +}; + +#endif diff --git a/Common/TextCollector.cpp b/Common/TextCollector.cpp new file mode 100644 index 0000000..0f985f2 --- /dev/null +++ b/Common/TextCollector.cpp @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2010,2011 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 "TextCollector.h" +#include "DStarDefines.h" +#include "Utils.h" + +const unsigned int TEXT_DATA_LENGTH = 20U; +const unsigned int SLOW_DATA_BLOCK_LENGTH = 6U; + +CTextCollector::CTextCollector() : +m_data(NULL), +m_buffer(NULL), +m_slowData(SS_FIRST), +m_has0(false), +m_has1(false), +m_has2(false), +m_has3(false) +{ + m_data = new char[TEXT_DATA_LENGTH]; + m_buffer = new unsigned char[SLOW_DATA_BLOCK_LENGTH]; +} + +CTextCollector::~CTextCollector() +{ + delete[] m_data; + delete[] m_buffer; +} + +void CTextCollector::writeData(const CAMBEData& data) +{ + if (data.isSync()) { + sync(); + return; + } + + unsigned char buffer[DV_FRAME_MAX_LENGTH_BYTES]; + data.getData(buffer, DV_FRAME_MAX_LENGTH_BYTES); + + switch (m_slowData) { + case SS_FIRST: + m_buffer[0U] = buffer[VOICE_FRAME_LENGTH_BYTES + 0U] ^ SCRAMBLER_BYTE1; + m_buffer[1U] = buffer[VOICE_FRAME_LENGTH_BYTES + 1U] ^ SCRAMBLER_BYTE2; + m_buffer[2U] = buffer[VOICE_FRAME_LENGTH_BYTES + 2U] ^ SCRAMBLER_BYTE3; + m_slowData = SS_SECOND; + return; + + case SS_SECOND: + m_buffer[3U] = buffer[VOICE_FRAME_LENGTH_BYTES + 0U] ^ SCRAMBLER_BYTE1; + m_buffer[4U] = buffer[VOICE_FRAME_LENGTH_BYTES + 1U] ^ SCRAMBLER_BYTE2; + m_buffer[5U] = buffer[VOICE_FRAME_LENGTH_BYTES + 2U] ^ SCRAMBLER_BYTE3; + m_slowData = SS_FIRST; + break; + } + + switch (m_buffer[0U]) { + case SLOW_DATA_TYPE_TEXT | 0U: + m_data[0U] = m_buffer[1U] & 0x7FU; + m_data[1U] = m_buffer[2U] & 0x7FU; + m_data[2U] = m_buffer[3U] & 0x7FU; + m_data[3U] = m_buffer[4U] & 0x7FU; + m_data[4U] = m_buffer[5U] & 0x7FU; + m_has0 = true; + break; + case SLOW_DATA_TYPE_TEXT | 1U: + m_data[5U] = m_buffer[1U] & 0x7FU; + m_data[6U] = m_buffer[2U] & 0x7FU; + m_data[7U] = m_buffer[3U] & 0x7FU; + m_data[8U] = m_buffer[4U] & 0x7FU; + m_data[9U] = m_buffer[5U] & 0x7FU; + m_has1 = true; + break; + case SLOW_DATA_TYPE_TEXT | 2U: + m_data[10U] = m_buffer[1U] & 0x7FU; + m_data[11U] = m_buffer[2U] & 0x7FU; + m_data[12U] = m_buffer[3U] & 0x7FU; + m_data[13U] = m_buffer[4U] & 0x7FU; + m_data[14U] = m_buffer[5U] & 0x7FU; + m_has2 = true; + break; + case SLOW_DATA_TYPE_TEXT | 3U: + m_data[15U] = m_buffer[1U] & 0x7FU; + m_data[16U] = m_buffer[2U] & 0x7FU; + m_data[17U] = m_buffer[3U] & 0x7FU; + m_data[18U] = m_buffer[4U] & 0x7FU; + m_data[19U] = m_buffer[5U] & 0x7FU; + m_has3 = true; + break; + default: + break; + } +} + +void CTextCollector::reset() +{ + m_slowData = SS_FIRST; + m_has0 = false; + m_has1 = false; + m_has2 = false; + m_has3 = false; +} + +void CTextCollector::sync() +{ + m_slowData = SS_FIRST; +} + +bool CTextCollector::hasData() const +{ + return m_has0 && m_has1 && m_has2 && m_has3; +} + +wxString CTextCollector::getData() +{ + wxString text; + + if (!m_has0 || !m_has1 || !m_has2 || !m_has3) + return text; + + m_has0 = false; + m_has1 = false; + m_has2 = false; + m_has3 = false; + + return wxString(m_data, wxConvLocal, TEXT_DATA_LENGTH); +} diff --git a/Common/TextCollector.h b/Common/TextCollector.h new file mode 100644 index 0000000..955a822 --- /dev/null +++ b/Common/TextCollector.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2010 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 TextCollector_H +#define TextCollector_H + +#include "AMBEData.h" +#include "Defs.h" + +#include + +class CTextCollector { +public: + CTextCollector(); + ~CTextCollector(); + + void writeData(const CAMBEData& data); + + void sync(); + + void reset(); + + bool hasData() const; + + wxString getData(); + +private: + char* m_data; + unsigned char* m_buffer; + SLOWDATA_STATE m_slowData; + bool m_has0; + bool m_has1; + bool m_has2; + bool m_has3; +}; + +#endif diff --git a/Common/TextData.cpp b/Common/TextData.cpp new file mode 100644 index 0000000..2eb57d4 --- /dev/null +++ b/Common/TextData.cpp @@ -0,0 +1,106 @@ +/* + * 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 + * 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 "TextData.h" + +#include "DStarDefines.h" +#include "Utils.h" + +CTextData::CTextData(LINK_STATUS status, const wxString& reflector, const wxString& text, const in_addr& address, unsigned int port, bool temporary) : +m_status(status), +m_reflector(NULL), +m_text(NULL), +m_address(address), +m_port(port), +m_temporary(temporary) +{ + wxASSERT(port > 0U); + + m_reflector = new unsigned char[8U]; + m_text = new unsigned char[20U]; + + ::memset(m_reflector, ' ', 8U); + ::memset(m_text, ' ', 20U); + + if (status != LS_NONE) { + for (unsigned int i = 0U; i < reflector.Length() && i < 8U; i++) + m_reflector[i] = reflector.GetChar(i); + } + + for (unsigned int i = 0U; i < text.Length() && i < 20U; i++) + m_text[i] = text.GetChar(i); +} + +CTextData::CTextData(const wxString& text, const in_addr& address, unsigned int port, bool temporary) : +m_status(LS_NONE), +m_reflector(NULL), +m_text(NULL), +m_address(address), +m_port(port), +m_temporary(temporary) +{ + wxASSERT(port > 0U); + + m_reflector = new unsigned char[8U]; + m_text = new unsigned char[20U]; + + ::memset(m_reflector, ' ', 8U); + ::memset(m_text, ' ', 20U); + + for (unsigned int i = 0U; i < text.Length() && i < 20U; i++) + m_text[i] = text.GetChar(i); +} + +CTextData::~CTextData() +{ + delete[] m_reflector; + delete[] m_text; +} + +unsigned int CTextData::getHBRepeaterData(unsigned char *data, unsigned int length) const +{ + wxASSERT(data != NULL); + wxASSERT(length >= 34U); + + data[0] = 'D'; + data[1] = 'S'; + data[2] = 'R'; + data[3] = 'P'; + + if (m_temporary) { + data[4] = 0x01U; // Temporary text data + ::memcpy(data + 5U, m_text, 20U); + return 25U; + } else { + data[4] = 0x00U; // Permanent text data + ::memcpy(data + 5U, m_text, 20U); + data[25U] = (unsigned char)m_status; + ::memcpy(data + 26U, m_reflector, 8U); + return 34U; + } +} + +in_addr CTextData::getAddress() const +{ + return m_address; +} + +unsigned int CTextData::getPort() const +{ + return m_port; +} diff --git a/Common/TextData.h b/Common/TextData.h new file mode 100644 index 0000000..4184445 --- /dev/null +++ b/Common/TextData.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010,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 + * 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 TextData_H +#define TextData_H + +#include +#include "Defs.h" + +#if defined(__WINDOWS__) +#include "Inaddr.h" +#else +#include +#endif + +class CTextData { +public: + CTextData(LINK_STATUS status, const wxString& reflector, const wxString& text, const in_addr& address, unsigned int port, bool temporary = false); + CTextData(const wxString& text, const in_addr& address, unsigned int port, bool temporary = true); + virtual ~CTextData(); + + unsigned int getHBRepeaterData(unsigned char* data, unsigned int length) const; + + in_addr getAddress() const; + unsigned int getPort() const; + +private: + LINK_STATUS m_status; + unsigned char* m_reflector; + unsigned char* m_text; + in_addr m_address; + unsigned int m_port; + bool m_temporary; +}; + +#endif diff --git a/Common/Timer.cpp b/Common/Timer.cpp new file mode 100644 index 0000000..39e2ff1 --- /dev/null +++ b/Common/Timer.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2009,2010 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 "Timer.h" + +#include + +CTimer::CTimer(unsigned int ticksPerSec, unsigned int secs, unsigned int msecs) : +m_ticksPerSec(ticksPerSec), +m_timeout(0U), +m_timer(0U) +{ + wxASSERT(ticksPerSec > 0U); + + if (secs > 0U || msecs > 0U) { + // m_timeout = ((secs * 1000U + msecs) * m_ticksPerSec) / 1000U + 1U; + unsigned wxLongLong_t temp = (secs * wxULL(1000) + msecs) * m_ticksPerSec; + m_timeout = (unsigned int)(temp / wxULL(1000) + wxULL(1)); + } +} + +CTimer::~CTimer() +{ +} + +void CTimer::setTimeout(unsigned int secs, unsigned int msecs) +{ + if (secs > 0U || msecs > 0U) { + // m_timeout = ((secs * 1000U + msecs) * m_ticksPerSec) / 1000U + 1U; + unsigned wxLongLong_t temp = (secs * wxULL(1000) + msecs) * m_ticksPerSec; + m_timeout = (unsigned int)(temp / wxULL(1000) + wxULL(1)); + } else { + m_timeout = 0U; + m_timer = 0U; + } +} + +unsigned int CTimer::getTimeout() const +{ + if (m_timeout == 0U) + return 0U; + + return (m_timeout - 1U) / m_ticksPerSec; +} + +unsigned int CTimer::getTimer() const +{ + if (m_timer == 0U) + return 0U; + + return (m_timer - 1U) / m_ticksPerSec; +} diff --git a/Common/Timer.h b/Common/Timer.h new file mode 100644 index 0000000..dd09f96 --- /dev/null +++ b/Common/Timer.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef Timer_H +#define Timer_H + +class CTimer { +public: + CTimer(unsigned int ticksPerSec, unsigned int secs = 0U, unsigned int msecs = 0U); + ~CTimer(); + + void setTimeout(unsigned int secs, unsigned int msecs = 0U); + + unsigned int getTimeout() const; + unsigned int getTimer() const; + + unsigned int getRemaining() + { + if (m_timeout == 0U || m_timer == 0U) + return 0U; + + if (m_timer >= m_timeout) + return 0U; + + return (m_timeout - m_timer) / m_ticksPerSec; + } + + bool isRunning() + { + return m_timer > 0U; + } + + void start(unsigned int secs, unsigned int msecs = 0U) + { + setTimeout(secs, msecs); + + start(); + } + + void start() + { + if (m_timeout > 0U) + m_timer = 1U; + } + + void stop() + { + m_timer = 0U; + } + + bool hasExpired() + { + if (m_timeout == 0U || m_timer == 0U) + return false; + + if (m_timer >= m_timeout) + return true; + + return false; + } + + void clock(unsigned int t = 1U) + { + if (m_timer > 0U && m_timeout > 0U) + m_timer += t; + } + +private: + unsigned int m_ticksPerSec; + unsigned int m_timeout; + unsigned int m_timer; +}; + +#endif diff --git a/Common/UDPReaderWriter.cpp b/Common/UDPReaderWriter.cpp new file mode 100644 index 0000000..5a18a8e --- /dev/null +++ b/Common/UDPReaderWriter.cpp @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2006-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 "UDPReaderWriter.h" + +#if !defined(__WINDOWS__) +#include +#endif + + +CUDPReaderWriter::CUDPReaderWriter(const wxString& address, unsigned int port) : +m_address(address), +m_port(port), +m_addr(), +m_fd(-1) +{ +#if defined(__WINDOWS__) + WSAData data; + int wsaRet = ::WSAStartup(MAKEWORD(2, 2), &data); + if (wsaRet != 0) + wxLogError(wxT("Error from WSAStartup")); +#endif +} + +CUDPReaderWriter::~CUDPReaderWriter() +{ +#if defined(__WINDOWS__) + ::WSACleanup(); +#endif +} + +in_addr CUDPReaderWriter::lookup(const wxString& hostname) +{ + in_addr addr; +#if defined(WIN32) + unsigned long address = ::inet_addr(hostname.mb_str()); + if (address != INADDR_NONE && address != INADDR_ANY) { + addr.s_addr = address; + return addr; + } + + struct hostent* hp = ::gethostbyname(hostname.mb_str()); + if (hp != NULL) { + ::memcpy(&addr, hp->h_addr_list[0], sizeof(struct in_addr)); + return addr; + } + + wxLogError(wxT("Cannot find address for host %s"), hostname.c_str()); + + addr.s_addr = INADDR_NONE; + return addr; +#else + in_addr_t address = ::inet_addr(hostname.mb_str()); + if (address != in_addr_t(-1)) { + addr.s_addr = address; + return addr; + } + + struct hostent* hp = ::gethostbyname(hostname.mb_str()); + if (hp != NULL) { + ::memcpy(&addr, hp->h_addr_list[0], sizeof(struct in_addr)); + return addr; + } + + wxLogError(wxT("Cannot find address for host %s"), hostname.c_str()); + + addr.s_addr = INADDR_NONE; + return addr; +#endif +} + +bool CUDPReaderWriter::open() +{ + m_fd = ::socket(PF_INET, SOCK_DGRAM, 0); + if (m_fd < 0) { +#if defined(__WINDOWS__) + wxLogError(wxT("Cannot create the UDP socket, err: %lu"), ::GetLastError()); +#else + wxLogError(wxT("Cannot create the UDP socket, err: %d"), errno); +#endif + return false; + } + + if (m_port > 0U) { + sockaddr_in addr; + ::memset(&addr, 0x00, sizeof(sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_port = htons(m_port); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + if (!m_address.IsEmpty()) { +#if defined(__WINDOWS__) + addr.sin_addr.s_addr = ::inet_addr(m_address.mb_str()); +#else + addr.sin_addr.s_addr = ::inet_addr(m_address.mb_str()); +#endif + if (addr.sin_addr.s_addr == INADDR_NONE) { + wxLogError(wxT("The address is invalid - %s"), m_address.c_str()); + return false; + } + } + + int reuse = 1; + if (::setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) == -1) { +#if defined(__WINDOWS__) + wxLogError(wxT("Cannot set the UDP socket option (port: %u), err: %lu"), m_port, ::GetLastError()); +#else + wxLogError(wxT("Cannot set the UDP socket option (port: %u), err: %d"), m_port, errno); +#endif + return false; + } + + if (::bind(m_fd, (sockaddr*)&addr, sizeof(sockaddr_in)) == -1) { +#if defined(__WINDOWS__) + wxLogError(wxT("Cannot bind the UDP address (port: %u), err: %lu"), m_port, ::GetLastError()); +#else + wxLogError(wxT("Cannot bind the UDP address (port: %u), err: %d"), m_port, errno); +#endif + return false; + } + } + + return true; +} + +int CUDPReaderWriter::read(unsigned char* buffer, unsigned int length, in_addr& address, unsigned int& port) +{ + // Check that the readfrom() won't block + fd_set readFds; + FD_ZERO(&readFds); +#if defined(__WINDOWS__) + FD_SET((unsigned int)m_fd, &readFds); +#else + FD_SET(m_fd, &readFds); +#endif + + // Return immediately + timeval tv; + tv.tv_sec = 0L; + tv.tv_usec = 0L; + + int ret = ::select(m_fd + 1, &readFds, NULL, NULL, &tv); + if (ret < 0) { +#if defined(__WINDOWS__) + wxLogError(wxT("Error returned from UDP select (port: %u), err: %lu"), m_port, ::GetLastError()); +#else + wxLogError(wxT("Error returned from UDP select (port: %u), err: %d"), m_port, errno); +#endif + return -1; + } + + if (ret == 0) + return 0; + + sockaddr_in addr; +#if defined(__WINDOWS__) + int size = sizeof(sockaddr_in); +#else + socklen_t size = sizeof(sockaddr_in); +#endif + + ssize_t len = ::recvfrom(m_fd, (char*)buffer, length, 0, (sockaddr *)&addr, &size); + if (len <= 0) { +#if defined(__WINDOWS__) + wxLogError(wxT("Error returned from recvfrom (port: %u), err: %lu"), m_port, ::GetLastError()); +#else + wxLogError(wxT("Error returned from recvfrom (port: %u), err: %d"), m_port, errno); +#endif + return -1; + } + + address = addr.sin_addr; + port = ntohs(addr.sin_port); + + return len; +} + +bool CUDPReaderWriter::write(const unsigned char* buffer, unsigned int length, const in_addr& address, unsigned int port) +{ + sockaddr_in addr; + ::memset(&addr, 0x00, sizeof(sockaddr_in)); + + addr.sin_family = AF_INET; + addr.sin_addr = address; + addr.sin_port = htons(port); + + ssize_t ret = ::sendto(m_fd, (char *)buffer, length, 0, (sockaddr *)&addr, sizeof(sockaddr_in)); + if (ret < 0) { +#if defined(__WINDOWS__) + wxLogError(wxT("Error returned from sendto (port: %u), err: %lu"), m_port, ::GetLastError()); +#else + wxLogError(wxT("Error returned from sendto (port: %u), err: %d"), m_port, errno); +#endif + return false; + } + + if (ret != ssize_t(length)) + return false; + + return true; +} + +void CUDPReaderWriter::close() +{ +#if defined(__WINDOWS__) + ::closesocket(m_fd); +#else + ::close(m_fd); +#endif +} + +unsigned int CUDPReaderWriter::getPort() const +{ + return m_port; +} diff --git a/Common/UDPReaderWriter.h b/Common/UDPReaderWriter.h new file mode 100644 index 0000000..4167662 --- /dev/null +++ b/Common/UDPReaderWriter.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2009-2011,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 UDPReaderWriter_H +#define UDPReaderWriter_H + +#include + +#if !defined(__WINDOWS__) +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +class CUDPReaderWriter { +public: + CUDPReaderWriter(const wxString& address, unsigned int port); + ~CUDPReaderWriter(); + + static in_addr lookup(const wxString& hostName); + + bool open(); + + int read(unsigned char* buffer, unsigned int length, in_addr& address, unsigned int& port); + bool write(const unsigned char* buffer, unsigned int length, const in_addr& address, unsigned int port); + + void close(); + + unsigned int getPort() const; + +private: + wxString m_address; + unsigned short m_port; + in_addr m_addr; + int m_fd; +}; + +#endif diff --git a/Common/UserCache.cpp b/Common/UserCache.cpp new file mode 100644 index 0000000..2536abf --- /dev/null +++ b/Common/UserCache.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2010 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 "UserCache.h" + +const unsigned int CACHE_SIZE = 1000U; + +CUserCache::CUserCache() : +m_cache(CACHE_SIZE) +{ +} + +CUserCache::~CUserCache() +{ + for (CUserCache_t::iterator it = m_cache.begin(); it != m_cache.end(); ++it) + delete it->second; +} + +CUserRecord* CUserCache::find(const wxString& user) +{ + return m_cache[user]; +} + +void CUserCache::update(const wxString& user, const wxString& repeater, const wxString& timestamp) +{ + CUserRecord* rec = m_cache[user]; + + if (rec == NULL) + // A brand new record is needed + m_cache[user] = new CUserRecord(user, repeater, timestamp); + else if(timestamp.Cmp(rec->getTimeStamp()) > 0) { + // Update an existing record, but only if the received timestamp is newer + rec->setRepeater(repeater); + rec->setTimestamp(timestamp); + } +} + +unsigned int CUserCache::getCount() const +{ + return m_cache.size(); +} diff --git a/Common/UserCache.h b/Common/UserCache.h new file mode 100644 index 0000000..c040ad2 --- /dev/null +++ b/Common/UserCache.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2010 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 UserCache_H +#define UserCache_H + +#include +#include + +class CUserRecord { +public: + CUserRecord(const wxString& user, const wxString& repeater, const wxString& timestamp) : + m_user(user), + m_repeater(repeater), + m_timestamp(timestamp) + { + } + + wxString getUser() const + { + return m_user; + } + + wxString getRepeater() const + { + return m_repeater; + } + + wxString getTimeStamp() const + { + return m_timestamp; + } + + void setRepeater(const wxString& repeater) + { + m_repeater = repeater; + } + + void setTimestamp(const wxString& timestamp) + { + m_timestamp = timestamp; + } + +private: + wxString m_user; + wxString m_repeater; + wxString m_timestamp; +}; + +WX_DECLARE_STRING_HASH_MAP(CUserRecord*, CUserCache_t); + +class CUserCache { +public: + CUserCache(); + ~CUserCache(); + + CUserRecord* find(const wxString& user); + + void update(const wxString& user, const wxString& repeater, const wxString& timestamp); + + unsigned int getCount() const; + +private: + CUserCache_t m_cache; +}; + +#endif diff --git a/Common/Utils.cpp b/Common/Utils.cpp new file mode 100644 index 0000000..3cb449b --- /dev/null +++ b/Common/Utils.cpp @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2009,2013 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; version 2 of the License. + * + * 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. + */ + +#include "Utils.h" + +void CUtils::dump(const wxChar* title, const bool* data, unsigned int length) +{ + wxASSERT(title != NULL); + wxASSERT(data != NULL); + + wxLogMessage(wxT("%s"), title); + + unsigned int offset = 0U; + + while (offset < length) { + wxString output; + + unsigned char buffer[16]; + unsigned int bytes = 0U; + for (unsigned int bits = 0U; bits < 128U && (offset + bits) < length; bits += 8U) + buffer[bytes++] = bitsToByte(data + offset + bits); + + for (unsigned i = 0U; i < bytes; i++) { + wxString temp; + temp.Printf(wxT("%02X "), buffer[i]); + output += temp; + } + + for (unsigned int i = bytes; i < 16U; i++) + output += wxT(" "); + + output += wxT(" *"); + + for (unsigned i = 0U; i < bytes; i++) { + unsigned char c = buffer[i]; + + if (::isprint(c)) + output += wxChar(c); + else + output += wxT('.'); + } + + output += wxT('*'); + + wxLogMessage(wxT("%04X: %s"), offset / 8U, output.c_str()); + + offset += 128U; + } +} + +void CUtils::dumpRev(const wxChar* title, const bool* data, unsigned int length) +{ + wxASSERT(title != NULL); + wxASSERT(data != NULL); + + wxLogMessage(wxT("%s"), title); + + unsigned int offset = 0U; + + while (offset < length) { + wxString output; + + unsigned char buffer[16]; + unsigned int bytes = 0U; + for (unsigned int bits = 0U; bits < 128U && (offset + bits) < length; bits += 8U) + buffer[bytes++] = bitsToByteRev(data + offset + bits); + + for (unsigned i = 0U; i < bytes; i++) { + wxString temp; + temp.Printf(wxT("%02X "), buffer[i]); + output += temp; + } + + for (unsigned int i = bytes; i < 16U; i++) + output += wxT(" "); + + output += wxT(" *"); + + for (unsigned i = 0U; i < bytes; i++) { + unsigned char c = buffer[i]; + + if (::isprint(c)) + output += wxChar(c); + else + output += wxT('.'); + } + + output += wxT('*'); + + wxLogMessage(wxT("%04X: %s"), offset / 8U, output.c_str()); + + offset += 128U; + } +} + +void CUtils::dump(const wxChar* title, const unsigned char* data, unsigned int length) +{ + wxASSERT(title != NULL); + wxASSERT(data != NULL); + + wxLogMessage(wxT("%s"), title); + + unsigned int offset = 0U; + + while (length > 0U) { + wxString output; + + unsigned int bytes = (length > 16U) ? 16U : length; + + for (unsigned i = 0U; i < bytes; i++) { + wxString temp; + temp.Printf(wxT("%02X "), data[offset + i]); + output += temp; + } + + for (unsigned int i = bytes; i < 16U; i++) + output += wxT(" "); + + output += wxT(" *"); + + for (unsigned i = 0U; i < bytes; i++) { + unsigned char c = data[offset + i]; + + if (::isprint(c)) + output += wxChar(c); + else + output += wxT('.'); + } + + output += wxT('*'); + + wxLogMessage(wxT("%04X: %s"), offset, output.c_str()); + + offset += 16U; + + if (length >= 16U) + length -= 16U; + else + length = 0U; + } +} + +unsigned char CUtils::bitsToByte(const bool* bits) +{ + wxASSERT(bits != NULL); + + unsigned char val = 0x00; + + for (unsigned int i = 0U; i < 8U; i++) { + val <<= 1; + + if (bits[i]) + val |= 0x01; + } + + return val; +} + +unsigned char CUtils::bitsToByteRev(const bool* bits) +{ + wxASSERT(bits != NULL); + + unsigned char val = 0x00; + + for (unsigned int i = 0U; i < 8U; i++) { + val >>= 1; + + if (bits[i]) + val |= 0x80; + } + + return val; +} + +void CUtils::byteToBits(unsigned char byte, bool* data) +{ + wxASSERT(data != NULL); + + unsigned char mask = 0x80U; + for (unsigned int i = 0U; i < 8U; i++, mask >>= 1) + data[i] = byte & mask ? true : false; +} + +void CUtils::byteToBitsRev(unsigned char byte, bool* data) +{ + wxASSERT(data != NULL); + + unsigned char mask = 0x01U; + for (unsigned int i = 0U; i < 8U; i++, mask <<= 1) + data[i] = byte & mask ? true : false; +} + +wxString CUtils::latLonToLoc(double latitude, double longitude) +{ + if (latitude < -90.0 || latitude > 90.0) + return wxEmptyString; + + if (longitude < -360.0 || longitude > 360.0) + return wxEmptyString; + + latitude += 90.0; + + if (longitude > 180.0) + longitude -= 360.0; + + if (longitude < -180.0) + longitude += 360.0; + + longitude += 180.0; + + char locator[6U]; + + double lon = ::floor(longitude / 20.0); + double lat = ::floor(latitude / 10.0); + + locator[0U] = 'A' + (unsigned int)lon; + locator[1U] = 'A' + (unsigned int)lat; + + longitude -= lon * 20.0; + latitude -= lat * 10.0; + + lon = ::floor(longitude / 2.0); + lat = ::floor(latitude / 1.0); + + locator[2U] = '0' + (unsigned int)lon; + locator[3U] = '0' + (unsigned int)lat; + + longitude -= lon * 2.0; + latitude -= lat * 1.0; + + lon = ::floor(longitude / (2.0 / 24.0)); + lat = ::floor(latitude / (1.0 / 24.0)); + + locator[4U] = 'A' + (unsigned int)lon; + locator[5U] = 'A' + (unsigned int)lat; + + return wxString(locator, wxConvLocal, 6U); +} + +void CUtils::clean(wxString &str, const wxString& allowed) +{ + for (unsigned int i = 0U; i < str.Len(); i++) { + int n = allowed.Find(str.GetChar(i)); + if (n == wxNOT_FOUND) + str.SetChar(i, wxT(' ')); + } +} diff --git a/Common/Utils.h b/Common/Utils.h new file mode 100644 index 0000000..2f51ab5 --- /dev/null +++ b/Common/Utils.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2009,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; version 2 of the License. + * + * 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. + */ + +#ifndef Utils_H +#define Utils_H + +#include + +enum TRISTATE { + STATE_FALSE, + STATE_TRUE, + STATE_UNKNOWN +}; + +class CUtils { +public: + static void dump(const wxChar* title, const bool* data, unsigned int length); + static void dumpRev(const wxChar* title, const bool* data, unsigned int length); + static void dump(const wxChar* title, const unsigned char* data, unsigned int length); + static unsigned char bitsToByte(const bool* bits); + static unsigned char bitsToByteRev(const bool* bits); + static void byteToBits(unsigned char byte, bool* bits); + static void byteToBitsRev(unsigned char byte, bool* bits); + static wxString latLonToLoc(double latitude, double longitude); + static void clean(wxString& str, const wxString& allowed); + +private: +}; + +#endif diff --git a/Common/Version.h b/Common/Version.h new file mode 100644 index 0000000..9138fe5 --- /dev/null +++ b/Common/Version.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2010-2015,2018 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 Version_H +#define Version_H + +#include + +const wxString VENDOR_NAME = wxT("G4KLX"); + +#if defined(__WXDEBUG__) +const wxString VERSION = wxT("20180509 - DEBUG"); +#else +const wxString VERSION = wxT("20180509"); +#endif + +#endif diff --git a/Common/VersionUnit.cpp b/Common/VersionUnit.cpp new file mode 100644 index 0000000..e9ca109 --- /dev/null +++ b/Common/VersionUnit.cpp @@ -0,0 +1,142 @@ +/* + * 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 + * 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 "SlowDataEncoder.h" +#include "DStarDefines.h" +#include "VersionUnit.h" +#include "HeaderData.h" +#include "Version.h" + +const unsigned int NUM_FRAMES = 20U; + +CVersionUnit::CVersionUnit(IRepeaterCallback* handler, const wxString& callsign) : +m_handler(handler), +m_callsign(callsign), +m_status(VS_IDLE), +m_timer(1000U, REPLY_TIME), +m_data(NULL), +m_id(0U), +m_out(0U), +m_time() +{ + wxASSERT(handler != NULL); + + m_data = new CAMBEData*[NUM_FRAMES]; + + wxString versionText; + versionText.Printf(wxT("ircDDB GW - %s"), VERSION.Left(8U).c_str()); + + wxLogMessage(wxT("Version text set to \"%s\""), versionText.c_str()); + + CSlowDataEncoder encoder; + encoder.setTextData(versionText); + + // Seq No and end + for (unsigned int i = 0U; i < NUM_FRAMES; i++) { + unsigned char buffer[DV_FRAME_LENGTH_BYTES]; + ::memcpy(buffer + 0U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + + // Insert sync bytes when the sequence number is zero, slow data otherwise + if (i == 0U) { + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); + encoder.sync(); + } else { + encoder.getTextData(buffer + VOICE_FRAME_LENGTH_BYTES); + } + + m_data[i] = new CAMBEData; + m_data[i]->setData(buffer, DV_FRAME_LENGTH_BYTES); + m_data[i]->setSeq(i); + + if (i == (NUM_FRAMES - 1U)) + m_data[i]->setEnd(true); + } +} + +CVersionUnit::~CVersionUnit() +{ + for (unsigned int i = 0U; i < NUM_FRAMES; i++) + delete m_data[i]; + + delete[] m_data; +} + +void CVersionUnit::sendVersion() +{ + if (m_status != VS_IDLE) + return; + + m_id = CHeaderData::createId(); + + m_status = VS_WAIT; + m_timer.start(); +} + +void CVersionUnit::clock(unsigned int ms) +{ + m_timer.clock(ms); + + if (m_status == VS_WAIT && m_timer.hasExpired()) { + m_timer.stop(); + + // RPT1 and RPT2 will be filled in later + CHeaderData header; + header.setMyCall1(m_callsign); + header.setMyCall2(wxT("VERS")); + header.setYourCall(wxT("CQCQCQ ")); + header.setId(m_id); + + m_handler->process(header, DIR_INCOMING, AS_VERSION); + + m_out = 0U; + m_status = VS_TRANSMIT; + + m_time.Start(); + + return; + } + + if (m_status == VS_TRANSMIT) { + unsigned int needed = m_time.Time() / DSTAR_FRAME_TIME_MS; + + while (m_out < needed) { + CAMBEData* data = m_data[m_out]; + data->setId(m_id); + + m_out++; + + m_handler->process(*data, DIR_INCOMING, AS_VERSION); + + if (m_out == NUM_FRAMES) { + m_out = 0U; + m_status = VS_IDLE; + return; + } + } + + return; + } +} + +void CVersionUnit::cancel() +{ + m_status = VS_IDLE; + m_out = 0U; + + m_timer.stop(); +} diff --git a/Common/VersionUnit.h b/Common/VersionUnit.h new file mode 100644 index 0000000..b70326e --- /dev/null +++ b/Common/VersionUnit.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2012 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 VersionUnit_H +#define VersionUnit_H + +#include "RepeaterCallback.h" +#include "AMBEData.h" +#include "Timer.h" +#include "Defs.h" + +#include + +enum VERSION_STATUS { + VS_IDLE, + VS_WAIT, + VS_TRANSMIT +}; + +class CVersionUnit { +public: + CVersionUnit(IRepeaterCallback* handler, const wxString& callsign); + ~CVersionUnit(); + + void sendVersion(); + + void cancel(); + + void clock(unsigned int ms); + +private: + IRepeaterCallback* m_handler; + wxString m_callsign; + VERSION_STATUS m_status; + CTimer m_timer; + CAMBEData** m_data; + unsigned int m_id; + unsigned int m_out; + wxStopWatch m_time; +}; + +#endif diff --git a/Common/XLXHostsFileDownloader.cpp b/Common/XLXHostsFileDownloader.cpp new file mode 100644 index 0000000..bcf86de --- /dev/null +++ b/Common/XLXHostsFileDownloader.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2010-2013,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 + * 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 "XLXHostsFileDownloader.h" +#ifdef XLX_USE_WGET +#include +#else +#include +#endif +#include + +/* wxHTTP randomly crashes when called on a worker thread, this must be called from main thread ! */ +wxString CXLXHostsFileDownloader::Download(const wxString & xlxHostsFileURL) +{ +#ifdef XLX_USE_WGET + wxString xlxHostsFileName = wxFileName::CreateTempFileName(_T("XLX_Hosts_")); + wxString commandLine = _T("wget -q -O ") + xlxHostsFileName + _T(" ") + xlxHostsFileURL; + bool execResult = wxShell(commandLine); + + if(!execResult) { + wxLogError(_T("Unable do download XLX host file, make sure wget is installed")); + return wxEmptyString; + } + + return xlxHostsFileName; +#else + wxHTTP http; + http.SetNotify(false); + http.SetFlags(wxSOCKET_WAITALL); + http.SetHeader( _T("Accept") , _T("text/*") ); + http.SetHeader( _T("User-Agent"), _T("ircddbGateway") ); + http.SetTimeout(5); // seconds + + wxLogMessage(_T("Downloading XLX reflector list from %s"), xlxHostsFileURL.c_str()); + + // remove "http://" from the url, i.e. minus 7 chars + size_t len = xlxHostsFileURL.length(); + wxString path = xlxHostsFileURL.Right(len-7); + + // find the first forward slash + int slash = path.Find(wxChar('/')); + len = path.length(); + wxString server = path.Left(slash); + path = path.Right(len-slash); + + // Note that Connect() wants a host address, not an URL. 80 is the server's port. + if(!http.Connect(server, 80 )) { + wxLogError(_T("Failed to connect to %s"), server); + return wxEmptyString; + } + + wxInputStream* in = NULL; + if((in = http.GetInputStream(path)) && in->IsOk()) { + wxFile file; + wxString xlxHostsFileName = wxFileName::CreateTempFileName(_T("XLX_Hosts_"), &file); + wxLogMessage(_T("Created temporary file %s"), xlxHostsFileName); + if(!file.IsOpened()) { + wxLogError(_T("Failed to open temporary file %s"), xlxHostsFileName); + wxDELETE(in); + return wxEmptyString; + } + + //in->Read(fileStream); + //fileStream.Write(*in); + //in->Read(tempFileStream); + //Both call above seem to not work when run in a separate thread, hence the old fashion loop below + unsigned char buffer[2048]; + while(!in->Eof()) { + in->Read(buffer, 2048); + int readCount = in->LastRead(); + if(readCount <= 0) + break; + + file.Write(buffer, readCount); + } + wxLogMessage(_T("XLX Successfuly downloaded to %s"), xlxHostsFileName); + file.Close(); + http.Close(); + wxDELETE(in); + return xlxHostsFileName; + } + + wxLogError(_T("Failed to get HTTP stream")); + if(in) wxDELETE(in); + + return wxEmptyString; +#endif +} diff --git a/Common/XLXHostsFileDownloader.h b/Common/XLXHostsFileDownloader.h new file mode 100644 index 0000000..7fba376 --- /dev/null +++ b/Common/XLXHostsFileDownloader.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2010-2013,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 + * 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 XLXHostsFileDownloader_H +#define XLXHostsFileDownloader_H +#define XLX_USE_WGET +#include + +class CXLXHostsFileDownloader { +public: + static wxString Download(const wxString & xlxHostsFileURL); +}; + +#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/DCS_Hosts.txt b/Data/DCS_Hosts.txt new file mode 100644 index 0000000..1d3667d --- /dev/null +++ b/Data/DCS_Hosts.txt @@ -0,0 +1,244 @@ +DCS000 206.208.56.92 +DCS001 dcs001.xreflector.net +DCS002 dcs002.xreflector.net +DCS003 dcs003.xreflector.net +DCS004 dcs004.xreflector.net +DCS005 www.bm-dmr.uk +DCS006 dcs006.xreflector.net +DCS007 dcs007.xreflector.net +DCS008 dcs008.xreflector.net +DCS009 dcs009.xreflector.net +DCS010 dcs010.xreflector.net +DCS011 dcs011.xreflector.net +DCS012 dcs012.xreflector.net +DCS013 dcs013.xreflector.net +DCS014 dcs014.xreflector.net +DCS015 dcs015.xreflector.net +DCS016 dcs016.xreflector.net +DCS017 dcs017.xreflector.net +DCS018 dcs018.xreflector.net +DCS019 dcs019.xreflector.net +DCS021 dcs021.xreflector.net +DCS022 dcs022.xreflector.net +DCS023 dcs023.xreflector.net +DCS024 dcs024.xreflector.net +DCS025 dcs025.xreflector.net +DCS026 dcs026.xreflector.net +DCS027 dcs027.xreflector.net +DCS028 xlx028.org L +DCS029 dcs029.xreflector.net +DCS032 dcs032.xreflector.net +DCS033 dcs033.xreflector.net +DCS035 45.79.94.184 +DCS039 185.203.118.66 +DCS040 109.71.45.29 +DCS041 45.62.226.137 +DCS044 dcs044.xreflector.net +DCS045 64.137.172.60 +DCS046 176.10.140.161 +DCS047 121.94.218.243 +DCS050 212.237.17.133 +DCS051 93.186.254.219 +DCS052 60.47.177.125 +DCS054 52.86.180.251 +DCS055 52.80.4.154 +DCS057 74.193.209.20 +DCS058 118.236.151.192 +DCS060 212.237.36.181 +DCS061 68.115.205.110 +DCS062 70.88.145.163 +DCS064 202.241.175.133 +DCS066 79.16.44.125 +DCS068 92.222.145.202 +DCS069 89.36.214.120 +DCS071 211.60.41.185 +DCS072 211.60.41.186 +DCS075 5.135.162.136 +DCS076 203.137.116.117 +DCS077 5.249.151.111 +DCS078 109.10.128.221 +DCS079 121.162.91.94 +DCS080 121.85.180.61 +DCS081 121.84.13.151 +DCS082 108.21.232.21 +DCS083 108.21.232.22 +DCS085 27.92.11.123 +DCS086 220.133.89.28 +DCS087 44.137.36.209 +DCS088 194.109.192.235 +DCS089 194.109.192.236 +DCS090 91.92.136.252 +DCS092 94.177.190.50 +DCS093 98.29.99.252 +DCS095 58.1.93.223 +DCS098 111.168.216.126 +DCS099 212.237.59.103 +DCS100 96.94.7.196 +DCS101 104.233.105.86 +DCS102 206.208.56.13 +DCS103 64.137.248.42 +DCS104 206.208.56.93 +DCS111 81.95.126.168 +DCS112 216.45.55.151 +DCS114 91.121.136.94 +DCS115 217.182.128.3 +DCS116 31.185.101.211 +DCS118 5.249.148.252 +DCS119 125.129.207.86 +DCS120 24.43.83.140 +DCS121 174.37.249.156 +DCS124 211.14.169.234 +DCS125 213.181.208.52 +DCS127 190.112.228.107 +DCS128 153.249.96.5 +DCS145 178.59.23.138 +DCS146 87.228.241.43 +DCS147 46.41.1.127 +DCS150 212.237.3.87 +DCS170 210.178.113.173 +DCS171 121.162.91.45 +DCS185 118.152.21.76 +DCS204 185.85.18.162 +DCS206 193.190.240.227 +DCS208 151.80.155.39 +DCS210 45.62.210.243 +DCS212 52.38.90.188 +DCS214 185.47.129.230 +DCS216 74.214.25.135 +DCS222 93.189.136.50 +DCS227 86.122.173.9 +DCS228 212.237.33.114 +DCS230 80.250.3.114 +DCS232 213.47.219.169 +DCS241 151.80.158.227 +DCS242 73.14.84.43 +DCS246 172.93.48.159 +DCS248 158.69.206.45 +DCS252 118.21.66.186 +DCS255 245.115.35.52 +DCS261 44.144.0.23 +DCS262 44.144.0.24 +DCS264 52.2.131.118 +DCS265 51.255.43.60 +DCS270 158.64.26.132 +DCS291 60.112.168.104 +DCS295 45.62.238.78 +DCS298 133.208.206.141 +DCS299 125.236.227.43 +DCS300 64.137.172.56 +DCS302 144.217.241.23 +DCS307 104.36.40.243 +DCS310 64.137.165.141 +DCS311 78.47.206.12 +DCS313 xlx313.openstd.net +DCS317 44.48.8.15 +DCS321 31.207.110.231 +DCS333 194.116.29.73 +DCS334 199.119.98.174 +DCS336 23.226.233.133 +DCS339 198.98.53.247 +DCS357 52.39.82.54 +DCS359 79.232.250.96 +DCS365 59.139.141.204 +DCS370 188.213.168.24 +DCS373 101.143.24.88 +DCS382 61.116.7.187 +DCS389 106.71.106.79 +DCS390 31.14.140.230 +DCS398 45.62.243.153 +DCS404 91.229.143.187 +DCS412 42.151.104.175 +DCS433 217.160.22.17 +DCS440 114.176.44.90 +DCS441 203.137.99.110 +DCS444 188.68.37.51 +DCS450 64.137.161.11 +DCS486 51.255.172.249 +DCS499 59.157.4.151 +DCS500 172.104.32.192 +DCS502 74.208.88.137 +DCS515 163.44.167.125 +DCS518 176.9.1.168 +DCS519 24.55.202.247 +DCS520 202.6.18.14 +DCS538 133.137.49.171 +DCS550 151.80.141.175 +DCS555 64.137.186.11 +DCS569 219.122.146.110 +DCS570 104.128.230.153 +DCS573 216.189.148.204 +DCS595 219.104.19.203 +DCS600 13.69.14.204 +DCS601 51.141.52.193 +DCS602 212.56.100.200 +DCS603 24.233.107.8 +DCS610 103.1.213.21 +DCS616 44.103.39.7 +DCS626 202.137.244.157 +DCS666 54.144.216.63 +DCS689 97.107.128.47 +DCS699 82.102.5.239 +DCS700 78.47.222.93 +DCS706 93.186.255.126 +DCS707 90.145.156.196 +DCS708 202.218.37.62 +DCS709 212.237.34.32 +DCS711 212.237.18.27 +DCS712 153.227.250.188 +DCS714 85.214.119.76 +DCS724 191.232.36.180 +DCS737 195.130.75.246 +DCS746 178.254.34.44 +DCS747 93.209.43.173 +DCS748 64.137.197.36 +DCS750 203.86.206.49 +DCS751 210.55.201.126 +DCS755 193.248.45.246 +DCS766 201.62.48.61 +DCS773 94.177.175.230 +DCS777 45.62.251.163 +DCS781 175.179.238.153 +DCS787 87.154.55.113 +DCS789 75.144.65.122 +DCS800 52.26.26.195 +DCS801 77.117.160.175 +DCS807 118.8.17.123 +DCS812 126.25.168.252 +DCS813 97.76.81.165 +DCS850 88.198.94.77 +DCS870 103.3.234.95 +DCS880 176.10.105.211 +DCS886 118.163.103.178 +DCS887 118.163.103.177 +DCS888 31.14.135.7 +DCS897 92.222.23.124 +DCS900 85.214.114.27 +DCS901 77.117.160.175 +DCS906 212.237.11.53 +DCS908 212.237.2.183 +DCS909 216.86.147.198 +DCS910 94.177.207.26 +DCS911 5.196.73.89 +DCS921 44.143.184.83 +DCS929 158.69.166.132 +DCS930 94.177.160.5 +DCS931 173.50.88.34 +DCS933 37.59.119.115 +DCS940 202.218.37.121 +DCS950 158.64.26.134 +DCS960 80.211.226.89 +DCS964 52.173.142.244 +DCS967 95.158.165.32 +DCS972 46.121.158.50 +DCS974 94.177.217.52 +DCS975 176.31.161.9 +DCS976 212.237.36.71 +DCS978 193.70.0.229 +DCS986 81.89.102.160 +DCS987 185.32.183.148 +DCS989 xrf989.bbhill.net +DCS990 35.164.237.63 +DCS995 142.116.255.245 +DCS997 94.177.187.40 +DCS998 44.140.236.20 +DCS999 94.177.173.53 diff --git a/Data/DExtra_Hosts.txt b/Data/DExtra_Hosts.txt new file mode 100644 index 0000000..d97b33d --- /dev/null +++ b/Data/DExtra_Hosts.txt @@ -0,0 +1,199 @@ +#>>Downloaded from W6KD host file server +#>>Last updated 17 NOV 2016 by W6KD + +XRF000 000.xreflector.org +XRF001 xlx001.homepc.it +XRF002 xrf002.dstar.club +XRF003 xrf003.iw0red.it +XRF004 xrf004.kb8zgl.net +XRF005 216.16.240.236 +XRF006 xrf006.xrefl.net +XRF007 xrf007.ea5gf.es +XRF008 95.110.231.219 +XRF010 xlx010.n8qq.com +XRF011 xlx011.n8qq.com +XRF012 xrf012.papasys.com +XRF014 xrf014.iz0rin.it +XRF015 xrf015.theapplecore.me +XRF016 xrf016.ampr.at +XRF018 99.167.129.166 +XRF019 66.30.81.236 +XRF020 67.210.212.144 +XRF021 44.103.32.250 +XRF022 xrf022.tms-it.net +XRF024 xrf024.dstar.at +XRF025 025.ham-digital.es +XRF027 194.116.29.78 +XRF028 stn028.dstar.be +XRF029 xrf029.tms-it.net +XRF030 xrf030.oe3xht.at +XRF032 xlx032.epf.lu +XRF033 46.226.178.81 +XRF035 xrf035.wa7dre.org +XRF036 xrf036.ddns.net +XRF037 185.58.193.163 +XRF038 66.6.171.228 +XRF040 xrf040.dyndns.org +XRF041 167.88.37.80 +XRF042 xrf042.luthienstar.fr +XRF043 xrf043.aotnet.it +XRF044 82.1.185.173 +XRF045 45.62.233.223 +XRF046 xlx.c4fm.se +XRF047 xlx047.ddns.net +XRF052 xrf052.dip.jp +XRF055 95.110.229.195 +XRF061 68.115.205.110 +XRF062 xlx.maryland-dstar.net +XRF063 162.248.141.148 +XRF064 xrf064.owari.biz +XRF067 xrf067.f5kav.org +XRF068 xrf068.ircddb.it +XRF069 069.xreflector.es +XRF070 xrf070.iptime.org +XRF071 xrf.elechomebrew.com +XRF073 147.102.7.34 +XRF074 xrf074.dyndns.org +XRF075 xrf075.ir9bs.it +XRF076 xrf076.xreflector-jp.org +XRF077 xrf077.duckdns.org +XRF078 xrf078.duckdns.org +XRF080 jr3vh.jpn.ph.jp +XRF081 ja3gqj.dip.jp +XRF084 xrf084.fabbroni.eu +XRF085 jr1ofp.dip.jp +XRF088 xrf088.pa4tw.nl +XRF091 091.xreflector.es +XRF098 xrf098.dip.jp +XRF100 xlx100.xlxreflector.org +XRF101 xlx101.xlxreflector.org +XRF102 xlx102.xlxreflector.org +XRF103 xlx103.xlxreflector.org +XRF104 xlx104.xlxreflector.org +XRF112 112.xreflector.es +XRF113 xlx113.homepc.it +XRF113 xrf113.dstarspain.es +XRF118 xlx118.ns0.it +XRF120 24.43.83.140 +XRF123 213.126.90.100 +XRF125 xlx125.dyndns.hu +XRF132 xrf132.dstar.radom.pl +XRF133 xrf133.gb7de.co.uk +XRF145 178.59.21.32 +XRF147 46.41.1.127 +XRF150 xrf150.dstarspain.es +XRF170 dvham.mooo.com +XRF177 xlx177.webandcloud.net +XRF200 xrf200.theapplecore.co.uk +XRF204 xlx204.ph0dv.nl +XRF210 210.xreflector.org +XRF212 xlx212.dstar.club +XRF214 xlx214.sustrai.org +XRF216 74.214.25.135 +XRF223 k0pra.ddns.net +XRF232 xrf232.tms-it.net +XRF248 xrf248.dyndns.org +XRF250 xrf250.dstar.su +XRF255 xrf255.reflector.up4dar.de +XRF262 xrf262.reflector.up4dar.de +XRF264 52.2.131.118 +XRF270 xrf270.reflector.up4dar.de +XRF275 xrf275.dyndns.org +XRF295 xrf295.dyndns.org +XRF300 300.xreflector.org +XRF307 xlx307.ddns.net +XRF308 xlx308.w6kd.com +XRF310 xrf310.xrefl.net +XRF311 xrf311.ernix.de +XRF312 xrf312.xrefl.net +XRF313 xlx313.xrefl.net +XRF314 xlx314.xrefl.net +XRF317 xrf317.crossroadsdmr.org +XRF321 vps.makeitrad.com +XRF333 xrf333.f1smf.com +XRF336 xrf336.mawcg.org +XRF350 350.dstarspain.es +XRF353 94.173.206.53 +XRF357 xlx357.w6kd.com +XRF370 xrf370.selfip.com +XRF387 195.130.59.77 +XRF390 xrf390.aotnet.it +XRF398 104.167.117.71 +XRF400 xrf400.no-ip.org +XRF404 xlx.bendiksverden.net +XRF413 xlx413.xrefl.net +XRF420 kc9qen.com +XRF423 4ix.hacktic.de +XRF433 xrf433.de +XRF440 dg8rp.dynaccess.de +XRF443 xrf443.arisondrio.it +XRF444 xlx444.pa3dfn.nl +XRF450 450.xreflector.org +XRF456 xrf456.de +XRF490 xrf490.dyndns.org +XRF500 125.63.57.138 +XRF502 74.208.88.137 +XRF518 xrf518.n18.de +XRF519 24.55.196.105 +XRF550 550.xreflector.es +XRF555 xrf555.w6kd.com +XRF556 xrf556.w6kd.com +XRF569 xlxreflector.jpn.ph +XRF570 xrf.no-ip.org +XRF573 216.189.148.204 +XRF580 67.20.31.79 +XRF600 xrf600.gb7de.co.uk +XRF603 xlx603.cnharc.org +XRF610 xrf610.vkradio.com +XRF666 vpngrf.webandcloud.net +XRF669 xrf669.no-ip.org +XRF678 xrf678.ddns.net +XRF699 xlx.tekniksnack.se +XRF700 xrf700.d-star.se +XRF706 xlx706.iz0rin.it +XRF707 xrf707.openquad.net +XRF710 oe7mfi.ddns.net +XRF714 85.214.119.76 +XRF719 199.227.117.121 +XRF720 xrf720.freestar.us +XRF724 191.232.36.180 +XRF727 w4icy.inerrantenergy.com +XRF737 195.130.75.246 +XRF740 imagewell.duckdns.org +XRF747 xrf747.de +XRF748 xrf748.dyndns.org +XRF750 104.128.230.153 +XRF757 xrf757.openquad.net +XRF766 xlx.amrase.org.br +XRF767 xrf767.de +XRF773 xrf773.iz0rin.it +XRF777 112.218.40.91 +XRF787 xrf787.de +XRF789 xrf789.dstarxlx.com.br +XRF807 hajikko.iobb.net +XRF810 810.xreflector.org +XRF813 kj4qal.inerrantenergy.com +XRF828 xlx828.ddnss.de +XRF850 xrf850.xrfmaster.net +XRF851 xrf851.rsdt.de +XRF860 xrf.njpaasterisk.org +XRF870 103.18.207.114 +XRF886 xrf886.metropit.net +XRF888 xlx888.ns0.it +XRF897 92.222.23.124 +XRF901 xrf901.dyndns.org +XRF902 xrf902.dyndns.org +XRF905 199.212.121.20 +XRF906 xrf906.radioclubveleta.es +XRF909 xrf909.ealink.org +XRF911 5.196.73.89 +XRF920 xrf920.oe7xxr.ampr.at +XRF929 xrf929.ddns.net +XRF930 xreflector.ddns.net +XRF950 xlx950.epf.lu +XRF986 81.89.102.160 +XRF987 xrf987.metro-uhf.org +XRF988 988.xreflector.es +XRF989 xrf989.bbhill.net +XRF998 xlx.sm7.hamnet.nu +XRF999 xrf999.no-ip.org diff --git a/Data/DPlus_Hosts.txt b/Data/DPlus_Hosts.txt new file mode 100644 index 0000000..5e6bf6c --- /dev/null +++ b/Data/DPlus_Hosts.txt @@ -0,0 +1,110 @@ +#>>Downloaded from W6KD host file server +#>>Last updated 17 NOV 2016 by W6KD +#>>Request changes on this server's lists by posting at +#>>http://xrefl.boards.net/board/2/directory-changes-forum +REF001 ref001.dstargateway.org +REF002 ref002.dstargateway.org +REF003 ref003.dstargateway.org +REF004 ref004.dstargateway.org +REF005 ref005.dstargateway.org +REF006 ref006.dstargateway.org +REF007 ref007.dstargateway.org +REF008 ref008.dstargateway.org +REF009 ref009.dstargateway.org +REF010 ref010.dstargateway.org +REF011 ref011.dstargateway.org +REF012 ref012.dstargateway.org +REF013 ref013.dstargateway.org +REF014 ref014.dstargateway.org +REF015 ref015.dstargateway.org +REF016 ref016.dstargateway.org +REF017 ref017.dstargateway.org +REF018 ref018.dstargateway.org +REF019 ref019.dstargateway.org +REF020 ref020.dstargateway.org +REF021 ref021.dstargateway.org +REF022 ref022.dstargateway.org +REF023 ref023.dstargateway.org +REF024 ref024.dstargateway.org +REF025 ref025.dstargateway.org +REF026 ref026.dstargateway.org +REF027 ref027.dstargateway.org +REF028 ref028.dstargateway.org +REF029 ref029.dstargateway.org +REF030 ref030.dstargateway.org +REF031 ref031.dstargateway.org +REF032 ref032.dstargateway.org +REF033 ref033.dstargateway.org +REF034 ref034.dstargateway.org +REF035 ref035.dstargateway.org +REF036 ref036.dstargateway.org +REF037 ref037.dstargateway.org +REF038 ref038.dstargateway.org +REF039 ref039.dstargateway.org +REF040 ref040.dstargateway.org +REF041 ref041.dstargateway.org +REF042 ref042.dstargateway.org +REF043 ref043.dstargateway.org +REF044 ref044.dstargateway.org +REF045 ref045.dstargateway.org +REF046 ref046.dstargateway.org +REF047 ref047.dstargateway.org +REF048 ref048.dstargateway.org +REF049 ref049.dstargateway.org +REF050 ref050.dstargateway.org +REF051 ref051.dstargateway.org +REF052 ref052.dstargateway.org +REF053 ref053.dstargateway.org +REF054 ref054.dstargateway.org +REF055 ref055.dstargateway.org +REF056 ref056.dstargateway.org +REF057 ref057.dstargateway.org +REF058 ref058.dstargateway.org +REF059 ref059.dstargateway.org +REF060 ref060.dstargateway.org +REF061 ref061.dstargateway.org +REF062 ref062.dstargateway.org +REF063 ref063.dstargateway.org +REF064 ref064.dstargateway.org +REF065 ref065.dstargateway.org +REF066 ref066.dstargateway.org +REF067 ref067.dstargateway.org +REF068 ref068.dstargateway.org +REF069 ref069.dstargateway.org +REF070 ref070.dstargateway.org +REF071 ref071.dstargateway.org +REF072 ref072.dstargateway.org +#REF073 ref073.dstargateway.org +#REF074 ref074.dstargateway.org +REF075 ref075.dstargateway.org +REF076 ref076.dstargateway.org +REF077 ref077.dstargateway.org +REF078 ref078.dstargateway.org +#REF079 ref079.dstargateway.org +#REF080 ref080.dstargateway.org +#REF081 ref081.dstargateway.org +#REF082 ref082.dstargateway.org +#REF083 ref083.dstargateway.org +#REF084 ref084.dstargateway.org +#REF085 ref085.dstargateway.org +#REF086 ref086.dstargateway.org +#REF087 ref087.dstargateway.org +#REF088 ref088.dstargateway.org +#REF089 ref089.dstargateway.org +#REF090 ref090.dstargateway.org +#REF091 ref091.dstargateway.org +#REF092 ref092.dstargateway.org +#REF093 ref093.dstargateway.org +#REF094 ref094.dstargateway.org +#REF095 ref095.dstargateway.org +#REF096 ref096.dstargateway.org +#REF097 ref097.dstargateway.org +#REF098 ref098.dstargateway.org +#REF099 ref099.dstargateway.org +REF117 ref001.dstargateway.org +REF212 xlx212.dstar.club +REF308 xlx308.w6kd.com +REF313 xlx313.xrefl.net +REF357 xlx357.w6kd.com +REF404 xlx.bendiksverden.net +REF850 xrf850.xrfmaster.net diff --git a/Data/TIME_de_DE.ambe b/Data/TIME_de_DE.ambe new file mode 100644 index 0000000000000000000000000000000000000000..132926a5b5bf79678aa5da21c523b28c09eb1846 GIT binary patch literal 17248 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 WdWdnbWQkj=ucHu1%p-yIt^Wf~9=6W_ literal 0 HcmV?d00001 diff --git a/Data/TIME_fr_FR.indx b/Data/TIME_fr_FR.indx new file mode 100644 index 0000000..085f7c8 --- /dev/null +++ b/Data/TIME_fr_FR.indx @@ -0,0 +1,20 @@ +bonjour 0 79 +cinq 80 48 +deux 129 42 +dix 172 55 +et_demie 228 69 +et_quart 298 71 +heure 370 64 +heures 435 64 +huit 500 47 +il_est 548 58 +midi 607 73 +minuit 681 68 +moins_le_quart 750 89 +neuf 840 46 +onze 887 65 +quatre 953 45 +sept 999 49 +six 1049 53 +trois 1103 43 +une 1147 72 diff --git a/Data/TIME_se_SE.ambe b/Data/TIME_se_SE.ambe new file mode 100644 index 0000000000000000000000000000000000000000..d9969bfed7ace8b8b5fb5a7fe55352c39142e03a GIT binary patch literal 15340 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 Q5mhj02_70iIwP|93mKUH5&!@I literal 0 HcmV?d00001 diff --git a/Data/de_DE.indx b/Data/de_DE.indx new file mode 100644 index 0000000..0f9fcdd --- /dev/null +++ b/Data/de_DE.indx @@ -0,0 +1,44 @@ +0 78 24 +1 121 24 +2 163 25 +3 202 28 +4 248 30 +5 295 27 +6 340 32 +7 391 32 +8 440 25 +9 484 32 +alpha 533 25 +bravo 576 31 +charlie 629 26 +delta 676 30 +A 725 19 +B 769 21 +C 818 27 +D 870 23 +E 917 23 +F 965 23 +G 1014 22 +H 1060 24 +I 1109 18 +J 1151 26 +K 1207 26 +L 1258 23 +M 1305 21 +N 1351 25 +O 1401 19 +P 1446 19 +Q 1493 24 +R 1543 23 +S 1591 21 +T 1644 23 +U 1693 22 +V 1740 19 +W 1784 22 +X 1831 24 +Y 1883 34 +Z 1943 25 +linkedto 1995 40 +notlinked 2088 50 +linkingto 2158 44 +isbusy 2268 40 diff --git a/Data/dk_DK.ambe b/Data/dk_DK.ambe new file mode 100644 index 0000000000000000000000000000000000000000..d9cc78a32051d41c6101ade209a6139d5a39c3fc GIT binary patch literal 19669 zcma&OcRbZ^A3x4<92|Rd>|<{ol8zCM5t5?FDl;olD62X+I5_q?HpkvGJ9`r%Ma$ls zBBT6heY!vQ{ri5u|DDI%>$<83M;+9J*G4FnRw^G<(=AS-#A%1&jpd(wz%%T8O#W8(Si%lsnTLc&5)Uo#p@0C-e8*f6Obw?Q~wh=l*+M|BUsc zXIi^o8lKHP@TWe1%{}So`iKtvULVY#b^bLkHcTn7F};o9$-RyrGLa8N2g2=Sj2^NX z<496hiTyBr-}t&W9Vp(dOVaSyea>%G{{H3pOl??0Z1L zfam+7&4buC)Jc_xO^qMM253I=zw`Q#qWk)5%8R!Q-{&IRD$qEbe` zjj1iCK6(XCDT>hJmbYIbtZSuF_jqyvn=1@@a)9vO9o}`mWMuTztNAM$#rI}k`q|v= z+}I1R+E*zAJd8rGUSL~6zkNOGWJD8F%dNHI1ig2+^U_KAo>|_V4M5)qUMArVuU(hdEeoTgmp8v+#h+}dL%xba!IVaurh`?5ZyFd)zpx#ijBS<*r8t5V z@;~-=Nk4gF!2YcRC+%{_&8l%PBk%3)rhcq@@%(22z89n9;L?)4BTDJxJqpJN>X#G` zSM?9A$jCb(vG%-A{U)mR;8Z{h6Qa4P+ZGnV1h&c%`YR>9a+4aMaD{!w)pg@~V>4vl(k*^$A@yHZT zlYqpmb+eKu#>F?8>-@*vHtf{f65mCN8emkUDg2Ibo|u;{X?|0xt+>;%E!&F~EMAsp z^Q(`z9~%I*^4Bp5Bx|~M-@)&Y0Ul59^AFT0T_1_33K#zkIwi_6(;5zc^`Bl z>=q;C8P;|Pn1+>N&zpBtZdMzsQ;5(CA5k>H6iKh-UeDqbYDHnmsD-^WU}&pIZGwcS zgJeA!l=q@1_MGz0Ez;e4P`fi!aPe-cy95l5BU{0-$z)aeiNd|C(E=5D@2=#XCrY^0 z`e@54inhZ<894GnQH6kXnOqtjk%5x@$Q+F58yDcK6D~o-DC0z0tkX!KgWiLS0Or3QkL`KLuGnIXgbM^@Nj9ZA74Vk z%k)4XxtZyI%h3Ky1|_v~`sH^&XRT0Dr>%xy#=koJ^`{>lqNGOt5ss_=h!0AUvO^tq zCE9W^T_O9NYg1p=7USmF6U~3>q|WT~AIDK?!6VzU=f|zLFP3G0bSHW)HfaT8`^osw zt$$vywxFm{@A~wM0{E=wwk+>Qq@_a7MggWNL-!cK>?pVCu?I=%w@o^%8PIv0((N*F z@Q!B=HmPNM;#PaI#=#lrV#9a>kMLwC_+_@qzgL#A0&B;hQ;caW#Z!Y6fAvs_i zbn+XEv~tsB1)(N9?b)U{ahRp^QRPk1E+vKc3|VwZA>Er*5FbkCE`!ZtX(%4bjFGWf zMXZ;T^wy)t;1FVF8(-7<6nmk>b4WIzTKw-7TMYSUp?)vjpT+#WDIq^M1u-A~XLeZQcH)=I#dHn3zc*~1Cw zj2aClNp?&>^ou4o#xStSX(HWA*LnEk!QPLUF#yz~BFZW<{Z=*z4 zM_i&jp;{OR32S7z(dqzUCB1h(;+yj&6XT79=aFhV#@=79ylg058P|Qcsv4R}IUe|! z^^3v4lGO`;f_E-cwy%dijER{*-?el;X6P#8$*|X8PO0Est>?3S z@WbuWR^zCzvhT&qzfxRs>p86b3dt82hNpBy zKt6>s^H?{>0Y9Spzha~qYsa54+gU*MBLbm$avCH3j>mpSAebMK6_q2|&!{QV+#8d2 z7NMOT{z)F5+Bp;}XU~3VBftlJ?a}MNFbS<}aygpmHj5l(KlH7P;yuwKF0~lN>DnBf zoXgWGK>oJ5e5p1I0VOWFl-`0u#mx`;*x+86=4YD^d+Je3GWH$wpHPk;jxV>_D=u8$2_BECEfFTA zt{`~XSplozrz%O0l37-3@?@<2ipnJ=NWA?4%h{o6Y%RM)Ce#lMwVKnYOPJ)OB+5ZQbU)GN{ z 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 literal 0 HcmV?d00001 diff --git a/Data/en_GB.indx b/Data/en_GB.indx new file mode 100644 index 0000000..ea3889d --- /dev/null +++ b/Data/en_GB.indx @@ -0,0 +1,44 @@ +0 85 41 +1 144 24 +2 186 25 +3 229 30 +4 278 35 +5 331 35 +6 384 39 +7 441 34 +8 494 29 +9 541 35 +alpha 595 31 +bravo 646 33 +charlie 700 31 +delta 753 30 +A 804 24 +B 850 33 +C 898 23 +D 933 33 +E 990 34 +F 1041 34 +G 1093 34 +H 1145 40 +I 1204 30 +J 1253 35 +K 1307 33 +L 1358 30 +M 1406 28 +N 1452 30 +O 1500 23 +P 1541 32 +Q 1592 39 +R 1648 23 +S 1689 33 +T 1742 32 +U 1791 35 +V 1845 33 +W 1896 38 +X 1953 35 +Y 2007 28 +Z 2060 37 +linked 2124 19 +notlinked 2194 44 +linking 2258 31 +isbusy 2350 33 diff --git a/Data/en_US.ambe b/Data/en_US.ambe new file mode 100644 index 0000000000000000000000000000000000000000..6b91c9187c53a32f3860b3be240e45ebd3bbddc1 GIT binary patch literal 26734 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 literal 0 HcmV?d00001 diff --git a/Data/no_NO.indx b/Data/no_NO.indx new file mode 100644 index 0000000..a3548c0 --- /dev/null +++ b/Data/no_NO.indx @@ -0,0 +1,44 @@ +0 67 28 +1 102 29 +2 137 27 +3 170 25 +4 199 36 +5 238 30 +6 272 38 +7 314 31 +8 349 34 +9 386 28 +alpha 418 36 +bravo 472 30 +charlie 519 34 +delta 569 34 +A 622 23 +B 665 24 +C 710 24 +D 758 21 +E 803 28 +F 856 24 +G 903 32 +H 955 31 +I 1007 18 +J 1045 30 +K 1100 28 +L 1152 19 +M 1196 20 +N 1241 17 +O 1282 27 +P 1332 24 +Q 1378 24 +R 1426 19 +S 1470 31 +T 1524 24 +U 1572 23 +V 1616 20 +W 1658 40 +X 1722 32 +Y 1779 24 +Z 1823 29 +linkedto 1882 40 +notlinked 1966 49 +linkingto 2034 40 +isbusy 2148 38 diff --git a/Data/pl_PL.ambe b/Data/pl_PL.ambe new file mode 100644 index 0000000000000000000000000000000000000000..565d99f569cd41afad4cecf0d67c60adb2bedccf GIT binary patch literal 24169 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 literal 0 HcmV?d00001 diff --git a/Data/pl_PL.indx b/Data/pl_PL.indx new file mode 100644 index 0000000..9f41cf0 --- /dev/null +++ b/Data/pl_PL.indx @@ -0,0 +1,44 @@ +0 99 36 +1 163 38 +2 229 29 +3 290 31 +4 351 37 +5 420 42 +6 490 41 +7 559 43 +8 630 34 +9 692 50 +alpha 771 30 +bravo 820 32 +charlie 873 35 +delta 931 34 +A 987 17 +B 1012 30 +C 1070 25 +D 1118 27 +E 1169 29 +F 1221 36 +G 1281 30 +H 1333 26 +I 1383 18 +J 1425 32 +K 1485 23 +L 1532 26 +M 1581 26 +N 1629 24 +O 1676 18 +P 1724 23 +Q 1773 20 +R 1817 23 +S 1863 35 +T 1926 25 +U 1975 22 +V 2021 28 +W 2070 15 +X 2107 31 +Y 2161 39 +Z 2226 12 +linkedto 2263 45 +notlinked 2360 51 +linkingto 2433 45 +isbusy 2559 59 diff --git a/Data/se_SE.ambe b/Data/se_SE.ambe new file mode 100644 index 0000000000000000000000000000000000000000..7e5567c03ba33b0374a415d11370bbf321d3f55f GIT binary patch literal 24205 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 ROlC3t9BoIjdH#a?{tuS2It>5- literal 0 HcmV?d00001 diff --git a/Data/se_SE.indx b/Data/se_SE.indx new file mode 100644 index 0000000..1f78bef --- /dev/null +++ b/Data/se_SE.indx @@ -0,0 +1,44 @@ +0 83 35 +1 138 33 +2 188 32 +3 240 28 +4 286 41 +5 345 30 +6 393 43 +7 451 34 +8 505 35 +9 555 28 +alpha 603 35 +bravo 660 35 +charlie 716 32 +delta 768 48 +A 836 30 +B 894 28 +C 946 33 +D 1005 22 +E 1053 34 +F 1115 20 +G 1160 36 +H 1223 30 +I 1280 27 +J 1333 35 +K 1393 29 +L 1453 25 +M 1507 26 +N 1560 20 +O 1607 26 +P 1657 28 +Q 1714 34 +R 1781 23 +S 1833 30 +T 1890 25 +U 1943 23 +V 1989 35 +W 2050 47 +X 2126 27 +Y 2178 19 +Z 2223 45 +linkedto 2298 39 +notlinked 2390 51 +linkingto 2461 36 +isbusy 2577 54 diff --git a/GUICommon/AddressTextCtrl.cpp b/GUICommon/AddressTextCtrl.cpp new file mode 100644 index 0000000..c40cfce --- /dev/null +++ b/GUICommon/AddressTextCtrl.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2002,2003,2009 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 "AddressTextCtrl.h" + +CAddressTextCtrl::CAddressTextCtrl(wxWindow* parent, int id, const wxString& value, const wxPoint& pos, const wxSize& size, long style) : +CRestrictedTextCtrl(parent, id, value, pos, size, style, ADDRESS_CHARS) +{ +} + +CAddressTextCtrl::~CAddressTextCtrl() +{ +} + diff --git a/GUICommon/AddressTextCtrl.h b/GUICommon/AddressTextCtrl.h new file mode 100644 index 0000000..a0c99c5 --- /dev/null +++ b/GUICommon/AddressTextCtrl.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2002,2003,2009 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 AddressTextCtrl_H +#define AddressTextCtrl_H + +#include + +#include "RestrictedTextCtrl.h" + +const wxString ADDRESS_CHARS = wxT("0123456789."); + +class CAddressTextCtrl : public CRestrictedTextCtrl { + +public: + CAddressTextCtrl(wxWindow* parent, int id, const wxString& value, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0L); + virtual ~CAddressTextCtrl(); + + private: +}; + +#endif diff --git a/GUICommon/CallsignTextCtrl.cpp b/GUICommon/CallsignTextCtrl.cpp new file mode 100644 index 0000000..f37b11a --- /dev/null +++ b/GUICommon/CallsignTextCtrl.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2002,2003 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 "CallsignTextCtrl.h" + +CCallsignTextCtrl::CCallsignTextCtrl(wxWindow* parent, int id, const wxString& value, const wxPoint& pos, const wxSize& size, long style) : +CRestrictedTextCtrl(parent, id, value, pos, size, style, CALLSIGN_CHARS) +{ +} + +CCallsignTextCtrl::~CCallsignTextCtrl() +{ +} + diff --git a/GUICommon/CallsignTextCtrl.h b/GUICommon/CallsignTextCtrl.h new file mode 100644 index 0000000..70a422e --- /dev/null +++ b/GUICommon/CallsignTextCtrl.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2002,2003 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 CallsignTextCtrl_H +#define CallsignTextCtrl_H + +#include + +#include "RestrictedTextCtrl.h" + +const wxString CALLSIGN_CHARS = wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789/ "); + +class CCallsignTextCtrl : public CRestrictedTextCtrl { + +public: + CCallsignTextCtrl(wxWindow* parent, int id, const wxString& value, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0L); + virtual ~CCallsignTextCtrl(); + + private: +}; + +#endif diff --git a/GUICommon/DCSSet.cpp b/GUICommon/DCSSet.cpp new file mode 100644 index 0000000..6339de8 --- /dev/null +++ b/GUICommon/DCSSet.cpp @@ -0,0 +1,156 @@ +/* + * 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 + * 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 "HostFile.h" +#include "DCSSet.h" +#include "Defs.h" + +#include + +const unsigned int CONTROL_WIDTH = 130U; + +const unsigned int BORDER_SIZE = 5U; + +const int CHOICE_ENABLED = 8787; + +BEGIN_EVENT_TABLE(CDCSSet, wxPanel) + EVT_CHOICE(CHOICE_ENABLED, CDCSSet::onEnabled) +END_EVENT_TABLE() + + +CDCSSet::CDCSSet(wxWindow* parent, int id, const wxString& title, bool dcsEnabled, bool ccsEnabled, const wxString& ccsHost) : +wxPanel(parent, id), +m_title(title), +m_dcsEnabled(NULL), +m_ccsEnabled(NULL), +m_ccsHosts(NULL) +{ + wxFlexGridSizer* sizer = new wxFlexGridSizer(2); + + wxStaticText* dcsEnabledLabel = new wxStaticText(this, -1, wxT("DCS")); + sizer->Add(dcsEnabledLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_dcsEnabled = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + m_dcsEnabled->Append(_("Disabled")); + m_dcsEnabled->Append(_("Enabled")); + sizer->Add(m_dcsEnabled, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_dcsEnabled->SetSelection(dcsEnabled ? 1 : 0); + + wxStaticText* ccsEnabledLabel = new wxStaticText(this, -1, wxT("CCS")); + sizer->Add(ccsEnabledLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_ccsEnabled = new wxChoice(this, CHOICE_ENABLED, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + m_ccsEnabled->Append(_("Disabled")); + m_ccsEnabled->Append(_("Enabled")); + sizer->Add(m_ccsEnabled, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_ccsEnabled->SetSelection(ccsEnabled ? 1 : 0); + + wxStaticText* ccsHostsLabel = new wxStaticText(this, -1, _("Server")); + sizer->Add(ccsHostsLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_ccsHosts = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + sizer->Add(m_ccsHosts, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxFileName fileName(wxFileName::GetHomeDir(), CCS_HOSTS_FILE_NAME); + if (!fileName.IsFileReadable()) { +#if defined(__WINDOWS__) + fileName.Assign(::wxGetCwd(), CCS_HOSTS_FILE_NAME); +#else + fileName.Assign(wxT(DATA_DIR), CCS_HOSTS_FILE_NAME); +#endif + } + + CHostFile file(fileName.GetFullPath(), false); + + for (unsigned int i = 0U; i < file.getCount(); i++) + m_ccsHosts->Append(file.getName(i)); + + if (ccsHost.IsEmpty()) { + m_ccsHosts->SetSelection(0); + } else { + bool res = m_ccsHosts->SetStringSelection(ccsHost); + if (!res) + m_ccsHosts->SetSelection(0); + } + + if (ccsEnabled) + m_ccsHosts->Enable(); + else + m_ccsHosts->Disable(); + + SetAutoLayout(true); + + SetSizer(sizer); +} + + +CDCSSet::~CDCSSet() +{ +} + +bool CDCSSet::Validate() +{ + int n = m_dcsEnabled->GetCurrentSelection(); + if (n == wxNOT_FOUND) + return false; + + n = m_ccsEnabled->GetCurrentSelection(); + if (n == wxNOT_FOUND) + return false; + + if (m_ccsHosts->GetCurrentSelection() == wxNOT_FOUND) + return false; + + return true; +} + +bool CDCSSet::getDCSEnabled() const +{ + int c = m_dcsEnabled->GetCurrentSelection(); + if (c == wxNOT_FOUND) + return false; + + return c == 1; +} + +bool CDCSSet::getCCSEnabled() const +{ + int c = m_ccsEnabled->GetCurrentSelection(); + if (c == wxNOT_FOUND) + return false; + + return c == 1; +} + +wxString CDCSSet::getCCSHost() const +{ + int n = m_ccsHosts->GetSelection(); + if (n == wxNOT_FOUND) + return wxEmptyString; + + return m_ccsHosts->GetStringSelection(); +} + +void CDCSSet::onEnabled(wxCommandEvent &event) +{ + int n = m_ccsEnabled->GetCurrentSelection(); + if (n != 1) + m_ccsHosts->Disable(); + else + m_ccsHosts->Enable(); +} diff --git a/GUICommon/DCSSet.h b/GUICommon/DCSSet.h new file mode 100644 index 0000000..1771636 --- /dev/null +++ b/GUICommon/DCSSet.h @@ -0,0 +1,46 @@ +/* + * 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 + * 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 DCSSet_H +#define DCSSet_H + +#include + +class CDCSSet : public wxPanel { +public: + CDCSSet(wxWindow* parent, int id, const wxString& title, bool dcsEnabled, bool ccsEnabled, const wxString& ccsHost); + virtual ~CDCSSet(); + + virtual bool Validate(); + + virtual bool getDCSEnabled() const; + virtual bool getCCSEnabled() const; + virtual wxString getCCSHost() const; + + virtual void onEnabled(wxCommandEvent& event); + +private: + wxString m_title; + wxChoice* m_dcsEnabled; + wxChoice* m_ccsEnabled; + wxChoice* m_ccsHosts; + + DECLARE_EVENT_TABLE() +}; + +#endif diff --git a/GUICommon/DExtraSet.cpp b/GUICommon/DExtraSet.cpp new file mode 100644 index 0000000..f96f948 --- /dev/null +++ b/GUICommon/DExtraSet.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2011,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 "DExtraSet.h" + +const unsigned int CONTROL_WIDTH = 130U; + +const unsigned int BORDER_SIZE = 5U; + +CDExtraSet::CDExtraSet(wxWindow* parent, int id, const wxString& title, bool enabled, unsigned int maxDongles, unsigned int maxLinks) : +wxPanel(parent, id), +m_title(title), +m_enabled(NULL), +m_maxDongles(NULL) +{ + wxFlexGridSizer* sizer = new wxFlexGridSizer(2); + + wxStaticText* enabledLabel = new wxStaticText(this, -1, _("DExtra")); + sizer->Add(enabledLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_enabled = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + m_enabled->Append(_("Disabled")); + m_enabled->Append(_("Enabled")); + sizer->Add(m_enabled, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_enabled->SetSelection(enabled ? 1 : 0); + + wxStaticText* maxDonglesLabel = new wxStaticText(this, -1, _("Max. Dongles")); + sizer->Add(maxDonglesLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_maxDongles = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + for (unsigned int i = 0U; i <= maxLinks; i++) { + wxString text; + text.Printf(wxT("%u"), i); + m_maxDongles->Append(text); + } + sizer->Add(m_maxDongles, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_maxDongles->SetSelection(maxDongles); + + SetAutoLayout(true); + + SetSizer(sizer); +} + + +CDExtraSet::~CDExtraSet() +{ +} + +bool CDExtraSet::Validate() +{ + int n = m_enabled->GetCurrentSelection(); + if (n == wxNOT_FOUND) + return false; + + n = m_maxDongles->GetCurrentSelection(); + if (n == wxNOT_FOUND) + return false; + + return true; +} + +bool CDExtraSet::getEnabled() const +{ + int c = m_enabled->GetCurrentSelection(); + if (c == wxNOT_FOUND) + return false; + + return c == 1; +} + +unsigned int CDExtraSet::getMaxDongles() const +{ + int c = m_maxDongles->GetCurrentSelection(); + if (c == wxNOT_FOUND) + return 0U; + + return (unsigned int)c; +} diff --git a/GUICommon/DExtraSet.h b/GUICommon/DExtraSet.h new file mode 100644 index 0000000..1e50733 --- /dev/null +++ b/GUICommon/DExtraSet.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2011,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 DExtraSet_H +#define DExtraSet_H + +#include + +class CDExtraSet : public wxPanel { +public: + CDExtraSet(wxWindow* parent, int id, const wxString& title, bool enabled, unsigned int maxDongles, unsigned int maxLinks); + virtual ~CDExtraSet(); + + virtual bool Validate(); + + virtual bool getEnabled() const; + virtual unsigned int getMaxDongles() const; + +private: + wxString m_title; + wxChoice* m_enabled; + wxChoice* m_maxDongles; +}; + +#endif diff --git a/GUICommon/DPRSSet.cpp b/GUICommon/DPRSSet.cpp new file mode 100644 index 0000000..5a99dde --- /dev/null +++ b/GUICommon/DPRSSet.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2010 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 "DPRSSet.h" + +const unsigned int BORDER_SIZE = 5U; +const unsigned int CONTROL_WIDTH1 = 200U; +const unsigned int CONTROL_WIDTH2 = 80U; + +const unsigned int PORT_LENGTH = 5U; +const unsigned int PASSWORD_LENGTH = 5U; + +CDPRSSet::CDPRSSet(wxWindow* parent, int id, const wxString& title, bool enabled, const wxString& hostname, unsigned int port) : +wxPanel(parent, id), +m_title(title), +m_enabled(NULL), +m_hostname(NULL), +m_port(NULL) +{ + wxFlexGridSizer* sizer = new wxFlexGridSizer(2); + + wxStaticText* enabledLabel = new wxStaticText(this, -1, _("D-PRS")); + sizer->Add(enabledLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_enabled = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_enabled->Append(_("Disabled")); + m_enabled->Append(_("Enabled")); + sizer->Add(m_enabled, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_enabled->SetSelection(enabled ? 1 : 0); + + wxStaticText* hostnameLabel = new wxStaticText(this, -1, _("Hostname")); + sizer->Add(hostnameLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_hostname = new wxTextCtrl(this, -1, hostname, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + sizer->Add(m_hostname, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* portLabel = new wxStaticText(this, -1, _("Port")); + sizer->Add(portLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxString buffer; + buffer.Printf(wxT("%u"), port); + + m_port = new CPortTextCtrl(this, -1, buffer, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1)); + m_port->SetMaxLength(PORT_LENGTH); + sizer->Add(m_port, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + SetAutoLayout(true); + + SetSizer(sizer); +} + + +CDPRSSet::~CDPRSSet() +{ +} + +bool CDPRSSet::Validate() +{ + int n = m_enabled->GetCurrentSelection(); + if (n == wxNOT_FOUND) + return false; + + wxString hostname = m_hostname->GetValue(); + if (hostname.IsEmpty()) + return true; + + unsigned int port = getPort(); + + if (port == 0U || port > 65535U) { + wxMessageDialog dialog(this, _("The Port is not valid"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + return true; +} + +bool CDPRSSet::getEnabled() const +{ + int c = m_enabled->GetCurrentSelection(); + if (c == wxNOT_FOUND) + return false; + + return c == 1; +} + +wxString CDPRSSet::getHostname() const +{ + return m_hostname->GetValue(); +} + +unsigned int CDPRSSet::getPort() const +{ + unsigned long n; + + m_port->GetValue().ToULong(&n); + + return n; +} diff --git a/GUICommon/DPRSSet.h b/GUICommon/DPRSSet.h new file mode 100644 index 0000000..2d18220 --- /dev/null +++ b/GUICommon/DPRSSet.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2010 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 DPRSSet_H +#define DPRSSet_H + +#include "PortTextCtrl.h" + +#include + +class CDPRSSet : public wxPanel { +public: + CDPRSSet(wxWindow* parent, int id, const wxString& title, bool enabled, const wxString& hostname, unsigned int port); + virtual ~CDPRSSet(); + + virtual bool Validate(); + + virtual bool getEnabled() const; + virtual wxString getHostname() const; + virtual unsigned int getPort() const; + +private: + wxString m_title; + wxChoice* m_enabled; + wxTextCtrl* m_hostname; + CPortTextCtrl* m_port; +}; + +#endif diff --git a/GUICommon/DPlusSet.cpp b/GUICommon/DPlusSet.cpp new file mode 100644 index 0000000..5c70b33 --- /dev/null +++ b/GUICommon/DPlusSet.cpp @@ -0,0 +1,122 @@ +/* + * 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 + * 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 "DPlusSet.h" + +const unsigned int CONTROL_WIDTH = 130U; + +const unsigned int BORDER_SIZE = 5U; + +CDPlusSet::CDPlusSet(wxWindow* parent, int id, const wxString& title, bool enabled, unsigned int maxDongles, unsigned int maxLinks, const wxString& login) : +wxPanel(parent, id), +m_title(title), +m_enabled(NULL), +m_maxDongles(NULL), +m_login(NULL) +{ + wxFlexGridSizer* sizer = new wxFlexGridSizer(2); + + wxStaticText* enabledLabel = new wxStaticText(this, -1, _("D-Plus")); + sizer->Add(enabledLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_enabled = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + m_enabled->Append(_("Disabled")); + m_enabled->Append(_("Enabled")); + sizer->Add(m_enabled, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_enabled->SetSelection(enabled ? 1 : 0); + + wxStaticText* maxDonglesLabel = new wxStaticText(this, -1, _("Max. Dongles")); + sizer->Add(maxDonglesLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_maxDongles = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + for (unsigned int i = 0U; i <= maxLinks; i++) { + wxString text; + text.Printf(wxT("%u"), i); + m_maxDongles->Append(text); + } + sizer->Add(m_maxDongles, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_maxDongles->SetSelection(maxDongles); + + wxStaticText* loginLabel = new wxStaticText(this, -1, _("Login")); + sizer->Add(loginLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_login = new CCallsignTextCtrl(this, -1, login, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + m_login->SetMaxLength(LONG_CALLSIGN_LENGTH); + sizer->Add(m_login, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + SetAutoLayout(true); + + SetSizer(sizer); +} + + +CDPlusSet::~CDPlusSet() +{ +} + +bool CDPlusSet::Validate() +{ + int n = m_enabled->GetCurrentSelection(); + if (n == wxNOT_FOUND) + return false; + + if (n == 1) { + wxString login = m_login->GetValue(); + if (login.IsEmpty() || login.IsSameAs(wxT(" "))) { + wxMessageDialog dialog(this, _("The D-Plus Login is blank"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + } + + n = m_maxDongles->GetCurrentSelection(); + if (n == wxNOT_FOUND) + return false; + + return true; +} + +bool CDPlusSet::getEnabled() const +{ + int c = m_enabled->GetCurrentSelection(); + if (c == wxNOT_FOUND) + return false; + + return c == 1; +} + +unsigned int CDPlusSet::getMaxDongles() const +{ + int c = m_maxDongles->GetCurrentSelection(); + if (c == wxNOT_FOUND) + return 0U; + + return (unsigned int)c; +} + +wxString CDPlusSet::getLogin() const +{ + wxString callsign = m_login->GetValue(); + + callsign.MakeUpper(); + callsign.Append(wxT(" ")); + callsign.Truncate(LONG_CALLSIGN_LENGTH); + + return callsign; +} diff --git a/GUICommon/DPlusSet.h b/GUICommon/DPlusSet.h new file mode 100644 index 0000000..2e9225b --- /dev/null +++ b/GUICommon/DPlusSet.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2010,2011,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 DPlusSet_H +#define DPlusSet_H + +#include "CallsignTextCtrl.h" +#include "Defs.h" + +#include + +class CDPlusSet : public wxPanel { +public: + CDPlusSet(wxWindow* parent, int id, const wxString& title, bool enabled, unsigned int maxDongles, unsigned int maxLinks, const wxString& login); + virtual ~CDPlusSet(); + + virtual bool Validate(); + + virtual bool getEnabled() const; + virtual unsigned int getMaxDongles() const; + virtual wxString getLogin() const; + +private: + wxString m_title; + wxChoice* m_enabled; + wxChoice* m_maxDongles; + CCallsignTextCtrl* m_login; +}; + +#endif diff --git a/GUICommon/DescriptionTextCtrl.cpp b/GUICommon/DescriptionTextCtrl.cpp new file mode 100644 index 0000000..e69bba8 --- /dev/null +++ b/GUICommon/DescriptionTextCtrl.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2002,2003,2009,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 "DescriptionTextCtrl.h" + +CDescriptionTextCtrl::CDescriptionTextCtrl(wxWindow* parent, int id, const wxString& value, const wxPoint& pos, const wxSize& size, long style) : +CRestrictedTextCtrl(parent, id, value, pos, size, style, DESCRIPTION_CHARS) +{ +} + +CDescriptionTextCtrl::~CDescriptionTextCtrl() +{ +} + diff --git a/GUICommon/DescriptionTextCtrl.h b/GUICommon/DescriptionTextCtrl.h new file mode 100644 index 0000000..e0f626b --- /dev/null +++ b/GUICommon/DescriptionTextCtrl.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2002,2003,2009,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 DescriptionTextCtrl_H +#define DescriptionTextCtrl_H + +#include + +#include "RestrictedTextCtrl.h" + +const wxString DESCRIPTION_CHARS = wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,&*()-+=@/?:;"); + +class CDescriptionTextCtrl : public CRestrictedTextCtrl { + +public: + CDescriptionTextCtrl(wxWindow* parent, int id, const wxString& value, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0L); + virtual ~CDescriptionTextCtrl(); + + private: +}; + +#endif diff --git a/GUICommon/GUICommon.vcxproj b/GUICommon/GUICommon.vcxproj new file mode 100644 index 0000000..b04a02c --- /dev/null +++ b/GUICommon/GUICommon.vcxproj @@ -0,0 +1,169 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {02D03515-0BBE-4553-8C40-566A597478F8} + GUICommon + Win32Proj + 10.0.16299.0 + + + + StaticLibrary + v141 + Unicode + true + + + StaticLibrary + v141 + Unicode + true + + + StaticLibrary + v141 + Unicode + + + StaticLibrary + v141 + Unicode + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>14.0.24720.0 + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + $(WXWIN)\include;$(WXWIN)\lib\vc_dll\mswud;$(IncludePath) + + + $(WXWIN)\include;$(WXWIN)\lib\vc_dll\mswud;$(IncludePath) + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + + + + Disabled + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_LIB;DCS_LINK;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + Level3 + EditAndContinue + + + + + Disabled + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_LIB;DCS_LINK;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + ProgramDatabase + + + + + MaxSpeed + true + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_LIB;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + Level3 + ProgramDatabase + + + + + MaxSpeed + true + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_LIB;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/GUICommon/GUICommon.vcxproj.filters b/GUICommon/GUICommon.vcxproj.filters new file mode 100644 index 0000000..479d80d --- /dev/null +++ b/GUICommon/GUICommon.vcxproj.filters @@ -0,0 +1,101 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/GUICommon/PortTextCtrl.cpp b/GUICommon/PortTextCtrl.cpp new file mode 100644 index 0000000..51435d9 --- /dev/null +++ b/GUICommon/PortTextCtrl.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2002,2003,2009 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 "PortTextCtrl.h" + +CPortTextCtrl::CPortTextCtrl(wxWindow* parent, int id, const wxString& value, const wxPoint& pos, const wxSize& size, long style) : +CRestrictedTextCtrl(parent, id, value, pos, size, style, PORT_CHARS) +{ +} + +CPortTextCtrl::~CPortTextCtrl() +{ +} + diff --git a/GUICommon/PortTextCtrl.h b/GUICommon/PortTextCtrl.h new file mode 100644 index 0000000..8d096c8 --- /dev/null +++ b/GUICommon/PortTextCtrl.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2002,2003,2009 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 PortTextCtrl_H +#define PortTextCtrl_H + +#include + +#include "RestrictedTextCtrl.h" + +const wxString PORT_CHARS = wxT("0123456789"); + +class CPortTextCtrl : public CRestrictedTextCtrl { + +public: + CPortTextCtrl(wxWindow* parent, int id, const wxString& value, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0L); + virtual ~CPortTextCtrl(); + + private: +}; + +#endif diff --git a/GUICommon/RemoteSet.cpp b/GUICommon/RemoteSet.cpp new file mode 100644 index 0000000..f37ff59 --- /dev/null +++ b/GUICommon/RemoteSet.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2011 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 "RemoteSet.h" + +const unsigned int PASSWORD_WIDTH = 120U; +const unsigned int PORT_WIDTH = 80U; + +const unsigned int PORT_LENGTH = 5U; + +const unsigned int BORDER_SIZE = 5U; + +CRemoteSet::CRemoteSet(wxWindow* parent, int id, const wxString& title, bool enabled, const wxString& password, unsigned int port) : +wxPanel(parent, id), +m_title(title), +m_enabled(NULL), +m_password(NULL), +m_port(NULL) +{ + wxFlexGridSizer* sizer = new wxFlexGridSizer(2); + + wxStaticText* enabledLabel = new wxStaticText(this, -1, _("Remote")); + sizer->Add(enabledLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_enabled = new wxChoice(this, -1, wxDefaultPosition, wxSize(PASSWORD_WIDTH, -1)); + m_enabled->Append(_("Disabled")); + m_enabled->Append(_("Enabled")); + sizer->Add(m_enabled, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_enabled->SetSelection(enabled ? 1 : 0); + + wxStaticText* passwordLabel = new wxStaticText(this, -1, _("Password")); + sizer->Add(passwordLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_password = new wxTextCtrl(this, -1, password, wxDefaultPosition, wxSize(PASSWORD_WIDTH, -1), wxTE_PASSWORD); + sizer->Add(m_password, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* repeaterPortLabel = new wxStaticText(this, -1, _("Port")); + sizer->Add(repeaterPortLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxString buffer; + buffer.Printf(wxT("%u"), port); + + m_port = new CPortTextCtrl(this, -1, buffer, wxDefaultPosition, wxSize(PORT_WIDTH, -1)); + m_port->SetMaxLength(PORT_LENGTH); + sizer->Add(m_port, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + SetAutoLayout(true); + + SetSizer(sizer); +} + + +CRemoteSet::~CRemoteSet() +{ +} + +bool CRemoteSet::Validate() +{ + int n = m_enabled->GetCurrentSelection(); + if (n == wxNOT_FOUND) { + wxMessageDialog dialog(this, _("Remote Enabled is not set"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + if (n == 0) + return true; + + wxString password = getPassword(); + if (password.IsEmpty()) { + wxMessageDialog dialog(this, _("The Remote Password is empty"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + unsigned int port = getPort(); + if (port == 0U || port > 65535U) { + wxMessageDialog dialog(this, _("The Remote Port is not valid"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + return true; +} + +bool CRemoteSet::getEnabled() const +{ + int n = m_enabled->GetCurrentSelection(); + + return n == 1; +} + +wxString CRemoteSet::getPassword() const +{ + return m_password->GetValue(); +} + +unsigned int CRemoteSet::getPort() const +{ + unsigned long n; + m_port->GetValue().ToULong(&n); + + return n; +} diff --git a/GUICommon/RemoteSet.h b/GUICommon/RemoteSet.h new file mode 100644 index 0000000..2f4a58a --- /dev/null +++ b/GUICommon/RemoteSet.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2011 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 RemoteSet_H +#define RemoteSet_H + +#include "PortTextCtrl.h" + +#include + +class CRemoteSet : public wxPanel { +public: + CRemoteSet(wxWindow* parent, int id, const wxString& title, bool enabled, const wxString& password, unsigned int port); + virtual ~CRemoteSet(); + + virtual bool Validate(); + + virtual bool getEnabled() const; + virtual wxString getPassword() const; + virtual unsigned int getPort() const; + +private: + wxString m_title; + wxChoice* m_enabled; + wxTextCtrl* m_password; + CPortTextCtrl* m_port; +}; + +#endif diff --git a/GUICommon/RepeaterDataSet.cpp b/GUICommon/RepeaterDataSet.cpp new file mode 100644 index 0000000..ce3fc01 --- /dev/null +++ b/GUICommon/RepeaterDataSet.cpp @@ -0,0 +1,554 @@ +/* + * 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 + * 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 "RepeaterDataSet.h" +#include "DStarDefines.h" +#include "HostFile.h" + +#include + +const unsigned int CONTROL_WIDTH1 = 130U; +const unsigned int CONTROL_WIDTH2 = 80U; +const unsigned int CONTROL_WIDTH3 = 40U; + +const unsigned int ADDRESS_LENGTH = 15U; +const unsigned int PORT_LENGTH = 5U; + +const unsigned int BORDER_SIZE = 5U; + + +const int CHOICE_BAND = 8745; +const int CHOICE_TYPE = 8746; + +BEGIN_EVENT_TABLE(CRepeaterDataSet, wxPanel) + EVT_CHOICE(CHOICE_BAND, CRepeaterDataSet::onBand) + EVT_CHOICE(CHOICE_TYPE, CRepeaterDataSet::onType) +END_EVENT_TABLE() + + +CRepeaterDataSet::CRepeaterDataSet(wxWindow* parent, int id, const wxString& title, const wxString& band, HW_TYPE type, const wxString& address, unsigned int port, unsigned char band1, unsigned char band2, unsigned char band3, bool dplusEnabled, bool dExtraEnabled, bool dcsEnabled, const wxString& reflector, bool atStartup, RECONNECT reconnect) : +wxPanel(parent, id), +m_title(title), +m_band(NULL), +m_type(NULL), +m_address(NULL), +m_port(NULL), +m_band1(NULL), +m_band2(NULL), +m_band3(NULL), +m_reflector(NULL), +m_channel(NULL), +m_startup(NULL), +m_reconnect(NULL) +{ + wxFlexGridSizer* sizer = new wxFlexGridSizer(3); + + wxStaticText* bandLabel = new wxStaticText(this, -1, _("Band")); + sizer->Add(bandLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_band = new wxChoice(this, CHOICE_BAND, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1)); + m_band->Append(_("None")); + m_band->Append(wxT("A")); + m_band->Append(wxT("B")); + m_band->Append(wxT("C")); + m_band->Append(wxT("D")); + m_band->Append(wxT("E")); + m_band->Append(wxT("AD")); + m_band->Append(wxT("BD")); + m_band->Append(wxT("CD")); + m_band->Append(wxT("DD")); + m_band->Append(wxT("ED")); + sizer->Add(m_band, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + bool res = m_band->SetStringSelection(band); + if (!res) + m_band->SetSelection(0); + + wxStaticText* dummy1Label = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummy1Label, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxStaticText* hardwareTypeLabel = new wxStaticText(this, -1, _("Type")); + sizer->Add(hardwareTypeLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_type = new wxChoice(this, CHOICE_TYPE, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_type->Append(_("Homebrew")); + m_type->Append(wxT("Icom")); + m_type->Append(_("Dummy")); + sizer->Add(m_type, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_type->SetSelection(int(type)); + + wxStaticText* dummy2Label = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummy2Label, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxStaticText* addressLabel = new wxStaticText(this, -1, _("Address")); + sizer->Add(addressLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_address = new CAddressTextCtrl(this, -1, address, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_address->SetMaxLength(ADDRESS_LENGTH); + sizer->Add(m_address, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* dummy3Label = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummy3Label, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxStaticText* portLabel = new wxStaticText(this, -1, _("Port")); + sizer->Add(portLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxString buffer; + buffer.Printf(wxT("%u"), port); + + m_port = new CPortTextCtrl(this, -1, buffer, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1)); + m_port->SetMaxLength(PORT_LENGTH); + sizer->Add(m_port, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* dummy4Label = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummy4Label, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxStaticText* icomBandsLabel = new wxStaticText(this, -1, _("Bands")); + sizer->Add(icomBandsLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxBoxSizer* bandSizer = new wxBoxSizer(wxHORIZONTAL); + + buffer.Printf(wxT("%u"), band1); + m_band1 = new CPortTextCtrl(this, -1, buffer, wxDefaultPosition, wxSize(CONTROL_WIDTH3, -1)); + m_band1->SetMaxLength(2U); + bandSizer->Add(m_band1, 0, wxALIGN_LEFT, BORDER_SIZE); + + buffer.Printf(wxT("%u"), band2); + m_band2 = new CPortTextCtrl(this, -1, buffer, wxDefaultPosition, wxSize(CONTROL_WIDTH3, -1)); + m_band2->SetMaxLength(2U); + bandSizer->Add(m_band2, 0, wxLEFT | wxALIGN_LEFT, BORDER_SIZE); + + buffer.Printf(wxT("%u"), band3); + m_band3 = new CPortTextCtrl(this, -1, buffer, wxDefaultPosition, wxSize(CONTROL_WIDTH3, -1)); + m_band3->SetMaxLength(2U); + bandSizer->Add(m_band3, 0, wxLEFT | wxALIGN_LEFT, BORDER_SIZE); + + sizer->Add(bandSizer, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxStaticText* dummy0Label = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummy0Label, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxStaticText* reflectorLabel = new wxStaticText(this, -1, _("Reflector")); + sizer->Add(reflectorLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_reflector = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_reflector->Append(_("None")); + + if (dplusEnabled) { + wxFileName fileName1(wxFileName::GetHomeDir(), DPLUS_HOSTS_FILE_NAME); + if (fileName1.IsFileReadable()) { + CHostFile file(fileName1.GetFullPath(), false); + + for (unsigned int i = 0U; i < file.getCount(); i++) + m_reflector->Append(file.getName(i).Trim()); + } + +#if defined(__WINDOWS__) + wxFileName fileName4(::wxGetCwd(), DPLUS_HOSTS_FILE_NAME); +#else + wxFileName fileName4(wxT(DATA_DIR), DPLUS_HOSTS_FILE_NAME); +#endif + if (fileName4.IsFileReadable()) { + CHostFile file(fileName4.GetFullPath(), false); + + for (unsigned int i = 0U; i < file.getCount(); i++) { + wxString name = file.getName(i).Trim(); + if (m_reflector->FindString(name) == wxNOT_FOUND) + m_reflector->Append(name); + } + } + } + + if (dExtraEnabled) { + wxFileName fileName2(wxFileName::GetHomeDir(), DEXTRA_HOSTS_FILE_NAME); + if (fileName2.IsFileReadable()) { + CHostFile file(fileName2.GetFullPath(), false); + + for (unsigned int i = 0U; i < file.getCount(); i++) + m_reflector->Append(file.getName(i).Trim()); + } + +#if defined(__WINDOWS__) + wxFileName fileName5(::wxGetCwd(), DEXTRA_HOSTS_FILE_NAME); +#else + wxFileName fileName5(wxT(DATA_DIR), DEXTRA_HOSTS_FILE_NAME); +#endif + if (fileName5.IsFileReadable()) { + CHostFile file(fileName5.GetFullPath(), false); + + for (unsigned int i = 0U; i < file.getCount(); i++) { + wxString name = file.getName(i).Trim(); + if (m_reflector->FindString(name) == wxNOT_FOUND) + m_reflector->Append(name); + } + } + } + + if (dcsEnabled) { + wxFileName fileName3(wxFileName::GetHomeDir(), DCS_HOSTS_FILE_NAME); + if (fileName3.IsFileReadable()) { + CHostFile file(fileName3.GetFullPath(), false); + + for (unsigned int i = 0U; i < file.getCount(); i++) + m_reflector->Append(file.getName(i).Trim()); + } + +#if defined(__WINDOWS__) + wxFileName fileName6(::wxGetCwd(), DCS_HOSTS_FILE_NAME); +#else + wxFileName fileName6(wxT(DATA_DIR), DCS_HOSTS_FILE_NAME); +#endif + if (fileName6.IsFileReadable()) { + CHostFile file(fileName6.GetFullPath(), false); + + for (unsigned int i = 0U; i < file.getCount(); i++) { + wxString name = file.getName(i).Trim(); + if (m_reflector->FindString(name) == wxNOT_FOUND) + m_reflector->Append(name); + } + } + } + + sizer->Add(m_reflector, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + if (reflector.IsEmpty()) { + m_reflector->SetSelection(0); + } else { + wxString name = reflector; + name.Truncate(7U); + name.Trim(); + bool res = m_reflector->SetStringSelection(name); + if (!res) + m_reflector->SetSelection(0); + } + + m_channel = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1)); + m_channel->Append(wxT("A")); + m_channel->Append(wxT("B")); + m_channel->Append(wxT("C")); + m_channel->Append(wxT("D")); + m_channel->Append(wxT("E")); + m_channel->Append(wxT("F")); + m_channel->Append(wxT("G")); + m_channel->Append(wxT("H")); + m_channel->Append(wxT("I")); + m_channel->Append(wxT("J")); + m_channel->Append(wxT("K")); + m_channel->Append(wxT("L")); + m_channel->Append(wxT("M")); + m_channel->Append(wxT("N")); + m_channel->Append(wxT("O")); + m_channel->Append(wxT("P")); + m_channel->Append(wxT("Q")); + m_channel->Append(wxT("R")); + m_channel->Append(wxT("S")); + m_channel->Append(wxT("T")); + m_channel->Append(wxT("U")); + m_channel->Append(wxT("V")); + m_channel->Append(wxT("W")); + m_channel->Append(wxT("X")); + m_channel->Append(wxT("Y")); + m_channel->Append(wxT("Z")); + sizer->Add(m_channel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + res = m_channel->SetStringSelection(reflector.Right(1U)); + if (!res) + m_channel->SetSelection(0); + + wxStaticText* startupLabel = new wxStaticText(this, -1, _("Startup")); + sizer->Add(startupLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_startup = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_startup->Append(_("No")); + m_startup->Append(_("Yes")); + sizer->Add(m_startup, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_startup->SetSelection(atStartup ? 1 : 0); + + wxStaticText* dummy5Label = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummy5Label, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxStaticText* reconnectLabel = new wxStaticText(this, -1, _("Reconnect")); + sizer->Add(reconnectLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_reconnect = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_reconnect->Append(_("Never")); + m_reconnect->Append(_("Fixed")); + m_reconnect->Append(_("5 minutes")); + m_reconnect->Append(_("10 minutes")); + m_reconnect->Append(_("15 minutes")); + m_reconnect->Append(_("20 minutes")); + m_reconnect->Append(_("25 minutes")); + m_reconnect->Append(_("30 minutes")); + m_reconnect->Append(_("60 minutes")); + m_reconnect->Append(_("90 minutes")); + m_reconnect->Append(_("120 minutes")); + m_reconnect->Append(_("180 minutes")); + sizer->Add(m_reconnect, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_reconnect->SetSelection(int(reconnect)); + + if (isDDMode()) { + m_type->SetSelection(1); + m_reflector->SetSelection(0); + m_channel->SetSelection(0); + m_startup->SetSelection(0); + m_reconnect->SetSelection(0); + + m_type->Disable(); + m_reflector->Disable(); + m_channel->Disable(); + m_startup->Disable(); + m_reconnect->Disable(); + } else { + m_type->Enable(); + m_reflector->Enable(); + m_channel->Enable(); + m_startup->Enable(); + m_reconnect->Enable(); + } + + if (type == HW_ICOM) { + m_band1->Enable(); + m_band2->Enable(); + m_band3->Enable(); + } else { + m_band1->Disable(); + m_band2->Disable(); + m_band3->Disable(); + } + + SetAutoLayout(true); + + SetSizer(sizer); +} + + +CRepeaterDataSet::~CRepeaterDataSet() +{ +} + +bool CRepeaterDataSet::Validate() +{ + int band = m_band->GetCurrentSelection(); + if (band == wxNOT_FOUND) + return false; + + if (band == 0) + return true; + + if (m_type->GetCurrentSelection() == wxNOT_FOUND) { + wxMessageDialog dialog(this, _("The Hardware Type is not set"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + wxString address = getAddress(); + + if (address.IsEmpty()) { + wxMessageDialog dialog(this, _("The Repeater Address is not valid"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + unsigned int port = getPort(); + + if (port == 0U || port > 65535U) { + wxMessageDialog dialog(this, _("The Repeater Port is not valid"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + if (m_reflector->GetCurrentSelection() == wxNOT_FOUND) + return false; + + if (m_channel->GetCurrentSelection() == wxNOT_FOUND) + return false; + + if (m_startup->GetCurrentSelection() == wxNOT_FOUND) + return false; + + if (m_reconnect->GetCurrentSelection() == wxNOT_FOUND) + return false; + + return true; +} + +wxString CRepeaterDataSet::getBand() const +{ + int c = m_band->GetCurrentSelection(); + + switch (c) { + case 0: + return wxT(" "); + case 1: + return wxT("A"); + case 2: + return wxT("B"); + case 3: + return wxT("C"); + case 4: + return wxT("D"); + case 5: + return wxT("E"); + case 6: + return wxT("AD"); + case 7: + return wxT("BD"); + case 8: + return wxT("CD"); + case 9: + return wxT("DD"); + case 10: + return wxT("ED"); + default: + return wxT(" "); + } +} + +wxString CRepeaterDataSet::getAddress() const +{ + return m_address->GetValue(); +} + +HW_TYPE CRepeaterDataSet::getType() const +{ + if (isDDMode()) + return HW_ICOM; + + int n = m_type->GetCurrentSelection(); + if (n == wxNOT_FOUND) + n = 0; + + return HW_TYPE(n); +} + +unsigned int CRepeaterDataSet::getPort() const +{ + unsigned long n; + m_port->GetValue().ToULong(&n); + + return n; +} + +unsigned char CRepeaterDataSet::getBand1() const +{ + unsigned long n; + m_band1->GetValue().ToULong(&n); + + return n; +} + +unsigned char CRepeaterDataSet::getBand2() const +{ + unsigned long n; + m_band2->GetValue().ToULong(&n); + + return n; +} + +unsigned char CRepeaterDataSet::getBand3() const +{ + unsigned long n; + m_band3->GetValue().ToULong(&n); + + return n; +} + +wxString CRepeaterDataSet::getReflector() const +{ + if (isDDMode()) + return wxEmptyString; + + if (m_reflector->GetCurrentSelection() == 0) + return wxEmptyString; + + wxString reflector = m_reflector->GetStringSelection(); + + reflector.Append(wxT(" ")); + reflector.Truncate(LONG_CALLSIGN_LENGTH - 1U); + reflector.Append(m_channel->GetStringSelection()); + + return reflector; +} + +bool CRepeaterDataSet::atStartup() const +{ + if (isDDMode()) + return false; + + int n = m_startup->GetCurrentSelection(); + + return n == 1; +} + +RECONNECT CRepeaterDataSet::getReconnect() const +{ + if (isDDMode()) + return RECONNECT_NEVER; + + int n = m_reconnect->GetCurrentSelection(); + + return RECONNECT(n); +} + +void CRepeaterDataSet::onBand(wxCommandEvent &event) +{ + if (isDDMode()) { + m_reflector->SetSelection(0); + m_channel->SetSelection(0); + m_startup->SetSelection(0); + m_reconnect->SetSelection(0); + + m_reflector->Disable(); + m_channel->Disable(); + m_startup->Disable(); + m_reconnect->Disable(); + } else { + m_reflector->Enable(); + m_channel->Enable(); + m_startup->Enable(); + m_reconnect->Enable(); + } +} + +void CRepeaterDataSet::onType(wxCommandEvent &event) +{ + int n = m_type->GetCurrentSelection(); + if (n != 1) { + m_band1->Disable(); + m_band2->Disable(); + m_band3->Disable(); + } else { + m_band1->Enable(); + m_band2->Enable(); + m_band3->Enable(); + } +} + +bool CRepeaterDataSet::isDDMode() const +{ + int c = m_band->GetCurrentSelection(); + + switch (c) { + case 6: + case 7: + case 8: + case 9: + case 10: + return true; + default: + return false; + } +} diff --git a/GUICommon/RepeaterDataSet.h b/GUICommon/RepeaterDataSet.h new file mode 100644 index 0000000..5048eb9 --- /dev/null +++ b/GUICommon/RepeaterDataSet.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2010,2011,2012 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 RepeaterDataSet_H +#define RepeaterDataSet_H + +#include "AddressTextCtrl.h" +#include "PortTextCtrl.h" +#include "Defs.h" + +#include + +class CRepeaterDataSet : public wxPanel { +public: + CRepeaterDataSet(wxWindow* parent, int id, const wxString& title, const wxString& band, HW_TYPE type, const wxString& address, unsigned int port, unsigned char band1, unsigned char band2, unsigned char band3, bool dplusEnabled, bool dExtraEnabled, bool dcsEnabled, const wxString& reflector, bool atStartup, RECONNECT reconnect); + virtual ~CRepeaterDataSet(); + + virtual bool Validate(); + + virtual wxString getBand() const; + + virtual HW_TYPE getType() const; + virtual wxString getAddress() const; + virtual unsigned int getPort() const; + + virtual unsigned char getBand1() const; + virtual unsigned char getBand2() const; + virtual unsigned char getBand3() const; + + virtual wxString getReflector() const; + virtual bool atStartup() const; + virtual RECONNECT getReconnect() const; + + virtual void onBand(wxCommandEvent& event); + virtual void onType(wxCommandEvent& event); + +private: + wxString m_title; + wxChoice* m_band; + wxChoice* m_type; + CAddressTextCtrl* m_address; + CPortTextCtrl* m_port; + CPortTextCtrl* m_band1; + CPortTextCtrl* m_band2; + CPortTextCtrl* m_band3; + wxChoice* m_reflector; + wxChoice* m_channel; + wxChoice* m_startup; + wxChoice* m_reconnect; + + bool isDDMode() const; + + DECLARE_EVENT_TABLE() +}; + +#endif diff --git a/GUICommon/RepeaterInfoSet.cpp b/GUICommon/RepeaterInfoSet.cpp new file mode 100644 index 0000000..0feebbf --- /dev/null +++ b/GUICommon/RepeaterInfoSet.cpp @@ -0,0 +1,218 @@ +/* + * 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 + * 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 "RepeaterInfoSet.h" +#include "DStarDefines.h" + +const unsigned int CONTROL_WIDTH1 = 130U; +const unsigned int CONTROL_WIDTH2 = 80U; +const unsigned int CONTROL_WIDTH3 = 40U; + +const unsigned int DESCRIPTION_LENGTH = 20U; +const unsigned int FREQUENCY_LENGTH = 9U; +const unsigned int OFFSET_LENGTH = 6U; +const unsigned int PORT_LENGTH = 5U; +const unsigned int URL_LENGTH = 40U; + +const unsigned int BORDER_SIZE = 5U; + + +CRepeaterInfoSet::CRepeaterInfoSet(wxWindow* parent, int id, const wxString& title, double frequency, double offset, double range, double latitude, double longitude, double agl, const wxString& description1, const wxString& description2, const wxString& url) : +wxPanel(parent, id), +m_title(title), +m_frequency(NULL), +m_offset(NULL), +m_range(NULL), +m_latitude(NULL), +m_longitude(NULL), +m_agl(NULL), +m_description1(NULL), +m_description2(NULL), +m_url(NULL) +{ + wxFlexGridSizer* sizer = new wxFlexGridSizer(2); + + wxStaticText* frequencyLabel = new wxStaticText(this, -1, _("Frequency (MHz)")); + sizer->Add(frequencyLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxString buffer; + buffer.Printf(wxT("%.5lf"), frequency); + + m_frequency = new wxTextCtrl(this, -1, buffer, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_frequency->SetMaxLength(FREQUENCY_LENGTH); + sizer->Add(m_frequency, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* offsetLabel = new wxStaticText(this, -1, _("Offset (MHz)")); + sizer->Add(offsetLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + buffer.Printf(wxT("%.4lf"), offset); + + m_offset = new wxTextCtrl(this, -1, buffer, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_offset->SetMaxLength(OFFSET_LENGTH); + sizer->Add(m_offset, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* rangeLabel = new wxStaticText(this, -1, _("Range (kms)")); + sizer->Add(rangeLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + buffer.Printf(wxT("%.0lf"), range); + + m_range = new wxTextCtrl(this, -1, buffer, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1)); + m_range->SetMaxLength(PORT_LENGTH); + sizer->Add(m_range, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* latitudeLabel = new wxStaticText(this, -1, _("Latitude")); + sizer->Add(latitudeLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + buffer.Printf(wxT("%lf"), latitude); + + m_latitude = new wxTextCtrl(this, -1, buffer, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + sizer->Add(m_latitude, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* longitudeLabel = new wxStaticText(this, -1, _("Longitude")); + sizer->Add(longitudeLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + buffer.Printf(wxT("%lf"), longitude); + + m_longitude = new wxTextCtrl(this, -1, buffer, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + sizer->Add(m_longitude, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* aglLabel = new wxStaticText(this, -1, _("AGL (m)")); + sizer->Add(aglLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + buffer.Printf(wxT("%.0lf"), agl); + + m_agl = new wxTextCtrl(this, -1, buffer, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1)); + m_agl->SetMaxLength(PORT_LENGTH); + sizer->Add(m_agl, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* descriptionLabel = new wxStaticText(this, -1, _("QTH")); + sizer->Add(descriptionLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_description1 = new CDescriptionTextCtrl(this, -1, description1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_description1->SetMaxLength(DESCRIPTION_LENGTH); + sizer->Add(m_description1, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* dummyLabel = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummyLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_description2 = new CDescriptionTextCtrl(this, -1, description2, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_description2->SetMaxLength(DESCRIPTION_LENGTH); + sizer->Add(m_description2, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* urlLabel = new wxStaticText(this, -1, _("URL")); + sizer->Add(urlLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_url = new CDescriptionTextCtrl(this, -1, url, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_url->SetMaxLength(URL_LENGTH); + sizer->Add(m_url, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + SetAutoLayout(true); + + SetSizer(sizer); +} + + +CRepeaterInfoSet::~CRepeaterInfoSet() +{ +} + +bool CRepeaterInfoSet::Validate() +{ + double latitude = getLatitude(); + + if (latitude < -90.0 || latitude > 90.0) { + wxMessageDialog dialog(this, _("The Latitude is invalid"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + double longitude = getLongitude(); + + if (longitude < -180.0 || longitude > 180.0) { + wxMessageDialog dialog(this, _("The Longitude is invalid"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + return true; +} + +double CRepeaterInfoSet::getFrequency() const +{ + double n; + m_frequency->GetValue().ToDouble(&n); + + return n; +} + +double CRepeaterInfoSet::getOffset() const +{ + double n; + m_offset->GetValue().ToDouble(&n); + + return n; +} + +double CRepeaterInfoSet::getRange() const +{ + double n; + m_range->GetValue().ToDouble(&n); + + return n; +} + +double CRepeaterInfoSet::getLatitude() const +{ + double val; + + m_latitude->GetValue().ToDouble(&val); + + return val; +} + +double CRepeaterInfoSet::getLongitude() const +{ + double val; + + m_longitude->GetValue().ToDouble(&val); + + return val; +} + +wxString CRepeaterInfoSet::getDescription1() const +{ + return m_description1->GetValue(); +} + +wxString CRepeaterInfoSet::getDescription2() const +{ + return m_description2->GetValue(); +} + +wxString CRepeaterInfoSet::getURL() const +{ + return m_url->GetValue(); +} + +double CRepeaterInfoSet::getAGL() const +{ + double n; + m_agl->GetValue().ToDouble(&n); + + return n; +} diff --git a/GUICommon/RepeaterInfoSet.h b/GUICommon/RepeaterInfoSet.h new file mode 100644 index 0000000..c9a9568 --- /dev/null +++ b/GUICommon/RepeaterInfoSet.h @@ -0,0 +1,58 @@ +/* + * 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 + * 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 RepeaterInfoSet_H +#define RepeaterInfoSet_H + +#include "DescriptionTextCtrl.h" + +#include + +class CRepeaterInfoSet : public wxPanel { +public: + CRepeaterInfoSet(wxWindow* parent, int id, const wxString& title, double frequency, double offset, double range, double latitude, double longitude, double agl, const wxString& description1, const wxString& description2, const wxString& url); + virtual ~CRepeaterInfoSet(); + + virtual bool Validate(); + + virtual double getFrequency() const; + virtual double getOffset() const; + virtual double getRange() const; + + virtual double getLatitude() const; + virtual double getLongitude() const; + virtual double getAGL() const; + + virtual wxString getDescription1() const; + virtual wxString getDescription2() const; + virtual wxString getURL() const; + +private: + wxString m_title; + wxTextCtrl* m_frequency; + wxTextCtrl* m_offset; + wxTextCtrl* m_range; + wxTextCtrl* m_latitude; + wxTextCtrl* m_longitude; + wxTextCtrl* m_agl; + CDescriptionTextCtrl* m_description1; + CDescriptionTextCtrl* m_description2; + CDescriptionTextCtrl* m_url; +}; + +#endif diff --git a/GUICommon/RestrictedTextCtrl.cpp b/GUICommon/RestrictedTextCtrl.cpp new file mode 100644 index 0000000..a291044 --- /dev/null +++ b/GUICommon/RestrictedTextCtrl.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2002,2003,2009 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 "RestrictedTextCtrl.h" + +CRestrictedTextCtrl::CRestrictedTextCtrl(wxWindow* parent, int id, const wxString& value, const wxPoint& pos, const wxSize& size, long style, const wxString& wantedChars) : +wxTextCtrl() +{ + wxASSERT(parent != NULL); + + wxArrayString charList; + + for (unsigned int i = 0; i < wantedChars.Length(); i++) + charList.Add(wantedChars.Mid(i, 1)); + + wxTextValidator validator(wxFILTER_INCLUDE_CHAR_LIST); + validator.SetIncludes(charList); + + Create(parent, id, value, pos, size, style, validator); +} + +CRestrictedTextCtrl::~CRestrictedTextCtrl() +{ +} + diff --git a/GUICommon/RestrictedTextCtrl.h b/GUICommon/RestrictedTextCtrl.h new file mode 100644 index 0000000..f3d57b6 --- /dev/null +++ b/GUICommon/RestrictedTextCtrl.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2002,2003 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 RestrictedTextCtrl_H +#define RestrictedTextCtrl_H + +#include + +class CRestrictedTextCtrl : public wxTextCtrl { +public: + CRestrictedTextCtrl(wxWindow* parent, int id, const wxString& value, const wxPoint& pos, const wxSize& size, long style, const wxString& wantedChars); + virtual ~CRestrictedTextCtrl(); + +private: +}; + +#endif diff --git a/GUICommon/StarNetSet.cpp b/GUICommon/StarNetSet.cpp new file mode 100644 index 0000000..b45fb12 --- /dev/null +++ b/GUICommon/StarNetSet.cpp @@ -0,0 +1,509 @@ +/* + * Copyright (C) 2011,2012,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 "DStarDefines.h" +#include "StarNetSet.h" +#include "HostFile.h" // DEXTRA_LINK || DCS_LINK +#include "Defs.h" // DEXTRA_LINK || DCS_LINK + +#include // DEXTRA_LINK || DCS_LINK + +const unsigned int BAND_WIDTH = 60U; +const unsigned int CALLSIGN_WIDTH = 120U; +const unsigned int INFO_WIDTH = 150U; +const unsigned int TIMEOUT_WIDTH = 150U; + +const unsigned int CONTROL_WIDTH1 = 150U; // DEXTRA_LINK +const unsigned int CONTROL_WIDTH2 = 60U; // DEXTRA_LINK + +const unsigned int INFO_LENGTH = 20U; + +const unsigned int BORDER_SIZE = 5U; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +CStarNetSet::CStarNetSet(wxWindow* parent, int id, const wxString& title, const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) : +#else +CStarNetSet::CStarNetSet(wxWindow* parent, int id, const wxString& title, const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) : +#endif +wxPanel(parent, id), +m_title(title), +m_band(NULL), +m_callsign(NULL), +m_logoff(NULL), +m_info(NULL), +m_permanent(NULL), +m_userTimeout(NULL), +m_groupTimeout(NULL), +m_callsignSwitch(NULL), +m_txMsgSwitch(NULL) +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +,m_reflector(NULL) +#endif +{ + wxFlexGridSizer* sizer = new wxFlexGridSizer(3); + + wxStaticText* bandLabel = new wxStaticText(this, -1, _("Band")); + sizer->Add(bandLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_band = new wxChoice(this, -1, wxDefaultPosition, wxSize(BAND_WIDTH, -1)); + m_band->Append(wxT("A")); + m_band->Append(wxT("B")); + m_band->Append(wxT("C")); + m_band->Append(wxT("D")); + sizer->Add(m_band, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + bool res = m_band->SetStringSelection(band); + if (!res) + m_band->SetSelection(0); + + wxStaticText* dummy1Label = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummy1Label, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxStaticText* callsignLabel = new wxStaticText(this, -1, _("Group Call")); + sizer->Add(callsignLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxString call = callsign; + call.Truncate(LONG_CALLSIGN_LENGTH); + + m_callsign = new CCallsignTextCtrl(this, -1, call, wxDefaultPosition, wxSize(CALLSIGN_WIDTH, -1)); + m_callsign->SetMaxLength(LONG_CALLSIGN_LENGTH); + sizer->Add(m_callsign, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* dummy2Label = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummy2Label, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxStaticText* logoffLabel = new wxStaticText(this, -1, _("Logoff Call")); + sizer->Add(logoffLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + call = logoff; + call.Truncate(LONG_CALLSIGN_LENGTH); + + m_logoff = new CCallsignTextCtrl(this, -1, call, wxDefaultPosition, wxSize(CALLSIGN_WIDTH, -1)); + m_logoff->SetMaxLength(LONG_CALLSIGN_LENGTH); + sizer->Add(m_logoff, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* dummy3Label = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummy3Label, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxStaticText* infoLabel = new wxStaticText(this, -1, _("Information")); + sizer->Add(infoLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_info = new wxTextCtrl(this, -1, info, wxDefaultPosition, wxSize(INFO_WIDTH, -1)); + m_info->SetMaxLength(INFO_LENGTH); + sizer->Add(m_info, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* dummy4Label = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummy4Label, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxStaticText* permanentLabel = new wxStaticText(this, -1, _("Permanent Calls")); + sizer->Add(permanentLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + call = permanent; + call.Truncate(LONG_CALLSIGN_LENGTH); + + m_permanent = new CCallsignTextCtrl(this, -1, call, wxDefaultPosition, wxSize(INFO_WIDTH, -1)); + sizer->Add(m_permanent, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* dummy4ALabel = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummy4ALabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxStaticText* userTimeoutLabel = new wxStaticText(this, -1, _("User Timeout")); + sizer->Add(userTimeoutLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_userTimeout = new wxChoice(this, -1, wxDefaultPosition, wxSize(TIMEOUT_WIDTH, -1)); + m_userTimeout->Append(_("Never")); + m_userTimeout->Append(_("30 mins")); + m_userTimeout->Append(_("60 mins")); + m_userTimeout->Append(_("120 mins")); + m_userTimeout->Append(_("180 mins")); + m_userTimeout->Append(_("240 mins")); + m_userTimeout->Append(_("300 mins")); + sizer->Add(m_userTimeout, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + switch (userTimeout) { + case 0U: m_userTimeout->SetSelection(0); break; + case 30U: m_userTimeout->SetSelection(1); break; + case 60U: m_userTimeout->SetSelection(2); break; + case 120U: m_userTimeout->SetSelection(3); break; + case 180U: m_userTimeout->SetSelection(4); break; + case 240U: m_userTimeout->SetSelection(5); break; + default: m_userTimeout->SetSelection(6); break; + } + + wxStaticText* dummy5Label = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummy5Label, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxStaticText* groupTimeoutLabel = new wxStaticText(this, -1, _("Group Timeout")); + sizer->Add(groupTimeoutLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_groupTimeout = new wxChoice(this, -1, wxDefaultPosition, wxSize(TIMEOUT_WIDTH, -1)); + m_groupTimeout->Append(_("Never")); + m_groupTimeout->Append(_("30 mins")); + m_groupTimeout->Append(_("60 mins")); + m_groupTimeout->Append(_("120 mins")); + m_groupTimeout->Append(_("180 mins")); + m_groupTimeout->Append(_("240 mins")); + m_groupTimeout->Append(_("300 mins")); + sizer->Add(m_groupTimeout, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + switch (groupTimeout) { + case 0U: m_groupTimeout->SetSelection(0); break; + case 30U: m_groupTimeout->SetSelection(1); break; + case 60U: m_groupTimeout->SetSelection(2); break; + case 120U: m_groupTimeout->SetSelection(3); break; + case 180U: m_groupTimeout->SetSelection(4); break; + case 240U: m_groupTimeout->SetSelection(5); break; + default: m_groupTimeout->SetSelection(6); break; + } + + wxStaticText* dummy6Label = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummy6Label, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxStaticText* callsignSwitchLabel = new wxStaticText(this, -1, _("MYCALL Setting")); + sizer->Add(callsignSwitchLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_callsignSwitch = new wxChoice(this, -1, wxDefaultPosition, wxSize(CALLSIGN_WIDTH, -1)); + m_callsignSwitch->Append(_("Group")); + m_callsignSwitch->Append(_("User")); + sizer->Add(m_callsignSwitch, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + switch (callsignSwitch) { + case SCS_GROUP_CALLSIGN: m_callsignSwitch->SetSelection(0); break; + case SCS_USER_CALLSIGN: m_callsignSwitch->SetSelection(1); break; + default: m_callsignSwitch->SetSelection(0); break; + } + + wxStaticText* dummy7Label = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummy7Label, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxStaticText* txMsgSwitchLabel = new wxStaticText(this, -1, _("TX Message")); + sizer->Add(txMsgSwitchLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_txMsgSwitch = new wxChoice(this, -1, wxDefaultPosition, wxSize(CALLSIGN_WIDTH, -1)); + m_txMsgSwitch->Append(_("Off")); + m_txMsgSwitch->Append(_("On")); + sizer->Add(m_txMsgSwitch, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_txMsgSwitch->SetSelection(txMsgSwitch ? 1 : 0); + + wxStaticText* dummy8Label = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummy8Label, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + +#if defined(DEXTRA_LINK) + wxStaticText* reflectorLabel = new wxStaticText(this, -1, _("Reflector")); + sizer->Add(reflectorLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_reflector = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_reflector->Append(_("None")); + + wxFileName fileName(wxFileName::GetHomeDir(), DEXTRA_HOSTS_FILE_NAME); + if (!fileName.IsFileReadable()) { + wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str()); +#if defined(__WINDOWS__) + fileName.Assign(::wxGetCwd(), DEXTRA_HOSTS_FILE_NAME); +#else + fileName.Assign(wxT(DATA_DIR), DEXTRA_HOSTS_FILE_NAME); +#endif + if (!fileName.IsFileReadable()) + wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str()); + } + + CHostFile file(fileName.GetFullPath(), false); + + for (unsigned int i = 0U; i < file.getCount(); i++) + m_reflector->Append(file.getName(i)); + + sizer->Add(m_reflector, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + if (reflector.IsEmpty()) { + m_reflector->SetSelection(0); + } else { + wxString name = reflector; + name.SetChar(LONG_CALLSIGN_LENGTH - 1U, wxT(' ')); + bool res = m_reflector->SetStringSelection(name); + if (!res) + m_reflector->SetSelection(0); + } + + m_channel = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1)); + m_channel->Append(wxT("A")); + m_channel->Append(wxT("B")); + m_channel->Append(wxT("C")); + m_channel->Append(wxT("D")); + m_channel->Append(wxT("E")); + sizer->Add(m_channel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + res = m_channel->SetStringSelection(reflector.Right(1U)); + if (!res) + m_channel->SetSelection(0); +#endif + +#if defined(DCS_LINK) + wxStaticText* reflectorLabel = new wxStaticText(this, -1, _("Reflector")); + sizer->Add(reflectorLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_reflector = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_reflector->Append(_("None")); + + wxFileName fileName(wxFileName::GetHomeDir(), DCS_HOSTS_FILE_NAME); + if (!fileName.IsFileReadable()) { + wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str()); +#if defined(__WINDOWS__) + fileName.Assign(::wxGetCwd(), DCS_HOSTS_FILE_NAME); +#else + fileName.Assign(wxT(DATA_DIR), DCS_HOSTS_FILE_NAME); +#endif + if (!fileName.IsFileReadable()) + wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str()); + } + + CHostFile file(fileName.GetFullPath(), false); + + for (unsigned int i = 0U; i < file.getCount(); i++) + m_reflector->Append(file.getName(i)); + + sizer->Add(m_reflector, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + if (reflector.IsEmpty()) { + m_reflector->SetSelection(0); + } else { + wxString name = reflector; + name.SetChar(LONG_CALLSIGN_LENGTH - 1U, wxT(' ')); + bool res = m_reflector->SetStringSelection(name); + if (!res) + m_reflector->SetSelection(0); + } + + m_channel = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1)); + m_channel->Append(wxT("A")); + m_channel->Append(wxT("B")); + m_channel->Append(wxT("C")); + m_channel->Append(wxT("D")); + m_channel->Append(wxT("E")); + m_channel->Append(wxT("F")); + m_channel->Append(wxT("G")); + m_channel->Append(wxT("H")); + m_channel->Append(wxT("I")); + m_channel->Append(wxT("J")); + m_channel->Append(wxT("K")); + m_channel->Append(wxT("L")); + m_channel->Append(wxT("M")); + m_channel->Append(wxT("N")); + m_channel->Append(wxT("O")); + m_channel->Append(wxT("P")); + m_channel->Append(wxT("Q")); + m_channel->Append(wxT("R")); + m_channel->Append(wxT("S")); + m_channel->Append(wxT("T")); + m_channel->Append(wxT("U")); + m_channel->Append(wxT("V")); + m_channel->Append(wxT("W")); + m_channel->Append(wxT("X")); + m_channel->Append(wxT("Y")); + m_channel->Append(wxT("Z")); + sizer->Add(m_channel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + res = m_channel->SetStringSelection(reflector.Right(1U)); + if (!res) + m_channel->SetSelection(0); +#endif + + SetAutoLayout(true); + + SetSizer(sizer); +} + + +CStarNetSet::~CStarNetSet() +{ +} + +bool CStarNetSet::Validate() +{ + int n = m_band->GetCurrentSelection(); + if (n == wxNOT_FOUND) { + wxMessageDialog dialog(this, _("The StarNet Band is not set"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + n = m_userTimeout->GetCurrentSelection(); + if (n == wxNOT_FOUND) { + wxMessageDialog dialog(this, _("The StarNet user timeout is not set"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + n = m_groupTimeout->GetCurrentSelection(); + if (n == wxNOT_FOUND) { + wxMessageDialog dialog(this, _("The StarNet group timeout is not set"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + n = m_callsignSwitch->GetCurrentSelection(); + if (n == wxNOT_FOUND) { + wxMessageDialog dialog(this, _("The MYCALL Setting is not set"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + n = m_txMsgSwitch->GetCurrentSelection(); + if (n == wxNOT_FOUND) { + wxMessageDialog dialog(this, _("The TX Message switch is not set"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + n = m_reflector->GetCurrentSelection(); + if (n == wxNOT_FOUND) { + wxMessageDialog dialog(this, _("The StarNet reflector link is not set"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } +#endif + + return true; +} + +wxString CStarNetSet::getBand() const +{ + return m_band->GetStringSelection(); +} + +wxString CStarNetSet::getCallsign() const +{ + wxString callsign = m_callsign->GetValue(); + + callsign.MakeUpper(); + callsign.Append(wxT(" ")); + callsign.Truncate(LONG_CALLSIGN_LENGTH); + + return callsign; +} + +wxString CStarNetSet::getLogoff() const +{ + wxString callsign = m_logoff->GetValue(); + + callsign.MakeUpper(); + callsign.Append(wxT(" ")); + callsign.Truncate(LONG_CALLSIGN_LENGTH); + + return callsign; +} + +wxString CStarNetSet::getInfo() const +{ + return m_info->GetValue(); +} + +wxString CStarNetSet::getPermanent() const +{ + wxString callsign = m_permanent->GetValue(); + + callsign.MakeUpper(); + callsign.Append(wxT(" ")); + callsign.Truncate(LONG_CALLSIGN_LENGTH); + + return callsign; +} + +unsigned int CStarNetSet::getUserTimeout() const +{ + int n = m_userTimeout->GetCurrentSelection(); + switch (n) { + case 0: + return 0U; + case 1: + return 30U; + case 2: + return 60U; + case 3: + return 120U; + case 4: + return 180U; + case 5: + return 240U; + default: + return 300U; + } +} + +unsigned int CStarNetSet::getGroupTimeout() const +{ + int n = m_groupTimeout->GetCurrentSelection(); + switch (n) { + case 0: + return 0U; + case 1: + return 30U; + case 2: + return 60U; + case 3: + return 120U; + case 4: + return 180U; + case 5: + return 240U; + default: + return 300U; + } +} + +STARNET_CALLSIGN_SWITCH CStarNetSet::getCallsignSwitch() const +{ + int n = m_callsignSwitch->GetCurrentSelection(); + switch (n) { + case 0: + return SCS_GROUP_CALLSIGN; + case 1: + return SCS_USER_CALLSIGN; + default: + return SCS_GROUP_CALLSIGN; + } +} + +bool CStarNetSet::getTXMsgSwitch() const +{ + int n = m_txMsgSwitch->GetCurrentSelection(); + switch (n) { + case 0: + return false; + case 1: + return true; + default: + return false; + } +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +wxString CStarNetSet::getReflector() const +{ + int n = m_reflector->GetCurrentSelection(); + int c = m_channel->GetCurrentSelection(); + + if (n == 0) + return wxEmptyString; + + wxString reflector = m_reflector->GetStringSelection(); + + reflector.Append(wxT(" ")); + reflector.Truncate(LONG_CALLSIGN_LENGTH - 1U); + reflector.Append((char)('A' + c)); + + return reflector; +} +#endif diff --git a/GUICommon/StarNetSet.h b/GUICommon/StarNetSet.h new file mode 100644 index 0000000..7ccf9f0 --- /dev/null +++ b/GUICommon/StarNetSet.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2011,2012 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 StarNetSet_H +#define StarNetSet_H + +#include "CallsignTextCtrl.h" +#include "Defs.h" + +#include + +class CStarNetSet : public wxPanel { +public: +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + CStarNetSet(wxWindow* parent, int id, const wxString& title, const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); +#else + CStarNetSet(wxWindow* parent, int id, const wxString& title, const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); +#endif + virtual ~CStarNetSet(); + + virtual bool Validate(); + + virtual wxString getBand() const; + virtual wxString getCallsign() const; + virtual wxString getLogoff() const; + virtual wxString getInfo() const; + virtual wxString getPermanent() const; + virtual unsigned int getGroupTimeout() const; + virtual unsigned int getUserTimeout() const; + virtual STARNET_CALLSIGN_SWITCH getCallsignSwitch() const; + virtual bool getTXMsgSwitch() const; +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + virtual wxString getReflector() const; +#endif + +private: + wxString m_title; + wxChoice* m_band; + CCallsignTextCtrl* m_callsign; + CCallsignTextCtrl* m_logoff; + wxTextCtrl* m_info; + CCallsignTextCtrl* m_permanent; + wxChoice* m_userTimeout; + wxChoice* m_groupTimeout; + wxChoice* m_callsignSwitch; + wxChoice* m_txMsgSwitch; +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + wxChoice* m_reflector; + wxChoice* m_channel; +#endif +}; + +#endif diff --git a/GUICommon/XLXSet.cpp b/GUICommon/XLXSet.cpp new file mode 100644 index 0000000..616e6c0 --- /dev/null +++ b/GUICommon/XLXSet.cpp @@ -0,0 +1,143 @@ +/* + * 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 + * 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 "HostFile.h" +#include "XLXSet.h" +#include "Defs.h" + +// TODO F4FXL try to figure out why below symbols are not found under ubuntu +//#include + +const unsigned int CONTROL_WIDTH = 130U; + +const unsigned int BORDER_SIZE = 5U; + +const int CHOICE_ENABLED = 8788; + +BEGIN_EVENT_TABLE(CXLXSet, wxPanel) + EVT_CHOICE(CHOICE_ENABLED, CXLXSet::onEnabled) +END_EVENT_TABLE() + + +CXLXSet::CXLXSet(wxWindow* parent, int id, const wxString& title, bool xlxEnabled, bool xlxOverrideLocal, const wxString& xlxHostsFileUrl) : +wxPanel(parent, id), +m_title(title), +m_xlxEnabled(NULL), +m_xlxOverrideLocal(NULL), +m_xlxHostsFileUrl(NULL) +{ + wxFlexGridSizer* sizer = new wxFlexGridSizer(2); + + wxStaticText* xlxEnabledLabel = new wxStaticText(this, -1, wxT("XLX")); + sizer->Add(xlxEnabledLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_xlxEnabled = new wxChoice(this, CHOICE_ENABLED, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + m_xlxEnabled->Append(_("Disabled")); + m_xlxEnabled->Append(_("Enabled")); + sizer->Add(m_xlxEnabled, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_xlxEnabled->SetSelection(xlxEnabled ? 1 : 0); + + wxStaticText* xlxOverrideLocalLabel = new wxStaticText(this, -1, _("Override local hosts files")); + sizer->Add(xlxOverrideLocalLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_xlxOverrideLocal = new wxChoice(this, CHOICE_ENABLED, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + m_xlxOverrideLocal->Append(_("No")); + m_xlxOverrideLocal->Append(_("Yes")); + sizer->Add(m_xlxOverrideLocal, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_xlxOverrideLocal->SetSelection(xlxOverrideLocal ? 1 : 0); + + wxStaticText* xlxHostsFileUrlLabel = new wxStaticText(this, -1, _("Hosts file URL")); + sizer->Add(xlxHostsFileUrlLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_xlxHostsFileUrl = new wxTextCtrl(this, -1, xlxHostsFileUrl, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + sizer->Add(m_xlxHostsFileUrl, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + + if (xlxEnabled) + m_xlxHostsFileUrl->Enable(); + else + m_xlxHostsFileUrl->Disable(); + + SetAutoLayout(true); + + SetSizer(sizer); +} + + +CXLXSet::~CXLXSet() +{ +} + +bool CXLXSet::Validate() +{ + int n = m_xlxEnabled->GetCurrentSelection(); + if (n == wxNOT_FOUND) + return false; + + n = m_xlxOverrideLocal->GetCurrentSelection(); + if (n == wxNOT_FOUND) + return false; + + // TODO F4FXL try to figure out why below symbols are not found under ubuntu + /*wxString value = m_xlxHostsFileUrl->GetValue(); + wxURL url(value); + if (url.GetError() != wxURL_NOERR) + return false;*/ + + return true; +} + +bool CXLXSet::getXLXOverrideLocal() const +{ + int c = m_xlxEnabled->GetCurrentSelection(); + if (c == wxNOT_FOUND) + return false; + + return c == 1; +} + +bool CXLXSet::getXLXEnabled() const +{ + int c = m_xlxEnabled->GetCurrentSelection(); + if (c == wxNOT_FOUND) + return false; + + return c == 1; +} + +wxString CXLXSet::getXLXHostsFileUrl() const +{ + wxString value = m_xlxHostsFileUrl->GetValue(); + + + // TODO F4FXL try to figure out why below symbols are not found under ubuntu + //wxURL url(value); + //if (url.GetError() == wxURL_NOERR) + // return value; + + return wxEmptyString; +} + +void CXLXSet::onEnabled(wxCommandEvent &event) +{ + int n = m_xlxEnabled->GetCurrentSelection(); + if (n != 1) + m_xlxHostsFileUrl->Disable(); + else + m_xlxHostsFileUrl->Enable(); +} diff --git a/GUICommon/XLXSet.h b/GUICommon/XLXSet.h new file mode 100644 index 0000000..f765b55 --- /dev/null +++ b/GUICommon/XLXSet.h @@ -0,0 +1,46 @@ +/* + * 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 + * 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 XLXSet_H +#define XLXSet_H + +#include + +class CXLXSet : public wxPanel { +public: + CXLXSet(wxWindow* parent, int id, const wxString& title, bool xlxEnabled, bool xlxOverrideLocal, const wxString& xlxHostsFileUrl); + virtual ~CXLXSet(); + + virtual bool Validate(); + + virtual bool getXLXEnabled() const; + virtual bool getXLXOverrideLocal() const; + virtual wxString getXLXHostsFileUrl() const; + + virtual void onEnabled(wxCommandEvent& event); + +private: + wxString m_title; + wxChoice* m_xlxEnabled; + wxChoice* m_xlxOverrideLocal; + wxTextCtrl* m_xlxHostsFileUrl; + + DECLARE_EVENT_TABLE() +}; + +#endif diff --git a/README.md b/README.md new file mode 100644 index 0000000..d099084 --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +This is the ircDDB Gateway. It allows a D-Star Repeater to interface into callsign routing via ircDDB and all of the different reflector types. It includes many facilities, including: + +* Supports Icom stacks. +* Supports homebrew repeaters. +* Icom DD mode under Linux with Internet access. +* Callsign routing via ircDDB. +* D-Plus REF reflectors. +* DExtra XRF reflectors. +* DCS reflectors. +* XLX reflectors. +* CCS7 routing. +* D-RATS data transfers. +* Gateway DPRS data to aprs.fi. +* Full multi lingual text and voice announcements. +* DTMF or UR call control. +* Remote control interface. +* StarNet server. +* Ability to set policies for reflector usage. + +There are many external programs that allow for inserting voice or text messages, as well as remote control operation. + +They all build on 32-bit and 64-bit Linux as well as on Windows using Visual Studio 2017 on x86 and x64. + +This software is licenced under the GPL v2. diff --git a/RemoteControl/RemoteControl.vcxproj b/RemoteControl/RemoteControl.vcxproj new file mode 100644 index 0000000..ba4020a --- /dev/null +++ b/RemoteControl/RemoteControl.vcxproj @@ -0,0 +1,214 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {F7756875-1F58-4006-AD55-5C963AB682C0} + RemoteControl + Win32Proj + 10.0.16299.0 + + + + Application + v141 + Unicode + true + + + Application + v141 + Unicode + true + + + Application + v141 + Unicode + + + Application + v141 + Unicode + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>14.0.24720.0 + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + $(WXWIN)\include;$(WXWIN)\lib\vc_dll\mswud;$(IncludePath) + + + true + $(WXWIN)\include;$(WXWIN)\lib\vc_x64_dll\mswud;$(IncludePath) + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + + + false + + + + Disabled + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;../GUICommon;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + Level4 + EditAndContinue + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_dll;%(AdditionalLibraryDirectories) + true + Windows + MachineX86 + + + + + Disabled + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;../GUICommon;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + + + Level4 + ProgramDatabase + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_x64_dll;%(AdditionalLibraryDirectories) + true + Windows + + + + + MaxSpeed + true + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;../GUICommon;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + Level3 + ProgramDatabase + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_dll;%(AdditionalLibraryDirectories) + true + Windows + true + true + MachineX86 + + + + + MaxSpeed + true + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;../GUICommon;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_x64_dll;%(AdditionalLibraryDirectories) + true + Windows + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {e793cb8e-2ac9-431a-bbfc-3f52537bb3cf} + false + + + {02d03515-0bbe-4553-8c40-566a597478f8} + false + + + + + + \ No newline at end of file diff --git a/RemoteControl/RemoteControl.vcxproj.filters b/RemoteControl/RemoteControl.vcxproj.filters new file mode 100644 index 0000000..af844a0 --- /dev/null +++ b/RemoteControl/RemoteControl.vcxproj.filters @@ -0,0 +1,98 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/RemoteControl/RemoteControlApp.cpp b/RemoteControl/RemoteControlApp.cpp new file mode 100644 index 0000000..ec215a3 --- /dev/null +++ b/RemoteControl/RemoteControlApp.cpp @@ -0,0 +1,141 @@ +/* + * 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 + * 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 "RemoteControlDefs.h" +#include "RemoteControlApp.h" +#include "Version.h" + +#include +#include + +IMPLEMENT_APP(CRemoteControlApp) + +const wxChar* NAME_PARAM = wxT("Name"); + +CRemoteControlApp::CRemoteControlApp() : +wxApp(), +m_name(), +m_frame(NULL), +m_config(NULL) +{ +} + +CRemoteControlApp::~CRemoteControlApp() +{ +} + +bool CRemoteControlApp::OnInit() +{ + SetVendorName(VENDOR_NAME); + + if (!wxApp::OnInit()) + return false; + + m_config = new CRemoteControlConfig(new wxConfig(APPLICATION_NAME), m_name); + + wxString frameName = APPLICATION_NAME + wxT(" - "); + if (!m_name.IsEmpty()) { + frameName.Append(m_name); + frameName.Append(wxT(" - ")); + } + frameName.Append(VERSION); + + wxPoint position = wxDefaultPosition; + + int x, y; + getPosition(x, y); + if (x >= 0 && y >= 0) + position = wxPoint(x, y); + + m_frame = new CRemoteControlFrame(frameName, position); + m_frame->Show(); + + SetTopWindow(m_frame); + + return wxApp::OnInit(); +} + +int CRemoteControlApp::OnExit() +{ + delete m_config; + + return 0; +} + +void CRemoteControlApp::OnInitCmdLine(wxCmdLineParser& parser) +{ + parser.AddParam(NAME_PARAM, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + + wxApp::OnInitCmdLine(parser); +} + +bool CRemoteControlApp::OnCmdLineParsed(wxCmdLineParser& parser) +{ + if (!wxApp::OnCmdLineParsed(parser)) + return false; + + if (parser.GetParamCount() > 0U) + m_name = parser.GetParam(0U); + + return true; +} + +void CRemoteControlApp::getConfig(wxString& address, unsigned int& port, wxString& password) const +{ + m_config->getConfig(address, port, password); +} + +void CRemoteControlApp::setConfig(const wxString& address, unsigned int port, const wxString& password) const +{ + m_config->setConfig(address, port, password); +} + +void CRemoteControlApp::getPosition(int& x, int& y) const +{ + m_config->getPosition(x, y); +} + +void CRemoteControlApp::setPosition(int x, int y) +{ + m_config->setPosition(x, y); +} + +void CRemoteControlApp::repeaterRefresh(const wxString& callsign) +{ + m_frame->repeaterRefresh(callsign); +} + +void CRemoteControlApp::starNetRefresh(const wxString& callsign) +{ + m_frame->starNetRefresh(callsign); +} + +void CRemoteControlApp::link(const wxString& callsign, RECONNECT reconnect, const wxString& reflector) +{ + m_frame->link(callsign, reconnect, reflector); +} + +void CRemoteControlApp::unlink(const wxString& callsign, PROTOCOL protocol, const wxString& reflector) +{ + m_frame->unlink(callsign, protocol, reflector); +} + +void CRemoteControlApp::starNetLogoff(const wxString& callsign, const wxString& user) +{ + m_frame->starNetLogoff(callsign, user); +} diff --git a/RemoteControl/RemoteControlApp.h b/RemoteControl/RemoteControlApp.h new file mode 100644 index 0000000..6fde68e --- /dev/null +++ b/RemoteControl/RemoteControlApp.h @@ -0,0 +1,59 @@ +/* + * 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 + * 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 RemoteControlApp_H +#define RemoteControlApp_H + +#include "RemoteControlConfig.h" +#include "RemoteControlFrame.h" + +#include + +class CRemoteControlApp : public wxApp { + +public: + CRemoteControlApp(); + virtual ~CRemoteControlApp(); + + virtual bool OnInit(); + virtual int OnExit(); + + virtual void OnInitCmdLine(wxCmdLineParser& parser); + virtual bool OnCmdLineParsed(wxCmdLineParser& parser); + + virtual void getConfig(wxString& address, unsigned int& port, wxString& password) const; + virtual void setConfig(const wxString& address, unsigned int port, const wxString& password) const; + + virtual void getPosition(int& x, int& y) const; + virtual void setPosition(int x, int y); + + virtual void repeaterRefresh(const wxString& callsign); + virtual void starNetRefresh(const wxString& callsign); + virtual void link(const wxString& callsign, RECONNECT reconnect, const wxString& reflector); + virtual void unlink(const wxString& callsign, PROTOCOL protocol, const wxString& reflector); + virtual void starNetLogoff(const wxString& callsign, const wxString& user); + +private: + wxString m_name; + CRemoteControlFrame* m_frame; + CRemoteControlConfig* m_config; +}; + +DECLARE_APP(CRemoteControlApp) + +#endif diff --git a/RemoteControl/RemoteControlAppD.cpp b/RemoteControl/RemoteControlAppD.cpp new file mode 100644 index 0000000..2deea20 --- /dev/null +++ b/RemoteControl/RemoteControlAppD.cpp @@ -0,0 +1,287 @@ +/* + * 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 "RemoteControlRemoteControlHandler.h" +#include "RemoteControlConfig.h" +#include "RemoteControlDefs.h" +#include "DStarDefines.h" +#include "SHA256.h" +#include "Defs.h" + +#include + +const wxChar* NAME_OPTION = wxT("name"); +const wxChar* REPEATER_PARAM = wxT("Callsign"); +const wxChar* ACTION_PARAM = wxT("Action"); +const wxChar* RECONNECT_PARAM = wxT("Param1"); +const wxChar* REFLECTOR_PARAM = wxT("Param2"); + +void sendHash(CRemoteControlRemoteControlHandler* handler, const wxString& password, unsigned int rnd) +{ + wxASSERT(handler != NULL); + + unsigned int len = password.Len() + sizeof(unsigned int); + unsigned char* in = new unsigned char[len]; + unsigned char* out = new unsigned char[32U]; + + ::memcpy(in, &rnd, sizeof(unsigned int)); + for (unsigned int i = 0U; i < password.Len(); i++) + in[i + sizeof(unsigned int)] = password.GetChar(i); + + CSHA256 sha256; + sha256.buffer(in, len, out); + + handler->sendHash(out, 32U); + + delete[] in; + delete[] out; +} + +int main(int argc, char** argv) +{ + bool res = ::wxInitialize(); + if (!res) { + ::fprintf(stderr, "remotecontrold: failed to initialise the wxWidgets library, exiting\n"); + return -1; + } + + wxCmdLineParser parser(argc, argv); + parser.AddOption(NAME_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddParam(REPEATER_PARAM, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddParam(ACTION_PARAM, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddParam(RECONNECT_PARAM, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddParam(REFLECTOR_PARAM, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + + int cmd = parser.Parse(); + if (cmd != 0) { + ::wxUninitialize(); + return 0; + } + + wxString name; + parser.Found(NAME_OPTION, &name); + + if (parser.GetParamCount() < 2U) { + ::fprintf(stderr, "remotecontrold: invalid command line usage: remotecontrold [-name ] link \n"); + ::fprintf(stderr, " remotecontrold [-name ] unlink\n"); + ::fprintf(stderr, " remotecontrold [-name ] drop \n"); + ::fprintf(stderr, " remotecontrold [-name ] drop all\n"); + ::wxUninitialize(); + return 1; + } + + wxString repeater = parser.GetParam(0U); + repeater.Replace(wxT("_"), wxT(" ")); + repeater.resize(LONG_CALLSIGN_LENGTH, wxT(' ')); + repeater.MakeUpper(); + + wxString actionText = parser.GetParam(1U); + + wxString user; // For STARnet Digital + wxString reflector; // For linking + RECONNECT reconnect = RECONNECT_NEVER; // For linking + + if (actionText.IsSameAs(wxT("link"), false)) { + if (parser.GetParamCount() < 4U) { + ::fprintf(stderr, "remotecontrold: invalid command line usage: remotecontrold [-name ] link \n"); + ::wxUninitialize(); + return 1; + } + + wxString reconnectText = parser.GetParam(2U); + + reflector = parser.GetParam(3U); + reflector.Replace(wxT("_"), wxT(" ")); + reflector.resize(LONG_CALLSIGN_LENGTH, wxT(' ')); + reflector.MakeUpper(); + + if (reconnectText.IsSameAs(wxT("never"), false)) { + reconnect = RECONNECT_NEVER; + } else if (reconnectText.IsSameAs(wxT("fixed"), false)) { + reconnect = RECONNECT_FIXED; + } else if (reconnectText.IsSameAs(wxT("5"), false)) { + reconnect = RECONNECT_5MINS; + } else if (reconnectText.IsSameAs(wxT("10"), false)) { + reconnect = RECONNECT_10MINS; + } else if (reconnectText.IsSameAs(wxT("15"), false)) { + reconnect = RECONNECT_15MINS; + } else if (reconnectText.IsSameAs(wxT("20"), false)) { + reconnect = RECONNECT_20MINS; + } else if (reconnectText.IsSameAs(wxT("25"), false)) { + reconnect = RECONNECT_25MINS; + } else if (reconnectText.IsSameAs(wxT("30"), false)) { + reconnect = RECONNECT_30MINS; + } else if (reconnectText.IsSameAs(wxT("60"), false)) { + reconnect = RECONNECT_60MINS; + } else if (reconnectText.IsSameAs(wxT("90"), false)) { + reconnect = RECONNECT_90MINS; + } else if (reconnectText.IsSameAs(wxT("120"), false)) { + reconnect = RECONNECT_120MINS; + } else if (reconnectText.IsSameAs(wxT("180"), false)) { + reconnect = RECONNECT_180MINS; + } else { + ::fprintf(stderr, "remotecontrold: invalid reconnect value passed\n"); + ::wxUninitialize(); + return 1; + } + } else if (actionText.IsSameAs(wxT("unlink"), false)) { + reconnect = RECONNECT_NEVER; + reflector.Clear(); + } else if (actionText.IsSameAs(wxT("drop"), false)) { + if (parser.GetParamCount() < 3U) { + ::fprintf(stderr, "remotecontrold: invalid command line usage: remotecontrold [-name ] drop \n"); + ::fprintf(stderr, " remotecontrold [-name ] drop all\n"); + ::wxUninitialize(); + return 1; + } + + user = parser.GetParam(2U); + user.Replace(wxT("_"), wxT(" ")); + user.resize(LONG_CALLSIGN_LENGTH, wxT(' ')); + user.MakeUpper(); + } else { + ::fprintf(stderr, "remotecontrold: invalid action value passed, only drop, link or unlink are allowed\n"); + ::wxUninitialize(); + return 1; + } + + CRemoteControlConfig config(new wxConfig(APPLICATION_NAME), name); + + wxString address, password; + unsigned int port; + config.getConfig(address, port, password); + + if (address.IsEmpty() || port == 0U || password.IsEmpty()) { + ::fprintf(stderr, "remotecontrold: no address, port, or password is set\n"); + ::wxUninitialize(); + return 1; + } + + CRemoteControlRemoteControlHandler handler(address, port); + + bool ret = handler.open(); + if (!ret) { + ::fprintf(stderr, "remotecontrold: uanble to open the UDP port\n"); + ::wxUninitialize(); + return 1; + } + + ret = handler.login(); + if (!ret) { + handler.close(); + ::fprintf(stderr, "remotecontrold: uanble to login to the gateway/starnetserver\n"); + ::wxUninitialize(); + return 1; + } + + unsigned int count = 0U; + while (count < 10U) { + ::wxMilliSleep(100UL); + + RC_TYPE type = handler.readType(); + if (type == RCT_RANDOM) + break; + + if (type == RCT_NONE) + handler.retry(); + + count++; + } + + if (count >= 10U) { + handler.close(); + ::fprintf(stderr, "remotecontrold: unable to get a response from the gateway/starnetserver\n"); + ::wxUninitialize(); + return 1; + } + + unsigned int rnd = handler.readRandom(); + sendHash(&handler, password, rnd); + + count = 0U; + while (count < 10U) { + ::wxMilliSleep(100UL); + + RC_TYPE type = handler.readType(); + if (type == RCT_ACK) + break; + + if (type == RCT_NAK) { + handler.close(); + ::fprintf(stderr, "remotecontrold: invalid password sent to the gateway/starnetserver\n"); + ::wxUninitialize(); + return 1; + } + + if (type == RCT_NONE) + handler.retry(); + + count++; + } + + if (count >= 10U) { + handler.close(); + ::fprintf(stderr, "remotecontrold: unable to get a response from the gateway/starnetserver\n"); + ::wxUninitialize(); + return 1; + } + + handler.setLoggedIn(true); + + if (actionText.IsSameAs(wxT("drop"), false)) + handler.logoff(repeater, user); + else + handler.link(repeater, reconnect, reflector); + + count = 0U; + while (count < 10U) { + ::wxMilliSleep(100UL); + + RC_TYPE type = handler.readType(); + if (type == RCT_ACK) + break; + + if (type == RCT_NAK) { + handler.close(); + ::fprintf(stderr, "remotecontrold: drop/link/unlink command rejected by the gateway/starnetserver\n"); + ::wxUninitialize(); + return 1; + } + + if (type == RCT_NONE) + handler.retry(); + + count++; + } + + if (count >= 10U) { + handler.close(); + ::fprintf(stderr, "remotecontrold: unable to get a response from the gateway/starnetserver\n"); + ::wxUninitialize(); + return 1; + } + + ::fprintf(stdout, "remotecontrold: command accepted by the gateway/starnetserver\n"); + + handler.logout(); + handler.close(); + + ::wxUninitialize(); + + return 0; +} diff --git a/RemoteControl/RemoteControlCallsignData.cpp b/RemoteControl/RemoteControlCallsignData.cpp new file mode 100644 index 0000000..43ab9a7 --- /dev/null +++ b/RemoteControl/RemoteControlCallsignData.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2011 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 "RemoteControlCallsignData.h" + +CRemoteControlCallsignData::CRemoteControlCallsignData() : +m_repeaters(), +m_starNets() +{ +} + +CRemoteControlCallsignData::~CRemoteControlCallsignData() +{ +} + +void CRemoteControlCallsignData::addRepeater(const wxString& callsign) +{ + m_repeaters.Add(callsign); +} + +void CRemoteControlCallsignData::addStarNet(const wxString& callsign) +{ + m_starNets.Add(callsign); +} + +unsigned int CRemoteControlCallsignData::getRepeaterCount() const +{ + return m_repeaters.GetCount(); +} + +wxString CRemoteControlCallsignData::getRepeater(unsigned int n) const +{ + return m_repeaters.Item(n); +} + +unsigned int CRemoteControlCallsignData::getStarNetCount() const +{ + return m_starNets.GetCount(); +} + +wxString CRemoteControlCallsignData::getStarNet(unsigned int n) const +{ + return m_starNets.Item(n); +} diff --git a/RemoteControl/RemoteControlCallsignData.h b/RemoteControl/RemoteControlCallsignData.h new file mode 100644 index 0000000..737f603 --- /dev/null +++ b/RemoteControl/RemoteControlCallsignData.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2011 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 RemoteControlCallsignData_H +#define RemoteControlCallsignData_H + +#include + +class CRemoteControlCallsignData { +public: + CRemoteControlCallsignData(); + ~CRemoteControlCallsignData(); + + void addRepeater(const wxString& callsign); + void addStarNet(const wxString& callsign); + + unsigned int getRepeaterCount() const; + unsigned int getStarNetCount() const; + + wxString getRepeater(unsigned int n) const; + wxString getStarNet(unsigned int n) const; + +private: + wxArrayString m_repeaters; + wxArrayString m_starNets; +}; + +#endif diff --git a/RemoteControl/RemoteControlConfig.cpp b/RemoteControl/RemoteControlConfig.cpp new file mode 100644 index 0000000..b07ae89 --- /dev/null +++ b/RemoteControl/RemoteControlConfig.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2011,2012 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 "RemoteControlConfig.h" + +const wxString KEY_ADDRESS = wxT("/address"); +const wxString KEY_PORT = wxT("/port"); +const wxString KEY_PASSWORD = wxT("/password"); +const wxString KEY_WINDOW_X = wxT("/windowX"); +const wxString KEY_WINDOW_Y = wxT("/windowY"); + +const wxString DEFAULT_ADDRESS = wxEmptyString; +const long DEFAULT_PORT = 0L; +const wxString DEFAULT_PASSWORD = wxEmptyString; +const long DEFAULT_WINDOW_X = -1L; +const long DEFAULT_WINDOW_Y = -1L; + + +CRemoteControlConfig::CRemoteControlConfig(wxConfigBase* config, const wxString& name) : +m_config(config), +m_name() +{ + wxASSERT(config != NULL); + + if (!name.IsEmpty()) + m_name = wxT("/") + name; +} + +CRemoteControlConfig::~CRemoteControlConfig() +{ + delete m_config; +} + +void CRemoteControlConfig::getConfig(wxString& address, unsigned int& port, wxString& password) const +{ + wxString addressKey = m_name + KEY_ADDRESS; + wxString portKey = m_name + KEY_PORT; + wxString passwordKey = m_name + KEY_PASSWORD; + + m_config->Read(addressKey, &address, DEFAULT_ADDRESS); + + long temp; + m_config->Read(portKey, &temp, DEFAULT_PORT); + port = (unsigned int)temp; + + m_config->Read(passwordKey, &password, DEFAULT_PASSWORD); +} + +void CRemoteControlConfig::setConfig(const wxString& address, unsigned int port, const wxString& password) const +{ + wxString addressKey = m_name + KEY_ADDRESS; + wxString portKey = m_name + KEY_PORT; + wxString passwordKey = m_name + KEY_PASSWORD; + + m_config->Write(addressKey, address); + m_config->Write(portKey, long(port)); + m_config->Write(passwordKey, password); + m_config->Flush(); +} + + +void CRemoteControlConfig::getPosition(int& x, int& y) const +{ + wxString xKey = m_name + KEY_WINDOW_X; + wxString yKey = m_name + KEY_WINDOW_Y; + + long temp; + m_config->Read(xKey, &temp, DEFAULT_WINDOW_X); + x = (unsigned int)temp; + + m_config->Read(yKey, &temp, DEFAULT_WINDOW_Y); + y = (unsigned int)temp; +} + +void CRemoteControlConfig::setPosition(int x, int y) +{ + wxString xKey = m_name + KEY_WINDOW_X; + wxString yKey = m_name + KEY_WINDOW_Y; + + m_config->Write(xKey, long(x)); + m_config->Write(yKey, long(y)); + m_config->Flush(); +} diff --git a/RemoteControl/RemoteControlConfig.h b/RemoteControl/RemoteControlConfig.h new file mode 100644 index 0000000..238dd80 --- /dev/null +++ b/RemoteControl/RemoteControlConfig.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2011,2012 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 RemoteControlConfig_H +#define RemoteControlConfig_H + +#include +#include + +class CRemoteControlConfig { + +public: + CRemoteControlConfig(wxConfigBase* config, const wxString& name); + ~CRemoteControlConfig(); + + void getConfig(wxString& address, unsigned int& port, wxString& password) const; + void setConfig(const wxString& address, unsigned int port, const wxString& password) const; + + void getPosition(int& x, int& y) const; + void setPosition(int x, int y); + +private: + wxConfigBase* m_config; + wxString m_name; +}; + +#endif diff --git a/RemoteControl/RemoteControlDefs.h b/RemoteControl/RemoteControlDefs.h new file mode 100644 index 0000000..dfffaa9 --- /dev/null +++ b/RemoteControl/RemoteControlDefs.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2011 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 RemoteControlDefs_H +#define RemoteControlDefs_H + +#include + +const wxString APPLICATION_NAME = wxT("Remote Control"); + +const wxString CONFIG_FILE_NAME = wxT(".Remote Control"); + +#endif diff --git a/RemoteControl/RemoteControlFrame.cpp b/RemoteControl/RemoteControlFrame.cpp new file mode 100644 index 0000000..8403851 --- /dev/null +++ b/RemoteControl/RemoteControlFrame.cpp @@ -0,0 +1,348 @@ +/* + * Copyright (C) 2011-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 + * 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 "RemoteControlPreferences.h" +#include "RemoteControlFrame.h" +#include "RemoteControlDefs.h" +#include "RemoteControlApp.h" +#include "Version.h" +#include "SHA256.h" + +const unsigned int BORDER_SIZE = 5U; + +#if defined(__WINDOWS__) +const unsigned int MAIN_HEIGHT = 350U; +const unsigned int MAIN_WIDTH = 500U; +#else +const unsigned int MAIN_HEIGHT = 350U; +const unsigned int MAIN_WIDTH = 520U; +#endif + +#include + +enum { + Menu_Edit_Preferences = 7000 +}; + +BEGIN_EVENT_TABLE(CRemoteControlFrame, wxFrame) + EVT_MENU(wxID_EXIT, CRemoteControlFrame::onQuit) + EVT_MENU(Menu_Edit_Preferences, CRemoteControlFrame::onPreferences) + EVT_MENU(wxID_ABOUT, CRemoteControlFrame::onAbout) + + EVT_TIMER(-1, CRemoteControlFrame::onTimer) + + EVT_CLOSE(CRemoteControlFrame::onClose) +END_EVENT_TABLE() + +CRemoteControlFrame::CRemoteControlFrame(const wxString& title, const wxPoint& position) : +wxFrame(NULL, -1, title, position), +m_state(RCFS_NORMAL), +m_timer(this), +m_noteBook(NULL), +m_handler(NULL), +m_password(), +m_repeaters(), +m_starNets(), +m_it1(), +m_it2() +{ + SetMenuBar(createMenuBar()); + + wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); + + wxPanel* panel = new wxPanel(this, -1); + + wxBoxSizer* panelSizer = new wxBoxSizer(wxVERTICAL); + + m_noteBook = new wxNotebook(panel, -1, wxDefaultPosition, wxSize(MAIN_WIDTH, MAIN_HEIGHT)); + panelSizer->Add(m_noteBook, 0, wxALL | wxGROW, BORDER_SIZE); + + panel->SetSizer(panelSizer); + + panelSizer->SetSizeHints(panel); + + mainSizer->Add(panel); + + SetSizer(mainSizer); + mainSizer->SetSizeHints(this); + + wxString address; + unsigned int port; + ::wxGetApp().getConfig(address, port, m_password); + + if (address.IsEmpty() || port == 0U || m_password.IsEmpty()) + return; + + m_handler = new CRemoteControlRemoteControlHandler(address, port); + + bool ret = m_handler->open(); + if (!ret) { + delete m_handler; + m_handler = NULL; + return; + } + + ret = m_handler->login(); + if (!ret) { + m_handler->close(); + delete m_handler; + m_handler = NULL; + return; + } + + m_state = RCFS_LOGIN; + + m_timer.Start(100); +} + +CRemoteControlFrame::~CRemoteControlFrame() +{ +} + +wxMenuBar* CRemoteControlFrame::createMenuBar() +{ + wxMenu* fileMenu = new wxMenu(); + fileMenu->Append(wxID_EXIT, _("Exit")); + + wxMenu* editMenu = new wxMenu(); + editMenu->Append(Menu_Edit_Preferences, _("Preferences...")); + + wxMenu* helpMenu = new wxMenu(); + helpMenu->Append(wxID_ABOUT, _("About Remote Control")); + + wxMenuBar* menuBar = new wxMenuBar(); + menuBar->Append(fileMenu, _("File")); + menuBar->Append(editMenu, _("Edit")); + menuBar->Append(helpMenu, _("Help")); + + return menuBar; +} + +void CRemoteControlFrame::onQuit(wxCommandEvent&) +{ + Close(false); +} + +void CRemoteControlFrame::onClose(wxCloseEvent&) +{ + int x, y; + GetPosition(&x, &y); + if (x >= 0 && y >= 0) + ::wxGetApp().setPosition(x, y); + + m_timer.Stop(); + + if (m_handler != NULL) { + m_handler->logout(); + m_handler->close(); + } + + Destroy(); +} + +void CRemoteControlFrame::onPreferences(wxCommandEvent&) +{ + wxString address, password; + unsigned int port; + ::wxGetApp().getConfig(address, port, password); + + CRemoteControlPreferences dialog1(this, -1, address, port, password); + if (dialog1.ShowModal() != wxID_OK) + return; + + address = dialog1.getAddress(); + port = dialog1.getPort(); + password = dialog1.getPassword(); + + ::wxGetApp().setConfig(address, port, password); + + wxMessageDialog dialog2(this, _("The changes made will not take effect\nuntil the application is restarted"), _("Remote Control Information"), wxICON_INFORMATION); + dialog2.ShowModal(); +} + +void CRemoteControlFrame::onAbout(wxCommandEvent&) +{ + wxAboutDialogInfo info; + info.AddDeveloper(wxT("Jonathan Naylor, G4KLX")); + info.SetCopyright(wxT("(C) 2011-2015 using GPL v2 or later")); + info.SetName(APPLICATION_NAME); + info.SetVersion(VERSION); + info.SetDescription(_("This program allows for the controlling of\nircDDB Gateway and STARnet Digital Server.")); + + ::wxAboutBox(info); +} + +void CRemoteControlFrame::onTimer(wxTimerEvent&) +{ + RC_TYPE type = m_handler->readType(); + + switch (type) { + case RCT_NONE: + m_handler->retry(); + break; + + case RCT_ACK: + if (m_state == RCFS_HASH) { + m_handler->setLoggedIn(true); + m_handler->getCallsigns(); + m_state = RCFS_NORMAL; + } + break; + + case RCT_NAK: { + if (m_state == RCFS_HASH || m_state == RCFS_LOGIN) + m_handler->setLoggedIn(false); + + wxString text = m_handler->readNAK(); + m_state = RCFS_NORMAL; + ::wxMessageBox(text); + } + break; + + case RCT_RANDOM: { + unsigned int rnd = m_handler->readRandom(); + sendHash(rnd); + m_state = RCFS_HASH; + } + break; + + case RCT_CALLSIGNS: { + m_noteBook->DeleteAllPages(); + + CRemoteControlCallsignData* data = m_handler->readCallsigns(); + for (unsigned int i = 0U; i < data->getRepeaterCount(); i++) + addRepeater(data->getRepeater(i)); + for (unsigned int i = 0U; i < data->getStarNetCount(); i++) + addStarNet(data->getStarNet(i)); + delete data; + + m_state = RCFS_GET_REPEATERS; + m_it1 = m_repeaters.begin(); + m_it2 = m_starNets.begin(); + getNextRepeater(); + } + break; + + case RCT_REPEATER: { + CRemoteControlRepeaterData* data = m_handler->readRepeater(); + wxString callsign = data->getCallsign(); + m_repeaters[callsign]->add(*data); + delete data; + + if (m_state == RCFS_GET_REPEATERS || m_state == RCFS_GET_STARNETS) + getNextRepeater(); + } + break; + + case RCT_STARNET: { + CRemoteControlStarNetGroup* data = m_handler->readStarNetGroup(); + wxString callsign = data->getCallsign(); + m_starNets[callsign]->add(*data); + delete data; + + if (m_state == RCFS_GET_REPEATERS || m_state == RCFS_GET_STARNETS) + getNextStarNet(); + } + break; + } +} + +void CRemoteControlFrame::addRepeater(const wxString& callsign) +{ + CRemoteControlRepeaterPanel* repeater = new CRemoteControlRepeaterPanel(m_noteBook, -1, callsign); + + m_noteBook->AddPage(repeater, callsign, false); + + m_repeaters[callsign] = repeater; +} + +void CRemoteControlFrame::addStarNet(const wxString& callsign) +{ + CRemoteControlStarNetPanel* starNet = new CRemoteControlStarNetPanel(m_noteBook, -1, callsign); + + m_noteBook->AddPage(starNet, callsign, false); + + m_starNets[callsign] = starNet; +} + +void CRemoteControlFrame::repeaterRefresh(const wxString& callsign) +{ + m_handler->getRepeater(callsign); +} + +void CRemoteControlFrame::starNetRefresh(const wxString& callsign) +{ + m_handler->getStarNet(callsign); +} + +void CRemoteControlFrame::link(const wxString& callsign, RECONNECT reconnect, const wxString& reflector) +{ + m_handler->link(callsign, reconnect, reflector); +} + +void CRemoteControlFrame::unlink(const wxString& callsign, PROTOCOL protocol, const wxString& reflector) +{ + m_handler->unlink(callsign, protocol, reflector); +} + +void CRemoteControlFrame::starNetLogoff(const wxString& callsign, const wxString& user) +{ + m_handler->logoff(callsign, user); +} + +void CRemoteControlFrame::sendHash(unsigned int rnd) +{ + unsigned int len = m_password.Len() + sizeof(unsigned int); + unsigned char* in = new unsigned char[len]; + unsigned char* out = new unsigned char[32U]; + + ::memcpy(in, &rnd, sizeof(unsigned int)); + for (unsigned int i = 0U; i < m_password.Len(); i++) + in[i + sizeof(unsigned int)] = m_password.GetChar(i); + + CSHA256 sha256; + sha256.buffer(in, len, out); + + m_handler->sendHash(out, 32U); + + delete[] in; + delete[] out; +} + +void CRemoteControlFrame::getNextRepeater() +{ + if (m_it1 == m_repeaters.end()) { + m_state = RCFS_GET_STARNETS; + getNextStarNet(); + return; + } + + m_handler->getRepeater(m_it1->first); + ++m_it1; +} + +void CRemoteControlFrame::getNextStarNet() +{ + if (m_it2 == m_starNets.end()) { + m_state = RCFS_NORMAL; + return; + } + + m_handler->getStarNet(m_it2->first); + ++m_it2; +} diff --git a/RemoteControl/RemoteControlFrame.h b/RemoteControl/RemoteControlFrame.h new file mode 100644 index 0000000..c145c39 --- /dev/null +++ b/RemoteControl/RemoteControlFrame.h @@ -0,0 +1,79 @@ +/* + * 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 + * 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 RemoteControlFrame_H +#define RemoteControlFrame_H + +#include "RemoteControlRemoteControlHandler.h" +#include "RemoteControlRepeaterPanel.h" +#include "RemoteControlStarNetPanel.h" +#include "Defs.h" + +#include +#include + +WX_DECLARE_STRING_HASH_MAP(CRemoteControlRepeaterPanel*, CRepeater_t); +WX_DECLARE_STRING_HASH_MAP(CRemoteControlStarNetPanel*, CStarNet_t); + +enum RCF_STATE { + RCFS_NORMAL, + RCFS_LOGIN, + RCFS_HASH, + RCFS_GET_REPEATERS, + RCFS_GET_STARNETS +}; + +class CRemoteControlFrame : public wxFrame { +public: + CRemoteControlFrame(const wxString& title, const wxPoint& position); + virtual ~CRemoteControlFrame(); + + virtual void onQuit(wxCommandEvent& event); + virtual void onPreferences(wxCommandEvent& event); + virtual void onAbout(wxCommandEvent& event); + virtual void onClose(wxCloseEvent& event); + virtual void onTimer(wxTimerEvent& event); + + virtual void repeaterRefresh(const wxString& callsign); + virtual void starNetRefresh(const wxString& callsign); + virtual void link(const wxString& callsign, RECONNECT reconnect, const wxString& reflector); + virtual void unlink(const wxString& callsign, PROTOCOL protocol, const wxString& reflector); + virtual void starNetLogoff(const wxString& callsign, const wxString& user); + +private: + RCF_STATE m_state; + wxTimer m_timer; + wxNotebook* m_noteBook; + CRemoteControlRemoteControlHandler* m_handler; + wxString m_password; + CRepeater_t m_repeaters; + CStarNet_t m_starNets; + CRepeater_t::iterator m_it1; + CStarNet_t::iterator m_it2; + + DECLARE_EVENT_TABLE() + + wxMenuBar* createMenuBar(); + void addRepeater(const wxString& callsign); + void addStarNet(const wxString& callsign); + void sendHash(unsigned int rnd); + void getNextRepeater(); + void getNextStarNet(); +}; + +#endif diff --git a/RemoteControl/RemoteControlLinkData.cpp b/RemoteControl/RemoteControlLinkData.cpp new file mode 100644 index 0000000..ed274b9 --- /dev/null +++ b/RemoteControl/RemoteControlLinkData.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2011 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 "RemoteControlLinkData.h" + +CRemoteControlLinkData::CRemoteControlLinkData(const wxString& callsign, wxInt32 protocol, wxInt32 linked, wxInt32 direction, wxInt32 dongle) : +m_callsign(callsign), +m_protocol(PROTOCOL(protocol)), +m_linked(false), +m_direction(DIRECTION(direction)), +m_dongle(false) +{ + m_linked = linked == 1; + m_dongle = dongle == 1; +} + +CRemoteControlLinkData::~CRemoteControlLinkData() +{ +} + +wxString CRemoteControlLinkData::getCallsign() const +{ + return m_callsign; +} + +PROTOCOL CRemoteControlLinkData::getProtocol() const +{ + return m_protocol; +} + +bool CRemoteControlLinkData::isLinked() const +{ + return m_linked; +} + +DIRECTION CRemoteControlLinkData::getDirection() const +{ + return m_direction; +} + +bool CRemoteControlLinkData::isDongle() const +{ + return m_dongle; +} diff --git a/RemoteControl/RemoteControlLinkData.h b/RemoteControl/RemoteControlLinkData.h new file mode 100644 index 0000000..8d69016 --- /dev/null +++ b/RemoteControl/RemoteControlLinkData.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2011 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 RemoteControlLinkData_H +#define RemoteControlLinkData_H + +#include "Defs.h" + +#include + +class CRemoteControlLinkData { +public: + CRemoteControlLinkData(const wxString& callsign, wxInt32 protocol, wxInt32 linked, wxInt32 direction, wxInt32 dongle); + ~CRemoteControlLinkData(); + + wxString getCallsign() const; + PROTOCOL getProtocol() const; + bool isLinked() const; + DIRECTION getDirection() const; + bool isDongle() const; + +private: + wxString m_callsign; + PROTOCOL m_protocol; + bool m_linked; + DIRECTION m_direction; + bool m_dongle; +}; + +#endif diff --git a/RemoteControl/RemoteControlPreferences.cpp b/RemoteControl/RemoteControlPreferences.cpp new file mode 100644 index 0000000..6b35889 --- /dev/null +++ b/RemoteControl/RemoteControlPreferences.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2011 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 "RemoteControlPreferences.h" +#include "RemoteControlDefs.h" + +const unsigned int BORDER_SIZE = 5U; + +CRemoteControlPreferences::CRemoteControlPreferences(wxWindow* parent, int id, const wxString& address, unsigned int port, const wxString& password) : +wxDialog(parent, id, wxString(_("Remote Control Preferences"))), +m_remote(NULL) +{ + wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); + + wxNotebook* noteBook = new wxNotebook(this, -1); + + m_remote = new CRemoteControlRemoteSet(noteBook, -1, APPLICATION_NAME, address, port, password); + noteBook->AddPage(m_remote, _("Gateway"), true); + + mainSizer->Add(noteBook, 1, wxALL | wxGROW, BORDER_SIZE); + + mainSizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + SetAutoLayout(true); + Layout(); + + mainSizer->Fit(this); + mainSizer->SetSizeHints(this); + + SetSizer(mainSizer); +} + +CRemoteControlPreferences::~CRemoteControlPreferences() +{ +} + +bool CRemoteControlPreferences::Validate() +{ + return m_remote->Validate(); +} + +wxString CRemoteControlPreferences::getAddress() const +{ + return m_remote->getAddress(); +} + +unsigned int CRemoteControlPreferences::getPort() const +{ + return m_remote->getPort(); +} + +wxString CRemoteControlPreferences::getPassword() const +{ + return m_remote->getPassword(); +} diff --git a/RemoteControl/RemoteControlPreferences.h b/RemoteControl/RemoteControlPreferences.h new file mode 100644 index 0000000..a4c6ce6 --- /dev/null +++ b/RemoteControl/RemoteControlPreferences.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2011 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 RemoteControlPreferences_H +#define RemoteControlPreferences_H + +#include +#include + +#include "RemoteControlRemoteSet.h" + +class CRemoteControlPreferences : public wxDialog { +public: + CRemoteControlPreferences(wxWindow* parent, int id, const wxString& address, unsigned int port, const wxString& password); + virtual ~CRemoteControlPreferences(); + + virtual bool Validate(); + + virtual wxString getAddress() const; + virtual unsigned int getPort() const; + virtual wxString getPassword() const; + +private: + CRemoteControlRemoteSet* m_remote; +}; + +#endif diff --git a/RemoteControl/RemoteControlRemoteControlHandler.cpp b/RemoteControl/RemoteControlRemoteControlHandler.cpp new file mode 100644 index 0000000..2719812 --- /dev/null +++ b/RemoteControl/RemoteControlRemoteControlHandler.cpp @@ -0,0 +1,557 @@ +/* + * Copyright (C) 2011,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 "RemoteControlRemoteControlHandler.h" +#include "DStarDefines.h" + +const unsigned int BUFFER_LENGTH = 2000U; + +const unsigned int MAX_RETRIES = 3U; + +CRemoteControlRemoteControlHandler::CRemoteControlRemoteControlHandler(const wxString& address, unsigned int port) : +m_socket(wxEmptyString, 0U), +m_address(), +m_port(port), +m_loggedIn(false), +m_retryCount(0U), +m_type(RCT_NONE), +m_inBuffer(NULL), +m_inLength(0U), +m_outBuffer(NULL), +m_outLength(0U) +{ + wxASSERT(!address.IsEmpty()); + wxASSERT(port > 0U); + + m_address = CUDPReaderWriter::lookup(address); + + m_inBuffer = new unsigned char[BUFFER_LENGTH]; + m_outBuffer = new unsigned char[BUFFER_LENGTH]; +} + +CRemoteControlRemoteControlHandler::~CRemoteControlRemoteControlHandler() +{ + delete[] m_inBuffer; + delete[] m_outBuffer; +} + +bool CRemoteControlRemoteControlHandler::open() +{ + return m_socket.open(); +} + +RC_TYPE CRemoteControlRemoteControlHandler::readType() +{ + m_type = RCT_NONE; + + in_addr address; + unsigned int port; + + int length = m_socket.read(m_inBuffer, BUFFER_LENGTH, address, port); + if (length <= 0) + return m_type; + + m_inLength = length; + + if (::memcmp(m_inBuffer, "ACK", 3U) == 0) { + m_retryCount = 0U; + m_type = RCT_ACK; + return m_type; + } else if (::memcmp(m_inBuffer, "NAK", 3U) == 0) { + m_retryCount = 0U; + m_type = RCT_NAK; + return m_type; + } else if (::memcmp(m_inBuffer, "RND", 3U) == 0) { + m_retryCount = 0U; + m_type = RCT_RANDOM; + return m_type; + } else if (::memcmp(m_inBuffer, "CAL", 3U) == 0) { + m_retryCount = 0U; + m_type = RCT_CALLSIGNS; + return m_type; + } else if (::memcmp(m_inBuffer, "RPT", 3U) == 0) { + m_retryCount = 0U; + m_type = RCT_REPEATER; + return m_type; + } else if (::memcmp(m_inBuffer, "SNT", 3U) == 0) { + m_retryCount = 0U; + m_type = RCT_STARNET; + return m_type; + } + + return m_type; +} + +wxString CRemoteControlRemoteControlHandler::readNAK() +{ + if (m_type != RCT_NAK) + return wxEmptyString; + + wxString text((char*)(m_inBuffer + 3U), wxConvLocal); + + return text; +} + +unsigned int CRemoteControlRemoteControlHandler::readRandom() +{ + if (m_type != RCT_RANDOM) + return 0U; + + wxUint32 random; + ::memcpy(&random, m_inBuffer + 3U, sizeof(wxUint32)); + + return wxUINT32_SWAP_ON_BE(random); +} + +CRemoteControlCallsignData* CRemoteControlRemoteControlHandler::readCallsigns() +{ + if (m_type != RCT_CALLSIGNS) + return NULL; + + CRemoteControlCallsignData* data = new CRemoteControlCallsignData; + + unsigned char* p = m_inBuffer + 3U; + unsigned int pos = 3U; + + while (pos < m_inLength) { + unsigned char type = *p; + pos += 1U; + p += 1U; + + wxString callsign((char*)p, wxConvLocal, LONG_CALLSIGN_LENGTH); + pos += LONG_CALLSIGN_LENGTH; + p += LONG_CALLSIGN_LENGTH; + + switch (type) { + case 'R': + data->addRepeater(callsign); + break; + case 'S': + data->addStarNet(callsign); + break; + default: // ???? + break; + } + } + + return data; +} + +CRemoteControlRepeaterData* CRemoteControlRemoteControlHandler::readRepeater() +{ + if (m_type != RCT_REPEATER) + return NULL; + + unsigned char* p = m_inBuffer + 3U; + unsigned int pos = 3U; + + wxString callsign((char*)p, wxConvLocal, LONG_CALLSIGN_LENGTH); + pos += LONG_CALLSIGN_LENGTH; + p += LONG_CALLSIGN_LENGTH; + + wxInt32 reconnect; + ::memcpy(&reconnect, p, sizeof(wxInt32)); + pos += sizeof(wxInt32); + p += sizeof(wxInt32); + + wxString reflector((char*)p, wxConvLocal, LONG_CALLSIGN_LENGTH); + pos += LONG_CALLSIGN_LENGTH; + p += LONG_CALLSIGN_LENGTH; + + CRemoteControlRepeaterData* data = new CRemoteControlRepeaterData(callsign, wxINT32_SWAP_ON_BE(reconnect), reflector); + + while (pos < m_inLength) { + wxString callsign((char*)p, wxConvLocal, LONG_CALLSIGN_LENGTH); + pos += LONG_CALLSIGN_LENGTH; + p += LONG_CALLSIGN_LENGTH; + + wxInt32 protocol; + ::memcpy(&protocol, p, sizeof(wxInt32)); + pos += sizeof(wxInt32); + p += sizeof(wxInt32); + + wxInt32 linked; + ::memcpy(&linked, p, sizeof(wxInt32)); + pos += sizeof(wxInt32); + p += sizeof(wxInt32); + + wxInt32 direction; + ::memcpy(&direction, p, sizeof(wxInt32)); + pos += sizeof(wxInt32); + p += sizeof(wxInt32); + + wxInt32 dongle; + ::memcpy(&dongle, p, sizeof(wxInt32)); + pos += sizeof(wxInt32); + p += sizeof(wxInt32); + + data->addLink(callsign, wxINT32_SWAP_ON_BE(protocol), wxINT32_SWAP_ON_BE(linked), wxINT32_SWAP_ON_BE(direction), wxINT32_SWAP_ON_BE(dongle)); + } + + return data; +} + +CRemoteControlStarNetGroup* CRemoteControlRemoteControlHandler::readStarNetGroup() +{ + if (m_type != RCT_STARNET) + return NULL; + + unsigned char* p = m_inBuffer + 3U; + unsigned int pos = 3U; + + wxString callsign((char*)p, wxConvLocal, LONG_CALLSIGN_LENGTH); + pos += LONG_CALLSIGN_LENGTH; + p += LONG_CALLSIGN_LENGTH; + + wxString logoff((char*)p, wxConvLocal, LONG_CALLSIGN_LENGTH); + pos += LONG_CALLSIGN_LENGTH; + p += LONG_CALLSIGN_LENGTH; + + wxUint32 timer; + ::memcpy(&timer, p, sizeof(wxUint32)); + pos += sizeof(wxUint32); + p += sizeof(wxUint32); + + wxUint32 timeout; + ::memcpy(&timeout, p, sizeof(wxUint32)); + pos += sizeof(wxUint32); + p += sizeof(wxUint32); + + CRemoteControlStarNetGroup* group = new CRemoteControlStarNetGroup(callsign, logoff, wxUINT32_SWAP_ON_BE(timer), wxUINT32_SWAP_ON_BE(timeout)); + + while (pos < m_inLength) { + wxString callsign((char*)p, wxConvLocal, LONG_CALLSIGN_LENGTH); + pos += LONG_CALLSIGN_LENGTH; + p += LONG_CALLSIGN_LENGTH; + + ::memcpy(&timer, p, sizeof(wxUint32)); + pos += sizeof(wxUint32); + p += sizeof(wxUint32); + + ::memcpy(&timeout, p, sizeof(wxUint32)); + pos += sizeof(wxUint32); + p += sizeof(wxUint32); + + group->addUser(callsign, wxUINT32_SWAP_ON_BE(timer), wxUINT32_SWAP_ON_BE(timeout)); + } + + return group; +} + +bool CRemoteControlRemoteControlHandler::login() +{ + if (m_loggedIn) + return false; + + if (m_address.s_addr == INADDR_NONE) + return false; + + ::memcpy(m_outBuffer, "LIN", 3U); + m_outLength = 3U; + + bool ret = m_socket.write(m_outBuffer, m_outLength, m_address, m_port); + if (!ret) { + m_retryCount = 0U; + return false; + } else { + m_retryCount = 1U; + return true; + } +} + +void CRemoteControlRemoteControlHandler::setLoggedIn(bool set) +{ + m_loggedIn = set; +} + +bool CRemoteControlRemoteControlHandler::getCallsigns() +{ + if (!m_loggedIn || m_retryCount > 0U) + return false; + + ::memcpy(m_outBuffer, "GCS", 3U); + m_outLength = 3U; + + bool ret = m_socket.write(m_outBuffer, m_outLength, m_address, m_port); + if (!ret) { + m_retryCount = 0U; + return false; + } else { + m_retryCount = 1U; + return true; + } +} + +bool CRemoteControlRemoteControlHandler::sendHash(const unsigned char* hash, unsigned int length) +{ + wxASSERT(hash != NULL); + wxASSERT(length > 0U); + + if (m_loggedIn || m_retryCount > 0U) + return false; + + unsigned char* p = m_outBuffer; + m_outLength = 0U; + + ::memcpy(p, "SHA", 3U); + m_outLength += 3U; + p += 3U; + + ::memcpy(p, hash, length); + m_outLength += length; + p += length; + + bool ret = m_socket.write(m_outBuffer, m_outLength, m_address, m_port); + if (!ret) { + m_retryCount = 0U; + return false; + } else { + m_retryCount = 1U; + return true; + } +} + +bool CRemoteControlRemoteControlHandler::getRepeater(const wxString& callsign) +{ + wxASSERT(!callsign.IsEmpty()); + + if (!m_loggedIn || m_retryCount > 0U) + return false; + + unsigned char* p = m_outBuffer; + m_outLength = 0U; + + ::memcpy(p, "GRP", 3U); + m_outLength += 3U; + p += 3U; + + ::memset(p, ' ', LONG_CALLSIGN_LENGTH); + for (unsigned int i = 0U; i < callsign.Len(); i++) + p[i] = callsign.GetChar(i); + + m_outLength += LONG_CALLSIGN_LENGTH; + p += LONG_CALLSIGN_LENGTH; + + bool ret = m_socket.write(m_outBuffer, m_outLength, m_address, m_port); + if (!ret) { + m_retryCount = 0U; + return false; + } else { + m_retryCount = 1U; + return true; + } +} + +bool CRemoteControlRemoteControlHandler::getStarNet(const wxString& callsign) +{ + wxASSERT(!callsign.IsEmpty()); + + if (!m_loggedIn || m_retryCount > 0U) + return false; + + unsigned char* p = m_outBuffer; + m_outLength = 0U; + + ::memcpy(p, "GSN", 3U); + m_outLength += 3U; + p += 3U; + + ::memset(p, ' ', LONG_CALLSIGN_LENGTH); + for (unsigned int i = 0U; i < callsign.Len(); i++) + p[i] = callsign.GetChar(i); + + m_outLength += LONG_CALLSIGN_LENGTH; + p += LONG_CALLSIGN_LENGTH; + + bool ret = m_socket.write(m_outBuffer, m_outLength, m_address, m_port); + if (!ret) { + m_retryCount = 0U; + return false; + } else { + m_retryCount = 1U; + return true; + } +} + +bool CRemoteControlRemoteControlHandler::link(const wxString& callsign, RECONNECT reconnect, const wxString& reflector) +{ + wxASSERT(!callsign.IsEmpty()); + + if (!m_loggedIn || m_retryCount > 0U) + return false; + + unsigned char* p = m_outBuffer; + m_outLength = 0U; + + ::memcpy(p, "LNK", 3U); + m_outLength += 3U; + p += 3U; + + ::memset(p, ' ', LONG_CALLSIGN_LENGTH); + for (unsigned int i = 0U; i < callsign.Len(); i++) + p[i] = callsign.GetChar(i); + + m_outLength += LONG_CALLSIGN_LENGTH; + p += LONG_CALLSIGN_LENGTH; + + wxInt32 temp1 = wxInt32(reconnect); + wxInt32 temp2 = wxINT32_SWAP_ON_BE(temp1); + ::memcpy(p, &temp2, sizeof(wxInt32)); + m_outLength += sizeof(wxInt32); + p += sizeof(wxInt32); + + ::memset(p, ' ', LONG_CALLSIGN_LENGTH); + for (unsigned int i = 0U; i < reflector.Len(); i++) + p[i] = reflector.GetChar(i); + + m_outLength += LONG_CALLSIGN_LENGTH; + p += LONG_CALLSIGN_LENGTH; + + bool ret = m_socket.write(m_outBuffer, m_outLength, m_address, m_port); + if (!ret) { + m_retryCount = 0U; + return false; + } else { + m_retryCount = 1U; + return true; + } +} + +bool CRemoteControlRemoteControlHandler::unlink(const wxString& callsign, PROTOCOL protocol, const wxString& reflector) +{ + wxASSERT(!callsign.IsEmpty()); + + if (!m_loggedIn || m_retryCount > 0U) + return false; + + unsigned char* p = m_outBuffer; + m_outLength = 0U; + + ::memcpy(p, "UNL", 3U); + m_outLength += 3U; + p += 3U; + + ::memset(p, ' ', LONG_CALLSIGN_LENGTH); + for (unsigned int i = 0U; i < callsign.Len(); i++) + p[i] = callsign.GetChar(i); + + m_outLength += LONG_CALLSIGN_LENGTH; + p += LONG_CALLSIGN_LENGTH; + + wxInt32 temp1 = wxInt32(protocol); + wxInt32 temp2 = wxINT32_SWAP_ON_BE(temp1); + ::memcpy(p, &temp2, sizeof(wxInt32)); + m_outLength += sizeof(wxInt32); + p += sizeof(wxInt32); + + ::memset(p, ' ', LONG_CALLSIGN_LENGTH); + for (unsigned int i = 0U; i < reflector.Len(); i++) + p[i] = reflector.GetChar(i); + + m_outLength += LONG_CALLSIGN_LENGTH; + p += LONG_CALLSIGN_LENGTH; + + bool ret = m_socket.write(m_outBuffer, m_outLength, m_address, m_port); + if (!ret) { + m_retryCount = 0U; + return false; + } else { + m_retryCount = 1U; + return true; + } +} + +bool CRemoteControlRemoteControlHandler::logoff(const wxString& callsign, const wxString& user) +{ + wxASSERT(!callsign.IsEmpty()); + wxASSERT(!user.IsEmpty()); + + if (!m_loggedIn || m_retryCount > 0U) + return false; + + unsigned char* p = m_outBuffer; + m_outLength = 0U; + + ::memcpy(p, "LGO", 3U); + m_outLength += 3U; + p += 3U; + + ::memset(p, ' ', LONG_CALLSIGN_LENGTH); + for (unsigned int i = 0U; i < callsign.Len(); i++) + p[i] = callsign.GetChar(i); + + m_outLength += LONG_CALLSIGN_LENGTH; + p += LONG_CALLSIGN_LENGTH; + + ::memset(p, ' ', LONG_CALLSIGN_LENGTH); + for (unsigned int i = 0U; i < user.Len(); i++) + p[i] = user.GetChar(i); + + m_outLength += LONG_CALLSIGN_LENGTH; + p += LONG_CALLSIGN_LENGTH; + + bool ret = m_socket.write(m_outBuffer, m_outLength, m_address, m_port); + if (!ret) { + m_retryCount = 0U; + return false; + } else { + m_retryCount = 1U; + return true; + } +} + +bool CRemoteControlRemoteControlHandler::logout() +{ + if (!m_loggedIn || m_retryCount > 0U) + return false; + + ::memcpy(m_outBuffer, "LOG", 3U); + m_outLength = 3U; + + for (unsigned int i = 0U; i < 5U; i++) { + bool ret = m_socket.write(m_outBuffer, m_outLength, m_address, m_port); + if (!ret) { + m_retryCount = 0U; + return false; + } + } + + m_retryCount = 1U; + + return true; +} + +bool CRemoteControlRemoteControlHandler::retry() +{ + if (m_retryCount > 0U) { + m_retryCount++; + if (m_retryCount >= MAX_RETRIES) { + m_retryCount = 0U; + return false; + } + + m_socket.write(m_outBuffer, m_outLength, m_address, m_port); + } + + return true; +} + +void CRemoteControlRemoteControlHandler::close() +{ + m_socket.close(); +} diff --git a/RemoteControl/RemoteControlRemoteControlHandler.h b/RemoteControl/RemoteControlRemoteControlHandler.h new file mode 100644 index 0000000..e9d315b --- /dev/null +++ b/RemoteControl/RemoteControlRemoteControlHandler.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2011,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 RemoteControlRemoteControlHandler_H +#define RemoteControlRemoteControlHandler_H + +#include "RemoteControlRepeaterData.h" +#include "RemoteControlStarNetGroup.h" +#include "RemoteControlCallsignData.h" +#include "UDPReaderWriter.h" + +#include + +enum RC_TYPE { + RCT_NONE, + RCT_ACK, + RCT_NAK, + RCT_RANDOM, + RCT_CALLSIGNS, + RCT_REPEATER, + RCT_STARNET +}; + +class CRemoteControlRemoteControlHandler { +public: + CRemoteControlRemoteControlHandler(const wxString& address, unsigned int port); + ~CRemoteControlRemoteControlHandler(); + + bool open(); + + RC_TYPE readType(); + + wxString readNAK(); + unsigned int readRandom(); + CRemoteControlCallsignData* readCallsigns(); + CRemoteControlRepeaterData* readRepeater(); + CRemoteControlStarNetGroup* readStarNetGroup(); + + bool login(); + bool sendHash(const unsigned char* hash, unsigned int length); + + void setLoggedIn(bool set); + + bool getCallsigns(); + bool getRepeater(const wxString& callsign); + bool getStarNet(const wxString& callsign); + + bool link(const wxString& callsign, RECONNECT reconnect, const wxString& reflector); + bool unlink(const wxString& callsign, PROTOCOL protocol, const wxString& reflector); + bool logoff(const wxString& callsign, const wxString& user); + + bool logout(); + + bool retry(); + + void close(); + +private: + CUDPReaderWriter m_socket; + in_addr m_address; + unsigned int m_port; + bool m_loggedIn; + unsigned int m_retryCount; + RC_TYPE m_type; + unsigned char* m_inBuffer; + unsigned int m_inLength; + unsigned char* m_outBuffer; + unsigned int m_outLength; +}; + +#endif diff --git a/RemoteControl/RemoteControlRemoteSet.cpp b/RemoteControl/RemoteControlRemoteSet.cpp new file mode 100644 index 0000000..0c8f788 --- /dev/null +++ b/RemoteControl/RemoteControlRemoteSet.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2011 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 "RemoteControlRemoteSet.h" +#include "DStarDefines.h" + +const unsigned int PASSWORD_WIDTH = 120U; +const unsigned int PORT_WIDTH = 80U; + +const unsigned int PORT_LENGTH = 5U; + +const unsigned int BORDER_SIZE = 5U; + +CRemoteControlRemoteSet::CRemoteControlRemoteSet(wxWindow* parent, int id, const wxString& title, const wxString& address, unsigned int port, const wxString& password) : +wxPanel(parent, id), +m_title(title), +m_address(NULL), +m_port(NULL), +m_password(NULL) +{ + wxFlexGridSizer* sizer = new wxFlexGridSizer(2); + + wxStaticText* addressLabel = new wxStaticText(this, -1, _("Address")); + sizer->Add(addressLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_address = new wxTextCtrl(this, -1, address, wxDefaultPosition, wxSize(PASSWORD_WIDTH, -1)); + sizer->Add(m_address, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* portLabel = new wxStaticText(this, -1, _("Port")); + sizer->Add(portLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxString buffer; + buffer.Printf(wxT("%u"), port); + + m_port = new CPortTextCtrl(this, -1, buffer, wxDefaultPosition, wxSize(PORT_WIDTH, -1)); + m_port->SetMaxLength(PORT_LENGTH); + sizer->Add(m_port, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* passwordLabel = new wxStaticText(this, -1, _("Password")); + sizer->Add(passwordLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_password = new wxTextCtrl(this, -1, password, wxDefaultPosition, wxSize(PASSWORD_WIDTH, -1), wxTE_PASSWORD); + sizer->Add(m_password, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + SetAutoLayout(true); + + SetSizer(sizer); +} + + +CRemoteControlRemoteSet::~CRemoteControlRemoteSet() +{ +} + +bool CRemoteControlRemoteSet::Validate() +{ + wxString address = getAddress(); + if (address.IsEmpty()) { + wxMessageDialog dialog(this, _("The Address is empty"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + unsigned int port = getPort(); + if (port == 0U || port > 65535U) { + wxMessageDialog dialog(this, _("The Port is not valid"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + wxString password = getPassword(); + if (password.IsEmpty()) { + wxMessageDialog dialog(this, _("The Password is empty"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + return true; +} + +wxString CRemoteControlRemoteSet::getAddress() const +{ + return m_address->GetValue(); +} + +unsigned int CRemoteControlRemoteSet::getPort() const +{ + unsigned long n; + m_port->GetValue().ToULong(&n); + + return n; +} + +wxString CRemoteControlRemoteSet::getPassword() const +{ + return m_password->GetValue(); +} diff --git a/RemoteControl/RemoteControlRemoteSet.h b/RemoteControl/RemoteControlRemoteSet.h new file mode 100644 index 0000000..a74c354 --- /dev/null +++ b/RemoteControl/RemoteControlRemoteSet.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2011 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 RemoteControlRemoteSet_H +#define RemoteControlRemoteSet_H + +#include "PortTextCtrl.h" + +#include + +class CRemoteControlRemoteSet : public wxPanel { +public: + CRemoteControlRemoteSet(wxWindow* parent, int id, const wxString& title, const wxString& address, unsigned int port, const wxString& password); + virtual ~CRemoteControlRemoteSet(); + + virtual bool Validate(); + + virtual wxString getAddress() const; + virtual unsigned int getPort() const; + virtual wxString getPassword() const; + +private: + wxString m_title; + wxTextCtrl* m_address; + CPortTextCtrl* m_port; + wxTextCtrl* m_password; +}; + +#endif diff --git a/RemoteControl/RemoteControlRepeaterData.cpp b/RemoteControl/RemoteControlRepeaterData.cpp new file mode 100644 index 0000000..bd1148c --- /dev/null +++ b/RemoteControl/RemoteControlRepeaterData.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2011 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 "RemoteControlRepeaterData.h" + +#include +WX_DEFINE_OBJARRAY(CRemoteLinkData_t); + +CRemoteControlRepeaterData::CRemoteControlRepeaterData(const wxString& callsign, wxInt32 reconnect, const wxString& reflector) : +m_callsign(callsign), +m_reconnect(RECONNECT(reconnect)), +m_reflector(reflector), +m_links() +{ +} + +CRemoteControlRepeaterData::~CRemoteControlRepeaterData() +{ + m_links.Clear(); +} + +void CRemoteControlRepeaterData::addLink(const wxString& callsign, wxInt32 protocol, wxInt32 linked, wxInt32 direction, wxInt32 dongle) +{ + CRemoteControlLinkData data(callsign, protocol, linked, direction, dongle); + + m_links.Add(data); +} + +wxString CRemoteControlRepeaterData::getCallsign() const +{ + return m_callsign; +} + +RECONNECT CRemoteControlRepeaterData::getReconnect() const +{ + return m_reconnect; +} + +wxString CRemoteControlRepeaterData::getReflector() const +{ + return m_reflector; +} + +unsigned int CRemoteControlRepeaterData::getLinkCount() const +{ + return m_links.GetCount(); +} + +CRemoteControlLinkData& CRemoteControlRepeaterData::getLink(unsigned int n) const +{ + return m_links.Item(n); +} diff --git a/RemoteControl/RemoteControlRepeaterData.h b/RemoteControl/RemoteControlRepeaterData.h new file mode 100644 index 0000000..5a5a4e3 --- /dev/null +++ b/RemoteControl/RemoteControlRepeaterData.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2011 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 RemoteControlRepeaterData_H +#define RemoteControlRepeaterData_H + +#include "RemoteControlLinkData.h" + +#include +#include + +WX_DECLARE_OBJARRAY(CRemoteControlLinkData, CRemoteLinkData_t); + +class CRemoteControlRepeaterData { +public: + CRemoteControlRepeaterData(const wxString& callsign, wxInt32 reconnect, const wxString& reflector); + ~CRemoteControlRepeaterData(); + + void addLink(const wxString& callsign, wxInt32 protocol, wxInt32 linked, wxInt32 direction, wxInt32 dongle); + + wxString getCallsign() const; + RECONNECT getReconnect() const; + wxString getReflector() const; + + unsigned int getLinkCount() const; + CRemoteControlLinkData& getLink(unsigned int n) const; + +private: + wxString m_callsign; + RECONNECT m_reconnect; + wxString m_reflector; + CRemoteLinkData_t m_links; +}; + +#endif diff --git a/RemoteControl/RemoteControlRepeaterPanel.cpp b/RemoteControl/RemoteControlRepeaterPanel.cpp new file mode 100644 index 0000000..78b5356 --- /dev/null +++ b/RemoteControl/RemoteControlRepeaterPanel.cpp @@ -0,0 +1,397 @@ +/* + * 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 + * 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 "RemoteControlRepeaterPanel.h" +#include "RemoteControlApp.h" +#include "DStarDefines.h" +#include "HostFile.h" + +#include + +enum { + List_Reflectors = 7100, + Button_Refresh, + Button_Link, + Button_Unlink +}; + +BEGIN_EVENT_TABLE(CRemoteControlRepeaterPanel, wxPanel) + EVT_LIST_ITEM_SELECTED(List_Reflectors, CRemoteControlRepeaterPanel::onSelect) + EVT_BUTTON(Button_Refresh, CRemoteControlRepeaterPanel::onRefresh) + EVT_BUTTON(Button_Link, CRemoteControlRepeaterPanel::onLink) + EVT_BUTTON(Button_Unlink, CRemoteControlRepeaterPanel::onUnlink) +END_EVENT_TABLE() + +#if defined(__WINDOWS__) +const unsigned int LIST_HEIGHT = 350U; +const unsigned int LIST_WIDTH = 350U; +const unsigned int CALLSIGN_WIDTH = 100U; +const unsigned int PROTOCOL_WIDTH = 55U; +const unsigned int DIRECTION_WIDTH = 55U; +const unsigned int TYPE_WIDTH = 65U; +const unsigned int STATE_WIDTH = 55U; +const unsigned int BUTTON_WIDTH = 75U; +const unsigned int REFLECTOR_WIDTH = 75U; +const unsigned int CHANNEL_WIDTH = 40U; +const unsigned int RECONNECT_WIDTH = 75U; +#else +const unsigned int LIST_HEIGHT = 350U; +const unsigned int LIST_WIDTH = 350U; +const unsigned int CALLSIGN_WIDTH = 100U; +const unsigned int PROTOCOL_WIDTH = 55U; +const unsigned int DIRECTION_WIDTH = 55U; +const unsigned int TYPE_WIDTH = 65U; +const unsigned int STATE_WIDTH = 55U; +const unsigned int BUTTON_WIDTH = 75U; +const unsigned int REFLECTOR_WIDTH = 90U; +const unsigned int CHANNEL_WIDTH = 50U; +const unsigned int RECONNECT_WIDTH = 100U; +#endif + +const unsigned int BORDER_SIZE = 5U; + +CRemoteControlRepeaterPanel::CRemoteControlRepeaterPanel(wxWindow* parent, int id, const wxString& callsign) : +wxPanel(parent, id), +m_callsign(callsign), +m_list(NULL), +m_reflector(NULL), +m_channel(NULL), +m_reconnect(NULL), +m_unlink(NULL), +m_selected(-1), +m_reflectors(), +m_protocols() +{ + wxBoxSizer* sizer1 = new wxBoxSizer(wxHORIZONTAL); + + m_list = new wxListCtrl(this, List_Reflectors, wxDefaultPosition, wxSize(LIST_WIDTH, LIST_HEIGHT), wxLC_REPORT | wxLC_SINGLE_SEL); + m_list->InsertColumn(0L, _("Callsign")); + m_list->SetColumnWidth(0L, CALLSIGN_WIDTH); + m_list->InsertColumn(1L, _("Protocol")); + m_list->SetColumnWidth(1L, PROTOCOL_WIDTH); + m_list->InsertColumn(2L, _("Direction")); + m_list->SetColumnWidth(2L, DIRECTION_WIDTH); + m_list->InsertColumn(3L, _("Type")); + m_list->SetColumnWidth(3L, TYPE_WIDTH); + m_list->InsertColumn(4L, _("State")); + m_list->SetColumnWidth(4L, STATE_WIDTH); + sizer1->Add(m_list, 0, wxTOP | wxBOTTOM | wxLEFT | wxEXPAND, BORDER_SIZE); + + wxBoxSizer* sizer2 = new wxBoxSizer(wxVERTICAL); + + wxButton* refreshButton = new wxButton(this, Button_Refresh, _("Refresh"), wxDefaultPosition, wxSize(BUTTON_WIDTH, -1)); + sizer2->Add(refreshButton, 0, wxALL, BORDER_SIZE); + + wxStaticText* dummy1Label = new wxStaticText(this, -1, wxEmptyString); + sizer2->Add(dummy1Label, 0, wxALL, BORDER_SIZE); + + wxStaticText* dummy2Label = new wxStaticText(this, -1, wxEmptyString); + sizer2->Add(dummy2Label, 0, wxALL, BORDER_SIZE); + + wxBoxSizer* sizer3 = new wxBoxSizer(wxHORIZONTAL); + + m_reflector = new wxChoice(this, -1, wxDefaultPosition, wxSize(REFLECTOR_WIDTH, -1)); + m_reflector->Append(_("None")); + + wxFileName fileName1(wxFileName::GetHomeDir(), DPLUS_HOSTS_FILE_NAME); + if (fileName1.IsFileReadable()) { + CHostFile file(fileName1.GetFullPath(), false); + + for (unsigned int i = 0U; i < file.getCount(); i++) + m_reflector->Append(file.getName(i).Trim()); + } + +#if defined(__WINDOWS__) + wxFileName fileName4(::wxGetCwd(), DPLUS_HOSTS_FILE_NAME); +#else + wxFileName fileName4(wxT(DATA_DIR), DPLUS_HOSTS_FILE_NAME); +#endif + if (fileName4.IsFileReadable()) { + CHostFile file(fileName4.GetFullPath(), false); + + for (unsigned int i = 0U; i < file.getCount(); i++) { + wxString name = file.getName(i).Trim(); + if (m_reflector->FindString(name) == wxNOT_FOUND) + m_reflector->Append(name); + } + } + + wxFileName fileName2(wxFileName::GetHomeDir(), DEXTRA_HOSTS_FILE_NAME); + if (fileName2.IsFileReadable()) { + CHostFile file(fileName2.GetFullPath(), false); + + for (unsigned int i = 0U; i < file.getCount(); i++) + m_reflector->Append(file.getName(i).Trim()); + } + +#if defined(__WINDOWS__) + wxFileName fileName5(::wxGetCwd(), DEXTRA_HOSTS_FILE_NAME); +#else + wxFileName fileName5(wxT(DATA_DIR), DEXTRA_HOSTS_FILE_NAME); +#endif + if (fileName5.IsFileReadable()) { + CHostFile file(fileName5.GetFullPath(), false); + + for (unsigned int i = 0U; i < file.getCount(); i++) { + wxString name = file.getName(i).Trim(); + if (m_reflector->FindString(name) == wxNOT_FOUND) + m_reflector->Append(name); + } + } + + wxFileName fileName3(wxFileName::GetHomeDir(), DCS_HOSTS_FILE_NAME); + if (fileName3.IsFileReadable()) { + CHostFile file(fileName3.GetFullPath(), false); + + for (unsigned int i = 0U; i < file.getCount(); i++) + m_reflector->Append(file.getName(i).Trim()); + } + +#if defined(__WINDOWS__) + wxFileName fileName6(::wxGetCwd(), DCS_HOSTS_FILE_NAME); +#else + wxFileName fileName6(wxT(DATA_DIR), DCS_HOSTS_FILE_NAME); +#endif + if (fileName6.IsFileReadable()) { + CHostFile file(fileName6.GetFullPath(), false); + + for (unsigned int i = 0U; i < file.getCount(); i++) { + wxString name = file.getName(i).Trim(); + if (m_reflector->FindString(name) == wxNOT_FOUND) + m_reflector->Append(name); + } + } + + sizer3->Add(m_reflector, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_reflector->SetSelection(0); + + m_channel = new wxChoice(this, -1, wxDefaultPosition, wxSize(CHANNEL_WIDTH, -1)); + m_channel->Append(wxT("A")); + m_channel->Append(wxT("B")); + m_channel->Append(wxT("C")); + m_channel->Append(wxT("D")); + m_channel->Append(wxT("E")); + m_channel->Append(wxT("F")); + m_channel->Append(wxT("G")); + m_channel->Append(wxT("H")); + m_channel->Append(wxT("I")); + m_channel->Append(wxT("J")); + m_channel->Append(wxT("K")); + m_channel->Append(wxT("L")); + m_channel->Append(wxT("M")); + m_channel->Append(wxT("N")); + m_channel->Append(wxT("O")); + m_channel->Append(wxT("P")); + m_channel->Append(wxT("Q")); + m_channel->Append(wxT("R")); + m_channel->Append(wxT("S")); + m_channel->Append(wxT("T")); + m_channel->Append(wxT("U")); + m_channel->Append(wxT("V")); + m_channel->Append(wxT("W")); + m_channel->Append(wxT("X")); + m_channel->Append(wxT("Y")); + m_channel->Append(wxT("Z")); + sizer3->Add(m_channel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_channel->SetSelection(0); + + sizer2->Add(sizer3); + + m_reconnect = new wxChoice(this, -1, wxDefaultPosition, wxSize(RECONNECT_WIDTH, -1)); + m_reconnect->Append(_("Never")); + m_reconnect->Append(_("Fixed")); + m_reconnect->Append(_("5 minutes")); + m_reconnect->Append(_("10 minutes")); + m_reconnect->Append(_("15 minutes")); + m_reconnect->Append(_("20 minutes")); + m_reconnect->Append(_("25 minutes")); + m_reconnect->Append(_("30 minutes")); + m_reconnect->Append(_("60 minutes")); + m_reconnect->Append(_("90 minutes")); + m_reconnect->Append(_("120 minutes")); + m_reconnect->Append(_("180 minutes")); + sizer2->Add(m_reconnect, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_reconnect->SetSelection(0); + + wxStaticText* dummy3Label = new wxStaticText(this, -1, wxEmptyString); + sizer2->Add(dummy3Label, 0, wxALL, BORDER_SIZE); + + wxButton* linkButton = new wxButton(this, Button_Link, _("Link"), wxDefaultPosition, wxSize(BUTTON_WIDTH, -1)); + sizer2->Add(linkButton, 0, wxALL, BORDER_SIZE); + + m_unlink = new wxButton(this, Button_Unlink, _("Unlink"), wxDefaultPosition, wxSize(BUTTON_WIDTH, -1)); + sizer2->Add(m_unlink, 0, wxALL, BORDER_SIZE); + m_unlink->Disable(); + + sizer1->Add(sizer2); + + SetAutoLayout(true); + + SetSizer(sizer1); +} + + +CRemoteControlRepeaterPanel::~CRemoteControlRepeaterPanel() +{ +} + +void CRemoteControlRepeaterPanel::add(const CRemoteControlRepeaterData& data) +{ + m_list->DeleteAllItems(); + m_unlink->Disable(); + m_reflectors.Clear(); + m_protocols.Clear(); + + RECONNECT reconnect = data.getReconnect(); + m_reconnect->SetSelection(int(reconnect)); + + wxString reflector = data.getReflector(); + reflector.Append(wxT(" ")); + reflector.Truncate(LONG_CALLSIGN_LENGTH); + + bool res = m_channel->SetStringSelection(reflector.Right(1)); + if (!res) + m_channel->SetSelection(0); + + reflector.Truncate(LONG_CALLSIGN_LENGTH - 1U); + reflector.Append(wxT(" ")); + + res = m_reflector->SetStringSelection(reflector); + if (!res) + m_reflector->SetSelection(0); + + unsigned int count = data.getLinkCount(); + for (unsigned int i = 0U; i < count; i++) { + CRemoteControlLinkData& link = data.getLink(i); + + wxString callsign = link.getCallsign(); + PROTOCOL protocol = link.getProtocol(); + bool isLinked = link.isLinked(); + DIRECTION direction = link.getDirection(); + bool isDongle = link.isDongle(); + + m_reflectors.Add(callsign); + m_protocols.Add(protocol); + + m_list->InsertItem(0L, callsign); + + // To allow for sorting + m_list->SetItemData(0L, long(protocol)); + + switch (protocol) { + case PROTO_DEXTRA: + m_list->SetItem(0L, 1, wxT("DExtra")); + break; + case PROTO_DPLUS: + m_list->SetItem(0L, 1, wxT("D-Plus")); + break; + case PROTO_DCS: + m_list->SetItem(0L, 1, wxT("DCS")); + break; + case PROTO_CCS: + m_list->SetItem(0L, 1, wxT("CCS")); + break; + default: + m_list->SetItem(0L, 1, wxT("?????")); + break; + } + + switch (direction){ + case DIR_INCOMING: + m_list->SetItem(0L, 2, _("IN")); + break; + case DIR_OUTGOING: + m_list->SetItem(0L, 2, _("OUT")); + break; + default: + m_list->SetItem(0L, 2, wxT("???")); + break; + } + + if (isDongle) + m_list->SetItem(0L, 3, _("Dongle")); + else + m_list->SetItem(0L, 3, _("Repeater")); + + if (isLinked) + m_list->SetItem(0L, 4, _("Linked")); + else + m_list->SetItem(0L, 4, _("Linking")); + } +} + +void CRemoteControlRepeaterPanel::onRefresh(wxCommandEvent&) +{ + ::wxGetApp().repeaterRefresh(m_callsign); +} + +void CRemoteControlRepeaterPanel::onLink(wxCommandEvent&) +{ + int n = m_reconnect->GetCurrentSelection(); + if (n == wxNOT_FOUND) + return; + + RECONNECT reconnect = RECONNECT(n); + + n = m_reflector->GetCurrentSelection(); + if (n == wxNOT_FOUND) + return; + + wxString reflector = wxEmptyString; + + if (n > 0) { + n = m_channel->GetCurrentSelection(); + if (n == wxNOT_FOUND) + return; + + reflector = m_reflector->GetStringSelection(); + if (reflector.IsEmpty()) + return; + + wxString channel = m_channel->GetStringSelection(); + if (channel.IsEmpty()) + return; + + reflector.Append(wxT(" ")); + reflector.Truncate(LONG_CALLSIGN_LENGTH - 1U); + reflector.Append(channel); + } + + ::wxGetApp().link(m_callsign, reconnect, reflector); +} + +void CRemoteControlRepeaterPanel::onUnlink(wxCommandEvent&) +{ + if (m_selected == -1) + return; + + wxString reflector = m_reflectors.Item(m_selected); + PROTOCOL protocol = m_protocols.Item(m_selected); + + m_selected = -1; + m_unlink->Disable(); + + ::wxGetApp().unlink(m_callsign, protocol, reflector); +} + +void CRemoteControlRepeaterPanel::onSelect(wxListEvent& event) +{ + m_selected = event.GetSelection(); + + m_unlink->Enable(); +} diff --git a/RemoteControl/RemoteControlRepeaterPanel.h b/RemoteControl/RemoteControlRepeaterPanel.h new file mode 100644 index 0000000..dbe48e6 --- /dev/null +++ b/RemoteControl/RemoteControlRepeaterPanel.h @@ -0,0 +1,55 @@ +/* + * 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 + * 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 RemoteControlRepeaterPanel_H +#define RemoteControlRepeaterPanel_H + +#include "RemoteControlRepeaterData.h" + +#include +#include + +WX_DEFINE_ARRAY_INT(PROTOCOL, CArrayProtocol); + +class CRemoteControlRepeaterPanel : public wxPanel { +public: + CRemoteControlRepeaterPanel(wxWindow* parent, int id, const wxString& callsign); + virtual ~CRemoteControlRepeaterPanel(); + + virtual void add(const CRemoteControlRepeaterData& data); + + virtual void onRefresh(wxCommandEvent& event); + virtual void onLink(wxCommandEvent& event); + virtual void onUnlink(wxCommandEvent& event); + virtual void onSelect(wxListEvent& event); + +private: + wxString m_callsign; + wxListCtrl* m_list; + wxChoice* m_reflector; + wxChoice* m_channel; + wxChoice* m_reconnect; + wxButton* m_unlink; + int m_selected; + wxArrayString m_reflectors; + CArrayProtocol m_protocols; + + DECLARE_EVENT_TABLE() +}; + +#endif diff --git a/RemoteControl/RemoteControlStarNetGroup.cpp b/RemoteControl/RemoteControlStarNetGroup.cpp new file mode 100644 index 0000000..3247654 --- /dev/null +++ b/RemoteControl/RemoteControlStarNetGroup.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2011 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 "RemoteControlStarNetGroup.h" + +#include +WX_DEFINE_OBJARRAY(CRemoteUserData_t); + +CRemoteControlStarNetGroup::CRemoteControlStarNetGroup(const wxString& callsign, const wxString& logoff, wxUint32 timer, wxUint32 timeout) : +m_callsign(callsign), +m_logoff(logoff), +m_timer((unsigned int)timer), +m_timeout((unsigned int)timeout), +m_users() +{ + if (m_logoff.IsSameAs(wxT(" "))) + m_logoff.Clear(); +} + +CRemoteControlStarNetGroup::~CRemoteControlStarNetGroup() +{ + m_users.Clear(); +} + +void CRemoteControlStarNetGroup::addUser(const wxString& callsign, wxUint32 timer, wxUint32 timeout) +{ + CRemoteControlStarNetUser user(callsign, timer, timeout); + + m_users.Add(user); +} + +wxString CRemoteControlStarNetGroup::getCallsign() const +{ + return m_callsign; +} + +wxString CRemoteControlStarNetGroup::getLogoff() const +{ + return m_logoff; +} + +unsigned int CRemoteControlStarNetGroup::getTimer() const +{ + return m_timer; +} + +unsigned int CRemoteControlStarNetGroup::getTimeout() const +{ + return m_timeout; +} + +unsigned int CRemoteControlStarNetGroup::getUserCount() const +{ + return m_users.GetCount(); +} + +CRemoteControlStarNetUser& CRemoteControlStarNetGroup::getUser(unsigned int n) const +{ + return m_users.Item(n); +} diff --git a/RemoteControl/RemoteControlStarNetGroup.h b/RemoteControl/RemoteControlStarNetGroup.h new file mode 100644 index 0000000..ea1e380 --- /dev/null +++ b/RemoteControl/RemoteControlStarNetGroup.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2011 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 RemoteControlStarNetGroup_H +#define RemoteControlStarNetGroup_H + +#include "RemoteControlStarNetUser.h" + +#include + +#include +WX_DECLARE_OBJARRAY(CRemoteControlStarNetUser, CRemoteUserData_t); + +class CRemoteControlStarNetGroup { +public: + CRemoteControlStarNetGroup(const wxString& callsign, const wxString& logoff, wxUint32 timer, wxUint32 timeout); + ~CRemoteControlStarNetGroup(); + + void addUser(const wxString& callsign, wxUint32 timer, wxUint32 timeout); + + wxString getCallsign() const; + wxString getLogoff() const; + unsigned int getTimer() const; + unsigned int getTimeout() const; + + unsigned int getUserCount() const; + CRemoteControlStarNetUser& getUser(unsigned int n) const; + +private: + wxString m_callsign; + wxString m_logoff; + unsigned int m_timer; + unsigned int m_timeout; + CRemoteUserData_t m_users; +}; + +#endif diff --git a/RemoteControl/RemoteControlStarNetPanel.cpp b/RemoteControl/RemoteControlStarNetPanel.cpp new file mode 100644 index 0000000..7efe0e6 --- /dev/null +++ b/RemoteControl/RemoteControlStarNetPanel.cpp @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2011 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 "RemoteControlStarNetPanel.h" +#include "RemoteControlApp.h" + +enum { + Button_Refresh = 7100, + Button_LogoffAll, + List_StarNets, + Menu_Logoff +}; + +BEGIN_EVENT_TABLE(CRemoteControlStarNetPanel, wxPanel) + EVT_BUTTON(Button_Refresh, CRemoteControlStarNetPanel::onRefresh) + EVT_BUTTON(Button_LogoffAll, CRemoteControlStarNetPanel::onLogoffAll) + EVT_LIST_ITEM_RIGHT_CLICK(List_StarNets, CRemoteControlStarNetPanel::onClick) + EVT_MENU(Menu_Logoff, CRemoteControlStarNetPanel::onLogoff) +END_EVENT_TABLE() + +#if defined(__WINDOWS__) +const unsigned int LIST_HEIGHT = 350U; +const unsigned int LIST_WIDTH = 350U; +const unsigned int CALLSIGN_WIDTH = 100U; +const unsigned int TIMER_WIDTH = 145U; +const unsigned int BUTTON_WIDTH = 75U; +#else +const unsigned int LIST_HEIGHT = 350U; +const unsigned int LIST_WIDTH = 350U; +const unsigned int CALLSIGN_WIDTH = 100U; +const unsigned int TIMER_WIDTH = 145U; +const unsigned int BUTTON_WIDTH = 75U; +#endif + +const unsigned int BORDER_SIZE = 5U; + +CRemoteControlStarNetPanel::CRemoteControlStarNetPanel(wxWindow* parent, int id, const wxString& callsign) : +wxPanel(parent, id), +m_callsign(callsign), +m_list(NULL), +m_logoff(NULL), +m_timer(NULL), +m_logoffAll(NULL), +m_chosen(), +m_menu(NULL) +{ + wxBoxSizer* sizer1 = new wxBoxSizer(wxHORIZONTAL); + + m_list = new wxListCtrl(this, List_StarNets, wxDefaultPosition, wxSize(LIST_WIDTH, LIST_HEIGHT), wxLC_REPORT | wxLC_SINGLE_SEL); + m_list->InsertColumn(0L, _("Callsign")); + m_list->SetColumnWidth(0L, CALLSIGN_WIDTH); + m_list->InsertColumn(1L, _("User Timer")); + m_list->SetColumnWidth(1L, TIMER_WIDTH); + sizer1->Add(m_list, 0, wxTOP | wxBOTTOM | wxLEFT | wxEXPAND, BORDER_SIZE); + + wxBoxSizer* sizer2 = new wxBoxSizer(wxVERTICAL); + + wxButton* refreshButton = new wxButton(this, Button_Refresh, _("Refresh"), wxDefaultPosition, wxSize(BUTTON_WIDTH, -1)); + sizer2->Add(refreshButton, 0, wxALL, BORDER_SIZE); + + wxStaticText* dummy1Label = new wxStaticText(this, -1, wxEmptyString); + sizer2->Add(dummy1Label, 0, wxALL, BORDER_SIZE); + + wxStaticText* logoffLabel = new wxStaticText(this, -1, _("Logoff Callsign")); + sizer2->Add(logoffLabel, 0, wxALL, BORDER_SIZE); + + m_logoff = new wxStaticText(this, -1, wxEmptyString); + sizer2->Add(m_logoff, 0, wxALL, BORDER_SIZE); + + wxStaticText* dummy3Label = new wxStaticText(this, -1, wxEmptyString); + sizer2->Add(dummy3Label, 0, wxALL, BORDER_SIZE); + + wxStaticText* timerLabel = new wxStaticText(this, -1, _("Group Timer")); + sizer2->Add(timerLabel, 0, wxALL, BORDER_SIZE); + + m_timer = new wxStaticText(this, -1, wxEmptyString); + sizer2->Add(m_timer, 0, wxALL, BORDER_SIZE); + + wxStaticText* dummy5Label = new wxStaticText(this, -1, wxEmptyString); + sizer2->Add(dummy5Label, 0, wxALL, BORDER_SIZE); + + m_logoffAll = new wxButton(this, Button_LogoffAll, _("Logoff All"), wxDefaultPosition, wxSize(BUTTON_WIDTH, -1)); + m_logoffAll->Disable(); + sizer2->Add(m_logoffAll, 0, wxALL, BORDER_SIZE); + + sizer1->Add(sizer2); + + SetAutoLayout(true); + + SetSizer(sizer1); + + m_menu = new wxMenu; + m_menu->Append(Menu_Logoff, _("Logoff")); +} + + +CRemoteControlStarNetPanel::~CRemoteControlStarNetPanel() +{ +} + +void CRemoteControlStarNetPanel::add(const CRemoteControlStarNetGroup& data) +{ + m_list->DeleteAllItems(); + + wxString logoff = data.getLogoff(); + if (logoff.IsEmpty()) + m_logoff->SetLabel(_("None")); + else + m_logoff->SetLabel(logoff); + + unsigned int timer = data.getTimer(); + unsigned int timeout = data.getTimeout(); + + if (timeout == 0U) { + m_timer->SetLabel(_("None")); + } else { + wxString text; + text.Printf(wxT("%u/%u"), timer, timeout); + m_timer->SetLabel(text); + } + + unsigned int count = data.getUserCount(); + for (unsigned int i = 0U; i < count; i++) { + CRemoteControlStarNetUser& user = data.getUser(i); + + wxString callsign = user.getCallsign(); + timer = user.getTimer(); + timeout = user.getTimeout(); + + m_list->InsertItem(0L, callsign); + + if (timeout == 0U) { + m_list->SetItem(0L, 1, _("None")); + } else { + wxString text; + text.Printf(wxT("%u/%u"), timer, timeout); + m_list->SetItem(0L, 1, text); + } + } + + if (count > 0U) + m_logoffAll->Enable(); + else + m_logoffAll->Disable(); +} + +void CRemoteControlStarNetPanel::onClick(wxListEvent& event) +{ + long n = event.GetIndex(); + if (n < 0) { + m_chosen.Clear(); + return; + } + + m_chosen = m_list->GetItemText(n); + + PopupMenu(m_menu); +} + +void CRemoteControlStarNetPanel::onRefresh(wxCommandEvent&) +{ + ::wxGetApp().starNetRefresh(m_callsign); +} + +void CRemoteControlStarNetPanel::onLogoff(wxCommandEvent&) +{ + ::wxGetApp().starNetLogoff(m_callsign, m_chosen); +} + +void CRemoteControlStarNetPanel::onLogoffAll(wxCommandEvent&) +{ + ::wxGetApp().starNetLogoff(m_callsign, wxT("ALL ")); +} diff --git a/RemoteControl/RemoteControlStarNetPanel.h b/RemoteControl/RemoteControlStarNetPanel.h new file mode 100644 index 0000000..e9cc682 --- /dev/null +++ b/RemoteControl/RemoteControlStarNetPanel.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2011 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 RemoteControlStarNetPanel_H +#define RemoteControlStarNetPanel_H + +#include "RemoteControlStarNetGroup.h" + +#include +#include + +class CRemoteControlStarNetPanel : public wxPanel { +public: + CRemoteControlStarNetPanel(wxWindow* parent, int id, const wxString& callsign); + virtual ~CRemoteControlStarNetPanel(); + + virtual void add(const CRemoteControlStarNetGroup& data); + + virtual void onClick(wxListEvent& event); + virtual void onRefresh(wxCommandEvent& event); + virtual void onLogoff(wxCommandEvent& event); + virtual void onLogoffAll(wxCommandEvent& event); + +private: + wxString m_callsign; + wxListCtrl* m_list; + wxStaticText* m_logoff; + wxStaticText* m_timer; + wxButton* m_logoffAll; + wxString m_chosen; + wxMenu* m_menu; + + DECLARE_EVENT_TABLE() +}; + +#endif diff --git a/RemoteControl/RemoteControlStarNetUser.cpp b/RemoteControl/RemoteControlStarNetUser.cpp new file mode 100644 index 0000000..545fd2e --- /dev/null +++ b/RemoteControl/RemoteControlStarNetUser.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2011 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 "RemoteControlStarNetUser.h" + +CRemoteControlStarNetUser::CRemoteControlStarNetUser(const wxString& callsign, wxUint32 timer, wxUint32 timeout) : +m_callsign(callsign), +m_timer((unsigned int)timer), +m_timeout((unsigned int)timeout) +{ +} + +CRemoteControlStarNetUser::~CRemoteControlStarNetUser() +{ +} + +wxString CRemoteControlStarNetUser::getCallsign() const +{ + return m_callsign; +} + +unsigned int CRemoteControlStarNetUser::getTimer() const +{ + return m_timer; +} + +unsigned int CRemoteControlStarNetUser::getTimeout() const +{ + return m_timeout; +} diff --git a/RemoteControl/RemoteControlStarNetUser.h b/RemoteControl/RemoteControlStarNetUser.h new file mode 100644 index 0000000..a066eda --- /dev/null +++ b/RemoteControl/RemoteControlStarNetUser.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2011 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 RemoteControlStarNetUser_H +#define RemoteControlStarNetUser_H + +#include + +class CRemoteControlStarNetUser { +public: + CRemoteControlStarNetUser(const wxString& callsign, wxUint32 timer, wxUint32 timeout); + ~CRemoteControlStarNetUser(); + + wxString getCallsign() const; + unsigned int getTimer() const; + unsigned int getTimeout() const; + +private: + wxString m_callsign; + unsigned int m_timer; + unsigned int m_timeout; +}; + +#endif diff --git a/StarNetServer/StarNetServer.vcxproj b/StarNetServer/StarNetServer.vcxproj new file mode 100644 index 0000000..4c7a44c --- /dev/null +++ b/StarNetServer/StarNetServer.vcxproj @@ -0,0 +1,212 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {4E9989C8-80D4-4F6E-BCA1-754DCED3AF5F} + StarNetServer + Win32Proj + 10.0.16299.0 + + + + Application + v141 + Unicode + true + + + Application + v141 + Unicode + true + + + Application + v141 + Unicode + + + Application + v141 + Unicode + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>14.0.24720.0 + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + $(WXWIN)\include;$(WXWIN)\lib\vc_dll\mswud;$(IncludePath) + + + true + $(WXWIN)\include;$(WXWIN)\lib\vc_x64_dll\mswud;$(IncludePath) + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + + + false + + + + Disabled + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;../GUICommon;../ircDDB;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;DCS_LINK;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + Level4 + EditAndContinue + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_dll;%(AdditionalLibraryDirectories) + true + Windows + MachineX86 + + + + + Disabled + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;../GUICommon;../ircDDB;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;DCS_LINK;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + + + Level4 + ProgramDatabase + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_x64_dll;%(AdditionalLibraryDirectories) + true + Windows + + + + + MaxSpeed + true + $(WXWIN)\include\msvc;$(WXWIN)\include;../ircDDB;../Common;../GUICommon;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + Level3 + ProgramDatabase + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_dll;%(AdditionalLibraryDirectories) + true + Windows + true + true + MachineX86 + + + + + MaxSpeed + true + $(WXWIN)\include\msvc;$(WXWIN)\include;../ircDDB;../Common;../GUICommon;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_x64_dll;%(AdditionalLibraryDirectories) + true + Windows + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {e793cb8e-2ac9-431a-bbfc-3f52537bb3cf} + false + + + {02d03515-0bbe-4553-8c40-566a597478f8} + false + + + {276bc54d-5581-4a0c-afd5-a5bdc947f0ad} + false + + + + + + \ No newline at end of file diff --git a/StarNetServer/StarNetServer.vcxproj.filters b/StarNetServer/StarNetServer.vcxproj.filters new file mode 100644 index 0000000..1cd5368 --- /dev/null +++ b/StarNetServer/StarNetServer.vcxproj.filters @@ -0,0 +1,80 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/StarNetServer/StarNetServerApp.cpp b/StarNetServer/StarNetServerApp.cpp new file mode 100644 index 0000000..84f48bf --- /dev/null +++ b/StarNetServer/StarNetServerApp.cpp @@ -0,0 +1,947 @@ +/* + * Copyright (C) 2010,2011,2012,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 + * 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 "StarNetServerLogRedirect.h" +#include "StarNetServerThread.h" +#include "StarNetServerDefs.h" +#include "StarNetServerApp.h" +#include "Version.h" +#include "Logger.h" +#include "IRCDDBClient.h" + +#include +#include +#include + +IMPLEMENT_APP(CStarNetServerApp) + +const wxChar* NOLOGGING_SWITCH = wxT("nolog"); +const wxChar* GUI_SWITCH = wxT("gui"); +const wxChar* LOGDIR_OPTION = wxT("logdir"); +const wxChar* CONFDIR_OPTION = wxT("confdir"); + +CStarNetServerApp::CStarNetServerApp() : +wxApp(), +m_nolog(false), +m_gui(false), +m_logDir(), +m_confDir(), +m_frame(NULL), +m_thread(NULL), +m_config(NULL), +m_logChain(NULL) +{ +} + +CStarNetServerApp::~CStarNetServerApp() +{ +} + +bool CStarNetServerApp::OnInit() +{ +#if defined(__WINDOWS__) + WSAData data; + int wsaRet = ::WSAStartup(MAKEWORD(2, 2), &data); + if (wsaRet != 0) { + wxLogError(wxT("Error from WSAStartup")); + return false; + } +#endif + + SetVendorName(VENDOR_NAME); + + if (!wxApp::OnInit()) + return false; + + if (!m_nolog) { +#if defined(__WINDOWS__) + if (m_logDir.IsEmpty()) + m_logDir = ::wxGetHomeDir(); +#else + if (m_logDir.IsEmpty()) + m_logDir = wxT(LOG_DIR); +#endif + + wxLog* log = new CLogger(m_logDir, LOG_BASE_NAME); + wxLog::SetActiveTarget(log); + } else { + new wxLogNull; + } + + m_logChain = new wxLogChain(new CStarNetServerLogRedirect); + +#if defined(__WINDOWS__) + if (m_confDir.IsEmpty()) + m_confDir = wxGetHomeDir(); + + m_config = new CStarNetServerConfig(new wxConfig(APPLICATION_NAME), m_confDir); +#else + if (m_confDir.IsEmpty()) + m_confDir = wxT(CONF_DIR); + + m_config = new CStarNetServerConfig(m_confDir); +#endif + + wxPoint position = wxDefaultPosition; + + int x, y; + getPosition(x, y); + if (x >= 0 && y >= 0) + position = wxPoint(x, y); + + m_frame = new CStarNetServerFrame(APPLICATION_NAME + wxT(" - ") + VERSION, position, m_gui); + m_frame->Show(); + + SetTopWindow(m_frame); + + wxLogInfo(wxT("Starting ") + APPLICATION_NAME + wxT(" - ") + VERSION); + + // Log the version of wxWidgets and the Operating System + wxLogInfo(wxT("Using wxWidgets %d.%d.%d on %s"), wxMAJOR_VERSION, wxMINOR_VERSION, wxRELEASE_NUMBER, ::wxGetOsDescription().c_str()); + + createThread(); + + return true; +} + +int CStarNetServerApp::OnExit() +{ + m_logChain->SetLog(NULL); + + wxLogInfo(APPLICATION_NAME + wxT(" is exiting")); + + m_thread->kill(); + + delete m_config; + +#if defined(__WINDOWS__) + ::WSACleanup(); +#endif + + return 0; +} + +void CStarNetServerApp::OnInitCmdLine(wxCmdLineParser& parser) +{ + parser.AddSwitch(NOLOGGING_SWITCH, wxEmptyString, wxEmptyString, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddSwitch(GUI_SWITCH, wxEmptyString, wxEmptyString, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption(LOGDIR_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption(CONFDIR_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + + wxApp::OnInitCmdLine(parser); +} + +bool CStarNetServerApp::OnCmdLineParsed(wxCmdLineParser& parser) +{ + if (!wxApp::OnCmdLineParsed(parser)) + return false; + + m_nolog = parser.Found(NOLOGGING_SWITCH); + m_gui = parser.Found(GUI_SWITCH); + + wxString logDir; + bool found = parser.Found(LOGDIR_OPTION, &logDir); + if (found) + m_logDir = logDir; + + wxString confDir; + found = parser.Found(CONFDIR_OPTION, &confDir); + if (found) + m_confDir = confDir; + + return true; +} + +#if defined(__WXDEBUG__) +void CStarNetServerApp::OnAssertFailure(const wxChar* file, int line, const wxChar* func, const wxChar* cond, const wxChar* msg) +{ + wxLogFatalError(wxT("Assertion failed on line %d in file %s and function %s: %s %s"), line, file, func, cond, msg); +} +#endif + +void CStarNetServerApp::showLog(const wxString& text) +{ + m_frame->showLog(text); +} + +void CStarNetServerApp::getGateway(wxString& callsign, wxString& address) const +{ + m_config->getGateway(callsign, address); +} + +void CStarNetServerApp::setGateway(const wxString& callsign, const wxString& address) +{ + m_config->setGateway(callsign, address); +} + +void CStarNetServerApp::getIrcDDB(wxString& hostname, wxString& username, wxString& password) const +{ + m_config->getIrcDDB(hostname, username, password); +} + +void CStarNetServerApp::setIrcDDB(const wxString& hostname, const wxString& username, const wxString& password) +{ + m_config->setIrcDDB(hostname, username, password); +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetServerApp::getStarNet1(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +{ + m_config->getStarNet1(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); +} + +void CStarNetServerApp::setStarNet1(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +{ + m_config->setStarNet1(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); +} + +void CStarNetServerApp::getStarNet2(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +{ + m_config->getStarNet2(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); +} + +void CStarNetServerApp::setStarNet2(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +{ + m_config->setStarNet2(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); +} + +void CStarNetServerApp::getStarNet3(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +{ + m_config->getStarNet3(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); +} + +void CStarNetServerApp::setStarNet3(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +{ + m_config->setStarNet3(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); +} + +void CStarNetServerApp::getStarNet4(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +{ + m_config->getStarNet4(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); +} + +void CStarNetServerApp::setStarNet4(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +{ + m_config->setStarNet4(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); +} + +void CStarNetServerApp::getStarNet5(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +{ + m_config->getStarNet5(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); +} + +void CStarNetServerApp::setStarNet5(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +{ + m_config->setStarNet5(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); +} + +void CStarNetServerApp::getStarNet6(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +{ + m_config->getStarNet6(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); +} + +void CStarNetServerApp::setStarNet6(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +{ + m_config->setStarNet6(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); +} + +void CStarNetServerApp::getStarNet7(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +{ + m_config->getStarNet7(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); +} + +void CStarNetServerApp::setStarNet7(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +{ + m_config->setStarNet7(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); +} + +void CStarNetServerApp::getStarNet8(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +{ + m_config->getStarNet8(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); +} + +void CStarNetServerApp::setStarNet8(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +{ + m_config->setStarNet8(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); +} + +void CStarNetServerApp::getStarNet9(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +{ + m_config->getStarNet9(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); +} + +void CStarNetServerApp::setStarNet9(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +{ + m_config->setStarNet9(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); +} + +void CStarNetServerApp::getStarNet10(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +{ + m_config->getStarNet10(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); +} + +void CStarNetServerApp::setStarNet10(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +{ + m_config->setStarNet10(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); +} + +void CStarNetServerApp::getStarNet11(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +{ + m_config->getStarNet11(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); +} + +void CStarNetServerApp::setStarNet11(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +{ + m_config->setStarNet11(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); +} + +void CStarNetServerApp::getStarNet12(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +{ + m_config->getStarNet12(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); +} + +void CStarNetServerApp::setStarNet12(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +{ + m_config->setStarNet12(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); +} + +void CStarNetServerApp::getStarNet13(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +{ + m_config->getStarNet13(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); +} + +void CStarNetServerApp::setStarNet13(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +{ + m_config->setStarNet13(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); +} + +void CStarNetServerApp::getStarNet14(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +{ + m_config->getStarNet14(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); +} + +void CStarNetServerApp::setStarNet14(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +{ + m_config->setStarNet14(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); +} + +void CStarNetServerApp::getStarNet15(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +{ + m_config->getStarNet15(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); +} + +void CStarNetServerApp::setStarNet15(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +{ + m_config->setStarNet15(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); +} +#else +void CStarNetServerApp::getStarNet1(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +{ + m_config->getStarNet1(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); +} + +void CStarNetServerApp::setStarNet1(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +{ + m_config->setStarNet1(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); +} + +void CStarNetServerApp::getStarNet2(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +{ + m_config->getStarNet2(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); +} + +void CStarNetServerApp::setStarNet2(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +{ + m_config->setStarNet2(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); +} + +void CStarNetServerApp::getStarNet3(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +{ + m_config->getStarNet3(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); +} + +void CStarNetServerApp::setStarNet3(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +{ + m_config->setStarNet3(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); +} + +void CStarNetServerApp::getStarNet4(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +{ + m_config->getStarNet4(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); +} + +void CStarNetServerApp::setStarNet4(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +{ + m_config->setStarNet4(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); +} + +void CStarNetServerApp::getStarNet5(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +{ + m_config->getStarNet5(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); +} + +void CStarNetServerApp::setStarNet5(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +{ + m_config->setStarNet5(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); +} + +void CStarNetServerApp::getStarNet6(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +{ + m_config->getStarNet6(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); +} + +void CStarNetServerApp::setStarNet6(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +{ + m_config->setStarNet6(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); +} + +void CStarNetServerApp::getStarNet7(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +{ + m_config->getStarNet7(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); +} + +void CStarNetServerApp::setStarNet7(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +{ + m_config->setStarNet7(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); +} + +void CStarNetServerApp::getStarNet8(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +{ + m_config->getStarNet8(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); +} + +void CStarNetServerApp::setStarNet8(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +{ + m_config->setStarNet8(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); +} + +void CStarNetServerApp::getStarNet9(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +{ + m_config->getStarNet9(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); +} + +void CStarNetServerApp::setStarNet9(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +{ + m_config->setStarNet9(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); +} + +void CStarNetServerApp::getStarNet10(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +{ + m_config->getStarNet10(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); +} + +void CStarNetServerApp::setStarNet10(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +{ + m_config->setStarNet10(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); +} + +void CStarNetServerApp::getStarNet11(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +{ + m_config->getStarNet11(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); +} + +void CStarNetServerApp::setStarNet11(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +{ + m_config->setStarNet11(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); +} + +void CStarNetServerApp::getStarNet12(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +{ + m_config->getStarNet12(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); +} + +void CStarNetServerApp::setStarNet12(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +{ + m_config->setStarNet12(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); +} + +void CStarNetServerApp::getStarNet13(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +{ + m_config->getStarNet13(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); +} + +void CStarNetServerApp::setStarNet13(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +{ + m_config->setStarNet13(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); +} + +void CStarNetServerApp::getStarNet14(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +{ + m_config->getStarNet14(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); +} + +void CStarNetServerApp::setStarNet14(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +{ + m_config->setStarNet14(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); +} + +void CStarNetServerApp::getStarNet15(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +{ + m_config->getStarNet15(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); +} + +void CStarNetServerApp::setStarNet15(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +{ + m_config->setStarNet15(band, callsign, logoff, info, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); +} +#endif + +void CStarNetServerApp::getRemote(bool& enabled, wxString& password, unsigned int& port) const +{ + m_config->getRemote(enabled, password, port); +} + +void CStarNetServerApp::setRemote(bool enabled, const wxString& password, unsigned int port) +{ + m_config->setRemote(enabled, password, port); +} + +void CStarNetServerApp::getMiscellaneous(bool& logEnabled) const +{ + m_config->getMiscellaneous(logEnabled); +} + +void CStarNetServerApp::setMiscellaneous(bool logEnabled) +{ + m_config->setMiscellaneous(logEnabled); +} + +void CStarNetServerApp::getPosition(int& x, int& y) const +{ + m_config->getPosition(x, y); +} + +void CStarNetServerApp::setPosition(int x, int y) +{ + m_config->setPosition(x, y); +} + +bool CStarNetServerApp::writeConfig() +{ + return m_config->write(); +} + +void CStarNetServerApp::createThread() +{ + CStarNetServerThread* thread = new CStarNetServerThread(m_nolog, m_logDir); + + wxString callsign, address; + getGateway(callsign, address); + + callsign.Append(wxT(" ")); + callsign.Truncate(LONG_CALLSIGN_LENGTH - 1U); + callsign.Append(wxT("G")); + + wxLogInfo(wxT("Gateway callsign set to %s, local address set to %s"), callsign.c_str(), address.c_str()); + + bool logEnabled; + getMiscellaneous(logEnabled); + wxLogInfo(wxT("Log enabled set to %d"), int(logEnabled)); + + wxString hostname, username, password; + getIrcDDB(hostname, username, password); + wxLogInfo(wxT("ircDDB host set to %s, username set to %s"), hostname.c_str(), username.c_str()); + + if (!hostname.IsEmpty() && !username.IsEmpty()) { +#if defined(__WINDOWS__) + CIRCDDB* ircDDB = new CIRCDDBClient(hostname, 9007U, username, password, wxT("win_") + LOG_BASE_NAME + wxT("-") + VERSION, address); +#else + CIRCDDB* ircDDB = new CIRCDDBClient(hostname, 9007U, username, password, wxT("linux_") + LOG_BASE_NAME + wxT("-") + VERSION, address); +#endif + bool res = ircDDB->open(); + if (!res) + wxLogError(wxT("Cannot initialise the ircDDB protocol handler")); + else + thread->setIRC(ircDDB); + } + + wxString starNetBand1, starNetCallsign1, starNetLogoff1, starNetInfo1, starNetPermanent1, starNetLink1; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout1, starNetGroupTimeout1; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch1; + bool starNetTXMsgSwitch1; + getStarNet1(starNetBand1, starNetCallsign1, starNetLogoff1, starNetInfo1, starNetPermanent1, starNetUserTimeout1, starNetGroupTimeout1, starNetCallsignSwitch1, starNetTXMsgSwitch1 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,starNetLink1 +#endif +); + + if (!starNetCallsign1.IsEmpty() && !starNetCallsign1.IsSameAs(wxT(" "))) { + wxString repeater = callsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand1); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + thread->addStarNet(starNetCallsign1, starNetLogoff1, repeater, starNetInfo1, starNetPermanent1, starNetUserTimeout1, starNetGroupTimeout1, starNetCallsignSwitch1, starNetTXMsgSwitch1, starNetLink1); + wxLogInfo(wxT("StarNet 1 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign1.c_str(), starNetLogoff1.c_str(), repeater.c_str(), starNetInfo1.c_str(), starNetPermanent1.c_str(), starNetUserTimeout1, starNetGroupTimeout1, int(starNetCallsignSwitch1), int(starNetTXMsgSwitch1), starNetLink1.c_str()); +#else + thread->addStarNet(starNetCallsign1, starNetLogoff1, repeater, starNetInfo1, starNetPermanent1, starNetUserTimeout1, starNetGroupTimeout1, starNetCallsignSwitch1, starNetTXMsgSwitch1); + wxLogInfo(wxT("StarNet 1 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign1.c_str(), starNetLogoff1.c_str(), repeater.c_str(), starNetInfo1.c_str(), starNetPermanent1.c_str(), starNetUserTimeout1, starNetGroupTimeout1, int(starNetCallsignSwitch1), int(starNetTXMsgSwitch1)); +#endif + } + + wxString starNetBand2, starNetCallsign2, starNetLogoff2, starNetInfo2, starNetPermanent2, starNetLink2; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout2, starNetGroupTimeout2; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch2; + bool starNetTXMsgSwitch2; + getStarNet2(starNetBand2, starNetCallsign2, starNetLogoff2, starNetInfo2, starNetPermanent2, starNetUserTimeout2, starNetGroupTimeout2, starNetCallsignSwitch2, starNetTXMsgSwitch2 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,starNetLink2 +#endif +); + + if (!starNetCallsign2.IsEmpty() && !starNetCallsign2.IsSameAs(wxT(" "))) { + wxString repeater = callsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand2); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + thread->addStarNet(starNetCallsign2, starNetLogoff2, repeater, starNetInfo2, starNetPermanent2, starNetUserTimeout2, starNetGroupTimeout2, starNetCallsignSwitch2, starNetTXMsgSwitch2, starNetLink2); + wxLogInfo(wxT("StarNet 2 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign2.c_str(), starNetLogoff2.c_str(), repeater.c_str(), starNetInfo2.c_str(), starNetPermanent2.c_str(), starNetUserTimeout2, starNetGroupTimeout2, int(starNetCallsignSwitch2), int(starNetTXMsgSwitch2), starNetLink2.c_str()); +#else + thread->addStarNet(starNetCallsign2, starNetLogoff2, repeater, starNetInfo2, starNetPermanent2, starNetUserTimeout2, starNetGroupTimeout2, starNetCallsignSwitch2, starNetTXMsgSwitch2); + wxLogInfo(wxT("StarNet 2 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign2.c_str(), starNetLogoff2.c_str(), repeater.c_str(), starNetInfo2.c_str(), starNetPermanent2.c_str(), starNetUserTimeout2, starNetGroupTimeout2, int(starNetCallsignSwitch2), int(starNetTXMsgSwitch2)); +#endif + } + + wxString starNetBand3, starNetCallsign3, starNetLogoff3, starNetInfo3, starNetPermanent3, starNetLink3; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout3, starNetGroupTimeout3; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch3; + bool starNetTXMsgSwitch3; + getStarNet3(starNetBand3, starNetCallsign3, starNetLogoff3, starNetInfo3, starNetPermanent3, starNetUserTimeout3, starNetGroupTimeout3, starNetCallsignSwitch3, starNetTXMsgSwitch3 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,starNetLink3 +#endif +); + + if (!starNetCallsign3.IsEmpty() && !starNetCallsign3.IsSameAs(wxT(" "))) { + wxString repeater = callsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand3); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + thread->addStarNet(starNetCallsign3, starNetLogoff3, repeater, starNetInfo3, starNetPermanent3, starNetUserTimeout3, starNetGroupTimeout3, starNetCallsignSwitch3, starNetTXMsgSwitch3, starNetLink3); + wxLogInfo(wxT("StarNet 3 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign3.c_str(), starNetLogoff3.c_str(), repeater.c_str(), starNetInfo3.c_str(), starNetPermanent3.c_str(), starNetUserTimeout3, starNetGroupTimeout3, int(starNetCallsignSwitch3), int(starNetTXMsgSwitch3), starNetLink3.c_str()); +#else + thread->addStarNet(starNetCallsign3, starNetLogoff3, repeater, starNetInfo3, starNetPermanent3, starNetUserTimeout3, starNetGroupTimeout3, starNetCallsignSwitch3, starNetTXMsgSwitch3); + wxLogInfo(wxT("StarNet 3 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign3.c_str(), starNetLogoff3.c_str(), repeater.c_str(), starNetInfo3.c_str(), starNetPermanent3.c_str(), starNetUserTimeout3, starNetGroupTimeout3, int(starNetCallsignSwitch3), int(starNetTXMsgSwitch3)); +#endif + } + + wxString starNetBand4, starNetCallsign4, starNetLogoff4, starNetInfo4, starNetPermanent4, starNetLink4; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout4, starNetGroupTimeout4; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch4; + bool starNetTXMsgSwitch4; + getStarNet4(starNetBand4, starNetCallsign4, starNetLogoff4, starNetInfo4, starNetPermanent4, starNetUserTimeout4, starNetGroupTimeout4, starNetCallsignSwitch4, starNetTXMsgSwitch4 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,starNetLink4 +#endif +); + + if (!starNetCallsign4.IsEmpty() && !starNetCallsign4.IsSameAs(wxT(" "))) { + wxString repeater = callsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand4); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + thread->addStarNet(starNetCallsign4, starNetLogoff4, repeater, starNetInfo4, starNetPermanent4, starNetUserTimeout4, starNetGroupTimeout4, starNetCallsignSwitch4, starNetTXMsgSwitch4, starNetLink4); + wxLogInfo(wxT("StarNet 4 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign4.c_str(), starNetLogoff4.c_str(), repeater.c_str(), starNetInfo4.c_str(), starNetPermanent4.c_str(), starNetUserTimeout4, starNetGroupTimeout4, int(starNetCallsignSwitch4), int(starNetTXMsgSwitch4), starNetLink4.c_str()); +#else + thread->addStarNet(starNetCallsign4, starNetLogoff4, repeater, starNetInfo4, starNetPermanent4, starNetUserTimeout4, starNetGroupTimeout4, starNetCallsignSwitch4, starNetTXMsgSwitch4); + wxLogInfo(wxT("StarNet 4 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign4.c_str(), starNetLogoff4.c_str(), repeater.c_str(), starNetInfo4.c_str(), starNetPermanent4.c_str(), starNetUserTimeout4, starNetGroupTimeout4, int(starNetCallsignSwitch4), int(starNetTXMsgSwitch4)); +#endif + } + + wxString starNetBand5, starNetCallsign5, starNetLogoff5, starNetInfo5, starNetPermanent5, starNetLink5; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout5, starNetGroupTimeout5; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch5; + bool starNetTXMsgSwitch5; + getStarNet5(starNetBand5, starNetCallsign5, starNetLogoff5, starNetInfo5, starNetPermanent5, starNetUserTimeout5, starNetGroupTimeout5, starNetCallsignSwitch5, starNetTXMsgSwitch5 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,starNetLink5 +#endif +); + + if (!starNetCallsign5.IsEmpty() && !starNetCallsign5.IsSameAs(wxT(" "))) { + wxString repeater = callsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand5); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + thread->addStarNet(starNetCallsign5, starNetLogoff5, repeater, starNetInfo5, starNetPermanent5, starNetUserTimeout5, starNetGroupTimeout5, starNetCallsignSwitch5, starNetTXMsgSwitch5, starNetLink5); + wxLogInfo(wxT("StarNet 5 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign5.c_str(), starNetLogoff5.c_str(), repeater.c_str(), starNetInfo5.c_str(), starNetPermanent5.c_str(), starNetUserTimeout5, starNetGroupTimeout5, int(starNetCallsignSwitch5), int(starNetTXMsgSwitch5), starNetLink5.c_str()); +#else + thread->addStarNet(starNetCallsign5, starNetLogoff5, repeater, starNetInfo5, starNetPermanent5, starNetUserTimeout5, starNetGroupTimeout5, starNetCallsignSwitch5, starNetTXMsgSwitch5); + wxLogInfo(wxT("StarNet 5 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign5.c_str(), starNetLogoff5.c_str(), repeater.c_str(), starNetInfo5.c_str(), starNetPermanent5.c_str(), starNetUserTimeout5, starNetGroupTimeout5, int(starNetCallsignSwitch5), int(starNetTXMsgSwitch5)); +#endif + } + + wxString starNetBand6, starNetCallsign6, starNetLogoff6, starNetInfo6, starNetPermanent6, starNetLink6; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout6, starNetGroupTimeout6; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch6; + bool starNetTXMsgSwitch6; + getStarNet6(starNetBand6, starNetCallsign6, starNetLogoff6, starNetInfo6, starNetPermanent6, starNetUserTimeout6, starNetGroupTimeout6, starNetCallsignSwitch6, starNetTXMsgSwitch6 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,starNetLink6 +#endif +); + + if (!starNetCallsign6.IsEmpty() && !starNetCallsign6.IsSameAs(wxT(" "))) { + wxString repeater = callsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand6); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + thread->addStarNet(starNetCallsign6, starNetLogoff6, repeater, starNetInfo6, starNetPermanent6, starNetUserTimeout6, starNetGroupTimeout6, starNetCallsignSwitch6, starNetTXMsgSwitch6, starNetLink6); + wxLogInfo(wxT("StarNet 6 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign6.c_str(), starNetLogoff6.c_str(), repeater.c_str(), starNetInfo6.c_str(), starNetPermanent6.c_str(), starNetUserTimeout6, starNetGroupTimeout6, int(starNetCallsignSwitch6), int(starNetTXMsgSwitch6), starNetLink6.c_str()); +#else + thread->addStarNet(starNetCallsign6, starNetLogoff6, repeater, starNetInfo6, starNetPermanent6, starNetUserTimeout6, starNetGroupTimeout6, starNetCallsignSwitch6, starNetTXMsgSwitch6); + wxLogInfo(wxT("StarNet 6 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign6.c_str(), starNetLogoff6.c_str(), repeater.c_str(), starNetInfo6.c_str(), starNetPermanent6.c_str(), starNetUserTimeout6, starNetGroupTimeout6, int(starNetCallsignSwitch6), int(starNetTXMsgSwitch6)); +#endif + } + + wxString starNetBand7, starNetCallsign7, starNetLogoff7, starNetInfo7, starNetPermanent7, starNetLink7; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout7, starNetGroupTimeout7; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch7; + bool starNetTXMsgSwitch7; + getStarNet7(starNetBand7, starNetCallsign7, starNetLogoff7, starNetInfo7, starNetPermanent7, starNetUserTimeout7, starNetGroupTimeout7, starNetCallsignSwitch7, starNetTXMsgSwitch7 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,starNetLink7 +#endif +); + + if (!starNetCallsign7.IsEmpty() && !starNetCallsign7.IsSameAs(wxT(" "))) { + wxString repeater = callsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand7); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + thread->addStarNet(starNetCallsign7, starNetLogoff7, repeater, starNetInfo7, starNetPermanent7, starNetUserTimeout7, starNetGroupTimeout7, starNetCallsignSwitch7, starNetTXMsgSwitch7, starNetLink7); + wxLogInfo(wxT("StarNet 7 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign7.c_str(), starNetLogoff7.c_str(), repeater.c_str(), starNetInfo7.c_str(), starNetPermanent7.c_str(), starNetUserTimeout7, starNetGroupTimeout7, int(starNetCallsignSwitch7), int(starNetTXMsgSwitch7), starNetLink7.c_str()); +#else + thread->addStarNet(starNetCallsign7, starNetLogoff7, repeater, starNetInfo7, starNetPermanent7, starNetUserTimeout7, starNetGroupTimeout7, starNetCallsignSwitch7, starNetTXMsgSwitch7); + wxLogInfo(wxT("StarNet 7 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign7.c_str(), starNetLogoff7.c_str(), repeater.c_str(), starNetInfo7.c_str(), starNetPermanent7.c_str(), starNetUserTimeout7, starNetGroupTimeout7, int(starNetCallsignSwitch7), int(starNetTXMsgSwitch7)); +#endif + } + + wxString starNetBand8, starNetCallsign8, starNetLogoff8, starNetInfo8, starNetPermanent8, starNetLink8; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout8, starNetGroupTimeout8; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch8; + bool starNetTXMsgSwitch8; + getStarNet8(starNetBand8, starNetCallsign8, starNetLogoff8, starNetInfo8, starNetPermanent8, starNetUserTimeout8, starNetGroupTimeout8, starNetCallsignSwitch8, starNetTXMsgSwitch8 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,starNetLink8 +#endif +); + + if (!starNetCallsign8.IsEmpty() && !starNetCallsign8.IsSameAs(wxT(" "))) { + wxString repeater = callsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand8); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + thread->addStarNet(starNetCallsign8, starNetLogoff8, repeater, starNetInfo8, starNetPermanent8, starNetUserTimeout8, starNetGroupTimeout8, starNetCallsignSwitch8, starNetTXMsgSwitch8, starNetLink8); + wxLogInfo(wxT("StarNet 8 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign8.c_str(), starNetLogoff8.c_str(), repeater.c_str(), starNetInfo8.c_str(), starNetPermanent8.c_str(), starNetUserTimeout8, starNetGroupTimeout8, int(starNetCallsignSwitch8), int(starNetTXMsgSwitch8), starNetLink8.c_str()); +#else + thread->addStarNet(starNetCallsign8, starNetLogoff8, repeater, starNetInfo8, starNetPermanent8, starNetUserTimeout8, starNetGroupTimeout8, starNetCallsignSwitch8, starNetTXMsgSwitch8); + wxLogInfo(wxT("StarNet 8 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign8.c_str(), starNetLogoff8.c_str(), repeater.c_str(), starNetInfo8.c_str(), starNetPermanent8.c_str(), starNetUserTimeout8, starNetGroupTimeout8, int(starNetCallsignSwitch8), int(starNetTXMsgSwitch8)); +#endif + } + + wxString starNetBand9, starNetCallsign9, starNetLogoff9, starNetInfo9, starNetPermanent9, starNetLink9; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout9, starNetGroupTimeout9; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch9; + bool starNetTXMsgSwitch9; + getStarNet9(starNetBand9, starNetCallsign9, starNetLogoff9, starNetInfo9, starNetPermanent9, starNetUserTimeout9, starNetGroupTimeout9, starNetCallsignSwitch9, starNetTXMsgSwitch9 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,starNetLink9 +#endif +); + + if (!starNetCallsign9.IsEmpty() && !starNetCallsign9.IsSameAs(wxT(" "))) { + wxString repeater = callsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand9); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + thread->addStarNet(starNetCallsign9, starNetLogoff9, repeater, starNetInfo9, starNetPermanent9, starNetUserTimeout9, starNetGroupTimeout9, starNetCallsignSwitch9, starNetTXMsgSwitch9, starNetLink9); + wxLogInfo(wxT("StarNet 9 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign9.c_str(), starNetLogoff9.c_str(), repeater.c_str(), starNetInfo9.c_str(), starNetPermanent9.c_str(), starNetUserTimeout9, starNetGroupTimeout9, int(starNetCallsignSwitch9), int(starNetTXMsgSwitch9), starNetLink9.c_str()); +#else + thread->addStarNet(starNetCallsign9, starNetLogoff9, repeater, starNetInfo9, starNetPermanent9, starNetUserTimeout9, starNetGroupTimeout9, starNetCallsignSwitch9, starNetTXMsgSwitch9); + wxLogInfo(wxT("StarNet 9 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign9.c_str(), starNetLogoff9.c_str(), repeater.c_str(), starNetInfo9.c_str(), starNetPermanent9.c_str(), starNetUserTimeout9, starNetGroupTimeout9, int(starNetCallsignSwitch9), int(starNetTXMsgSwitch9)); +#endif + } + + wxString starNetBand10, starNetCallsign10, starNetLogoff10, starNetInfo10, starNetPermanent10, starNetLink10; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout10, starNetGroupTimeout10; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch10; + bool starNetTXMsgSwitch10; + getStarNet10(starNetBand10, starNetCallsign10, starNetLogoff10, starNetInfo10, starNetPermanent10, starNetUserTimeout10, starNetGroupTimeout10, starNetCallsignSwitch10, starNetTXMsgSwitch10 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,starNetLink10 +#endif +); + + if (!starNetCallsign10.IsEmpty() && !starNetCallsign10.IsSameAs(wxT(" "))) { + wxString repeater = callsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand10); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + thread->addStarNet(starNetCallsign10, starNetLogoff10, repeater, starNetInfo10, starNetPermanent10, starNetUserTimeout10, starNetGroupTimeout10, starNetCallsignSwitch10, starNetTXMsgSwitch10, starNetLink10); + wxLogInfo(wxT("StarNet 10 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign10.c_str(), starNetLogoff10.c_str(), repeater.c_str(), starNetInfo10.c_str(), starNetPermanent10.c_str(), starNetUserTimeout10, starNetGroupTimeout10, int(starNetCallsignSwitch10), int(starNetTXMsgSwitch10), starNetLink10.c_str()); +#else + thread->addStarNet(starNetCallsign10, starNetLogoff10, repeater, starNetInfo10, starNetPermanent10, starNetUserTimeout10, starNetGroupTimeout10, starNetCallsignSwitch10, starNetTXMsgSwitch10); + wxLogInfo(wxT("StarNet 10 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign10.c_str(), starNetLogoff10.c_str(), repeater.c_str(), starNetInfo10.c_str(), starNetPermanent10.c_str(), starNetUserTimeout10, starNetGroupTimeout10, int(starNetCallsignSwitch10), int(starNetTXMsgSwitch10)); +#endif + } + + wxString starNetBand11, starNetCallsign11, starNetLogoff11, starNetInfo11, starNetPermanent11, starNetLink11; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout11, starNetGroupTimeout11; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch11; + bool starNetTXMsgSwitch11; + getStarNet11(starNetBand11, starNetCallsign11, starNetLogoff11, starNetInfo11, starNetPermanent11, starNetUserTimeout11, starNetGroupTimeout11, starNetCallsignSwitch11, starNetTXMsgSwitch11 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,starNetLink11 +#endif +); + + if (!starNetCallsign11.IsEmpty() && !starNetCallsign11.IsSameAs(wxT(" "))) { + wxString repeater = callsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand11); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + thread->addStarNet(starNetCallsign11, starNetLogoff11, repeater, starNetInfo11, starNetPermanent11, starNetUserTimeout11, starNetGroupTimeout11, starNetCallsignSwitch11, starNetTXMsgSwitch11, starNetLink11); + wxLogInfo(wxT("StarNet 11 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign11.c_str(), starNetLogoff11.c_str(), repeater.c_str(), starNetInfo11.c_str(), starNetPermanent11.c_str(), starNetUserTimeout11, starNetGroupTimeout11, int(starNetCallsignSwitch11), int(starNetTXMsgSwitch11), starNetLink11.c_str()); +#else + thread->addStarNet(starNetCallsign11, starNetLogoff11, repeater, starNetInfo11, starNetPermanent11, starNetUserTimeout11, starNetGroupTimeout11, starNetCallsignSwitch11, starNetTXMsgSwitch11); + wxLogInfo(wxT("StarNet 11 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign11.c_str(), starNetLogoff11.c_str(), repeater.c_str(), starNetInfo11.c_str(), starNetPermanent11.c_str(), starNetUserTimeout11, starNetGroupTimeout11, int(starNetCallsignSwitch11), int(starNetTXMsgSwitch11)); +#endif + } + + wxString starNetBand12, starNetCallsign12, starNetLogoff12, starNetInfo12, starNetPermanent12, starNetLink12; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout12, starNetGroupTimeout12; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch12; + bool starNetTXMsgSwitch12; + getStarNet12(starNetBand12, starNetCallsign12, starNetLogoff12, starNetInfo12, starNetPermanent12, starNetUserTimeout12, starNetGroupTimeout12, starNetCallsignSwitch12, starNetTXMsgSwitch12 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,starNetLink12 +#endif +); + + if (!starNetCallsign12.IsEmpty() && !starNetCallsign12.IsSameAs(wxT(" "))) { + wxString repeater = callsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand12); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + thread->addStarNet(starNetCallsign12, starNetLogoff12, repeater, starNetInfo12, starNetPermanent12, starNetUserTimeout12, starNetGroupTimeout12, starNetCallsignSwitch12, starNetTXMsgSwitch12, starNetLink12); + wxLogInfo(wxT("StarNet 12 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign12.c_str(), starNetLogoff12.c_str(), repeater.c_str(), starNetInfo12.c_str(), starNetPermanent12.c_str(), starNetUserTimeout12, starNetGroupTimeout12, int(starNetCallsignSwitch12), int(starNetTXMsgSwitch12), starNetLink12.c_str()); +#else + thread->addStarNet(starNetCallsign12, starNetLogoff12, repeater, starNetInfo12, starNetPermanent12, starNetUserTimeout12, starNetGroupTimeout12, starNetCallsignSwitch12, starNetTXMsgSwitch12); + wxLogInfo(wxT("StarNet 12 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign12.c_str(), starNetLogoff12.c_str(), repeater.c_str(), starNetInfo12.c_str(), starNetPermanent12.c_str(), starNetUserTimeout12, starNetGroupTimeout12, int(starNetCallsignSwitch12), int(starNetTXMsgSwitch12)); +#endif + } + + wxString starNetBand13, starNetCallsign13, starNetLogoff13, starNetInfo13, starNetPermanent13, starNetLink13; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout13, starNetGroupTimeout13; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch13; + bool starNetTXMsgSwitch13; + getStarNet13(starNetBand13, starNetCallsign13, starNetLogoff13, starNetInfo13, starNetPermanent13, starNetUserTimeout13, starNetGroupTimeout13, starNetCallsignSwitch13, starNetTXMsgSwitch13 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,starNetLink13 +#endif +); + + if (!starNetCallsign13.IsEmpty() && !starNetCallsign13.IsSameAs(wxT(" "))) { + wxString repeater = callsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand13); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + thread->addStarNet(starNetCallsign13, starNetLogoff13, repeater, starNetInfo13, starNetPermanent13, starNetUserTimeout13, starNetGroupTimeout13, starNetCallsignSwitch13, starNetTXMsgSwitch13, starNetLink13); + wxLogInfo(wxT("StarNet 13 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign13.c_str(), starNetLogoff13.c_str(), repeater.c_str(), starNetInfo13.c_str(), starNetPermanent13.c_str(), starNetUserTimeout13, starNetGroupTimeout13, int(starNetCallsignSwitch13), int(starNetTXMsgSwitch13), starNetLink13.c_str()); +#else + thread->addStarNet(starNetCallsign13, starNetLogoff13, repeater, starNetInfo13, starNetPermanent13, starNetUserTimeout13, starNetGroupTimeout13, starNetCallsignSwitch13, starNetTXMsgSwitch13); + wxLogInfo(wxT("StarNet 13 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign13.c_str(), starNetLogoff13.c_str(), repeater.c_str(), starNetInfo13.c_str(), starNetPermanent13.c_str(), starNetUserTimeout13, starNetGroupTimeout13, int(starNetCallsignSwitch13), int(starNetTXMsgSwitch13)); +#endif + } + + wxString starNetBand14, starNetCallsign14, starNetLogoff14, starNetInfo14, starNetPermanent14, starNetLink14; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout14, starNetGroupTimeout14; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch14; + bool starNetTXMsgSwitch14; + getStarNet14(starNetBand14, starNetCallsign14, starNetLogoff14, starNetInfo14, starNetPermanent14, starNetUserTimeout14, starNetGroupTimeout14, starNetCallsignSwitch14, starNetTXMsgSwitch14 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,starNetLink14 +#endif +); + + if (!starNetCallsign14.IsEmpty() && !starNetCallsign14.IsSameAs(wxT(" "))) { + wxString repeater = callsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand14); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + thread->addStarNet(starNetCallsign14, starNetLogoff14, repeater, starNetInfo14, starNetPermanent14, starNetUserTimeout14, starNetGroupTimeout14, starNetCallsignSwitch14, starNetTXMsgSwitch14, starNetLink14); + wxLogInfo(wxT("StarNet 14 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign14.c_str(), starNetLogoff14.c_str(), repeater.c_str(), starNetInfo14.c_str(), starNetPermanent14.c_str(), starNetUserTimeout14, starNetGroupTimeout14, int(starNetCallsignSwitch14), int(starNetTXMsgSwitch14), starNetLink14.c_str()); +#else + thread->addStarNet(starNetCallsign14, starNetLogoff14, repeater, starNetInfo14, starNetPermanent14, starNetUserTimeout14, starNetGroupTimeout14, starNetCallsignSwitch14, starNetTXMsgSwitch14); + wxLogInfo(wxT("StarNet 14 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign14.c_str(), starNetLogoff14.c_str(), repeater.c_str(), starNetInfo14.c_str(), starNetPermanent14.c_str(), starNetUserTimeout14, starNetGroupTimeout14, int(starNetCallsignSwitch14), int(starNetTXMsgSwitch14)); +#endif + } + + wxString starNetBand15, starNetCallsign15, starNetLogoff15, starNetInfo15, starNetPermanent15, starNetLink15; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout15, starNetGroupTimeout15; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch15; + bool starNetTXMsgSwitch15; + getStarNet15(starNetBand15, starNetCallsign15, starNetLogoff15, starNetInfo15, starNetPermanent15, starNetUserTimeout15, starNetGroupTimeout15, starNetCallsignSwitch15, starNetTXMsgSwitch15 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,starNetLink15 +#endif +); + + if (!starNetCallsign15.IsEmpty() && !starNetCallsign15.IsSameAs(wxT(" "))) { + wxString repeater = callsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand15); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + thread->addStarNet(starNetCallsign15, starNetLogoff15, repeater, starNetInfo15, starNetPermanent15, starNetUserTimeout15, starNetGroupTimeout15, starNetCallsignSwitch15, starNetTXMsgSwitch15, starNetLink15); + wxLogInfo(wxT("StarNet 15 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign15.c_str(), starNetLogoff15.c_str(), repeater.c_str(), starNetInfo15.c_str(), starNetPermanent15.c_str(), starNetUserTimeout15, starNetGroupTimeout15, int(starNetCallsignSwitch15), int(starNetTXMsgSwitch15), starNetLink15.c_str()); +#else + thread->addStarNet(starNetCallsign15, starNetLogoff15, repeater, starNetInfo15, starNetPermanent15, starNetUserTimeout15, starNetGroupTimeout15, starNetCallsignSwitch15, starNetTXMsgSwitch15); + wxLogInfo(wxT("StarNet 15 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign15.c_str(), starNetLogoff15.c_str(), repeater.c_str(), starNetInfo15.c_str(), starNetPermanent15.c_str(), starNetUserTimeout15, starNetGroupTimeout15, int(starNetCallsignSwitch15), int(starNetTXMsgSwitch15)); +#endif + } + + bool remoteEnabled; + wxString remotePassword; + unsigned int remotePort; + getRemote(remoteEnabled, remotePassword, remotePort); + wxLogInfo(wxT("Remote enabled set to %d, port set to %u"), int(remoteEnabled), remotePort); + thread->setRemote(remoteEnabled, remotePassword, remotePort); + + thread->setLog(logEnabled); + thread->setAddress(address); + thread->setCallsign(callsign); + + // Convert the worker class into a thread + m_thread = new CStarNetServerThreadHelper(thread); + m_thread->start(); +} diff --git a/StarNetServer/StarNetServerApp.h b/StarNetServer/StarNetServerApp.h new file mode 100644 index 0000000..c97689e --- /dev/null +++ b/StarNetServer/StarNetServerApp.h @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2010,2011,2012 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 StarNetServerApp_H +#define StarNetServerApp_H + +#include "StarNetServerThreadHelper.h" +#include "StarNetServerConfig.h" +#include "StarNetServerFrame.h" +#include "Defs.h" + +#include + +class CStarNetServerApp : public wxApp { + +public: + CStarNetServerApp(); + virtual ~CStarNetServerApp(); + + virtual bool OnInit(); + virtual int OnExit(); + + virtual void OnInitCmdLine(wxCmdLineParser& parser); + virtual bool OnCmdLineParsed(wxCmdLineParser& parser); + + // This is overridden because dialog boxes from threads are bad news +#if defined(__WXDEBUG__) + virtual void OnAssertFailure(const wxChar* file, int line, const wxChar* func, const wxChar* cond, const wxChar* msg); +#endif + + virtual void showLog(const wxString& text); + + virtual void getGateway(wxString& callsign, wxString& address) const; + virtual void setGateway(const wxString& callsign, const wxString& address); + + virtual void getIrcDDB(wxString& hostname, wxString& username, wxString& password) const; + virtual void setIrcDDB(const wxString& hostname, const wxString& username, const wxString& password); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + virtual void getStarNet1(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + virtual void setStarNet1(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); + + virtual void getStarNet2(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + virtual void setStarNet2(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); + + virtual void getStarNet3(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + virtual void setStarNet3(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); + + virtual void getStarNet4(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + virtual void setStarNet4(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); + + virtual void getStarNet5(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + virtual void setStarNet5(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); + + virtual void getStarNet6(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + virtual void setStarNet6(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); + + virtual void getStarNet7(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + virtual void setStarNet7(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); + + virtual void getStarNet8(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + virtual void setStarNet8(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); + + virtual void getStarNet9(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + virtual void setStarNet9(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); + + virtual void getStarNet10(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + virtual void setStarNet10(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); + + virtual void getStarNet11(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + virtual void setStarNet11(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); + + virtual void getStarNet12(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + virtual void setStarNet12(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); + + virtual void getStarNet13(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + virtual void setStarNet13(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); + + virtual void getStarNet14(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + virtual void setStarNet14(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); + + virtual void getStarNet15(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + virtual void setStarNet15(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); +#else + virtual void getStarNet1(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + virtual void setStarNet1(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); + + virtual void getStarNet2(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + virtual void setStarNet2(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); + + virtual void getStarNet3(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + virtual void setStarNet3(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); + + virtual void getStarNet4(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + virtual void setStarNet4(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); + + virtual void getStarNet5(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + virtual void setStarNet5(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); + + virtual void getStarNet6(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + virtual void setStarNet6(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); + + virtual void getStarNet7(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + virtual void setStarNet7(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); + + virtual void getStarNet8(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + virtual void setStarNet8(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); + + virtual void getStarNet9(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + virtual void setStarNet9(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); + + virtual void getStarNet10(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + virtual void setStarNet10(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); + + virtual void getStarNet11(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + virtual void setStarNet11(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); + + virtual void getStarNet12(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + virtual void setStarNet12(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); + + virtual void getStarNet13(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + virtual void setStarNet13(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); + + virtual void getStarNet14(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + virtual void setStarNet14(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); + + virtual void getStarNet15(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + virtual void setStarNet15(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); +#endif + + virtual void getRemote(bool& enabled, wxString& password, unsigned int& port) const; + virtual void setRemote(bool enabled, const wxString& password, unsigned int port); + + virtual void getMiscellaneous(bool& logEnabled) const; + virtual void setMiscellaneous(bool logEnabled); + + virtual void getPosition(int& x, int& y) const; + virtual void setPosition(int x, int y); + + virtual bool writeConfig(); + +private: + bool m_nolog; + bool m_gui; + wxString m_logDir; + wxString m_confDir; + CStarNetServerFrame* m_frame; + CStarNetServerThreadHelper* m_thread; + CStarNetServerConfig* m_config; + wxLogChain* m_logChain; + + void createThread(); +}; + +DECLARE_APP(CStarNetServerApp) + +#endif diff --git a/StarNetServer/StarNetServerAppD.cpp b/StarNetServer/StarNetServerAppD.cpp new file mode 100644 index 0000000..5fd1610 --- /dev/null +++ b/StarNetServer/StarNetServerAppD.cpp @@ -0,0 +1,561 @@ +/* + * 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 + * 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 "IcomRepeaterProtocolHandler.h" +#include "HBRepeaterProtocolHandler.h" +#include "StarNetServerConfig.h" +#include "StarNetServerAppD.h" +#include "StarNetServerDefs.h" +#include "APRSWriter.h" +#include "Version.h" +#include "Logger.h" +#include "IRCDDBClient.h" + +#include +#include +#include +#include + +#include +#include +#include + +const wxChar* NOLOGGING_SWITCH = wxT("nolog"); +const wxChar* LOGDIR_OPTION = wxT("logdir"); +const wxChar* CONFDIR_OPTION = wxT("confdir"); +const wxChar* DAEMON_SWITCH = wxT("daemon"); + + +int main(int argc, char** argv) +{ + bool res = ::wxInitialize(); + if (!res) { + ::fprintf(stderr, "starnetserverd: failed to initialise the wxWidgets library, exiting\n"); + return -1; + } + + wxCmdLineParser parser(argc, argv); + parser.AddSwitch(NOLOGGING_SWITCH, wxEmptyString, wxEmptyString, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddSwitch(DAEMON_SWITCH, wxEmptyString, wxEmptyString, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption(LOGDIR_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption(CONFDIR_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + + int cmd = parser.Parse(); + if (cmd != 0) { + ::wxUninitialize(); + return 0; + } + + bool nolog = parser.Found(NOLOGGING_SWITCH); + bool daemon = parser.Found(DAEMON_SWITCH); + + wxString logDir; + bool found = parser.Found(LOGDIR_OPTION, &logDir); + if (!found) + logDir.Clear(); + + wxString confDir; + found = parser.Found(CONFDIR_OPTION, &confDir); + if (!found) + confDir = wxT(CONF_DIR); + + if (daemon) { + pid_t pid = ::fork(); + + if (pid < 0) { + ::fprintf(stderr, "starnetserverd: error in fork(), exiting\n"); + ::wxUninitialize(); + return 1; + } + + // If this is the parent, exit + if (pid > 0) + return 0; + + // We are the child from here onwards + ::setsid(); + + ::chdir("/"); + + ::umask(0); + } + + CStarNetServerAppD gateway(nolog, logDir, confDir); + + if (!gateway.init()) { + ::wxUninitialize(); + return 1; + } + + gateway.run(); + + ::wxUninitialize(); + return 0; +} + +CStarNetServerAppD::CStarNetServerAppD(bool nolog, const wxString& logDir, const wxString& confDir) : +m_nolog(nolog), +m_logDir(logDir), +m_confDir(confDir), +m_thread(NULL) +{ +} + +CStarNetServerAppD::~CStarNetServerAppD() +{ +} + +bool CStarNetServerAppD::init() +{ + if (!m_nolog) { + if (m_logDir.IsEmpty()) + m_logDir = wxT(LOG_DIR); + + wxLog* log = new CLogger(m_logDir, LOG_BASE_NAME); + wxLog::SetActiveTarget(log); + } else { + new wxLogNull; + } + + wxLogInfo(wxT("Starting ") + APPLICATION_NAME + wxT(" daemon - ") + VERSION); + + // Log the version of wxWidgets and the Operating System + wxLogInfo(wxT("Using wxWidgets %d.%d.%d on %s"), wxMAJOR_VERSION, wxMINOR_VERSION, wxRELEASE_NUMBER, ::wxGetOsDescription().c_str()); + + return createThread(); +} + +void CStarNetServerAppD::run() +{ + m_thread->run(); + + wxLogInfo(APPLICATION_NAME + wxT(" is exiting")); +} + +bool CStarNetServerAppD::createThread() +{ + CStarNetServerConfig config(m_confDir); + + m_thread = new CStarNetServerThread(m_nolog, m_logDir); + + wxString callsign, address; + config.getGateway(callsign, address); + + callsign.Append(wxT(" ")); + callsign.Truncate(LONG_CALLSIGN_LENGTH - 1U); + callsign.Append(wxT("G")); + + wxLogInfo(wxT("Gateway callsign set to %s, local address set to %s"), callsign.c_str(), address.c_str()); + + bool logEnabled; + config.getMiscellaneous(logEnabled); + wxLogInfo(wxT("Log enabled set to %d"), int(logEnabled)); + + wxString hostname, username, password; + config.getIrcDDB(hostname, username, password); + wxLogInfo(wxT("ircDDB host set to %s, username set to %s"), hostname.c_str(), username.c_str()); + + if (!hostname.IsEmpty() && !username.IsEmpty()) { +#if defined(__WINDOWS__) + CIRCDDB* ircDDB = new CIRCDDBClient(hostname, 9007U, username, password, wxT("win_") + LOG_BASE_NAME + wxT("-") + VERSION, address); +#else + CIRCDDB* ircDDB = new CIRCDDBClient(hostname, 9007U, username, password, wxT("linux_") + LOG_BASE_NAME + wxT("-") + VERSION, address); +#endif + bool res = ircDDB->open(); + if (!res) { + wxLogError(wxT("Cannot initialise the ircDDB protocol handler")); + return false; + } + + m_thread->setIRC(ircDDB); + } + + wxString starNetBand1, starNetCallsign1, starNetLogoff1, starNetInfo1, starNetPermanent1, starNetLink1; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout1, starNetGroupTimeout1; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch1; + bool starNetTXMsgSwitch1; + config.getStarNet1(starNetBand1, starNetCallsign1, starNetLogoff1, starNetInfo1, starNetPermanent1, starNetUserTimeout1, starNetGroupTimeout1, starNetCallsignSwitch1, starNetTXMsgSwitch1 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,starNetLink1 +#endif +); + + if (!starNetCallsign1.IsEmpty() && !starNetCallsign1.IsSameAs(wxT(" "))) { + wxString repeater = callsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand1); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_thread->addStarNet(starNetCallsign1, starNetLogoff1, repeater, starNetInfo1, starNetPermanent1, starNetUserTimeout1, starNetGroupTimeout1, starNetCallsignSwitch1, starNetTXMsgSwitch1, starNetLink1); + wxLogInfo(wxT("StarNet 1 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign1.c_str(), starNetLogoff1.c_str(), repeater.c_str(), starNetInfo1.c_str(), starNetPermanent1.c_str(), starNetUserTimeout1, starNetGroupTimeout1, int(starNetCallsignSwitch1), int(starNetTXMsgSwitch1), starNetLink1.c_str()); +#else + m_thread->addStarNet(starNetCallsign1, starNetLogoff1, repeater, starNetInfo1, starNetPermanent1, starNetUserTimeout1, starNetGroupTimeout1, starNetCallsignSwitch1, starNetTXMsgSwitch1); + wxLogInfo(wxT("StarNet 1 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign1.c_str(), starNetLogoff1.c_str(), repeater.c_str(), starNetInfo1.c_str(), starNetPermanent1.c_str(), starNetUserTimeout1, starNetGroupTimeout1, int(starNetCallsignSwitch1), int(starNetTXMsgSwitch1)); +#endif + } + + wxString starNetBand2, starNetCallsign2, starNetLogoff2, starNetInfo2, starNetPermanent2, starNetLink2; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout2, starNetGroupTimeout2; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch2; + bool starNetTXMsgSwitch2; + config.getStarNet2(starNetBand2, starNetCallsign2, starNetLogoff2, starNetInfo2, starNetPermanent2, starNetUserTimeout2, starNetGroupTimeout2, starNetCallsignSwitch2, starNetTXMsgSwitch2 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,starNetLink2 +#endif +); + + if (!starNetCallsign2.IsEmpty() && !starNetCallsign2.IsSameAs(wxT(" "))) { + wxString repeater = callsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand2); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_thread->addStarNet(starNetCallsign2, starNetLogoff2, repeater, starNetInfo2, starNetPermanent2, starNetUserTimeout2, starNetGroupTimeout2, starNetCallsignSwitch2, starNetTXMsgSwitch2, starNetLink2); + wxLogInfo(wxT("StarNet 2 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign2.c_str(), starNetLogoff2.c_str(), repeater.c_str(), starNetInfo2.c_str(), starNetPermanent2.c_str(), starNetUserTimeout2, starNetGroupTimeout2, int(starNetCallsignSwitch2), int(starNetTXMsgSwitch2), starNetLink2.c_str()); +#else + m_thread->addStarNet(starNetCallsign2, starNetLogoff2, repeater, starNetInfo2, starNetPermanent2, starNetUserTimeout2, starNetGroupTimeout2, starNetCallsignSwitch2, starNetTXMsgSwitch2); + wxLogInfo(wxT("StarNet 2 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign2.c_str(), starNetLogoff2.c_str(), repeater.c_str(), starNetInfo2.c_str(), starNetPermanent2.c_str(), starNetUserTimeout2, starNetGroupTimeout2, int(starNetCallsignSwitch2), int(starNetTXMsgSwitch2)); +#endif + } + + wxString starNetBand3, starNetCallsign3, starNetLogoff3, starNetInfo3, starNetPermanent3, starNetLink3; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout3, starNetGroupTimeout3; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch3; + bool starNetTXMsgSwitch3; + config.getStarNet3(starNetBand3, starNetCallsign3, starNetLogoff3, starNetInfo3, starNetPermanent3, starNetUserTimeout3, starNetGroupTimeout3, starNetCallsignSwitch3, starNetTXMsgSwitch3 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,starNetLink3 +#endif +); + + if (!starNetCallsign3.IsEmpty() && !starNetCallsign3.IsSameAs(wxT(" "))) { + wxString repeater = callsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand3); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_thread->addStarNet(starNetCallsign3, starNetLogoff3, repeater, starNetInfo3, starNetPermanent3, starNetUserTimeout3, starNetGroupTimeout3, starNetCallsignSwitch3, starNetTXMsgSwitch3, starNetLink3); + wxLogInfo(wxT("StarNet 3 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign3.c_str(), starNetLogoff3.c_str(), repeater.c_str(), starNetInfo3.c_str(), starNetPermanent3.c_str(), starNetUserTimeout3, starNetGroupTimeout3, int(starNetCallsignSwitch3), int(starNetTXMsgSwitch3), starNetLink3.c_str()); +#else + m_thread->addStarNet(starNetCallsign3, starNetLogoff3, repeater, starNetInfo3, starNetPermanent3, starNetUserTimeout3, starNetGroupTimeout3, starNetCallsignSwitch3, starNetTXMsgSwitch3); + wxLogInfo(wxT("StarNet 3 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign3.c_str(), starNetLogoff3.c_str(), repeater.c_str(), starNetInfo3.c_str(), starNetPermanent3.c_str(), starNetUserTimeout3, starNetGroupTimeout3, int(starNetCallsignSwitch3), int(starNetTXMsgSwitch3)); +#endif + } + + wxString starNetBand4, starNetCallsign4, starNetLogoff4, starNetInfo4, starNetPermanent4, starNetLink4; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout4, starNetGroupTimeout4; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch4; + bool starNetTXMsgSwitch4; + config.getStarNet4(starNetBand4, starNetCallsign4, starNetLogoff4, starNetInfo4, starNetPermanent4, starNetUserTimeout4, starNetGroupTimeout4, starNetCallsignSwitch4, starNetTXMsgSwitch4 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,starNetLink4 +#endif +); + + if (!starNetCallsign4.IsEmpty() && !starNetCallsign4.IsSameAs(wxT(" "))) { + wxString repeater = callsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand4); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_thread->addStarNet(starNetCallsign4, starNetLogoff4, repeater, starNetInfo4, starNetPermanent4, starNetUserTimeout4, starNetGroupTimeout4, starNetCallsignSwitch4, starNetTXMsgSwitch4, starNetLink4); + wxLogInfo(wxT("StarNet 4 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign4.c_str(), starNetLogoff4.c_str(), repeater.c_str(), starNetInfo4.c_str(), starNetPermanent4.c_str(), starNetUserTimeout4, starNetGroupTimeout4, int(starNetCallsignSwitch4), int(starNetTXMsgSwitch4), starNetLink4.c_str()); +#else + m_thread->addStarNet(starNetCallsign4, starNetLogoff4, repeater, starNetInfo4, starNetPermanent4, starNetUserTimeout4, starNetGroupTimeout4, starNetCallsignSwitch4, starNetTXMsgSwitch4); + wxLogInfo(wxT("StarNet 4 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign4.c_str(), starNetLogoff4.c_str(), repeater.c_str(), starNetInfo4.c_str(), starNetPermanent4.c_str(), starNetUserTimeout4, starNetGroupTimeout4, int(starNetCallsignSwitch4), int(starNetTXMsgSwitch4)); +#endif + } + + wxString starNetBand5, starNetCallsign5, starNetLogoff5, starNetInfo5, starNetPermanent5, starNetLink5; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout5, starNetGroupTimeout5; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch5; + bool starNetTXMsgSwitch5; + config.getStarNet5(starNetBand5, starNetCallsign5, starNetLogoff5, starNetInfo5, starNetPermanent5, starNetUserTimeout5, starNetGroupTimeout5, starNetCallsignSwitch5, starNetTXMsgSwitch5 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,starNetLink5 +#endif +); + + if (!starNetCallsign5.IsEmpty() && !starNetCallsign5.IsSameAs(wxT(" "))) { + wxString repeater = callsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand5); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_thread->addStarNet(starNetCallsign5, starNetLogoff5, repeater, starNetInfo5, starNetPermanent5, starNetUserTimeout5, starNetGroupTimeout5, starNetCallsignSwitch5, starNetTXMsgSwitch5, starNetLink5); + wxLogInfo(wxT("StarNet 5 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign5.c_str(), starNetLogoff5.c_str(), repeater.c_str(), starNetInfo5.c_str(), starNetPermanent5.c_str(), starNetUserTimeout5, starNetGroupTimeout5, int(starNetCallsignSwitch5), int(starNetTXMsgSwitch5), starNetLink5.c_str()); +#else + m_thread->addStarNet(starNetCallsign5, starNetLogoff5, repeater, starNetInfo5, starNetPermanent5, starNetUserTimeout5, starNetGroupTimeout5, starNetCallsignSwitch5, starNetTXMsgSwitch5); + wxLogInfo(wxT("StarNet 5 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign5.c_str(), starNetLogoff5.c_str(), repeater.c_str(), starNetInfo5.c_str(), starNetPermanent5.c_str(), starNetUserTimeout5, starNetGroupTimeout5, int(starNetCallsignSwitch5), int(starNetTXMsgSwitch5)); +#endif + } + + wxString starNetBand6, starNetCallsign6, starNetLogoff6, starNetInfo6, starNetPermanent6, starNetLink6; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout6, starNetGroupTimeout6; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch6; + bool starNetTXMsgSwitch6; + config.getStarNet6(starNetBand6, starNetCallsign6, starNetLogoff6, starNetInfo6, starNetPermanent6, starNetUserTimeout6, starNetGroupTimeout6, starNetCallsignSwitch6, starNetTXMsgSwitch6 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,starNetLink6 +#endif +); + + if (!starNetCallsign6.IsEmpty() && !starNetCallsign6.IsSameAs(wxT(" "))) { + wxString repeater = callsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand6); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_thread->addStarNet(starNetCallsign6, starNetLogoff6, repeater, starNetInfo6, starNetPermanent6, starNetUserTimeout6, starNetGroupTimeout6, starNetCallsignSwitch6, starNetTXMsgSwitch6, starNetLink6); + wxLogInfo(wxT("StarNet 6 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign6.c_str(), starNetLogoff6.c_str(), repeater.c_str(), starNetInfo6.c_str(), starNetPermanent6.c_str(), starNetUserTimeout6, starNetGroupTimeout6, int(starNetCallsignSwitch6), int(starNetTXMsgSwitch6), starNetLink6.c_str()); +#else + m_thread->addStarNet(starNetCallsign6, starNetLogoff6, repeater, starNetInfo6, starNetPermanent6, starNetUserTimeout6, starNetGroupTimeout6, starNetCallsignSwitch6, starNetTXMsgSwitch6); + wxLogInfo(wxT("StarNet 6 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign6.c_str(), starNetLogoff6.c_str(), repeater.c_str(), starNetInfo6.c_str(), starNetPermanent6.c_str(), starNetUserTimeout6, starNetGroupTimeout6, int(starNetCallsignSwitch6), int(starNetTXMsgSwitch6)); +#endif + } + + wxString starNetBand7, starNetCallsign7, starNetLogoff7, starNetInfo7, starNetPermanent7, starNetLink7; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout7, starNetGroupTimeout7; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch7; + bool starNetTXMsgSwitch7; + config.getStarNet7(starNetBand7, starNetCallsign7, starNetLogoff7, starNetInfo7, starNetPermanent7, starNetUserTimeout7, starNetGroupTimeout7, starNetCallsignSwitch7, starNetTXMsgSwitch7 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,starNetLink7 +#endif +); + + if (!starNetCallsign7.IsEmpty() && !starNetCallsign7.IsSameAs(wxT(" "))) { + wxString repeater = callsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand7); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_thread->addStarNet(starNetCallsign7, starNetLogoff7, repeater, starNetInfo7, starNetPermanent7, starNetUserTimeout7, starNetGroupTimeout7, starNetCallsignSwitch7, starNetTXMsgSwitch7, starNetLink7); + wxLogInfo(wxT("StarNet 7 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign7.c_str(), starNetLogoff7.c_str(), repeater.c_str(), starNetInfo7.c_str(), starNetPermanent7.c_str(), starNetUserTimeout7, starNetGroupTimeout7, int(starNetCallsignSwitch7), int(starNetTXMsgSwitch7), starNetLink7.c_str()); +#else + m_thread->addStarNet(starNetCallsign7, starNetLogoff7, repeater, starNetInfo7, starNetPermanent7, starNetUserTimeout7, starNetGroupTimeout7, starNetCallsignSwitch7, starNetTXMsgSwitch7); + wxLogInfo(wxT("StarNet 7 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign7.c_str(), starNetLogoff7.c_str(), repeater.c_str(), starNetInfo7.c_str(), starNetPermanent7.c_str(), starNetUserTimeout7, starNetGroupTimeout7, int(starNetCallsignSwitch7), int(starNetTXMsgSwitch7)); +#endif + } + + wxString starNetBand8, starNetCallsign8, starNetLogoff8, starNetInfo8, starNetPermanent8, starNetLink8; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout8, starNetGroupTimeout8; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch8; + bool starNetTXMsgSwitch8; + config.getStarNet8(starNetBand8, starNetCallsign8, starNetLogoff8, starNetInfo8, starNetPermanent8, starNetUserTimeout8, starNetGroupTimeout8, starNetCallsignSwitch8, starNetTXMsgSwitch8 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,starNetLink8 +#endif +); + + if (!starNetCallsign8.IsEmpty() && !starNetCallsign8.IsSameAs(wxT(" "))) { + wxString repeater = callsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand8); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_thread->addStarNet(starNetCallsign8, starNetLogoff8, repeater, starNetInfo8, starNetPermanent8, starNetUserTimeout8, starNetGroupTimeout8, starNetCallsignSwitch8, starNetTXMsgSwitch8, starNetLink8); + wxLogInfo(wxT("StarNet 8 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign8.c_str(), starNetLogoff8.c_str(), repeater.c_str(), starNetInfo8.c_str(), starNetPermanent8.c_str(), starNetUserTimeout8, starNetGroupTimeout8, int(starNetCallsignSwitch8), int(starNetTXMsgSwitch8), starNetLink8.c_str()); +#else + m_thread->addStarNet(starNetCallsign8, starNetLogoff8, repeater, starNetInfo8, starNetPermanent8, starNetUserTimeout8, starNetGroupTimeout8, starNetCallsignSwitch8, starNetTXMsgSwitch8); + wxLogInfo(wxT("StarNet 8 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign8.c_str(), starNetLogoff8.c_str(), repeater.c_str(), starNetInfo8.c_str(), starNetPermanent8.c_str(), starNetUserTimeout8, starNetGroupTimeout8, int(starNetCallsignSwitch8), int(starNetTXMsgSwitch8)); +#endif + } + + wxString starNetBand9, starNetCallsign9, starNetLogoff9, starNetInfo9, starNetPermanent9, starNetLink9; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout9, starNetGroupTimeout9; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch9; + bool starNetTXMsgSwitch9; + config.getStarNet9(starNetBand9, starNetCallsign9, starNetLogoff9, starNetInfo9, starNetPermanent9, starNetUserTimeout9, starNetGroupTimeout9, starNetCallsignSwitch9, starNetTXMsgSwitch9 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,starNetLink9 +#endif +); + + if (!starNetCallsign9.IsEmpty() && !starNetCallsign9.IsSameAs(wxT(" "))) { + wxString repeater = callsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand9); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_thread->addStarNet(starNetCallsign9, starNetLogoff9, repeater, starNetInfo9, starNetPermanent9, starNetUserTimeout9, starNetGroupTimeout9, starNetCallsignSwitch9, starNetTXMsgSwitch9, starNetLink9); + wxLogInfo(wxT("StarNet 9 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign9.c_str(), starNetLogoff9.c_str(), repeater.c_str(), starNetInfo9.c_str(), starNetPermanent9.c_str(), starNetUserTimeout9, starNetGroupTimeout9, int(starNetCallsignSwitch9), int(starNetTXMsgSwitch9), starNetLink9.c_str()); +#else + m_thread->addStarNet(starNetCallsign9, starNetLogoff9, repeater, starNetInfo9, starNetPermanent9, starNetUserTimeout9, starNetGroupTimeout9, starNetCallsignSwitch9, starNetTXMsgSwitch9); + wxLogInfo(wxT("StarNet 9 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign9.c_str(), starNetLogoff9.c_str(), repeater.c_str(), starNetInfo9.c_str(), starNetPermanent9.c_str(), starNetUserTimeout9, starNetGroupTimeout9, int(starNetCallsignSwitch9), int(starNetTXMsgSwitch9)); +#endif + } + + wxString starNetBand10, starNetCallsign10, starNetLogoff10, starNetInfo10, starNetPermanent10, starNetLink10; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout10, starNetGroupTimeout10; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch10; + bool starNetTXMsgSwitch10; + config.getStarNet10(starNetBand10, starNetCallsign10, starNetLogoff10, starNetInfo10, starNetPermanent10, starNetUserTimeout10, starNetGroupTimeout10, starNetCallsignSwitch10, starNetTXMsgSwitch10 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,starNetLink10 +#endif +); + + if (!starNetCallsign10.IsEmpty() && !starNetCallsign10.IsSameAs(wxT(" "))) { + wxString repeater = callsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand10); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_thread->addStarNet(starNetCallsign10, starNetLogoff10, repeater, starNetInfo10, starNetPermanent10, starNetUserTimeout10, starNetGroupTimeout10, starNetCallsignSwitch10, starNetTXMsgSwitch10, starNetLink10); + wxLogInfo(wxT("StarNet 10 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign10.c_str(), starNetLogoff10.c_str(), repeater.c_str(), starNetInfo10.c_str(), starNetPermanent10.c_str(), starNetUserTimeout10, starNetGroupTimeout10, int(starNetCallsignSwitch10), int(starNetTXMsgSwitch10), starNetLink10.c_str()); +#else + m_thread->addStarNet(starNetCallsign10, starNetLogoff10, repeater, starNetInfo10, starNetPermanent10, starNetUserTimeout10, starNetGroupTimeout10, starNetCallsignSwitch10, starNetTXMsgSwitch10); + wxLogInfo(wxT("StarNet 10 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign10.c_str(), starNetLogoff10.c_str(), repeater.c_str(), starNetInfo10.c_str(), starNetPermanent10.c_str(), starNetUserTimeout10, starNetGroupTimeout10, int(starNetCallsignSwitch10), int(starNetTXMsgSwitch10)); +#endif + } + + wxString starNetBand11, starNetCallsign11, starNetLogoff11, starNetInfo11, starNetPermanent11, starNetLink11; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout11, starNetGroupTimeout11; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch11; + bool starNetTXMsgSwitch11; + config.getStarNet11(starNetBand11, starNetCallsign11, starNetLogoff11, starNetInfo11, starNetPermanent11, starNetUserTimeout11, starNetGroupTimeout11, starNetCallsignSwitch11, starNetTXMsgSwitch11 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,starNetLink11 +#endif +); + + if (!starNetCallsign11.IsEmpty() && !starNetCallsign11.IsSameAs(wxT(" "))) { + wxString repeater = callsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand11); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_thread->addStarNet(starNetCallsign11, starNetLogoff11, repeater, starNetInfo11, starNetPermanent11, starNetUserTimeout11, starNetGroupTimeout11, starNetCallsignSwitch11, starNetTXMsgSwitch11, starNetLink11); + wxLogInfo(wxT("StarNet 11 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign11.c_str(), starNetLogoff11.c_str(), repeater.c_str(), starNetInfo11.c_str(), starNetPermanent11.c_str(), starNetUserTimeout11, starNetGroupTimeout11, int(starNetCallsignSwitch11), int(starNetTXMsgSwitch11), starNetLink11.c_str()); +#else + m_thread->addStarNet(starNetCallsign11, starNetLogoff11, repeater, starNetInfo11, starNetPermanent11, starNetUserTimeout11, starNetGroupTimeout11, starNetCallsignSwitch11, starNetTXMsgSwitch11); + wxLogInfo(wxT("StarNet 11 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign11.c_str(), starNetLogoff11.c_str(), repeater.c_str(), starNetInfo11.c_str(), starNetPermanent11.c_str(), starNetUserTimeout11, starNetGroupTimeout11, int(starNetCallsignSwitch11), int(starNetTXMsgSwitch11)); +#endif + } + + wxString starNetBand12, starNetCallsign12, starNetLogoff12, starNetInfo12, starNetPermanent12, starNetLink12; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout12, starNetGroupTimeout12; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch12; + bool starNetTXMsgSwitch12; + config.getStarNet12(starNetBand12, starNetCallsign12, starNetLogoff12, starNetInfo12, starNetPermanent12, starNetUserTimeout12, starNetGroupTimeout12, starNetCallsignSwitch12, starNetTXMsgSwitch12 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,starNetLink12 +#endif +); + + if (!starNetCallsign12.IsEmpty() && !starNetCallsign12.IsSameAs(wxT(" "))) { + wxString repeater = callsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand12); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_thread->addStarNet(starNetCallsign12, starNetLogoff12, repeater, starNetInfo12, starNetPermanent12, starNetUserTimeout12, starNetGroupTimeout12, starNetCallsignSwitch12, starNetTXMsgSwitch12, starNetLink12); + wxLogInfo(wxT("StarNet 12 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign12.c_str(), starNetLogoff12.c_str(), repeater.c_str(), starNetInfo12.c_str(), starNetPermanent12.c_str(), starNetUserTimeout12, starNetGroupTimeout12, int(starNetCallsignSwitch12), int(starNetTXMsgSwitch12), starNetLink12.c_str()); +#else + m_thread->addStarNet(starNetCallsign12, starNetLogoff12, repeater, starNetInfo12, starNetPermanent12, starNetUserTimeout12, starNetGroupTimeout12, starNetCallsignSwitch12, starNetTXMsgSwitch12); + wxLogInfo(wxT("StarNet 12 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign12.c_str(), starNetLogoff12.c_str(), repeater.c_str(), starNetInfo12.c_str(), starNetPermanent12.c_str(), starNetUserTimeout12, starNetGroupTimeout12, int(starNetCallsignSwitch12), int(starNetTXMsgSwitch12)); +#endif + } + + wxString starNetBand13, starNetCallsign13, starNetLogoff13, starNetInfo13, starNetPermanent13, starNetLink13; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout13, starNetGroupTimeout13; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch13; + bool starNetTXMsgSwitch13; + config.getStarNet13(starNetBand13, starNetCallsign13, starNetLogoff13, starNetInfo13, starNetPermanent13, starNetUserTimeout13, starNetGroupTimeout13, starNetCallsignSwitch13, starNetTXMsgSwitch13 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,starNetLink13 +#endif +); + + if (!starNetCallsign13.IsEmpty() && !starNetCallsign13.IsSameAs(wxT(" "))) { + wxString repeater = callsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand13); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_thread->addStarNet(starNetCallsign13, starNetLogoff13, repeater, starNetInfo13, starNetPermanent13, starNetUserTimeout13, starNetGroupTimeout13, starNetCallsignSwitch13, starNetTXMsgSwitch13, starNetLink13); + wxLogInfo(wxT("StarNet 13 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign13.c_str(), starNetLogoff13.c_str(), repeater.c_str(), starNetInfo13.c_str(), starNetPermanent13.c_str(), starNetUserTimeout13, starNetGroupTimeout13, int(starNetCallsignSwitch13), int(starNetTXMsgSwitch13), starNetLink13.c_str()); +#else + m_thread->addStarNet(starNetCallsign13, starNetLogoff13, repeater, starNetInfo13, starNetPermanent13, starNetUserTimeout13, starNetGroupTimeout13, starNetCallsignSwitch13, starNetTXMsgSwitch13); + wxLogInfo(wxT("StarNet 13 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign13.c_str(), starNetLogoff13.c_str(), repeater.c_str(), starNetInfo13.c_str(), starNetPermanent13.c_str(), starNetUserTimeout13, starNetGroupTimeout13, int(starNetCallsignSwitch13), int(starNetTXMsgSwitch13)); +#endif + } + + wxString starNetBand14, starNetCallsign14, starNetLogoff14, starNetInfo14, starNetPermanent14, starNetLink14; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout14, starNetGroupTimeout14; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch14; + bool starNetTXMsgSwitch14; + config.getStarNet14(starNetBand14, starNetCallsign14, starNetLogoff14, starNetInfo14, starNetPermanent14, starNetUserTimeout14, starNetGroupTimeout14, starNetCallsignSwitch14, starNetTXMsgSwitch14 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,starNetLink14 +#endif +); + + if (!starNetCallsign14.IsEmpty() && !starNetCallsign14.IsSameAs(wxT(" "))) { + wxString repeater = callsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand14); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_thread->addStarNet(starNetCallsign14, starNetLogoff14, repeater, starNetInfo14, starNetPermanent14, starNetUserTimeout14, starNetGroupTimeout14, starNetCallsignSwitch14, starNetTXMsgSwitch14, starNetLink14); + wxLogInfo(wxT("StarNet 14 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign14.c_str(), starNetLogoff14.c_str(), repeater.c_str(), starNetInfo14.c_str(), starNetPermanent14.c_str(), starNetUserTimeout14, starNetGroupTimeout14, int(starNetCallsignSwitch14), int(starNetTXMsgSwitch14), starNetLink14.c_str()); +#else + m_thread->addStarNet(starNetCallsign14, starNetLogoff14, repeater, starNetInfo14, starNetPermanent14, starNetUserTimeout14, starNetGroupTimeout14, starNetCallsignSwitch14, starNetTXMsgSwitch14); + wxLogInfo(wxT("StarNet 14 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign14.c_str(), starNetLogoff14.c_str(), repeater.c_str(), starNetInfo14.c_str(), starNetPermanent14.c_str(), starNetUserTimeout14, starNetGroupTimeout14, int(starNetCallsignSwitch14), int(starNetTXMsgSwitch14)); +#endif + } + + wxString starNetBand15, starNetCallsign15, starNetLogoff15, starNetInfo15, starNetPermanent15, starNetLink15; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout15, starNetGroupTimeout15; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch15; + bool starNetTXMsgSwitch15; + config.getStarNet15(starNetBand15, starNetCallsign15, starNetLogoff15, starNetInfo15, starNetPermanent15, starNetUserTimeout15, starNetGroupTimeout15, starNetCallsignSwitch15, starNetTXMsgSwitch15 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,starNetLink15 +#endif +); + + if (!starNetCallsign15.IsEmpty() && !starNetCallsign15.IsSameAs(wxT(" "))) { + wxString repeater = callsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand15); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_thread->addStarNet(starNetCallsign15, starNetLogoff15, repeater, starNetInfo15, starNetPermanent15, starNetUserTimeout15, starNetGroupTimeout15, starNetCallsignSwitch15, starNetTXMsgSwitch15, starNetLink15); + wxLogInfo(wxT("StarNet 15 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign15.c_str(), starNetLogoff15.c_str(), repeater.c_str(), starNetInfo15.c_str(), starNetPermanent15.c_str(), starNetUserTimeout15, starNetGroupTimeout15, int(starNetCallsignSwitch15), int(starNetTXMsgSwitch15), starNetLink15.c_str()); +#else + m_thread->addStarNet(starNetCallsign15, starNetLogoff15, repeater, starNetInfo15, starNetPermanent15, starNetUserTimeout15, starNetGroupTimeout15, starNetCallsignSwitch15, starNetTXMsgSwitch15); + wxLogInfo(wxT("StarNet 15 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign15.c_str(), starNetLogoff15.c_str(), repeater.c_str(), starNetInfo15.c_str(), starNetPermanent15.c_str(), starNetUserTimeout15, starNetGroupTimeout15, int(starNetCallsignSwitch15), int(starNetTXMsgSwitch15)); +#endif + } + + bool remoteEnabled; + wxString remotePassword; + unsigned int remotePort; + config.getRemote(remoteEnabled, remotePassword, remotePort); + wxLogInfo(wxT("Remote enabled set to %d, port set to %u"), int(remoteEnabled), remotePort); + m_thread->setRemote(remoteEnabled, remotePassword, remotePort); + + m_thread->setLog(logEnabled); + m_thread->setAddress(address); + m_thread->setCallsign(callsign); + + return true; +} + diff --git a/StarNetServer/StarNetServerAppD.h b/StarNetServer/StarNetServerAppD.h new file mode 100644 index 0000000..05380bb --- /dev/null +++ b/StarNetServer/StarNetServerAppD.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2010,2011 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 StarNetServerAppD_H +#define StarNetServerAppD_H + +#include "StarNetServerThread.h" + +#include +#include + +class CStarNetServerAppD { + +public: + CStarNetServerAppD(bool nolog, const wxString& logDir, const wxString& confDir); + ~CStarNetServerAppD(); + + bool init(); + + void run(); + +private: + bool m_nolog; + wxString m_logDir; + wxString m_confDir; + CStarNetServerThread* m_thread; + + bool createThread(); +}; + +#endif diff --git a/StarNetServer/StarNetServerCallsignSet.cpp b/StarNetServer/StarNetServerCallsignSet.cpp new file mode 100644 index 0000000..fdb68f9 --- /dev/null +++ b/StarNetServer/StarNetServerCallsignSet.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2010,2011 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 "StarNetServerCallsignSet.h" +#include "DStarDefines.h" +#include + +const unsigned int CALLSIGN_WIDTH = 120U; +const unsigned int ADDRESS_WIDTH = 120U; + +const unsigned int ADDRESS_LENGTH = 15U; + +const unsigned int BORDER_SIZE = 5U; + +CStarNetServerCallsignSet::CStarNetServerCallsignSet(wxWindow* parent, int id, const wxString& title, const wxString& callsign, const wxString& address) : +wxPanel(parent, id), +m_title(title), +m_callsign(NULL), +m_address(NULL) +{ + wxFlexGridSizer* sizer = new wxFlexGridSizer(3); + + wxStaticText* callsignLabel = new wxStaticText(this, -1, _("Callsign")); + sizer->Add(callsignLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxString call = callsign; + call.Truncate(LONG_CALLSIGN_LENGTH - 1U); + call.Trim(); + + m_callsign = new CCallsignTextCtrl(this, -1, call, wxDefaultPosition, wxSize(CALLSIGN_WIDTH, -1)); + m_callsign->SetMaxLength(LONG_CALLSIGN_LENGTH); + sizer->Add(m_callsign, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* gLabel = new wxStaticText(this, -1, wxT("G")); + sizer->Add(gLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxStaticText* addressLabel = new wxStaticText(this, -1, _("Address")); + sizer->Add(addressLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_address = new CAddressTextCtrl(this, -1, address, wxDefaultPosition, wxSize(ADDRESS_WIDTH, -1)); + m_address->SetMaxLength(ADDRESS_LENGTH); + sizer->Add(m_address, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + SetAutoLayout(true); + + SetSizer(sizer); +} + + +CStarNetServerCallsignSet::~CStarNetServerCallsignSet() +{ +} + +bool CStarNetServerCallsignSet::Validate() +{ + wxString callsign = getCallsign(); + + if (callsign.IsEmpty()) { + wxMessageDialog dialog(this, _("The Callsign Callsign is not valid"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + return true; +} + +wxString CStarNetServerCallsignSet::getCallsign() const +{ + wxString callsign = m_callsign->GetValue(); + + callsign.MakeUpper(); + + return callsign; +} + +wxString CStarNetServerCallsignSet::getAddress() const +{ + return m_address->GetValue(); +} diff --git a/StarNetServer/StarNetServerCallsignSet.h b/StarNetServer/StarNetServerCallsignSet.h new file mode 100644 index 0000000..8b05c7c --- /dev/null +++ b/StarNetServer/StarNetServerCallsignSet.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2010,2011 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 StarNetServerCallsignSet_H +#define StarNetServerCallsignSet_H + +#include "CallsignTextCtrl.h" +#include "AddressTextCtrl.h" +#include "Defs.h" + +#include + +class CStarNetServerCallsignSet : public wxPanel { +public: + CStarNetServerCallsignSet(wxWindow* parent, int id, const wxString& title, const wxString& callsign, const wxString& address); + virtual ~CStarNetServerCallsignSet(); + + virtual bool Validate(); + + virtual wxString getCallsign() const; + virtual wxString getAddress() const; + +private: + wxString m_title; + CCallsignTextCtrl* m_callsign; + CAddressTextCtrl* m_address; +}; + +#endif diff --git a/StarNetServer/StarNetServerConfig.cpp b/StarNetServer/StarNetServerConfig.cpp new file mode 100644 index 0000000..fd7bbac --- /dev/null +++ b/StarNetServer/StarNetServerConfig.cpp @@ -0,0 +1,2415 @@ +/* + * Copyright (C) 2010,2011,2012 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 "StarNetServerConfig.h" +#include "StarNetServerDefs.h" + +#include + +const wxString KEY_CALLSIGN = wxT("callsign"); +const wxString KEY_ADDRESS = wxT("address"); +const wxString KEY_IRCDDB_HOSTNAME = wxT("ircddbHostname"); +const wxString KEY_IRCDDB_USERNAME = wxT("ircddbUsername"); +const wxString KEY_IRCDDB_PASSWORD = wxT("ircddbPassword"); +const wxString KEY_STARNET_BAND1 = wxT("starNetBand1"); +const wxString KEY_STARNET_CALLSIGN1 = wxT("starNetCallsign1"); +const wxString KEY_STARNET_LOGOFF1 = wxT("starNetLogoff1"); +const wxString KEY_STARNET_INFO1 = wxT("starNetInfo1"); +const wxString KEY_STARNET_PERMANENT1 = wxT("starNetPermanent1"); +const wxString KEY_STARNET_USER_TIMEOUT1 = wxT("starNetUserTimeout1"); +const wxString KEY_STARNET_GROUP_TIMEOUT1 = wxT("starNetGroupTimeout1"); +const wxString KEY_STARNET_CALLSIGN_SWITCH1 = wxT("starNetCallsignSwitch1"); +const wxString KEY_STARNET_TXMSG_SWITCH1 = wxT("starNetTXMsgSwitch1"); +const wxString KEY_STARNET_REFLECTOR1 = wxT("starNetReflector1"); // DEXTRA_LINK +const wxString KEY_STARNET_BAND2 = wxT("starNetBand2"); +const wxString KEY_STARNET_CALLSIGN2 = wxT("starNetCallsign2"); +const wxString KEY_STARNET_LOGOFF2 = wxT("starNetLogoff2"); +const wxString KEY_STARNET_INFO2 = wxT("starNetInfo2"); +const wxString KEY_STARNET_PERMANENT2 = wxT("starNetPermanent2"); +const wxString KEY_STARNET_USER_TIMEOUT2 = wxT("starNetUserTimeout2"); +const wxString KEY_STARNET_GROUP_TIMEOUT2 = wxT("starNetGroupTimeout2"); +const wxString KEY_STARNET_CALLSIGN_SWITCH2 = wxT("starNetCallsignSwitch2"); +const wxString KEY_STARNET_TXMSG_SWITCH2 = wxT("starNetTXMsgSwitch2"); +const wxString KEY_STARNET_REFLECTOR2 = wxT("starNetReflector2"); // DEXTRA_LINK +const wxString KEY_STARNET_BAND3 = wxT("starNetBand3"); +const wxString KEY_STARNET_CALLSIGN3 = wxT("starNetCallsign3"); +const wxString KEY_STARNET_LOGOFF3 = wxT("starNetLogoff3"); +const wxString KEY_STARNET_INFO3 = wxT("starNetInfo3"); +const wxString KEY_STARNET_PERMANENT3 = wxT("starNetPermanent3"); +const wxString KEY_STARNET_USER_TIMEOUT3 = wxT("starNetUserTimeout3"); +const wxString KEY_STARNET_GROUP_TIMEOUT3 = wxT("starNetGroupTimeout3"); +const wxString KEY_STARNET_CALLSIGN_SWITCH3 = wxT("starNetCallsignSwitch3"); +const wxString KEY_STARNET_TXMSG_SWITCH3 = wxT("starNetTXMsgSwitch3"); +const wxString KEY_STARNET_REFLECTOR3 = wxT("starNetReflector3"); // DEXTRA_LINK +const wxString KEY_STARNET_BAND4 = wxT("starNetBand4"); +const wxString KEY_STARNET_CALLSIGN4 = wxT("starNetCallsign4"); +const wxString KEY_STARNET_LOGOFF4 = wxT("starNetLogoff4"); +const wxString KEY_STARNET_INFO4 = wxT("starNetInfo4"); +const wxString KEY_STARNET_PERMANENT4 = wxT("starNetPermanent4"); +const wxString KEY_STARNET_USER_TIMEOUT4 = wxT("starNetUserTimeout4"); +const wxString KEY_STARNET_GROUP_TIMEOUT4 = wxT("starNetGroupTimeout4"); +const wxString KEY_STARNET_CALLSIGN_SWITCH4 = wxT("starNetCallsignSwitch4"); +const wxString KEY_STARNET_TXMSG_SWITCH4 = wxT("starNetTXMsgSwitch4"); +const wxString KEY_STARNET_REFLECTOR4 = wxT("starNetReflector4"); // DEXTRA_LINK +const wxString KEY_STARNET_BAND5 = wxT("starNetBand5"); +const wxString KEY_STARNET_CALLSIGN5 = wxT("starNetCallsign5"); +const wxString KEY_STARNET_LOGOFF5 = wxT("starNetLogoff5"); +const wxString KEY_STARNET_INFO5 = wxT("starNetInfo5"); +const wxString KEY_STARNET_PERMANENT5 = wxT("starNetPermanent5"); +const wxString KEY_STARNET_USER_TIMEOUT5 = wxT("starNetUserTimeout5"); +const wxString KEY_STARNET_GROUP_TIMEOUT5 = wxT("starNetGroupTimeout5"); +const wxString KEY_STARNET_CALLSIGN_SWITCH5 = wxT("starNetCallsignSwitch5"); +const wxString KEY_STARNET_TXMSG_SWITCH5 = wxT("starNetTXMsgSwitch5"); +const wxString KEY_STARNET_REFLECTOR5 = wxT("starNetReflector5"); // DEXTRA_LINK +const wxString KEY_STARNET_BAND6 = wxT("starNetBand6"); +const wxString KEY_STARNET_CALLSIGN6 = wxT("starNetCallsign6"); +const wxString KEY_STARNET_LOGOFF6 = wxT("starNetLogoff6"); +const wxString KEY_STARNET_INFO6 = wxT("starNetInfo6"); +const wxString KEY_STARNET_PERMANENT6 = wxT("starNetPermanent6"); +const wxString KEY_STARNET_USER_TIMEOUT6 = wxT("starNetUserTimeout6"); +const wxString KEY_STARNET_GROUP_TIMEOUT6 = wxT("starNetGroupTimeout6"); +const wxString KEY_STARNET_CALLSIGN_SWITCH6 = wxT("starNetCallsignSwitch6"); +const wxString KEY_STARNET_TXMSG_SWITCH6 = wxT("starNetTXMsgSwitch6"); +const wxString KEY_STARNET_REFLECTOR6 = wxT("starNetReflector6"); // DEXTRA_LINK +const wxString KEY_STARNET_BAND7 = wxT("starNetBand7"); +const wxString KEY_STARNET_CALLSIGN7 = wxT("starNetCallsign7"); +const wxString KEY_STARNET_LOGOFF7 = wxT("starNetLogoff7"); +const wxString KEY_STARNET_INFO7 = wxT("starNetInfo7"); +const wxString KEY_STARNET_PERMANENT7 = wxT("starNetPermanent7"); +const wxString KEY_STARNET_USER_TIMEOUT7 = wxT("starNetUserTimeout7"); +const wxString KEY_STARNET_GROUP_TIMEOUT7 = wxT("starNetGroupTimeout7"); +const wxString KEY_STARNET_CALLSIGN_SWITCH7 = wxT("starNetCallsignSwitch7"); +const wxString KEY_STARNET_TXMSG_SWITCH7 = wxT("starNetTXMsgSwitch7"); +const wxString KEY_STARNET_REFLECTOR7 = wxT("starNetReflector7"); // DEXTRA_LINK +const wxString KEY_STARNET_BAND8 = wxT("starNetBand8"); +const wxString KEY_STARNET_CALLSIGN8 = wxT("starNetCallsign8"); +const wxString KEY_STARNET_LOGOFF8 = wxT("starNetLogoff8"); +const wxString KEY_STARNET_INFO8 = wxT("starNetInfo8"); +const wxString KEY_STARNET_PERMANENT8 = wxT("starNetPermanent8"); +const wxString KEY_STARNET_USER_TIMEOUT8 = wxT("starNetUserTimeout8"); +const wxString KEY_STARNET_GROUP_TIMEOUT8 = wxT("starNetGroupTimeout8"); +const wxString KEY_STARNET_CALLSIGN_SWITCH8 = wxT("starNetCallsignSwitch8"); +const wxString KEY_STARNET_TXMSG_SWITCH8 = wxT("starNetTXMsgSwitch8"); +const wxString KEY_STARNET_REFLECTOR8 = wxT("starNetReflector8"); // DEXTRA_LINK +const wxString KEY_STARNET_BAND9 = wxT("starNetBand9"); +const wxString KEY_STARNET_CALLSIGN9 = wxT("starNetCallsign9"); +const wxString KEY_STARNET_LOGOFF9 = wxT("starNetLogoff9"); +const wxString KEY_STARNET_INFO9 = wxT("starNetInfo9"); +const wxString KEY_STARNET_PERMANENT9 = wxT("starNetPermanent9"); +const wxString KEY_STARNET_USER_TIMEOUT9 = wxT("starNetUserTimeout9"); +const wxString KEY_STARNET_GROUP_TIMEOUT9 = wxT("starNetGroupTimeout9"); +const wxString KEY_STARNET_CALLSIGN_SWITCH9 = wxT("starNetCallsignSwitch9"); +const wxString KEY_STARNET_TXMSG_SWITCH9 = wxT("starNetTXMsgSwitch9"); +const wxString KEY_STARNET_REFLECTOR9 = wxT("starNetReflector9"); // DEXTRA_LINK +const wxString KEY_STARNET_BAND10 = wxT("starNetBand10"); +const wxString KEY_STARNET_CALLSIGN10 = wxT("starNetCallsign10"); +const wxString KEY_STARNET_LOGOFF10 = wxT("starNetLogoff10"); +const wxString KEY_STARNET_INFO10 = wxT("starNetInfo10"); +const wxString KEY_STARNET_PERMANENT10 = wxT("starNetPermanent10"); +const wxString KEY_STARNET_USER_TIMEOUT10 = wxT("starNetUserTimeout10"); +const wxString KEY_STARNET_GROUP_TIMEOUT10 = wxT("starNetGroupTimeout10"); +const wxString KEY_STARNET_CALLSIGN_SWITCH10 = wxT("starNetCallsignSwitch10"); +const wxString KEY_STARNET_TXMSG_SWITCH10 = wxT("starNetTXMsgSwitch10"); +const wxString KEY_STARNET_REFLECTOR10 = wxT("starNetReflector10"); // DEXTRA_LINK +const wxString KEY_STARNET_BAND11 = wxT("starNetBand11"); +const wxString KEY_STARNET_CALLSIGN11 = wxT("starNetCallsign11"); +const wxString KEY_STARNET_LOGOFF11 = wxT("starNetLogoff11"); +const wxString KEY_STARNET_INFO11 = wxT("starNetInfo11"); +const wxString KEY_STARNET_PERMANENT11 = wxT("starNetPermanent11"); +const wxString KEY_STARNET_USER_TIMEOUT11 = wxT("starNetUserTimeout11"); +const wxString KEY_STARNET_GROUP_TIMEOUT11 = wxT("starNetGroupTimeout11"); +const wxString KEY_STARNET_CALLSIGN_SWITCH11 = wxT("starNetCallsignSwitch11"); +const wxString KEY_STARNET_TXMSG_SWITCH11 = wxT("starNetTXMsgSwitch11"); +const wxString KEY_STARNET_REFLECTOR11 = wxT("starNetReflector11"); // DEXTRA_LINK +const wxString KEY_STARNET_BAND12 = wxT("starNetBand12"); +const wxString KEY_STARNET_CALLSIGN12 = wxT("starNetCallsign12"); +const wxString KEY_STARNET_LOGOFF12 = wxT("starNetLogoff12"); +const wxString KEY_STARNET_INFO12 = wxT("starNetInfo12"); +const wxString KEY_STARNET_PERMANENT12 = wxT("starNetPermanent12"); +const wxString KEY_STARNET_USER_TIMEOUT12 = wxT("starNetUserTimeout12"); +const wxString KEY_STARNET_GROUP_TIMEOUT12 = wxT("starNetGroupTimeout12"); +const wxString KEY_STARNET_CALLSIGN_SWITCH12 = wxT("starNetCallsignSwitch12"); +const wxString KEY_STARNET_TXMSG_SWITCH12 = wxT("starNetTXMsgSwitch12"); +const wxString KEY_STARNET_REFLECTOR12 = wxT("starNetReflector12"); // DEXTRA_LINK +const wxString KEY_STARNET_BAND13 = wxT("starNetBand13"); +const wxString KEY_STARNET_CALLSIGN13 = wxT("starNetCallsign13"); +const wxString KEY_STARNET_LOGOFF13 = wxT("starNetLogoff13"); +const wxString KEY_STARNET_INFO13 = wxT("starNetInfo13"); +const wxString KEY_STARNET_PERMANENT13 = wxT("starNetPermanent13"); +const wxString KEY_STARNET_USER_TIMEOUT13 = wxT("starNetUserTimeout13"); +const wxString KEY_STARNET_GROUP_TIMEOUT13 = wxT("starNetGroupTimeout13"); +const wxString KEY_STARNET_CALLSIGN_SWITCH13 = wxT("starNetCallsignSwitch13"); +const wxString KEY_STARNET_TXMSG_SWITCH13 = wxT("starNetTXMsgSwitch13"); +const wxString KEY_STARNET_REFLECTOR13 = wxT("starNetReflector13"); // DEXTRA_LINK +const wxString KEY_STARNET_BAND14 = wxT("starNetBand14"); +const wxString KEY_STARNET_CALLSIGN14 = wxT("starNetCallsign14"); +const wxString KEY_STARNET_LOGOFF14 = wxT("starNetLogoff14"); +const wxString KEY_STARNET_INFO14 = wxT("starNetInfo14"); +const wxString KEY_STARNET_PERMANENT14 = wxT("starNetPermanent14"); +const wxString KEY_STARNET_USER_TIMEOUT14 = wxT("starNetUserTimeout14"); +const wxString KEY_STARNET_GROUP_TIMEOUT14 = wxT("starNetGroupTimeout14"); +const wxString KEY_STARNET_CALLSIGN_SWITCH14 = wxT("starNetCallsignSwitch14"); +const wxString KEY_STARNET_TXMSG_SWITCH14 = wxT("starNetTXMsgSwitch14"); +const wxString KEY_STARNET_REFLECTOR14 = wxT("starNetReflector14"); // DEXTRA_LINK +const wxString KEY_STARNET_BAND15 = wxT("starNetBand15"); +const wxString KEY_STARNET_CALLSIGN15 = wxT("starNetCallsign15"); +const wxString KEY_STARNET_LOGOFF15 = wxT("starNetLogoff15"); +const wxString KEY_STARNET_INFO15 = wxT("starNetInfo15"); +const wxString KEY_STARNET_PERMANENT15 = wxT("starNetPermanent15"); +const wxString KEY_STARNET_USER_TIMEOUT15 = wxT("starNetUserTimeout15"); +const wxString KEY_STARNET_GROUP_TIMEOUT15 = wxT("starNetGroupTimeout15"); +const wxString KEY_STARNET_CALLSIGN_SWITCH15 = wxT("starNetCallsignSwitch15"); +const wxString KEY_STARNET_TXMSG_SWITCH15 = wxT("starNetTXMsgSwitch15"); +const wxString KEY_STARNET_REFLECTOR15 = wxT("starNetReflector15"); // DEXTRA_LINK +const wxString KEY_REMOTE_ENABLED = wxT("remoteEnabled"); +const wxString KEY_REMOTE_PASSWORD = wxT("remotePassword"); +const wxString KEY_REMOTE_PORT = wxT("remotePort"); +const wxString KEY_LOG_ENABLED = wxT("logEnabled"); +const wxString KEY_WINDOW_X = wxT("windowX"); +const wxString KEY_WINDOW_Y = wxT("windowY"); + +const wxString DEFAULT_CALLSIGN = wxEmptyString; +const wxString DEFAULT_ADDRESS = wxEmptyString; +const wxString DEFAULT_IRCDDB_HOSTNAME = wxT("group1-irc.ircddb.net"); +const wxString DEFAULT_IRCDDB_USERNAME = wxEmptyString; +const wxString DEFAULT_IRCDDB_PASSWORD = wxEmptyString; +const wxString DEFAULT_STARNET_BAND = wxEmptyString; +const wxString DEFAULT_STARNET_CALLSIGN = wxEmptyString; +const wxString DEFAULT_STARNET_LOGOFF = wxEmptyString; +const wxString DEFAULT_STARNET_INFO = wxEmptyString; +const wxString DEFAULT_STARNET_PERMANENT = wxEmptyString; +const unsigned int DEFAULT_STARNET_USER_TIMEOUT = 300U; +const unsigned int DEFAULT_STARNET_GROUP_TIMEOUT = 300U; +const STARNET_CALLSIGN_SWITCH DEFAULT_STARNET_CALLSIGN_SWITCH = SCS_GROUP_CALLSIGN; +const bool DEFAULT_STARNET_TXMSG_SWITCH = true; +const wxString DEFAULT_STARNET_REFLECTOR = wxEmptyString; +const bool DEFAULT_REMOTE_ENABLED = false; +const wxString DEFAULT_REMOTE_PASSWORD = wxEmptyString; +const unsigned int DEFAULT_REMOTE_PORT = 0U; +const bool DEFAULT_LOG_ENABLED = false; +const int DEFAULT_WINDOW_X = -1; +const int DEFAULT_WINDOW_Y = -1; + +#if defined(__WINDOWS__) + +CStarNetServerConfig::CStarNetServerConfig(wxConfigBase* config, const wxString& dir) : +m_config(config), +m_fileName(), +m_callsign(DEFAULT_CALLSIGN), +m_address(DEFAULT_ADDRESS), +m_ircddbHostname(DEFAULT_IRCDDB_HOSTNAME), +m_ircddbUsername(DEFAULT_IRCDDB_USERNAME), +m_ircddbPassword(DEFAULT_IRCDDB_PASSWORD), +m_starNet1Band(DEFAULT_STARNET_BAND), +m_starNet1Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet1Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet1Info(DEFAULT_STARNET_INFO), +m_starNet1Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet1UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet1GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet1CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet1TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet1Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet2Band(DEFAULT_STARNET_BAND), +m_starNet2Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet2Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet2Info(DEFAULT_STARNET_INFO), +m_starNet2Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet2UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet2GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet2CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet2TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet2Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet3Band(DEFAULT_STARNET_BAND), +m_starNet3Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet3Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet3Info(DEFAULT_STARNET_INFO), +m_starNet3Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet3UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet3GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet3CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet3TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet3Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet4Band(DEFAULT_STARNET_BAND), +m_starNet4Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet4Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet4Info(DEFAULT_STARNET_INFO), +m_starNet4Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet4UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet4GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet4CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet4TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet4Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet5Band(DEFAULT_STARNET_BAND), +m_starNet5Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet5Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet5Info(DEFAULT_STARNET_INFO), +m_starNet5Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet5UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet5GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet5CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet5TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet5Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet6Band(DEFAULT_STARNET_BAND), +m_starNet6Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet6Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet6Info(DEFAULT_STARNET_INFO), +m_starNet6Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet6UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet6GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet6CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet6TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet6Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet7Band(DEFAULT_STARNET_BAND), +m_starNet7Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet7Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet7Info(DEFAULT_STARNET_INFO), +m_starNet7Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet7UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet7GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet7CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet7TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet7Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet8Band(DEFAULT_STARNET_BAND), +m_starNet8Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet8Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet8Info(DEFAULT_STARNET_INFO), +m_starNet8Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet8UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet8GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet8CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet8TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet8Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet9Band(DEFAULT_STARNET_BAND), +m_starNet9Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet9Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet9Info(DEFAULT_STARNET_INFO), +m_starNet9Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet9UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet9GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet9CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet9TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet9Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet10Band(DEFAULT_STARNET_BAND), +m_starNet10Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet10Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet10Info(DEFAULT_STARNET_INFO), +m_starNet10Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet10UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet10GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet10CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet10TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet10Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet11Band(DEFAULT_STARNET_BAND), +m_starNet11Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet11Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet11Info(DEFAULT_STARNET_INFO), +m_starNet11Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet11UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet11GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet11CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet11TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet11Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet12Band(DEFAULT_STARNET_BAND), +m_starNet12Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet12Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet12Info(DEFAULT_STARNET_INFO), +m_starNet12Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet12UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet12GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet12CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet12TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet12Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet13Band(DEFAULT_STARNET_BAND), +m_starNet13Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet13Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet13Info(DEFAULT_STARNET_INFO), +m_starNet13Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet13UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet13GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet13CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet13TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet13Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet14Band(DEFAULT_STARNET_BAND), +m_starNet14Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet14Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet14Info(DEFAULT_STARNET_INFO), +m_starNet14Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet14UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet14GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet14CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet14TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet14Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet15Band(DEFAULT_STARNET_BAND), +m_starNet15Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet15Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet15Info(DEFAULT_STARNET_INFO), +m_starNet15Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet15UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet15GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet15CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet15TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet15Reflector(DEFAULT_STARNET_REFLECTOR), +m_remoteEnabled(DEFAULT_REMOTE_ENABLED), +m_remotePassword(DEFAULT_REMOTE_PASSWORD), +m_remotePort(DEFAULT_REMOTE_PORT), +m_logEnabled(DEFAULT_LOG_ENABLED), +m_x(DEFAULT_WINDOW_X), +m_y(DEFAULT_WINDOW_Y) +{ + wxASSERT(config != NULL); + wxASSERT(!dir.IsEmpty()); + + m_fileName.Assign(dir, CONFIG_FILE_NAME); + + wxTextFile file(m_fileName.GetFullPath()); + + long temp; + + m_config->Read(wxT("/") + KEY_CALLSIGN, &m_callsign, DEFAULT_CALLSIGN); + + m_config->Read(wxT("/") + KEY_ADDRESS, &m_address, DEFAULT_ADDRESS); + + m_config->Read(wxT("/") + KEY_IRCDDB_HOSTNAME, &m_ircddbHostname, DEFAULT_IRCDDB_HOSTNAME); + + m_config->Read(wxT("/") + KEY_IRCDDB_USERNAME, &m_ircddbUsername, DEFAULT_IRCDDB_USERNAME); + + m_config->Read(wxT("/") + KEY_IRCDDB_PASSWORD, &m_ircddbPassword, DEFAULT_IRCDDB_PASSWORD); + + m_config->Read(wxT("/") + KEY_STARNET_BAND1, &m_starNet1Band, DEFAULT_STARNET_BAND); + + m_config->Read(wxT("/") + KEY_STARNET_CALLSIGN1, &m_starNet1Callsign, DEFAULT_STARNET_CALLSIGN); + + m_config->Read(wxT("/") + KEY_STARNET_LOGOFF1, &m_starNet1Logoff, DEFAULT_STARNET_LOGOFF); + + m_config->Read(wxT("/") + KEY_STARNET_INFO1, &m_starNet1Info, DEFAULT_STARNET_INFO); + + m_config->Read(wxT("/") + KEY_STARNET_PERMANENT1, &m_starNet1Permanent, DEFAULT_STARNET_PERMANENT); + + m_config->Read(wxT("/") + KEY_STARNET_USER_TIMEOUT1, &temp, long(DEFAULT_STARNET_USER_TIMEOUT)); + m_starNet1UserTimeout = (unsigned int)temp; + + m_config->Read(wxT("/") + KEY_STARNET_GROUP_TIMEOUT1, &temp, long(DEFAULT_STARNET_GROUP_TIMEOUT)); + m_starNet1GroupTimeout = (unsigned int)temp; + + m_config->Read(wxT("/") + KEY_STARNET_CALLSIGN_SWITCH1, &temp, long(DEFAULT_STARNET_CALLSIGN_SWITCH)); + m_starNet1CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp); + + m_config->Read(wxT("/") + KEY_STARNET_TXMSG_SWITCH1, &m_starNet1TxMsgSwitch, DEFAULT_STARNET_TXMSG_SWITCH); + + m_config->Read(wxT("/") + KEY_STARNET_REFLECTOR1, &m_starNet1Reflector, DEFAULT_STARNET_REFLECTOR); + + m_config->Read(wxT("/") + KEY_STARNET_BAND2, &m_starNet2Band, DEFAULT_STARNET_BAND); + + m_config->Read(wxT("/") + KEY_STARNET_CALLSIGN2, &m_starNet2Callsign, DEFAULT_STARNET_CALLSIGN); + + m_config->Read(wxT("/") + KEY_STARNET_LOGOFF2, &m_starNet2Logoff, DEFAULT_STARNET_LOGOFF); + + m_config->Read(wxT("/") + KEY_STARNET_INFO2, &m_starNet2Info, DEFAULT_STARNET_INFO); + + m_config->Read(wxT("/") + KEY_STARNET_PERMANENT2, &m_starNet2Permanent, DEFAULT_STARNET_PERMANENT); + + m_config->Read(wxT("/") + KEY_STARNET_USER_TIMEOUT2, &temp, long(DEFAULT_STARNET_USER_TIMEOUT)); + m_starNet2UserTimeout = (unsigned int)temp; + + m_config->Read(wxT("/") + KEY_STARNET_GROUP_TIMEOUT2, &temp, long(DEFAULT_STARNET_GROUP_TIMEOUT)); + m_starNet2GroupTimeout = (unsigned int)temp; + + m_config->Read(wxT("/") + KEY_STARNET_CALLSIGN_SWITCH2, &temp, long(DEFAULT_STARNET_CALLSIGN_SWITCH)); + m_starNet2CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp); + + m_config->Read(wxT("/") + KEY_STARNET_TXMSG_SWITCH2, &m_starNet2TxMsgSwitch, DEFAULT_STARNET_TXMSG_SWITCH); + + m_config->Read(wxT("/") + KEY_STARNET_REFLECTOR2, &m_starNet2Reflector, DEFAULT_STARNET_REFLECTOR); + + m_config->Read(wxT("/") + KEY_STARNET_BAND3, &m_starNet3Band, DEFAULT_STARNET_BAND); + + m_config->Read(wxT("/") + KEY_STARNET_CALLSIGN3, &m_starNet3Callsign, DEFAULT_STARNET_CALLSIGN); + + m_config->Read(wxT("/") + KEY_STARNET_LOGOFF3, &m_starNet3Logoff, DEFAULT_STARNET_LOGOFF); + + m_config->Read(wxT("/") + KEY_STARNET_INFO3, &m_starNet3Info, DEFAULT_STARNET_INFO); + + m_config->Read(wxT("/") + KEY_STARNET_PERMANENT3, &m_starNet3Permanent, DEFAULT_STARNET_PERMANENT); + + m_config->Read(wxT("/") + KEY_STARNET_USER_TIMEOUT3, &temp, long(DEFAULT_STARNET_USER_TIMEOUT)); + m_starNet3UserTimeout = (unsigned int)temp; + + m_config->Read(wxT("/") + KEY_STARNET_GROUP_TIMEOUT3, &temp, long(DEFAULT_STARNET_GROUP_TIMEOUT)); + m_starNet3GroupTimeout = (unsigned int)temp; + + m_config->Read(wxT("/") + KEY_STARNET_CALLSIGN_SWITCH3, &temp, long(DEFAULT_STARNET_CALLSIGN_SWITCH)); + m_starNet3CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp); + + m_config->Read(wxT("/") + KEY_STARNET_TXMSG_SWITCH3, &m_starNet3TxMsgSwitch, DEFAULT_STARNET_TXMSG_SWITCH); + + m_config->Read(wxT("/") + KEY_STARNET_REFLECTOR3, &m_starNet3Reflector, DEFAULT_STARNET_REFLECTOR); + + m_config->Read(wxT("/") + KEY_STARNET_BAND4, &m_starNet4Band, DEFAULT_STARNET_BAND); + + m_config->Read(wxT("/") + KEY_STARNET_CALLSIGN4, &m_starNet4Callsign, DEFAULT_STARNET_CALLSIGN); + + m_config->Read(wxT("/") + KEY_STARNET_LOGOFF4, &m_starNet4Logoff, DEFAULT_STARNET_LOGOFF); + + m_config->Read(wxT("/") + KEY_STARNET_INFO4, &m_starNet4Info, DEFAULT_STARNET_INFO); + + m_config->Read(wxT("/") + KEY_STARNET_PERMANENT4, &m_starNet4Permanent, DEFAULT_STARNET_PERMANENT); + + m_config->Read(wxT("/") + KEY_STARNET_USER_TIMEOUT4, &temp, long(DEFAULT_STARNET_USER_TIMEOUT)); + m_starNet4UserTimeout = (unsigned int)temp; + + m_config->Read(wxT("/") + KEY_STARNET_GROUP_TIMEOUT4, &temp, long(DEFAULT_STARNET_GROUP_TIMEOUT)); + m_starNet4GroupTimeout = (unsigned int)temp; + + m_config->Read(wxT("/") + KEY_STARNET_CALLSIGN_SWITCH4, &temp, long(DEFAULT_STARNET_CALLSIGN_SWITCH)); + m_starNet4CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp); + + m_config->Read(wxT("/") + KEY_STARNET_TXMSG_SWITCH4, &m_starNet4TxMsgSwitch, DEFAULT_STARNET_TXMSG_SWITCH); + + m_config->Read(wxT("/") + KEY_STARNET_REFLECTOR4, &m_starNet4Reflector, DEFAULT_STARNET_REFLECTOR); + + m_config->Read(wxT("/") + KEY_STARNET_BAND5, &m_starNet5Band, DEFAULT_STARNET_BAND); + + m_config->Read(wxT("/") + KEY_STARNET_CALLSIGN5, &m_starNet5Callsign, DEFAULT_STARNET_CALLSIGN); + + m_config->Read(wxT("/") + KEY_STARNET_LOGOFF5, &m_starNet5Logoff, DEFAULT_STARNET_LOGOFF); + + m_config->Read(wxT("/") + KEY_STARNET_INFO5, &m_starNet5Info, DEFAULT_STARNET_INFO); + + m_config->Read(wxT("/") + KEY_STARNET_PERMANENT5, &m_starNet5Permanent, DEFAULT_STARNET_PERMANENT); + + m_config->Read(wxT("/") + KEY_STARNET_USER_TIMEOUT5, &temp, long(DEFAULT_STARNET_USER_TIMEOUT)); + m_starNet5UserTimeout = (unsigned int)temp; + + m_config->Read(wxT("/") + KEY_STARNET_GROUP_TIMEOUT5, &temp, long(DEFAULT_STARNET_GROUP_TIMEOUT)); + m_starNet5GroupTimeout = (unsigned int)temp; + + m_config->Read(wxT("/") + KEY_STARNET_CALLSIGN_SWITCH5, &temp, long(DEFAULT_STARNET_CALLSIGN_SWITCH)); + m_starNet5CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp); + + m_config->Read(wxT("/") + KEY_STARNET_TXMSG_SWITCH5, &m_starNet5TxMsgSwitch, DEFAULT_STARNET_TXMSG_SWITCH); + + m_config->Read(wxT("/") + KEY_STARNET_REFLECTOR5, &m_starNet5Reflector, DEFAULT_STARNET_REFLECTOR); + + m_config->Read(wxT("/") + KEY_STARNET_BAND6, &m_starNet6Band, DEFAULT_STARNET_BAND); + + m_config->Read(wxT("/") + KEY_STARNET_CALLSIGN6, &m_starNet6Callsign, DEFAULT_STARNET_CALLSIGN); + + m_config->Read(wxT("/") + KEY_STARNET_LOGOFF6, &m_starNet6Logoff, DEFAULT_STARNET_LOGOFF); + + m_config->Read(wxT("/") + KEY_STARNET_INFO6, &m_starNet6Info, DEFAULT_STARNET_INFO); + + m_config->Read(wxT("/") + KEY_STARNET_PERMANENT6, &m_starNet6Permanent, DEFAULT_STARNET_PERMANENT); + + m_config->Read(wxT("/") + KEY_STARNET_USER_TIMEOUT6, &temp, long(DEFAULT_STARNET_USER_TIMEOUT)); + m_starNet6UserTimeout = (unsigned int)temp; + + m_config->Read(wxT("/") + KEY_STARNET_GROUP_TIMEOUT6, &temp, long(DEFAULT_STARNET_GROUP_TIMEOUT)); + m_starNet6GroupTimeout = (unsigned int)temp; + + m_config->Read(wxT("/") + KEY_STARNET_CALLSIGN_SWITCH6, &temp, long(DEFAULT_STARNET_CALLSIGN_SWITCH)); + m_starNet6CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp); + + m_config->Read(wxT("/") + KEY_STARNET_TXMSG_SWITCH6, &m_starNet6TxMsgSwitch, DEFAULT_STARNET_TXMSG_SWITCH); + + m_config->Read(wxT("/") + KEY_STARNET_REFLECTOR6, &m_starNet6Reflector, DEFAULT_STARNET_REFLECTOR); + + m_config->Read(wxT("/") + KEY_STARNET_BAND7, &m_starNet7Band, DEFAULT_STARNET_BAND); + + m_config->Read(wxT("/") + KEY_STARNET_CALLSIGN7, &m_starNet7Callsign, DEFAULT_STARNET_CALLSIGN); + + m_config->Read(wxT("/") + KEY_STARNET_LOGOFF7, &m_starNet7Logoff, DEFAULT_STARNET_LOGOFF); + + m_config->Read(wxT("/") + KEY_STARNET_INFO7, &m_starNet7Info, DEFAULT_STARNET_INFO); + + m_config->Read(wxT("/") + KEY_STARNET_PERMANENT7, &m_starNet7Permanent, DEFAULT_STARNET_PERMANENT); + + m_config->Read(wxT("/") + KEY_STARNET_USER_TIMEOUT7, &temp, long(DEFAULT_STARNET_USER_TIMEOUT)); + m_starNet7UserTimeout = (unsigned int)temp; + + m_config->Read(wxT("/") + KEY_STARNET_GROUP_TIMEOUT7, &temp, long(DEFAULT_STARNET_GROUP_TIMEOUT)); + m_starNet7GroupTimeout = (unsigned int)temp; + + m_config->Read(wxT("/") + KEY_STARNET_CALLSIGN_SWITCH7, &temp, long(DEFAULT_STARNET_CALLSIGN_SWITCH)); + m_starNet7CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp); + + m_config->Read(wxT("/") + KEY_STARNET_TXMSG_SWITCH7, &m_starNet7TxMsgSwitch, DEFAULT_STARNET_TXMSG_SWITCH); + + m_config->Read(wxT("/") + KEY_STARNET_REFLECTOR7, &m_starNet7Reflector, DEFAULT_STARNET_REFLECTOR); + + m_config->Read(wxT("/") + KEY_STARNET_BAND8, &m_starNet8Band, DEFAULT_STARNET_BAND); + + m_config->Read(wxT("/") + KEY_STARNET_CALLSIGN8, &m_starNet8Callsign, DEFAULT_STARNET_CALLSIGN); + + m_config->Read(wxT("/") + KEY_STARNET_LOGOFF8, &m_starNet8Logoff, DEFAULT_STARNET_LOGOFF); + + m_config->Read(wxT("/") + KEY_STARNET_INFO8, &m_starNet8Info, DEFAULT_STARNET_INFO); + + m_config->Read(wxT("/") + KEY_STARNET_PERMANENT8, &m_starNet8Permanent, DEFAULT_STARNET_PERMANENT); + + m_config->Read(wxT("/") + KEY_STARNET_USER_TIMEOUT8, &temp, long(DEFAULT_STARNET_USER_TIMEOUT)); + m_starNet8UserTimeout = (unsigned int)temp; + + m_config->Read(wxT("/") + KEY_STARNET_GROUP_TIMEOUT8, &temp, long(DEFAULT_STARNET_GROUP_TIMEOUT)); + m_starNet8GroupTimeout = (unsigned int)temp; + + m_config->Read(wxT("/") + KEY_STARNET_CALLSIGN_SWITCH8, &temp, long(DEFAULT_STARNET_CALLSIGN_SWITCH)); + m_starNet8CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp); + + m_config->Read(wxT("/") + KEY_STARNET_TXMSG_SWITCH8, &m_starNet8TxMsgSwitch, DEFAULT_STARNET_TXMSG_SWITCH); + + m_config->Read(wxT("/") + KEY_STARNET_REFLECTOR8, &m_starNet8Reflector, DEFAULT_STARNET_REFLECTOR); + + m_config->Read(wxT("/") + KEY_STARNET_BAND9, &m_starNet9Band, DEFAULT_STARNET_BAND); + + m_config->Read(wxT("/") + KEY_STARNET_CALLSIGN9, &m_starNet9Callsign, DEFAULT_STARNET_CALLSIGN); + + m_config->Read(wxT("/") + KEY_STARNET_LOGOFF9, &m_starNet9Logoff, DEFAULT_STARNET_LOGOFF); + + m_config->Read(wxT("/") + KEY_STARNET_INFO9, &m_starNet9Info, DEFAULT_STARNET_INFO); + + m_config->Read(wxT("/") + KEY_STARNET_PERMANENT9, &m_starNet9Permanent, DEFAULT_STARNET_PERMANENT); + + m_config->Read(wxT("/") + KEY_STARNET_USER_TIMEOUT9, &temp, long(DEFAULT_STARNET_USER_TIMEOUT)); + m_starNet9UserTimeout = (unsigned int)temp; + + m_config->Read(wxT("/") + KEY_STARNET_GROUP_TIMEOUT9, &temp, long(DEFAULT_STARNET_GROUP_TIMEOUT)); + m_starNet9GroupTimeout = (unsigned int)temp; + + m_config->Read(wxT("/") + KEY_STARNET_CALLSIGN_SWITCH9, &temp, long(DEFAULT_STARNET_CALLSIGN_SWITCH)); + m_starNet9CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp); + + m_config->Read(wxT("/") + KEY_STARNET_TXMSG_SWITCH9, &m_starNet9TxMsgSwitch, DEFAULT_STARNET_TXMSG_SWITCH); + + m_config->Read(wxT("/") + KEY_STARNET_REFLECTOR9, &m_starNet9Reflector, DEFAULT_STARNET_REFLECTOR); + + m_config->Read(wxT("/") + KEY_STARNET_BAND10, &m_starNet10Band, DEFAULT_STARNET_BAND); + + m_config->Read(wxT("/") + KEY_STARNET_CALLSIGN10, &m_starNet10Callsign, DEFAULT_STARNET_CALLSIGN); + + m_config->Read(wxT("/") + KEY_STARNET_LOGOFF10, &m_starNet10Logoff, DEFAULT_STARNET_LOGOFF); + + m_config->Read(wxT("/") + KEY_STARNET_INFO10, &m_starNet10Info, DEFAULT_STARNET_INFO); + + m_config->Read(wxT("/") + KEY_STARNET_PERMANENT10, &m_starNet10Permanent, DEFAULT_STARNET_PERMANENT); + + m_config->Read(wxT("/") + KEY_STARNET_USER_TIMEOUT10, &temp, long(DEFAULT_STARNET_USER_TIMEOUT)); + m_starNet10UserTimeout = (unsigned int)temp; + + m_config->Read(wxT("/") + KEY_STARNET_GROUP_TIMEOUT10, &temp, long(DEFAULT_STARNET_GROUP_TIMEOUT)); + m_starNet10GroupTimeout = (unsigned int)temp; + + m_config->Read(wxT("/") + KEY_STARNET_CALLSIGN_SWITCH10, &temp, long(DEFAULT_STARNET_CALLSIGN_SWITCH)); + m_starNet10CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp); + + m_config->Read(wxT("/") + KEY_STARNET_TXMSG_SWITCH10, &m_starNet10TxMsgSwitch, DEFAULT_STARNET_TXMSG_SWITCH); + + m_config->Read(wxT("/") + KEY_STARNET_REFLECTOR10, &m_starNet10Reflector, DEFAULT_STARNET_REFLECTOR); + + m_config->Read(wxT("/") + KEY_STARNET_BAND11, &m_starNet11Band, DEFAULT_STARNET_BAND); + + m_config->Read(wxT("/") + KEY_STARNET_CALLSIGN11, &m_starNet11Callsign, DEFAULT_STARNET_CALLSIGN); + + m_config->Read(wxT("/") + KEY_STARNET_LOGOFF11, &m_starNet11Logoff, DEFAULT_STARNET_LOGOFF); + + m_config->Read(wxT("/") + KEY_STARNET_INFO11, &m_starNet11Info, DEFAULT_STARNET_INFO); + + m_config->Read(wxT("/") + KEY_STARNET_PERMANENT11, &m_starNet11Permanent, DEFAULT_STARNET_PERMANENT); + + m_config->Read(wxT("/") + KEY_STARNET_USER_TIMEOUT11, &temp, long(DEFAULT_STARNET_USER_TIMEOUT)); + m_starNet11UserTimeout = (unsigned int)temp; + + m_config->Read(wxT("/") + KEY_STARNET_GROUP_TIMEOUT11, &temp, long(DEFAULT_STARNET_GROUP_TIMEOUT)); + m_starNet11GroupTimeout = (unsigned int)temp; + + m_config->Read(wxT("/") + KEY_STARNET_CALLSIGN_SWITCH11, &temp, long(DEFAULT_STARNET_CALLSIGN_SWITCH)); + m_starNet11CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp); + + m_config->Read(wxT("/") + KEY_STARNET_TXMSG_SWITCH11, &m_starNet11TxMsgSwitch, DEFAULT_STARNET_TXMSG_SWITCH); + + m_config->Read(wxT("/") + KEY_STARNET_REFLECTOR11, &m_starNet11Reflector, DEFAULT_STARNET_REFLECTOR); + + m_config->Read(wxT("/") + KEY_STARNET_BAND12, &m_starNet12Band, DEFAULT_STARNET_BAND); + + m_config->Read(wxT("/") + KEY_STARNET_CALLSIGN12, &m_starNet12Callsign, DEFAULT_STARNET_CALLSIGN); + + m_config->Read(wxT("/") + KEY_STARNET_LOGOFF12, &m_starNet12Logoff, DEFAULT_STARNET_LOGOFF); + + m_config->Read(wxT("/") + KEY_STARNET_INFO12, &m_starNet12Info, DEFAULT_STARNET_INFO); + + m_config->Read(wxT("/") + KEY_STARNET_PERMANENT12, &m_starNet12Permanent, DEFAULT_STARNET_PERMANENT); + + m_config->Read(wxT("/") + KEY_STARNET_USER_TIMEOUT12, &temp, long(DEFAULT_STARNET_USER_TIMEOUT)); + m_starNet12UserTimeout = (unsigned int)temp; + + m_config->Read(wxT("/") + KEY_STARNET_GROUP_TIMEOUT12, &temp, long(DEFAULT_STARNET_GROUP_TIMEOUT)); + m_starNet12GroupTimeout = (unsigned int)temp; + + m_config->Read(wxT("/") + KEY_STARNET_CALLSIGN_SWITCH12, &temp, long(DEFAULT_STARNET_CALLSIGN_SWITCH)); + m_starNet12CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp); + + m_config->Read(wxT("/") + KEY_STARNET_TXMSG_SWITCH12, &m_starNet12TxMsgSwitch, DEFAULT_STARNET_TXMSG_SWITCH); + + m_config->Read(wxT("/") + KEY_STARNET_REFLECTOR12, &m_starNet12Reflector, DEFAULT_STARNET_REFLECTOR); + + m_config->Read(wxT("/") + KEY_STARNET_BAND13, &m_starNet13Band, DEFAULT_STARNET_BAND); + + m_config->Read(wxT("/") + KEY_STARNET_CALLSIGN13, &m_starNet13Callsign, DEFAULT_STARNET_CALLSIGN); + + m_config->Read(wxT("/") + KEY_STARNET_LOGOFF13, &m_starNet13Logoff, DEFAULT_STARNET_LOGOFF); + + m_config->Read(wxT("/") + KEY_STARNET_INFO13, &m_starNet13Info, DEFAULT_STARNET_INFO); + + m_config->Read(wxT("/") + KEY_STARNET_PERMANENT13, &m_starNet13Permanent, DEFAULT_STARNET_PERMANENT); + + m_config->Read(wxT("/") + KEY_STARNET_USER_TIMEOUT13, &temp, long(DEFAULT_STARNET_USER_TIMEOUT)); + m_starNet13UserTimeout = (unsigned int)temp; + + m_config->Read(wxT("/") + KEY_STARNET_GROUP_TIMEOUT13, &temp, long(DEFAULT_STARNET_GROUP_TIMEOUT)); + m_starNet13GroupTimeout = (unsigned int)temp; + + m_config->Read(wxT("/") + KEY_STARNET_CALLSIGN_SWITCH13, &temp, long(DEFAULT_STARNET_CALLSIGN_SWITCH)); + m_starNet13CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp); + + m_config->Read(wxT("/") + KEY_STARNET_TXMSG_SWITCH13, &m_starNet13TxMsgSwitch, DEFAULT_STARNET_TXMSG_SWITCH); + + m_config->Read(wxT("/") + KEY_STARNET_REFLECTOR13, &m_starNet13Reflector, DEFAULT_STARNET_REFLECTOR); + + m_config->Read(wxT("/") + KEY_STARNET_BAND14, &m_starNet14Band, DEFAULT_STARNET_BAND); + + m_config->Read(wxT("/") + KEY_STARNET_CALLSIGN14, &m_starNet14Callsign, DEFAULT_STARNET_CALLSIGN); + + m_config->Read(wxT("/") + KEY_STARNET_LOGOFF14, &m_starNet14Logoff, DEFAULT_STARNET_LOGOFF); + + m_config->Read(wxT("/") + KEY_STARNET_INFO14, &m_starNet14Info, DEFAULT_STARNET_INFO); + + m_config->Read(wxT("/") + KEY_STARNET_PERMANENT14, &m_starNet14Permanent, DEFAULT_STARNET_PERMANENT); + + m_config->Read(wxT("/") + KEY_STARNET_USER_TIMEOUT14, &temp, long(DEFAULT_STARNET_USER_TIMEOUT)); + m_starNet14UserTimeout = (unsigned int)temp; + + m_config->Read(wxT("/") + KEY_STARNET_GROUP_TIMEOUT14, &temp, long(DEFAULT_STARNET_GROUP_TIMEOUT)); + m_starNet14GroupTimeout = (unsigned int)temp; + + m_config->Read(wxT("/") + KEY_STARNET_CALLSIGN_SWITCH14, &temp, long(DEFAULT_STARNET_CALLSIGN_SWITCH)); + m_starNet14CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp); + + m_config->Read(wxT("/") + KEY_STARNET_TXMSG_SWITCH14, &m_starNet14TxMsgSwitch, DEFAULT_STARNET_TXMSG_SWITCH); + + m_config->Read(wxT("/") + KEY_STARNET_REFLECTOR14, &m_starNet14Reflector, DEFAULT_STARNET_REFLECTOR); + + m_config->Read(wxT("/") + KEY_STARNET_BAND15, &m_starNet15Band, DEFAULT_STARNET_BAND); + + m_config->Read(wxT("/") + KEY_STARNET_CALLSIGN15, &m_starNet15Callsign, DEFAULT_STARNET_CALLSIGN); + + m_config->Read(wxT("/") + KEY_STARNET_LOGOFF15, &m_starNet15Logoff, DEFAULT_STARNET_LOGOFF); + + m_config->Read(wxT("/") + KEY_STARNET_INFO15, &m_starNet15Info, DEFAULT_STARNET_INFO); + + m_config->Read(wxT("/") + KEY_STARNET_PERMANENT15, &m_starNet15Permanent, DEFAULT_STARNET_PERMANENT); + + m_config->Read(wxT("/") + KEY_STARNET_USER_TIMEOUT15, &temp, long(DEFAULT_STARNET_USER_TIMEOUT)); + m_starNet15UserTimeout = (unsigned int)temp; + + m_config->Read(wxT("/") + KEY_STARNET_GROUP_TIMEOUT15, &temp, long(DEFAULT_STARNET_GROUP_TIMEOUT)); + m_starNet15GroupTimeout = (unsigned int)temp; + + m_config->Read(wxT("/") + KEY_STARNET_CALLSIGN_SWITCH15, &temp, long(DEFAULT_STARNET_CALLSIGN_SWITCH)); + m_starNet15CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp); + + m_config->Read(wxT("/") + KEY_STARNET_TXMSG_SWITCH15, &m_starNet15TxMsgSwitch, DEFAULT_STARNET_TXMSG_SWITCH); + + m_config->Read(wxT("/") + KEY_STARNET_REFLECTOR15, &m_starNet15Reflector, DEFAULT_STARNET_REFLECTOR); + + m_config->Read(wxT("/") + KEY_REMOTE_ENABLED, &m_remoteEnabled, DEFAULT_REMOTE_ENABLED); + + m_config->Read(wxT("/") + KEY_REMOTE_PASSWORD, &m_remotePassword, DEFAULT_REMOTE_PASSWORD); + + m_config->Read(wxT("/") + KEY_REMOTE_PORT, &temp, long(DEFAULT_REMOTE_PORT)); + m_remotePort = (unsigned int)temp; + + m_config->Read(wxT("/") + KEY_LOG_ENABLED, &m_logEnabled, DEFAULT_LOG_ENABLED); + + m_config->Read(wxT("/") + KEY_WINDOW_X, &temp, long(DEFAULT_WINDOW_X)); + m_x = int(temp); + + m_config->Read(wxT("/") + KEY_WINDOW_Y, &temp, long(DEFAULT_WINDOW_Y)); + m_y = int(temp); +} + +CStarNetServerConfig::~CStarNetServerConfig() +{ + delete m_config; +} + +#else + +CStarNetServerConfig::CStarNetServerConfig(const wxString& dir) : +m_fileName(), +m_callsign(DEFAULT_CALLSIGN), +m_address(DEFAULT_ADDRESS), +m_ircddbHostname(DEFAULT_IRCDDB_HOSTNAME), +m_ircddbUsername(DEFAULT_IRCDDB_USERNAME), +m_ircddbPassword(DEFAULT_IRCDDB_PASSWORD), +m_starNet1Band(DEFAULT_STARNET_BAND), +m_starNet1Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet1Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet1Info(DEFAULT_STARNET_INFO), +m_starNet1Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet1UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet1GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet1CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet1TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet1Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet2Band(DEFAULT_STARNET_BAND), +m_starNet2Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet2Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet2Info(DEFAULT_STARNET_INFO), +m_starNet2Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet2UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet2GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet2CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet2TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet2Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet3Band(DEFAULT_STARNET_BAND), +m_starNet3Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet3Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet3Info(DEFAULT_STARNET_INFO), +m_starNet3Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet3UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet3GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet3CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet3TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet3Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet4Band(DEFAULT_STARNET_BAND), +m_starNet4Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet4Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet4Info(DEFAULT_STARNET_INFO), +m_starNet4Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet4UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet4GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet4CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet4TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet4Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet5Band(DEFAULT_STARNET_BAND), +m_starNet5Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet5Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet5Info(DEFAULT_STARNET_INFO), +m_starNet5Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet5UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet5GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet5CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet5TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet5Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet6Band(DEFAULT_STARNET_BAND), +m_starNet6Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet6Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet6Info(DEFAULT_STARNET_INFO), +m_starNet6Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet6UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet6GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet6CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet6TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet6Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet7Band(DEFAULT_STARNET_BAND), +m_starNet7Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet7Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet7Info(DEFAULT_STARNET_INFO), +m_starNet7Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet7UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet7GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet7CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet7TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet7Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet8Band(DEFAULT_STARNET_BAND), +m_starNet8Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet8Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet8Info(DEFAULT_STARNET_INFO), +m_starNet8Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet8UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet8GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet8CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet8TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet8Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet9Band(DEFAULT_STARNET_BAND), +m_starNet9Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet9Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet9Info(DEFAULT_STARNET_INFO), +m_starNet9Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet9UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet9GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet9CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet9TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet9Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet10Band(DEFAULT_STARNET_BAND), +m_starNet10Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet10Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet10Info(DEFAULT_STARNET_INFO), +m_starNet10Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet10UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet10GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet10CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet10TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet10Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet11Band(DEFAULT_STARNET_BAND), +m_starNet11Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet11Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet11Info(DEFAULT_STARNET_INFO), +m_starNet11Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet11UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet11GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet11CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet11TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet11Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet12Band(DEFAULT_STARNET_BAND), +m_starNet12Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet12Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet12Info(DEFAULT_STARNET_INFO), +m_starNet12Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet12UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet12GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet12CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet12TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet12Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet13Band(DEFAULT_STARNET_BAND), +m_starNet13Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet13Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet13Info(DEFAULT_STARNET_INFO), +m_starNet13Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet13UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet13GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet13CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet13TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet13Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet14Band(DEFAULT_STARNET_BAND), +m_starNet14Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet14Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet14Info(DEFAULT_STARNET_INFO), +m_starNet14Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet14UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet14GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet14CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet14TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet14Reflector(DEFAULT_STARNET_REFLECTOR), +m_starNet15Band(DEFAULT_STARNET_BAND), +m_starNet15Callsign(DEFAULT_STARNET_CALLSIGN), +m_starNet15Logoff(DEFAULT_STARNET_LOGOFF), +m_starNet15Info(DEFAULT_STARNET_INFO), +m_starNet15Permanent(DEFAULT_STARNET_PERMANENT), +m_starNet15UserTimeout(DEFAULT_STARNET_USER_TIMEOUT), +m_starNet15GroupTimeout(DEFAULT_STARNET_GROUP_TIMEOUT), +m_starNet15CallsignSwitch(DEFAULT_STARNET_CALLSIGN_SWITCH), +m_starNet15TxMsgSwitch(DEFAULT_STARNET_TXMSG_SWITCH), +m_starNet15Reflector(DEFAULT_STARNET_REFLECTOR), +m_remoteEnabled(DEFAULT_REMOTE_ENABLED), +m_remotePassword(DEFAULT_REMOTE_PASSWORD), +m_remotePort(DEFAULT_REMOTE_PORT), +m_logEnabled(DEFAULT_LOG_ENABLED), +m_x(DEFAULT_WINDOW_X), +m_y(DEFAULT_WINDOW_Y) +{ + wxASSERT(!dir.IsEmpty()); + + m_fileName.Assign(dir, CONFIG_FILE_NAME); + + wxTextFile file(m_fileName.GetFullPath()); + + bool exists = file.Exists(); + if (!exists) + return; + + bool ret = file.Open(); + if (!ret) { + wxLogError(wxT("Cannot open the config file - %s"), m_fileName.GetFullPath().c_str()); + return; + } + + long temp1; + unsigned long temp2; + + wxString str = file.GetFirstLine(); + + while (!file.Eof()) { + if (str.GetChar(0U) == wxT('#')) { + str = file.GetNextLine(); + continue; + } + + int n = str.Find(wxT('=')); + if (n == wxNOT_FOUND) { + str = file.GetNextLine(); + continue; + } + + wxString key = str.Left(n); + wxString val = str.Mid(n + 1U); + + if (key.IsSameAs(KEY_CALLSIGN)) { + m_callsign = val; + } else if (key.IsSameAs(KEY_ADDRESS)) { + m_address = val; + } else if (key.IsSameAs(KEY_IRCDDB_HOSTNAME)) { + m_ircddbHostname = val; + } else if (key.IsSameAs(KEY_IRCDDB_USERNAME)) { + m_ircddbUsername = val; + } else if (key.IsSameAs(KEY_IRCDDB_PASSWORD)) { + m_ircddbPassword = val; + } else if (key.IsSameAs(KEY_STARNET_BAND1)) { + m_starNet1Band = val; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN1)) { + m_starNet1Callsign = val; + } else if (key.IsSameAs(KEY_STARNET_LOGOFF1)) { + m_starNet1Logoff = val; + } else if (key.IsSameAs(KEY_STARNET_INFO1)) { + m_starNet1Info = val; + } else if (key.IsSameAs(KEY_STARNET_PERMANENT1)) { + m_starNet1Permanent = val; + } else if (key.IsSameAs(KEY_STARNET_USER_TIMEOUT1)) { + val.ToULong(&temp2); + m_starNet1UserTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_GROUP_TIMEOUT1)) { + val.ToULong(&temp2); + m_starNet1GroupTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN_SWITCH1)) { + val.ToLong(&temp1); + m_starNet1CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp1); + } else if (key.IsSameAs(KEY_STARNET_TXMSG_SWITCH1)) { + val.ToLong(&temp1); + m_starNet1TxMsgSwitch = temp1 == 1L; + } else if (key.IsSameAs(KEY_STARNET_REFLECTOR1)) { + m_starNet1Reflector = val; + } else if (key.IsSameAs(KEY_STARNET_BAND2)) { + m_starNet2Band = val; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN2)) { + m_starNet2Callsign = val; + } else if (key.IsSameAs(KEY_STARNET_LOGOFF2)) { + m_starNet2Logoff = val; + } else if (key.IsSameAs(KEY_STARNET_INFO2)) { + m_starNet2Info = val; + } else if (key.IsSameAs(KEY_STARNET_PERMANENT2)) { + m_starNet2Permanent = val; + } else if (key.IsSameAs(KEY_STARNET_USER_TIMEOUT2)) { + val.ToULong(&temp2); + m_starNet2UserTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_GROUP_TIMEOUT2)) { + val.ToULong(&temp2); + m_starNet2GroupTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN_SWITCH2)) { + val.ToLong(&temp1); + m_starNet2CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp1); + } else if (key.IsSameAs(KEY_STARNET_TXMSG_SWITCH2)) { + val.ToLong(&temp1); + m_starNet2TxMsgSwitch = temp1 == 1L; + } else if (key.IsSameAs(KEY_STARNET_REFLECTOR2)) { + m_starNet2Reflector = val; + } else if (key.IsSameAs(KEY_STARNET_BAND3)) { + m_starNet3Band = val; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN3)) { + m_starNet3Callsign = val; + } else if (key.IsSameAs(KEY_STARNET_LOGOFF3)) { + m_starNet3Logoff = val; + } else if (key.IsSameAs(KEY_STARNET_INFO3)) { + m_starNet3Info = val; + } else if (key.IsSameAs(KEY_STARNET_PERMANENT3)) { + m_starNet3Permanent = val; + } else if (key.IsSameAs(KEY_STARNET_USER_TIMEOUT3)) { + val.ToULong(&temp2); + m_starNet3UserTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_GROUP_TIMEOUT3)) { + val.ToULong(&temp2); + m_starNet3GroupTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN_SWITCH3)) { + val.ToLong(&temp1); + m_starNet3CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp1); + } else if (key.IsSameAs(KEY_STARNET_TXMSG_SWITCH3)) { + val.ToLong(&temp1); + m_starNet3TxMsgSwitch = temp1 == 1L; + } else if (key.IsSameAs(KEY_STARNET_REFLECTOR3)) { + m_starNet3Reflector = val; + } else if (key.IsSameAs(KEY_STARNET_BAND4)) { + m_starNet4Band = val; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN4)) { + m_starNet4Callsign = val; + } else if (key.IsSameAs(KEY_STARNET_LOGOFF4)) { + m_starNet4Logoff = val; + } else if (key.IsSameAs(KEY_STARNET_INFO4)) { + m_starNet4Info = val; + } else if (key.IsSameAs(KEY_STARNET_PERMANENT4)) { + m_starNet4Permanent = val; + } else if (key.IsSameAs(KEY_STARNET_USER_TIMEOUT4)) { + val.ToULong(&temp2); + m_starNet4UserTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_GROUP_TIMEOUT4)) { + val.ToULong(&temp2); + m_starNet4GroupTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN_SWITCH4)) { + val.ToLong(&temp1); + m_starNet4CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp1); + } else if (key.IsSameAs(KEY_STARNET_TXMSG_SWITCH4)) { + val.ToLong(&temp1); + m_starNet4TxMsgSwitch = temp1 == 1L; + } else if (key.IsSameAs(KEY_STARNET_REFLECTOR4)) { + m_starNet4Reflector = val; + } else if (key.IsSameAs(KEY_STARNET_BAND5)) { + m_starNet5Band = val; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN5)) { + m_starNet5Callsign = val; + } else if (key.IsSameAs(KEY_STARNET_LOGOFF5)) { + m_starNet5Logoff = val; + } else if (key.IsSameAs(KEY_STARNET_INFO5)) { + m_starNet5Info = val; + } else if (key.IsSameAs(KEY_STARNET_PERMANENT5)) { + m_starNet5Permanent = val; + } else if (key.IsSameAs(KEY_STARNET_USER_TIMEOUT5)) { + val.ToULong(&temp2); + m_starNet5UserTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_GROUP_TIMEOUT5)) { + val.ToULong(&temp2); + m_starNet5GroupTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN_SWITCH5)) { + val.ToLong(&temp1); + m_starNet5CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp1); + } else if (key.IsSameAs(KEY_STARNET_TXMSG_SWITCH5)) { + val.ToLong(&temp1); + m_starNet5TxMsgSwitch = temp1 == 1L; + } else if (key.IsSameAs(KEY_STARNET_REFLECTOR5)) { + m_starNet5Reflector = val; + } else if (key.IsSameAs(KEY_STARNET_BAND6)) { + m_starNet6Band = val; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN6)) { + m_starNet6Callsign = val; + } else if (key.IsSameAs(KEY_STARNET_LOGOFF6)) { + m_starNet6Logoff = val; + } else if (key.IsSameAs(KEY_STARNET_INFO6)) { + m_starNet6Info = val; + } else if (key.IsSameAs(KEY_STARNET_PERMANENT6)) { + m_starNet6Permanent = val; + } else if (key.IsSameAs(KEY_STARNET_USER_TIMEOUT6)) { + val.ToULong(&temp2); + m_starNet6UserTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_GROUP_TIMEOUT6)) { + val.ToULong(&temp2); + m_starNet6GroupTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN_SWITCH6)) { + val.ToLong(&temp1); + m_starNet6CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp1); + } else if (key.IsSameAs(KEY_STARNET_TXMSG_SWITCH6)) { + val.ToLong(&temp1); + m_starNet6TxMsgSwitch = temp1 == 1L; + } else if (key.IsSameAs(KEY_STARNET_REFLECTOR6)) { + m_starNet6Reflector = val; + } else if (key.IsSameAs(KEY_STARNET_BAND7)) { + m_starNet7Band = val; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN7)) { + m_starNet7Callsign = val; + } else if (key.IsSameAs(KEY_STARNET_LOGOFF7)) { + m_starNet7Logoff = val; + } else if (key.IsSameAs(KEY_STARNET_INFO7)) { + m_starNet7Info = val; + } else if (key.IsSameAs(KEY_STARNET_PERMANENT7)) { + m_starNet7Permanent = val; + } else if (key.IsSameAs(KEY_STARNET_USER_TIMEOUT7)) { + val.ToULong(&temp2); + m_starNet7UserTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_GROUP_TIMEOUT7)) { + val.ToULong(&temp2); + m_starNet7GroupTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN_SWITCH7)) { + val.ToLong(&temp1); + m_starNet7CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp1); + } else if (key.IsSameAs(KEY_STARNET_TXMSG_SWITCH7)) { + val.ToLong(&temp1); + m_starNet7TxMsgSwitch = temp1 == 1L; + } else if (key.IsSameAs(KEY_STARNET_REFLECTOR7)) { + m_starNet7Reflector = val; + } else if (key.IsSameAs(KEY_STARNET_BAND8)) { + m_starNet8Band = val; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN8)) { + m_starNet8Callsign = val; + } else if (key.IsSameAs(KEY_STARNET_LOGOFF8)) { + m_starNet8Logoff = val; + } else if (key.IsSameAs(KEY_STARNET_INFO8)) { + m_starNet8Info = val; + } else if (key.IsSameAs(KEY_STARNET_PERMANENT8)) { + m_starNet8Permanent = val; + } else if (key.IsSameAs(KEY_STARNET_USER_TIMEOUT8)) { + val.ToULong(&temp2); + m_starNet8UserTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_GROUP_TIMEOUT8)) { + val.ToULong(&temp2); + m_starNet8GroupTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN_SWITCH8)) { + val.ToLong(&temp1); + m_starNet8CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp1); + } else if (key.IsSameAs(KEY_STARNET_TXMSG_SWITCH8)) { + val.ToLong(&temp1); + m_starNet8TxMsgSwitch = temp1 == 1L; + } else if (key.IsSameAs(KEY_STARNET_REFLECTOR8)) { + m_starNet8Reflector = val; + } else if (key.IsSameAs(KEY_STARNET_BAND9)) { + m_starNet9Band = val; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN9)) { + m_starNet9Callsign = val; + } else if (key.IsSameAs(KEY_STARNET_LOGOFF9)) { + m_starNet9Logoff = val; + } else if (key.IsSameAs(KEY_STARNET_INFO9)) { + m_starNet9Info = val; + } else if (key.IsSameAs(KEY_STARNET_PERMANENT9)) { + m_starNet9Permanent = val; + } else if (key.IsSameAs(KEY_STARNET_USER_TIMEOUT9)) { + val.ToULong(&temp2); + m_starNet9UserTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_GROUP_TIMEOUT9)) { + val.ToULong(&temp2); + m_starNet9GroupTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN_SWITCH9)) { + val.ToLong(&temp1); + m_starNet9CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp1); + } else if (key.IsSameAs(KEY_STARNET_TXMSG_SWITCH9)) { + val.ToLong(&temp1); + m_starNet9TxMsgSwitch = temp1 == 1L; + } else if (key.IsSameAs(KEY_STARNET_REFLECTOR9)) { + m_starNet9Reflector = val; + } else if (key.IsSameAs(KEY_STARNET_BAND10)) { + m_starNet10Band = val; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN10)) { + m_starNet10Callsign = val; + } else if (key.IsSameAs(KEY_STARNET_LOGOFF10)) { + m_starNet10Logoff = val; + } else if (key.IsSameAs(KEY_STARNET_INFO10)) { + m_starNet10Info = val; + } else if (key.IsSameAs(KEY_STARNET_PERMANENT10)) { + m_starNet10Permanent = val; + } else if (key.IsSameAs(KEY_STARNET_USER_TIMEOUT10)) { + val.ToULong(&temp2); + m_starNet10UserTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_GROUP_TIMEOUT10)) { + val.ToULong(&temp2); + m_starNet10GroupTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN_SWITCH10)) { + val.ToLong(&temp1); + m_starNet10CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp1); + } else if (key.IsSameAs(KEY_STARNET_TXMSG_SWITCH10)) { + val.ToLong(&temp1); + m_starNet10TxMsgSwitch = temp1 == 1L; + } else if (key.IsSameAs(KEY_STARNET_REFLECTOR10)) { + m_starNet10Reflector = val; + } else if (key.IsSameAs(KEY_STARNET_BAND11)) { + m_starNet11Band = val; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN11)) { + m_starNet11Callsign = val; + } else if (key.IsSameAs(KEY_STARNET_LOGOFF11)) { + m_starNet11Logoff = val; + } else if (key.IsSameAs(KEY_STARNET_INFO11)) { + m_starNet11Info = val; + } else if (key.IsSameAs(KEY_STARNET_PERMANENT11)) { + m_starNet11Permanent = val; + } else if (key.IsSameAs(KEY_STARNET_USER_TIMEOUT11)) { + val.ToULong(&temp2); + m_starNet11UserTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_GROUP_TIMEOUT11)) { + val.ToULong(&temp2); + m_starNet11GroupTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN_SWITCH11)) { + val.ToLong(&temp1); + m_starNet11CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp1); + } else if (key.IsSameAs(KEY_STARNET_TXMSG_SWITCH11)) { + val.ToLong(&temp1); + m_starNet11TxMsgSwitch = temp1 == 1L; + } else if (key.IsSameAs(KEY_STARNET_REFLECTOR11)) { + m_starNet11Reflector = val; + } else if (key.IsSameAs(KEY_STARNET_BAND12)) { + m_starNet12Band = val; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN12)) { + m_starNet12Callsign = val; + } else if (key.IsSameAs(KEY_STARNET_LOGOFF12)) { + m_starNet12Logoff = val; + } else if (key.IsSameAs(KEY_STARNET_INFO12)) { + m_starNet12Info = val; + } else if (key.IsSameAs(KEY_STARNET_PERMANENT12)) { + m_starNet12Permanent = val; + } else if (key.IsSameAs(KEY_STARNET_USER_TIMEOUT12)) { + val.ToULong(&temp2); + m_starNet12UserTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_GROUP_TIMEOUT12)) { + val.ToULong(&temp2); + m_starNet12GroupTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN_SWITCH12)) { + val.ToLong(&temp1); + m_starNet12CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp1); + } else if (key.IsSameAs(KEY_STARNET_TXMSG_SWITCH12)) { + val.ToLong(&temp1); + m_starNet12TxMsgSwitch = temp1 == 1L; + } else if (key.IsSameAs(KEY_STARNET_REFLECTOR12)) { + m_starNet12Reflector = val; + } else if (key.IsSameAs(KEY_STARNET_BAND13)) { + m_starNet13Band = val; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN13)) { + m_starNet13Callsign = val; + } else if (key.IsSameAs(KEY_STARNET_LOGOFF13)) { + m_starNet13Logoff = val; + } else if (key.IsSameAs(KEY_STARNET_INFO13)) { + m_starNet13Info = val; + } else if (key.IsSameAs(KEY_STARNET_PERMANENT13)) { + m_starNet13Permanent = val; + } else if (key.IsSameAs(KEY_STARNET_USER_TIMEOUT13)) { + val.ToULong(&temp2); + m_starNet13UserTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_GROUP_TIMEOUT13)) { + val.ToULong(&temp2); + m_starNet13GroupTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN_SWITCH13)) { + val.ToLong(&temp1); + m_starNet13CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp1); + } else if (key.IsSameAs(KEY_STARNET_TXMSG_SWITCH13)) { + val.ToLong(&temp1); + m_starNet13TxMsgSwitch = temp1 == 1L; + } else if (key.IsSameAs(KEY_STARNET_REFLECTOR13)) { + m_starNet13Reflector = val; + } else if (key.IsSameAs(KEY_STARNET_BAND14)) { + m_starNet14Band = val; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN14)) { + m_starNet14Callsign = val; + } else if (key.IsSameAs(KEY_STARNET_LOGOFF14)) { + m_starNet14Logoff = val; + } else if (key.IsSameAs(KEY_STARNET_INFO14)) { + m_starNet14Info = val; + } else if (key.IsSameAs(KEY_STARNET_PERMANENT14)) { + m_starNet14Permanent = val; + } else if (key.IsSameAs(KEY_STARNET_USER_TIMEOUT14)) { + val.ToULong(&temp2); + m_starNet14UserTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_GROUP_TIMEOUT14)) { + val.ToULong(&temp2); + m_starNet14GroupTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN_SWITCH14)) { + val.ToLong(&temp1); + m_starNet14CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp1); + } else if (key.IsSameAs(KEY_STARNET_TXMSG_SWITCH14)) { + val.ToLong(&temp1); + m_starNet14TxMsgSwitch = temp1 == 1L; + } else if (key.IsSameAs(KEY_STARNET_REFLECTOR14)) { + m_starNet14Reflector = val; + } else if (key.IsSameAs(KEY_STARNET_BAND15)) { + m_starNet15Band = val; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN15)) { + m_starNet15Callsign = val; + } else if (key.IsSameAs(KEY_STARNET_LOGOFF15)) { + m_starNet15Logoff = val; + } else if (key.IsSameAs(KEY_STARNET_INFO15)) { + m_starNet15Info = val; + } else if (key.IsSameAs(KEY_STARNET_PERMANENT15)) { + m_starNet15Permanent = val; + } else if (key.IsSameAs(KEY_STARNET_USER_TIMEOUT15)) { + val.ToULong(&temp2); + m_starNet15UserTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_GROUP_TIMEOUT15)) { + val.ToULong(&temp2); + m_starNet15GroupTimeout = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_STARNET_CALLSIGN_SWITCH15)) { + val.ToLong(&temp1); + m_starNet15CallsignSwitch = STARNET_CALLSIGN_SWITCH(temp1); + } else if (key.IsSameAs(KEY_STARNET_TXMSG_SWITCH15)) { + val.ToLong(&temp1); + m_starNet15TxMsgSwitch = temp1 == 1L; + } else if (key.IsSameAs(KEY_STARNET_REFLECTOR15)) { + m_starNet15Reflector = val; + } else if (key.IsSameAs(KEY_REMOTE_ENABLED)) { + val.ToLong(&temp1); + m_remoteEnabled = temp1 == 1L; + } else if (key.IsSameAs(KEY_REMOTE_PASSWORD)) { + m_remotePassword = val; + } else if (key.IsSameAs(KEY_REMOTE_PORT)) { + val.ToULong(&temp2); + m_remotePort = (unsigned int)temp2; + } else if (key.IsSameAs(KEY_LOG_ENABLED)) { + val.ToLong(&temp1); + m_logEnabled = temp1 == 1L; + } else if (key.IsSameAs(KEY_WINDOW_X)) { + val.ToLong(&temp1); + m_x = int(temp1); + } else if (key.IsSameAs(KEY_WINDOW_Y)) { + val.ToLong(&temp1); + m_y = int(temp1); + } + + str = file.GetNextLine(); + } + + file.Close(); +} + +CStarNetServerConfig::~CStarNetServerConfig() +{ +} + +#endif + +void CStarNetServerConfig::getGateway(wxString& callsign, wxString& address) const +{ + callsign = m_callsign; + address = m_address; +} + +void CStarNetServerConfig::setGateway(const wxString& callsign, const wxString& address) +{ + m_callsign = callsign; + m_address = address; +} + +void CStarNetServerConfig::getIrcDDB(wxString& hostname, wxString& username, wxString& password) const +{ + hostname = m_ircddbHostname; + username = m_ircddbUsername; + password = m_ircddbPassword; +} + +void CStarNetServerConfig::setIrcDDB(const wxString& hostname, const wxString& username, const wxString& password) +{ + m_ircddbHostname = hostname; + m_ircddbUsername = username; + m_ircddbPassword = password; +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetServerConfig::getStarNet1(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +#else +void CStarNetServerConfig::getStarNet1(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +#endif +{ + band = m_starNet1Band; + callsign = m_starNet1Callsign; + logoff = m_starNet1Logoff; + info = m_starNet1Info; + permanent = m_starNet1Permanent; + userTimeout = m_starNet1UserTimeout; + groupTimeout = m_starNet1GroupTimeout; + callsignSwitch = m_starNet1CallsignSwitch; + txMsgSwitch = m_starNet1TxMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + reflector = m_starNet1Reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetServerConfig::setStarNet1(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +#else +void CStarNetServerConfig::setStarNet1(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +#endif +{ + m_starNet1Band = band; + m_starNet1Callsign = callsign; + m_starNet1Logoff = logoff; + m_starNet1Info = info; + m_starNet1Permanent = permanent; + m_starNet1UserTimeout = userTimeout; + m_starNet1GroupTimeout = groupTimeout; + m_starNet1CallsignSwitch = callsignSwitch; + m_starNet1TxMsgSwitch = txMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_starNet1Reflector = reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetServerConfig::getStarNet2(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +#else +void CStarNetServerConfig::getStarNet2(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +#endif +{ + band = m_starNet2Band; + callsign = m_starNet2Callsign; + logoff = m_starNet2Logoff; + info = m_starNet2Info; + permanent = m_starNet2Permanent; + userTimeout = m_starNet2UserTimeout; + groupTimeout = m_starNet2GroupTimeout; + callsignSwitch = m_starNet2CallsignSwitch; + txMsgSwitch = m_starNet2TxMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + reflector = m_starNet2Reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetServerConfig::setStarNet2(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +#else +void CStarNetServerConfig::setStarNet2(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +#endif +{ + m_starNet2Band = band; + m_starNet2Callsign = callsign; + m_starNet2Logoff = logoff; + m_starNet2Info = info; + m_starNet2Permanent = permanent; + m_starNet2UserTimeout = userTimeout; + m_starNet2GroupTimeout = groupTimeout; + m_starNet2CallsignSwitch = callsignSwitch; + m_starNet2TxMsgSwitch = txMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_starNet2Reflector = reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetServerConfig::getStarNet3(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +#else +void CStarNetServerConfig::getStarNet3(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +#endif +{ + band = m_starNet3Band; + callsign = m_starNet3Callsign; + logoff = m_starNet3Logoff; + info = m_starNet3Info; + permanent = m_starNet3Permanent; + userTimeout = m_starNet3UserTimeout; + groupTimeout = m_starNet3GroupTimeout; + callsignSwitch = m_starNet3CallsignSwitch; + txMsgSwitch = m_starNet3TxMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + reflector = m_starNet3Reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetServerConfig::setStarNet3(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +#else +void CStarNetServerConfig::setStarNet3(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +#endif +{ + m_starNet3Band = band; + m_starNet3Callsign = callsign; + m_starNet3Logoff = logoff; + m_starNet3Info = info; + m_starNet3Permanent = permanent; + m_starNet3UserTimeout = userTimeout; + m_starNet3GroupTimeout = groupTimeout; + m_starNet3CallsignSwitch = callsignSwitch; + m_starNet3TxMsgSwitch = txMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_starNet3Reflector = reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetServerConfig::getStarNet4(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +#else +void CStarNetServerConfig::getStarNet4(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +#endif +{ + band = m_starNet4Band; + callsign = m_starNet4Callsign; + logoff = m_starNet4Logoff; + info = m_starNet4Info; + permanent = m_starNet4Permanent; + userTimeout = m_starNet4UserTimeout; + groupTimeout = m_starNet4GroupTimeout; + callsignSwitch = m_starNet4CallsignSwitch; + txMsgSwitch = m_starNet4TxMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + reflector = m_starNet4Reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetServerConfig::setStarNet4(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +#else +void CStarNetServerConfig::setStarNet4(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +#endif +{ + m_starNet4Band = band; + m_starNet4Callsign = callsign; + m_starNet4Logoff = logoff; + m_starNet4Info = info; + m_starNet4Permanent = permanent; + m_starNet4UserTimeout = userTimeout; + m_starNet4GroupTimeout = groupTimeout; + m_starNet4CallsignSwitch = callsignSwitch; + m_starNet4TxMsgSwitch = txMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_starNet4Reflector = reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetServerConfig::getStarNet5(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +#else +void CStarNetServerConfig::getStarNet5(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +#endif +{ + band = m_starNet5Band; + callsign = m_starNet5Callsign; + logoff = m_starNet5Logoff; + info = m_starNet5Info; + permanent = m_starNet5Permanent; + userTimeout = m_starNet5UserTimeout; + groupTimeout = m_starNet5GroupTimeout; + callsignSwitch = m_starNet5CallsignSwitch; + txMsgSwitch = m_starNet5TxMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + reflector = m_starNet5Reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetServerConfig::setStarNet5(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +#else +void CStarNetServerConfig::setStarNet5(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +#endif +{ + m_starNet5Band = band; + m_starNet5Callsign = callsign; + m_starNet5Logoff = logoff; + m_starNet5Info = info; + m_starNet5Permanent = permanent; + m_starNet5UserTimeout = userTimeout; + m_starNet5GroupTimeout = groupTimeout; + m_starNet5CallsignSwitch = callsignSwitch; + m_starNet5TxMsgSwitch = txMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_starNet5Reflector = reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetServerConfig::getStarNet6(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +#else +void CStarNetServerConfig::getStarNet6(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +#endif +{ + band = m_starNet6Band; + callsign = m_starNet6Callsign; + logoff = m_starNet6Logoff; + info = m_starNet6Info; + permanent = m_starNet6Permanent; + userTimeout = m_starNet6UserTimeout; + groupTimeout = m_starNet6GroupTimeout; + callsignSwitch = m_starNet6CallsignSwitch; + txMsgSwitch = m_starNet6TxMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + reflector = m_starNet6Reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetServerConfig::setStarNet6(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +#else +void CStarNetServerConfig::setStarNet6(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +#endif +{ + m_starNet6Band = band; + m_starNet6Callsign = callsign; + m_starNet6Logoff = logoff; + m_starNet6Info = info; + m_starNet6Permanent = permanent; + m_starNet6UserTimeout = userTimeout; + m_starNet6GroupTimeout = groupTimeout; + m_starNet6CallsignSwitch = callsignSwitch; + m_starNet6TxMsgSwitch = txMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_starNet6Reflector = reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetServerConfig::getStarNet7(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +#else +void CStarNetServerConfig::getStarNet7(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +#endif +{ + band = m_starNet7Band; + callsign = m_starNet7Callsign; + logoff = m_starNet7Logoff; + info = m_starNet7Info; + permanent = m_starNet7Permanent; + userTimeout = m_starNet7UserTimeout; + groupTimeout = m_starNet7GroupTimeout; + callsignSwitch = m_starNet7CallsignSwitch; + txMsgSwitch = m_starNet7TxMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + reflector = m_starNet7Reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetServerConfig::setStarNet7(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +#else +void CStarNetServerConfig::setStarNet7(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +#endif +{ + m_starNet7Band = band; + m_starNet7Callsign = callsign; + m_starNet7Logoff = logoff; + m_starNet7Info = info; + m_starNet7Permanent = permanent; + m_starNet7UserTimeout = userTimeout; + m_starNet7GroupTimeout = groupTimeout; + m_starNet7CallsignSwitch = callsignSwitch; + m_starNet7TxMsgSwitch = txMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_starNet7Reflector = reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetServerConfig::getStarNet8(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +#else +void CStarNetServerConfig::getStarNet8(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +#endif +{ + band = m_starNet8Band; + callsign = m_starNet8Callsign; + logoff = m_starNet8Logoff; + info = m_starNet8Info; + permanent = m_starNet8Permanent; + userTimeout = m_starNet8UserTimeout; + groupTimeout = m_starNet8GroupTimeout; + callsignSwitch = m_starNet8CallsignSwitch; + txMsgSwitch = m_starNet8TxMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + reflector = m_starNet8Reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetServerConfig::setStarNet8(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +#else +void CStarNetServerConfig::setStarNet8(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +#endif +{ + m_starNet8Band = band; + m_starNet8Callsign = callsign; + m_starNet8Logoff = logoff; + m_starNet8Info = info; + m_starNet8Permanent = permanent; + m_starNet8UserTimeout = userTimeout; + m_starNet8GroupTimeout = groupTimeout; + m_starNet8CallsignSwitch = callsignSwitch; + m_starNet8TxMsgSwitch = txMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_starNet8Reflector = reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetServerConfig::getStarNet9(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +#else +void CStarNetServerConfig::getStarNet9(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +#endif +{ + band = m_starNet9Band; + callsign = m_starNet9Callsign; + logoff = m_starNet9Logoff; + info = m_starNet9Info; + permanent = m_starNet9Permanent; + userTimeout = m_starNet9UserTimeout; + groupTimeout = m_starNet9GroupTimeout; + callsignSwitch = m_starNet9CallsignSwitch; + txMsgSwitch = m_starNet9TxMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + reflector = m_starNet9Reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetServerConfig::setStarNet9(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +#else +void CStarNetServerConfig::setStarNet9(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +#endif +{ + m_starNet9Band = band; + m_starNet9Callsign = callsign; + m_starNet9Logoff = logoff; + m_starNet9Info = info; + m_starNet9Permanent = permanent; + m_starNet9UserTimeout = userTimeout; + m_starNet9GroupTimeout = groupTimeout; + m_starNet9CallsignSwitch = callsignSwitch; + m_starNet9TxMsgSwitch = txMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_starNet9Reflector = reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetServerConfig::getStarNet10(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +#else +void CStarNetServerConfig::getStarNet10(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +#endif +{ + band = m_starNet10Band; + callsign = m_starNet10Callsign; + logoff = m_starNet10Logoff; + info = m_starNet10Info; + permanent = m_starNet10Permanent; + userTimeout = m_starNet10UserTimeout; + groupTimeout = m_starNet10GroupTimeout; + callsignSwitch = m_starNet10CallsignSwitch; + txMsgSwitch = m_starNet10TxMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + reflector = m_starNet10Reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetServerConfig::setStarNet10(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +#else +void CStarNetServerConfig::setStarNet10(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +#endif +{ + m_starNet10Band = band; + m_starNet10Callsign = callsign; + m_starNet10Logoff = logoff; + m_starNet10Info = info; + m_starNet10Permanent = permanent; + m_starNet10UserTimeout = userTimeout; + m_starNet10GroupTimeout = groupTimeout; + m_starNet10CallsignSwitch = callsignSwitch; + m_starNet10TxMsgSwitch = txMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_starNet10Reflector = reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetServerConfig::getStarNet11(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +#else +void CStarNetServerConfig::getStarNet11(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +#endif +{ + band = m_starNet11Band; + callsign = m_starNet11Callsign; + logoff = m_starNet11Logoff; + info = m_starNet11Info; + permanent = m_starNet11Permanent; + userTimeout = m_starNet11UserTimeout; + groupTimeout = m_starNet11GroupTimeout; + callsignSwitch = m_starNet11CallsignSwitch; + txMsgSwitch = m_starNet11TxMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + reflector = m_starNet11Reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetServerConfig::setStarNet11(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +#else +void CStarNetServerConfig::setStarNet11(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +#endif +{ + m_starNet11Band = band; + m_starNet11Callsign = callsign; + m_starNet11Logoff = logoff; + m_starNet11Info = info; + m_starNet11Permanent = permanent; + m_starNet11UserTimeout = userTimeout; + m_starNet11GroupTimeout = groupTimeout; + m_starNet11CallsignSwitch = callsignSwitch; + m_starNet11TxMsgSwitch = txMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_starNet11Reflector = reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetServerConfig::getStarNet12(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +#else +void CStarNetServerConfig::getStarNet12(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +#endif +{ + band = m_starNet12Band; + callsign = m_starNet12Callsign; + logoff = m_starNet12Logoff; + info = m_starNet12Info; + permanent = m_starNet12Permanent; + userTimeout = m_starNet12UserTimeout; + groupTimeout = m_starNet12GroupTimeout; + callsignSwitch = m_starNet12CallsignSwitch; + txMsgSwitch = m_starNet12TxMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + reflector = m_starNet12Reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetServerConfig::setStarNet12(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +#else +void CStarNetServerConfig::setStarNet12(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +#endif +{ + m_starNet12Band = band; + m_starNet12Callsign = callsign; + m_starNet12Logoff = logoff; + m_starNet12Info = info; + m_starNet12Permanent = permanent; + m_starNet12UserTimeout = userTimeout; + m_starNet12GroupTimeout = groupTimeout; + m_starNet12CallsignSwitch = callsignSwitch; + m_starNet12TxMsgSwitch = txMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_starNet12Reflector = reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetServerConfig::getStarNet13(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +#else +void CStarNetServerConfig::getStarNet13(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +#endif +{ + band = m_starNet13Band; + callsign = m_starNet13Callsign; + logoff = m_starNet13Logoff; + info = m_starNet13Info; + permanent = m_starNet13Permanent; + userTimeout = m_starNet13UserTimeout; + groupTimeout = m_starNet13GroupTimeout; + callsignSwitch = m_starNet13CallsignSwitch; + txMsgSwitch = m_starNet13TxMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + reflector = m_starNet13Reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetServerConfig::setStarNet13(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +#else +void CStarNetServerConfig::setStarNet13(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +#endif +{ + m_starNet13Band = band; + m_starNet13Callsign = callsign; + m_starNet13Logoff = logoff; + m_starNet13Info = info; + m_starNet13Permanent = permanent; + m_starNet13UserTimeout = userTimeout; + m_starNet13GroupTimeout = groupTimeout; + m_starNet13CallsignSwitch = callsignSwitch; + m_starNet13TxMsgSwitch = txMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_starNet13Reflector = reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetServerConfig::getStarNet14(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +#else +void CStarNetServerConfig::getStarNet14(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +#endif +{ + band = m_starNet14Band; + callsign = m_starNet14Callsign; + logoff = m_starNet14Logoff; + info = m_starNet14Info; + permanent = m_starNet14Permanent; + userTimeout = m_starNet14UserTimeout; + groupTimeout = m_starNet14GroupTimeout; + callsignSwitch = m_starNet14CallsignSwitch; + txMsgSwitch = m_starNet14TxMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + reflector = m_starNet14Reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetServerConfig::setStarNet14(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +#else +void CStarNetServerConfig::setStarNet14(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +#endif +{ + m_starNet14Band = band; + m_starNet14Callsign = callsign; + m_starNet14Logoff = logoff; + m_starNet14Info = info; + m_starNet14Permanent = permanent; + m_starNet14UserTimeout = userTimeout; + m_starNet14GroupTimeout = groupTimeout; + m_starNet14CallsignSwitch = callsignSwitch; + m_starNet14TxMsgSwitch = txMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_starNet14Reflector = reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetServerConfig::getStarNet15(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const +#else +void CStarNetServerConfig::getStarNet15(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const +#endif +{ + band = m_starNet15Band; + callsign = m_starNet15Callsign; + logoff = m_starNet15Logoff; + info = m_starNet15Info; + permanent = m_starNet15Permanent; + userTimeout = m_starNet15UserTimeout; + groupTimeout = m_starNet15GroupTimeout; + callsignSwitch = m_starNet15CallsignSwitch; + txMsgSwitch = m_starNet15TxMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + reflector = m_starNet15Reflector; +#endif +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetServerConfig::setStarNet15(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +#else +void CStarNetServerConfig::setStarNet15(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +#endif +{ + m_starNet15Band = band; + m_starNet15Callsign = callsign; + m_starNet15Logoff = logoff; + m_starNet15Info = info; + m_starNet15Permanent = permanent; + m_starNet15UserTimeout = userTimeout; + m_starNet15GroupTimeout = groupTimeout; + m_starNet15CallsignSwitch = callsignSwitch; + m_starNet15TxMsgSwitch = txMsgSwitch; + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_starNet15Reflector = reflector; +#endif +} + +void CStarNetServerConfig::getRemote(bool& enabled, wxString& password, unsigned int& port) const +{ + enabled = m_remoteEnabled; + password = m_remotePassword; + port = m_remotePort; +} + +void CStarNetServerConfig::setRemote(bool enabled, const wxString& password, unsigned int port) +{ + m_remoteEnabled = enabled; + m_remotePassword = password; + m_remotePort = port; +} + +void CStarNetServerConfig::getMiscellaneous(bool& enabled) const +{ + enabled = m_logEnabled; +} + +void CStarNetServerConfig::setMiscellaneous(bool enabled) +{ + m_logEnabled = enabled; +} + +void CStarNetServerConfig::getPosition(int& x, int& y) const +{ + x = m_x; + y = m_y; +} + +void CStarNetServerConfig::setPosition(int x, int y) +{ + m_x = x; + m_y = y; +} + +bool CStarNetServerConfig::write() +{ +#if defined(__WINDOWS__) + m_config->Write(wxT("/") + KEY_CALLSIGN, m_callsign); + m_config->Write(wxT("/") + KEY_ADDRESS, m_address); + m_config->Write(wxT("/") + KEY_IRCDDB_HOSTNAME, m_ircddbHostname); + m_config->Write(wxT("/") + KEY_IRCDDB_USERNAME, m_ircddbUsername); + m_config->Write(wxT("/") + KEY_IRCDDB_PASSWORD, m_ircddbPassword); + m_config->Write(wxT("/") + KEY_STARNET_BAND1, m_starNet1Band); + m_config->Write(wxT("/") + KEY_STARNET_CALLSIGN1, m_starNet1Callsign); + m_config->Write(wxT("/") + KEY_STARNET_LOGOFF1, m_starNet1Logoff); + m_config->Write(wxT("/") + KEY_STARNET_INFO1, m_starNet1Info); + m_config->Write(wxT("/") + KEY_STARNET_PERMANENT1, m_starNet1Permanent); + m_config->Write(wxT("/") + KEY_STARNET_USER_TIMEOUT1, long(m_starNet1UserTimeout)); + m_config->Write(wxT("/") + KEY_STARNET_GROUP_TIMEOUT1, long(m_starNet1GroupTimeout)); + m_config->Write(wxT("/") + KEY_STARNET_CALLSIGN_SWITCH1, long(m_starNet1CallsignSwitch)); + m_config->Write(wxT("/") + KEY_STARNET_TXMSG_SWITCH1, m_starNet1TxMsgSwitch); + m_config->Write(wxT("/") + KEY_STARNET_REFLECTOR1, m_starNet1Reflector); + m_config->Write(wxT("/") + KEY_STARNET_BAND2, m_starNet2Band); + m_config->Write(wxT("/") + KEY_STARNET_CALLSIGN2, m_starNet2Callsign); + m_config->Write(wxT("/") + KEY_STARNET_LOGOFF2, m_starNet2Logoff); + m_config->Write(wxT("/") + KEY_STARNET_INFO2, m_starNet2Info); + m_config->Write(wxT("/") + KEY_STARNET_PERMANENT2, m_starNet2Permanent); + m_config->Write(wxT("/") + KEY_STARNET_USER_TIMEOUT2, long(m_starNet2UserTimeout)); + m_config->Write(wxT("/") + KEY_STARNET_GROUP_TIMEOUT2, long(m_starNet2GroupTimeout)); + m_config->Write(wxT("/") + KEY_STARNET_CALLSIGN_SWITCH2, long(m_starNet2CallsignSwitch)); + m_config->Write(wxT("/") + KEY_STARNET_TXMSG_SWITCH2, m_starNet2TxMsgSwitch); + m_config->Write(wxT("/") + KEY_STARNET_REFLECTOR2, m_starNet2Reflector); + m_config->Write(wxT("/") + KEY_STARNET_BAND3, m_starNet3Band); + m_config->Write(wxT("/") + KEY_STARNET_CALLSIGN3, m_starNet3Callsign); + m_config->Write(wxT("/") + KEY_STARNET_LOGOFF3, m_starNet3Logoff); + m_config->Write(wxT("/") + KEY_STARNET_INFO3, m_starNet3Info); + m_config->Write(wxT("/") + KEY_STARNET_PERMANENT3, m_starNet3Permanent); + m_config->Write(wxT("/") + KEY_STARNET_USER_TIMEOUT3, long(m_starNet3UserTimeout)); + m_config->Write(wxT("/") + KEY_STARNET_GROUP_TIMEOUT3, long(m_starNet3GroupTimeout)); + m_config->Write(wxT("/") + KEY_STARNET_CALLSIGN_SWITCH3, long(m_starNet3CallsignSwitch)); + m_config->Write(wxT("/") + KEY_STARNET_TXMSG_SWITCH3, m_starNet3TxMsgSwitch); + m_config->Write(wxT("/") + KEY_STARNET_REFLECTOR3, m_starNet3Reflector); + m_config->Write(wxT("/") + KEY_STARNET_BAND4, m_starNet4Band); + m_config->Write(wxT("/") + KEY_STARNET_CALLSIGN4, m_starNet4Callsign); + m_config->Write(wxT("/") + KEY_STARNET_LOGOFF4, m_starNet4Logoff); + m_config->Write(wxT("/") + KEY_STARNET_INFO4, m_starNet4Info); + m_config->Write(wxT("/") + KEY_STARNET_PERMANENT4, m_starNet4Permanent); + m_config->Write(wxT("/") + KEY_STARNET_USER_TIMEOUT4, long(m_starNet4UserTimeout)); + m_config->Write(wxT("/") + KEY_STARNET_GROUP_TIMEOUT4, long(m_starNet4GroupTimeout)); + m_config->Write(wxT("/") + KEY_STARNET_CALLSIGN_SWITCH4, long(m_starNet4CallsignSwitch)); + m_config->Write(wxT("/") + KEY_STARNET_TXMSG_SWITCH4, m_starNet4TxMsgSwitch); + m_config->Write(wxT("/") + KEY_STARNET_REFLECTOR4, m_starNet4Reflector); + m_config->Write(wxT("/") + KEY_STARNET_BAND5, m_starNet5Band); + m_config->Write(wxT("/") + KEY_STARNET_CALLSIGN5, m_starNet5Callsign); + m_config->Write(wxT("/") + KEY_STARNET_LOGOFF5, m_starNet5Logoff); + m_config->Write(wxT("/") + KEY_STARNET_INFO5, m_starNet5Info); + m_config->Write(wxT("/") + KEY_STARNET_PERMANENT5, m_starNet5Permanent); + m_config->Write(wxT("/") + KEY_STARNET_USER_TIMEOUT5, long(m_starNet5UserTimeout)); + m_config->Write(wxT("/") + KEY_STARNET_GROUP_TIMEOUT5, long(m_starNet5GroupTimeout)); + m_config->Write(wxT("/") + KEY_STARNET_CALLSIGN_SWITCH5, long(m_starNet5CallsignSwitch)); + m_config->Write(wxT("/") + KEY_STARNET_TXMSG_SWITCH5, m_starNet5TxMsgSwitch); + m_config->Write(wxT("/") + KEY_STARNET_REFLECTOR5, m_starNet5Reflector); + m_config->Write(wxT("/") + KEY_STARNET_BAND6, m_starNet6Band); + m_config->Write(wxT("/") + KEY_STARNET_CALLSIGN6, m_starNet6Callsign); + m_config->Write(wxT("/") + KEY_STARNET_LOGOFF6, m_starNet6Logoff); + m_config->Write(wxT("/") + KEY_STARNET_INFO6, m_starNet6Info); + m_config->Write(wxT("/") + KEY_STARNET_PERMANENT6, m_starNet6Permanent); + m_config->Write(wxT("/") + KEY_STARNET_USER_TIMEOUT6, long(m_starNet6UserTimeout)); + m_config->Write(wxT("/") + KEY_STARNET_GROUP_TIMEOUT6, long(m_starNet6GroupTimeout)); + m_config->Write(wxT("/") + KEY_STARNET_CALLSIGN_SWITCH6, long(m_starNet6CallsignSwitch)); + m_config->Write(wxT("/") + KEY_STARNET_TXMSG_SWITCH6, m_starNet6TxMsgSwitch); + m_config->Write(wxT("/") + KEY_STARNET_REFLECTOR6, m_starNet6Reflector); + m_config->Write(wxT("/") + KEY_STARNET_BAND7, m_starNet7Band); + m_config->Write(wxT("/") + KEY_STARNET_CALLSIGN7, m_starNet7Callsign); + m_config->Write(wxT("/") + KEY_STARNET_LOGOFF7, m_starNet7Logoff); + m_config->Write(wxT("/") + KEY_STARNET_INFO7, m_starNet7Info); + m_config->Write(wxT("/") + KEY_STARNET_PERMANENT7, m_starNet7Permanent); + m_config->Write(wxT("/") + KEY_STARNET_USER_TIMEOUT7, long(m_starNet7UserTimeout)); + m_config->Write(wxT("/") + KEY_STARNET_GROUP_TIMEOUT7, long(m_starNet7GroupTimeout)); + m_config->Write(wxT("/") + KEY_STARNET_CALLSIGN_SWITCH7, long(m_starNet7CallsignSwitch)); + m_config->Write(wxT("/") + KEY_STARNET_TXMSG_SWITCH7, m_starNet7TxMsgSwitch); + m_config->Write(wxT("/") + KEY_STARNET_REFLECTOR7, m_starNet7Reflector); + m_config->Write(wxT("/") + KEY_STARNET_BAND8, m_starNet8Band); + m_config->Write(wxT("/") + KEY_STARNET_CALLSIGN8, m_starNet8Callsign); + m_config->Write(wxT("/") + KEY_STARNET_LOGOFF8, m_starNet8Logoff); + m_config->Write(wxT("/") + KEY_STARNET_INFO8, m_starNet8Info); + m_config->Write(wxT("/") + KEY_STARNET_PERMANENT8, m_starNet8Permanent); + m_config->Write(wxT("/") + KEY_STARNET_USER_TIMEOUT8, long(m_starNet8UserTimeout)); + m_config->Write(wxT("/") + KEY_STARNET_GROUP_TIMEOUT8, long(m_starNet8GroupTimeout)); + m_config->Write(wxT("/") + KEY_STARNET_CALLSIGN_SWITCH8, long(m_starNet8CallsignSwitch)); + m_config->Write(wxT("/") + KEY_STARNET_TXMSG_SWITCH8, m_starNet8TxMsgSwitch); + m_config->Write(wxT("/") + KEY_STARNET_REFLECTOR8, m_starNet8Reflector); + m_config->Write(wxT("/") + KEY_STARNET_BAND9, m_starNet9Band); + m_config->Write(wxT("/") + KEY_STARNET_CALLSIGN9, m_starNet9Callsign); + m_config->Write(wxT("/") + KEY_STARNET_LOGOFF9, m_starNet9Logoff); + m_config->Write(wxT("/") + KEY_STARNET_INFO9, m_starNet9Info); + m_config->Write(wxT("/") + KEY_STARNET_PERMANENT9, m_starNet9Permanent); + m_config->Write(wxT("/") + KEY_STARNET_USER_TIMEOUT9, long(m_starNet9UserTimeout)); + m_config->Write(wxT("/") + KEY_STARNET_GROUP_TIMEOUT9, long(m_starNet9GroupTimeout)); + m_config->Write(wxT("/") + KEY_STARNET_CALLSIGN_SWITCH9, long(m_starNet9CallsignSwitch)); + m_config->Write(wxT("/") + KEY_STARNET_TXMSG_SWITCH9, m_starNet9TxMsgSwitch); + m_config->Write(wxT("/") + KEY_STARNET_REFLECTOR9, m_starNet9Reflector); + m_config->Write(wxT("/") + KEY_STARNET_BAND10, m_starNet10Band); + m_config->Write(wxT("/") + KEY_STARNET_CALLSIGN10, m_starNet10Callsign); + m_config->Write(wxT("/") + KEY_STARNET_LOGOFF10, m_starNet10Logoff); + m_config->Write(wxT("/") + KEY_STARNET_INFO10, m_starNet10Info); + m_config->Write(wxT("/") + KEY_STARNET_PERMANENT10, m_starNet10Permanent); + m_config->Write(wxT("/") + KEY_STARNET_USER_TIMEOUT10, long(m_starNet10UserTimeout)); + m_config->Write(wxT("/") + KEY_STARNET_GROUP_TIMEOUT10, long(m_starNet10GroupTimeout)); + m_config->Write(wxT("/") + KEY_STARNET_CALLSIGN_SWITCH10, long(m_starNet10CallsignSwitch)); + m_config->Write(wxT("/") + KEY_STARNET_TXMSG_SWITCH10, m_starNet10TxMsgSwitch); + m_config->Write(wxT("/") + KEY_STARNET_REFLECTOR10, m_starNet10Reflector); + m_config->Write(wxT("/") + KEY_STARNET_BAND11, m_starNet11Band); + m_config->Write(wxT("/") + KEY_STARNET_CALLSIGN11, m_starNet11Callsign); + m_config->Write(wxT("/") + KEY_STARNET_LOGOFF11, m_starNet11Logoff); + m_config->Write(wxT("/") + KEY_STARNET_INFO11, m_starNet11Info); + m_config->Write(wxT("/") + KEY_STARNET_PERMANENT11, m_starNet11Permanent); + m_config->Write(wxT("/") + KEY_STARNET_USER_TIMEOUT11, long(m_starNet11UserTimeout)); + m_config->Write(wxT("/") + KEY_STARNET_GROUP_TIMEOUT11, long(m_starNet11GroupTimeout)); + m_config->Write(wxT("/") + KEY_STARNET_CALLSIGN_SWITCH11, long(m_starNet11CallsignSwitch)); + m_config->Write(wxT("/") + KEY_STARNET_TXMSG_SWITCH11, m_starNet11TxMsgSwitch); + m_config->Write(wxT("/") + KEY_STARNET_REFLECTOR11, m_starNet11Reflector); + m_config->Write(wxT("/") + KEY_STARNET_BAND12, m_starNet12Band); + m_config->Write(wxT("/") + KEY_STARNET_CALLSIGN12, m_starNet12Callsign); + m_config->Write(wxT("/") + KEY_STARNET_LOGOFF12, m_starNet12Logoff); + m_config->Write(wxT("/") + KEY_STARNET_INFO12, m_starNet12Info); + m_config->Write(wxT("/") + KEY_STARNET_PERMANENT12, m_starNet12Permanent); + m_config->Write(wxT("/") + KEY_STARNET_USER_TIMEOUT12, long(m_starNet12UserTimeout)); + m_config->Write(wxT("/") + KEY_STARNET_GROUP_TIMEOUT12, long(m_starNet12GroupTimeout)); + m_config->Write(wxT("/") + KEY_STARNET_CALLSIGN_SWITCH12, long(m_starNet12CallsignSwitch)); + m_config->Write(wxT("/") + KEY_STARNET_TXMSG_SWITCH12, m_starNet12TxMsgSwitch); + m_config->Write(wxT("/") + KEY_STARNET_REFLECTOR12, m_starNet12Reflector); + m_config->Write(wxT("/") + KEY_STARNET_BAND13, m_starNet13Band); + m_config->Write(wxT("/") + KEY_STARNET_CALLSIGN13, m_starNet13Callsign); + m_config->Write(wxT("/") + KEY_STARNET_LOGOFF13, m_starNet13Logoff); + m_config->Write(wxT("/") + KEY_STARNET_INFO13, m_starNet13Info); + m_config->Write(wxT("/") + KEY_STARNET_PERMANENT13, m_starNet13Permanent); + m_config->Write(wxT("/") + KEY_STARNET_USER_TIMEOUT13, long(m_starNet13UserTimeout)); + m_config->Write(wxT("/") + KEY_STARNET_GROUP_TIMEOUT13, long(m_starNet13GroupTimeout)); + m_config->Write(wxT("/") + KEY_STARNET_CALLSIGN_SWITCH13, long(m_starNet13CallsignSwitch)); + m_config->Write(wxT("/") + KEY_STARNET_TXMSG_SWITCH13, m_starNet13TxMsgSwitch); + m_config->Write(wxT("/") + KEY_STARNET_REFLECTOR13, m_starNet13Reflector); + m_config->Write(wxT("/") + KEY_STARNET_BAND14, m_starNet14Band); + m_config->Write(wxT("/") + KEY_STARNET_CALLSIGN14, m_starNet14Callsign); + m_config->Write(wxT("/") + KEY_STARNET_LOGOFF14, m_starNet14Logoff); + m_config->Write(wxT("/") + KEY_STARNET_INFO14, m_starNet14Info); + m_config->Write(wxT("/") + KEY_STARNET_PERMANENT14, m_starNet14Permanent); + m_config->Write(wxT("/") + KEY_STARNET_USER_TIMEOUT14, long(m_starNet14UserTimeout)); + m_config->Write(wxT("/") + KEY_STARNET_GROUP_TIMEOUT14, long(m_starNet14GroupTimeout)); + m_config->Write(wxT("/") + KEY_STARNET_CALLSIGN_SWITCH14, long(m_starNet14CallsignSwitch)); + m_config->Write(wxT("/") + KEY_STARNET_TXMSG_SWITCH14, m_starNet14TxMsgSwitch); + m_config->Write(wxT("/") + KEY_STARNET_REFLECTOR14, m_starNet14Reflector); + m_config->Write(wxT("/") + KEY_STARNET_BAND15, m_starNet15Band); + m_config->Write(wxT("/") + KEY_STARNET_CALLSIGN15, m_starNet15Callsign); + m_config->Write(wxT("/") + KEY_STARNET_LOGOFF15, m_starNet15Logoff); + m_config->Write(wxT("/") + KEY_STARNET_INFO15, m_starNet15Info); + m_config->Write(wxT("/") + KEY_STARNET_PERMANENT15, m_starNet15Permanent); + m_config->Write(wxT("/") + KEY_STARNET_USER_TIMEOUT15, long(m_starNet15UserTimeout)); + m_config->Write(wxT("/") + KEY_STARNET_GROUP_TIMEOUT15, long(m_starNet15GroupTimeout)); + m_config->Write(wxT("/") + KEY_STARNET_CALLSIGN_SWITCH15, long(m_starNet15CallsignSwitch)); + m_config->Write(wxT("/") + KEY_STARNET_TXMSG_SWITCH15, m_starNet15TxMsgSwitch); + m_config->Write(wxT("/") + KEY_STARNET_REFLECTOR15, m_starNet15Reflector); + m_config->Write(wxT("/") + KEY_REMOTE_ENABLED, m_remoteEnabled); + m_config->Write(wxT("/") + KEY_REMOTE_PASSWORD, m_remotePassword); + m_config->Write(wxT("/") + KEY_REMOTE_PORT, long(m_remotePort)); + m_config->Write(wxT("/") + KEY_LOG_ENABLED, m_logEnabled); + m_config->Write(wxT("/") + KEY_WINDOW_X, long(m_x)); + m_config->Write(wxT("/") + KEY_WINDOW_Y, long(m_y)); + m_config->Flush(); +#endif + + wxTextFile file(m_fileName.GetFullPath()); + + bool exists = file.Exists(); + if (exists) { + bool ret = file.Open(); + if (!ret) { + wxLogError(wxT("Cannot open the config file - %s"), m_fileName.GetFullPath().c_str()); + return false; + } + + // Remove the existing file entries + file.Clear(); + } else { + bool ret = file.Create(); + if (!ret) { + wxLogError(wxT("Cannot create the config file - %s"), m_fileName.GetFullPath().c_str()); + return false; + } + } + + wxString buffer; + buffer.Printf(wxT("%s=%s"), KEY_CALLSIGN.c_str(), m_callsign.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_ADDRESS.c_str(), m_address.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_IRCDDB_HOSTNAME.c_str(), m_ircddbHostname.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_IRCDDB_USERNAME.c_str(), m_ircddbUsername.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_IRCDDB_PASSWORD.c_str(), m_ircddbPassword.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_BAND1.c_str(), m_starNet1Band.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_CALLSIGN1.c_str(), m_starNet1Callsign.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_LOGOFF1.c_str(), m_starNet1Logoff.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_INFO1.c_str(), m_starNet1Info.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_PERMANENT1.c_str(), m_starNet1Permanent.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_USER_TIMEOUT1.c_str(), m_starNet1UserTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_GROUP_TIMEOUT1.c_str(), m_starNet1GroupTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_CALLSIGN_SWITCH1.c_str(), int(m_starNet1CallsignSwitch)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_TXMSG_SWITCH1.c_str(), m_starNet1TxMsgSwitch ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_REFLECTOR1.c_str(), m_starNet1Reflector.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_BAND2.c_str(), m_starNet2Band.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_CALLSIGN2.c_str(), m_starNet2Callsign.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_LOGOFF2.c_str(), m_starNet2Logoff.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_INFO2.c_str(), m_starNet2Info.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_PERMANENT2.c_str(), m_starNet2Permanent.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_USER_TIMEOUT2.c_str(), m_starNet2UserTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_GROUP_TIMEOUT2.c_str(), m_starNet2GroupTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_CALLSIGN_SWITCH2.c_str(), int(m_starNet2CallsignSwitch)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_TXMSG_SWITCH2.c_str(), m_starNet2TxMsgSwitch ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_REFLECTOR2.c_str(), m_starNet2Reflector.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_BAND3.c_str(), m_starNet3Band.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_CALLSIGN3.c_str(), m_starNet3Callsign.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_LOGOFF3.c_str(), m_starNet3Logoff.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_INFO3.c_str(), m_starNet3Info.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_PERMANENT3.c_str(), m_starNet3Permanent.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_USER_TIMEOUT3.c_str(), m_starNet3UserTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_GROUP_TIMEOUT3.c_str(), m_starNet3GroupTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_CALLSIGN_SWITCH3.c_str(), int(m_starNet3CallsignSwitch)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_TXMSG_SWITCH3.c_str(), m_starNet3TxMsgSwitch ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_REFLECTOR3.c_str(), m_starNet3Reflector.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_BAND4.c_str(), m_starNet4Band.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_CALLSIGN4.c_str(), m_starNet4Callsign.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_LOGOFF4.c_str(), m_starNet4Logoff.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_INFO4.c_str(), m_starNet4Info.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_PERMANENT4.c_str(), m_starNet4Permanent.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_USER_TIMEOUT4.c_str(), m_starNet4UserTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_GROUP_TIMEOUT4.c_str(), m_starNet4GroupTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_CALLSIGN_SWITCH4.c_str(), int(m_starNet4CallsignSwitch)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_TXMSG_SWITCH4.c_str(), m_starNet4TxMsgSwitch ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_REFLECTOR4.c_str(), m_starNet4Reflector.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_BAND5.c_str(), m_starNet5Band.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_CALLSIGN5.c_str(), m_starNet5Callsign.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_LOGOFF5.c_str(), m_starNet5Logoff.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_INFO5.c_str(), m_starNet5Info.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_PERMANENT5.c_str(), m_starNet5Permanent.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_USER_TIMEOUT5.c_str(), m_starNet5UserTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_GROUP_TIMEOUT5.c_str(), m_starNet5GroupTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_CALLSIGN_SWITCH5.c_str(), int(m_starNet5CallsignSwitch)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_TXMSG_SWITCH5.c_str(), m_starNet5TxMsgSwitch ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_REFLECTOR5.c_str(), m_starNet5Reflector.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_BAND6.c_str(), m_starNet6Band.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_CALLSIGN6.c_str(), m_starNet6Callsign.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_LOGOFF6.c_str(), m_starNet6Logoff.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_INFO6.c_str(), m_starNet6Info.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_PERMANENT6.c_str(), m_starNet6Permanent.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_USER_TIMEOUT6.c_str(), m_starNet6UserTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_GROUP_TIMEOUT6.c_str(), m_starNet6GroupTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_CALLSIGN_SWITCH6.c_str(), int(m_starNet6CallsignSwitch)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_TXMSG_SWITCH6.c_str(), m_starNet6TxMsgSwitch ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_REFLECTOR6.c_str(), m_starNet6Reflector.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_BAND7.c_str(), m_starNet7Band.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_CALLSIGN7.c_str(), m_starNet7Callsign.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_LOGOFF7.c_str(), m_starNet7Logoff.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_INFO7.c_str(), m_starNet7Info.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_PERMANENT7.c_str(), m_starNet7Permanent.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_USER_TIMEOUT7.c_str(), m_starNet7UserTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_GROUP_TIMEOUT7.c_str(), m_starNet7GroupTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_CALLSIGN_SWITCH7.c_str(), int(m_starNet7CallsignSwitch)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_TXMSG_SWITCH7.c_str(), m_starNet7TxMsgSwitch ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_REFLECTOR7.c_str(), m_starNet7Reflector.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_BAND8.c_str(), m_starNet8Band.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_CALLSIGN8.c_str(), m_starNet8Callsign.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_LOGOFF8.c_str(), m_starNet8Logoff.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_INFO8.c_str(), m_starNet8Info.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_PERMANENT8.c_str(), m_starNet8Permanent.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_USER_TIMEOUT8.c_str(), m_starNet8UserTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_GROUP_TIMEOUT8.c_str(), m_starNet8GroupTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_CALLSIGN_SWITCH8.c_str(), int(m_starNet8CallsignSwitch)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_TXMSG_SWITCH8.c_str(), m_starNet8TxMsgSwitch ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_REFLECTOR8.c_str(), m_starNet8Reflector.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_BAND9.c_str(), m_starNet9Band.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_CALLSIGN9.c_str(), m_starNet9Callsign.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_LOGOFF9.c_str(), m_starNet9Logoff.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_INFO9.c_str(), m_starNet9Info.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_PERMANENT9.c_str(), m_starNet9Permanent.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_USER_TIMEOUT9.c_str(), m_starNet9UserTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_GROUP_TIMEOUT9.c_str(), m_starNet9GroupTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_CALLSIGN_SWITCH9.c_str(), int(m_starNet9CallsignSwitch)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_TXMSG_SWITCH9.c_str(), m_starNet9TxMsgSwitch ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_REFLECTOR9.c_str(), m_starNet9Reflector.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_BAND10.c_str(), m_starNet10Band.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_CALLSIGN10.c_str(), m_starNet10Callsign.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_LOGOFF10.c_str(), m_starNet10Logoff.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_INFO10.c_str(), m_starNet10Info.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_PERMANENT10.c_str(), m_starNet10Permanent.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_USER_TIMEOUT10.c_str(), m_starNet10UserTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_GROUP_TIMEOUT10.c_str(), m_starNet10GroupTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_CALLSIGN_SWITCH10.c_str(), int(m_starNet10CallsignSwitch)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_TXMSG_SWITCH10.c_str(), m_starNet10TxMsgSwitch ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_REFLECTOR10.c_str(), m_starNet10Reflector.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_BAND11.c_str(), m_starNet11Band.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_CALLSIGN11.c_str(), m_starNet11Callsign.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_LOGOFF11.c_str(), m_starNet11Logoff.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_INFO11.c_str(), m_starNet11Info.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_PERMANENT11.c_str(), m_starNet11Permanent.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_USER_TIMEOUT11.c_str(), m_starNet11UserTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_GROUP_TIMEOUT11.c_str(), m_starNet11GroupTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_CALLSIGN_SWITCH11.c_str(), int(m_starNet11CallsignSwitch)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_TXMSG_SWITCH11.c_str(), m_starNet11TxMsgSwitch ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_REFLECTOR11.c_str(), m_starNet11Reflector.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_BAND12.c_str(), m_starNet12Band.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_CALLSIGN12.c_str(), m_starNet12Callsign.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_LOGOFF12.c_str(), m_starNet12Logoff.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_INFO12.c_str(), m_starNet12Info.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_PERMANENT12.c_str(), m_starNet12Permanent.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_USER_TIMEOUT12.c_str(), m_starNet12UserTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_GROUP_TIMEOUT12.c_str(), m_starNet12GroupTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_CALLSIGN_SWITCH12.c_str(), int(m_starNet12CallsignSwitch)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_TXMSG_SWITCH12.c_str(), m_starNet12TxMsgSwitch ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_REFLECTOR12.c_str(), m_starNet12Reflector.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_BAND13.c_str(), m_starNet13Band.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_CALLSIGN13.c_str(), m_starNet13Callsign.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_LOGOFF13.c_str(), m_starNet13Logoff.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_INFO13.c_str(), m_starNet13Info.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_PERMANENT13.c_str(), m_starNet13Permanent.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_USER_TIMEOUT13.c_str(), m_starNet13UserTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_GROUP_TIMEOUT13.c_str(), m_starNet13GroupTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_CALLSIGN_SWITCH13.c_str(), int(m_starNet13CallsignSwitch)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_TXMSG_SWITCH13.c_str(), m_starNet13TxMsgSwitch ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_REFLECTOR13.c_str(), m_starNet13Reflector.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_BAND14.c_str(), m_starNet14Band.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_CALLSIGN14.c_str(), m_starNet14Callsign.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_LOGOFF14.c_str(), m_starNet14Logoff.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_INFO14.c_str(), m_starNet14Info.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_PERMANENT14.c_str(), m_starNet14Permanent.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_USER_TIMEOUT14.c_str(), m_starNet14UserTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_GROUP_TIMEOUT14.c_str(), m_starNet14GroupTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_CALLSIGN_SWITCH14.c_str(), int(m_starNet14CallsignSwitch)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_TXMSG_SWITCH14.c_str(), m_starNet14TxMsgSwitch ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_REFLECTOR14.c_str(), m_starNet14Reflector.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_BAND15.c_str(), m_starNet15Band.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_CALLSIGN15.c_str(), m_starNet15Callsign.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_LOGOFF15.c_str(), m_starNet15Logoff.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_INFO15.c_str(), m_starNet15Info.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_PERMANENT15.c_str(), m_starNet15Permanent.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_USER_TIMEOUT15.c_str(), m_starNet15UserTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_STARNET_GROUP_TIMEOUT15.c_str(), m_starNet15GroupTimeout); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_CALLSIGN_SWITCH15.c_str(), int(m_starNet15CallsignSwitch)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_STARNET_TXMSG_SWITCH15.c_str(), m_starNet15TxMsgSwitch ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_STARNET_REFLECTOR15.c_str(), m_starNet15Reflector.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_REMOTE_ENABLED.c_str(), m_remoteEnabled ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_REMOTE_PASSWORD.c_str(), m_remotePassword.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_REMOTE_PORT.c_str(), m_remotePort); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_LOG_ENABLED.c_str(), m_logEnabled ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_WINDOW_X.c_str(), m_x); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_WINDOW_Y.c_str(), m_y); file.AddLine(buffer); + + bool ret = file.Write(); + if (!ret) { + file.Close(); + wxLogError(wxT("Cannot write the config file - %s"), m_fileName.GetFullPath().c_str()); + return false; + } + + file.Close(); + + return true; +} diff --git a/StarNetServer/StarNetServerConfig.h b/StarNetServer/StarNetServerConfig.h new file mode 100644 index 0000000..cf6f2a0 --- /dev/null +++ b/StarNetServer/StarNetServerConfig.h @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2010,2011,2012,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. + */ + +#ifndef StarNetServerConfig_H +#define StarNetServerConfig_H + +#include "Defs.h" + +#include +#include +#include + +class CStarNetServerConfig { +public: +#if defined(__WINDOWS__) + CStarNetServerConfig(wxConfigBase* config, const wxString& dir); +#else + CStarNetServerConfig(const wxString& dir); +#endif + ~CStarNetServerConfig(); + + void getGateway(wxString& callsign, wxString& address) const; + void setGateway(const wxString& callsign, const wxString& address); + + void getIrcDDB(wxString& hostname, wxString& username, wxString& password) const; + void setIrcDDB(const wxString& hostname, const wxString& username, const wxString& password); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + void getStarNet1(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + void setStarNet1(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); + + void getStarNet2(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + void setStarNet2(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); + + void getStarNet3(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + void setStarNet3(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); + + void getStarNet4(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + void setStarNet4(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); + + void getStarNet5(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + void setStarNet5(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); + + void getStarNet6(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + void setStarNet6(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); + + void getStarNet7(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + void setStarNet7(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); + + void getStarNet8(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + void setStarNet8(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); + + void getStarNet9(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + void setStarNet9(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); + + void getStarNet10(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + void setStarNet10(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); + + void getStarNet11(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + void setStarNet11(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); + + void getStarNet12(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + void setStarNet12(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); + + void getStarNet13(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + void setStarNet13(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); + + void getStarNet14(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + void setStarNet14(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); + + void getStarNet15(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, wxString& reflector) const; + void setStarNet15(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); +#else + void getStarNet1(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + void setStarNet1(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); + + void getStarNet2(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + void setStarNet2(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); + + void getStarNet3(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + void setStarNet3(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); + + void getStarNet4(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + void setStarNet4(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); + + void getStarNet5(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + void setStarNet5(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); + + void getStarNet6(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + void setStarNet6(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); + + void getStarNet7(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + void setStarNet7(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); + + void getStarNet8(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + void setStarNet8(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); + + void getStarNet9(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + void setStarNet9(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); + + void getStarNet10(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + void setStarNet10(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); + + void getStarNet11(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + void setStarNet11(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); + + void getStarNet12(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + void setStarNet12(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); + + void getStarNet13(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + void setStarNet13(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); + + void getStarNet14(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + void setStarNet14(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); + + void getStarNet15(wxString& band, wxString& callsign, wxString& logoff, wxString& info, wxString& permanent, unsigned int& userTimeout, unsigned int& groupTimeout, STARNET_CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch) const; + void setStarNet15(const wxString& band, const wxString& callsign, const wxString& logoff, const wxString& info, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); +#endif + + void getRemote(bool& enabled, wxString& password, unsigned int& port) const; + void setRemote(bool enabled, const wxString& password, unsigned int port); + + void getMiscellaneous(bool& enabled) const; + void setMiscellaneous(bool enabled); + + void getPosition(int& x, int& y) const; + void setPosition(int x, int y); + + bool write(); + +private: +#if defined(__WINDOWS__) + wxConfigBase* m_config; +#endif + wxFileName m_fileName; + wxString m_callsign; + wxString m_address; + wxString m_ircddbHostname; + wxString m_ircddbUsername; + wxString m_ircddbPassword; + wxString m_starNet1Band; + wxString m_starNet1Callsign; + wxString m_starNet1Logoff; + wxString m_starNet1Info; + wxString m_starNet1Permanent; + unsigned int m_starNet1UserTimeout; + unsigned int m_starNet1GroupTimeout; + STARNET_CALLSIGN_SWITCH m_starNet1CallsignSwitch; + bool m_starNet1TxMsgSwitch; + wxString m_starNet1Reflector; + wxString m_starNet2Band; + wxString m_starNet2Callsign; + wxString m_starNet2Logoff; + wxString m_starNet2Info; + wxString m_starNet2Permanent; + unsigned int m_starNet2UserTimeout; + unsigned int m_starNet2GroupTimeout; + STARNET_CALLSIGN_SWITCH m_starNet2CallsignSwitch; + bool m_starNet2TxMsgSwitch; + wxString m_starNet2Reflector; + wxString m_starNet3Band; + wxString m_starNet3Callsign; + wxString m_starNet3Logoff; + wxString m_starNet3Info; + wxString m_starNet3Permanent; + unsigned int m_starNet3UserTimeout; + unsigned int m_starNet3GroupTimeout; + STARNET_CALLSIGN_SWITCH m_starNet3CallsignSwitch; + bool m_starNet3TxMsgSwitch; + wxString m_starNet3Reflector; + wxString m_starNet4Band; + wxString m_starNet4Callsign; + wxString m_starNet4Logoff; + wxString m_starNet4Info; + wxString m_starNet4Permanent; + unsigned int m_starNet4UserTimeout; + unsigned int m_starNet4GroupTimeout; + STARNET_CALLSIGN_SWITCH m_starNet4CallsignSwitch; + bool m_starNet4TxMsgSwitch; + wxString m_starNet4Reflector; + wxString m_starNet5Band; + wxString m_starNet5Callsign; + wxString m_starNet5Logoff; + wxString m_starNet5Info; + wxString m_starNet5Permanent; + unsigned int m_starNet5UserTimeout; + unsigned int m_starNet5GroupTimeout; + STARNET_CALLSIGN_SWITCH m_starNet5CallsignSwitch; + bool m_starNet5TxMsgSwitch; + wxString m_starNet5Reflector; + wxString m_starNet6Band; + wxString m_starNet6Callsign; + wxString m_starNet6Logoff; + wxString m_starNet6Info; + wxString m_starNet6Permanent; + unsigned int m_starNet6UserTimeout; + unsigned int m_starNet6GroupTimeout; + STARNET_CALLSIGN_SWITCH m_starNet6CallsignSwitch; + bool m_starNet6TxMsgSwitch; + wxString m_starNet6Reflector; + wxString m_starNet7Band; + wxString m_starNet7Callsign; + wxString m_starNet7Logoff; + wxString m_starNet7Info; + wxString m_starNet7Permanent; + unsigned int m_starNet7UserTimeout; + unsigned int m_starNet7GroupTimeout; + STARNET_CALLSIGN_SWITCH m_starNet7CallsignSwitch; + bool m_starNet7TxMsgSwitch; + wxString m_starNet7Reflector; + wxString m_starNet8Band; + wxString m_starNet8Callsign; + wxString m_starNet8Logoff; + wxString m_starNet8Info; + wxString m_starNet8Permanent; + unsigned int m_starNet8UserTimeout; + unsigned int m_starNet8GroupTimeout; + STARNET_CALLSIGN_SWITCH m_starNet8CallsignSwitch; + bool m_starNet8TxMsgSwitch; + wxString m_starNet8Reflector; + wxString m_starNet9Band; + wxString m_starNet9Callsign; + wxString m_starNet9Logoff; + wxString m_starNet9Info; + wxString m_starNet9Permanent; + unsigned int m_starNet9UserTimeout; + unsigned int m_starNet9GroupTimeout; + STARNET_CALLSIGN_SWITCH m_starNet9CallsignSwitch; + bool m_starNet9TxMsgSwitch; + wxString m_starNet9Reflector; + wxString m_starNet10Band; + wxString m_starNet10Callsign; + wxString m_starNet10Logoff; + wxString m_starNet10Info; + wxString m_starNet10Permanent; + unsigned int m_starNet10UserTimeout; + unsigned int m_starNet10GroupTimeout; + STARNET_CALLSIGN_SWITCH m_starNet10CallsignSwitch; + bool m_starNet10TxMsgSwitch; + wxString m_starNet10Reflector; + wxString m_starNet11Band; + wxString m_starNet11Callsign; + wxString m_starNet11Logoff; + wxString m_starNet11Info; + wxString m_starNet11Permanent; + unsigned int m_starNet11UserTimeout; + unsigned int m_starNet11GroupTimeout; + STARNET_CALLSIGN_SWITCH m_starNet11CallsignSwitch; + bool m_starNet11TxMsgSwitch; + wxString m_starNet11Reflector; + wxString m_starNet12Band; + wxString m_starNet12Callsign; + wxString m_starNet12Logoff; + wxString m_starNet12Info; + wxString m_starNet12Permanent; + unsigned int m_starNet12UserTimeout; + unsigned int m_starNet12GroupTimeout; + STARNET_CALLSIGN_SWITCH m_starNet12CallsignSwitch; + bool m_starNet12TxMsgSwitch; + wxString m_starNet12Reflector; + wxString m_starNet13Band; + wxString m_starNet13Callsign; + wxString m_starNet13Logoff; + wxString m_starNet13Info; + wxString m_starNet13Permanent; + unsigned int m_starNet13UserTimeout; + unsigned int m_starNet13GroupTimeout; + STARNET_CALLSIGN_SWITCH m_starNet13CallsignSwitch; + bool m_starNet13TxMsgSwitch; + wxString m_starNet13Reflector; + wxString m_starNet14Band; + wxString m_starNet14Callsign; + wxString m_starNet14Logoff; + wxString m_starNet14Info; + wxString m_starNet14Permanent; + unsigned int m_starNet14UserTimeout; + unsigned int m_starNet14GroupTimeout; + STARNET_CALLSIGN_SWITCH m_starNet14CallsignSwitch; + bool m_starNet14TxMsgSwitch; + wxString m_starNet14Reflector; + wxString m_starNet15Band; + wxString m_starNet15Callsign; + wxString m_starNet15Logoff; + wxString m_starNet15Info; + wxString m_starNet15Permanent; + unsigned int m_starNet15UserTimeout; + unsigned int m_starNet15GroupTimeout; + STARNET_CALLSIGN_SWITCH m_starNet15CallsignSwitch; + bool m_starNet15TxMsgSwitch; + wxString m_starNet15Reflector; + bool m_remoteEnabled; + wxString m_remotePassword; + unsigned int m_remotePort; + bool m_logEnabled; + int m_x; + int m_y; +}; + +#endif diff --git a/StarNetServer/StarNetServerDefs.h b/StarNetServer/StarNetServerDefs.h new file mode 100644 index 0000000..d1f3538 --- /dev/null +++ b/StarNetServer/StarNetServerDefs.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2010,2011,2012,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. + */ + +#ifndef StarNetServerDefs_H +#define StarNetServerDefs_H + +#include + +const wxString APPLICATION_NAME = wxT("StarNet Server"); + +const wxString LOG_BASE_NAME = wxT("STARnetServer"); + +const wxString CONFIG_FILE_NAME = wxT("starnetserver"); + +const unsigned int MAX_STARNETS = 15U; +const unsigned int MAX_DEXTRA_LINKS = 15U; +const unsigned int MAX_DCS_LINKS = 15U; +const unsigned int MAX_ROUTES = 0U; + +#endif diff --git a/StarNetServer/StarNetServerFrame.cpp b/StarNetServer/StarNetServerFrame.cpp new file mode 100644 index 0000000..c931797 --- /dev/null +++ b/StarNetServer/StarNetServerFrame.cpp @@ -0,0 +1,719 @@ +/* + * 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 + * 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 "StarNetServerPreferences.h" +#include "StarNetServerFrame.h" +#include "StarNetServerDefs.h" +#include "StarNetServerApp.h" +#include "LogEvent.h" +#include "Version.h" + +const unsigned int BORDER_SIZE = 5U; + +#if defined(__WINDOWS__) +const unsigned int LOGTEXT_WIDTH = 560U; +#else +const unsigned int LOGTEXT_WIDTH = 700U; +#endif + +#include +#include + +DEFINE_EVENT_TYPE(LOG_EVENT) + +enum { + Menu_Edit_Preferences = 6000, + Menu_View_Updates +}; + +BEGIN_EVENT_TABLE(CStarNetServerFrame, wxFrame) + EVT_MENU(wxID_EXIT, CStarNetServerFrame::onQuit) + EVT_MENU(Menu_Edit_Preferences, CStarNetServerFrame::onPreferences) + EVT_MENU(Menu_View_Updates, CStarNetServerFrame::onUpdates) + EVT_MENU(wxID_ABOUT, CStarNetServerFrame::onAbout) + + EVT_CLOSE(CStarNetServerFrame::onClose) + + EVT_CUSTOM(LOG_EVENT, wxID_ANY, CStarNetServerFrame::onLog) +END_EVENT_TABLE() + +CStarNetServerFrame::CStarNetServerFrame(const wxString& title, const wxPoint& position, bool gui) : +wxFrame(NULL, -1, title, position), +#if defined(__WXDEBUG__) +m_updates(true) +#else +m_updates(gui) +#endif +{ + SetMenuBar(createMenuBar()); + + wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); + + wxPanel* panel = new wxPanel(this); + + wxBoxSizer* panelSizer = new wxBoxSizer(wxVERTICAL); + + wxStaticBoxSizer* log1Sizer = new wxStaticBoxSizer(new wxStaticBox(panel, -1, _("Log")), wxVERTICAL); + wxBoxSizer* log2Sizer = new wxBoxSizer(wxVERTICAL); + + for (unsigned int i = 0U; i < 20U; i++) { + m_logLine[i] = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(LOGTEXT_WIDTH, -1)); + m_logLine[i]->Wrap(LOGTEXT_WIDTH); + log2Sizer->Add(m_logLine[i], 0, wxTOP | wxLEFT | wxRIGHT, BORDER_SIZE); + } + + log1Sizer->Add(log2Sizer); + panelSizer->Add(log1Sizer, 0, wxALL, BORDER_SIZE); + + panel->SetSizer(panelSizer); + panelSizer->SetSizeHints(panel); + + mainSizer->Add(panel); + + SetSizer(mainSizer); + mainSizer->SetSizeHints(this); +} + +CStarNetServerFrame::~CStarNetServerFrame() +{ +} + +wxMenuBar* CStarNetServerFrame::createMenuBar() +{ + wxMenu* fileMenu = new wxMenu(); + fileMenu->Append(wxID_EXIT, _("Exit")); + + wxMenu* editMenu = new wxMenu(); + editMenu->Append(Menu_Edit_Preferences, _("Preferences...")); + + wxMenu* viewMenu = new wxMenu(); + viewMenu->AppendCheckItem(Menu_View_Updates, _("GUI Updates")); + viewMenu->Check(Menu_View_Updates, m_updates); + + wxMenu* helpMenu = new wxMenu(); + helpMenu->Append(wxID_ABOUT, _("About StarNet Server")); + + wxMenuBar* menuBar = new wxMenuBar(); + menuBar->Append(fileMenu, _("File")); + menuBar->Append(editMenu, _("Edit")); + menuBar->Append(viewMenu, _("View")); + menuBar->Append(helpMenu, _("Help")); + + return menuBar; +} + +void CStarNetServerFrame::onQuit(wxCommandEvent&) +{ + Close(false); +} + +void CStarNetServerFrame::onClose(wxCloseEvent&) +{ + int x, y; + GetPosition(&x, &y); + if (x >= 0 && y >= 0) { + ::wxGetApp().setPosition(x, y); + ::wxGetApp().writeConfig(); + } + + Destroy(); +} + +void CStarNetServerFrame::onPreferences(wxCommandEvent&) +{ + wxString callsign, address; + ::wxGetApp().getGateway(callsign, address); + + wxString hostname, username, password; + ::wxGetApp().getIrcDDB(hostname, username, password); + + unsigned int remotePort; + wxString remotePassword; + bool remoteEnabled; + ::wxGetApp().getRemote(remoteEnabled, remotePassword, remotePort); + + bool logEnabled; + ::wxGetApp().getMiscellaneous(logEnabled); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + wxString starNetBand1, starNetCallsign1, starNetLogoff1, starNetInfo1, starNetLink1, starNetPermanent1; + unsigned int starNetUserTimeout1, starNetGroupTimeout1; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch1; + bool starNetTXMsgSwitch1; + ::wxGetApp().getStarNet1(starNetBand1, starNetCallsign1, starNetLogoff1, starNetInfo1, starNetPermanent1, starNetUserTimeout1, starNetGroupTimeout1, starNetCallsignSwitch1, starNetTXMsgSwitch1, starNetLink1); + + wxString starNetBand2, starNetCallsign2, starNetLogoff2, starNetInfo2, starNetLink2, starNetPermanent2; + unsigned int starNetUserTimeout2, starNetGroupTimeout2; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch2; + bool starNetTXMsgSwitch2; + ::wxGetApp().getStarNet2(starNetBand2, starNetCallsign2, starNetLogoff2, starNetInfo2, starNetPermanent2, starNetUserTimeout2, starNetGroupTimeout2, starNetCallsignSwitch2, starNetTXMsgSwitch2, starNetLink2); + + wxString starNetBand3, starNetCallsign3, starNetLogoff3, starNetInfo3, starNetLink3, starNetPermanent3; + unsigned int starNetUserTimeout3, starNetGroupTimeout3; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch3; + bool starNetTXMsgSwitch3; + ::wxGetApp().getStarNet3(starNetBand3, starNetCallsign3, starNetLogoff3, starNetInfo3, starNetPermanent3, starNetUserTimeout3, starNetGroupTimeout3, starNetCallsignSwitch3, starNetTXMsgSwitch3, starNetLink3); + + wxString starNetBand4, starNetCallsign4, starNetLogoff4, starNetInfo4, starNetLink4, starNetPermanent4; + unsigned int starNetUserTimeout4, starNetGroupTimeout4; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch4; + bool starNetTXMsgSwitch4; + ::wxGetApp().getStarNet4(starNetBand4, starNetCallsign4, starNetLogoff4, starNetInfo4, starNetPermanent4, starNetUserTimeout4, starNetGroupTimeout4, starNetCallsignSwitch4, starNetTXMsgSwitch4, starNetLink4); + + wxString starNetBand5, starNetCallsign5, starNetLogoff5, starNetInfo5, starNetLink5, starNetPermanent5; + unsigned int starNetUserTimeout5, starNetGroupTimeout5; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch5; + bool starNetTXMsgSwitch5; + ::wxGetApp().getStarNet5(starNetBand5, starNetCallsign5, starNetLogoff5, starNetInfo5, starNetPermanent5, starNetUserTimeout5, starNetGroupTimeout5, starNetCallsignSwitch5, starNetTXMsgSwitch5, starNetLink5); + + wxString starNetBand6, starNetCallsign6, starNetLogoff6, starNetInfo6, starNetLink6, starNetPermanent6; + unsigned int starNetUserTimeout6, starNetGroupTimeout6; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch6; + bool starNetTXMsgSwitch6; + ::wxGetApp().getStarNet6(starNetBand6, starNetCallsign6, starNetLogoff6, starNetInfo6, starNetPermanent6, starNetUserTimeout6, starNetGroupTimeout6, starNetCallsignSwitch6, starNetTXMsgSwitch6, starNetLink6); + + wxString starNetBand7, starNetCallsign7, starNetLogoff7, starNetInfo7, starNetLink7, starNetPermanent7; + unsigned int starNetUserTimeout7, starNetGroupTimeout7; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch7; + bool starNetTXMsgSwitch7; + ::wxGetApp().getStarNet7(starNetBand7, starNetCallsign7, starNetLogoff7, starNetInfo7, starNetPermanent7, starNetUserTimeout7, starNetGroupTimeout7, starNetCallsignSwitch7, starNetTXMsgSwitch7, starNetLink7); + + wxString starNetBand8, starNetCallsign8, starNetLogoff8, starNetInfo8, starNetLink8, starNetPermanent8; + unsigned int starNetUserTimeout8, starNetGroupTimeout8; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch8; + bool starNetTXMsgSwitch8; + ::wxGetApp().getStarNet8(starNetBand8, starNetCallsign8, starNetLogoff8, starNetInfo8, starNetPermanent8, starNetUserTimeout8, starNetGroupTimeout8, starNetCallsignSwitch8, starNetTXMsgSwitch8, starNetLink8); + + wxString starNetBand9, starNetCallsign9, starNetLogoff9, starNetInfo9, starNetLink9, starNetPermanent9; + unsigned int starNetUserTimeout9, starNetGroupTimeout9; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch9; + bool starNetTXMsgSwitch9; + ::wxGetApp().getStarNet9(starNetBand9, starNetCallsign9, starNetLogoff9, starNetInfo9, starNetPermanent9, starNetUserTimeout9, starNetGroupTimeout9, starNetCallsignSwitch9, starNetTXMsgSwitch9, starNetLink9); + + wxString starNetBand10, starNetCallsign10, starNetInfo10, starNetLogoff10, starNetLink10, starNetPermanent10; + unsigned int starNetUserTimeout10, starNetGroupTimeout10; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch10; + bool starNetTXMsgSwitch10; + ::wxGetApp().getStarNet10(starNetBand10, starNetCallsign10, starNetLogoff10, starNetInfo10, starNetPermanent10, starNetUserTimeout10, starNetGroupTimeout10, starNetCallsignSwitch10, starNetTXMsgSwitch10, starNetLink10); + + wxString starNetBand11, starNetCallsign11, starNetLogoff11, starNetInfo11, starNetLink11, starNetPermanent11; + unsigned int starNetUserTimeout11, starNetGroupTimeout11; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch11; + bool starNetTXMsgSwitch11; + ::wxGetApp().getStarNet11(starNetBand11, starNetCallsign11, starNetLogoff11, starNetInfo11, starNetPermanent11, starNetUserTimeout11, starNetGroupTimeout11, starNetCallsignSwitch11, starNetTXMsgSwitch11, starNetLink11); + + wxString starNetBand12, starNetCallsign12, starNetLogoff12, starNetInfo12, starNetLink12, starNetPermanent12; + unsigned int starNetUserTimeout12, starNetGroupTimeout12; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch12; + bool starNetTXMsgSwitch12; + ::wxGetApp().getStarNet12(starNetBand12, starNetCallsign12, starNetLogoff12, starNetInfo12, starNetPermanent12, starNetUserTimeout12, starNetGroupTimeout12, starNetCallsignSwitch12, starNetTXMsgSwitch12, starNetLink12); + + wxString starNetBand13, starNetCallsign13, starNetLogoff13, starNetInfo13, starNetLink13, starNetPermanent13; + unsigned int starNetUserTimeout13, starNetGroupTimeout13; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch13; + bool starNetTXMsgSwitch13; + ::wxGetApp().getStarNet13(starNetBand13, starNetCallsign13, starNetLogoff13, starNetInfo13, starNetPermanent13, starNetUserTimeout13, starNetGroupTimeout13, starNetCallsignSwitch13, starNetTXMsgSwitch13, starNetLink13); + + wxString starNetBand14, starNetCallsign14, starNetLogoff14, starNetInfo14, starNetLink14, starNetPermanent14; + unsigned int starNetUserTimeout14, starNetGroupTimeout14; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch14; + bool starNetTXMsgSwitch14; + ::wxGetApp().getStarNet14(starNetBand14, starNetCallsign14, starNetLogoff14, starNetInfo14, starNetPermanent14, starNetUserTimeout14, starNetGroupTimeout14, starNetCallsignSwitch14, starNetTXMsgSwitch14, starNetLink14); + + wxString starNetBand15, starNetCallsign15, starNetLogoff15, starNetInfo15, starNetLink15, starNetPermanent15; + unsigned int starNetUserTimeout15, starNetGroupTimeout15; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch15; + bool starNetTXMsgSwitch15; + ::wxGetApp().getStarNet15(starNetBand15, starNetCallsign15, starNetLogoff15, starNetInfo15, starNetPermanent15, starNetUserTimeout15, starNetGroupTimeout15, starNetCallsignSwitch15, starNetTXMsgSwitch15, starNetLink15); + + CStarNetServerPreferences dialog1(this, -1, callsign, address, + hostname, username, password, logEnabled, + starNetBand1, starNetCallsign1, starNetLogoff1, starNetInfo1, starNetPermanent1, starNetUserTimeout1, starNetGroupTimeout1, starNetCallsignSwitch1, starNetTXMsgSwitch1, starNetLink1, + starNetBand2, starNetCallsign2, starNetLogoff2, starNetInfo2, starNetPermanent2, starNetUserTimeout2, starNetGroupTimeout2, starNetCallsignSwitch2, starNetTXMsgSwitch2, starNetLink2, + starNetBand3, starNetCallsign3, starNetLogoff3, starNetInfo3, starNetPermanent3, starNetUserTimeout3, starNetGroupTimeout3, starNetCallsignSwitch3, starNetTXMsgSwitch3, starNetLink3, + starNetBand4, starNetCallsign4, starNetLogoff4, starNetInfo4, starNetPermanent4, starNetUserTimeout4, starNetGroupTimeout4, starNetCallsignSwitch4, starNetTXMsgSwitch4, starNetLink4, + starNetBand5, starNetCallsign5, starNetLogoff5, starNetInfo5, starNetPermanent5, starNetUserTimeout5, starNetGroupTimeout5, starNetCallsignSwitch5, starNetTXMsgSwitch5, starNetLink5, + starNetBand6, starNetCallsign6, starNetLogoff6, starNetInfo6, starNetPermanent6, starNetUserTimeout6, starNetGroupTimeout6, starNetCallsignSwitch6, starNetTXMsgSwitch6, starNetLink6, + starNetBand7, starNetCallsign7, starNetLogoff7, starNetInfo7, starNetPermanent7, starNetUserTimeout7, starNetGroupTimeout7, starNetCallsignSwitch7, starNetTXMsgSwitch7, starNetLink7, + starNetBand8, starNetCallsign8, starNetLogoff8, starNetInfo8, starNetPermanent8, starNetUserTimeout8, starNetGroupTimeout8, starNetCallsignSwitch8, starNetTXMsgSwitch8, starNetLink8, + starNetBand9, starNetCallsign9, starNetLogoff9, starNetInfo9, starNetPermanent9, starNetUserTimeout9, starNetGroupTimeout9, starNetCallsignSwitch9, starNetTXMsgSwitch9, starNetLink9, + starNetBand10, starNetCallsign10, starNetLogoff10, starNetInfo10, starNetPermanent10, starNetUserTimeout10, starNetGroupTimeout10, starNetCallsignSwitch10, starNetTXMsgSwitch10, starNetLink10, + starNetBand11, starNetCallsign11, starNetLogoff11, starNetInfo11, starNetPermanent11, starNetUserTimeout11, starNetGroupTimeout11, starNetCallsignSwitch11, starNetTXMsgSwitch11, starNetLink11, + starNetBand12, starNetCallsign12, starNetLogoff12, starNetInfo12, starNetPermanent12, starNetUserTimeout12, starNetGroupTimeout12, starNetCallsignSwitch12, starNetTXMsgSwitch12, starNetLink12, + starNetBand13, starNetCallsign13, starNetLogoff13, starNetInfo13, starNetPermanent13, starNetUserTimeout13, starNetGroupTimeout13, starNetCallsignSwitch13, starNetTXMsgSwitch13, starNetLink13, + starNetBand14, starNetCallsign14, starNetLogoff14, starNetInfo14, starNetPermanent14, starNetUserTimeout14, starNetGroupTimeout14, starNetCallsignSwitch14, starNetTXMsgSwitch14, starNetLink14, + starNetBand15, starNetCallsign15, starNetLogoff15, starNetInfo15, starNetPermanent15, starNetUserTimeout15, starNetGroupTimeout15, starNetCallsignSwitch15, starNetTXMsgSwitch15, starNetLink15, + remoteEnabled, remotePassword, remotePort); +#else + wxString starNetBand1, starNetCallsign1, starNetLogoff1, starNetInfo1, starNetPermanent1; + unsigned int starNetUserTimeout1, starNetGroupTimeout1; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch1; + bool starNetTXMsgSwitch1; + ::wxGetApp().getStarNet1(starNetBand1, starNetCallsign1, starNetLogoff1, starNetInfo1, starNetPermanent1, starNetUserTimeout1, starNetGroupTimeout1, starNetCallsignSwitch1, starNetTXMsgSwitch1); + + wxString starNetBand2, starNetCallsign2, starNetLogoff2, starNetInfo2, starNetPermanent2; + unsigned int starNetUserTimeout2, starNetGroupTimeout2; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch2; + bool starNetTXMsgSwitch2; + ::wxGetApp().getStarNet2(starNetBand2, starNetCallsign2, starNetLogoff2, starNetInfo2, starNetPermanent2, starNetUserTimeout2, starNetGroupTimeout2, starNetCallsignSwitch2, starNetTXMsgSwitch2); + + wxString starNetBand3, starNetCallsign3, starNetLogoff3, starNetInfo3, starNetPermanent3; + unsigned int starNetUserTimeout3, starNetGroupTimeout3; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch3; + bool starNetTXMsgSwitch3; + ::wxGetApp().getStarNet3(starNetBand3, starNetCallsign3, starNetLogoff3, starNetInfo3, starNetPermanent3, starNetUserTimeout3, starNetGroupTimeout3, starNetCallsignSwitch3, starNetTXMsgSwitch3); + + wxString starNetBand4, starNetCallsign4, starNetLogoff4, starNetInfo4, starNetPermanent4; + unsigned int starNetUserTimeout4, starNetGroupTimeout4; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch4; + bool starNetTXMsgSwitch4; + ::wxGetApp().getStarNet4(starNetBand4, starNetCallsign4, starNetLogoff4, starNetInfo4, starNetPermanent4, starNetUserTimeout4, starNetGroupTimeout4, starNetCallsignSwitch4, starNetTXMsgSwitch4); + + wxString starNetBand5, starNetCallsign5, starNetLogoff5, starNetInfo5, starNetPermanent5; + unsigned int starNetUserTimeout5, starNetGroupTimeout5; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch5; + bool starNetTXMsgSwitch5; + ::wxGetApp().getStarNet5(starNetBand5, starNetCallsign5, starNetLogoff5, starNetInfo5, starNetPermanent5, starNetUserTimeout5, starNetGroupTimeout5, starNetCallsignSwitch5, starNetTXMsgSwitch5); + + wxString starNetBand6, starNetCallsign6, starNetLogoff6, starNetInfo6, starNetPermanent6; + unsigned int starNetUserTimeout6, starNetGroupTimeout6; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch6; + bool starNetTXMsgSwitch6; + ::wxGetApp().getStarNet6(starNetBand6, starNetCallsign6, starNetLogoff6, starNetInfo6, starNetPermanent6, starNetUserTimeout6, starNetGroupTimeout6, starNetCallsignSwitch6, starNetTXMsgSwitch6); + + wxString starNetBand7, starNetCallsign7, starNetLogoff7, starNetInfo7, starNetPermanent7; + unsigned int starNetUserTimeout7, starNetGroupTimeout7; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch7; + bool starNetTXMsgSwitch7; + ::wxGetApp().getStarNet7(starNetBand7, starNetCallsign7, starNetLogoff7, starNetInfo7, starNetPermanent7, starNetUserTimeout7, starNetGroupTimeout7, starNetCallsignSwitch7, starNetTXMsgSwitch7); + + wxString starNetBand8, starNetCallsign8, starNetLogoff8, starNetInfo8, starNetPermanent8; + unsigned int starNetUserTimeout8, starNetGroupTimeout8; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch8; + bool starNetTXMsgSwitch8; + ::wxGetApp().getStarNet8(starNetBand8, starNetCallsign8, starNetLogoff8, starNetInfo8, starNetPermanent8, starNetUserTimeout8, starNetGroupTimeout8, starNetCallsignSwitch8, starNetTXMsgSwitch8); + + wxString starNetBand9, starNetCallsign9, starNetLogoff9, starNetInfo9, starNetPermanent9; + unsigned int starNetUserTimeout9, starNetGroupTimeout9; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch9; + bool starNetTXMsgSwitch9; + ::wxGetApp().getStarNet9(starNetBand9, starNetCallsign9, starNetLogoff9, starNetInfo9, starNetPermanent9, starNetUserTimeout9, starNetGroupTimeout9, starNetCallsignSwitch9, starNetTXMsgSwitch9); + + wxString starNetBand10, starNetCallsign10, starNetLogoff10, starNetInfo10, starNetPermanent10; + unsigned int starNetUserTimeout10, starNetGroupTimeout10; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch10; + bool starNetTXMsgSwitch10; + ::wxGetApp().getStarNet10(starNetBand10, starNetCallsign10, starNetLogoff10, starNetInfo10, starNetPermanent10, starNetUserTimeout10, starNetGroupTimeout10, starNetCallsignSwitch10, starNetTXMsgSwitch10); + + wxString starNetBand11, starNetCallsign11, starNetLogoff11, starNetInfo11, starNetPermanent11; + unsigned int starNetUserTimeout11, starNetGroupTimeout11; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch11; + bool starNetTXMsgSwitch11; + ::wxGetApp().getStarNet11(starNetBand11, starNetCallsign11, starNetLogoff11, starNetInfo11, starNetPermanent11, starNetUserTimeout11, starNetGroupTimeout11, starNetCallsignSwitch11, starNetTXMsgSwitch11); + + wxString starNetBand12, starNetCallsign12, starNetLogoff12, starNetInfo12, starNetPermanent12; + unsigned int starNetUserTimeout12, starNetGroupTimeout12; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch12; + bool starNetTXMsgSwitch12; + ::wxGetApp().getStarNet12(starNetBand12, starNetCallsign12, starNetLogoff12, starNetInfo12, starNetPermanent12, starNetUserTimeout12, starNetGroupTimeout12, starNetCallsignSwitch12, starNetTXMsgSwitch12); + + wxString starNetBand13, starNetCallsign13, starNetLogoff13, starNetInfo13, starNetPermanent13; + unsigned int starNetUserTimeout13, starNetGroupTimeout13; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch13; + bool starNetTXMsgSwitch13; + ::wxGetApp().getStarNet13(starNetBand13, starNetCallsign13, starNetLogoff13, starNetInfo13, starNetPermanent13, starNetUserTimeout13, starNetGroupTimeout13, starNetCallsignSwitch13, starNetTXMsgSwitch13); + + wxString starNetBand14, starNetCallsign14, starNetLogoff14, starNetInfo14, starNetPermanent14; + unsigned int starNetUserTimeout14, starNetGroupTimeout14; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch14; + bool starNetTXMsgSwitch14; + ::wxGetApp().getStarNet14(starNetBand14, starNetCallsign14, starNetLogoff14, starNetInfo14, starNetPermanent14, starNetUserTimeout14, starNetGroupTimeout14, starNetCallsignSwitch14, starNetTXMsgSwitch14); + + wxString starNetBand15, starNetCallsign15, starNetLogoff15, starNetInfo15, starNetPermanent15; + unsigned int starNetUserTimeout15, starNetGroupTimeout15; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch15; + bool starNetTXMsgSwitch15; + ::wxGetApp().getStarNet15(starNetBand15, starNetCallsign15, starNetLogoff15, starNetInfo15, starNetPermanent15, starNetUserTimeout15, starNetGroupTimeout15, starNetCallsignSwitch15, starNetTXMsgSwitch15); + + CStarNetServerPreferences dialog1(this, -1, callsign, address, + hostname, username, password, logEnabled, + starNetBand1, starNetCallsign1, starNetLogoff1, starNetInfo1, starNetPermanent1, starNetUserTimeout1, starNetGroupTimeout1, starNetCallsignSwitch1, starNetTXMsgSwitch1, + starNetBand2, starNetCallsign2, starNetLogoff2, starNetInfo2, starNetPermanent2, starNetUserTimeout2, starNetGroupTimeout2, starNetCallsignSwitch2, starNetTXMsgSwitch2, + starNetBand3, starNetCallsign3, starNetLogoff3, starNetInfo3, starNetPermanent3, starNetUserTimeout3, starNetGroupTimeout3, starNetCallsignSwitch3, starNetTXMsgSwitch3, + starNetBand4, starNetCallsign4, starNetLogoff4, starNetInfo4, starNetPermanent4, starNetUserTimeout4, starNetGroupTimeout4, starNetCallsignSwitch4, starNetTXMsgSwitch4, + starNetBand5, starNetCallsign5, starNetLogoff5, starNetInfo5, starNetPermanent5, starNetUserTimeout5, starNetGroupTimeout5, starNetCallsignSwitch5, starNetTXMsgSwitch5, + starNetBand6, starNetCallsign6, starNetLogoff6, starNetInfo6, starNetPermanent6, starNetUserTimeout6, starNetGroupTimeout6, starNetCallsignSwitch6, starNetTXMsgSwitch6, + starNetBand7, starNetCallsign7, starNetLogoff7, starNetInfo7, starNetPermanent7, starNetUserTimeout7, starNetGroupTimeout7, starNetCallsignSwitch7, starNetTXMsgSwitch7, + starNetBand8, starNetCallsign8, starNetLogoff8, starNetInfo8, starNetPermanent8, starNetUserTimeout8, starNetGroupTimeout8, starNetCallsignSwitch8, starNetTXMsgSwitch8, + starNetBand9, starNetCallsign9, starNetLogoff9, starNetInfo9, starNetPermanent9, starNetUserTimeout9, starNetGroupTimeout9, starNetCallsignSwitch9, starNetTXMsgSwitch9, + starNetBand10, starNetCallsign10, starNetLogoff10, starNetInfo10, starNetPermanent10, starNetUserTimeout10, starNetGroupTimeout10, starNetCallsignSwitch10, starNetTXMsgSwitch10, + starNetBand11, starNetCallsign11, starNetLogoff11, starNetInfo11, starNetPermanent11, starNetUserTimeout11, starNetGroupTimeout11, starNetCallsignSwitch11, starNetTXMsgSwitch11, + starNetBand12, starNetCallsign12, starNetLogoff12, starNetInfo12, starNetPermanent12, starNetUserTimeout12, starNetGroupTimeout12, starNetCallsignSwitch12, starNetTXMsgSwitch12, + starNetBand13, starNetCallsign13, starNetLogoff13, starNetInfo13, starNetPermanent13, starNetUserTimeout13, starNetGroupTimeout13, starNetCallsignSwitch13, starNetTXMsgSwitch13, + starNetBand14, starNetCallsign14, starNetLogoff14, starNetInfo14, starNetPermanent14, starNetUserTimeout14, starNetGroupTimeout14, starNetCallsignSwitch14, starNetTXMsgSwitch14, + starNetBand15, starNetCallsign15, starNetLogoff15, starNetInfo15, starNetPermanent15, starNetUserTimeout15, starNetGroupTimeout15, starNetCallsignSwitch15, starNetTXMsgSwitch15, + remoteEnabled, remotePassword, remotePort); +#endif + if (dialog1.ShowModal() != wxID_OK) + return; + + callsign = dialog1.getCallsign(); + address = dialog1.getAddress(); + + hostname = dialog1.getHostname(); + username = dialog1.getUsername(); + password = dialog1.getPassword(); + + starNetBand1 = dialog1.getStarNetBand1(); + starNetCallsign1 = dialog1.getStarNetCallsign1(); + starNetLogoff1 = dialog1.getStarNetLogoff1(); + starNetInfo1 = dialog1.getStarNetInfo1(); + starNetPermanent1 = dialog1.getStarNetPermanent1(); + starNetUserTimeout1 = dialog1.getStarNetUserTimeout1(); + starNetGroupTimeout1 = dialog1.getStarNetGroupTimeout1(); + starNetCallsignSwitch1 = dialog1.getStarNetCallsignSwitch1(); + starNetTXMsgSwitch1 = dialog1.getStarNetTXMsgSwitch1(); +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + starNetLink1 = dialog1.getStarNetLink1(); +#endif + + starNetBand2 = dialog1.getStarNetBand2(); + starNetCallsign2 = dialog1.getStarNetCallsign2(); + starNetLogoff2 = dialog1.getStarNetLogoff2(); + starNetInfo2 = dialog1.getStarNetInfo2(); + starNetPermanent2 = dialog1.getStarNetPermanent2(); + starNetUserTimeout2 = dialog1.getStarNetUserTimeout2(); + starNetGroupTimeout2 = dialog1.getStarNetGroupTimeout2(); + starNetCallsignSwitch2 = dialog1.getStarNetCallsignSwitch2(); + starNetTXMsgSwitch2 = dialog1.getStarNetTXMsgSwitch2(); +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + starNetLink2 = dialog1.getStarNetLink2(); +#endif + + starNetBand3 = dialog1.getStarNetBand3(); + starNetCallsign3 = dialog1.getStarNetCallsign3(); + starNetLogoff3 = dialog1.getStarNetLogoff3(); + starNetInfo3 = dialog1.getStarNetInfo3(); + starNetPermanent3 = dialog1.getStarNetPermanent3(); + starNetUserTimeout3 = dialog1.getStarNetUserTimeout3(); + starNetGroupTimeout3 = dialog1.getStarNetGroupTimeout3(); + starNetCallsignSwitch3 = dialog1.getStarNetCallsignSwitch3(); + starNetTXMsgSwitch3 = dialog1.getStarNetTXMsgSwitch3(); +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + starNetLink3 = dialog1.getStarNetLink3(); +#endif + + starNetBand4 = dialog1.getStarNetBand4(); + starNetCallsign4 = dialog1.getStarNetCallsign4(); + starNetLogoff4 = dialog1.getStarNetLogoff4(); + starNetInfo4 = dialog1.getStarNetInfo4(); + starNetPermanent4 = dialog1.getStarNetPermanent4(); + starNetUserTimeout4 = dialog1.getStarNetUserTimeout4(); + starNetGroupTimeout4 = dialog1.getStarNetGroupTimeout4(); + starNetCallsignSwitch4 = dialog1.getStarNetCallsignSwitch4(); + starNetTXMsgSwitch4 = dialog1.getStarNetTXMsgSwitch4(); +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + starNetLink4 = dialog1.getStarNetLink4(); +#endif + + starNetBand5 = dialog1.getStarNetBand5(); + starNetCallsign5 = dialog1.getStarNetCallsign5(); + starNetLogoff5 = dialog1.getStarNetLogoff5(); + starNetInfo5 = dialog1.getStarNetInfo5(); + starNetPermanent5 = dialog1.getStarNetPermanent5(); + starNetUserTimeout5 = dialog1.getStarNetUserTimeout5(); + starNetGroupTimeout5 = dialog1.getStarNetGroupTimeout5(); + starNetCallsignSwitch5 = dialog1.getStarNetCallsignSwitch5(); + starNetTXMsgSwitch5 = dialog1.getStarNetTXMsgSwitch5(); +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + starNetLink5 = dialog1.getStarNetLink5(); +#endif + + starNetBand6 = dialog1.getStarNetBand6(); + starNetCallsign6 = dialog1.getStarNetCallsign6(); + starNetLogoff6 = dialog1.getStarNetLogoff6(); + starNetInfo6 = dialog1.getStarNetInfo6(); + starNetPermanent6 = dialog1.getStarNetPermanent6(); + starNetUserTimeout6 = dialog1.getStarNetUserTimeout6(); + starNetGroupTimeout6 = dialog1.getStarNetGroupTimeout6(); + starNetCallsignSwitch6 = dialog1.getStarNetCallsignSwitch6(); + starNetTXMsgSwitch6 = dialog1.getStarNetTXMsgSwitch6(); +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + starNetLink6 = dialog1.getStarNetLink6(); +#endif + + starNetBand7 = dialog1.getStarNetBand7(); + starNetCallsign7 = dialog1.getStarNetCallsign7(); + starNetLogoff7 = dialog1.getStarNetLogoff7(); + starNetInfo7 = dialog1.getStarNetInfo7(); + starNetPermanent7 = dialog1.getStarNetPermanent7(); + starNetUserTimeout7 = dialog1.getStarNetUserTimeout7(); + starNetGroupTimeout7 = dialog1.getStarNetGroupTimeout7(); + starNetCallsignSwitch7 = dialog1.getStarNetCallsignSwitch7(); + starNetTXMsgSwitch7 = dialog1.getStarNetTXMsgSwitch7(); +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + starNetLink7 = dialog1.getStarNetLink7(); +#endif + + starNetBand8 = dialog1.getStarNetBand8(); + starNetCallsign8 = dialog1.getStarNetCallsign8(); + starNetLogoff8 = dialog1.getStarNetLogoff8(); + starNetInfo8 = dialog1.getStarNetInfo8(); + starNetPermanent8 = dialog1.getStarNetPermanent8(); + starNetUserTimeout8 = dialog1.getStarNetUserTimeout8(); + starNetGroupTimeout8 = dialog1.getStarNetGroupTimeout8(); + starNetCallsignSwitch8 = dialog1.getStarNetCallsignSwitch8(); + starNetTXMsgSwitch8 = dialog1.getStarNetTXMsgSwitch8(); +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + starNetLink8 = dialog1.getStarNetLink8(); +#endif + + starNetBand9 = dialog1.getStarNetBand9(); + starNetCallsign9 = dialog1.getStarNetCallsign9(); + starNetLogoff9 = dialog1.getStarNetLogoff9(); + starNetInfo9 = dialog1.getStarNetInfo9(); + starNetPermanent9 = dialog1.getStarNetPermanent9(); + starNetUserTimeout9 = dialog1.getStarNetUserTimeout9(); + starNetGroupTimeout9 = dialog1.getStarNetGroupTimeout9(); + starNetCallsignSwitch9 = dialog1.getStarNetCallsignSwitch9(); + starNetTXMsgSwitch9 = dialog1.getStarNetTXMsgSwitch9(); +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + starNetLink9 = dialog1.getStarNetLink9(); +#endif + + starNetBand10 = dialog1.getStarNetBand10(); + starNetCallsign10 = dialog1.getStarNetCallsign10(); + starNetLogoff10 = dialog1.getStarNetLogoff10(); + starNetInfo10 = dialog1.getStarNetInfo10(); + starNetPermanent10 = dialog1.getStarNetPermanent10(); + starNetUserTimeout10 = dialog1.getStarNetUserTimeout10(); + starNetGroupTimeout10 = dialog1.getStarNetGroupTimeout10(); + starNetCallsignSwitch10 = dialog1.getStarNetCallsignSwitch10(); + starNetTXMsgSwitch10 = dialog1.getStarNetTXMsgSwitch10(); +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + starNetLink10 = dialog1.getStarNetLink10(); +#endif + + starNetBand11 = dialog1.getStarNetBand11(); + starNetCallsign11 = dialog1.getStarNetCallsign11(); + starNetLogoff11 = dialog1.getStarNetLogoff11(); + starNetInfo11 = dialog1.getStarNetInfo11(); + starNetPermanent11 = dialog1.getStarNetPermanent11(); + starNetUserTimeout11 = dialog1.getStarNetUserTimeout11(); + starNetGroupTimeout11 = dialog1.getStarNetGroupTimeout11(); + starNetCallsignSwitch11 = dialog1.getStarNetCallsignSwitch11(); + starNetTXMsgSwitch11 = dialog1.getStarNetTXMsgSwitch11(); +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + starNetLink11 = dialog1.getStarNetLink11(); +#endif + + starNetBand12 = dialog1.getStarNetBand12(); + starNetCallsign12 = dialog1.getStarNetCallsign12(); + starNetLogoff12 = dialog1.getStarNetLogoff12(); + starNetInfo12 = dialog1.getStarNetInfo12(); + starNetPermanent12 = dialog1.getStarNetPermanent12(); + starNetUserTimeout12 = dialog1.getStarNetUserTimeout12(); + starNetGroupTimeout12 = dialog1.getStarNetGroupTimeout12(); + starNetCallsignSwitch12 = dialog1.getStarNetCallsignSwitch12(); + starNetTXMsgSwitch12 = dialog1.getStarNetTXMsgSwitch12(); +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + starNetLink12 = dialog1.getStarNetLink12(); +#endif + + starNetBand13 = dialog1.getStarNetBand13(); + starNetCallsign13 = dialog1.getStarNetCallsign13(); + starNetLogoff13 = dialog1.getStarNetLogoff13(); + starNetInfo13 = dialog1.getStarNetInfo13(); + starNetPermanent13 = dialog1.getStarNetPermanent13(); + starNetUserTimeout13 = dialog1.getStarNetUserTimeout13(); + starNetGroupTimeout13 = dialog1.getStarNetGroupTimeout13(); + starNetCallsignSwitch13 = dialog1.getStarNetCallsignSwitch13(); + starNetTXMsgSwitch13 = dialog1.getStarNetTXMsgSwitch13(); +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + starNetLink13 = dialog1.getStarNetLink13(); +#endif + + starNetBand14 = dialog1.getStarNetBand14(); + starNetCallsign14 = dialog1.getStarNetCallsign14(); + starNetLogoff14 = dialog1.getStarNetLogoff14(); + starNetInfo14 = dialog1.getStarNetInfo14(); + starNetPermanent14 = dialog1.getStarNetPermanent14(); + starNetUserTimeout14 = dialog1.getStarNetUserTimeout14(); + starNetGroupTimeout14 = dialog1.getStarNetGroupTimeout14(); + starNetCallsignSwitch14 = dialog1.getStarNetCallsignSwitch14(); + starNetTXMsgSwitch14 = dialog1.getStarNetTXMsgSwitch14(); +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + starNetLink14 = dialog1.getStarNetLink14(); +#endif + + starNetBand15 = dialog1.getStarNetBand15(); + starNetCallsign15 = dialog1.getStarNetCallsign15(); + starNetLogoff15 = dialog1.getStarNetLogoff15(); + starNetInfo15 = dialog1.getStarNetInfo15(); + starNetPermanent15 = dialog1.getStarNetPermanent15(); + starNetUserTimeout15 = dialog1.getStarNetUserTimeout15(); + starNetGroupTimeout15 = dialog1.getStarNetGroupTimeout15(); + starNetCallsignSwitch15 = dialog1.getStarNetCallsignSwitch15(); + starNetTXMsgSwitch15 = dialog1.getStarNetTXMsgSwitch15(); +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + starNetLink15 = dialog1.getStarNetLink15(); +#endif + + remoteEnabled = dialog1.getRemoteEnabled(); + remotePassword = dialog1.getRemotePassword(); + remotePort = dialog1.getRemotePort(); + + logEnabled = dialog1.getLogEnabled(); + + ::wxGetApp().setGateway(callsign, address); + ::wxGetApp().setIrcDDB(hostname, username, password); +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ::wxGetApp().setStarNet1(starNetBand1, starNetCallsign1, starNetLogoff1, starNetInfo1, starNetPermanent1, starNetUserTimeout1, starNetGroupTimeout1, starNetCallsignSwitch1, starNetTXMsgSwitch1, starNetLink1); + ::wxGetApp().setStarNet2(starNetBand2, starNetCallsign2, starNetLogoff2, starNetInfo2, starNetPermanent2, starNetUserTimeout2, starNetGroupTimeout2, starNetCallsignSwitch2, starNetTXMsgSwitch2, starNetLink2); + ::wxGetApp().setStarNet3(starNetBand3, starNetCallsign3, starNetLogoff3, starNetInfo3, starNetPermanent3, starNetUserTimeout3, starNetGroupTimeout3, starNetCallsignSwitch3, starNetTXMsgSwitch3, starNetLink3); + ::wxGetApp().setStarNet4(starNetBand4, starNetCallsign4, starNetLogoff4, starNetInfo4, starNetPermanent4, starNetUserTimeout4, starNetGroupTimeout4, starNetCallsignSwitch4, starNetTXMsgSwitch4, starNetLink4); + ::wxGetApp().setStarNet5(starNetBand5, starNetCallsign5, starNetLogoff5, starNetInfo5, starNetPermanent5, starNetUserTimeout5, starNetGroupTimeout5, starNetCallsignSwitch5, starNetTXMsgSwitch5, starNetLink5); + ::wxGetApp().setStarNet6(starNetBand6, starNetCallsign6, starNetLogoff6, starNetInfo6, starNetPermanent6, starNetUserTimeout6, starNetGroupTimeout6, starNetCallsignSwitch6, starNetTXMsgSwitch6, starNetLink6); + ::wxGetApp().setStarNet7(starNetBand7, starNetCallsign7, starNetLogoff7, starNetInfo7, starNetPermanent7, starNetUserTimeout7, starNetGroupTimeout7, starNetCallsignSwitch7, starNetTXMsgSwitch7, starNetLink7); + ::wxGetApp().setStarNet8(starNetBand8, starNetCallsign8, starNetLogoff8, starNetInfo8, starNetPermanent8, starNetUserTimeout8, starNetGroupTimeout8, starNetCallsignSwitch8, starNetTXMsgSwitch8, starNetLink8); + ::wxGetApp().setStarNet9(starNetBand9, starNetCallsign9, starNetLogoff9, starNetInfo9, starNetPermanent9, starNetUserTimeout9, starNetGroupTimeout9, starNetCallsignSwitch9, starNetTXMsgSwitch9, starNetLink9); + ::wxGetApp().setStarNet10(starNetBand10, starNetCallsign10, starNetLogoff10, starNetInfo10, starNetPermanent10, starNetUserTimeout10, starNetGroupTimeout10, starNetCallsignSwitch10, starNetTXMsgSwitch10, starNetLink10); + ::wxGetApp().setStarNet11(starNetBand11, starNetCallsign11, starNetLogoff11, starNetInfo11, starNetPermanent11, starNetUserTimeout11, starNetGroupTimeout11, starNetCallsignSwitch11, starNetTXMsgSwitch11, starNetLink11); + ::wxGetApp().setStarNet12(starNetBand12, starNetCallsign12, starNetLogoff12, starNetInfo12, starNetPermanent12, starNetUserTimeout12, starNetGroupTimeout12, starNetCallsignSwitch12, starNetTXMsgSwitch12, starNetLink12); + ::wxGetApp().setStarNet13(starNetBand13, starNetCallsign13, starNetLogoff13, starNetInfo13, starNetPermanent13, starNetUserTimeout13, starNetGroupTimeout13, starNetCallsignSwitch13, starNetTXMsgSwitch13, starNetLink13); + ::wxGetApp().setStarNet14(starNetBand14, starNetCallsign14, starNetLogoff14, starNetInfo14, starNetPermanent14, starNetUserTimeout14, starNetGroupTimeout14, starNetCallsignSwitch14, starNetTXMsgSwitch14, starNetLink14); + ::wxGetApp().setStarNet15(starNetBand15, starNetCallsign15, starNetLogoff15, starNetInfo15, starNetPermanent15, starNetUserTimeout15, starNetGroupTimeout15, starNetCallsignSwitch15, starNetTXMsgSwitch15, starNetLink15); +#else + ::wxGetApp().setStarNet1(starNetBand1, starNetCallsign1, starNetLogoff1, starNetInfo1, starNetPermanent1, starNetUserTimeout1, starNetGroupTimeout1, starNetCallsignSwitch1, starNetTXMsgSwitch1); + ::wxGetApp().setStarNet2(starNetBand2, starNetCallsign2, starNetLogoff2, starNetInfo2, starNetPermanent2, starNetUserTimeout2, starNetGroupTimeout2, starNetCallsignSwitch2, starNetTXMsgSwitch2); + ::wxGetApp().setStarNet3(starNetBand3, starNetCallsign3, starNetLogoff3, starNetInfo3, starNetPermanent3, starNetUserTimeout3, starNetGroupTimeout3, starNetCallsignSwitch3, starNetTXMsgSwitch3); + ::wxGetApp().setStarNet4(starNetBand4, starNetCallsign4, starNetLogoff4, starNetInfo4, starNetPermanent4, starNetUserTimeout4, starNetGroupTimeout4, starNetCallsignSwitch4, starNetTXMsgSwitch4); + ::wxGetApp().setStarNet5(starNetBand5, starNetCallsign5, starNetLogoff5, starNetInfo5, starNetPermanent5, starNetUserTimeout5, starNetGroupTimeout5, starNetCallsignSwitch5, starNetTXMsgSwitch5); + ::wxGetApp().setStarNet6(starNetBand6, starNetCallsign6, starNetLogoff6, starNetInfo6, starNetPermanent6, starNetUserTimeout6, starNetGroupTimeout6, starNetCallsignSwitch6, starNetTXMsgSwitch6); + ::wxGetApp().setStarNet7(starNetBand7, starNetCallsign7, starNetLogoff7, starNetInfo7, starNetPermanent7, starNetUserTimeout7, starNetGroupTimeout7, starNetCallsignSwitch7, starNetTXMsgSwitch7); + ::wxGetApp().setStarNet8(starNetBand8, starNetCallsign8, starNetLogoff8, starNetInfo8, starNetPermanent8, starNetUserTimeout8, starNetGroupTimeout8, starNetCallsignSwitch8, starNetTXMsgSwitch8); + ::wxGetApp().setStarNet9(starNetBand9, starNetCallsign9, starNetLogoff9, starNetInfo9, starNetPermanent9, starNetUserTimeout9, starNetGroupTimeout9, starNetCallsignSwitch9, starNetTXMsgSwitch9); + ::wxGetApp().setStarNet10(starNetBand10, starNetCallsign10, starNetLogoff10, starNetInfo10, starNetPermanent10, starNetUserTimeout10, starNetGroupTimeout10, starNetCallsignSwitch10, starNetTXMsgSwitch10); + ::wxGetApp().setStarNet11(starNetBand11, starNetCallsign11, starNetLogoff11, starNetInfo11, starNetPermanent11, starNetUserTimeout11, starNetGroupTimeout11, starNetCallsignSwitch11, starNetTXMsgSwitch11); + ::wxGetApp().setStarNet12(starNetBand12, starNetCallsign12, starNetLogoff12, starNetInfo12, starNetPermanent12, starNetUserTimeout12, starNetGroupTimeout12, starNetCallsignSwitch12, starNetTXMsgSwitch12); + ::wxGetApp().setStarNet13(starNetBand13, starNetCallsign13, starNetLogoff13, starNetInfo13, starNetPermanent13, starNetUserTimeout13, starNetGroupTimeout13, starNetCallsignSwitch13, starNetTXMsgSwitch13); + ::wxGetApp().setStarNet14(starNetBand14, starNetCallsign14, starNetLogoff14, starNetInfo14, starNetPermanent14, starNetUserTimeout14, starNetGroupTimeout14, starNetCallsignSwitch14, starNetTXMsgSwitch14); + ::wxGetApp().setStarNet15(starNetBand15, starNetCallsign15, starNetLogoff15, starNetInfo15, starNetPermanent15, starNetUserTimeout15, starNetGroupTimeout15, starNetCallsignSwitch15, starNetTXMsgSwitch15); +#endif + ::wxGetApp().setRemote(remoteEnabled, remotePassword, remotePort); + ::wxGetApp().setMiscellaneous(logEnabled); + ::wxGetApp().writeConfig(); + + wxMessageDialog dialog2(this, _("The changes made will not take effect\nuntil the application is restarted"), _("StarNET Server Information"), wxICON_INFORMATION); + dialog2.ShowModal(); +} + +void CStarNetServerFrame::onUpdates(wxCommandEvent& event) +{ + m_updates = event.IsChecked(); +} + +void CStarNetServerFrame::onAbout(wxCommandEvent&) +{ + wxAboutDialogInfo info; + info.AddDeveloper(wxT("Jonathan Naylor, G4KLX")); + info.AddDeveloper(wxT("Michael Dirska, DL1BFF")); + info.SetCopyright(wxT("(C) 2011-2015 using GPL v2 or later")); + info.SetName(APPLICATION_NAME); + info.SetVersion(VERSION); + info.SetDescription(_("This program allows an Internet connected computer \nto become a StarNet Digital Server.")); + + ::wxAboutBox(info); +} + +void CStarNetServerFrame::onLog(wxEvent& event) +{ + CLogEvent& logEvent = dynamic_cast(event); + + wxString text; + + text = m_logLine[1U]->GetLabel(); + m_logLine[0U]->SetLabel(text); + + text = m_logLine[2U]->GetLabel(); + m_logLine[1U]->SetLabel(text); + + text = m_logLine[3U]->GetLabel(); + m_logLine[2U]->SetLabel(text); + + text = m_logLine[4U]->GetLabel(); + m_logLine[3U]->SetLabel(text); + + text = m_logLine[5U]->GetLabel(); + m_logLine[4U]->SetLabel(text); + + text = m_logLine[6U]->GetLabel(); + m_logLine[5U]->SetLabel(text); + + text = m_logLine[7U]->GetLabel(); + m_logLine[6U]->SetLabel(text); + + text = m_logLine[8U]->GetLabel(); + m_logLine[7U]->SetLabel(text); + + text = m_logLine[9U]->GetLabel(); + m_logLine[8U]->SetLabel(text); + + text = m_logLine[10U]->GetLabel(); + m_logLine[9U]->SetLabel(text); + + text = m_logLine[11U]->GetLabel(); + m_logLine[10U]->SetLabel(text); + + text = m_logLine[12U]->GetLabel(); + m_logLine[11U]->SetLabel(text); + + text = m_logLine[13U]->GetLabel(); + m_logLine[12U]->SetLabel(text); + + text = m_logLine[14U]->GetLabel(); + m_logLine[13U]->SetLabel(text); + + text = m_logLine[15U]->GetLabel(); + m_logLine[14U]->SetLabel(text); + + text = m_logLine[16U]->GetLabel(); + m_logLine[15U]->SetLabel(text); + + text = m_logLine[17U]->GetLabel(); + m_logLine[16U]->SetLabel(text); + + text = m_logLine[18U]->GetLabel(); + m_logLine[17U]->SetLabel(text); + + text = m_logLine[19U]->GetLabel(); + m_logLine[18U]->SetLabel(text); + + text = logEvent.getText(); + m_logLine[19U]->SetLabel(text); +} + +void CStarNetServerFrame::showLog(const wxString& text) +{ + if (!m_updates) + return; + + CLogEvent event(text, LOG_EVENT); + + AddPendingEvent(event); +} diff --git a/StarNetServer/StarNetServerFrame.h b/StarNetServer/StarNetServerFrame.h new file mode 100644 index 0000000..9561190 --- /dev/null +++ b/StarNetServer/StarNetServerFrame.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2010,2011,2012 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 StarNetServerFrame_H +#define StarNetServerFrame_H + +#include "Defs.h" + +#include +#include + +class CStarNetServerFrame : public wxFrame { +public: + CStarNetServerFrame(const wxString& title, const wxPoint& position, bool gui); + virtual ~CStarNetServerFrame(); + + virtual void onQuit(wxCommandEvent& event); + virtual void onPreferences(wxCommandEvent& event); + virtual void onUpdates(wxCommandEvent& event); + virtual void onAbout(wxCommandEvent& event); + virtual void onClose(wxCloseEvent& event); + virtual void onLog(wxEvent& event); + + virtual void showLog(const wxString& text); + +private: + wxStaticText* m_logLine[20]; + bool m_updates; + + DECLARE_EVENT_TABLE() + + wxMenuBar* createMenuBar(); +}; + +#endif diff --git a/StarNetServer/StarNetServerIrcDDBSet.cpp b/StarNetServer/StarNetServerIrcDDBSet.cpp new file mode 100644 index 0000000..f4f9b25 --- /dev/null +++ b/StarNetServer/StarNetServerIrcDDBSet.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2010,2012,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 "StarNetServerIrcDDBSet.h" + +const unsigned int BORDER_SIZE = 5U; +const unsigned int CONTROL_WIDTH1 = 200U; +const unsigned int CONTROL_WIDTH2 = 80U; + +const unsigned int PORT_LENGTH = 5U; + +CStarNetServerIrcDDBSet::CStarNetServerIrcDDBSet(wxWindow* parent, int id, const wxString& title, const wxString& hostname, const wxString& username, const wxString& password) : +wxPanel(parent, id), +m_title(title), +m_hostname(NULL), +m_username(NULL), +m_password(NULL) +{ + wxFlexGridSizer* sizer = new wxFlexGridSizer(2); + + wxStaticText* hostnameLabel = new wxStaticText(this, -1, _("Hostname")); + sizer->Add(hostnameLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_hostname = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_hostname->Append(wxT("group1-irc.ircddb.net")); + m_hostname->Append(wxT("group2-irc.ircddb.net")); + m_hostname->Append(wxT("irc1.openquad.net")); + m_hostname->Append(wxT("irc2.openquad.net")); + m_hostname->Append(wxT("irc3.openquad.net")); + m_hostname->Append(wxT("irc4.openquad.net")); + m_hostname->Append(wxT("irc5.openquad.net")); + m_hostname->Append(wxT("irc6.openquad.net")); + m_hostname->Append(wxT("server1-ik2xyp.free-dstar.org")); + m_hostname->Append(wxT("ircddb.dstar.su")); + sizer->Add(m_hostname, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_hostname->SetStringSelection(hostname); + + wxStaticText* usernameLabel = new wxStaticText(this, -1, _("Username")); + sizer->Add(usernameLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_username = new wxTextCtrl(this, -1, username, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1)); + sizer->Add(m_username, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* passwordLabel = new wxStaticText(this, -1, _("Password")); + sizer->Add(passwordLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_password = new wxTextCtrl(this, -1, password, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1), wxTE_PASSWORD); + sizer->Add(m_password, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + SetAutoLayout(true); + + SetSizer(sizer); +} + + +CStarNetServerIrcDDBSet::~CStarNetServerIrcDDBSet() +{ +} + +bool CStarNetServerIrcDDBSet::Validate() +{ + bool res = getHostname().IsEmpty(); + if (res) { + wxMessageDialog dialog(this, _("The Hostname may not be empty"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + res = getUsername().IsEmpty(); + if (res) { + wxMessageDialog dialog(this, _("The Username may not be empty"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + return true; +} + +wxString CStarNetServerIrcDDBSet::getHostname() const +{ + return m_hostname->GetStringSelection(); +} + +wxString CStarNetServerIrcDDBSet::getUsername() const +{ + return m_username->GetValue(); +} + +wxString CStarNetServerIrcDDBSet::getPassword() const +{ + return m_password->GetValue(); +} diff --git a/StarNetServer/StarNetServerIrcDDBSet.h b/StarNetServer/StarNetServerIrcDDBSet.h new file mode 100644 index 0000000..8eba83e --- /dev/null +++ b/StarNetServer/StarNetServerIrcDDBSet.h @@ -0,0 +1,42 @@ +/* + * 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 + * 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 StarNetServerIrcDDBSet_H +#define StarNetServerIrcDDBSet_H + +#include + +class CStarNetServerIrcDDBSet : public wxPanel { +public: + CStarNetServerIrcDDBSet(wxWindow* parent, int id, const wxString& title, const wxString& hostname, const wxString& username, const wxString& password); + virtual ~CStarNetServerIrcDDBSet(); + + virtual bool Validate(); + + virtual wxString getHostname() const; + virtual wxString getUsername() const; + virtual wxString getPassword() const; + +private: + wxString m_title; + wxChoice* m_hostname; + wxTextCtrl* m_username; + wxTextCtrl* m_password; +}; + +#endif diff --git a/StarNetServer/StarNetServerLogRedirect.cpp b/StarNetServer/StarNetServerLogRedirect.cpp new file mode 100644 index 0000000..800c485 --- /dev/null +++ b/StarNetServer/StarNetServerLogRedirect.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2002,2003,2009-2013,2018 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 "StarNetServerLogRedirect.h" +#include "StarNetServerApp.h" + +CStarNetServerLogRedirect::CStarNetServerLogRedirect() : +wxLog() +{ +} + +CStarNetServerLogRedirect::~CStarNetServerLogRedirect() +{ +} + +void CStarNetServerLogRedirect::DoLogRecord(wxLogLevel level, const wxString& msg, const wxLogRecordInfo& info) +{ + wxString letter; + + switch (level) { + case wxLOG_FatalError: letter = wxT("F"); break; + case wxLOG_Error: letter = wxT("E"); break; + case wxLOG_Warning: letter = wxT("W"); break; + case wxLOG_Info: letter = wxT("I"); break; + case wxLOG_Message: letter = wxT("M"); break; + case wxLOG_Status: letter = wxT("M"); break; + case wxLOG_Trace: letter = wxT("T"); break; + case wxLOG_Debug: letter = wxT("D"); break; + default: letter = wxT("U"); break; + } + + struct tm* tm = ::gmtime(&info.timestamp); + + wxString message; + message.Printf(wxT("%s: %04d-%02d-%02d %02d:%02d:%02d: %s\n"), letter.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, msg.c_str()); + + ::wxGetApp().showLog(message); + + if (level == wxLOG_FatalError) + ::abort(); +} diff --git a/StarNetServer/StarNetServerLogRedirect.h b/StarNetServer/StarNetServerLogRedirect.h new file mode 100644 index 0000000..753d3b4 --- /dev/null +++ b/StarNetServer/StarNetServerLogRedirect.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2002,2003,2009-2011,2018 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 StarNetServerLogRedirect_H +#define StarNetServerLogRedirect_H + +#include +#include + +class CStarNetServerLogRedirect : public wxLog { +public: + CStarNetServerLogRedirect(); + virtual ~CStarNetServerLogRedirect(); + + virtual void DoLogRecord(wxLogLevel level, const wxString& msg, const wxLogRecordInfo& info); + +private: +}; + +#endif diff --git a/StarNetServer/StarNetServerMiscellaneousSet.cpp b/StarNetServer/StarNetServerMiscellaneousSet.cpp new file mode 100644 index 0000000..8ea7bc5 --- /dev/null +++ b/StarNetServer/StarNetServerMiscellaneousSet.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2010,2011 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 "StarNetServerMiscellaneousSet.h" +#include "DStarDefines.h" + +const unsigned int CONTROL_WIDTH = 130U; + +const unsigned int BORDER_SIZE = 5U; + +CStarNetServerMiscellaneousSet::CStarNetServerMiscellaneousSet(wxWindow* parent, int id, const wxString& title, bool logEnabled) : +wxPanel(parent, id), +m_title(title), +m_logEnabled(NULL) +{ + wxFlexGridSizer* sizer = new wxFlexGridSizer(2); + + wxStaticText* logEnabledLabel = new wxStaticText(this, -1, _("GUI Log")); + sizer->Add(logEnabledLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_logEnabled = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + m_logEnabled->Append(_("Disabled")); + m_logEnabled->Append(_("Enabled")); + sizer->Add(m_logEnabled, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_logEnabled->SetSelection(logEnabled ? 1 : 0); + + SetAutoLayout(true); + + SetSizer(sizer); +} + + +CStarNetServerMiscellaneousSet::~CStarNetServerMiscellaneousSet() +{ +} + +bool CStarNetServerMiscellaneousSet::Validate() +{ + return m_logEnabled->GetCurrentSelection() != wxNOT_FOUND; +} + +bool CStarNetServerMiscellaneousSet::getLogEnabled() const +{ + int c = m_logEnabled->GetCurrentSelection(); + if (c == wxNOT_FOUND) + return false; + + return c == 1; +} diff --git a/StarNetServer/StarNetServerMiscellaneousSet.h b/StarNetServer/StarNetServerMiscellaneousSet.h new file mode 100644 index 0000000..ea72977 --- /dev/null +++ b/StarNetServer/StarNetServerMiscellaneousSet.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010,2011 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 StarNetServerMiscellaneousSet_H +#define StarNetServerMiscellaneousSet_H + +#include "Defs.h" + +#include + +class CStarNetServerMiscellaneousSet : public wxPanel { +public: + CStarNetServerMiscellaneousSet(wxWindow* parent, int id, const wxString& title, bool logEnabled); + virtual ~CStarNetServerMiscellaneousSet(); + + virtual bool Validate(); + + virtual bool getLogEnabled() const; + +private: + wxString m_title; + wxChoice* m_logEnabled; +}; + +#endif diff --git a/StarNetServer/StarNetServerPreferences.cpp b/StarNetServer/StarNetServerPreferences.cpp new file mode 100644 index 0000000..bede7ec --- /dev/null +++ b/StarNetServer/StarNetServerPreferences.cpp @@ -0,0 +1,1091 @@ +/* + * Copyright (C) 2010,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 + * 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 "StarNetServerPreferences.h" +#include "StarNetServerDefs.h" + +const unsigned int BORDER_SIZE = 5U; + +CStarNetServerPreferences::CStarNetServerPreferences(wxWindow* parent, int id, + const wxString& callsign, const wxString& address, + const wxString& hostname, const wxString& username, + const wxString& password, + bool logEnabled, +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + const wxString& starNetBand1, const wxString& callsign1, const wxString& logoff1, const wxString& info1, const wxString& permanent1, unsigned int userTimeout1, unsigned int groupTimeout1, STARNET_CALLSIGN_SWITCH callsignSwitch1, bool txMsgSwitch1, const wxString& link1, + const wxString& starNetBand2, const wxString& callsign2, const wxString& logoff2, const wxString& info2, const wxString& permanent2, unsigned int userTimeout2, unsigned int groupTimeout2, STARNET_CALLSIGN_SWITCH callsignSwitch2, bool txMsgSwitch2, const wxString& link2, + const wxString& starNetBand3, const wxString& callsign3, const wxString& logoff3, const wxString& info3, const wxString& permanent3, unsigned int userTimeout3, unsigned int groupTimeout3, STARNET_CALLSIGN_SWITCH callsignSwitch3, bool txMsgSwitch3, const wxString& link3, + const wxString& starNetBand4, const wxString& callsign4, const wxString& logoff4, const wxString& info4, const wxString& permanent4, unsigned int userTimeout4, unsigned int groupTimeout4, STARNET_CALLSIGN_SWITCH callsignSwitch4, bool txMsgSwitch4, const wxString& link4, + const wxString& starNetBand5, const wxString& callsign5, const wxString& logoff5, const wxString& info5, const wxString& permanent5, unsigned int userTimeout5, unsigned int groupTimeout5, STARNET_CALLSIGN_SWITCH callsignSwitch5, bool txMsgSwitch5, const wxString& link5, + const wxString& starNetBand6, const wxString& callsign6, const wxString& logoff6, const wxString& info6, const wxString& permanent6, unsigned int userTimeout6, unsigned int groupTimeout6, STARNET_CALLSIGN_SWITCH callsignSwitch6, bool txMsgSwitch6, const wxString& link6, + const wxString& starNetBand7, const wxString& callsign7, const wxString& logoff7, const wxString& info7, const wxString& permanent7, unsigned int userTimeout7, unsigned int groupTimeout7, STARNET_CALLSIGN_SWITCH callsignSwitch7, bool txMsgSwitch7, const wxString& link7, + const wxString& starNetBand8, const wxString& callsign8, const wxString& logoff8, const wxString& info8, const wxString& permanent8, unsigned int userTimeout8, unsigned int groupTimeout8, STARNET_CALLSIGN_SWITCH callsignSwitch8, bool txMsgSwitch8, const wxString& link8, + const wxString& starNetBand9, const wxString& callsign9, const wxString& logoff9, const wxString& info9, const wxString& permanent9, unsigned int userTimeout9, unsigned int groupTimeout9, STARNET_CALLSIGN_SWITCH callsignSwitch9, bool txMsgSwitch9, const wxString& link9, + const wxString& starNetBand10, const wxString& callsign10, const wxString& logoff10, const wxString& info10, const wxString& permanent10, unsigned int userTimeout10, unsigned int groupTimeout10, STARNET_CALLSIGN_SWITCH callsignSwitch10, bool txMsgSwitch10, const wxString& link10, + const wxString& starNetBand11, const wxString& callsign11, const wxString& logoff11, const wxString& info11, const wxString& permanent11, unsigned int userTimeout11, unsigned int groupTimeout11, STARNET_CALLSIGN_SWITCH callsignSwitch11, bool txMsgSwitch11, const wxString& link11, + const wxString& starNetBand12, const wxString& callsign12, const wxString& logoff12, const wxString& info12, const wxString& permanent12, unsigned int userTimeout12, unsigned int groupTimeout12, STARNET_CALLSIGN_SWITCH callsignSwitch12, bool txMsgSwitch12, const wxString& link12, + const wxString& starNetBand13, const wxString& callsign13, const wxString& logoff13, const wxString& info13, const wxString& permanent13, unsigned int userTimeout13, unsigned int groupTimeout13, STARNET_CALLSIGN_SWITCH callsignSwitch13, bool txMsgSwitch13, const wxString& link13, + const wxString& starNetBand14, const wxString& callsign14, const wxString& logoff14, const wxString& info14, const wxString& permanent14, unsigned int userTimeout14, unsigned int groupTimeout14, STARNET_CALLSIGN_SWITCH callsignSwitch14, bool txMsgSwitch14, const wxString& link14, + const wxString& starNetBand15, const wxString& callsign15, const wxString& logoff15, const wxString& info15, const wxString& permanent15, unsigned int userTimeout15, unsigned int groupTimeout15, STARNET_CALLSIGN_SWITCH callsignSwitch15, bool txMsgSwitch15, const wxString& link15, +#else + const wxString& starNetBand1, const wxString& callsign1, const wxString& logoff1, const wxString& info1, const wxString& permanent1, unsigned int userTimeout1, unsigned int groupTimeout1, STARNET_CALLSIGN_SWITCH callsignSwitch1, bool txMsgSwitch1, + const wxString& starNetBand2, const wxString& callsign2, const wxString& logoff2, const wxString& info2, const wxString& permanent2, unsigned int userTimeout2, unsigned int groupTimeout2, STARNET_CALLSIGN_SWITCH callsignSwitch2, bool txMsgSwitch2, + const wxString& starNetBand3, const wxString& callsign3, const wxString& logoff3, const wxString& info3, const wxString& permanent3, unsigned int userTimeout3, unsigned int groupTimeout3, STARNET_CALLSIGN_SWITCH callsignSwitch3, bool txMsgSwitch3, + const wxString& starNetBand4, const wxString& callsign4, const wxString& logoff4, const wxString& info4, const wxString& permanent4, unsigned int userTimeout4, unsigned int groupTimeout4, STARNET_CALLSIGN_SWITCH callsignSwitch4, bool txMsgSwitch4, + const wxString& starNetBand5, const wxString& callsign5, const wxString& logoff5, const wxString& info5, const wxString& permanent5, unsigned int userTimeout5, unsigned int groupTimeout5, STARNET_CALLSIGN_SWITCH callsignSwitch5, bool txMsgSwitch5, + const wxString& starNetBand6, const wxString& callsign6, const wxString& logoff6, const wxString& info6, const wxString& permanent6, unsigned int userTimeout6, unsigned int groupTimeout6, STARNET_CALLSIGN_SWITCH callsignSwitch6, bool txMsgSwitch6, + const wxString& starNetBand7, const wxString& callsign7, const wxString& logoff7, const wxString& info7, const wxString& permanent7, unsigned int userTimeout7, unsigned int groupTimeout7, STARNET_CALLSIGN_SWITCH callsignSwitch7, bool txMsgSwitch7, + const wxString& starNetBand8, const wxString& callsign8, const wxString& logoff8, const wxString& info8, const wxString& permanent8, unsigned int userTimeout8, unsigned int groupTimeout8, STARNET_CALLSIGN_SWITCH callsignSwitch8, bool txMsgSwitch8, + const wxString& starNetBand9, const wxString& callsign9, const wxString& logoff9, const wxString& info9, const wxString& permanent9, unsigned int userTimeout9, unsigned int groupTimeout9, STARNET_CALLSIGN_SWITCH callsignSwitch9, bool txMsgSwitch9, + const wxString& starNetBand10, const wxString& callsign10, const wxString& logoff10, const wxString& info10, const wxString& permanent10, unsigned int userTimeout10, unsigned int groupTimeout10, STARNET_CALLSIGN_SWITCH callsignSwitch10, bool txMsgSwitch10, + const wxString& starNetBand11, const wxString& callsign11, const wxString& logoff11, const wxString& info11, const wxString& permanent11, unsigned int userTimeout11, unsigned int groupTimeout11, STARNET_CALLSIGN_SWITCH callsignSwitch11, bool txMsgSwitch11, + const wxString& starNetBand12, const wxString& callsign12, const wxString& logoff12, const wxString& info12, const wxString& permanent12, unsigned int userTimeout12, unsigned int groupTimeout12, STARNET_CALLSIGN_SWITCH callsignSwitch12, bool txMsgSwitch12, + const wxString& starNetBand13, const wxString& callsign13, const wxString& logoff13, const wxString& info13, const wxString& permanent13, unsigned int userTimeout13, unsigned int groupTimeout13, STARNET_CALLSIGN_SWITCH callsignSwitch13, bool txMsgSwitch13, + const wxString& starNetBand14, const wxString& callsign14, const wxString& logoff14, const wxString& info14, const wxString& permanent14, unsigned int userTimeout14, unsigned int groupTimeout14, STARNET_CALLSIGN_SWITCH callsignSwitch14, bool txMsgSwitch14, + const wxString& starNetBand15, const wxString& callsign15, const wxString& logoff15, const wxString& info15, const wxString& permanent15, unsigned int userTimeout15, unsigned int groupTimeout15, STARNET_CALLSIGN_SWITCH callsignSwitch15, bool txMsgSwitch15, +#endif + bool remoteEnabled, const wxString& remotePassword, unsigned int remotePort) : +wxDialog(parent, id, wxString(_("StarNet Server Preferences"))), +m_callsign(NULL), +m_ircDDB(NULL), +m_starNet1(NULL), +m_starNet2(NULL), +m_starNet3(NULL), +m_starNet4(NULL), +m_starNet5(NULL), +m_starNet6(NULL), +m_starNet7(NULL), +m_starNet8(NULL), +m_starNet9(NULL), +m_starNet10(NULL), +m_starNet11(NULL), +m_starNet12(NULL), +m_starNet13(NULL), +m_starNet14(NULL), +m_starNet15(NULL), +m_remote(NULL), +m_miscellaneous(NULL) +{ + wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); + + wxNotebook* noteBook = new wxNotebook(this, -1); + + m_callsign = new CStarNetServerCallsignSet(noteBook, -1, APPLICATION_NAME, callsign, address); + noteBook->AddPage(m_callsign, _("Callsign"), true); + + m_ircDDB = new CStarNetServerIrcDDBSet(noteBook, -1, APPLICATION_NAME, hostname, username, password); + noteBook->AddPage(m_ircDDB, wxT("ircDDB"), false); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_starNet1 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand1, callsign1, logoff1, info1, permanent1, userTimeout1, groupTimeout1, callsignSwitch1, txMsgSwitch1, link1); + noteBook->AddPage(m_starNet1, wxT("StarNet 1"), false); + + m_starNet2 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand2, callsign2, logoff2, info2, permanent2, userTimeout2, groupTimeout2, callsignSwitch2, txMsgSwitch2, link2); + noteBook->AddPage(m_starNet2, wxT("StarNet 2"), false); + + m_starNet3 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand3, callsign3, logoff3, info3, permanent3, userTimeout3, groupTimeout3, callsignSwitch3, txMsgSwitch3, link3); + noteBook->AddPage(m_starNet3, wxT("StarNet 3"), false); + + m_starNet4 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand4, callsign4, logoff4, info4, permanent4, userTimeout4, groupTimeout4, callsignSwitch4, txMsgSwitch4, link4); + noteBook->AddPage(m_starNet4, wxT("StarNet 4"), false); + + m_starNet5 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand5, callsign5, logoff5, info5, permanent5, userTimeout5, groupTimeout5, callsignSwitch5, txMsgSwitch5, link5); + noteBook->AddPage(m_starNet5, wxT("StarNet 5"), false); + + m_starNet6 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand6, callsign6, logoff6, info6, permanent6, userTimeout6, groupTimeout6, callsignSwitch6, txMsgSwitch6, link6); + noteBook->AddPage(m_starNet6, wxT("StarNet 6"), false); + + m_starNet7 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand7, callsign7, logoff7, info7, permanent7, userTimeout7, groupTimeout7, callsignSwitch7, txMsgSwitch7, link7); + noteBook->AddPage(m_starNet7, wxT("StarNet 7"), false); + + m_starNet8 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand8, callsign8, logoff8, info8, permanent8, userTimeout8, groupTimeout8, callsignSwitch8, txMsgSwitch8, link8); + noteBook->AddPage(m_starNet8, wxT("StarNet 8"), false); + + m_starNet9 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand9, callsign9, logoff9, info9, permanent9, userTimeout9, groupTimeout9, callsignSwitch9, txMsgSwitch9, link9); + noteBook->AddPage(m_starNet9, wxT("StarNet 9"), false); + + m_starNet10 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand10, callsign10, logoff10, info10, permanent10, userTimeout10, groupTimeout10, callsignSwitch10, txMsgSwitch10, link10); + noteBook->AddPage(m_starNet10, wxT("StarNet 10"), false); + + m_starNet11 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand11, callsign11, logoff11, info11, permanent11, userTimeout11, groupTimeout11, callsignSwitch11, txMsgSwitch11, link11); + noteBook->AddPage(m_starNet11, wxT("StarNet 11"), false); + + m_starNet12 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand12, callsign12, logoff12, info12, permanent12, userTimeout12, groupTimeout12, callsignSwitch12, txMsgSwitch12, link12); + noteBook->AddPage(m_starNet12, wxT("StarNet 12"), false); + + m_starNet13 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand13, callsign13, logoff13, info13, permanent13, userTimeout13, groupTimeout13, callsignSwitch13, txMsgSwitch13, link13); + noteBook->AddPage(m_starNet13, wxT("StarNet 13"), false); + + m_starNet14 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand14, callsign14, logoff14, info14, permanent14, userTimeout14, groupTimeout14, callsignSwitch14, txMsgSwitch14, link14); + noteBook->AddPage(m_starNet14, wxT("StarNet 14"), false); + + m_starNet15 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand15, callsign15, logoff15, info15, permanent15, userTimeout15, groupTimeout15, callsignSwitch15, txMsgSwitch15, link15); + noteBook->AddPage(m_starNet15, wxT("StarNet 15"), false); +#else + m_starNet1 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand1, callsign1, logoff1, info1, permanent1, userTimeout1, groupTimeout1, callsignSwitch1, txMsgSwitch1); + noteBook->AddPage(m_starNet1, wxT("StarNet 1"), false); + + m_starNet2 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand2, callsign2, logoff2, info2, permanent2, userTimeout2, groupTimeout2, callsignSwitch2, txMsgSwitch2); + noteBook->AddPage(m_starNet2, wxT("StarNet 2"), false); + + m_starNet3 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand3, callsign3, logoff3, info3, permanent3, userTimeout3, groupTimeout3, callsignSwitch3, txMsgSwitch3); + noteBook->AddPage(m_starNet3, wxT("StarNet 3"), false); + + m_starNet4 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand4, callsign4, logoff4, info4, permanent4, userTimeout4, groupTimeout4, callsignSwitch4, txMsgSwitch4); + noteBook->AddPage(m_starNet4, wxT("StarNet 4"), false); + + m_starNet5 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand5, callsign5, logoff5, info5, permanent5, userTimeout5, groupTimeout5, callsignSwitch5, txMsgSwitch5); + noteBook->AddPage(m_starNet5, wxT("StarNet 5"), false); + + m_starNet6 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand6, callsign6, logoff6, info6, permanent6, userTimeout6, groupTimeout6, callsignSwitch6, txMsgSwitch6); + noteBook->AddPage(m_starNet6, wxT("StarNet 6"), false); + + m_starNet7 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand7, callsign7, logoff7, info7, permanent7, userTimeout7, groupTimeout7, callsignSwitch7, txMsgSwitch7); + noteBook->AddPage(m_starNet7, wxT("StarNet 7"), false); + + m_starNet8 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand8, callsign8, logoff8, info8, permanent8, userTimeout8, groupTimeout8, callsignSwitch8, txMsgSwitch8); + noteBook->AddPage(m_starNet8, wxT("StarNet 8"), false); + + m_starNet9 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand9, callsign9, logoff9, info9, permanent9, userTimeout9, groupTimeout9, callsignSwitch9, txMsgSwitch9); + noteBook->AddPage(m_starNet9, wxT("StarNet 9"), false); + + m_starNet10 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand10, callsign10, logoff10, info10, permanent10, userTimeout10, groupTimeout10, callsignSwitch10, txMsgSwitch10); + noteBook->AddPage(m_starNet10, wxT("StarNet 10"), false); + + m_starNet11 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand11, callsign11, logoff11, info11, permanent11, userTimeout11, groupTimeout11, callsignSwitch11, txMsgSwitch11); + noteBook->AddPage(m_starNet11, wxT("StarNet 11"), false); + + m_starNet12 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand12, callsign12, logoff12, info12, permanent12, userTimeout12, groupTimeout12, callsignSwitch12, txMsgSwitch12); + noteBook->AddPage(m_starNet12, wxT("StarNet 12"), false); + + m_starNet13 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand13, callsign13, logoff13, info13, permanent13, userTimeout13, groupTimeout13, callsignSwitch13, txMsgSwitch13); + noteBook->AddPage(m_starNet13, wxT("StarNet 13"), false); + + m_starNet14 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand14, callsign14, logoff14, info14, permanent14, userTimeout14, groupTimeout14, callsignSwitch14, txMsgSwitch14); + noteBook->AddPage(m_starNet14, wxT("StarNet 14"), false); + + m_starNet15 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand15, callsign15, logoff15, info15, permanent15, userTimeout15, groupTimeout15, callsignSwitch15, txMsgSwitch15); + noteBook->AddPage(m_starNet15, wxT("StarNet 15"), false); +#endif + + m_remote = new CRemoteSet(noteBook, -1, APPLICATION_NAME, remoteEnabled, remotePassword, remotePort); + noteBook->AddPage(m_remote, wxT("Remote"), false); + + m_miscellaneous = new CStarNetServerMiscellaneousSet(noteBook, -1, APPLICATION_NAME, logEnabled); + noteBook->AddPage(m_miscellaneous, wxT("Misc"), false); + + mainSizer->Add(noteBook, 1, wxALL | wxGROW, BORDER_SIZE); + + mainSizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + SetAutoLayout(true); + Layout(); + + mainSizer->Fit(this); + mainSizer->SetSizeHints(this); + + SetSizer(mainSizer); +} + +CStarNetServerPreferences::~CStarNetServerPreferences() +{ +} + +bool CStarNetServerPreferences::Validate() +{ + if (!m_callsign->Validate()) + return false; + + if (!m_ircDDB->Validate()) + return false; + + if (!m_starNet1->Validate()) + return false; + + if (!m_starNet2->Validate()) + return false; + + if (!m_starNet3->Validate()) + return false; + + if (!m_starNet4->Validate()) + return false; + + if (!m_starNet5->Validate()) + return false; + + if (!m_starNet6->Validate()) + return false; + + if (!m_starNet7->Validate()) + return false; + + if (!m_starNet8->Validate()) + return false; + + if (!m_starNet9->Validate()) + return false; + + if (!m_starNet10->Validate()) + return false; + + if (!m_starNet11->Validate()) + return false; + + if (!m_starNet12->Validate()) + return false; + + if (!m_starNet13->Validate()) + return false; + + if (!m_starNet14->Validate()) + return false; + + if (!m_starNet15->Validate()) + return false; + + if (!m_remote->Validate()) + return false; + + return m_miscellaneous->Validate(); +} + +wxString CStarNetServerPreferences::getCallsign() const +{ + return m_callsign->getCallsign(); +} + +wxString CStarNetServerPreferences::getAddress() const +{ + return m_callsign->getAddress(); +} + +wxString CStarNetServerPreferences::getHostname() const +{ + return m_ircDDB->getHostname(); +} + +wxString CStarNetServerPreferences::getUsername() const +{ + return m_ircDDB->getUsername(); +} + +wxString CStarNetServerPreferences::getPassword() const +{ + return m_ircDDB->getPassword(); +} + +bool CStarNetServerPreferences::getLogEnabled() const +{ + return m_miscellaneous->getLogEnabled(); +} + +wxString CStarNetServerPreferences::getStarNetBand1() const +{ + return m_starNet1->getBand(); +} + +wxString CStarNetServerPreferences::getStarNetCallsign1() const +{ + return m_starNet1->getCallsign(); +} + +wxString CStarNetServerPreferences::getStarNetLogoff1() const +{ + return m_starNet1->getLogoff(); +} + +wxString CStarNetServerPreferences::getStarNetInfo1() const +{ + return m_starNet1->getInfo(); +} + +wxString CStarNetServerPreferences::getStarNetPermanent1() const +{ + return m_starNet1->getPermanent(); +} + +unsigned int CStarNetServerPreferences::getStarNetUserTimeout1() const +{ + return m_starNet1->getUserTimeout(); +} + +unsigned int CStarNetServerPreferences::getStarNetGroupTimeout1() const +{ + return m_starNet1->getGroupTimeout(); +} + +STARNET_CALLSIGN_SWITCH CStarNetServerPreferences::getStarNetCallsignSwitch1() const +{ + return m_starNet1->getCallsignSwitch(); +} + +bool CStarNetServerPreferences::getStarNetTXMsgSwitch1() const +{ + return m_starNet1->getTXMsgSwitch(); +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +wxString CStarNetServerPreferences::getStarNetLink1() const +{ + return m_starNet1->getReflector(); +} +#endif + +wxString CStarNetServerPreferences::getStarNetBand2() const +{ + return m_starNet2->getBand(); +} + +wxString CStarNetServerPreferences::getStarNetCallsign2() const +{ + return m_starNet2->getCallsign(); +} + +wxString CStarNetServerPreferences::getStarNetLogoff2() const +{ + return m_starNet2->getLogoff(); +} + +wxString CStarNetServerPreferences::getStarNetInfo2() const +{ + return m_starNet2->getInfo(); +} + +wxString CStarNetServerPreferences::getStarNetPermanent2() const +{ + return m_starNet2->getPermanent(); +} + +unsigned int CStarNetServerPreferences::getStarNetUserTimeout2() const +{ + return m_starNet2->getUserTimeout(); +} + +unsigned int CStarNetServerPreferences::getStarNetGroupTimeout2() const +{ + return m_starNet2->getGroupTimeout(); +} + +STARNET_CALLSIGN_SWITCH CStarNetServerPreferences::getStarNetCallsignSwitch2() const +{ + return m_starNet2->getCallsignSwitch(); +} + +bool CStarNetServerPreferences::getStarNetTXMsgSwitch2() const +{ + return m_starNet2->getTXMsgSwitch(); +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +wxString CStarNetServerPreferences::getStarNetLink2() const +{ + return m_starNet2->getReflector(); +} +#endif + +wxString CStarNetServerPreferences::getStarNetBand3() const +{ + return m_starNet3->getBand(); +} + +wxString CStarNetServerPreferences::getStarNetCallsign3() const +{ + return m_starNet3->getCallsign(); +} + +wxString CStarNetServerPreferences::getStarNetLogoff3() const +{ + return m_starNet3->getLogoff(); +} + +wxString CStarNetServerPreferences::getStarNetInfo3() const +{ + return m_starNet3->getInfo(); +} + +wxString CStarNetServerPreferences::getStarNetPermanent3() const +{ + return m_starNet3->getPermanent(); +} + +unsigned int CStarNetServerPreferences::getStarNetUserTimeout3() const +{ + return m_starNet3->getUserTimeout(); +} + +unsigned int CStarNetServerPreferences::getStarNetGroupTimeout3() const +{ + return m_starNet3->getGroupTimeout(); +} + +STARNET_CALLSIGN_SWITCH CStarNetServerPreferences::getStarNetCallsignSwitch3() const +{ + return m_starNet3->getCallsignSwitch(); +} + +bool CStarNetServerPreferences::getStarNetTXMsgSwitch3() const +{ + return m_starNet3->getTXMsgSwitch(); +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +wxString CStarNetServerPreferences::getStarNetLink3() const +{ + return m_starNet3->getReflector(); +} +#endif + +wxString CStarNetServerPreferences::getStarNetBand4() const +{ + return m_starNet4->getBand(); +} + +wxString CStarNetServerPreferences::getStarNetCallsign4() const +{ + return m_starNet4->getCallsign(); +} + +wxString CStarNetServerPreferences::getStarNetLogoff4() const +{ + return m_starNet4->getLogoff(); +} + +wxString CStarNetServerPreferences::getStarNetInfo4() const +{ + return m_starNet4->getInfo(); +} + +wxString CStarNetServerPreferences::getStarNetPermanent4() const +{ + return m_starNet4->getPermanent(); +} + +unsigned int CStarNetServerPreferences::getStarNetUserTimeout4() const +{ + return m_starNet4->getUserTimeout(); +} + +unsigned int CStarNetServerPreferences::getStarNetGroupTimeout4() const +{ + return m_starNet4->getGroupTimeout(); +} + +STARNET_CALLSIGN_SWITCH CStarNetServerPreferences::getStarNetCallsignSwitch4() const +{ + return m_starNet4->getCallsignSwitch(); +} + +bool CStarNetServerPreferences::getStarNetTXMsgSwitch4() const +{ + return m_starNet4->getTXMsgSwitch(); +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +wxString CStarNetServerPreferences::getStarNetLink4() const +{ + return m_starNet4->getReflector(); +} +#endif + +wxString CStarNetServerPreferences::getStarNetBand5() const +{ + return m_starNet5->getBand(); +} + +wxString CStarNetServerPreferences::getStarNetCallsign5() const +{ + return m_starNet5->getCallsign(); +} + +wxString CStarNetServerPreferences::getStarNetLogoff5() const +{ + return m_starNet5->getLogoff(); +} + +wxString CStarNetServerPreferences::getStarNetInfo5() const +{ + return m_starNet5->getInfo(); +} + +wxString CStarNetServerPreferences::getStarNetPermanent5() const +{ + return m_starNet5->getPermanent(); +} + +unsigned int CStarNetServerPreferences::getStarNetUserTimeout5() const +{ + return m_starNet5->getUserTimeout(); +} + +unsigned int CStarNetServerPreferences::getStarNetGroupTimeout5() const +{ + return m_starNet5->getGroupTimeout(); +} + +STARNET_CALLSIGN_SWITCH CStarNetServerPreferences::getStarNetCallsignSwitch5() const +{ + return m_starNet5->getCallsignSwitch(); +} + +bool CStarNetServerPreferences::getStarNetTXMsgSwitch5() const +{ + return m_starNet5->getTXMsgSwitch(); +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +wxString CStarNetServerPreferences::getStarNetLink5() const +{ + return m_starNet5->getReflector(); +} +#endif + +wxString CStarNetServerPreferences::getStarNetBand6() const +{ + return m_starNet6->getBand(); +} + +wxString CStarNetServerPreferences::getStarNetCallsign6() const +{ + return m_starNet6->getCallsign(); +} + +wxString CStarNetServerPreferences::getStarNetLogoff6() const +{ + return m_starNet6->getLogoff(); +} + +wxString CStarNetServerPreferences::getStarNetInfo6() const +{ + return m_starNet6->getInfo(); +} + +wxString CStarNetServerPreferences::getStarNetPermanent6() const +{ + return m_starNet6->getPermanent(); +} + +unsigned int CStarNetServerPreferences::getStarNetUserTimeout6() const +{ + return m_starNet6->getUserTimeout(); +} + +unsigned int CStarNetServerPreferences::getStarNetGroupTimeout6() const +{ + return m_starNet6->getGroupTimeout(); +} + +STARNET_CALLSIGN_SWITCH CStarNetServerPreferences::getStarNetCallsignSwitch6() const +{ + return m_starNet6->getCallsignSwitch(); +} + +bool CStarNetServerPreferences::getStarNetTXMsgSwitch6() const +{ + return m_starNet6->getTXMsgSwitch(); +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +wxString CStarNetServerPreferences::getStarNetLink6() const +{ + return m_starNet6->getReflector(); +} +#endif + +wxString CStarNetServerPreferences::getStarNetBand7() const +{ + return m_starNet7->getBand(); +} + +wxString CStarNetServerPreferences::getStarNetCallsign7() const +{ + return m_starNet7->getCallsign(); +} + +wxString CStarNetServerPreferences::getStarNetLogoff7() const +{ + return m_starNet7->getLogoff(); +} + +wxString CStarNetServerPreferences::getStarNetInfo7() const +{ + return m_starNet7->getInfo(); +} + +wxString CStarNetServerPreferences::getStarNetPermanent7() const +{ + return m_starNet7->getPermanent(); +} + +unsigned int CStarNetServerPreferences::getStarNetUserTimeout7() const +{ + return m_starNet7->getUserTimeout(); +} + +unsigned int CStarNetServerPreferences::getStarNetGroupTimeout7() const +{ + return m_starNet7->getGroupTimeout(); +} + +STARNET_CALLSIGN_SWITCH CStarNetServerPreferences::getStarNetCallsignSwitch7() const +{ + return m_starNet7->getCallsignSwitch(); +} + +bool CStarNetServerPreferences::getStarNetTXMsgSwitch7() const +{ + return m_starNet7->getTXMsgSwitch(); +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +wxString CStarNetServerPreferences::getStarNetLink7() const +{ + return m_starNet7->getReflector(); +} +#endif + +wxString CStarNetServerPreferences::getStarNetBand8() const +{ + return m_starNet8->getBand(); +} + +wxString CStarNetServerPreferences::getStarNetCallsign8() const +{ + return m_starNet8->getCallsign(); +} + +wxString CStarNetServerPreferences::getStarNetLogoff8() const +{ + return m_starNet8->getLogoff(); +} + +wxString CStarNetServerPreferences::getStarNetInfo8() const +{ + return m_starNet8->getInfo(); +} + +wxString CStarNetServerPreferences::getStarNetPermanent8() const +{ + return m_starNet8->getPermanent(); +} + +unsigned int CStarNetServerPreferences::getStarNetUserTimeout8() const +{ + return m_starNet8->getUserTimeout(); +} + +unsigned int CStarNetServerPreferences::getStarNetGroupTimeout8() const +{ + return m_starNet8->getGroupTimeout(); +} + +STARNET_CALLSIGN_SWITCH CStarNetServerPreferences::getStarNetCallsignSwitch8() const +{ + return m_starNet8->getCallsignSwitch(); +} + +bool CStarNetServerPreferences::getStarNetTXMsgSwitch8() const +{ + return m_starNet8->getTXMsgSwitch(); +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +wxString CStarNetServerPreferences::getStarNetLink8() const +{ + return m_starNet8->getReflector(); +} +#endif + +wxString CStarNetServerPreferences::getStarNetBand9() const +{ + return m_starNet9->getBand(); +} + +wxString CStarNetServerPreferences::getStarNetCallsign9() const +{ + return m_starNet9->getCallsign(); +} + +wxString CStarNetServerPreferences::getStarNetLogoff9() const +{ + return m_starNet9->getLogoff(); +} + +wxString CStarNetServerPreferences::getStarNetInfo9() const +{ + return m_starNet9->getInfo(); +} + +wxString CStarNetServerPreferences::getStarNetPermanent9() const +{ + return m_starNet9->getPermanent(); +} + +unsigned int CStarNetServerPreferences::getStarNetUserTimeout9() const +{ + return m_starNet9->getUserTimeout(); +} + +unsigned int CStarNetServerPreferences::getStarNetGroupTimeout9() const +{ + return m_starNet9->getGroupTimeout(); +} + +STARNET_CALLSIGN_SWITCH CStarNetServerPreferences::getStarNetCallsignSwitch9() const +{ + return m_starNet9->getCallsignSwitch(); +} + +bool CStarNetServerPreferences::getStarNetTXMsgSwitch9() const +{ + return m_starNet9->getTXMsgSwitch(); +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +wxString CStarNetServerPreferences::getStarNetLink9() const +{ + return m_starNet9->getReflector(); +} +#endif + +wxString CStarNetServerPreferences::getStarNetBand10() const +{ + return m_starNet10->getBand(); +} + +wxString CStarNetServerPreferences::getStarNetCallsign10() const +{ + return m_starNet10->getCallsign(); +} + +wxString CStarNetServerPreferences::getStarNetLogoff10() const +{ + return m_starNet10->getLogoff(); +} + +wxString CStarNetServerPreferences::getStarNetInfo10() const +{ + return m_starNet10->getInfo(); +} + +wxString CStarNetServerPreferences::getStarNetPermanent10() const +{ + return m_starNet10->getPermanent(); +} + +unsigned int CStarNetServerPreferences::getStarNetUserTimeout10() const +{ + return m_starNet10->getUserTimeout(); +} + +unsigned int CStarNetServerPreferences::getStarNetGroupTimeout10() const +{ + return m_starNet10->getGroupTimeout(); +} + +STARNET_CALLSIGN_SWITCH CStarNetServerPreferences::getStarNetCallsignSwitch10() const +{ + return m_starNet10->getCallsignSwitch(); +} + +bool CStarNetServerPreferences::getStarNetTXMsgSwitch10() const +{ + return m_starNet10->getTXMsgSwitch(); +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +wxString CStarNetServerPreferences::getStarNetLink10() const +{ + return m_starNet10->getReflector(); +} +#endif + +wxString CStarNetServerPreferences::getStarNetBand11() const +{ + return m_starNet11->getBand(); +} + +wxString CStarNetServerPreferences::getStarNetCallsign11() const +{ + return m_starNet11->getCallsign(); +} + +wxString CStarNetServerPreferences::getStarNetLogoff11() const +{ + return m_starNet11->getLogoff(); +} + +wxString CStarNetServerPreferences::getStarNetInfo11() const +{ + return m_starNet11->getInfo(); +} + +wxString CStarNetServerPreferences::getStarNetPermanent11() const +{ + return m_starNet11->getPermanent(); +} + +unsigned int CStarNetServerPreferences::getStarNetUserTimeout11() const +{ + return m_starNet11->getUserTimeout(); +} + +unsigned int CStarNetServerPreferences::getStarNetGroupTimeout11() const +{ + return m_starNet11->getGroupTimeout(); +} + +STARNET_CALLSIGN_SWITCH CStarNetServerPreferences::getStarNetCallsignSwitch11() const +{ + return m_starNet11->getCallsignSwitch(); +} + +bool CStarNetServerPreferences::getStarNetTXMsgSwitch11() const +{ + return m_starNet11->getTXMsgSwitch(); +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +wxString CStarNetServerPreferences::getStarNetLink11() const +{ + return m_starNet11->getReflector(); +} +#endif + +wxString CStarNetServerPreferences::getStarNetBand12() const +{ + return m_starNet12->getBand(); +} + +wxString CStarNetServerPreferences::getStarNetCallsign12() const +{ + return m_starNet12->getCallsign(); +} + +wxString CStarNetServerPreferences::getStarNetLogoff12() const +{ + return m_starNet12->getLogoff(); +} + +wxString CStarNetServerPreferences::getStarNetInfo12() const +{ + return m_starNet12->getInfo(); +} + +wxString CStarNetServerPreferences::getStarNetPermanent12() const +{ + return m_starNet12->getPermanent(); +} + +unsigned int CStarNetServerPreferences::getStarNetUserTimeout12() const +{ + return m_starNet12->getUserTimeout(); +} + +unsigned int CStarNetServerPreferences::getStarNetGroupTimeout12() const +{ + return m_starNet12->getGroupTimeout(); +} + +STARNET_CALLSIGN_SWITCH CStarNetServerPreferences::getStarNetCallsignSwitch12() const +{ + return m_starNet12->getCallsignSwitch(); +} + +bool CStarNetServerPreferences::getStarNetTXMsgSwitch12() const +{ + return m_starNet12->getTXMsgSwitch(); +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +wxString CStarNetServerPreferences::getStarNetLink12() const +{ + return m_starNet12->getReflector(); +} +#endif + +wxString CStarNetServerPreferences::getStarNetBand13() const +{ + return m_starNet13->getBand(); +} + +wxString CStarNetServerPreferences::getStarNetCallsign13() const +{ + return m_starNet13->getCallsign(); +} + +wxString CStarNetServerPreferences::getStarNetLogoff13() const +{ + return m_starNet13->getLogoff(); +} + +wxString CStarNetServerPreferences::getStarNetInfo13() const +{ + return m_starNet13->getInfo(); +} + +wxString CStarNetServerPreferences::getStarNetPermanent13() const +{ + return m_starNet13->getPermanent(); +} + +unsigned int CStarNetServerPreferences::getStarNetUserTimeout13() const +{ + return m_starNet13->getUserTimeout(); +} + +unsigned int CStarNetServerPreferences::getStarNetGroupTimeout13() const +{ + return m_starNet13->getGroupTimeout(); +} + +STARNET_CALLSIGN_SWITCH CStarNetServerPreferences::getStarNetCallsignSwitch13() const +{ + return m_starNet13->getCallsignSwitch(); +} + +bool CStarNetServerPreferences::getStarNetTXMsgSwitch13() const +{ + return m_starNet13->getTXMsgSwitch(); +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +wxString CStarNetServerPreferences::getStarNetLink13() const +{ + return m_starNet13->getReflector(); +} +#endif + +wxString CStarNetServerPreferences::getStarNetBand14() const +{ + return m_starNet14->getBand(); +} + +wxString CStarNetServerPreferences::getStarNetCallsign14() const +{ + return m_starNet14->getCallsign(); +} + +wxString CStarNetServerPreferences::getStarNetLogoff14() const +{ + return m_starNet14->getLogoff(); +} + +wxString CStarNetServerPreferences::getStarNetInfo14() const +{ + return m_starNet14->getInfo(); +} + +wxString CStarNetServerPreferences::getStarNetPermanent14() const +{ + return m_starNet14->getPermanent(); +} + +unsigned int CStarNetServerPreferences::getStarNetUserTimeout14() const +{ + return m_starNet14->getUserTimeout(); +} + +unsigned int CStarNetServerPreferences::getStarNetGroupTimeout14() const +{ + return m_starNet14->getGroupTimeout(); +} + +STARNET_CALLSIGN_SWITCH CStarNetServerPreferences::getStarNetCallsignSwitch14() const +{ + return m_starNet14->getCallsignSwitch(); +} + +bool CStarNetServerPreferences::getStarNetTXMsgSwitch14() const +{ + return m_starNet14->getTXMsgSwitch(); +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +wxString CStarNetServerPreferences::getStarNetLink14() const +{ + return m_starNet14->getReflector(); +} +#endif + +wxString CStarNetServerPreferences::getStarNetBand15() const +{ + return m_starNet15->getBand(); +} + +wxString CStarNetServerPreferences::getStarNetCallsign15() const +{ + return m_starNet15->getCallsign(); +} + +wxString CStarNetServerPreferences::getStarNetLogoff15() const +{ + return m_starNet15->getLogoff(); +} + +wxString CStarNetServerPreferences::getStarNetInfo15() const +{ + return m_starNet15->getInfo(); +} + +wxString CStarNetServerPreferences::getStarNetPermanent15() const +{ + return m_starNet15->getPermanent(); +} + +unsigned int CStarNetServerPreferences::getStarNetUserTimeout15() const +{ + return m_starNet15->getUserTimeout(); +} + +unsigned int CStarNetServerPreferences::getStarNetGroupTimeout15() const +{ + return m_starNet15->getGroupTimeout(); +} + +STARNET_CALLSIGN_SWITCH CStarNetServerPreferences::getStarNetCallsignSwitch15() const +{ + return m_starNet15->getCallsignSwitch(); +} + +bool CStarNetServerPreferences::getStarNetTXMsgSwitch15() const +{ + return m_starNet15->getTXMsgSwitch(); +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +wxString CStarNetServerPreferences::getStarNetLink15() const +{ + return m_starNet15->getReflector(); +} +#endif + +bool CStarNetServerPreferences::getRemoteEnabled() const +{ + return m_remote->getEnabled(); +} + +wxString CStarNetServerPreferences::getRemotePassword() const +{ + return m_remote->getPassword(); +} + +unsigned int CStarNetServerPreferences::getRemotePort() const +{ + return m_remote->getPort(); +} diff --git a/StarNetServer/StarNetServerPreferences.h b/StarNetServer/StarNetServerPreferences.h new file mode 100644 index 0000000..7726d9b --- /dev/null +++ b/StarNetServer/StarNetServerPreferences.h @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2010,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 + * 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 StarNetServerPreferences_H +#define StarNetServerPreferences_H + +#include +#include + +#include "StarNetServerMiscellaneousSet.h" +#include "StarNetServerCallsignSet.h" +#include "StarNetServerIrcDDBSet.h" +#include "StarNetSet.h" +#include "RemoteSet.h" + +class CStarNetServerPreferences : public wxDialog { +public: + CStarNetServerPreferences(wxWindow* parent, int id, + const wxString& callsign, const wxString& address, + const wxString& hostname, const wxString& username, const wxString& password, + bool logEnabled, +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + const wxString& starNetBand1, const wxString& callsign1, const wxString& logoff1, const wxString& info1, const wxString& permanent1, unsigned int userTimeout1, unsigned int groupTimeout1, STARNET_CALLSIGN_SWITCH callsignSwitch1, bool txMsgSwitch1, const wxString& link1, + const wxString& starNetBand2, const wxString& callsign2, const wxString& logoff2, const wxString& info2, const wxString& permanent2, unsigned int userTimeout2, unsigned int groupTimeout2, STARNET_CALLSIGN_SWITCH callsignSwitch2, bool txMsgSwitch2, const wxString& link2, + const wxString& starNetBand3, const wxString& callsign3, const wxString& logoff3, const wxString& info3, const wxString& permanent3, unsigned int userTimeout3, unsigned int groupTimeout3, STARNET_CALLSIGN_SWITCH callsignSwitch3, bool txMsgSwitch3, const wxString& link3, + const wxString& starNetBand4, const wxString& callsign4, const wxString& logoff4, const wxString& info4, const wxString& permanent4, unsigned int userTimeout4, unsigned int groupTimeout4, STARNET_CALLSIGN_SWITCH callsignSwitch4, bool txMsgSwitch4, const wxString& link4, + const wxString& starNetBand5, const wxString& callsign5, const wxString& logoff5, const wxString& info5, const wxString& permanent5, unsigned int userTimeout5, unsigned int groupTimeout5, STARNET_CALLSIGN_SWITCH callsignSwitch5, bool txMsgSwitch5, const wxString& link5, + const wxString& starNetBand6, const wxString& callsign6, const wxString& logoff6, const wxString& info6, const wxString& permanent6, unsigned int userTimeout6, unsigned int groupTimeout6, STARNET_CALLSIGN_SWITCH callsignSwitch6, bool txMsgSwitch6, const wxString& link6, + const wxString& starNetBand7, const wxString& callsign7, const wxString& logoff7, const wxString& info7, const wxString& permanent7, unsigned int userTimeout7, unsigned int groupTimeout7, STARNET_CALLSIGN_SWITCH callsignSwitch7, bool txMsgSwitch7, const wxString& link7, + const wxString& starNetBand8, const wxString& callsign8, const wxString& logoff8, const wxString& info8, const wxString& permanent8, unsigned int userTimeout8, unsigned int groupTimeout8, STARNET_CALLSIGN_SWITCH callsignSwitch8, bool txMsgSwitch8, const wxString& link8, + const wxString& starNetBand9, const wxString& callsign9, const wxString& logoff9, const wxString& info9, const wxString& permanent9, unsigned int userTimeout9, unsigned int groupTimeout9, STARNET_CALLSIGN_SWITCH callsignSwitch9, bool txMsgSwitch9, const wxString& link9, + const wxString& starNetBand10, const wxString& callsign10, const wxString& logoff10, const wxString& info10, const wxString& permanent10, unsigned int userTimeout10, unsigned int groupTimeout10, STARNET_CALLSIGN_SWITCH callsignSwitch10, bool txMsgSwitch10, const wxString& link10, + const wxString& starNetBand11, const wxString& callsign11, const wxString& logoff11, const wxString& info11, const wxString& permanent11, unsigned int userTimeout11, unsigned int groupTimeout11, STARNET_CALLSIGN_SWITCH callsignSwitch11, bool txMsgSwitch11, const wxString& link11, + const wxString& starNetBand12, const wxString& callsign12, const wxString& logoff12, const wxString& info12, const wxString& permanent12, unsigned int userTimeout12, unsigned int groupTimeout12, STARNET_CALLSIGN_SWITCH callsignSwitch12, bool txMsgSwitch12, const wxString& link12, + const wxString& starNetBand13, const wxString& callsign13, const wxString& logoff13, const wxString& info13, const wxString& permanent13, unsigned int userTimeout13, unsigned int groupTimeout13, STARNET_CALLSIGN_SWITCH callsignSwitch13, bool txMsgSwitch13, const wxString& link13, + const wxString& starNetBand14, const wxString& callsign14, const wxString& logoff14, const wxString& info14, const wxString& permanent14, unsigned int userTimeout14, unsigned int groupTimeout14, STARNET_CALLSIGN_SWITCH callsignSwitch14, bool txMsgSwitch14, const wxString& link14, + const wxString& starNetBand15, const wxString& callsign15, const wxString& logoff15, const wxString& info15, const wxString& permanent15, unsigned int userTimeout15, unsigned int groupTimeout15, STARNET_CALLSIGN_SWITCH callsignSwitch15, bool txMsgSwitch15, const wxString& link15, +#else + const wxString& starNetBand1, const wxString& callsign1, const wxString& logoff1, const wxString& info1, const wxString& permanent1, unsigned int userTimeout1, unsigned int groupTimeout1, STARNET_CALLSIGN_SWITCH callsignSwitch1, bool txMsgSwitch1, + const wxString& starNetBand2, const wxString& callsign2, const wxString& logoff2, const wxString& info2, const wxString& permanent2, unsigned int userTimeout2, unsigned int groupTimeout2, STARNET_CALLSIGN_SWITCH callsignSwitch2, bool txMsgSwitch2, + const wxString& starNetBand3, const wxString& callsign3, const wxString& logoff3, const wxString& info3, const wxString& permanent3, unsigned int userTimeout3, unsigned int groupTimeout3, STARNET_CALLSIGN_SWITCH callsignSwitch3, bool txMsgSwitch3, + const wxString& starNetBand4, const wxString& callsign4, const wxString& logoff4, const wxString& info4, const wxString& permanent4, unsigned int userTimeout4, unsigned int groupTimeout4, STARNET_CALLSIGN_SWITCH callsignSwitch4, bool txMsgSwitch4, + const wxString& starNetBand5, const wxString& callsign5, const wxString& logoff5, const wxString& info5, const wxString& permanent5, unsigned int userTimeout5, unsigned int groupTimeout5, STARNET_CALLSIGN_SWITCH callsignSwitch5, bool txMsgSwitch5, + const wxString& starNetBand6, const wxString& callsign6, const wxString& logoff6, const wxString& info6, const wxString& permanent6, unsigned int userTimeout6, unsigned int groupTimeout6, STARNET_CALLSIGN_SWITCH callsignSwitch6, bool txMsgSwitch6, + const wxString& starNetBand7, const wxString& callsign7, const wxString& logoff7, const wxString& info7, const wxString& permanent7, unsigned int userTimeout7, unsigned int groupTimeout7, STARNET_CALLSIGN_SWITCH callsignSwitch7, bool txMsgSwitch7, + const wxString& starNetBand8, const wxString& callsign8, const wxString& logoff8, const wxString& info8, const wxString& permanent8, unsigned int userTimeout8, unsigned int groupTimeout8, STARNET_CALLSIGN_SWITCH callsignSwitch8, bool txMsgSwitch8, + const wxString& starNetBand9, const wxString& callsign9, const wxString& logoff9, const wxString& info9, const wxString& permanent9, unsigned int userTimeout9, unsigned int groupTimeout9, STARNET_CALLSIGN_SWITCH callsignSwitch9, bool txMsgSwitch9, + const wxString& starNetBand10, const wxString& callsign10, const wxString& logoff10, const wxString& info10, const wxString& permanent10, unsigned int userTimeout10, unsigned int groupTimeout10, STARNET_CALLSIGN_SWITCH callsignSwitch10, bool txMsgSwitch10, + const wxString& starNetBand11, const wxString& callsign11, const wxString& logoff11, const wxString& info11, const wxString& permanent11, unsigned int userTimeout11, unsigned int groupTimeout11, STARNET_CALLSIGN_SWITCH callsignSwitch11, bool txMsgSwitch11, + const wxString& starNetBand12, const wxString& callsign12, const wxString& logoff12, const wxString& info12, const wxString& permanent12, unsigned int userTimeout12, unsigned int groupTimeout12, STARNET_CALLSIGN_SWITCH callsignSwitch12, bool txMsgSwitch12, + const wxString& starNetBand13, const wxString& callsign13, const wxString& logoff13, const wxString& info13, const wxString& permanent13, unsigned int userTimeout13, unsigned int groupTimeout13, STARNET_CALLSIGN_SWITCH callsignSwitch13, bool txMsgSwitch13, + const wxString& starNetBand14, const wxString& callsign14, const wxString& logoff14, const wxString& info14, const wxString& permanent14, unsigned int userTimeout14, unsigned int groupTimeout14, STARNET_CALLSIGN_SWITCH callsignSwitch14, bool txMsgSwitch14, + const wxString& starNetBand15, const wxString& callsign15, const wxString& logoff15, const wxString& info15, const wxString& permanent15, unsigned int userTimeout15, unsigned int groupTimeout15, STARNET_CALLSIGN_SWITCH callsignSwitch15, bool txMsgSwitch15, +#endif + bool remoteEnabled, const wxString& remotePassword, unsigned int remotePort); + virtual ~CStarNetServerPreferences(); + + virtual bool Validate(); + + virtual wxString getCallsign() const; + virtual wxString getAddress() const; + + virtual wxString getHostname() const; + virtual wxString getUsername() const; + virtual wxString getPassword() const; + + virtual bool getLogEnabled() const; + + virtual wxString getStarNetBand1() const; + virtual wxString getStarNetCallsign1() const; + virtual wxString getStarNetLogoff1() const; + virtual wxString getStarNetInfo1() const; + virtual wxString getStarNetPermanent1() const; + virtual unsigned int getStarNetUserTimeout1() const; + virtual unsigned int getStarNetGroupTimeout1() const; + virtual STARNET_CALLSIGN_SWITCH getStarNetCallsignSwitch1() const; + virtual bool getStarNetTXMsgSwitch1() const; +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + virtual wxString getStarNetLink1() const; +#endif + + virtual wxString getStarNetBand2() const; + virtual wxString getStarNetCallsign2() const; + virtual wxString getStarNetLogoff2() const; + virtual wxString getStarNetInfo2() const; + virtual wxString getStarNetPermanent2() const; + virtual unsigned int getStarNetUserTimeout2() const; + virtual unsigned int getStarNetGroupTimeout2() const; + virtual STARNET_CALLSIGN_SWITCH getStarNetCallsignSwitch2() const; + virtual bool getStarNetTXMsgSwitch2() const; +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + virtual wxString getStarNetLink2() const; +#endif + + virtual wxString getStarNetBand3() const; + virtual wxString getStarNetCallsign3() const; + virtual wxString getStarNetLogoff3() const; + virtual wxString getStarNetInfo3() const; + virtual wxString getStarNetPermanent3() const; + virtual unsigned int getStarNetUserTimeout3() const; + virtual unsigned int getStarNetGroupTimeout3() const; + virtual STARNET_CALLSIGN_SWITCH getStarNetCallsignSwitch3() const; + virtual bool getStarNetTXMsgSwitch3() const; +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + virtual wxString getStarNetLink3() const; +#endif + + virtual wxString getStarNetBand4() const; + virtual wxString getStarNetCallsign4() const; + virtual wxString getStarNetLogoff4() const; + virtual wxString getStarNetInfo4() const; + virtual wxString getStarNetPermanent4() const; + virtual unsigned int getStarNetUserTimeout4() const; + virtual unsigned int getStarNetGroupTimeout4() const; + virtual STARNET_CALLSIGN_SWITCH getStarNetCallsignSwitch4() const; + virtual bool getStarNetTXMsgSwitch4() const; +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + virtual wxString getStarNetLink4() const; +#endif + + virtual wxString getStarNetBand5() const; + virtual wxString getStarNetCallsign5() const; + virtual wxString getStarNetLogoff5() const; + virtual wxString getStarNetInfo5() const; + virtual wxString getStarNetPermanent5() const; + virtual unsigned int getStarNetUserTimeout5() const; + virtual unsigned int getStarNetGroupTimeout5() const; + virtual STARNET_CALLSIGN_SWITCH getStarNetCallsignSwitch5() const; + virtual bool getStarNetTXMsgSwitch5() const; +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + virtual wxString getStarNetLink5() const; +#endif + + virtual wxString getStarNetBand6() const; + virtual wxString getStarNetCallsign6() const; + virtual wxString getStarNetLogoff6() const; + virtual wxString getStarNetInfo6() const; + virtual wxString getStarNetPermanent6() const; + virtual unsigned int getStarNetUserTimeout6() const; + virtual unsigned int getStarNetGroupTimeout6() const; + virtual STARNET_CALLSIGN_SWITCH getStarNetCallsignSwitch6() const; + virtual bool getStarNetTXMsgSwitch6() const; +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + virtual wxString getStarNetLink6() const; +#endif + + virtual wxString getStarNetBand7() const; + virtual wxString getStarNetCallsign7() const; + virtual wxString getStarNetLogoff7() const; + virtual wxString getStarNetInfo7() const; + virtual wxString getStarNetPermanent7() const; + virtual unsigned int getStarNetUserTimeout7() const; + virtual unsigned int getStarNetGroupTimeout7() const; + virtual STARNET_CALLSIGN_SWITCH getStarNetCallsignSwitch7() const; + virtual bool getStarNetTXMsgSwitch7() const; +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + virtual wxString getStarNetLink7() const; +#endif + + virtual wxString getStarNetBand8() const; + virtual wxString getStarNetCallsign8() const; + virtual wxString getStarNetLogoff8() const; + virtual wxString getStarNetInfo8() const; + virtual wxString getStarNetPermanent8() const; + virtual unsigned int getStarNetUserTimeout8() const; + virtual unsigned int getStarNetGroupTimeout8() const; + virtual STARNET_CALLSIGN_SWITCH getStarNetCallsignSwitch8() const; + virtual bool getStarNetTXMsgSwitch8() const; +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + virtual wxString getStarNetLink8() const; +#endif + + virtual wxString getStarNetBand9() const; + virtual wxString getStarNetCallsign9() const; + virtual wxString getStarNetLogoff9() const; + virtual wxString getStarNetInfo9() const; + virtual wxString getStarNetPermanent9() const; + virtual unsigned int getStarNetUserTimeout9() const; + virtual unsigned int getStarNetGroupTimeout9() const; + virtual STARNET_CALLSIGN_SWITCH getStarNetCallsignSwitch9() const; + virtual bool getStarNetTXMsgSwitch9() const; +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + virtual wxString getStarNetLink9() const; +#endif + + virtual wxString getStarNetBand10() const; + virtual wxString getStarNetCallsign10() const; + virtual wxString getStarNetLogoff10() const; + virtual wxString getStarNetInfo10() const; + virtual wxString getStarNetPermanent10() const; + virtual unsigned int getStarNetUserTimeout10() const; + virtual unsigned int getStarNetGroupTimeout10() const; + virtual STARNET_CALLSIGN_SWITCH getStarNetCallsignSwitch10() const; + virtual bool getStarNetTXMsgSwitch10() const; +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + virtual wxString getStarNetLink10() const; +#endif + + virtual wxString getStarNetBand11() const; + virtual wxString getStarNetCallsign11() const; + virtual wxString getStarNetLogoff11() const; + virtual wxString getStarNetInfo11() const; + virtual wxString getStarNetPermanent11() const; + virtual unsigned int getStarNetUserTimeout11() const; + virtual unsigned int getStarNetGroupTimeout11() const; + virtual STARNET_CALLSIGN_SWITCH getStarNetCallsignSwitch11() const; + virtual bool getStarNetTXMsgSwitch11() const; +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + virtual wxString getStarNetLink11() const; +#endif + + virtual wxString getStarNetBand12() const; + virtual wxString getStarNetCallsign12() const; + virtual wxString getStarNetLogoff12() const; + virtual wxString getStarNetInfo12() const; + virtual wxString getStarNetPermanent12() const; + virtual unsigned int getStarNetUserTimeout12() const; + virtual unsigned int getStarNetGroupTimeout12() const; + virtual STARNET_CALLSIGN_SWITCH getStarNetCallsignSwitch12() const; + virtual bool getStarNetTXMsgSwitch12() const; +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + virtual wxString getStarNetLink12() const; +#endif + + virtual wxString getStarNetBand13() const; + virtual wxString getStarNetCallsign13() const; + virtual wxString getStarNetLogoff13() const; + virtual wxString getStarNetInfo13() const; + virtual wxString getStarNetPermanent13() const; + virtual unsigned int getStarNetUserTimeout13() const; + virtual unsigned int getStarNetGroupTimeout13() const; + virtual STARNET_CALLSIGN_SWITCH getStarNetCallsignSwitch13() const; + virtual bool getStarNetTXMsgSwitch13() const; +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + virtual wxString getStarNetLink13() const; +#endif + + virtual wxString getStarNetBand14() const; + virtual wxString getStarNetCallsign14() const; + virtual wxString getStarNetLogoff14() const; + virtual wxString getStarNetInfo14() const; + virtual wxString getStarNetPermanent14() const; + virtual unsigned int getStarNetUserTimeout14() const; + virtual unsigned int getStarNetGroupTimeout14() const; + virtual STARNET_CALLSIGN_SWITCH getStarNetCallsignSwitch14() const; + virtual bool getStarNetTXMsgSwitch14() const; +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + virtual wxString getStarNetLink14() const; +#endif + + virtual wxString getStarNetBand15() const; + virtual wxString getStarNetCallsign15() const; + virtual wxString getStarNetLogoff15() const; + virtual wxString getStarNetInfo15() const; + virtual wxString getStarNetPermanent15() const; + virtual unsigned int getStarNetUserTimeout15() const; + virtual unsigned int getStarNetGroupTimeout15() const; + virtual STARNET_CALLSIGN_SWITCH getStarNetCallsignSwitch15() const; + virtual bool getStarNetTXMsgSwitch15() const; +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + virtual wxString getStarNetLink15() const; +#endif + + virtual bool getRemoteEnabled() const; + virtual wxString getRemotePassword() const; + virtual unsigned int getRemotePort() const; + +private: + CStarNetServerCallsignSet* m_callsign; + CStarNetServerIrcDDBSet* m_ircDDB; + CStarNetSet* m_starNet1; + CStarNetSet* m_starNet2; + CStarNetSet* m_starNet3; + CStarNetSet* m_starNet4; + CStarNetSet* m_starNet5; + CStarNetSet* m_starNet6; + CStarNetSet* m_starNet7; + CStarNetSet* m_starNet8; + CStarNetSet* m_starNet9; + CStarNetSet* m_starNet10; + CStarNetSet* m_starNet11; + CStarNetSet* m_starNet12; + CStarNetSet* m_starNet13; + CStarNetSet* m_starNet14; + CStarNetSet* m_starNet15; + CRemoteSet* m_remote; + CStarNetServerMiscellaneousSet* m_miscellaneous; +}; + +#endif diff --git a/StarNetServer/StarNetServerThread.cpp b/StarNetServer/StarNetServerThread.cpp new file mode 100644 index 0000000..2b86002 --- /dev/null +++ b/StarNetServer/StarNetServerThread.cpp @@ -0,0 +1,627 @@ +/* + * 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 + * 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 "StarNetServerThread.h" +#include "StarNetServerDefs.h" +#include "StarNetHandler.h" +#include "DExtraHandler.h" // DEXTRA LINK +#include "DCSHandler.h" // DCS LINK +#include "HeaderData.h" +#include "G2Handler.h" +#include "AMBEData.h" +#include "HostFile.h" // DEXTRA_LINK || DCS_LINK +#include "Utils.h" + +#include +#include + +const unsigned int REMOTE_DUMMY_PORT = 65015U; + +CStarNetServerThread::CStarNetServerThread(bool nolog, const wxString& logDir) : +m_nolog(nolog), +m_logDir(logDir), +m_killed(false), +m_stopped(true), +m_callsign(), +m_address(), +#if defined(DEXTRA_LINK) +m_dextraPool(NULL), +#endif +#if defined(DCS_LINK) +m_dcsPool(NULL), +#endif +m_g2Handler(NULL), +m_irc(NULL), +m_cache(), +m_logEnabled(false), +m_statusTimer(1000U, 1U), // 1 second +m_lastStatus(IS_DISCONNECTED), +m_remoteEnabled(false), +m_remotePassword(), +m_remotePort(0U), +m_remote(NULL) +{ + CHeaderData::initialise(); + CG2Handler::initialise(MAX_ROUTES); + CStarNetHandler::initialise(MAX_STARNETS); +#if defined(DEXTRA_LINK) + CDExtraHandler::initialise(MAX_DEXTRA_LINKS); +#endif +#if defined(DCS_LINK) + CDCSHandler::initialise(MAX_DCS_LINKS); +#endif +} + +CStarNetServerThread::~CStarNetServerThread() +{ + CHeaderData::finalise(); + CG2Handler::finalise(); + CStarNetHandler::finalise(); +#if defined(DEXTRA_LINK) + CDExtraHandler::finalise(); +#endif +#if defined(DCS_LINK) + CDCSHandler::finalise(); +#endif +} + +void CStarNetServerThread::run() +{ + // Truncate the old StarNet.log file + wxFileName fileName(m_logDir, STARNET_BASE_NAME, wxT("log")); + wxLogMessage(wxT("Truncating %s"), fileName.GetFullPath().c_str()); + + wxFFile file; + bool ret = file.Open(fileName.GetFullPath(), wxT("wt")); + if (ret) + file.Close(); + +#if defined(DEXTRA_LINK) + m_dextraPool = new CDExtraProtocolHandlerPool(MAX_DEXTRA_LINKS, DEXTRA_PORT, m_address); + ret = m_dextraPool->open(); + if (!ret) { + wxLogError(wxT("Could not open the DExtra protocol pool")); + delete m_dextraPool; + m_dextraPool = NULL; + } +#endif + +#if defined(DCS_LINK) + m_dcsPool = new CDCSProtocolHandlerPool(MAX_DCS_LINKS, DCS_PORT, m_address); + ret = m_dcsPool->open(); + if (!ret) { + wxLogError(wxT("Could not open the DCS protocol pool")); + delete m_dcsPool; + m_dcsPool = NULL; + } +#endif + + m_g2Handler = new CG2ProtocolHandler(G2_DV_PORT, m_address); + ret = m_g2Handler->open(); + if (!ret) { + wxLogError(wxT("Could not open the G2 protocol handler")); + delete m_g2Handler; + m_g2Handler = NULL; + } + + // Wait here until we have the essentials to run +#if defined(DEXTRA_LINK) + while (!m_killed && (m_g2Handler == NULL || m_dextraPool == NULL || m_irc == NULL || m_callsign.IsEmpty())) + ::wxMilliSleep(500UL); // 1/2 sec +#elif defined(DCS_LINK) + while (!m_killed && (m_g2Handler == NULL || m_dcsPool == NULL || m_irc == NULL || m_callsign.IsEmpty())) + ::wxMilliSleep(500UL); // 1/2 sec +#else + while (!m_killed && (m_g2Handler == NULL || m_irc == NULL || m_callsign.IsEmpty())) + ::wxMilliSleep(500UL); // 1/2 sec +#endif + + if (m_killed) + return; + + m_stopped = false; + + wxLogMessage(wxT("Starting the StarNet Server thread")); + + CHeaderLogger* headerLogger = NULL; + if (m_logEnabled) { + headerLogger = new CHeaderLogger(m_logDir); + ret = headerLogger->open(); + if (!ret) { + delete headerLogger; + headerLogger = NULL; + } + } + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + loadReflectors(); +#endif + + CG2Handler::setG2ProtocolHandler(m_g2Handler); + CG2Handler::setHeaderLogger(headerLogger); + +#if defined(DEXTRA_LINK) + CDExtraHandler::setCallsign(m_callsign); + CDExtraHandler::setDExtraProtocolHandlerPool(m_dextraPool); + CDExtraHandler::setHeaderLogger(headerLogger); +#endif +#if defined(DCS_LINK) + CDCSHandler::setDCSProtocolHandlerPool(m_dcsPool); + CDCSHandler::setHeaderLogger(headerLogger); + CDCSHandler::setGatewayType(GT_STARNET); +#endif + + CStarNetHandler::setCache(&m_cache); + CStarNetHandler::setGateway(m_callsign); + CStarNetHandler::setG2Handler(m_g2Handler); + CStarNetHandler::setIRC(m_irc); + CStarNetHandler::setLogging(m_logEnabled, m_logDir); +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + CStarNetHandler::link(); +#endif + + if (m_remoteEnabled && !m_remotePassword.IsEmpty() && m_remotePort > 0U) { + m_remote = new CRemoteHandler(m_remotePassword, m_remotePort); + bool res = m_remote->open(); + if (!res) { + delete m_remote; + m_remote = NULL; + } + } + + wxStopWatch stopWatch; + stopWatch.Start(); + + m_statusTimer.start(); + + try { + while (!m_killed) { + processIrcDDB(); + processG2(); +#if defined(DEXTRA_LINK) + processDExtra(); +#endif +#if defined(DCS_LINK) + processDCS(); +#endif + if (m_remote != NULL) + m_remote->process(); + + unsigned long ms = stopWatch.Time(); + stopWatch.Start(); + + m_statusTimer.clock(ms); + + CG2Handler::clock(ms); + CStarNetHandler::clock(ms); +#if defined(DEXTRA_LINK) + CDExtraHandler::clock(ms); +#endif +#if defined(DCS_LINK) + CDCSHandler::clock(ms); +#endif + + ::wxMilliSleep(TIME_PER_TIC_MS); + } + } + catch (std::exception& e) { + wxString message(e.what(), wxConvLocal); + wxLogError(wxT("Exception raised - \"%s\""), message.c_str()); + } + catch (...) { + wxLogError(wxT("Unknown exception raised")); + } + + wxLogMessage(wxT("Stopping the StarNet Server thread")); + +#if defined(DEXTRA_LINK) + // Unlink from all reflectors + CDExtraHandler::unlink(); + + m_dextraPool->close(); + delete m_dextraPool; +#endif + +#if defined(DCS_LINK) + // Unlink from all reflectors + CDCSHandler::unlink(); + + m_dcsPool->close(); + delete m_dcsPool; +#endif + + m_g2Handler->close(); + delete m_g2Handler; + + m_irc->close(); + delete m_irc; + + if (m_remote != NULL) { + m_remote->close(); + delete m_remote; + } + + if (headerLogger != NULL) { + headerLogger->close(); + delete headerLogger; + } +} + +void CStarNetServerThread::kill() +{ + m_killed = true; +} + +void CStarNetServerThread::setCallsign(const wxString& callsign) +{ + if (!m_stopped) + return; + + m_callsign = callsign; +} + +void CStarNetServerThread::setAddress(const wxString& address) +{ + m_address = address; +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CStarNetServerThread::addStarNet(const wxString& callsign, const wxString& logoff, const wxString& repeater, const wxString& infoText, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +{ + CStarNetHandler::add(callsign, logoff, repeater, infoText, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); +} +#else +void CStarNetServerThread::addStarNet(const wxString& callsign, const wxString& logoff, const wxString& repeater, const wxString& infoText, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +{ + CStarNetHandler::add(callsign, logoff, repeater, infoText, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); +} +#endif + +void CStarNetServerThread::setIRC(CIRCDDB* irc) +{ + wxASSERT(irc != NULL); + + m_irc = irc; +} + +void CStarNetServerThread::setLog(bool enabled) +{ + m_logEnabled = enabled; +} + +void CStarNetServerThread::setRemote(bool enabled, const wxString& password, unsigned int port) +{ + if (enabled) { + m_remoteEnabled = true; + m_remotePassword = password; + m_remotePort = port; + } else { + m_remoteEnabled = false; + m_remotePassword = password; + m_remotePort = REMOTE_DUMMY_PORT; + } +} + +void CStarNetServerThread::processIrcDDB() +{ + // Once per second + if (m_statusTimer.hasExpired()) { + int status = m_irc->getConnectionState(); + switch (status) { + case 0: + case 10: + if (m_lastStatus != IS_DISCONNECTED) { + wxLogInfo(wxT("Disconnected from ircDDB")); + m_lastStatus = IS_DISCONNECTED; + } + break; + case 7: + if (m_lastStatus != IS_CONNECTED) { + wxLogInfo(wxT("Connected to ircDDB")); + m_lastStatus = IS_CONNECTED; + } + break; + default: + if (m_lastStatus != IS_CONNECTING) { + wxLogInfo(wxT("Connecting to ircDDB")); + m_lastStatus = IS_CONNECTING; + } + break; + } + + m_statusTimer.start(); + } + + // Process all incoming ircDDB messages, updating the caches + for (;;) { + IRCDDB_RESPONSE_TYPE type = m_irc->getMessageType(); + + switch (type) { + case IDRT_NONE: + return; + + case IDRT_USER: { + wxString user, repeater, gateway, address, timestamp; + bool res = m_irc->receiveUser(user, repeater, gateway, address, timestamp); + if (!res) + break; + + if (!address.IsEmpty()) { + wxLogMessage(wxT("USER: %s %s %s %s"), user.c_str(), repeater.c_str(), gateway.c_str(), address.c_str()); + m_cache.updateUser(user, repeater, gateway, address, timestamp, DP_DEXTRA, false, false); + } else { + wxLogMessage(wxT("USER: %s NOT FOUND"), user.c_str()); + } + } + break; + + case IDRT_REPEATER: { + wxString repeater, gateway, address; + bool res = m_irc->receiveRepeater(repeater, gateway, address); + if (!res) + break; + + if (!address.IsEmpty()) { + wxLogMessage(wxT("REPEATER: %s %s %s"), repeater.c_str(), gateway.c_str(), address.c_str()); + m_cache.updateRepeater(repeater, gateway, address, DP_DEXTRA, false, false); + } else { + wxLogMessage(wxT("REPEATER: %s NOT FOUND"), repeater.c_str()); + } + } + break; + + case IDRT_GATEWAY: { + wxString gateway, address; + bool res = m_irc->receiveGateway(gateway, address); + if (!res) + break; + +#if defined(DEXTRA_LINK) + CDExtraHandler::gatewayUpdate(gateway, address); +#endif +#if defined(DCS_LINK) + CDCSHandler::gatewayUpdate(gateway, address); +#endif + + if (!address.IsEmpty()) { + wxLogMessage(wxT("GATEWAY: %s %s"), gateway.c_str(), address.c_str()); + m_cache.updateGateway(gateway, address, DP_DEXTRA, false, false); + } else { + wxLogMessage(wxT("GATEWAY: %s NOT FOUND"), gateway.c_str()); + } + } + break; + } + } +} + +#if defined(DEXTRA_LINK) +void CStarNetServerThread::processDExtra() +{ + for (;;) { + DEXTRA_TYPE type = m_dextraPool->read(); + + switch (type) { + case DE_NONE: + return; + + case DE_POLL: { + CPollData* poll = m_dextraPool->readPoll(); + if (poll != NULL) { + CDExtraHandler::process(*poll); + delete poll; + } + } + break; + + case DE_CONNECT: { + CConnectData* connect = m_dextraPool->readConnect(); + if (connect != NULL) { + CDExtraHandler::process(*connect); + delete connect; + } + } + break; + + case DE_HEADER: { + CHeaderData* header = m_dextraPool->readHeader(); + if (header != NULL) { + // wxLogMessage(wxT("DExtra header - My: %s/%s Your: %s Rpt1: %s Rpt2: %s"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str()); + CDExtraHandler::process(*header); + delete header; + } + } + break; + + case DE_AMBE: { + CAMBEData* data = m_dextraPool->readAMBE(); + if (data != NULL) { + CDExtraHandler::process(*data); + delete data; + } + } + break; + } + } +} +#endif + +#if defined(DCS_LINK) +void CStarNetServerThread::processDCS() +{ + for (;;) { + DCS_TYPE type = m_dcsPool->read(); + + switch (type) { + case DC_NONE: + return; + + case DC_POLL: { + CPollData* poll = m_dcsPool->readPoll(); + if (poll != NULL) { + CDCSHandler::process(*poll); + delete poll; + } + } + break; + + case DC_CONNECT: { + CConnectData* connect = m_dcsPool->readConnect(); + if (connect != NULL) { + CDCSHandler::process(*connect); + delete connect; + } + } + break; + + case DC_DATA: { + CAMBEData* data = m_dcsPool->readData(); + if (data != NULL) { + // wxLogMessage(wxT("DCS header - My: %s/%s Your: %s Rpt1: %s Rpt2: %s"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str()); + CDCSHandler::process(*data); + delete data; + } + } + break; + } + } +} +#endif + +void CStarNetServerThread::processG2() +{ + for (;;) { + G2_TYPE type = m_g2Handler->read(); + + switch (type) { + case GT_NONE: + return; + + case GT_HEADER: { + CHeaderData* header = m_g2Handler->readHeader(); + if (header != NULL) { + // wxLogMessage(wxT("G2 header - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); + CG2Handler::process(*header); + delete header; + } + } + break; + + case GT_AMBE: { + CAMBEData* data = m_g2Handler->readAMBE(); + if (data != NULL) { + CG2Handler::process(*data); + delete data; + } + } + break; + } + } +} + +#if defined(DEXTRA_LINK) +void CStarNetServerThread::loadReflectors() +{ + wxFileName fileName(wxFileName::GetHomeDir(), DEXTRA_HOSTS_FILE_NAME); + + if (!fileName.IsFileReadable()) { + wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str()); +#if defined(__WINDOWS__) + fileName.Assign(::wxGetCwd(), DEXTRA_HOSTS_FILE_NAME); +#else + fileName.Assign(wxT(DATA_DIR), DEXTRA_HOSTS_FILE_NAME); +#endif + if (!fileName.IsFileReadable()) + wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str()); + } + + unsigned int count = 0U; + + CHostFile hostFile(fileName.GetFullPath(), true); + for (unsigned int i = 0U; i < hostFile.getCount(); i++) { + wxString reflector = hostFile.getName(i); + in_addr address = CUDPReaderWriter::lookup(hostFile.getAddress(i)); + bool lock = hostFile.getLock(i); + + if (address.s_addr != INADDR_NONE) { + unsigned char* ucp = (unsigned char*)&address; + + wxString addrText; + addrText.Printf(wxT("%u.%u.%u.%u"), ucp[0U] & 0xFFU, ucp[1U] & 0xFFU, ucp[2U] & 0xFFU, ucp[3U] & 0xFFU); + + if (lock) + wxLogMessage(wxT("Locking %s to %s"), reflector.c_str(), addrText.c_str()); + + reflector.Append(wxT(" ")); + reflector.Truncate(LONG_CALLSIGN_LENGTH - 1U); + reflector.Append(wxT("G")); + m_cache.updateGateway(reflector, addrText, DP_DEXTRA, lock, true); + + count++; + } + } + + wxLogMessage(wxT("Loaded %u of %u DExtra reflectors"), count, hostFile.getCount()); +} +#endif + +#if defined(DCS_LINK) +void CStarNetServerThread::loadReflectors() +{ + wxFileName fileName(wxFileName::GetHomeDir(), DCS_HOSTS_FILE_NAME); + + if (!fileName.IsFileReadable()) { + wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str()); +#if defined(__WINDOWS__) + fileName.Assign(::wxGetCwd(), DCS_HOSTS_FILE_NAME); +#else + fileName.Assign(wxT(DATA_DIR), DCS_HOSTS_FILE_NAME); +#endif + if (!fileName.IsFileReadable()) + wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str()); + } + + unsigned int count = 0U; + + CHostFile hostFile(fileName.GetFullPath(), true); + for (unsigned int i = 0U; i < hostFile.getCount(); i++) { + wxString reflector = hostFile.getName(i); + in_addr address = CUDPReaderWriter::lookup(hostFile.getAddress(i)); + bool lock = hostFile.getLock(i); + + if (address.s_addr != INADDR_NONE) { + unsigned char* ucp = (unsigned char*)&address; + + wxString addrText; + addrText.Printf(wxT("%u.%u.%u.%u"), ucp[0U] & 0xFFU, ucp[1U] & 0xFFU, ucp[2U] & 0xFFU, ucp[3U] & 0xFFU); + + if (lock) + wxLogMessage(wxT("Locking %s to %s"), reflector.c_str(), addrText.c_str()); + + reflector.Append(wxT(" ")); + reflector.Truncate(LONG_CALLSIGN_LENGTH - 1U); + reflector.Append(wxT("G")); + m_cache.updateGateway(reflector, addrText, DP_DCS, lock, true); + + count++; + } + } + + wxLogMessage(wxT("Loaded %u of %u DCS reflectors"), count, hostFile.getCount()); +} +#endif diff --git a/StarNetServer/StarNetServerThread.h b/StarNetServer/StarNetServerThread.h new file mode 100644 index 0000000..aac957a --- /dev/null +++ b/StarNetServer/StarNetServerThread.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2010,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 + * 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 StarNetServerThread_H +#define StarNetServerThread_H + +#include "DExtraProtocolHandlerPool.h" // DEXTRA_LINK +#include "DCSProtocolHandlerPool.h" // DCS_LINK +#include "G2ProtocolHandler.h" +#include "RemoteHandler.h" +#include "CacheManager.h" +#include "IRCDDB.h" +#include "Timer.h" +#include "Defs.h" + +#include + +class CStarNetServerThread { +public: + CStarNetServerThread(bool nolog, const wxString& logDir); + virtual ~CStarNetServerThread(); + + virtual void setCallsign(const wxString& callsign); + virtual void setAddress(const wxString& address); +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + virtual void addStarNet(const wxString& callsign, const wxString& logoff, const wxString& repeater, const wxString& infoText, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); +#else + virtual void addStarNet(const wxString& callsign, const wxString& logoff, const wxString& repeater, const wxString& infoText, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); +#endif + virtual void setRemote(bool enabled, const wxString& password, unsigned int port); + virtual void setIRC(CIRCDDB* irc); + virtual void setLog(bool enabled); + + virtual void run(); + virtual void kill(); + +private: + bool m_nolog; + wxString m_logDir; + bool m_killed; + bool m_stopped; + wxString m_callsign; + wxString m_address; +#if defined(DEXTRA_LINK) + CDExtraProtocolHandlerPool* m_dextraPool; +#endif +#if defined(DCS_LINK) + CDCSProtocolHandlerPool* m_dcsPool; +#endif + CG2ProtocolHandler* m_g2Handler; + CIRCDDB* m_irc; + CCacheManager m_cache; + bool m_logEnabled; + CTimer m_statusTimer; + IRCDDB_STATUS m_lastStatus; + bool m_remoteEnabled; + wxString m_remotePassword; + unsigned int m_remotePort; + CRemoteHandler* m_remote; + + void processIrcDDB(); + void processG2(); +#if defined(DEXTRA_LINK) + void processDExtra(); + void loadReflectors(); +#endif +#if defined(DCS_LINK) + void processDCS(); + void loadReflectors(); +#endif +}; + +#endif diff --git a/StarNetServer/StarNetServerThreadHelper.cpp b/StarNetServer/StarNetServerThreadHelper.cpp new file mode 100644 index 0000000..e6bb482 --- /dev/null +++ b/StarNetServer/StarNetServerThreadHelper.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2012 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 "StarNetServerThreadHelper.h" + +CStarNetServerThreadHelper::CStarNetServerThreadHelper(CStarNetServerThread* thread) : +wxThread(wxTHREAD_JOINABLE), +m_thread(thread) +{ + wxASSERT(thread != NULL); +} + +CStarNetServerThreadHelper::~CStarNetServerThreadHelper() +{ + delete m_thread; +} + +void CStarNetServerThreadHelper::start() +{ + Create(); + + SetPriority(100U); + + Run(); +} + +void* CStarNetServerThreadHelper::Entry() +{ + wxASSERT(m_thread != NULL); + + m_thread->run(); + + return NULL; +} + +void CStarNetServerThreadHelper::kill() +{ + wxASSERT(m_thread != NULL); + + m_thread->kill(); + + Wait(); +} diff --git a/StarNetServer/StarNetServerThreadHelper.h b/StarNetServer/StarNetServerThreadHelper.h new file mode 100644 index 0000000..17796e8 --- /dev/null +++ b/StarNetServer/StarNetServerThreadHelper.h @@ -0,0 +1,42 @@ +/* + * 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 + * 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 StarNetServerThreadHelper_H +#define StarNetServerThreadHelper_H + +#include "StarNetServerThread.h" + +#include + +class CStarNetServerThreadHelper : public wxThread { + +public: + CStarNetServerThreadHelper(CStarNetServerThread* thread); + virtual ~CStarNetServerThreadHelper(); + + virtual void start(); + + virtual void* Entry(); + + virtual void kill(); + +private: + CStarNetServerThread* m_thread; +}; + +#endif diff --git a/TextTransmit/TextTransmit.cpp b/TextTransmit/TextTransmit.cpp new file mode 100644 index 0000000..091a8a0 --- /dev/null +++ b/TextTransmit/TextTransmit.cpp @@ -0,0 +1,209 @@ +/* + * Copyright (C) 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 "SlowDataEncoder.h" +#include "DStarDefines.h" +#include "TextTransmit.h" + +#include +#include + +const wxChar* REPEATER_PARAM = wxT("Repeater"); +const wxChar* FILE_OPTION = wxT("file"); +const wxChar* TEXT_OPTION = wxT("text"); + +int main(int argc, char** argv) +{ + bool res = ::wxInitialize(); + if (!res) { + ::fprintf(stderr, "texttransmit: failed to initialise the wxWidgets library, exiting\n"); + return 1; + } + + wxCmdLineParser parser(argc, argv); + parser.AddParam(REPEATER_PARAM, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption(TEXT_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption(FILE_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + + int cmd = parser.Parse(); + if (cmd != 0) { + ::wxUninitialize(); + return 1; + } + + if (parser.GetParamCount() < 1U) { + ::fprintf(stderr, "texttransmit: invalid command line usage: texttransmit -text |-file , exiting\n"); + ::wxUninitialize(); + return 1; + } + + wxString text; + bool textFound = parser.Found(TEXT_OPTION, &text); + + wxString filename; + bool fileFound = parser.Found(FILE_OPTION, &filename); + + if (!textFound && !fileFound) { + ::fprintf(stderr, "texttransmit: invalid command line usage: texttransmit -text |-file , exiting\n"); + ::wxUninitialize(); + return 1; + } + + if (textFound && fileFound) { + ::fprintf(stderr, "texttransmit: invalid command line usage: texttransmit -text |-file , exiting\n"); + ::wxUninitialize(); + return 1; + } + + wxString repeater = parser.GetParam(0U); + repeater.Replace(wxT("_"), wxT(" ")); + repeater.resize(LONG_CALLSIGN_LENGTH, wxT(' ')); + repeater.MakeUpper(); + + if (fileFound) { + wxTextFile file; + bool found = file.Open(filename); + if (!found) { + ::fprintf(stderr, "texttransmit: unable to open the file, exiting\n"); + ::wxUninitialize(); + return 1; + } + + text = file.GetFirstLine(); + + file.Close(); + } + + text.resize(20U, wxT(' ')); + + CTextTransmit tt(repeater, text); + bool ret = tt.run(); + + ::wxUninitialize(); + + return ret ? 0 : 1; +} + +CTextTransmit::CTextTransmit(const wxString& callsign, const wxString& text) : +m_socket(wxEmptyString, 0U), +m_callsign(callsign), +m_text(text) +{ +} + +CTextTransmit::~CTextTransmit() +{ +} + +bool CTextTransmit::run() +{ + bool opened = m_socket.open(); + if (!opened) + return false; + + in_addr address = CUDPReaderWriter::lookup(wxT("127.0.0.1")); + + unsigned int id = CHeaderData::createId(); + + wxString callsignG = m_callsign.Left(LONG_CALLSIGN_LENGTH - 1U); + callsignG.Append(wxT("G")); + + CHeaderData header; + header.setId(id); + header.setMyCall1(m_callsign); + header.setMyCall2(wxT("INFO")); + header.setRptCall1(callsignG); + header.setRptCall2(m_callsign); + header.setYourCall(wxT("CQCQCQ ")); + header.setDestination(address, G2_DV_PORT); + + sendHeader(header); + + CSlowDataEncoder encoder; + encoder.setHeaderData(header); + encoder.setTextData(m_text); + + CAMBEData data; + data.setDestination(address, G2_DV_PORT); + data.setId(id); + + wxStopWatch timer; + timer.Start(); + + unsigned int out = 0U; + + for (;;) { + unsigned int needed = timer.Time() / DSTAR_FRAME_TIME_MS; + + while (out < needed) { + data.setSeq(out); + + unsigned char buffer[DV_FRAME_LENGTH_BYTES]; + ::memcpy(buffer + 0U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + + // Insert sync bytes when the sequence number is zero, slow data otherwise + if (out == 0U) { + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); + encoder.sync(); + } else { + encoder.getTextData(buffer + VOICE_FRAME_LENGTH_BYTES); + } + + data.setData(buffer, DV_FRAME_LENGTH_BYTES); + + sendData(data); + out++; + + if (out == 21U) { + data.setData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES); + data.setSeq(0U); + data.setEnd(true); + + sendData(data); + + m_socket.close(); + + return true; + } + } + + ::wxMilliSleep(10UL); + } +} + +bool CTextTransmit::sendHeader(const CHeaderData& header) +{ + unsigned char buffer[60U]; + unsigned int length = header.getG2Data(buffer, 60U, true); + + for (unsigned int i = 0U; i < 2U; i++) { + bool res = m_socket.write(buffer, length, header.getYourAddress(), header.getYourPort()); + if (!res) + return false; + } + + return true; +} + +bool CTextTransmit::sendData(const CAMBEData& data) +{ + unsigned char buffer[40U]; + unsigned int length = data.getG2Data(buffer, 40U); + + return m_socket.write(buffer, length, data.getYourAddress(), data.getYourPort()); +} diff --git a/TextTransmit/TextTransmit.h b/TextTransmit/TextTransmit.h new file mode 100644 index 0000000..cd6e0a1 --- /dev/null +++ b/TextTransmit/TextTransmit.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 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. + */ + +#ifndef TextTransmit_H +#define TextTransmit_H + +#include "UDPReaderWriter.h" +#include "HeaderData.h" +#include "AMBEData.h" + +class CTextTransmit { +public: + CTextTransmit(const wxString& callsign, const wxString& text); + ~CTextTransmit(); + + bool run(); + +private: + CUDPReaderWriter m_socket; + wxString m_callsign; + wxString m_text; + + bool sendHeader(const CHeaderData& header); + bool sendData(const CAMBEData& data); +}; + +#endif diff --git a/TextTransmit/TextTransmit.vcxproj b/TextTransmit/TextTransmit.vcxproj new file mode 100644 index 0000000..9f636d8 --- /dev/null +++ b/TextTransmit/TextTransmit.vcxproj @@ -0,0 +1,185 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {AFB9BCE5-0D37-4707-93A1-DA14E39F2773} + TextTransmit + Win32Proj + 10.0.16299.0 + + + + Application + v141 + Unicode + true + + + Application + v141 + Unicode + true + + + Application + v141 + Unicode + + + Application + v141 + Unicode + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>14.0.24720.0 + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + $(WXWIN)\include;$(WXWIN)\lib\vc_dll\mswud;$(IncludePath) + + + true + $(WXWIN)\include;$(WXWIN)\lib\vc_x64_dll\mswud;$(IncludePath) + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + + + false + + + + Disabled + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATEWIN32;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + Level4 + EditAndContinue + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_dll;%(AdditionalLibraryDirectories) + true + Console + MachineX86 + + + + + Disabled + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATEWIN32;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + + + Level4 + ProgramDatabase + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_x64_dll;%(AdditionalLibraryDirectories) + true + Console + + + + + MaxSpeed + true + $(WXWIN)\include\msvc;$(WXWIN)\include;../ircDDB;../Common;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + Level3 + ProgramDatabase + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_dll;%(AdditionalLibraryDirectories) + true + Console + true + true + MachineX86 + + + + + MaxSpeed + true + $(WXWIN)\include\msvc;$(WXWIN)\include;../ircDDB;../Common;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_x64_dll;%(AdditionalLibraryDirectories) + true + Console + true + true + + + + + + + + + + + {e793cb8e-2ac9-431a-bbfc-3f52537bb3cf} + false + + + + + + \ No newline at end of file diff --git a/TextTransmit/TextTransmit.vcxproj.filters b/TextTransmit/TextTransmit.vcxproj.filters new file mode 100644 index 0000000..a917404 --- /dev/null +++ b/TextTransmit/TextTransmit.vcxproj.filters @@ -0,0 +1,23 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + + + Header Files + + + \ No newline at end of file diff --git a/TimeServer/TimeServer.vcxproj b/TimeServer/TimeServer.vcxproj new file mode 100644 index 0000000..ff96b71 --- /dev/null +++ b/TimeServer/TimeServer.vcxproj @@ -0,0 +1,206 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {99F336A5-6E33-4919-BF75-D6D8BA26345E} + TimeServer + Win32Proj + 10.0.16299.0 + + + + Application + v141 + Unicode + true + + + Application + v141 + Unicode + true + + + Application + v141 + Unicode + + + Application + v141 + Unicode + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>14.0.24720.0 + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + $(WXWIN)\include;$(WXWIN)\lib\vc_dll\mswud;$(IncludePath) + + + true + $(WXWIN)\include;$(WXWIN)\lib\vc_x64_dll\mswud;$(IncludePath) + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + + + false + + + + Disabled + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;../GUICommon;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + Level4 + EditAndContinue + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_dll;%(AdditionalLibraryDirectories) + true + Windows + MachineX86 + + + + + Disabled + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;../GUICommon;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + + + Level4 + ProgramDatabase + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_x64_dll;%(AdditionalLibraryDirectories) + true + Windows + + + + + MaxSpeed + true + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;../GUICommon;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + Level3 + ProgramDatabase + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_dll;%(AdditionalLibraryDirectories) + true + Windows + true + true + MachineX86 + + + + + MaxSpeed + true + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;../GUICommon;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_x64_dll;%(AdditionalLibraryDirectories) + true + Windows + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + {e793cb8e-2ac9-431a-bbfc-3f52537bb3cf} + false + + + {02d03515-0bbe-4553-8c40-566a597478f8} + false + + + + + + \ No newline at end of file diff --git a/TimeServer/TimeServer.vcxproj.filters b/TimeServer/TimeServer.vcxproj.filters new file mode 100644 index 0000000..d6e951c --- /dev/null +++ b/TimeServer/TimeServer.vcxproj.filters @@ -0,0 +1,74 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/TimeServer/TimeServerAnnouncementsSet.cpp b/TimeServer/TimeServerAnnouncementsSet.cpp new file mode 100644 index 0000000..37bbd22 --- /dev/null +++ b/TimeServer/TimeServerAnnouncementsSet.cpp @@ -0,0 +1,116 @@ +/* + * 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 + * 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 "TimeServerAnnouncementsSet.h" +#include "DStarDefines.h" + +const unsigned int BORDER_SIZE = 5U; +const unsigned int CONTROL_WIDTH = 150U; + +CTimeServerAnnouncementsSet::CTimeServerAnnouncementsSet(wxWindow* parent, int id, const wxString& title, LANGUAGE language, FORMAT format, INTERVAL interval) : +wxPanel(parent, id), +m_title(title), +m_language(NULL), +m_format(NULL), +m_interval(NULL) +{ + wxFlexGridSizer* sizer = new wxFlexGridSizer(2); + + wxStaticText* languageLabel = new wxStaticText(this, -1, _("Language")); + sizer->Add(languageLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_language = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + m_language->Append(wxT("English 1 (UK)")); + m_language->Append(wxT("English 2 (UK)")); + m_language->Append(wxT("English 1 (US)")); + m_language->Append(wxT("English 2 (US)")); + m_language->Append(wxT("Deutsch 1")); + m_language->Append(wxT("Deutsch 2")); + m_language->Append(wxT("Francais")); + m_language->Append(wxT("Nederlands")); + m_language->Append(wxT("Svenska")); + m_language->Append(wxT("Espanol")); + m_language->Append(wxT("Norsk")); + m_language->Append(wxT("Portugues")); + sizer->Add(m_language, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_language->SetSelection(int(language)); + + wxStaticText* formatLabel = new wxStaticText(this, -1, _("Format")); + sizer->Add(formatLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_format = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + m_format->Append(_("Voice, time only")); + m_format->Append(_("Voice, callsign + time")); + m_format->Append(_("Text, time only")); + sizer->Add(m_format, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_format->SetSelection(int(format)); + + wxStaticText* intervalLabel = new wxStaticText(this, -1, _("Interval")); + sizer->Add(intervalLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_interval = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + m_interval->Append(_("Every 15 minutes")); + m_interval->Append(_("Every 30 minutes")); + m_interval->Append(_("Every hour")); + sizer->Add(m_interval, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_interval->SetSelection(int(interval)); + + SetAutoLayout(true); + + SetSizer(sizer); +} + + +CTimeServerAnnouncementsSet::~CTimeServerAnnouncementsSet() +{ +} + +bool CTimeServerAnnouncementsSet::Validate() +{ + if (m_language->GetCurrentSelection() == wxNOT_FOUND) + return false; + + return m_interval->GetCurrentSelection() != wxNOT_FOUND; +} + +LANGUAGE CTimeServerAnnouncementsSet::getLanguage() const +{ + int n = m_language->GetCurrentSelection(); + if (n == wxNOT_FOUND) + return LANG_ENGLISH_UK_1; + + return LANGUAGE(n); +} + +FORMAT CTimeServerAnnouncementsSet::getFormat() const +{ + int n = m_format->GetCurrentSelection(); + if (n == wxNOT_FOUND) + return FORMAT_VOICE_TIME; + + return FORMAT(n); +} + +INTERVAL CTimeServerAnnouncementsSet::getInterval() const +{ + int n = m_interval->GetCurrentSelection(); + if (n == wxNOT_FOUND) + return INTERVAL_15MINS; + + return INTERVAL(n); +} diff --git a/TimeServer/TimeServerAnnouncementsSet.h b/TimeServer/TimeServerAnnouncementsSet.h new file mode 100644 index 0000000..621ec8e --- /dev/null +++ b/TimeServer/TimeServerAnnouncementsSet.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2012 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 TimeServerAnnouncementsSet_H +#define TimeServerAnnouncementsSet_H + +#include "TimeServerDefs.h" + +#include + +class CTimeServerAnnouncementsSet : public wxPanel { +public: + CTimeServerAnnouncementsSet(wxWindow* parent, int id, const wxString& title, LANGUAGE language, FORMAT format, INTERVAL interval); + virtual ~CTimeServerAnnouncementsSet(); + + virtual bool Validate(); + + virtual LANGUAGE getLanguage() const; + + virtual FORMAT getFormat() const; + + virtual INTERVAL getInterval() const; + +private: + wxString m_title; + wxChoice* m_language; + wxChoice* m_format; + wxChoice* m_interval; +}; + +#endif diff --git a/TimeServer/TimeServerApp.cpp b/TimeServer/TimeServerApp.cpp new file mode 100644 index 0000000..f1ec9f6 --- /dev/null +++ b/TimeServer/TimeServerApp.cpp @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2012,2014,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 + * 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 "TimeServerLogRedirect.h" +#include "TimeServerThread.h" +#include "TimeServerApp.h" +#include "Version.h" +#include "Logger.h" + +#include +#include +#include + +IMPLEMENT_APP(CTimeServerApp) + +const wxChar* NAME_PARAM = wxT("Name"); +const wxChar* NOLOGGING_SWITCH = wxT("nolog"); +const wxChar* GUI_SWITCH = wxT("gui"); +const wxChar* LOGDIR_OPTION = wxT("logdir"); +const wxChar* CONFDIR_OPTION = wxT("confdir"); + +static const wxString LOG_BASE_NAME ="timeserver"; + +CTimeServerApp::CTimeServerApp() : +wxApp(), +m_name(), +m_nolog(false), +m_gui(false), +m_logDir(), +m_confDir(), +m_frame(NULL), +m_thread(NULL), +m_config(NULL), +m_logChain(NULL) +{ +} + +CTimeServerApp::~CTimeServerApp() +{ +} + +bool CTimeServerApp::OnInit() +{ + SetVendorName(VENDOR_NAME); + + if (!wxApp::OnInit()) + return false; + + if (!m_nolog) { + wxString logBaseName = LOG_BASE_NAME; + if (!m_name.IsEmpty()) { + logBaseName.Append(wxT("_")); + logBaseName.Append(m_name); + } + +#if defined(__WINDOWS__) + if (m_logDir.IsEmpty()) + m_logDir = wxFileName::GetHomeDir(); +#else + if (m_logDir.IsEmpty()) + m_logDir = wxT(LOG_DIR); +#endif + + wxLog* log = new CLogger(m_logDir, logBaseName); + wxLog::SetActiveTarget(log); + } else { + new wxLogNull; + } + + m_logChain = new wxLogChain(new CTimeServerLogRedirect); + +#if defined(__WINDOWS__) + m_config = new CTimeServerConfig(new wxConfig(APPLICATION_NAME), m_name); +#else + if (m_confDir.IsEmpty()) + m_confDir = wxT(CONF_DIR); + + m_config = new CTimeServerConfig(m_confDir, m_name); +#endif + + wxString frameName = APPLICATION_NAME + wxT(" - "); + if (!m_name.IsEmpty()) { + frameName.Append(m_name); + frameName.Append(wxT(" - ")); + } + frameName.Append(VERSION); + + wxPoint position = wxDefaultPosition; + + int x, y; + getPosition(x, y); + if (x >= 0 && y >= 0) + position = wxPoint(x, y); + + m_frame = new CTimeServerFrame(frameName, position, m_gui); + m_frame->Show(); + + SetTopWindow(m_frame); + + wxLogInfo(wxT("Starting ") + APPLICATION_NAME + wxT(" - ") + VERSION); + + // Log the version of wxWidgets and the Operating System + wxLogInfo(wxT("Using wxWidgets %d.%d.%d on %s"), wxMAJOR_VERSION, wxMINOR_VERSION, wxRELEASE_NUMBER, ::wxGetOsDescription().c_str()); + + createThread(); + + return true; +} + +int CTimeServerApp::OnExit() +{ + m_logChain->SetLog(NULL); + + wxLogInfo(APPLICATION_NAME + wxT(" is exiting")); + + m_thread->kill(); + + delete m_config; + + return 0; +} + +void CTimeServerApp::OnInitCmdLine(wxCmdLineParser& parser) +{ + parser.AddSwitch(NOLOGGING_SWITCH, wxEmptyString, wxEmptyString, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddSwitch(GUI_SWITCH, wxEmptyString, wxEmptyString, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption(LOGDIR_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption(CONFDIR_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddParam(NAME_PARAM, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + + wxApp::OnInitCmdLine(parser); +} + +bool CTimeServerApp::OnCmdLineParsed(wxCmdLineParser& parser) +{ + if (!wxApp::OnCmdLineParsed(parser)) + return false; + + m_nolog = parser.Found(NOLOGGING_SWITCH); + m_gui = parser.Found(GUI_SWITCH); + + wxString logDir; + bool found = parser.Found(LOGDIR_OPTION, &logDir); + if (found) + m_logDir = logDir; + + wxString confDir; + found = parser.Found(CONFDIR_OPTION, &confDir); + if (found) + m_confDir = confDir; + + if (parser.GetParamCount() > 0U) + m_name = parser.GetParam(0U); + + return true; +} + +#if defined(__WXDEBUG__) +void CTimeServerApp::OnAssertFailure(const wxChar* file, int line, const wxChar* func, const wxChar* cond, const wxChar* msg) +{ + wxLogFatalError(wxT("Assertion failed on line %d in file %s and function %s: %s %s"), line, file, func, cond, msg); +} +#endif + +void CTimeServerApp::showLog(const wxString& text) +{ + m_frame->showLog(text); +} + +void CTimeServerApp::getGateway(wxString& callsign, bool& sendA, bool& sendB, bool& sendC, bool& sendD, bool& sendE, wxString& address) const +{ + m_config->getGateway(callsign, sendA, sendB, sendC, sendD, sendE, address); +} + +void CTimeServerApp::setGateway(const wxString& callsign, bool sendA, bool sendB, bool sendC, bool sendD, bool sendE, const wxString& address) +{ + m_config->setGateway(callsign, sendA, sendB, sendC, sendD, sendE, address); +} + +void CTimeServerApp::getAnnouncements(LANGUAGE& language, FORMAT& format, INTERVAL& interval) const +{ + m_config->getAnnouncements(language, format, interval); +} + +void CTimeServerApp::setAnnouncements(LANGUAGE language, FORMAT format, INTERVAL interval) +{ + m_config->setAnnouncements(language, format, interval); +} + +void CTimeServerApp::getPosition(int& x, int& y) const +{ + m_config->getPosition(x, y); +} + +void CTimeServerApp::setPosition(int x, int y) +{ + m_config->setPosition(x, y); +} + +bool CTimeServerApp::writeConfig() +{ + return m_config->write(); +} + +void CTimeServerApp::createThread() +{ + CTimeServerThread* thread = new CTimeServerThread; + + wxString callsign, address; + bool sendA, sendB, sendC, sendD, sendE; + getGateway(callsign, sendA, sendB, sendC, sendD, sendE, address); + callsign.MakeUpper(); + thread->setGateway(callsign, sendA, sendB, sendC, sendD, sendE, address); + wxLogInfo(wxT("Callsign set to %s, module %s%s%s%s, address: %s"), callsign.c_str(), sendA ? wxT("A") : wxT(""), sendB ? wxT("B") : wxT(""), sendC ? wxT("C") : wxT(""), sendD ? wxT("D") : wxT(""), sendE ? wxT("E") : wxT(""), address.c_str()); + + LANGUAGE language; + FORMAT format; + INTERVAL interval; + getAnnouncements(language, format, interval); + thread->setAnnouncements(language, format, interval); + wxLogInfo(wxT("Language: %d, format: %d, interval: %d"), int(language), int(format), int(interval)); + + // Convert the worker class into a thread + m_thread = new CTimeServerThreadHelper(thread); + m_thread->start(); +} diff --git a/TimeServer/TimeServerApp.h b/TimeServer/TimeServerApp.h new file mode 100644 index 0000000..d232786 --- /dev/null +++ b/TimeServer/TimeServerApp.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 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 TimeServerApp_H +#define TimeServerApp_H + +#include "TimeServerThreadHelper.h" +#include "TimeServerConfig.h" +#include "TimeServerFrame.h" +#include "TimeServerDefs.h" + +#include + +class CTimeServerApp : public wxApp { + +public: + CTimeServerApp(); + virtual ~CTimeServerApp(); + + virtual bool OnInit(); + virtual int OnExit(); + + virtual void OnInitCmdLine(wxCmdLineParser& parser); + virtual bool OnCmdLineParsed(wxCmdLineParser& parser); + + // This is overridden because dialog boxes from threads are bad news +#if defined(__WXDEBUG__) + virtual void OnAssertFailure(const wxChar* file, int line, const wxChar* func, const wxChar* cond, const wxChar* msg); +#endif + + virtual void showLog(const wxString& text); + + virtual void getGateway(wxString& callsign, bool& sendA, bool& sendB, bool& sendC, bool& sendD, bool& sendE, wxString& address) const; + virtual void setGateway(const wxString& callsign, bool sendA, bool sendB, bool sendC, bool sendD, bool sendE, const wxString& address); + + virtual void getAnnouncements(LANGUAGE& language, FORMAT& format, INTERVAL& interval) const; + virtual void setAnnouncements(LANGUAGE language, FORMAT format, INTERVAL interval); + + virtual void getPosition(int& x, int& y) const; + virtual void setPosition(int x, int y); + + virtual bool writeConfig(); + +private: + wxString m_name; + bool m_nolog; + bool m_gui; + wxString m_logDir; + wxString m_confDir; + CTimeServerFrame* m_frame; + CTimeServerThreadHelper* m_thread; + CTimeServerConfig* m_config; + wxLogChain* m_logChain; + + void createThread(); +}; + +DECLARE_APP(CTimeServerApp) + +#endif diff --git a/TimeServer/TimeServerConfig.cpp b/TimeServer/TimeServerConfig.cpp new file mode 100644 index 0000000..17a71a1 --- /dev/null +++ b/TimeServer/TimeServerConfig.cpp @@ -0,0 +1,331 @@ +/* + * Copyright (C) 2012 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 "TimeServerConfig.h" + +const wxString KEY_CALLSIGN = wxT("callsign"); +const wxString KEY_SENDA = wxT("sendA"); +const wxString KEY_SENDB = wxT("sendB"); +const wxString KEY_SENDC = wxT("sendC"); +const wxString KEY_SENDD = wxT("sendD"); +const wxString KEY_SENDE = wxT("sendE"); +const wxString KEY_ADDRESS = wxT("address"); +const wxString KEY_LANGUAGE = wxT("language"); +const wxString KEY_FORMAT = wxT("format"); +const wxString KEY_INTERVAL = wxT("interval"); +const wxString KEY_WINDOW_X = wxT("windowX"); +const wxString KEY_WINDOW_Y = wxT("windowY"); + + +const wxString DEFAULT_CALLSIGN = wxEmptyString; +const bool DEFAULT_SENDA = false; +const bool DEFAULT_SENDB = false; +const bool DEFAULT_SENDC = false; +const bool DEFAULT_SENDD = false; +const bool DEFAULT_SENDE = false; +const wxString DEFAULT_ADDRESS = wxT("127.0.0.1"); +const LANGUAGE DEFAULT_LANGUAGE = LANG_ENGLISH_UK_1; +const FORMAT DEFAULT_FORMAT = FORMAT_VOICE_TIME; +const INTERVAL DEFAULT_INTERVAL = INTERVAL_15MINS; +const int DEFAULT_WINDOW_X = -1; +const int DEFAULT_WINDOW_Y = -1; + + +#if defined(__WINDOWS__) + +CTimeServerConfig::CTimeServerConfig(wxConfigBase* config, const wxString& name) : +m_config(config), +m_name(wxT("/")), +m_callsign(DEFAULT_CALLSIGN), +m_sendA(DEFAULT_SENDA), +m_sendB(DEFAULT_SENDB), +m_sendC(DEFAULT_SENDC), +m_sendD(DEFAULT_SENDD), +m_sendE(DEFAULT_SENDE), +m_address(DEFAULT_ADDRESS), +m_language(DEFAULT_LANGUAGE), +m_format(DEFAULT_FORMAT), +m_interval(DEFAULT_INTERVAL), +m_x(DEFAULT_WINDOW_X), +m_y(DEFAULT_WINDOW_Y) +{ + wxASSERT(config != NULL); + + if (!name.IsEmpty()) + m_name = wxT("/") + name + wxT("/"); + + m_config->Read(m_name + KEY_CALLSIGN, &m_callsign, DEFAULT_CALLSIGN); + + m_config->Read(m_name + KEY_SENDA, &m_sendA, DEFAULT_SENDA); + + m_config->Read(m_name + KEY_SENDB, &m_sendB, DEFAULT_SENDB); + + m_config->Read(m_name + KEY_SENDC, &m_sendC, DEFAULT_SENDC); + + m_config->Read(m_name + KEY_SENDD, &m_sendD, DEFAULT_SENDD); + + m_config->Read(m_name + KEY_SENDE, &m_sendE, DEFAULT_SENDE); + + m_config->Read(m_name + KEY_ADDRESS, &m_address, DEFAULT_ADDRESS); + + long temp; + m_config->Read(m_name + KEY_LANGUAGE, &temp, long(DEFAULT_LANGUAGE)); + m_language = LANGUAGE(temp); + + m_config->Read(m_name + KEY_FORMAT, &temp, long(DEFAULT_FORMAT)); + m_format = FORMAT(temp); + + m_config->Read(m_name + KEY_INTERVAL, &temp, long(DEFAULT_INTERVAL)); + m_interval = INTERVAL(temp); + + m_config->Read(m_name + KEY_WINDOW_X, &temp, long(DEFAULT_WINDOW_X)); + m_x = int(temp); + + m_config->Read(m_name + KEY_WINDOW_Y, &temp, long(DEFAULT_WINDOW_Y)); + m_y = int(temp); +} + +CTimeServerConfig::~CTimeServerConfig() +{ + delete m_config; +} + +#else + +CTimeServerConfig::CTimeServerConfig(const wxString& dir, const wxString& name) : +m_fileName(), +m_callsign(DEFAULT_CALLSIGN), +m_sendA(DEFAULT_SENDA), +m_sendB(DEFAULT_SENDB), +m_sendC(DEFAULT_SENDC), +m_sendD(DEFAULT_SENDD), +m_sendE(DEFAULT_SENDE), +m_address(DEFAULT_ADDRESS), +m_language(DEFAULT_LANGUAGE), +m_format(DEFAULT_FORMAT), +m_interval(DEFAULT_INTERVAL), +m_x(DEFAULT_WINDOW_X), +m_y(DEFAULT_WINDOW_Y) +{ + wxASSERT(!dir.IsEmpty()); + + wxString fileName = CONFIG_FILE_NAME; + if (!name.IsEmpty()) + fileName = CONFIG_FILE_NAME + wxT("_") + name; + + m_fileName.Assign(dir, fileName); + + wxTextFile file(m_fileName.GetFullPath()); + + bool exists = file.Exists(); + if (!exists) + return; + + bool ret = file.Open(); + if (!ret) { + wxLogError(wxT("Cannot open the config file - %s"), m_fileName.GetFullPath().c_str()); + return; + } + + long temp; + + wxString str = file.GetFirstLine(); + + while (!file.Eof()) { + if (str.GetChar(0U) == wxT('#')) { + str = file.GetNextLine(); + continue; + } + + int n = str.Find(wxT('=')); + if (n == wxNOT_FOUND) { + str = file.GetNextLine(); + continue; + } + + wxString key = str.Left(n); + wxString val = str.Mid(n + 1U); + + if (key.IsSameAs(KEY_CALLSIGN)) { + m_callsign = val; + } else if (key.IsSameAs(KEY_SENDA)) { + val.ToLong(&temp); + m_sendA = temp == 1L; + } else if (key.IsSameAs(KEY_SENDB)) { + val.ToLong(&temp); + m_sendB = temp == 1L; + } else if (key.IsSameAs(KEY_SENDC)) { + val.ToLong(&temp); + m_sendC = temp == 1L; + } else if (key.IsSameAs(KEY_SENDD)) { + val.ToLong(&temp); + m_sendD = temp == 1L; + } else if (key.IsSameAs(KEY_SENDE)) { + val.ToLong(&temp); + m_sendE = temp == 1L; + } else if (key.IsSameAs(KEY_ADDRESS)) { + m_address = val; + } else if (key.IsSameAs(KEY_LANGUAGE)) { + val.ToLong(&temp); + m_language = LANGUAGE(temp); + } else if (key.IsSameAs(KEY_FORMAT)) { + val.ToLong(&temp); + m_format = FORMAT(temp); + } else if (key.IsSameAs(KEY_INTERVAL)) { + val.ToLong(&temp); + m_interval = INTERVAL(temp); + } else if (key.IsSameAs(KEY_WINDOW_X)) { + val.ToLong(&temp); + m_x = int(temp); + } else if (key.IsSameAs(KEY_WINDOW_Y)) { + val.ToLong(&temp); + m_y = int(temp); + } + + str = file.GetNextLine(); + } + + file.Close(); +} + +CTimeServerConfig::~CTimeServerConfig() +{ +} + +#endif + +void CTimeServerConfig::getGateway(wxString& callsign, bool& sendA, bool& sendB, bool& sendC, bool& sendD, bool& sendE, wxString& address) const +{ + callsign = m_callsign; + sendA = m_sendA; + sendB = m_sendB; + sendC = m_sendC; + sendD = m_sendD; + sendE = m_sendE; + address = m_address; +} + +void CTimeServerConfig::setGateway(const wxString& callsign, bool sendA, bool sendB, bool sendC, bool sendD, bool sendE, const wxString& address) +{ + m_callsign = callsign; + m_sendA = sendA; + m_sendB = sendB; + m_sendC = sendC; + m_sendD = sendD; + m_sendE = sendE; + m_address = address; +} + +void CTimeServerConfig::getAnnouncements(LANGUAGE& language, FORMAT& format, INTERVAL& interval) const +{ + language = m_language; + format = m_format; + interval = m_interval; +} + +void CTimeServerConfig::setAnnouncements(LANGUAGE language, FORMAT format, INTERVAL interval) +{ + m_language = language; + m_format = format; + m_interval = interval; +} + +void CTimeServerConfig::getPosition(int& x, int& y) const +{ + x = m_x; + y = m_y; +} + +void CTimeServerConfig::setPosition(int x, int y) +{ + m_x = x; + m_y = y; +} + +#if defined(__WINDOWS__) + +bool CTimeServerConfig::write() +{ + m_config->Write(m_name + KEY_CALLSIGN, m_callsign); + m_config->Write(m_name + KEY_SENDA, m_sendA); + m_config->Write(m_name + KEY_SENDB, m_sendB); + m_config->Write(m_name + KEY_SENDC, m_sendC); + m_config->Write(m_name + KEY_SENDD, m_sendD); + m_config->Write(m_name + KEY_SENDE, m_sendE); + m_config->Write(m_name + KEY_ADDRESS, m_address); + m_config->Write(m_name + KEY_LANGUAGE, long(m_language)); + m_config->Write(m_name + KEY_FORMAT, long(m_format)); + m_config->Write(m_name + KEY_INTERVAL, long(m_interval)); + m_config->Write(m_name + KEY_WINDOW_X, long(m_x)); + m_config->Write(m_name + KEY_WINDOW_Y, long(m_y)); + m_config->Flush(); + + return true; +} + +#else + +bool CTimeServerConfig::write() +{ + wxTextFile file(m_fileName.GetFullPath()); + + bool exists = file.Exists(); + if (exists) { + bool ret = file.Open(); + if (!ret) { + wxLogError(wxT("Cannot open the config file - %s"), m_fileName.GetFullPath().c_str()); + return false; + } + + // Remove the existing file entries + file.Clear(); + } else { + bool ret = file.Create(); + if (!ret) { + wxLogError(wxT("Cannot create the config file - %s"), m_fileName.GetFullPath().c_str()); + return false; + } + } + + wxString buffer; + buffer.Printf(wxT("%s=%s"), KEY_CALLSIGN.c_str(), m_callsign.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_SENDA.c_str(), m_sendA ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_SENDB.c_str(), m_sendB ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_SENDC.c_str(), m_sendC ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_SENDD.c_str(), m_sendD ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_SENDE.c_str(), m_sendE ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_ADDRESS.c_str(), m_address.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_LANGUAGE.c_str(), int(m_language)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_FORMAT.c_str(), int(m_format)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_INTERVAL.c_str(), int(m_interval)); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_WINDOW_X.c_str(), m_x); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_WINDOW_Y.c_str(), m_y); file.AddLine(buffer); + + bool ret = file.Write(); + if (!ret) { + file.Close(); + wxLogError(wxT("Cannot write the config file - %s"), m_fileName.GetFullPath().c_str()); + return false; + } + + file.Close(); + + return true; +} + +#endif diff --git a/TimeServer/TimeServerConfig.h b/TimeServer/TimeServerConfig.h new file mode 100644 index 0000000..869ed11 --- /dev/null +++ b/TimeServer/TimeServerConfig.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2012 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 TimeServerConfig_H +#define TimeServerConfig_H + +#include "TimeServerDefs.h" + +#include +#include +#include + +class CTimeServerConfig { +public: +#if defined(__WINDOWS__) + CTimeServerConfig(wxConfigBase* config, const wxString& name); +#else + CTimeServerConfig(const wxString& dir, const wxString& name); +#endif + ~CTimeServerConfig(); + + void getGateway(wxString& callsign, bool& sendA, bool& sendB, bool& sendC, bool& sendD, bool& sendE, wxString& address) const; + void setGateway(const wxString& callsign, bool sendA, bool sendB, bool sendC, bool sendD, bool sendE, const wxString& address); + + void getAnnouncements(LANGUAGE& language, FORMAT& format, INTERVAL& interval) const; + void setAnnouncements(LANGUAGE language, FORMAT format, INTERVAL interval); + + void getPosition(int& x, int& y) const; + void setPosition(int x, int y); + + bool write(); + +private: +#if defined(__WINDOWS__) + wxConfigBase* m_config; + wxString m_name; +#else + wxFileName m_fileName; +#endif + wxString m_callsign; + bool m_sendA; + bool m_sendB; + bool m_sendC; + bool m_sendD; + bool m_sendE; + wxString m_address; + LANGUAGE m_language; + FORMAT m_format; + INTERVAL m_interval; + int m_x; + int m_y; +}; + +#endif diff --git a/TimeServer/TimeServerD.cpp b/TimeServer/TimeServerD.cpp new file mode 100644 index 0000000..4ede2cc --- /dev/null +++ b/TimeServer/TimeServerD.cpp @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2012-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 + * 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 "TimeServerConfig.h" +#include "TimeServerD.h" +#include "Version.h" +#include "Logger.h" + +#include +#include +#include +#include + +#include +#include +#include + +const wxChar* NAME_PARAM = wxT("Name"); +const wxChar* NOLOGGING_SWITCH = wxT("nolog"); +const wxChar* LOGDIR_OPTION = wxT("logdir"); +const wxChar* CONFDIR_OPTION = wxT("confdir"); +const wxChar* DAEMON_SWITCH = wxT("daemon"); + +static const wxString LOG_BASE_NAME = "timeserverd"; + +int main(int argc, char** argv) +{ + bool res = ::wxInitialize(); + if (!res) { + ::fprintf(stderr, "timeserverd: failed to initialise the wxWidgets library, exiting\n"); + return -1; + } + + wxCmdLineParser parser(argc, argv); + parser.AddSwitch(NOLOGGING_SWITCH, wxEmptyString, wxEmptyString, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddSwitch(DAEMON_SWITCH, wxEmptyString, wxEmptyString, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption(LOGDIR_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption(CONFDIR_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddParam(NAME_PARAM, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + + int cmd = parser.Parse(); + if (cmd != 0) { + ::wxUninitialize(); + return 0; + } + + bool nolog = parser.Found(NOLOGGING_SWITCH); + bool daemon = parser.Found(DAEMON_SWITCH); + + wxString logDir; + bool found = parser.Found(LOGDIR_OPTION, &logDir); + if (!found) + logDir.Clear(); + + wxString confDir; + found = parser.Found(CONFDIR_OPTION, &confDir); + if (!found) + confDir = wxT(CONF_DIR); + + wxString name; + if (parser.GetParamCount() > 0U) + name = parser.GetParam(0U); + + if (daemon) { + pid_t pid = ::fork(); + + if (pid < 0) { + ::fprintf(stderr, "timeserverd: error in fork(), exiting\n"); + ::wxUninitialize(); + return 1; + } + + // If this is the parent, exit + if (pid > 0) + return 0; + + // We are the child from here onwards + ::setsid(); + + ::chdir("/"); + + ::umask(0); + } + + CTimeServerD gateway(nolog, logDir, confDir, name); + + if (!gateway.init()) { + ::wxUninitialize(); + return 1; + } + + gateway.run(); + + ::wxUninitialize(); + return 0; +} + +CTimeServerD::CTimeServerD(bool nolog, const wxString& logDir, const wxString& confDir, const wxString& name) : +m_name(name), +m_nolog(nolog), +m_logDir(logDir), +m_confDir(confDir), +m_thread(NULL) +{ +} + +CTimeServerD::~CTimeServerD() +{ +} + +bool CTimeServerD::init() +{ + if (!m_nolog) { + wxString logBaseName = LOG_BASE_NAME; + if (!m_name.IsEmpty()) { + logBaseName.Append(wxT("_")); + logBaseName.Append(m_name); + } + + if (m_logDir.IsEmpty()) + m_logDir = wxT(LOG_DIR); + + wxLog* log = new CLogger(m_logDir, logBaseName); + wxLog::SetActiveTarget(log); + } else { + new wxLogNull; + } + + wxLogInfo(wxT("Starting ") + APPLICATION_NAME + wxT(" daemon - ") + VERSION); + + // Log the version of wxWidgets and the Operating System + wxLogInfo(wxT("Using wxWidgets %d.%d.%d on %s"), wxMAJOR_VERSION, wxMINOR_VERSION, wxRELEASE_NUMBER, ::wxGetOsDescription().c_str()); + + return createThread(); +} + +void CTimeServerD::run() +{ + m_thread->run(); + + wxLogInfo(APPLICATION_NAME + wxT(" is exiting")); +} + +bool CTimeServerD::createThread() +{ + CTimeServerConfig config(m_confDir, m_name); + + m_thread = new CTimeServerThread; + + wxString callsign, address; + bool sendA, sendB, sendC, sendD, sendE; + config.getGateway(callsign, sendA, sendB, sendC, sendD, sendE, address); + callsign.MakeUpper(); + m_thread->setGateway(callsign, sendA, sendB, sendC, sendD, sendE, address); + wxLogInfo(wxT("Callsign set to %s, module %s%s%s%s, address: %s"), callsign.c_str(), sendA ? wxT("A") : wxT(""), sendB ? wxT("B") : wxT(""), sendC ? wxT("C") : wxT(""), sendD ? wxT("D") : wxT(""), sendE ? wxT("E") : wxT(""), address.c_str()); + + LANGUAGE language; + FORMAT format; + INTERVAL interval; + config.getAnnouncements(language, format, interval); + m_thread->setAnnouncements(language, format, interval); + wxLogInfo(wxT("Language: %d, format: %d, interval: %d"), int(language), int(format), int(interval)); + + return true; +} diff --git a/TimeServer/TimeServerD.h b/TimeServer/TimeServerD.h new file mode 100644 index 0000000..2006dec --- /dev/null +++ b/TimeServer/TimeServerD.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 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 TimeServerD_H +#define TimeServerD_H + +#include "TimeServerThread.h" +#include "TimeServerDefs.h" + +#include +#include + +class CTimeServerD { + +public: + CTimeServerD(bool nolog, const wxString& logDir, const wxString& confDir, const wxString& name); + ~CTimeServerD(); + + bool init(); + + void run(); + +private: + wxString m_name; + bool m_nolog; + wxString m_logDir; + wxString m_confDir; + CTimeServerThread* m_thread; + + bool createThread(); +}; + +#endif diff --git a/TimeServer/TimeServerDefs.h b/TimeServer/TimeServerDefs.h new file mode 100644 index 0000000..85245b4 --- /dev/null +++ b/TimeServer/TimeServerDefs.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2012,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. + */ + +#ifndef TimeServerDefs_H +#define TimeServerDefs_H + +#include + +const wxString APPLICATION_NAME = wxT("Time Server"); + +#if !defined(__WINDOWS__) +const wxString CONFIG_FILE_NAME = wxT("timeserver"); +#endif + +enum LANGUAGE { + LANG_ENGLISH_UK_1, + LANG_ENGLISH_UK_2, + LANG_ENGLISH_US_1, + LANG_ENGLISH_US_2, + LANG_DEUTSCH_1, + LANG_DEUTSCH_2, + LANG_FRANCAIS, + LANG_NEDERLANDS, + LANG_SVENSKA, + LANG_ESPANOL, + LANG_NORSK, + LANG_PORTUGUES +}; + +enum INTERVAL { + INTERVAL_15MINS, + INTERVAL_30MINS, + INTERVAL_60MINS +}; + +enum FORMAT { + FORMAT_VOICE_TIME, + FORMAT_VOICE_ALL, + FORMAT_TEXT_TIME +}; + +#endif diff --git a/TimeServer/TimeServerFrame.cpp b/TimeServer/TimeServerFrame.cpp new file mode 100644 index 0000000..d01b52d --- /dev/null +++ b/TimeServer/TimeServerFrame.cpp @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2012-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 + * 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 "TimeServerPreferences.h" +#include "TimeServerFrame.h" +#include "TimeServerApp.h" +#include "LogEvent.h" +#include "Version.h" + +#if defined(__WINDOWS__) +const unsigned int LOGTEXT_WIDTH = 560U; +#else +const unsigned int LOGTEXT_WIDTH = 700U; +#endif + +const unsigned int BORDER_SIZE = 5U; + +#include +#include + +DEFINE_EVENT_TYPE(LOG_EVENT) + +enum { + Menu_Edit_Preferences = 6000, + Menu_View_Updates +}; + +BEGIN_EVENT_TABLE(CTimeServerFrame, wxFrame) + EVT_MENU(wxID_EXIT, CTimeServerFrame::onQuit) + EVT_MENU(Menu_Edit_Preferences, CTimeServerFrame::onPreferences) + EVT_MENU(Menu_View_Updates, CTimeServerFrame::onUpdates) + EVT_MENU(wxID_ABOUT, CTimeServerFrame::onAbout) + + EVT_CLOSE(CTimeServerFrame::onClose) + + EVT_CUSTOM(LOG_EVENT, wxID_ANY, CTimeServerFrame::onLog) +END_EVENT_TABLE() + +CTimeServerFrame::CTimeServerFrame(const wxString& title, const wxPoint& position, bool gui) : +wxFrame(NULL, -1, title, position), +#if defined(__WXDEBUG__) +m_updates(true) +#else +m_updates(gui) +#endif +{ + SetMenuBar(createMenuBar()); + + wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); + + wxPanel* panel = new wxPanel(this); + + wxBoxSizer* panelSizer = new wxBoxSizer(wxVERTICAL); + + wxStaticBoxSizer* log1Sizer = new wxStaticBoxSizer(new wxStaticBox(panel, -1, _("Log")), wxVERTICAL); + wxBoxSizer* log2Sizer = new wxBoxSizer(wxVERTICAL); + + for (unsigned int i = 0U; i < 10U; i++) { + m_logLine[i] = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(LOGTEXT_WIDTH, -1)); + m_logLine[i]->Wrap(LOGTEXT_WIDTH); + log2Sizer->Add(m_logLine[i], 0, wxTOP | wxLEFT | wxRIGHT, BORDER_SIZE); + } + + log1Sizer->Add(log2Sizer); + panelSizer->Add(log1Sizer, 0, wxALL, BORDER_SIZE); + + panel->SetSizer(panelSizer); + panelSizer->SetSizeHints(panel); + + mainSizer->Add(panel); + + SetSizer(mainSizer); + mainSizer->SetSizeHints(this); +} + +CTimeServerFrame::~CTimeServerFrame() +{ +} + +wxMenuBar* CTimeServerFrame::createMenuBar() +{ + wxMenu* fileMenu = new wxMenu(); + fileMenu->Append(wxID_EXIT, _("Exit")); + + wxMenu* editMenu = new wxMenu(); + editMenu->Append(Menu_Edit_Preferences, _("Preferences...")); + + wxMenu* viewMenu = new wxMenu(); + viewMenu->AppendCheckItem(Menu_View_Updates, _("GUI Updates")); + viewMenu->Check(Menu_View_Updates, m_updates); + + wxMenu* helpMenu = new wxMenu(); + helpMenu->Append(wxID_ABOUT, _("About Time Server")); + + wxMenuBar* menuBar = new wxMenuBar(); + menuBar->Append(fileMenu, _("File")); + menuBar->Append(editMenu, _("Edit")); + menuBar->Append(viewMenu, _("View")); + menuBar->Append(helpMenu, _("Help")); + + return menuBar; +} + +void CTimeServerFrame::onQuit(wxCommandEvent&) +{ + Close(false); +} + +void CTimeServerFrame::onClose(wxCloseEvent&) +{ + int x, y; + GetPosition(&x, &y); + if (x >= 0 && y >= 0) { + ::wxGetApp().setPosition(x, y); + ::wxGetApp().writeConfig(); + } + + Destroy(); +} + +void CTimeServerFrame::onPreferences(wxCommandEvent&) +{ + wxString callsign, address; + bool sendA, sendB, sendC, sendD, sendE; + ::wxGetApp().getGateway(callsign, sendA, sendB, sendC, sendD, sendE, address); + + LANGUAGE language; + FORMAT format; + INTERVAL interval; + ::wxGetApp().getAnnouncements(language, format, interval); + + CTimeServerPreferences dialog1(this, -1, callsign, sendA, sendB, sendC, sendD, sendE, address, language, format, interval); + if (dialog1.ShowModal() != wxID_OK) + return; + + callsign = dialog1.getCallsign(); + sendA = dialog1.getSendA(); + sendB = dialog1.getSendB(); + sendC = dialog1.getSendC(); + sendD = dialog1.getSendD(); + sendE = dialog1.getSendE(); + address = dialog1.getAddress(); + language = dialog1.getLanguage(); + format = dialog1.getFormat(); + interval = dialog1.getInterval(); + + ::wxGetApp().setGateway(callsign, sendA, sendB, sendC, sendD, sendE, address); + ::wxGetApp().setAnnouncements(language, format, interval); + ::wxGetApp().writeConfig(); + + wxMessageDialog dialog2(this, _("The changes made will not take effect\nuntil the application is restarted"), _("DCS Gateway Information"), wxICON_INFORMATION); + dialog2.ShowModal(); +} + +void CTimeServerFrame::onUpdates(wxCommandEvent& event) +{ + m_updates = event.IsChecked(); +} + +void CTimeServerFrame::onAbout(wxCommandEvent&) +{ + wxAboutDialogInfo info; + info.AddDeveloper(wxT("Jonathan Naylor, G4KLX")); + info.SetCopyright(wxT("(C) 2012-2015 using GPL v2 or later")); + info.SetName(APPLICATION_NAME); + info.SetVersion(VERSION); + info.SetDescription(_("This program allows a computer running a gateway\nto have the time announced.")); + + ::wxAboutBox(info); +} + +void CTimeServerFrame::onLog(wxEvent& event) +{ + CLogEvent& logEvent = dynamic_cast(event); + + wxString text; + + text = m_logLine[1U]->GetLabel(); + m_logLine[0U]->SetLabel(text); + + text = m_logLine[2U]->GetLabel(); + m_logLine[1U]->SetLabel(text); + + text = m_logLine[3U]->GetLabel(); + m_logLine[2U]->SetLabel(text); + + text = m_logLine[4U]->GetLabel(); + m_logLine[3U]->SetLabel(text); + + text = m_logLine[5U]->GetLabel(); + m_logLine[4U]->SetLabel(text); + + text = m_logLine[6U]->GetLabel(); + m_logLine[5U]->SetLabel(text); + + text = m_logLine[7U]->GetLabel(); + m_logLine[6U]->SetLabel(text); + + text = m_logLine[8U]->GetLabel(); + m_logLine[7U]->SetLabel(text); + + text = m_logLine[9U]->GetLabel(); + m_logLine[8U]->SetLabel(text); + + text = logEvent.getText(); + m_logLine[9U]->SetLabel(text); +} + +void CTimeServerFrame::showLog(const wxString& text) +{ + if (!m_updates) + return; + + CLogEvent event(text, LOG_EVENT); + + AddPendingEvent(event); +} diff --git a/TimeServer/TimeServerFrame.h b/TimeServer/TimeServerFrame.h new file mode 100644 index 0000000..540dc69 --- /dev/null +++ b/TimeServer/TimeServerFrame.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2012 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 TimeServerFrame_H +#define TimeServerFrame_H + +#include "TimeServerDefs.h" + +#include +#include + +class CTimeServerFrame : public wxFrame { +public: + CTimeServerFrame(const wxString& title, const wxPoint& position, bool gui); + virtual ~CTimeServerFrame(); + + virtual void onQuit(wxCommandEvent& event); + virtual void onPreferences(wxCommandEvent& event); + virtual void onUpdates(wxCommandEvent& event); + virtual void onAbout(wxCommandEvent& event); + virtual void onClose(wxCloseEvent& event); + virtual void onLog(wxEvent& event); + + virtual void showLog(const wxString& text); + +private: + wxStaticText* m_logLine[10]; + bool m_updates; + + DECLARE_EVENT_TABLE() + + wxMenuBar* createMenuBar(); +}; + +#endif diff --git a/TimeServer/TimeServerGatewaySet.cpp b/TimeServer/TimeServerGatewaySet.cpp new file mode 100644 index 0000000..2223af3 --- /dev/null +++ b/TimeServer/TimeServerGatewaySet.cpp @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2012 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 "TimeServerGatewaySet.h" +#include "DStarDefines.h" + +const unsigned int BORDER_SIZE = 5U; +const unsigned int CONTROL_WIDTH = 150U; + +CTimeServerGatewaySet::CTimeServerGatewaySet(wxWindow* parent, int id, const wxString& title, const wxString& callsign, bool sendA, bool sendB, bool sendC, bool sendD, bool sendE, const wxString& address) : +wxPanel(parent, id), +m_title(title), +m_callsign(NULL), +m_address(NULL), +m_sendA(NULL), +m_sendB(NULL), +m_sendC(NULL), +m_sendD(NULL), +m_sendE(NULL) +{ + wxFlexGridSizer* sizer = new wxFlexGridSizer(2); + + wxStaticText* callsignLabel = new wxStaticText(this, -1, _("Callsign")); + sizer->Add(callsignLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_callsign = new CCallsignTextCtrl(this, -1, callsign, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + m_callsign->SetMaxLength(LONG_CALLSIGN_LENGTH - 1U); + sizer->Add(m_callsign, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* addressLabel = new wxStaticText(this, -1, _("Address")); + sizer->Add(addressLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_address = new CAddressTextCtrl(this, -1, address, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + sizer->Add(m_address, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* sendALabel = new wxStaticText(this, -1, _("Module A")); + sizer->Add(sendALabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_sendA = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + m_sendA->Append(_("No")); + m_sendA->Append(_("Yes")); + sizer->Add(m_sendA, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_sendA->SetSelection(sendA ? 1 : 0); + + wxStaticText* sendBLabel = new wxStaticText(this, -1, _("Module B")); + sizer->Add(sendBLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_sendB = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + m_sendB->Append(_("No")); + m_sendB->Append(_("Yes")); + sizer->Add(m_sendB, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_sendB->SetSelection(sendB ? 1 : 0); + + wxStaticText* sendCLabel = new wxStaticText(this, -1, _("Module C")); + sizer->Add(sendCLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_sendC = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + m_sendC->Append(_("No")); + m_sendC->Append(_("Yes")); + sizer->Add(m_sendC, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_sendC->SetSelection(sendC ? 1 : 0); + + wxStaticText* sendDLabel = new wxStaticText(this, -1, _("Module D")); + sizer->Add(sendDLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_sendD = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + m_sendD->Append(_("No")); + m_sendD->Append(_("Yes")); + sizer->Add(m_sendD, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_sendD->SetSelection(sendD ? 1 : 0); + + wxStaticText* sendELabel = new wxStaticText(this, -1, _("Module E")); + sizer->Add(sendELabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_sendE = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + m_sendE->Append(_("No")); + m_sendE->Append(_("Yes")); + sizer->Add(m_sendE, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_sendE->SetSelection(sendE ? 1 : 0); + + SetAutoLayout(true); + + SetSizer(sizer); +} + + +CTimeServerGatewaySet::~CTimeServerGatewaySet() +{ +} + +bool CTimeServerGatewaySet::Validate() +{ + bool res = getCallsign().IsEmpty(); + if (res) { + wxMessageDialog dialog(this, _("The Callsign may not be empty"), _("Time Server Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + res = getAddress().IsEmpty(); + if (res) { + wxMessageDialog dialog(this, _("The Address may not be empty"), _("Time Server Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + if (m_sendA->GetCurrentSelection() == wxNOT_FOUND) + return false; + + if (m_sendB->GetCurrentSelection() == wxNOT_FOUND) + return false; + + if (m_sendC->GetCurrentSelection() == wxNOT_FOUND) + return false; + + if (m_sendD->GetCurrentSelection() == wxNOT_FOUND) + return false; + + return m_sendE->GetCurrentSelection() != wxNOT_FOUND; +} + +wxString CTimeServerGatewaySet::getCallsign() const +{ + wxString callsign = m_callsign->GetValue(); + + callsign.MakeUpper(); + + return callsign; +} + +wxString CTimeServerGatewaySet::getAddress() const +{ + return m_address->GetValue(); +} + +bool CTimeServerGatewaySet::getSendA() const +{ + int n = m_sendA->GetCurrentSelection(); + + return n == 1; +} + +bool CTimeServerGatewaySet::getSendB() const +{ + int n = m_sendB->GetCurrentSelection(); + + return n == 1; +} + +bool CTimeServerGatewaySet::getSendC() const +{ + int n = m_sendC->GetCurrentSelection(); + + return n == 1; +} + +bool CTimeServerGatewaySet::getSendD() const +{ + int n = m_sendD->GetCurrentSelection(); + + return n == 1; +} + +bool CTimeServerGatewaySet::getSendE() const +{ + int n = m_sendE->GetCurrentSelection(); + + return n == 1; +} diff --git a/TimeServer/TimeServerGatewaySet.h b/TimeServer/TimeServerGatewaySet.h new file mode 100644 index 0000000..0a55e17 --- /dev/null +++ b/TimeServer/TimeServerGatewaySet.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2012 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 TimeServerGatewaySet_H +#define TimeServerGatewaySet_H + +#include "CallsignTextCtrl.h" +#include "AddressTextCtrl.h" + +#include + +class CTimeServerGatewaySet : public wxPanel { +public: + CTimeServerGatewaySet(wxWindow* parent, int id, const wxString& title, const wxString& callsign, bool sendA, bool sendB, + bool sendC, bool sendD, bool sendE, const wxString& address); + virtual ~CTimeServerGatewaySet(); + + virtual bool Validate(); + + virtual wxString getCallsign() const; + + virtual wxString getAddress() const; + + virtual bool getSendA() const; + virtual bool getSendB() const; + virtual bool getSendC() const; + virtual bool getSendD() const; + virtual bool getSendE() const; + +private: + wxString m_title; + CCallsignTextCtrl* m_callsign; + CAddressTextCtrl* m_address; + wxChoice* m_sendA; + wxChoice* m_sendB; + wxChoice* m_sendC; + wxChoice* m_sendD; + wxChoice* m_sendE; +}; + +#endif diff --git a/TimeServer/TimeServerLogRedirect.cpp b/TimeServer/TimeServerLogRedirect.cpp new file mode 100644 index 0000000..e30afd5 --- /dev/null +++ b/TimeServer/TimeServerLogRedirect.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2002,2003,2009-2013,2018 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 "TimeServerLogRedirect.h" +#include "TimeServerApp.h" + +CTimeServerLogRedirect::CTimeServerLogRedirect() : +wxLog() +{ +} + +CTimeServerLogRedirect::~CTimeServerLogRedirect() +{ +} + +void CTimeServerLogRedirect::DoLogRecord(wxLogLevel level, const wxString& msg, const wxLogRecordInfo& info) +{ + wxString letter; + + switch (level) { + case wxLOG_FatalError: letter = wxT("F"); break; + case wxLOG_Error: letter = wxT("E"); break; + case wxLOG_Warning: letter = wxT("W"); break; + case wxLOG_Info: letter = wxT("I"); break; + case wxLOG_Message: letter = wxT("M"); break; + case wxLOG_Status: letter = wxT("M"); break; + case wxLOG_Trace: letter = wxT("T"); break; + case wxLOG_Debug: letter = wxT("D"); break; + default: letter = wxT("U"); break; + } + + struct tm* tm = ::gmtime(&info.timestamp); + + wxString message; + message.Printf(wxT("%s: %04d-%02d-%02d %02d:%02d:%02d: %s\n"), letter.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, msg.c_str()); + + ::wxGetApp().showLog(message); + + if (level == wxLOG_FatalError) + ::abort(); +} diff --git a/TimeServer/TimeServerLogRedirect.h b/TimeServer/TimeServerLogRedirect.h new file mode 100644 index 0000000..f66cdd7 --- /dev/null +++ b/TimeServer/TimeServerLogRedirect.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2002,2003,2009-2011,2018 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 TimeServerLogRedirect_H +#define TimeServerLogRedirect_H + +#include +#include + +class CTimeServerLogRedirect : public wxLog { +public: + CTimeServerLogRedirect(); + virtual ~CTimeServerLogRedirect(); + + virtual void DoLogRecord(wxLogLevel level, const wxString& msg, const wxLogRecordInfo& info); + +private: +}; + +#endif diff --git a/TimeServer/TimeServerPreferences.cpp b/TimeServer/TimeServerPreferences.cpp new file mode 100644 index 0000000..d044e9d --- /dev/null +++ b/TimeServer/TimeServerPreferences.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2012 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 "TimeServerPreferences.h" +#include "DStarDefines.h" + +const unsigned int BORDER_SIZE = 5U; +const unsigned int CONTROL_WIDTH = 150U; + +CTimeServerPreferences::CTimeServerPreferences(wxWindow* parent, int id, const wxString& callsign, bool sendA, bool sendB, bool sendC, bool sendD, bool sendE, const wxString& address, LANGUAGE language, FORMAT format, INTERVAL interval) : +wxDialog(parent, id, wxString(_("Time Server Preferences"))), +m_gateway(NULL), +m_announcements(NULL) +{ + wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); + + wxNotebook* noteBook = new wxNotebook(this, -1); + + m_gateway = new CTimeServerGatewaySet(noteBook, -1, APPLICATION_NAME, callsign, sendA, sendB, sendC, sendD, sendE, address); + noteBook->AddPage(m_gateway, _("Gateway"), true); + + m_announcements = new CTimeServerAnnouncementsSet(noteBook, -1, APPLICATION_NAME, language, format, interval); + noteBook->AddPage(m_announcements, _("Announcements"), false); + + mainSizer->Add(noteBook, 1, wxALL | wxGROW, BORDER_SIZE); + + mainSizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + SetAutoLayout(true); + Layout(); + + mainSizer->Fit(this); + mainSizer->SetSizeHints(this); + + SetSizer(mainSizer); +} + + +CTimeServerPreferences::~CTimeServerPreferences() +{ +} + +bool CTimeServerPreferences::Validate() +{ + if (!m_gateway->Validate()) + return false; + + return m_announcements->Validate(); +} + +wxString CTimeServerPreferences::getCallsign() const +{ + return m_gateway->getCallsign(); +} + +wxString CTimeServerPreferences::getAddress() const +{ + return m_gateway->getAddress(); +} + +bool CTimeServerPreferences::getSendA() const +{ + return m_gateway->getSendA(); +} + +bool CTimeServerPreferences::getSendB() const +{ + return m_gateway->getSendB(); +} + +bool CTimeServerPreferences::getSendC() const +{ + return m_gateway->getSendC(); +} + +bool CTimeServerPreferences::getSendD() const +{ + return m_gateway->getSendD(); +} + +bool CTimeServerPreferences::getSendE() const +{ + return m_gateway->getSendE(); +} + +LANGUAGE CTimeServerPreferences::getLanguage() const +{ + return m_announcements->getLanguage(); +} + +FORMAT CTimeServerPreferences::getFormat() const +{ + return m_announcements->getFormat(); +} + +INTERVAL CTimeServerPreferences::getInterval() const +{ + return m_announcements->getInterval(); +} diff --git a/TimeServer/TimeServerPreferences.h b/TimeServer/TimeServerPreferences.h new file mode 100644 index 0000000..2249428 --- /dev/null +++ b/TimeServer/TimeServerPreferences.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2012 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 TimeServerPreferences_H +#define TimeServerPreferences_H + +#include "TimeServerAnnouncementsSet.h" +#include "TimeServerGatewaySet.h" +#include "TimeServerDefs.h" + +#include +#include + +class CTimeServerPreferences : public wxDialog { +public: + CTimeServerPreferences(wxWindow* parent, int id, const wxString& callsign, bool sendA, bool sendB, + bool sendC, bool sendD, bool sendE, const wxString& address, LANGUAGE language, FORMAT format, INTERVAL interval); + virtual ~CTimeServerPreferences(); + + virtual bool Validate(); + + virtual wxString getCallsign() const; + virtual wxString getAddress() const; + virtual bool getSendA() const; + virtual bool getSendB() const; + virtual bool getSendC() const; + virtual bool getSendD() const; + virtual bool getSendE() const; + + virtual LANGUAGE getLanguage() const; + virtual FORMAT getFormat() const; + virtual INTERVAL getInterval() const; + +private: + CTimeServerGatewaySet* m_gateway; + CTimeServerAnnouncementsSet* m_announcements; +}; + +#endif diff --git a/TimeServer/TimeServerThread.cpp b/TimeServer/TimeServerThread.cpp new file mode 100644 index 0000000..dd28cdc --- /dev/null +++ b/TimeServer/TimeServerThread.cpp @@ -0,0 +1,1466 @@ +/* + * 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 + * 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 "TimeServerThread.h" +#include "DStarDefines.h" +#include "Utils.h" + +#include +#include +#include +#include + +const unsigned int MAX_FRAMES = 60U * DSTAR_FRAMES_PER_SEC; + +const unsigned int SILENCE_LENGTH = 10U; + +enum SLOW_DATA { + SD_HEADER, + SD_TEXT +}; + +CTimeServerThread::CTimeServerThread() : +m_socket(wxEmptyString, 0U), +m_callsign(), +m_callsignA(), +m_callsignB(), +m_callsignC(), +m_callsignD(), +m_callsignE(), +m_callsignG(), +m_address(), +m_language(LANG_ENGLISH_UK_1), +m_format(FORMAT_VOICE_TIME), +m_interval(INTERVAL_15MINS), +m_ambe(NULL), +m_ambeLength(0U), +m_index(), +m_seqNo(0U), +m_in(0U), +m_encoder(), +m_data(NULL), +m_killed(false) +{ + m_address.s_addr = INADDR_NONE; + + m_data = new CAMBEData*[MAX_FRAMES]; + + for (unsigned int i = 0U; i < MAX_FRAMES; i++) + m_data[i] = NULL; +} + +CTimeServerThread::~CTimeServerThread() +{ + for (CIndexList_t::iterator it = m_index.begin(); it != m_index.end(); ++it) + delete it->second; + + delete[] m_ambe; + delete[] m_data; +} + +void CTimeServerThread::run() +{ + // Wait here until we have the essentials to run + while (!m_killed && m_address.s_addr == INADDR_NONE && m_callsignA.IsEmpty() && m_callsignB.IsEmpty() && m_callsignC.IsEmpty() && m_callsignD.IsEmpty() && m_callsignE.IsEmpty()) + ::wxMilliSleep(500UL); // 1/2 sec + + if (m_killed) + return; + + if (m_format != FORMAT_TEXT_TIME) { + bool ret = loadAMBE(); + if (!ret) { + wxLogWarning(wxT("Cannot load the AMBE data, using text only time")); + m_format = FORMAT_TEXT_TIME; + } + } + + wxLogMessage(wxT("Starting the Time Server thread")); + + unsigned int lastMin = 0U; + + while (!m_killed) { + time_t now; + ::time(&now); + + struct tm* tm = ::localtime(&now); + + unsigned int hour = tm->tm_hour; + unsigned int min = tm->tm_min; + + if (min != lastMin) { + if (m_interval == INTERVAL_15MINS && (min == 0U || min == 15U || min == 30U || min == 45U)) + sendTime(hour, min); + else if (m_interval == INTERVAL_30MINS && (min == 0U || min == 30U)) + sendTime(hour, min); + else if (m_interval == INTERVAL_60MINS && min == 0U) + sendTime(hour, min); + } + + lastMin = min; + + ::wxMilliSleep(450UL); + } + + wxLogMessage(wxT("Stopping the Time Server thread")); + + m_socket.close(); +} + +void CTimeServerThread::kill() +{ + m_killed = true; +} + +bool CTimeServerThread::setGateway(const wxString& callsign, bool sendA, bool sendB, bool sendC, bool sendD, bool sendE, const wxString& address) +{ + m_callsign = callsign; + m_callsign.resize(LONG_CALLSIGN_LENGTH - 1U, wxT(' ')); + + m_callsignG = m_callsign; + m_callsignG.Append(wxT("G")); + + if (sendA) { + m_callsignA = m_callsign; + m_callsignA.Append(wxT("A")); + } + + if (sendB) { + m_callsignB = m_callsign; + m_callsignB.Append(wxT("B")); + } + + if (sendC) { + m_callsignC = m_callsign; + m_callsignC.Append(wxT("C")); + } + + if (sendD) { + m_callsignD = m_callsign; + m_callsignD.Append(wxT("D")); + } + + if (sendE) { + m_callsignE = m_callsign; + m_callsignE.Append(wxT("E")); + } + + m_callsign.Append(wxT(" ")); + + m_address = CUDPReaderWriter::lookup(address); + + bool ret = m_socket.open(); + if (!ret) + return false; + + return true; +} + +void CTimeServerThread::setAnnouncements(LANGUAGE language, FORMAT format, INTERVAL interval) +{ + m_language = language; + m_format = format; + m_interval = interval; +} + +void CTimeServerThread::sendTime(unsigned int hour, unsigned int min) +{ + wxArrayString words; + + switch (m_language) { + case LANG_ENGLISH_UK_1: + words = sendTimeEnGB1(hour, min); + break; + case LANG_ENGLISH_UK_2: + words = sendTimeEnGB2(hour, min); + break; + case LANG_ENGLISH_US_1: + words = sendTimeEnUS1(hour, min); + break; + case LANG_ENGLISH_US_2: + words = sendTimeEnUS2(hour, min); + break; + case LANG_DEUTSCH_1: + words = sendTimeDeDE1(hour, min); + break; + case LANG_DEUTSCH_2: + words = sendTimeDeDE2(hour, min); + break; + case LANG_FRANCAIS: + words = sendTimeFrFR(hour, min); + break; + case LANG_NEDERLANDS: + words = sendTimeNlNL(hour, min); + break; + case LANG_SVENSKA: + words = sendTimeSeSE(hour, min); + break; + case LANG_ESPANOL: + words = sendTimeEsES(hour, min); + break; + case LANG_NORSK: + words = sendTimeNoNO(hour, min); + break; + case LANG_PORTUGUES: + words = sendTimePtPT(hour, min); + break; + default: + break; + } + + send(words, hour, min); +} + +wxArrayString CTimeServerThread::sendTimeEnGB1(unsigned int hour, unsigned int min) +{ + wxArrayString words; + + words.Add(wxT("It_is")); + + switch (hour) { + case 0U: + case 12U: + words.Add(wxT("twelve")); + break; + case 1U: + case 13U: + words.Add(wxT("one")); + break; + case 2U: + case 14U: + words.Add(wxT("two")); + break; + case 3U: + case 15U: + words.Add(wxT("three")); + break; + case 4U: + case 16U: + words.Add(wxT("four")); + break; + case 5U: + case 17U: + words.Add(wxT("five")); + break; + case 6U: + case 18U: + words.Add(wxT("six")); + break; + case 7U: + case 19U: + words.Add(wxT("seven")); + break; + case 8U: + case 20U: + words.Add(wxT("eight")); + break; + case 9U: + case 21U: + words.Add(wxT("nine")); + break; + case 10U: + case 22U: + words.Add(wxT("ten")); + break; + case 11U: + case 23U: + words.Add(wxT("eleven")); + break; + default: + break; + } + + switch (min) { + case 15U: + words.Add(wxT("fifteen")); + break; + case 30U: + words.Add(wxT("thirty")); + break; + case 45U: + words.Add(wxT("forty-five")); + break; + default: + break; + } + + if (hour >= 12U) + words.Add(wxT("PM")); + else + words.Add(wxT("AM")); + + return words; +} + +wxArrayString CTimeServerThread::sendTimeEnGB2(unsigned int hour, unsigned int min) +{ + wxArrayString words; + + words.Add(wxT("It_is")); + + if (min == 15U) { + words.Add(wxT("a_quarter_past")); + } else if (min == 30U) { + words.Add(wxT("half_past")); + } else if (min == 45U) { + words.Add(wxT("a_quarter_to")); + if (hour == 23U) + hour = 0U; + else + hour++; + } + + if (hour == 0U && min == 0U) { + words.Add(wxT("midnight")); + } else if (hour == 12U && min == 0U) { + words.Add(wxT("twelve")); + words.Add(wxT("noon")); + } else if (hour == 0U || hour == 12U) { + words.Add(wxT("twelve")); + } else if (hour == 1U || hour == 13U) { + words.Add(wxT("one")); + } else if (hour == 2U || hour == 14U) { + words.Add(wxT("two")); + } else if (hour == 3U || hour == 15U) { + words.Add(wxT("three")); + } else if (hour == 4U || hour == 16U) { + words.Add(wxT("four")); + } else if (hour == 5U || hour == 17U) { + words.Add(wxT("five")); + } else if (hour == 6U || hour == 18U) { + words.Add(wxT("six")); + } else if (hour == 7U || hour == 19U) { + words.Add(wxT("seven")); + } else if (hour == 8U || hour == 20U) { + words.Add(wxT("eight")); + } else if (hour == 9U || hour == 21U) { + words.Add(wxT("nine")); + } else if (hour == 10U || hour == 22U) { + words.Add(wxT("ten")); + } else if (hour == 11U || hour == 23U) { + words.Add(wxT("eleven")); + } + + if (hour != 0U && hour != 12U && min == 0U) + words.Add(wxT("O_Clock")); + + return words; +} + +wxArrayString CTimeServerThread::sendTimeEnUS1(unsigned int hour, unsigned int min) +{ + wxArrayString words; + + words.Add(wxT("It_is")); + + switch (hour) { + case 0U: + case 12U: + words.Add(wxT("twelve")); + break; + case 1U: + case 13U: + words.Add(wxT("one")); + break; + case 2U: + case 14U: + words.Add(wxT("two")); + break; + case 3U: + case 15U: + words.Add(wxT("three")); + break; + case 4U: + case 16U: + words.Add(wxT("four")); + break; + case 5U: + case 17U: + words.Add(wxT("five")); + break; + case 6U: + case 18U: + words.Add(wxT("six")); + break; + case 7U: + case 19U: + words.Add(wxT("seven")); + break; + case 8U: + case 20U: + words.Add(wxT("eight")); + break; + case 9U: + case 21U: + words.Add(wxT("nine")); + break; + case 10U: + case 22U: + words.Add(wxT("ten")); + break; + case 11U: + case 23U: + words.Add(wxT("eleven")); + break; + default: + break; + } + + switch (min) { + case 15U: + words.Add(wxT("fifteen")); + break; + case 30U: + words.Add(wxT("thirty")); + break; + case 45U: + words.Add(wxT("forty-five")); + break; + default: + break; + } + + if (hour >= 12U) + words.Add(wxT("PM")); + else + words.Add(wxT("AM")); + + return words; +} + +wxArrayString CTimeServerThread::sendTimeEnUS2(unsigned int hour, unsigned int min) +{ + wxArrayString words; + + words.Add(wxT("It_is")); + + if (min == 15U) { + words.Add(wxT("a_quarter_past")); + } else if (min == 30U) { + words.Add(wxT("half_past")); + } else if (min == 45U) { + words.Add(wxT("a_quarter_to")); + if (hour == 23U) + hour = 0U; + else + hour++; + } + + if (hour == 0U && min == 0U) { + words.Add(wxT("midnight")); + } else if (hour == 12U && min == 0U) { + words.Add(wxT("twelve")); + words.Add(wxT("noon")); + } else if (hour == 0U || hour == 12U) { + words.Add(wxT("twelve")); + } else if (hour == 1U || hour == 13U) { + words.Add(wxT("one")); + } else if (hour == 2U || hour == 14U) { + words.Add(wxT("two")); + } else if (hour == 3U || hour == 15U) { + words.Add(wxT("three")); + } else if (hour == 4U || hour == 16U) { + words.Add(wxT("four")); + } else if (hour == 5U || hour == 17U) { + words.Add(wxT("five")); + } else if (hour == 6U || hour == 18U) { + words.Add(wxT("six")); + } else if (hour == 7U || hour == 19U) { + words.Add(wxT("seven")); + } else if (hour == 8U || hour == 20U) { + words.Add(wxT("eight")); + } else if (hour == 9U || hour == 21U) { + words.Add(wxT("nine")); + } else if (hour == 10U || hour == 22U) { + words.Add(wxT("ten")); + } else if (hour == 11U || hour == 23U) { + words.Add(wxT("eleven")); + } + + if (hour != 0U && hour != 12U && min == 0U) + words.Add(wxT("O_Clock")); + + return words; +} + +wxArrayString CTimeServerThread::sendTimeDeDE1(unsigned int hour, unsigned int min) +{ + wxArrayString words; + + words.Add(wxT("Es_ist")); + + switch (hour) { + case 0U: words.Add(wxT("null")); break; + case 1U: words.Add(wxT("ein")); break; + case 2U: words.Add(wxT("zwei")); break; + case 3U: words.Add(wxT("drei")); break; + case 4U: words.Add(wxT("vier")); break; + case 5U: words.Add(wxT("fuenf")); break; + case 6U: words.Add(wxT("sechs")); break; + case 7U: words.Add(wxT("sieben")); break; + case 8U: words.Add(wxT("acht")); break; + case 9U: words.Add(wxT("neun")); break; + case 10U: words.Add(wxT("zehn")); break; + case 11U: words.Add(wxT("elf")); break; + case 12U: words.Add(wxT("zwoelf")); break; + case 13U: words.Add(wxT("dreizehn")); break; + case 14U: words.Add(wxT("vierzehn")); break; + case 15U: words.Add(wxT("fuenfzehn")); break; + case 16U: words.Add(wxT("sechzehn")); break; + case 17U: words.Add(wxT("siebzehn")); break; + case 18U: words.Add(wxT("achtzehn")); break; + case 19U: words.Add(wxT("neunzehn")); break; + case 20U: words.Add(wxT("zwanzig")); break; + case 21U: words.Add(wxT("einundzwanzig")); break; + case 22U: words.Add(wxT("zweiundzwanzig")); break; + case 23U: words.Add(wxT("dreiundzwanzig")); break; + default: break; + } + + words.Add(wxT("Uhr")); + + switch (min) { + case 15U: + words.Add(wxT("fuenfzehn")); + break; + case 30U: + words.Add(wxT("dreissig")); + break; + case 45U: + words.Add(wxT("fuenfundvierzig")); + break; + default: + break; + } + + return words; +} + +wxArrayString CTimeServerThread::sendTimeDeDE2(unsigned int hour, unsigned int min) +{ + wxArrayString words; + + words.Add(wxT("Es_ist")); + + if (min == 15U) { + words.Add(wxT("viertel_nach")); + } else if (min == 30U) { + words.Add(wxT("halb")); + if (hour == 23U) + hour = 0U; + else + hour++; + } else if (min == 45U) { + words.Add(wxT("viertel_vor")); + if (hour == 23U) + hour = 0U; + else + hour++; + } + + if (hour == 0U) { + words.Add(wxT("null")); + } else if (hour == 1U || hour == 13U) { + words.Add(wxT("ein")); + } else if (hour == 2U || hour == 14U) { + words.Add(wxT("zwei")); + } else if (hour == 3U || hour == 15U) { + words.Add(wxT("drei")); + } else if (hour == 4U || hour == 16U) { + words.Add(wxT("vier")); + } else if (hour == 5U || hour == 17U) { + words.Add(wxT("fuenf")); + } else if (hour == 6U || hour == 18U) { + words.Add(wxT("sechs")); + } else if (hour == 7U || hour == 19U) { + words.Add(wxT("sieben")); + } else if (hour == 8U || hour == 20U) { + words.Add(wxT("acht")); + } else if (hour == 9U || hour == 21U) { + words.Add(wxT("neun")); + } else if (hour == 10U || hour == 22U) { + words.Add(wxT("zehn")); + } else if (hour == 11U || hour == 23U) { + words.Add(wxT("elf")); + } else if (hour == 12U) { + words.Add(wxT("zwoelf")); + } + + if (min == 0U) + words.Add(wxT("Uhr")); + + return words; +} + +wxArrayString CTimeServerThread::sendTimeFrFR(unsigned int hour, unsigned int min) +{ + wxArrayString words; + + // if (hour > 17U) + // words.Add(wxT("bonsoir")); + // else + // words.Add(wxT("bonjour")); + + words.Add(wxT("il_est")); + + if (min == 45U) + hour++; + + if (hour == 0U) { + words.Add(wxT("minuit")); + } else if (hour == 1U || hour == 13U) { + words.Add(wxT("une")); + } else if (hour == 2U || hour == 14U) { + words.Add(wxT("deux")); + } else if (hour == 3U || hour == 15U) { + words.Add(wxT("trois")); + } else if (hour == 4U || hour == 16U) { + words.Add(wxT("quatre")); + } else if (hour == 5U || hour == 17U) { + words.Add(wxT("cinq")); + } else if (hour == 6U || hour == 18U) { + words.Add(wxT("six")); + } else if (hour == 7U || hour == 19U) { + words.Add(wxT("sept")); + } else if (hour == 8U || hour == 20U) { + words.Add(wxT("huit")); + } else if (hour == 9U || hour == 21U) { + words.Add(wxT("neuf")); + } else if (hour == 10U || hour == 22U) { + words.Add(wxT("dix")); + } else if (hour == 11U || hour == 23U) { + words.Add(wxT("onze")); + } else if (hour == 12U) { + words.Add(wxT("midi")); + } + + if (hour == 1U || hour == 13U) + words.Add(wxT("heure")); + else if (hour != 12U && hour != 0U) + words.Add(wxT("heures")); + + if (min == 15U) { + words.Add(wxT("et_quart")); + } else if (min == 30U) { + words.Add(wxT("et_demie")); + } else if (min == 45U) { + words.Add(wxT("moins_le_quart")); + if (hour == 23U) + hour = 0U; + else + hour++; + } + + return words; +} + +wxArrayString CTimeServerThread::sendTimeNlNL(unsigned int hour, unsigned int min) +{ + wxArrayString words; + + words.Add(wxT("Het_is")); + + if (min == 15U) { + words.Add(wxT("kwart_over")); + } else if (min == 30U) { + words.Add(wxT("half")); + if (hour == 23U) + hour = 0U; + else + hour++; + } else if (min == 45U) { + words.Add(wxT("kwart_voor")); + if (hour == 23U) + hour = 0U; + else + hour++; + } + + if (hour == 0U || hour == 12U) { + words.Add(wxT("twaalf")); + } else if (hour == 1U || hour == 13U) { + words.Add(wxT("een")); + } else if (hour == 2U || hour == 14U) { + words.Add(wxT("twee")); + } else if (hour == 3U || hour == 15U) { + words.Add(wxT("drie")); + } else if (hour == 4U || hour == 16U) { + words.Add(wxT("vier")); + } else if (hour == 5U || hour == 17U) { + words.Add(wxT("vijf")); + } else if (hour == 6U || hour == 18U) { + words.Add(wxT("zes")); + } else if (hour == 7U || hour == 19U) { + words.Add(wxT("zeven")); + } else if (hour == 8U || hour == 20U) { + words.Add(wxT("acht")); + } else if (hour == 9U || hour == 21U) { + words.Add(wxT("negen")); + } else if (hour == 10U || hour == 22U) { + words.Add(wxT("tien")); + } else if (hour == 11U || hour == 23U) { + words.Add(wxT("elf")); + } + + if (min == 0U) + words.Add(wxT("uur")); + + return words; +} + +wxArrayString CTimeServerThread::sendTimeSeSE(unsigned int hour, unsigned int min) +{ + wxArrayString words; + + words.Add(wxT("Klockan_ar")); + + if (min == 15U) { + words.Add(wxT("kvart_over")); + } else if (min == 30U) { + words.Add(wxT("halv")); + if (hour == 23U) + hour = 0U; + else + hour++; + } else if (min == 45U) { + words.Add(wxT("kvart_i")); + if (hour == 23U) + hour = 0U; + else + hour++; + } + + if (hour == 0U || hour == 12U) { + words.Add(wxT("tolv")); + } else if (hour == 1U || hour == 13U) { + words.Add(wxT("ett")); + } else if (hour == 2U || hour == 14U) { + words.Add(wxT("tva")); + } else if (hour == 3U || hour == 15U) { + words.Add(wxT("tre")); + } else if (hour == 4U || hour == 16U) { + words.Add(wxT("fyra")); + } else if (hour == 5U || hour == 17U) { + words.Add(wxT("fem")); + } else if (hour == 6U || hour == 18U) { + words.Add(wxT("sex")); + } else if (hour == 7U || hour == 19U) { + words.Add(wxT("sju")); + } else if (hour == 8U || hour == 20U) { + words.Add(wxT("atta")); + } else if (hour == 9U || hour == 21U) { + words.Add(wxT("nio")); + } else if (hour == 10U || hour == 22U) { + words.Add(wxT("tio")); + } else if (hour == 11U || hour == 23U) { + words.Add(wxT("elva")); + } + + return words; +} + +wxArrayString CTimeServerThread::sendTimeEsES(unsigned int hour, unsigned int min) +{ + wxArrayString words; + + if (min == 45U) { + hour++; + if (hour == 24U) + hour = 0U; + } + + if (hour == 1U) + words.Add(wxT("Es_la")); + else if (hour == 0U || hour == 12U) + words.Add(wxT("Es")); + else + words.Add(wxT("Son_las")); + + if (hour == 0U) { + words.Add(wxT("medianoche")); + } else if (hour == 1U || hour == 13U) { + words.Add(wxT("una")); + } else if (hour == 2U || hour == 14U) { + words.Add(wxT("dos")); + } else if (hour == 3U || hour == 15U) { + words.Add(wxT("tres")); + } else if (hour == 4U || hour == 16U) { + words.Add(wxT("cuarto")); + } else if (hour == 5U || hour == 17U) { + words.Add(wxT("cinco")); + } else if (hour == 6U || hour == 18U) { + words.Add(wxT("seis")); + } else if (hour == 7U || hour == 19U) { + words.Add(wxT("siete")); + } else if (hour == 8U || hour == 20U) { + words.Add(wxT("ocho")); + } else if (hour == 9U || hour == 21U) { + words.Add(wxT("nueve")); + } else if (hour == 10U || hour == 22U) { + words.Add(wxT("diez")); + } else if (hour == 11U || hour == 23U) { + words.Add(wxT("once")); + } else { + words.Add(wxT("mediodia")); + } + + if (min == 15U) + words.Add(wxT("y_cuarto")); + else if (min == 30U) + words.Add(wxT("y_media")); + else if (min == 45U) + words.Add(wxT("menos_cuarto")); + + if (hour > 0U && hour < 12U) + words.Add(wxT("de_la_manana")); + else if (hour > 12U && hour < 19U) + words.Add(wxT("de_la_tarde")); + else if (hour >= 19U && hour <= 23U) + words.Add(wxT("de_la_noche")); + + return words; +} + +wxArrayString CTimeServerThread::sendTimeNoNO(unsigned int hour, unsigned int min) +{ + wxArrayString words; + + words.Add(wxT("Klokken_er")); + + if (min == 15U) { + words.Add(wxT("kvart_over")); + } else if (min == 30U) { + words.Add(wxT("halv")); + if (hour == 23U) + hour = 0U; + else + hour++; + } else if (min == 45U) { + words.Add(wxT("kvart_pa")); + if (hour == 23U) + hour = 0U; + else + hour++; + } + + if (hour == 0U || hour == 12U) { + words.Add(wxT("tolv")); + } else if (hour == 1U || hour == 13U) { + words.Add(wxT("ett")); + } else if (hour == 2U || hour == 14U) { + words.Add(wxT("to")); + } else if (hour == 3U || hour == 15U) { + words.Add(wxT("tre")); + } else if (hour == 4U || hour == 16U) { + words.Add(wxT("fire")); + } else if (hour == 5U || hour == 17U) { + words.Add(wxT("fem")); + } else if (hour == 6U || hour == 18U) { + words.Add(wxT("seks")); + } else if (hour == 7U || hour == 19U) { + words.Add(wxT("sju")); + } else if (hour == 8U || hour == 20U) { + words.Add(wxT("atte")); + } else if (hour == 9U || hour == 21U) { + words.Add(wxT("ni")); + } else if (hour == 10U || hour == 22U) { + words.Add(wxT("ti")); + } else if (hour == 11U || hour == 23U) { + words.Add(wxT("elleve")); + } + + return words; +} + +wxArrayString CTimeServerThread::sendTimePtPT(unsigned int hour, unsigned int min) +{ + wxArrayString words; + + if (min == 45U) { + hour++; + if (hour == 24U) + hour = 0U; + } + + if (hour == 1U || hour == 13U) + words.Add(wxT("E")); + else if (hour == 0U || hour == 12U) + words.Add(wxT("Es")); + else + words.Add(wxT("Sao")); + + if (min == 45U) { + if (hour == 0U || hour == 12U || hour == 1U || hour == 13U) + words.Add(wxT("quinze_para")); + else + words.Add(wxT("quinze_para_as")); + } + + if (hour == 0U) { + words.Add(wxT("meia-noite")); + } else if (hour == 1U || hour == 13U) { + words.Add(wxT("uma")); + } else if (hour == 2U || hour == 14U) { + words.Add(wxT("duas")); + } else if (hour == 3U || hour == 15U) { + words.Add(wxT("tres")); + } else if (hour == 4U || hour == 16U) { + words.Add(wxT("quatro")); + } else if (hour == 5U || hour == 17U) { + words.Add(wxT("cinco")); + } else if (hour == 6U || hour == 18U) { + words.Add(wxT("seis")); + } else if (hour == 7U || hour == 19U) { + words.Add(wxT("sete")); + } else if (hour == 8U || hour == 20U) { + words.Add(wxT("oito")); + } else if (hour == 9U || hour == 21U) { + words.Add(wxT("nove")); + } else if (hour == 10U || hour == 22U) { + words.Add(wxT("dez")); + } else if (hour == 11U || hour == 23U) { + words.Add(wxT("onze")); + } else { + words.Add(wxT("meio-dia")); + } + + if (min == 0U) + words.Add(wxT("hora")); + else if (min == 15U) + words.Add(wxT("e_quinze")); + else if (min == 30U) + words.Add(wxT("e_meia")); + + return words; +} + +bool CTimeServerThread::loadAMBE() +{ + wxString ambeFileName; + wxString indxFileName; + + switch (m_language) { + case LANG_ENGLISH_US_1: + case LANG_ENGLISH_US_2: + ambeFileName = wxT("TIME_en_US.ambe"); + indxFileName = wxT("TIME_en_US.indx"); + break; + case LANG_DEUTSCH_1: + case LANG_DEUTSCH_2: + ambeFileName = wxT("TIME_de_DE.ambe"); + indxFileName = wxT("TIME_de_DE.indx"); + break; + case LANG_FRANCAIS: + ambeFileName = wxT("TIME_fr_FR.ambe"); + indxFileName = wxT("TIME_fr_FR.indx"); + break; + case LANG_NEDERLANDS: + ambeFileName = wxT("TIME_nl_NL.ambe"); + indxFileName = wxT("TIME_nl_NL.indx"); + break; + case LANG_SVENSKA: + ambeFileName = wxT("TIME_se_SE.ambe"); + indxFileName = wxT("TIME_se_SE.indx"); + break; + case LANG_ESPANOL: + ambeFileName = wxT("TIME_es_ES.ambe"); + indxFileName = wxT("TIME_es_ES.indx"); + break; + case LANG_NORSK: + ambeFileName = wxT("TIME_no_NO.ambe"); + indxFileName = wxT("TIME_no_NO.indx"); + break; + case LANG_PORTUGUES: + ambeFileName = wxT("TIME_pt_PT.ambe"); + indxFileName = wxT("TIME_pt_PT.indx"); + break; + default: + ambeFileName = wxT("TIME_en_GB.ambe"); + indxFileName = wxT("TIME_en_GB.indx"); + break; + } + + bool ret = readAMBE(ambeFileName); + if (!ret) { + delete[] m_ambe; + m_ambe = NULL; + return false; + } + + ret = readIndex(indxFileName); + if (!ret) { + delete[] m_ambe; + m_ambe = NULL; + return false; + } + + return true; +} + +bool CTimeServerThread::readAMBE(const wxString& name) +{ + wxFileName fileName(wxFileName::GetHomeDir(), name); + + if (!fileName.IsFileReadable()) { + wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str()); +#if defined(__WINDOWS__) + fileName.Assign(::wxGetCwd(), name); +#else + fileName.Assign(wxT(DATA_DIR), name); +#endif + if (!fileName.IsFileReadable()) { + wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str()); + return false; + } + } + + wxFFile file; + + bool ret = file.Open(fileName.GetFullPath().c_str(), wxT("rb")); + if (!ret) { + wxLogMessage(wxT("Cannot open %s for reading"), fileName.GetFullPath().c_str()); + return false; + } + + wxLogMessage(wxT("Reading %s"), fileName.GetFullPath().c_str()); + + unsigned char buffer[VOICE_FRAME_LENGTH_BYTES]; + + size_t n = file.Read(buffer, 4U); + if (n != 4U) { + wxLogMessage(wxT("Unable to read the header from %s"), fileName.GetFullPath().c_str()); + file.Close(); + return false; + } + + if (::memcmp(buffer, "AMBE", 4U) != 0) { + wxLogMessage(wxT("Invalid header from %s"), fileName.GetFullPath().c_str()); + file.Close(); + return false; + } + + // Length of the file minus the header + unsigned int length = file.Length() - 4U; + + // Hold the file data plus silence at the end + m_ambe = new unsigned char[length + SILENCE_LENGTH * VOICE_FRAME_LENGTH_BYTES]; + m_ambeLength = length / VOICE_FRAME_LENGTH_BYTES; + + // Add silence to the beginning of the buffer + unsigned char* p = m_ambe; + for (unsigned int i = 0U; i < SILENCE_LENGTH; i++, p += VOICE_FRAME_LENGTH_BYTES) + ::memcpy(p, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + + n = file.Read(p, length); + if (n != length) { + wxLogMessage(wxT("Unable to read the AMBE data from %s"), fileName.GetFullPath().c_str()); + file.Close(); + delete[] m_ambe; + m_ambe = NULL; + return false; + } + + file.Close(); + + return true; +} + +bool CTimeServerThread::readIndex(const wxString& name) +{ + wxFileName fileName(wxFileName::GetHomeDir(), name); + + if (!fileName.IsFileReadable()) { + wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str()); +#if defined(__WINDOWS__) + fileName.Assign(::wxGetCwd(), name); +#else + fileName.Assign(wxT(DATA_DIR), name); +#endif + if (!fileName.IsFileReadable()) { + wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str()); + return false; + } + } + + wxTextFile file; + + bool ret = file.Open(fileName.GetFullPath()); + if (!ret) { + wxLogMessage(wxT("Cannot open %s for reading"), fileName.GetFullPath().c_str()); + return false; + } + + // Add a silence entry at the beginning + m_index[wxT(" ")] = new CIndexRecord(wxT(" "), 0U, SILENCE_LENGTH); + + wxLogMessage(wxT("Reading %s"), fileName.GetFullPath().c_str()); + + unsigned int nLines = file.GetLineCount(); + + for (unsigned int i = 0; i < nLines; i++) { + wxString line = file.GetLine(i); + + if (line.length() > 0 && line.GetChar(0) != wxT('#')) { + wxStringTokenizer t(line, wxT(" \t\r\n"), wxTOKEN_STRTOK); + wxString name = t.GetNextToken(); + wxString startTxt = t.GetNextToken(); + wxString lengthTxt = t.GetNextToken(); + + if (!name.IsEmpty() && !startTxt.IsEmpty() && !lengthTxt.IsEmpty()) { + unsigned long start; + startTxt.ToULong(&start); + + unsigned long length; + lengthTxt.ToULong(&length); + + if (start >= m_ambeLength || (start + length) >= m_ambeLength) + wxLogError(wxT("The start or end for *%s* is out of range, start: %lu, end: %lu"), name.c_str(), start, start + length); + else + m_index[name] = new CIndexRecord(name, start + SILENCE_LENGTH, length); + } + } + } + + file.Close(); + + return true; +} + +bool CTimeServerThread::lookup(const wxString &id) +{ + CIndexRecord* info = m_index[id]; + if (info == NULL) { + // wxLogError(wxT("Cannot find the AMBE index for *%s*"), id.c_str()); + return false; + } + + unsigned int start = info->getStart(); + unsigned int length = info->getLength(); + + SLOW_DATA slowData = SD_TEXT; + + for (unsigned int i = 0U; i < length; i++) { + unsigned char* dataIn = m_ambe + (start + i) * VOICE_FRAME_LENGTH_BYTES; + + CAMBEData* dataOut = new CAMBEData; + dataOut->setDestination(m_address, G2_DV_PORT); + dataOut->setSeq(m_seqNo); + + unsigned char buffer[DV_FRAME_LENGTH_BYTES]; + ::memcpy(buffer + 0U, dataIn, VOICE_FRAME_LENGTH_BYTES); + + // Insert sync bytes when the sequence number is zero, slow data otherwise + if (m_seqNo == 0U) { + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); + m_encoder.sync(); + + switch (slowData) { + case SD_HEADER: + slowData = SD_TEXT; + break; + case SD_TEXT: + slowData = SD_HEADER; + break; + } + } else { + switch (slowData) { + case SD_HEADER: + m_encoder.getHeaderData(buffer + VOICE_FRAME_LENGTH_BYTES); + break; + case SD_TEXT: + m_encoder.getTextData(buffer + VOICE_FRAME_LENGTH_BYTES); + break; + } + } + + dataOut->setData(buffer, DV_FRAME_LENGTH_BYTES); + + m_seqNo++; + if (m_seqNo == 21U) + m_seqNo = 0U; + + m_data[m_in] = dataOut; + m_in++; + } + + return true; +} + +void CTimeServerThread::end() +{ + CAMBEData* dataOut = new CAMBEData; + dataOut->setData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES); + dataOut->setDestination(m_address, G2_DV_PORT); + dataOut->setSeq(m_seqNo); + dataOut->setEnd(true); + + m_data[m_in] = dataOut; + m_in++; +} + +bool CTimeServerThread::send(const wxArrayString &words, unsigned int hour, unsigned int min) +{ + unsigned int idA = CHeaderData::createId(); + unsigned int idB = CHeaderData::createId(); + unsigned int idC = CHeaderData::createId(); + unsigned int idD = CHeaderData::createId(); + unsigned int idE = CHeaderData::createId(); + + CHeaderData header; + header.setMyCall1(m_callsign); + header.setRptCall1(m_callsignG); + header.setRptCall2(m_callsign); // Just for the slow data header + header.setYourCall(wxT("CQCQCQ ")); + header.setDestination(m_address, G2_DV_PORT); + + wxString slowData; + switch (m_language) { + case LANG_DEUTSCH_1: + case LANG_DEUTSCH_2: + header.setMyCall2(wxT("ZEIT")); + slowData.Printf(wxT("Es ist %02u:%02u Uhr"), hour, min); + break; + case LANG_FRANCAIS: + header.setMyCall2(wxT("TIME")); + slowData.Printf(wxT("Il est %02u:%02u"), hour, min); + break; + case LANG_NEDERLANDS: + header.setMyCall2(wxT("TIJD")); + slowData.Printf(wxT("Het is %02u:%02u"), hour, min); + break; + case LANG_SVENSKA: + header.setMyCall2(wxT("TID ")); + slowData.Printf(wxT("Klockan ar %02u:%02u"), hour, min); + break; + case LANG_ENGLISH_US_1: + case LANG_ENGLISH_UK_1: + header.setMyCall2(wxT("TIME")); + if (hour == 0U) + slowData.Printf(wxT("It is 12:%02u AM"), min); + else if (hour == 12U) + slowData.Printf(wxT("It is 12:%02u PM"), min); + else if (hour > 12U) + slowData.Printf(wxT("It is %02u:%02u PM"), hour - 12U, min); + else + slowData.Printf(wxT("It is %02u:%02u AM"), hour, min); + break; + case LANG_ESPANOL: + header.setMyCall2(wxT("HORA")); + if (hour == 1U) + slowData.Printf(wxT("Es la %02u:%02u"), hour, min); + else + slowData.Printf(wxT("Son las %02u:%02u"), hour, min); + break; + case LANG_NORSK: + header.setMyCall2(wxT("TID ")); + slowData.Printf(wxT("Klokken er %02u:%02u"), hour, min); + break; + case LANG_PORTUGUES: + header.setMyCall2(wxT("HORA")); + if (hour == 1U) + slowData.Printf(wxT("E %02u:%02u"), hour, min); + else + slowData.Printf(wxT("Sao %02u:%02u"), hour, min); + break; + default: + header.setMyCall2(wxT("TIME")); + slowData.Printf(wxT("It is %02u:%02u"), hour, min); + break; + } + + m_encoder.setHeaderData(header); + m_encoder.setTextData(slowData); + + m_in = 0U; + + if (m_format != FORMAT_TEXT_TIME) { + wxString text = words.Item(0U); + for (unsigned int i = 1U; i < words.GetCount(); i++) { + text.Append(wxT(" ")); + text.Append(words.Item(i)); + } + + text.Replace(wxT("_"), wxT(" ")); + wxLogMessage(wxT("Sending voice \"%s\", sending text \"%s\""), text.c_str(), slowData.c_str()); + + m_seqNo = 0U; + + // Build the audio + lookup(wxT(" ")); + lookup(wxT(" ")); + lookup(wxT(" ")); + lookup(wxT(" ")); + + for (unsigned int i = 0U; i < words.GetCount(); i++) + lookup(words.Item(i)); + + lookup(wxT(" ")); + lookup(wxT(" ")); + lookup(wxT(" ")); + lookup(wxT(" ")); + + end(); + } else { + wxLogMessage(wxT("Sending text \"%s\""), slowData.c_str()); + + for (unsigned int i = 0U; i < 21U; i++) { + CAMBEData* dataOut = new CAMBEData; + dataOut->setDestination(m_address, G2_DV_PORT); + dataOut->setSeq(i); + + unsigned char buffer[DV_FRAME_LENGTH_BYTES]; + ::memcpy(buffer + 0U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES); + + // Insert sync bytes when the sequence number is zero, slow data otherwise + if (i == 0U) { + ::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES); + m_encoder.sync(); + } else { + m_encoder.getTextData(buffer + VOICE_FRAME_LENGTH_BYTES); + } + + dataOut->setData(buffer, DV_FRAME_LENGTH_BYTES); + + m_data[m_in] = dataOut; + m_in++; + } + + CAMBEData* dataOut = new CAMBEData; + dataOut->setData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES); + dataOut->setDestination(m_address, G2_DV_PORT); + dataOut->setSeq(0U); + dataOut->setEnd(true); + + m_data[m_in] = dataOut; + m_in++; + } + + if (m_in == 0U) { + wxLogWarning(wxT("Not sending, no audio files loaded")); + return false; + } + + if (!m_callsignA.IsEmpty()) { + header.setRptCall2(m_callsignA); + header.setId(idA); + sendHeader(header); + } + + if (!m_callsignB.IsEmpty()) { + header.setRptCall2(m_callsignB); + header.setId(idB); + sendHeader(header); + } + + if (!m_callsignC.IsEmpty()) { + header.setRptCall2(m_callsignC); + header.setId(idC); + sendHeader(header); + } + + if (!m_callsignD.IsEmpty()) { + header.setRptCall2(m_callsignD); + header.setId(idD); + sendHeader(header); + } + + if (!m_callsignE.IsEmpty()) { + header.setRptCall2(m_callsignE); + header.setId(idE); + sendHeader(header); + } + + unsigned int out = 0U; + + wxStopWatch timer; + timer.Start(); + + for (;;) { + unsigned int needed = timer.Time() / DSTAR_FRAME_TIME_MS; + + while (out < needed) { + CAMBEData* data = m_data[out]; + m_data[out] = NULL; + out++; + + if (!m_callsignA.IsEmpty()) { + data->setId(idA); + sendData(*data); + } + + if (!m_callsignB.IsEmpty()) { + data->setId(idB); + sendData(*data); + } + + if (!m_callsignC.IsEmpty()) { + data->setId(idC); + sendData(*data); + } + + if (!m_callsignD.IsEmpty()) { + data->setId(idD); + sendData(*data); + } + + if (!m_callsignE.IsEmpty()) { + data->setId(idE); + sendData(*data); + } + + delete data; + + if (m_in == out) + return true; + } + + ::wxMilliSleep(10UL); + } +} + +bool CTimeServerThread::sendHeader(const CHeaderData &header) +{ + unsigned char buffer[60U]; + unsigned int length = header.getG2Data(buffer, 60U, true); + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Header"), buffer, length); + return true; +#else + for (unsigned int i = 0U; i < 5U; i++) { + bool res = m_socket.write(buffer, length, header.getYourAddress(), header.getYourPort()); + if (!res) + return false; + } + + return true; +#endif +} + +bool CTimeServerThread::sendData(const CAMBEData& data) +{ + unsigned char buffer[40U]; + unsigned int length = data.getG2Data(buffer, 40U); + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Data"), buffer, length); + return true; +#else + return m_socket.write(buffer, length, data.getYourAddress(), data.getYourPort()); +#endif +} diff --git a/TimeServer/TimeServerThread.h b/TimeServer/TimeServerThread.h new file mode 100644 index 0000000..eb6d26e --- /dev/null +++ b/TimeServer/TimeServerThread.h @@ -0,0 +1,122 @@ +/* + * 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 + * 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 TimeServerThread_H +#define TimeServerThread_H + +#include "SlowDataEncoder.h" +#include "UDPReaderWriter.h" +#include "TimeServerDefs.h" +#include "HeaderData.h" +#include "AMBEData.h" + +#include + +class CIndexRecord { +public: + CIndexRecord(const wxString& name, unsigned int start, unsigned int length) : + m_name(name), + m_start(start), + m_length(length) + { + } + + wxString getName() const + { + return m_name; + } + + unsigned int getStart() const + { + return m_start; + } + + unsigned int getLength() const + { + return m_length; + } + +private: + wxString m_name; + unsigned int m_start; + unsigned int m_length; +}; + +WX_DECLARE_STRING_HASH_MAP(CIndexRecord*, CIndexList_t); + +class CTimeServerThread { +public: + CTimeServerThread(); + virtual ~CTimeServerThread(); + + virtual bool setGateway(const wxString& callsign, bool sendA, bool sendB, bool sendC, bool sendD, bool sendE, const wxString& address); + virtual void setAnnouncements(LANGUAGE language, FORMAT format, INTERVAL interval); + + virtual void run(); + virtual void kill(); + +private: + CUDPReaderWriter m_socket; + wxString m_callsign; + wxString m_callsignA; + wxString m_callsignB; + wxString m_callsignC; + wxString m_callsignD; + wxString m_callsignE; + wxString m_callsignG; + in_addr m_address; + LANGUAGE m_language; + FORMAT m_format; + INTERVAL m_interval; + unsigned char* m_ambe; + unsigned int m_ambeLength; + CIndexList_t m_index; + unsigned int m_seqNo; + unsigned int m_in; + CSlowDataEncoder m_encoder; + CAMBEData** m_data; + bool m_killed; + + void sendTime(unsigned int hour, unsigned int min); + + wxArrayString sendTimeEnGB1(unsigned int hour, unsigned int min); + wxArrayString sendTimeEnGB2(unsigned int hour, unsigned int min); + wxArrayString sendTimeEnUS1(unsigned int hour, unsigned int min); + wxArrayString sendTimeEnUS2(unsigned int hour, unsigned int min); + wxArrayString sendTimeDeDE1(unsigned int hour, unsigned int min); + wxArrayString sendTimeDeDE2(unsigned int hour, unsigned int min); + wxArrayString sendTimeFrFR(unsigned int hour, unsigned int min); + wxArrayString sendTimeNlNL(unsigned int hour, unsigned int min); + wxArrayString sendTimeSeSE(unsigned int hour, unsigned int min); + wxArrayString sendTimeEsES(unsigned int hour, unsigned int min); + wxArrayString sendTimeNoNO(unsigned int hour, unsigned int min); + wxArrayString sendTimePtPT(unsigned int hour, unsigned int min); + + bool send(const wxArrayString& words, unsigned int hour, unsigned int min); + bool sendHeader(const CHeaderData& header); + bool sendData(const CAMBEData& data); + + bool loadAMBE(); + bool readAMBE(const wxString& name); + bool readIndex(const wxString& name); + + bool lookup(const wxString& id); + void end(); +}; + +#endif diff --git a/TimeServer/TimeServerThreadHelper.cpp b/TimeServer/TimeServerThreadHelper.cpp new file mode 100644 index 0000000..f5b7f8e --- /dev/null +++ b/TimeServer/TimeServerThreadHelper.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2012 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 "TimeServerThreadHelper.h" + +CTimeServerThreadHelper::CTimeServerThreadHelper(CTimeServerThread* thread) : +wxThread(wxTHREAD_JOINABLE), +m_thread(thread) +{ + wxASSERT(thread != NULL); +} + +CTimeServerThreadHelper::~CTimeServerThreadHelper() +{ + delete m_thread; +} + +void CTimeServerThreadHelper::start() +{ + Create(); + + SetPriority(100U); + + Run(); +} + +void* CTimeServerThreadHelper::Entry() +{ + wxASSERT(m_thread != NULL); + + m_thread->run(); + + return NULL; +} + +void CTimeServerThreadHelper::kill() +{ + wxASSERT(m_thread != NULL); + + m_thread->kill(); + + Wait(); +} diff --git a/TimeServer/TimeServerThreadHelper.h b/TimeServer/TimeServerThreadHelper.h new file mode 100644 index 0000000..4c276d1 --- /dev/null +++ b/TimeServer/TimeServerThreadHelper.h @@ -0,0 +1,42 @@ +/* + * 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 + * 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 TimeServerThreadHelper_H +#define TimeServerThreadHelper_H + +#include "TimeServerThread.h" + +#include + +class CTimeServerThreadHelper : public wxThread { + +public: + CTimeServerThreadHelper(CTimeServerThread* thread); + virtual ~CTimeServerThreadHelper(); + + virtual void start(); + + virtual void* Entry(); + + virtual void kill(); + +private: + CTimeServerThread* m_thread; +}; + +#endif diff --git a/TimerControl/TimerControl.vcxproj b/TimerControl/TimerControl.vcxproj new file mode 100644 index 0000000..96779de --- /dev/null +++ b/TimerControl/TimerControl.vcxproj @@ -0,0 +1,209 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {68CFAB6D-AED3-4B8A-BCB3-EE4136CA00A3} + TimerControl + Win32Proj + 10.0.16299.0 + + + + Application + v141 + Unicode + true + + + Application + v141 + Unicode + true + + + Application + v141 + Unicode + + + Application + v141 + Unicode + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>14.0.24720.0 + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + $(WXWIN)\include;$(WXWIN)\lib\vc_dll\mswud;$(IncludePath) + + + true + $(WXWIN)\include;$(WXWIN)\lib\vc_x64_dll\mswud;$(IncludePath) + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + + + false + + + + Disabled + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;../GUICommon;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + Level4 + EditAndContinue + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_dll;%(AdditionalLibraryDirectories) + true + Windows + MachineX86 + + + + + Disabled + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;../GUICommon;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + + + Level4 + ProgramDatabase + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_x64_dll;%(AdditionalLibraryDirectories) + true + Windows + + + + + MaxSpeed + true + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;../GUICommon;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + Level3 + ProgramDatabase + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_dll;%(AdditionalLibraryDirectories) + true + Windows + true + true + MachineX86 + + + + + MaxSpeed + true + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;../GUICommon;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_x64_dll;%(AdditionalLibraryDirectories) + true + Windows + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {e793cb8e-2ac9-431a-bbfc-3f52537bb3cf} + false + + + {02d03515-0bbe-4553-8c40-566a597478f8} + false + + + + + + \ No newline at end of file diff --git a/TimerControl/TimerControl.vcxproj.filters b/TimerControl/TimerControl.vcxproj.filters new file mode 100644 index 0000000..592c7a5 --- /dev/null +++ b/TimerControl/TimerControl.vcxproj.filters @@ -0,0 +1,83 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/TimerControl/TimerControlApp.cpp b/TimerControl/TimerControlApp.cpp new file mode 100644 index 0000000..5ac83c6 --- /dev/null +++ b/TimerControl/TimerControlApp.cpp @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2011-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 + * 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 "TimerControlThread.h" +#include "TimerControlDefs.h" +#include "TimerControlApp.h" +#include "Version.h" +#include "Logger.h" + +#include +#include +#include + +IMPLEMENT_APP(CTimerControlApp) + +const wxChar* NAME_PARAM = wxT("Name"); +const wxChar* NOLOGGING_SWITCH = wxT("nolog"); +const wxChar* LOGDIR_OPTION = wxT("logdir"); +const wxChar* CONFDIR_OPTION = wxT("confdir"); + +static const wxString LOG_BASE_NAME = wxT("timercontrol"); + +CTimerControlApp::CTimerControlApp() : +wxApp(), +m_name(), +m_fileName(), +m_nolog(false), +m_logDir(), +m_confDir(), +m_frame(NULL), +m_config(NULL), +m_thread(NULL) +{ +} + +CTimerControlApp::~CTimerControlApp() +{ +} + +bool CTimerControlApp::OnInit() +{ + SetVendorName(VENDOR_NAME); + + if (!wxApp::OnInit()) + return false; + + if (!m_nolog) { + wxString logBaseName = LOG_BASE_NAME; + if (!m_name.IsEmpty()) { + logBaseName.Append(wxT("_")); + logBaseName.Append(m_name); + } + +#if defined(__WINDOWS__) + if (m_logDir.IsEmpty()) + m_logDir = wxFileName::GetHomeDir(); +#else + if (m_logDir.IsEmpty()) + m_logDir = LOG_DIR; +#endif + + wxLog* log = new CLogger(m_logDir, logBaseName); + wxLog::SetActiveTarget(log); + } else { + new wxLogNull; + } + +#if defined(__WINDOWS__) + m_config = new CTimerControlConfig(new wxConfig(APPLICATION_NAME), m_name); +#else + if (m_confDir.IsEmpty()) + m_confDir = wxT(CONF_DIR); + + m_config = new CTimerControlConfig(m_confDir, m_name); +#endif + + wxString frameName = APPLICATION_NAME + wxT(" - "); + if (!m_name.IsEmpty()) { + frameName.Append(m_name); + frameName.Append(wxT(" - ")); + } + frameName.Append(VERSION); + + if (!m_name.IsEmpty()) { + wxString fileBase = SCHEDULE_BASE_NAME; + fileBase.Append(wxT("_")); + fileBase.Append(m_name); + fileBase.Replace(wxT(" "), wxT("_")); + + wxString dir = m_confDir; + if (dir.IsEmpty()) + dir = wxFileName::GetHomeDir(); + + wxFileName fileName(dir, fileBase, wxT("dat")); + m_fileName = fileName.GetFullPath(); + } else { + wxString dir = m_confDir; + if (dir.IsEmpty()) + dir = wxFileName::GetHomeDir(); + + wxFileName fileName(dir, SCHEDULE_BASE_NAME, wxT("dat")); + m_fileName = fileName.GetFullPath(); + } + + wxPoint position = wxDefaultPosition; + + int x, y; + getPosition(x, y); + if (x >= 0 && y >= 0) + position = wxPoint(x, y); + + bool delay; + getDelay(delay); + + m_frame = new CTimerControlFrame(frameName, position, delay); + m_frame->Show(); + + SetTopWindow(m_frame); + + wxLogInfo(wxT("Starting ") + APPLICATION_NAME + wxT(" - ") + VERSION); + + // Log the version of wxWidgets and the Operating System + wxLogInfo(wxT("Using wxWidgets %d.%d.%d on %s"), wxMAJOR_VERSION, wxMINOR_VERSION, wxRELEASE_NUMBER, ::wxGetOsDescription().c_str()); + + createThread(); + + return wxApp::OnInit(); +} + +int CTimerControlApp::OnExit() +{ + wxLogInfo(APPLICATION_NAME + wxT(" is exiting")); + + m_thread->kill(); + + delete m_config; + + return 0; +} + +void CTimerControlApp::OnInitCmdLine(wxCmdLineParser& parser) +{ + parser.AddSwitch(NOLOGGING_SWITCH, wxEmptyString, wxEmptyString, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption(LOGDIR_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption(CONFDIR_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddParam(NAME_PARAM, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + + wxApp::OnInitCmdLine(parser); +} + +bool CTimerControlApp::OnCmdLineParsed(wxCmdLineParser& parser) +{ + if (!wxApp::OnCmdLineParsed(parser)) + return false; + + m_nolog = parser.Found(NOLOGGING_SWITCH); + + wxString logDir; + bool found = parser.Found(LOGDIR_OPTION, &logDir); + if (found) + m_logDir = logDir; + + wxString confDir; + found = parser.Found(CONFDIR_OPTION, &confDir); + if (found) + m_confDir = confDir; + + if (parser.GetParamCount() > 0U) + m_name = parser.GetParam(0U); + + return true; +} + +#if defined(__WXDEBUG__) +void CTimerControlApp::OnAssertFailure(const wxChar* file, int line, const wxChar* func, const wxChar* cond, const wxChar* msg) +{ + wxLogFatalError(wxT("Assertion failed on line %d in file %s and function %s: %s %s"), line, file, func, cond, msg); +} +#endif + +void CTimerControlApp::getGateway(wxString& address, unsigned int& port, wxString& password) const +{ + m_config->getGateway(address, port, password); +} + +void CTimerControlApp::setGateway(const wxString& address, unsigned int port, const wxString& password) +{ + m_config->setGateway(address, port, password); +} + +void CTimerControlApp::getDelay(bool& delay) const +{ + m_config->getDelay(delay); +} + +void CTimerControlApp::setDelay(bool delay) +{ + m_config->setDelay(delay); +} + +void CTimerControlApp::getPosition(int& x, int& y) const +{ + m_config->getPosition(x, y); +} + +void CTimerControlApp::setPosition(int x, int y) +{ + m_config->setPosition(x, y); +} + +void CTimerControlApp::writeConfig() +{ + m_config->write(); +} + +void CTimerControlApp::writeItems() +{ + m_frame->writeItems(); + + m_thread->reload(); +} + +void CTimerControlApp::createThread() +{ + CTimerControlThread* thread = new CTimerControlThread; + + wxString address, password; + unsigned int port; + getGateway(address, port, password); + thread->setGateway(address, port, password); + wxLogInfo(wxT("Gateway set to %s:%u"), address.c_str(), port); + + bool delay; + getDelay(delay); + thread->setDelay(delay); + wxLogInfo(wxT("Delay set to %d"), int(delay)); + + wxLogInfo(wxT("Schedule file is %s"), m_fileName.c_str()); + m_frame->setFileName(m_fileName); + thread->setFileName(m_fileName); + + thread->reload(); + + // Convert the worker class into a thread + m_thread = new CTimerControlThreadHelper(thread); + m_thread->start(); +} diff --git a/TimerControl/TimerControlApp.h b/TimerControl/TimerControlApp.h new file mode 100644 index 0000000..5cc0800 --- /dev/null +++ b/TimerControl/TimerControlApp.h @@ -0,0 +1,74 @@ +/* + * 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 + * 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 TimerControlApp_H +#define TimerControlApp_H + +#include "TimerControlThreadHelper.h" +#include "TimerControlConfig.h" +#include "TimerControlFrame.h" +#include "TimerControlItem.h" + +#include + +class CTimerControlApp : public wxApp { + +public: + CTimerControlApp(); + virtual ~CTimerControlApp(); + + virtual bool OnInit(); + virtual int OnExit(); + + virtual void OnInitCmdLine(wxCmdLineParser& parser); + virtual bool OnCmdLineParsed(wxCmdLineParser& parser); + + // This is overridden because dialog boxes from threads are bad news +#if defined(__WXDEBUG__) + virtual void OnAssertFailure(const wxChar* file, int line, const wxChar* func, const wxChar* cond, const wxChar* msg); +#endif + + virtual void getGateway(wxString& address, unsigned int& port, wxString& password) const; + virtual void setGateway(const wxString& address, unsigned int port, const wxString& password); + + virtual void getDelay(bool& enabled) const; + virtual void setDelay(bool enabled); + + virtual void getPosition(int& x, int& y) const; + virtual void setPosition(int x, int y); + + virtual void writeConfig(); + + virtual void writeItems(); + +private: + wxString m_name; + wxString m_fileName; + bool m_nolog; + wxString m_logDir; + wxString m_confDir; + CTimerControlFrame* m_frame; + CTimerControlConfig* m_config; + CTimerControlThreadHelper* m_thread; + + void createThread(); +}; + +DECLARE_APP(CTimerControlApp) + +#endif diff --git a/TimerControl/TimerControlAppD.cpp b/TimerControl/TimerControlAppD.cpp new file mode 100644 index 0000000..26ab75a --- /dev/null +++ b/TimerControl/TimerControlAppD.cpp @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2011-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 + * 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 "TimerControlConfig.h" +#include "TimerControlDefs.h" +#include "TimerControlAppD.h" +#include "Version.h" +#include "Logger.h" + +#include +#include +#include +#include + +const wxChar* NAME_PARAM = wxT("Name"); +const wxChar* NOLOGGING_SWITCH = wxT("nolog"); +const wxChar* LOGDIR_OPTION = wxT("logdir"); +const wxChar* CONFDIR_OPTION = wxT("confdir"); +const wxChar* DAEMON_SWITCH = wxT("daemon"); + +static const wxString LOG_BASE_NAME = wxT("timercontrold"); + +int main(int argc, char** argv) +{ + bool res = ::wxInitialize(); + if (!res) { + ::fprintf(stderr, "timercontrold: failed to initialise the wxWidgets library, exiting\n"); + return -1; + } + + wxCmdLineParser parser(argc, argv); + parser.AddSwitch(NOLOGGING_SWITCH, wxEmptyString, wxEmptyString, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddSwitch(DAEMON_SWITCH, wxEmptyString, wxEmptyString, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption(LOGDIR_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption(CONFDIR_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddParam(NAME_PARAM, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + + int cmd = parser.Parse(); + if (cmd != 0) { + ::wxUninitialize(); + return 0; + } + + bool nolog = parser.Found(NOLOGGING_SWITCH); + bool daemon = parser.Found(DAEMON_SWITCH); + + wxString logDir; + bool found = parser.Found(LOGDIR_OPTION, &logDir); + if (!found) + logDir.Clear(); + + wxString confDir; + found = parser.Found(CONFDIR_OPTION, &confDir); + if (!found) + confDir = wxT(CONF_DIR); + + wxString name; + if (parser.GetParamCount() > 0U) + name = parser.GetParam(0U); + + if (daemon) { + pid_t pid = ::fork(); + + if (pid < 0) { + ::fprintf(stderr, "timercontrold: error in fork(), exiting\n"); + ::wxUninitialize(); + return 1; + } + + // If this is the parent, exit + if (pid > 0) + return 0; + + // We are the child from here onwards + ::setsid(); + + ::chdir("/"); + + ::umask(0); + } + + CTimerControlAppD controller(nolog, logDir, confDir, name); + + if (!controller.init()) { + ::wxUninitialize(); + return 1; + } + + controller.run(); + + ::wxUninitialize(); + return 0; +} + +CTimerControlAppD::CTimerControlAppD(bool nolog, const wxString& logDir, const wxString& confDir, const wxString& name) : +m_name(name), +m_nolog(nolog), +m_logDir(logDir), +m_confDir(confDir), +m_fileName(), +m_thread(NULL) +{ +} + +CTimerControlAppD::~CTimerControlAppD() +{ +} + +bool CTimerControlAppD::init() +{ + if (!m_name.IsEmpty()) { + wxString fileBase = SCHEDULE_BASE_NAME; + fileBase.Append(wxT("_")); + fileBase.Append(m_name); + fileBase.Replace(wxT(" "), wxT("_")); + + wxString dir = m_confDir; + if (dir.IsEmpty()) + dir = wxFileName::GetHomeDir(); + + wxFileName fileName(dir, fileBase, wxT("dat")); + m_fileName = fileName.GetFullPath(); + } else { + wxString dir = m_confDir; + if (dir.IsEmpty()) + dir = wxFileName::GetHomeDir(); + + wxFileName fileName(dir, SCHEDULE_BASE_NAME, wxT("dat")); + m_fileName = fileName.GetFullPath(); + } + + if (!m_nolog) { + wxString logBaseName = LOG_BASE_NAME; + if (!m_name.IsEmpty()) { + logBaseName.Append(wxT("_")); + logBaseName.Append(m_name); + } + +#if defined(__WINDOWS__) + if (m_logDir.IsEmpty()) + m_logDir = wxFileName::GetHomeDir(); +#else + if (m_logDir.IsEmpty()) + m_logDir = LOG_DIR; +#endif + + wxLog* log = new CLogger(m_logDir, logBaseName); + wxLog::SetActiveTarget(log); + } else { + new wxLogNull; + } + + wxLogInfo(wxT("Starting ") + APPLICATION_NAME + wxT(" daemon - ") + VERSION); + + // Log the version of wxWidgets and the Operating System + wxLogInfo(wxT("Using wxWidgets %d.%d.%d on %s"), wxMAJOR_VERSION, wxMINOR_VERSION, wxRELEASE_NUMBER, ::wxGetOsDescription().c_str()); + + return createThread(); +} + +void CTimerControlAppD::run() +{ + m_thread->run(); + + wxLogInfo(APPLICATION_NAME + wxT(" is exiting")); +} + +bool CTimerControlAppD::createThread() +{ + CTimerControlConfig config(m_confDir, m_name); + + m_thread = new CTimerControlThread; + + wxString address, password; + unsigned int port; + config.getGateway(address, port, password); + m_thread->setGateway(address, port, password); + wxLogInfo(wxT("Gateway set to %s:%u"), address.c_str(), port); + + bool delay; + config.getDelay(delay); + m_thread->setDelay(delay); + wxLogInfo(wxT("Delay set to %d"), int(delay)); + + wxLogInfo(wxT("Schedule file is %s"), m_fileName.c_str()); + m_thread->setFileName(m_fileName); + + m_thread->reload(); + + return true; +} diff --git a/TimerControl/TimerControlAppD.h b/TimerControl/TimerControlAppD.h new file mode 100644 index 0000000..5710a91 --- /dev/null +++ b/TimerControl/TimerControlAppD.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2011 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 TimerControlAppD_H +#define TimerControlAppD_H + +#include "TimerControlThread.h" + +#include + +class CTimerControlAppD { + +public: + CTimerControlAppD(bool nolog, const wxString& logDir, const wxString& confDir, const wxString& name); + ~CTimerControlAppD(); + + bool init(); + + void run(); + +private: + wxString m_name; + bool m_nolog; + wxString m_logDir; + wxString m_confDir; + wxString m_fileName; + CTimerControlThread* m_thread; + + bool createThread(); +}; + +#endif diff --git a/TimerControl/TimerControlConfig.cpp b/TimerControl/TimerControlConfig.cpp new file mode 100644 index 0000000..47ad5de --- /dev/null +++ b/TimerControl/TimerControlConfig.cpp @@ -0,0 +1,251 @@ +/* + * 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 + * 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 "TimerControlConfig.h" +#include "TimerControlDefs.h" + +const wxString KEY_ADDRESS = wxT("address"); +const wxString KEY_PORT = wxT("port"); +const wxString KEY_PASSWORD = wxT("password"); +const wxString KEY_DELAY = wxT("delay"); +const wxString KEY_WINDOW_X = wxT("windowX"); +const wxString KEY_WINDOW_Y = wxT("windowY"); + +const wxString DEFAULT_ADDRESS = wxEmptyString; +const long DEFAULT_PORT = 0L; +const wxString DEFAULT_PASSWORD = wxEmptyString; +const bool DEFAULT_DELAY = false; +const long DEFAULT_WINDOW_X = -1L; +const long DEFAULT_WINDOW_Y = -1L; + + +#if defined(__WINDOWS__) + +CTimerControlConfig::CTimerControlConfig(wxConfigBase* config, const wxString& name) : +m_config(config), +m_name(wxT("/")), +m_address(DEFAULT_ADDRESS), +m_port(DEFAULT_PORT), +m_password(DEFAULT_PASSWORD), +m_delay(DEFAULT_DELAY), +m_x(DEFAULT_WINDOW_X), +m_y(DEFAULT_WINDOW_Y) +{ + wxASSERT(config != NULL); + + if (!name.IsEmpty()) + m_name = wxT("/") + name; + + m_config->Read(m_name + KEY_ADDRESS, &m_address, DEFAULT_ADDRESS); + + long temp; + m_config->Read(m_name + KEY_PORT, &temp, DEFAULT_PORT); + m_port = (unsigned int)temp; + + m_config->Read(m_name + KEY_PASSWORD, &m_password, DEFAULT_PASSWORD); + + m_config->Read(m_name + KEY_DELAY, &m_delay, DEFAULT_DELAY); + + m_config->Read(m_name + KEY_WINDOW_X, &temp, DEFAULT_WINDOW_X); + m_x = (unsigned int)temp; + + m_config->Read(m_name + KEY_WINDOW_Y, &temp, DEFAULT_WINDOW_Y); + m_y = (unsigned int)temp; +} + +CTimerControlConfig::~CTimerControlConfig() +{ + delete m_config; +} + +#else + +CTimerControlConfig::CTimerControlConfig(const wxString& dir, const wxString& name) : +m_fileName(), +m_address(DEFAULT_ADDRESS), +m_port(DEFAULT_PORT), +m_password(DEFAULT_PASSWORD), +m_delay(DEFAULT_DELAY), +m_x(DEFAULT_WINDOW_X), +m_y(DEFAULT_WINDOW_Y) +{ + wxASSERT(!dir.IsEmpty()); + + wxString fileName = CONFIG_FILE_NAME; + if (!name.IsEmpty()) + fileName = CONFIG_FILE_NAME + wxT("_") + name; + + m_fileName.Assign(dir, fileName); + + wxTextFile file(m_fileName.GetFullPath()); + + bool exists = file.Exists(); + if (!exists) + return; + + bool ret = file.Open(); + if (!ret) { + wxLogError(wxT("Cannot open the config file - %s"), m_fileName.GetFullPath().c_str()); + return; + } + + long temp; + + wxString str = file.GetFirstLine(); + + while (!file.Eof()) { + if (str.GetChar(0U) == wxT('#')) { + str = file.GetNextLine(); + continue; + } + + int n = str.Find(wxT('=')); + if (n == wxNOT_FOUND) { + str = file.GetNextLine(); + continue; + } + + wxString key = str.Left(n); + wxString val = str.Mid(n + 1U); + + if (key.IsSameAs(KEY_ADDRESS)) { + m_address = val; + } else if (key.IsSameAs(KEY_PORT)) { + val.ToLong(&temp); + m_port = (unsigned int)temp; + } else if (key.IsSameAs(KEY_PASSWORD)) { + m_password = val; + } else if (key.IsSameAs(KEY_DELAY)) { + val.ToLong(&temp); + m_delay = temp == 1L; + } else if (key.IsSameAs(KEY_WINDOW_X)) { + val.ToLong(&temp); + m_x = int(temp); + } else if (key.IsSameAs(KEY_WINDOW_Y)) { + val.ToLong(&temp); + m_y = int(temp); + } + + str = file.GetNextLine(); + } + + file.Close(); +} + +CTimerControlConfig::~CTimerControlConfig() +{ +} + +#endif + +void CTimerControlConfig::getGateway(wxString& address, unsigned int& port, wxString& password) const +{ + address = m_address; + port = m_port; + password = m_password; +} + +void CTimerControlConfig::setGateway(const wxString& address, unsigned int port, const wxString& password) +{ + m_address = address; + m_port = port; + m_password = password; +} + +void CTimerControlConfig::getDelay(bool& delay) const +{ + delay = m_delay; +} + +void CTimerControlConfig::setDelay(bool delay) +{ + m_delay = delay; +} + +void CTimerControlConfig::getPosition(int& x, int& y) const +{ + x = m_x; + y = m_y; +} + +void CTimerControlConfig::setPosition(int x, int y) +{ + m_x = x; + m_y = y; +} + +#if defined(__WINDOWS__) + +bool CTimerControlConfig::write() +{ + m_config->Write(m_name + KEY_ADDRESS, m_address); + m_config->Write(m_name + KEY_PORT, long(m_port)); + m_config->Write(m_name + KEY_PASSWORD, m_password); + m_config->Write(m_name + KEY_DELAY, m_delay); + m_config->Write(m_name + KEY_WINDOW_X, long(m_x)); + m_config->Write(m_name + KEY_WINDOW_Y, long(m_y)); + m_config->Flush(); + + return true; +} + +#else + +bool CTimerControlConfig::write() +{ + wxTextFile file(m_fileName.GetFullPath()); + + bool exists = file.Exists(); + if (exists) { + bool ret = file.Open(); + if (!ret) { + wxLogError(wxT("Cannot open the config file - %s"), m_fileName.GetFullPath().c_str()); + return false; + } + + // Remove the existing file entries + file.Clear(); + } else { + bool ret = file.Create(); + if (!ret) { + wxLogError(wxT("Cannot create the config file - %s"), m_fileName.GetFullPath().c_str()); + return false; + } + } + + wxString buffer; + buffer.Printf(wxT("%s=%s"), KEY_ADDRESS.c_str(), m_address.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%u"), KEY_PORT.c_str(), m_port); file.AddLine(buffer); + buffer.Printf(wxT("%s=%s"), KEY_PASSWORD.c_str(), m_password.c_str()); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_DELAY.c_str(), m_delay ? 1 : 0); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_WINDOW_X.c_str(), m_x); file.AddLine(buffer); + buffer.Printf(wxT("%s=%d"), KEY_WINDOW_Y.c_str(), m_y); file.AddLine(buffer); + + bool ret = file.Write(); + if (!ret) { + file.Close(); + wxLogError(wxT("Cannot write the config file - %s"), m_fileName.GetFullPath().c_str()); + return false; + } + + file.Close(); + + return true; +} + +#endif diff --git a/TimerControl/TimerControlConfig.h b/TimerControl/TimerControlConfig.h new file mode 100644 index 0000000..9fb5578 --- /dev/null +++ b/TimerControl/TimerControlConfig.h @@ -0,0 +1,62 @@ +/* + * 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 + * 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 TimerControlConfig_H +#define TimerControlConfig_H + +#include +#include +#include + +class CTimerControlConfig { + +public: +#if defined(__WINDOWS__) + CTimerControlConfig(wxConfigBase* config, const wxString& name); +#else + CTimerControlConfig(const wxString& dir, const wxString& name); +#endif + ~CTimerControlConfig(); + + void getGateway(wxString& address, unsigned int& port, wxString& password) const; + void setGateway(const wxString& address, unsigned int port, const wxString& password); + + void getDelay(bool& enabled) const; + void setDelay(bool enabled); + + void getPosition(int& x, int& y) const; + void setPosition(int x, int y); + + bool write(); + +private: +#if defined(__WINDOWS__) + wxConfigBase* m_config; + wxString m_name; +#else + wxFileName m_fileName; +#endif + wxString m_address; + unsigned int m_port; + wxString m_password; + bool m_delay; + int m_x; + int m_y; +}; + +#endif diff --git a/TimerControl/TimerControlDefs.h b/TimerControl/TimerControlDefs.h new file mode 100644 index 0000000..4ce01af --- /dev/null +++ b/TimerControl/TimerControlDefs.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2011 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 TimerControlDefs_H +#define TimerControlDefs_H + +#include + +const wxString APPLICATION_NAME = wxT("Timer Control"); + +#if !defined(__WINDOWS__) +const wxString CONFIG_FILE_NAME = wxT("timercontrol"); +#endif + +const wxString SCHEDULE_BASE_NAME = wxT("Schedule"); + +#endif diff --git a/TimerControl/TimerControlFrame.cpp b/TimerControl/TimerControlFrame.cpp new file mode 100644 index 0000000..65fdd3a --- /dev/null +++ b/TimerControl/TimerControlFrame.cpp @@ -0,0 +1,343 @@ +/* + * Copyright (C) 2011-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 + * 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 "TimerControlPreferences.h" +#include "TimerControlItemFile.h" +#include "TimerControlFrame.h" +#include "TimerControlItem.h" +#include "TimerControlDefs.h" +#include "TimerControlApp.h" +#include "Version.h" +#include "SHA256.h" + +const unsigned int BORDER_SIZE = 5U; + +#if defined(__WINDOWS__) +const unsigned int MAIN_HEIGHT = 350U; +const unsigned int MAIN_WIDTH = 500U; +#else +const unsigned int MAIN_HEIGHT = 400U; +const unsigned int MAIN_WIDTH = 590U; +#endif + +#include + +enum { + Menu_Edit_Preferences = 7000, + + Timer_Timer1, + Timer_Timer2 +}; + +BEGIN_EVENT_TABLE(CTimerControlFrame, wxFrame) + EVT_MENU(wxID_EXIT, CTimerControlFrame::onQuit) + EVT_MENU(Menu_Edit_Preferences, CTimerControlFrame::onPreferences) + EVT_MENU(wxID_ABOUT, CTimerControlFrame::onAbout) + + EVT_TIMER(Timer_Timer1, CTimerControlFrame::onTimer1) + EVT_TIMER(Timer_Timer2, CTimerControlFrame::onTimer2) + + EVT_CLOSE(CTimerControlFrame::onClose) +END_EVENT_TABLE() + +CTimerControlFrame::CTimerControlFrame(const wxString& title, const wxPoint& position, bool delay) : +wxFrame(NULL, -1, title, position), +m_state(TCFS_NORMAL), +m_timer1(this, Timer_Timer1), +m_timer2(this, Timer_Timer2), +m_noteBook(NULL), +m_handler(NULL), +m_password(), +m_fileName(), +m_repeaters() +{ + SetMenuBar(createMenuBar()); + + wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); + + wxPanel* panel = new wxPanel(this, -1); + + wxBoxSizer* panelSizer = new wxBoxSizer(wxVERTICAL); + + m_noteBook = new wxNotebook(panel, -1, wxDefaultPosition, wxSize(MAIN_WIDTH, MAIN_HEIGHT)); + panelSizer->Add(m_noteBook, 0, wxALL | wxGROW, BORDER_SIZE); + + panel->SetSizer(panelSizer); + + panelSizer->SetSizeHints(panel); + + mainSizer->Add(panel); + + SetSizer(mainSizer); + mainSizer->SetSizeHints(this); + + m_timer1.Start(delay ? 20000 : 100, wxTIMER_ONE_SHOT); +} + +CTimerControlFrame::~CTimerControlFrame() +{ +} + +wxMenuBar* CTimerControlFrame::createMenuBar() +{ + wxMenu* fileMenu = new wxMenu(); + fileMenu->Append(wxID_EXIT, _("Exit")); + + wxMenu* editMenu = new wxMenu(); + editMenu->Append(Menu_Edit_Preferences, _("Preferences...")); + + wxMenu* helpMenu = new wxMenu(); + helpMenu->Append(wxID_ABOUT, _("About Timer Control")); + + wxMenuBar* menuBar = new wxMenuBar(); + menuBar->Append(fileMenu, _("File")); + menuBar->Append(editMenu, _("Edit")); + menuBar->Append(helpMenu, _("Help")); + + return menuBar; +} + +void CTimerControlFrame::onQuit(wxCommandEvent&) +{ + Close(false); +} + +void CTimerControlFrame::onClose(wxCloseEvent&) +{ + int x, y; + GetPosition(&x, &y); + if (x >= 0 && y >= 0) { + ::wxGetApp().setPosition(x, y); + ::wxGetApp().writeConfig(); + } + + m_timer1.Stop(); + m_timer2.Stop(); + + if (m_handler != NULL) { + m_handler->logout(); + m_handler->close(); + } + + Destroy(); +} + +void CTimerControlFrame::onPreferences(wxCommandEvent&) +{ + wxString address, password; + unsigned int port; + ::wxGetApp().getGateway(address, port, password); + + bool delay; + ::wxGetApp().getDelay(delay); + + CTimerControlPreferences dialog1(this, -1, address, port, password, delay); + if (dialog1.ShowModal() != wxID_OK) + return; + + address = dialog1.getAddress(); + port = dialog1.getPort(); + password = dialog1.getPassword(); + + delay = dialog1.getDelay(); + + ::wxGetApp().setGateway(address, port, password); + ::wxGetApp().setDelay(delay); + ::wxGetApp().writeConfig(); + + wxMessageDialog dialog2(this, _("The changes made will not take effect\nuntil the application is restarted"), _("Timer Control Information"), wxICON_INFORMATION); + dialog2.ShowModal(); +} + +void CTimerControlFrame::onAbout(wxCommandEvent&) +{ + wxAboutDialogInfo info; + info.AddDeveloper(wxT("Jonathan Naylor, G4KLX")); + info.SetCopyright(wxT("(C) 2011-2015 using GPL v2 or later")); + info.SetName(APPLICATION_NAME); + info.SetVersion(VERSION); + info.SetDescription(_("This program allows for the controlling of\nthe ircDDB Gateway.")); + + ::wxAboutBox(info); +} + +void CTimerControlFrame::onTimer1(wxTimerEvent&) +{ + startup(); +} + +void CTimerControlFrame::onTimer2(wxTimerEvent&) +{ + TC_TYPE type = m_handler->readType(); + + switch (type) { + case TCT_NONE: + m_handler->retry(); + break; + + case TCT_ACK: + if (m_state == TCFS_HASH) { + m_handler->setLoggedIn(true); + m_handler->getCallsigns(); + m_state = TCFS_NORMAL; + } + break; + + case TCT_NAK: { + if (m_state == TCFS_HASH || m_state == TCFS_LOGIN) + m_handler->setLoggedIn(false); + m_handler->logout(); + + wxString text = m_handler->readNAK(); + m_state = TCFS_NORMAL; + ::wxMessageBox(text); + } + break; + + case TCT_RANDOM: { + unsigned int rnd = m_handler->readRandom(); + sendHash(rnd); + m_state = TCFS_HASH; + } + break; + + case TCT_CALLSIGNS: { + m_noteBook->DeleteAllPages(); + + wxArrayString data = m_handler->readCallsigns(); + + for (unsigned int i = 0U; i < data.GetCount(); i++) + addRepeater(data.Item(i)); + + m_handler->logout(); + + loadItems(); + } + break; + } +} + +void CTimerControlFrame::startup() +{ + wxString address; + unsigned int port; + ::wxGetApp().getGateway(address, port, m_password); + + if (address.IsEmpty() || port == 0U || m_password.IsEmpty()) + return; + + m_handler = new CTimerControlRemoteControlHandler(address, port); + + bool ret = m_handler->open(); + if (!ret) { + delete m_handler; + m_handler = NULL; + return; + } + + ret = m_handler->login(); + if (!ret) { + m_handler->close(); + delete m_handler; + m_handler = NULL; + return; + } + + m_state = TCFS_LOGIN; + + m_timer2.Start(100, wxTIMER_CONTINUOUS); +} + +void CTimerControlFrame::addRepeater(const wxString& callsign) +{ + CTimerControlRepeaterPanel* repeater = new CTimerControlRepeaterPanel(m_noteBook, -1, callsign); + + m_noteBook->AddPage(repeater, callsign, false); + + m_repeaters[callsign] = repeater; +} + +void CTimerControlFrame::setFileName(const wxString& fileName) +{ + m_fileName = fileName; +} + +void CTimerControlFrame::loadItems() +{ + CTimerControlItemFile file(m_fileName); + + unsigned int count; + CTimerControlItem** items = file.readItems(count); + + if (count == 0U) + return; + + for (CRepeater_t::iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) + (*it).second->load(items, count); + + delete[] items; +} + +void CTimerControlFrame::writeItems() +{ + // Count the total number of items from all the panels + unsigned int count = 0U; + for (CRepeater_t::iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) + count += (*it).second->getCount(); + + if (count == 0U) + return; + + CTimerControlItem** items = new CTimerControlItem*[count]; + + unsigned int n = 0U; + for (CRepeater_t::iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) { + unsigned int count1 = (*it).second->getCount(); + CTimerControlItem** items1 = (*it).second->getItems(); + + for (unsigned int i = 0U; i < count1; i++, n++) + items[n] = items1[i]; + + delete[] items1; + } + + CTimerControlItemFile file(m_fileName); + + file.writeItems(items, count); + + delete[] items; +} + +void CTimerControlFrame::sendHash(unsigned int rnd) +{ + unsigned int len = m_password.Len() + sizeof(unsigned int); + unsigned char* in = new unsigned char[len]; + unsigned char* out = new unsigned char[32U]; + + ::memcpy(in, &rnd, sizeof(unsigned int)); + for (unsigned int i = 0U; i < m_password.Len(); i++) + in[i + sizeof(unsigned int)] = m_password.GetChar(i); + + CSHA256 sha256; + sha256.buffer(in, len, out); + + m_handler->sendHash(out, 32U); + + delete[] in; + delete[] out; +} diff --git a/TimerControl/TimerControlFrame.h b/TimerControl/TimerControlFrame.h new file mode 100644 index 0000000..b608f7a --- /dev/null +++ b/TimerControl/TimerControlFrame.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2011,2012 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 TimerControlFrame_H +#define TimerControlFrame_H + +#include "TimerControlRemoteControlHandler.h" +#include "TimerControlRepeaterPanel.h" +#include "Defs.h" + +#include +#include + +WX_DECLARE_STRING_HASH_MAP(CTimerControlRepeaterPanel*, CRepeater_t); + +enum TCF_STATE { + TCFS_NORMAL, + TCFS_LOGIN, + TCFS_HASH +}; + +class CTimerControlFrame : public wxFrame { +public: + CTimerControlFrame(const wxString& title, const wxPoint& position, bool delay); + virtual ~CTimerControlFrame(); + + virtual void onQuit(wxCommandEvent& event); + virtual void onPreferences(wxCommandEvent& event); + virtual void onAbout(wxCommandEvent& event); + virtual void onClose(wxCloseEvent& event); + virtual void onTimer1(wxTimerEvent& event); + virtual void onTimer2(wxTimerEvent& event); + + virtual void setFileName(const wxString& fileName); + + virtual void writeItems(); + +private: + TCF_STATE m_state; + wxTimer m_timer1; + wxTimer m_timer2; + wxNotebook* m_noteBook; + CTimerControlRemoteControlHandler* m_handler; + wxString m_password; + wxString m_fileName; + CRepeater_t m_repeaters; + + DECLARE_EVENT_TABLE() + + wxMenuBar* createMenuBar(); + void startup(); + void addRepeater(const wxString& callsign); + void sendHash(unsigned int rnd); + void loadItems(); +}; + +#endif diff --git a/TimerControl/TimerControlItem.h b/TimerControl/TimerControlItem.h new file mode 100644 index 0000000..ec5ba64 --- /dev/null +++ b/TimerControl/TimerControlItem.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2011 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 TimerControlItem_H +#define TimerControlItem_H + +#include "Defs.h" + +#include + +struct CTimerControlItem { + wxString m_repeater; + unsigned int m_day; + unsigned int m_hour; + unsigned int m_minute; + wxString m_reflector; + RECONNECT m_reconnect; +}; + +#endif diff --git a/TimerControl/TimerControlItemFile.cpp b/TimerControl/TimerControlItemFile.cpp new file mode 100644 index 0000000..7de3932 --- /dev/null +++ b/TimerControl/TimerControlItemFile.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2011,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 "TimerControlItemFile.h" + +#include +#include + +const unsigned int RECORD_LENGTH = 32U; + +CTimerControlItemFile::CTimerControlItemFile(const wxString& fileName) : +m_fileName(fileName), +m_mutex() +{ +} + +CTimerControlItemFile::~CTimerControlItemFile() +{ +} + +bool CTimerControlItemFile::writeItems(CTimerControlItem** items, unsigned int count) +{ + wxMutexLocker locker(m_mutex); + + wxFFile file; + bool ret = file.Open(m_fileName, wxT("wb")); + if (!ret) { + wxLogError(wxT("Cannot open %s for writing"), m_fileName.c_str()); + return false; + } + + file.Write("TC1", 3U); + + for (unsigned int i = 0U; i < count; i++) { + unsigned char buffer[RECORD_LENGTH]; + ::memset(buffer, ' ', RECORD_LENGTH); + + wxString repeater = items[i]->m_repeater; + for (unsigned int j = 0U; j < repeater.Len(); j++) + buffer[j + 0U] = repeater.GetChar(j); + + wxUint32 day = wxUint32(items[i]->m_day); + ::memcpy(buffer + 8U, &day, sizeof(wxUint32)); + + wxUint32 hour = wxUint32(items[i]->m_hour); + ::memcpy(buffer + 12U, &hour, sizeof(wxUint32)); + + wxUint32 minute = wxUint32(items[i]->m_minute); + ::memcpy(buffer + 16U, &minute, sizeof(wxUint32)); + + wxString reflector = items[i]->m_reflector; + for (unsigned int j = 0U; j < reflector.Len(); j++) + buffer[j + 20U] = reflector.GetChar(j); + + wxUint32 reconnect = wxUint32(items[i]->m_reconnect); + ::memcpy(buffer + 28U, &reconnect, sizeof(wxUint32)); + + file.Write(buffer, RECORD_LENGTH); + } + + file.Close(); + + return true; +} + +CTimerControlItem** CTimerControlItemFile::readItems(unsigned int& count) +{ + wxMutexLocker locker(m_mutex); + + count = 0U; + + bool exists = wxFile::Exists(m_fileName); + if (!exists) + return NULL; + + wxFFile file; + bool ret = file.Open(m_fileName, wxT("rb")); + if (!ret) + return NULL; + + unsigned int length = file.Length(); + if (length < 3U) { + file.Close(); + return NULL; + } + + count = (length - 3U) / RECORD_LENGTH; + + unsigned char buffer[RECORD_LENGTH]; + file.Read(buffer, 3U); + + if (::memcmp(buffer, "TC1", 3U) != 0) { + file.Close(); + return NULL; + } + + CTimerControlItem** items = new CTimerControlItem*[count]; + + for (unsigned int i = 0U; i < count; i++) { + file.Read(buffer, RECORD_LENGTH); + + wxString repeater = wxString((char*)(buffer + 0U), wxConvLocal, 8U); + wxUint32* day = (wxUint32*)(buffer + 8U); + wxUint32* hour = (wxUint32*)(buffer + 12U); + wxUint32* minute = (wxUint32*)(buffer + 16U); + wxString reflector = wxString((char*)(buffer + 20U), wxConvLocal, 8U); + wxUint32* reconnect = (wxUint32*)(buffer + 28U); + + CTimerControlItem* item = new CTimerControlItem; + item->m_repeater = repeater; + item->m_day = *day; + item->m_hour = *hour; + item->m_minute = *minute; + item->m_reflector = reflector.Trim(); + item->m_reconnect = RECONNECT(*reconnect); + items[i] = item; + } + + file.Close(); + + return items; +} diff --git a/TimerControl/TimerControlItemFile.h b/TimerControl/TimerControlItemFile.h new file mode 100644 index 0000000..96fd02a --- /dev/null +++ b/TimerControl/TimerControlItemFile.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2011,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 TimerControlItemFile_H +#define TimerControlItemFile_H + +#include "TimerControlItem.h" + +#include + +class CTimerControlItemFile { +public: + CTimerControlItemFile(const wxString& fileName); + ~CTimerControlItemFile(); + + bool writeItems(CTimerControlItem** items, unsigned int count); + + CTimerControlItem** readItems(unsigned int& count); + +private: + wxString m_fileName; + wxMutex m_mutex; +}; + +#endif diff --git a/TimerControl/TimerControlPreferences.cpp b/TimerControl/TimerControlPreferences.cpp new file mode 100644 index 0000000..9e82fad --- /dev/null +++ b/TimerControl/TimerControlPreferences.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2011,2012 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 "TimerControlPreferences.h" +#include "TimerControlDefs.h" + +const unsigned int BORDER_SIZE = 5U; + +CTimerControlPreferences::CTimerControlPreferences(wxWindow* parent, int id, const wxString& address, unsigned int port, const wxString& password, bool delay) : +wxDialog(parent, id, wxString(_("Timer Control Preferences"))), +m_remote(NULL) +{ + wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); + + wxNotebook* noteBook = new wxNotebook(this, -1); + + m_remote = new CTimerControlRemoteSet(noteBook, -1, APPLICATION_NAME, address, port, password, delay); + noteBook->AddPage(m_remote, _("Gateway"), true); + + mainSizer->Add(noteBook, 1, wxALL | wxGROW, BORDER_SIZE); + + mainSizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + SetAutoLayout(true); + Layout(); + + mainSizer->Fit(this); + mainSizer->SetSizeHints(this); + + SetSizer(mainSizer); +} + +CTimerControlPreferences::~CTimerControlPreferences() +{ +} + +bool CTimerControlPreferences::Validate() +{ + return m_remote->Validate(); +} + +wxString CTimerControlPreferences::getAddress() const +{ + return m_remote->getAddress(); +} + +unsigned int CTimerControlPreferences::getPort() const +{ + return m_remote->getPort(); +} + +wxString CTimerControlPreferences::getPassword() const +{ + return m_remote->getPassword(); +} + +bool CTimerControlPreferences::getDelay() const +{ + return m_remote->getDelay(); +} diff --git a/TimerControl/TimerControlPreferences.h b/TimerControl/TimerControlPreferences.h new file mode 100644 index 0000000..b3d4ab5 --- /dev/null +++ b/TimerControl/TimerControlPreferences.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2011,2012 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 TimerControlPreferences_H +#define TimerControlPreferences_H + +#include +#include + +#include "TimerControlRemoteSet.h" + +class CTimerControlPreferences : public wxDialog { +public: + CTimerControlPreferences(wxWindow* parent, int id, const wxString& address, unsigned int port, const wxString& password, bool delay); + virtual ~CTimerControlPreferences(); + + virtual bool Validate(); + + virtual wxString getAddress() const; + virtual unsigned int getPort() const; + virtual wxString getPassword() const; + virtual bool getDelay() const; + +private: + CTimerControlRemoteSet* m_remote; +}; + +#endif diff --git a/TimerControl/TimerControlRemoteControlHandler.cpp b/TimerControl/TimerControlRemoteControlHandler.cpp new file mode 100644 index 0000000..24ffd52 --- /dev/null +++ b/TimerControl/TimerControlRemoteControlHandler.cpp @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2011,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 "TimerControlRemoteControlHandler.h" +#include "DStarDefines.h" + +const unsigned int BUFFER_LENGTH = 2000U; + +const unsigned int MAX_RETRIES = 3U; + +CTimerControlRemoteControlHandler::CTimerControlRemoteControlHandler(const wxString& address, unsigned int port) : +m_socket(wxEmptyString, 0U), +m_address(), +m_port(port), +m_loggedIn(false), +m_retryCount(0U), +m_type(TCT_NONE), +m_inBuffer(NULL), +m_inLength(0U), +m_outBuffer(NULL), +m_outLength(0U) +{ + wxASSERT(!address.IsEmpty()); + wxASSERT(port > 0U); + + m_address = CUDPReaderWriter::lookup(address); + + m_inBuffer = new unsigned char[BUFFER_LENGTH]; + m_outBuffer = new unsigned char[BUFFER_LENGTH]; +} + +CTimerControlRemoteControlHandler::~CTimerControlRemoteControlHandler() +{ + delete[] m_inBuffer; + delete[] m_outBuffer; +} + +bool CTimerControlRemoteControlHandler::open() +{ + return m_socket.open(); +} + +TC_TYPE CTimerControlRemoteControlHandler::readType() +{ + m_type = TCT_NONE; + + in_addr address; + unsigned int port; + + int length = m_socket.read(m_inBuffer, BUFFER_LENGTH, address, port); + if (length <= 0) + return m_type; + + m_inLength = length; + + if (::memcmp(m_inBuffer, "ACK", 3U) == 0) { + m_retryCount = 0U; + m_type = TCT_ACK; + return m_type; + } else if (::memcmp(m_inBuffer, "NAK", 3U) == 0) { + m_retryCount = 0U; + m_type = TCT_NAK; + return m_type; + } else if (::memcmp(m_inBuffer, "RND", 3U) == 0) { + m_retryCount = 0U; + m_type = TCT_RANDOM; + return m_type; + } else if (::memcmp(m_inBuffer, "CAL", 3U) == 0) { + m_retryCount = 0U; + m_type = TCT_CALLSIGNS; + return m_type; + } + + return m_type; +} + +wxString CTimerControlRemoteControlHandler::readNAK() +{ + if (m_type != TCT_NAK) + return wxEmptyString; + + wxString text((char*)(m_inBuffer + 3U), wxConvLocal); + + return text; +} + +unsigned int CTimerControlRemoteControlHandler::readRandom() +{ + if (m_type != TCT_RANDOM) + return 0U; + + wxUint32 random; + ::memcpy(&random, m_inBuffer + 3U, sizeof(wxUint32)); + + return wxUINT32_SWAP_ON_BE(random); +} + +wxArrayString CTimerControlRemoteControlHandler::readCallsigns() +{ + wxArrayString data; + + if (m_type != TCT_CALLSIGNS) + return data; + + unsigned char* p = m_inBuffer + 3U; + unsigned int pos = 3U; + + while (pos < m_inLength) { + unsigned char type = *p; + pos += 1U; + p += 1U; + + wxString callsign((char*)p, wxConvLocal, LONG_CALLSIGN_LENGTH); + pos += LONG_CALLSIGN_LENGTH; + p += LONG_CALLSIGN_LENGTH; + + if (type == 'R') + data.Add(callsign); + } + + return data; +} + +bool CTimerControlRemoteControlHandler::login() +{ + if (m_loggedIn) + return false; + + if (m_address.s_addr == INADDR_NONE) + return false; + + ::memcpy(m_outBuffer, "LIN", 3U); + m_outLength = 3U; + + bool ret = m_socket.write(m_outBuffer, m_outLength, m_address, m_port); + if (!ret) { + m_retryCount = 0U; + return false; + } else { + m_retryCount = 1U; + return true; + } +} + +void CTimerControlRemoteControlHandler::setLoggedIn(bool set) +{ + m_loggedIn = set; +} + +bool CTimerControlRemoteControlHandler::getCallsigns() +{ + if (!m_loggedIn || m_retryCount > 0U) + return false; + + ::memcpy(m_outBuffer, "GCS", 3U); + m_outLength = 3U; + + bool ret = m_socket.write(m_outBuffer, m_outLength, m_address, m_port); + if (!ret) { + m_retryCount = 0U; + return false; + } else { + m_retryCount = 1U; + return true; + } +} + +bool CTimerControlRemoteControlHandler::sendHash(const unsigned char* hash, unsigned int length) +{ + wxASSERT(hash != NULL); + wxASSERT(length > 0U); + + if (m_loggedIn || m_retryCount > 0U) + return false; + + unsigned char* p = m_outBuffer; + m_outLength = 0U; + + ::memcpy(p, "SHA", 3U); + m_outLength += 3U; + p += 3U; + + ::memcpy(p, hash, length); + m_outLength += length; + p += length; + + bool ret = m_socket.write(m_outBuffer, m_outLength, m_address, m_port); + if (!ret) { + m_retryCount = 0U; + return false; + } else { + m_retryCount = 1U; + return true; + } +} + +bool CTimerControlRemoteControlHandler::link(const wxString& callsign, RECONNECT reconnect, const wxString& reflector) +{ + wxASSERT(!callsign.IsEmpty()); + + if (!m_loggedIn || m_retryCount > 0U) + return false; + + unsigned char* p = m_outBuffer; + m_outLength = 0U; + + ::memcpy(p, "LNK", 3U); + m_outLength += 3U; + p += 3U; + + ::memset(p, ' ', LONG_CALLSIGN_LENGTH); + for (unsigned int i = 0U; i < callsign.Len(); i++) + p[i] = callsign.GetChar(i); + + m_outLength += LONG_CALLSIGN_LENGTH; + p += LONG_CALLSIGN_LENGTH; + + wxInt32 temp1 = wxInt32(reconnect); + wxInt32 temp2 = wxINT32_SWAP_ON_BE(temp1); + ::memcpy(p, &temp2, sizeof(wxInt32)); + m_outLength += sizeof(wxInt32); + p += sizeof(wxInt32); + + ::memset(p, ' ', LONG_CALLSIGN_LENGTH); + for (unsigned int i = 0U; i < reflector.Len(); i++) + p[i] = reflector.GetChar(i); + + m_outLength += LONG_CALLSIGN_LENGTH; + p += LONG_CALLSIGN_LENGTH; + + bool ret = m_socket.write(m_outBuffer, m_outLength, m_address, m_port); + if (!ret) { + m_retryCount = 0U; + return false; + } else { + m_retryCount = 1U; + return true; + } +} + +bool CTimerControlRemoteControlHandler::logout() +{ + if (!m_loggedIn || m_retryCount > 0U) + return false; + + ::memcpy(m_outBuffer, "LOG", 3U); + m_outLength = 3U; + + for (unsigned int i = 0U; i < 5U; i++) { + bool ret = m_socket.write(m_outBuffer, m_outLength, m_address, m_port); + if (!ret) { + m_retryCount = 0U; + return false; + } + } + + m_retryCount = 1U; + + return true; +} + +bool CTimerControlRemoteControlHandler::retry() +{ + if (m_retryCount > 0U) { + m_retryCount++; + if (m_retryCount >= MAX_RETRIES) { + m_retryCount = 0U; + return false; + } + + m_socket.write(m_outBuffer, m_outLength, m_address, m_port); + } + + return true; +} + +void CTimerControlRemoteControlHandler::close() +{ + m_socket.close(); +} diff --git a/TimerControl/TimerControlRemoteControlHandler.h b/TimerControl/TimerControlRemoteControlHandler.h new file mode 100644 index 0000000..18c576e --- /dev/null +++ b/TimerControl/TimerControlRemoteControlHandler.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2011 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 TimerControlRemoteControlHandler_H +#define TimerControlRemoteControlHandler_H + +#include "UDPReaderWriter.h" +#include "Defs.h" + +#include + +enum TC_TYPE { + TCT_NONE, + TCT_ACK, + TCT_NAK, + TCT_RANDOM, + TCT_CALLSIGNS +}; + +class CTimerControlRemoteControlHandler { +public: + CTimerControlRemoteControlHandler(const wxString& address, unsigned int port); + ~CTimerControlRemoteControlHandler(); + + bool open(); + + TC_TYPE readType(); + + wxString readNAK(); + unsigned int readRandom(); + wxArrayString readCallsigns(); + + bool login(); + bool sendHash(const unsigned char* hash, unsigned int length); + + void setLoggedIn(bool set); + + bool getCallsigns(); + + bool link(const wxString& callsign, RECONNECT reconnect, const wxString& reflector); + + bool logout(); + + bool retry(); + + void close(); + +private: + CUDPReaderWriter m_socket; + in_addr m_address; + unsigned int m_port; + bool m_loggedIn; + unsigned int m_retryCount; + TC_TYPE m_type; + unsigned char* m_inBuffer; + unsigned int m_inLength; + unsigned char* m_outBuffer; + unsigned int m_outLength; +}; + +#endif diff --git a/TimerControl/TimerControlRemoteSet.cpp b/TimerControl/TimerControlRemoteSet.cpp new file mode 100644 index 0000000..c04261e --- /dev/null +++ b/TimerControl/TimerControlRemoteSet.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2011,2012 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 "TimerControlRemoteSet.h" +#include "DStarDefines.h" + +const unsigned int PASSWORD_WIDTH = 120U; +const unsigned int PORT_WIDTH = 80U; + +const unsigned int PORT_LENGTH = 5U; + +const unsigned int BORDER_SIZE = 5U; + +CTimerControlRemoteSet::CTimerControlRemoteSet(wxWindow* parent, int id, const wxString& title, const wxString& address, unsigned int port, const wxString& password, bool delay) : +wxPanel(parent, id), +m_title(title), +m_address(NULL), +m_port(NULL), +m_password(NULL), +m_delay(NULL) +{ + wxFlexGridSizer* sizer = new wxFlexGridSizer(2); + + wxStaticText* addressLabel = new wxStaticText(this, -1, _("Address")); + sizer->Add(addressLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_address = new wxTextCtrl(this, -1, address, wxDefaultPosition, wxSize(PASSWORD_WIDTH, -1)); + sizer->Add(m_address, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* portLabel = new wxStaticText(this, -1, _("Port")); + sizer->Add(portLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxString buffer; + buffer.Printf(wxT("%u"), port); + + m_port = new CPortTextCtrl(this, -1, buffer, wxDefaultPosition, wxSize(PORT_WIDTH, -1)); + m_port->SetMaxLength(PORT_LENGTH); + sizer->Add(m_port, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* passwordLabel = new wxStaticText(this, -1, _("Password")); + sizer->Add(passwordLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_password = new wxTextCtrl(this, -1, password, wxDefaultPosition, wxSize(PASSWORD_WIDTH, -1), wxTE_PASSWORD); + sizer->Add(m_password, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* delayLabel = new wxStaticText(this, -1, _("Delay")); + sizer->Add(delayLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_delay = new wxChoice(this, -1, wxDefaultPosition, wxSize(PASSWORD_WIDTH, -1)); + m_delay->Append(_("Disabled")); + m_delay->Append(_("Enabled")); + sizer->Add(m_delay, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_delay->SetSelection(delay ? 1 : 0); + + SetAutoLayout(true); + + SetSizer(sizer); +} + + +CTimerControlRemoteSet::~CTimerControlRemoteSet() +{ +} + +bool CTimerControlRemoteSet::Validate() +{ + wxString address = getAddress(); + if (address.IsEmpty()) { + wxMessageDialog dialog(this, _("The Address is empty"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + unsigned int port = getPort(); + if (port == 0U || port > 65535U) { + wxMessageDialog dialog(this, _("The Port is not valid"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + wxString password = getPassword(); + if (password.IsEmpty()) { + wxMessageDialog dialog(this, _("The Password is empty"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + int n = m_delay->GetCurrentSelection(); + if (n == wxNOT_FOUND) + return false; + + return true; +} + +wxString CTimerControlRemoteSet::getAddress() const +{ + return m_address->GetValue(); +} + +unsigned int CTimerControlRemoteSet::getPort() const +{ + unsigned long n; + m_port->GetValue().ToULong(&n); + + return n; +} + +wxString CTimerControlRemoteSet::getPassword() const +{ + return m_password->GetValue(); +} + +bool CTimerControlRemoteSet::getDelay() const +{ + int n = m_delay->GetCurrentSelection(); + + if (n == wxNOT_FOUND) + return false; + + return n == 1; +} diff --git a/TimerControl/TimerControlRemoteSet.h b/TimerControl/TimerControlRemoteSet.h new file mode 100644 index 0000000..3315cc9 --- /dev/null +++ b/TimerControl/TimerControlRemoteSet.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2011,2012 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 TimerControlRemoteSet_H +#define TimerControlRemoteSet_H + +#include "PortTextCtrl.h" + +#include + +class CTimerControlRemoteSet : public wxPanel { +public: + CTimerControlRemoteSet(wxWindow* parent, int id, const wxString& title, const wxString& address, unsigned int port, const wxString& password, bool delay); + virtual ~CTimerControlRemoteSet(); + + virtual bool Validate(); + + virtual wxString getAddress() const; + virtual unsigned int getPort() const; + virtual wxString getPassword() const; + virtual bool getDelay() const; + +private: + wxString m_title; + wxTextCtrl* m_address; + CPortTextCtrl* m_port; + wxTextCtrl* m_password; + wxChoice* m_delay; +}; + +#endif diff --git a/TimerControl/TimerControlRepeaterPanel.cpp b/TimerControl/TimerControlRepeaterPanel.cpp new file mode 100644 index 0000000..9f5fa08 --- /dev/null +++ b/TimerControl/TimerControlRepeaterPanel.cpp @@ -0,0 +1,592 @@ +/* + * 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 + * 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 "TimerControlRepeaterPanel.h" +#include "TimerControlApp.h" +#include "DStarDefines.h" +#include "HostFile.h" + +#include + +#include + +enum { + List_Select = 7100, + Button_Add, + Button_Modify, + Button_Delete +}; + +BEGIN_EVENT_TABLE(CTimerControlRepeaterPanel, wxPanel) + EVT_LIST_ITEM_SELECTED(List_Select, CTimerControlRepeaterPanel::onSelect) + + EVT_BUTTON(Button_Add, CTimerControlRepeaterPanel::onAdd) + EVT_BUTTON(Button_Modify, CTimerControlRepeaterPanel::onModify) + EVT_BUTTON(Button_Delete, CTimerControlRepeaterPanel::onDelete) +END_EVENT_TABLE() + +#if defined(__WINDOWS__) +const unsigned int LIST_HEIGHT = 350U; +const unsigned int LIST_WIDTH = 350U; +const unsigned int DAY_WIDTH = 75U; +const unsigned int TIME_WIDTH = 60U; +const unsigned int CALLSIGN_WIDTH = 100U; +const unsigned int TYPE_WIDTH = 85U; +const unsigned int BUTTON_WIDTH = 75U; +const unsigned int HOUR_WIDTH = 40U; +const unsigned int MINUTE_WIDTH = 40U; +const unsigned int REFLECTOR_WIDTH = 80U; +const unsigned int CHANNEL_WIDTH = 40U; +const unsigned int RECONNECT_WIDTH = 75U; +#else +const unsigned int LIST_HEIGHT = 350U; +const unsigned int LIST_WIDTH = 350U; +const unsigned int DAY_WIDTH = 100U; +const unsigned int TIME_WIDTH = 100U; +const unsigned int CALLSIGN_WIDTH = 100U; +const unsigned int TYPE_WIDTH = 65U; +const unsigned int BUTTON_WIDTH = 75U; +const unsigned int HOUR_WIDTH = 50U; +const unsigned int MINUTE_WIDTH = 50U; +const unsigned int REFLECTOR_WIDTH = 90U; +const unsigned int CHANNEL_WIDTH = 50U; +const unsigned int RECONNECT_WIDTH = 100U; +#endif + +const unsigned int BORDER_SIZE = 5U; + +int itemCompare(const void* a, const void* b) +{ + const CTimerControlItem** first = (const CTimerControlItem**)a; + const CTimerControlItem** second = (const CTimerControlItem**)b; + + if ((*first)->m_day != (*second)->m_day) + return int((*second)->m_day) - int((*first)->m_day); + + if ((*first)->m_hour != (*second)->m_hour) + return int((*second)->m_hour) - int((*first)->m_hour); + + return int((*second)->m_minute) - int((*first)->m_minute); +} + +CTimerControlRepeaterPanel::CTimerControlRepeaterPanel(wxWindow* parent, int id, const wxString& callsign) : +wxPanel(parent, id), +m_callsign(callsign), +m_list(NULL), +m_day(NULL), +m_hour(NULL), +m_minute(NULL), +m_reflector(NULL), +m_channel(NULL), +m_reconnect(NULL), +m_item(NULL), +m_n(0) +{ + wxBoxSizer* sizer1 = new wxBoxSizer(wxHORIZONTAL); + + m_list = new wxListCtrl(this, List_Select, wxDefaultPosition, wxSize(LIST_WIDTH, LIST_HEIGHT), wxLC_REPORT | wxLC_SINGLE_SEL); + m_list->InsertColumn(0L, _("Day")); + m_list->SetColumnWidth(0L, DAY_WIDTH); + m_list->InsertColumn(1L, _("Time")); + m_list->SetColumnWidth(1L, TIME_WIDTH); + m_list->InsertColumn(2L, _("Type")); + m_list->SetColumnWidth(2L, TYPE_WIDTH); + m_list->InsertColumn(3L, _("Reflector")); + m_list->SetColumnWidth(3L, REFLECTOR_WIDTH); + sizer1->Add(m_list, 0, wxTOP | wxBOTTOM | wxLEFT | wxEXPAND, BORDER_SIZE); + + wxBoxSizer* sizer2 = new wxBoxSizer(wxVERTICAL); + + m_day = new wxChoice(this, -1, wxDefaultPosition, wxSize(DAY_WIDTH, -1)); + m_day->Append(_("Sunday")); + m_day->Append(_("Monday")); + m_day->Append(_("Tuesday")); + m_day->Append(_("Wednesday")); + m_day->Append(_("Thursday")); + m_day->Append(_("Friday")); + m_day->Append(_("Saturday")); + m_day->Append(_("Every day")); + m_day->Append(_("Mon-Fri")); + m_day->Append(_("Sat-Sun")); + sizer2->Add(m_day, 0, wxALL, BORDER_SIZE); + m_day->SetSelection(0); + + wxBoxSizer* sizer2a = new wxBoxSizer(wxHORIZONTAL); + + m_hour = new wxChoice(this, -1, wxDefaultPosition, wxSize(HOUR_WIDTH, -1)); + m_hour->Append(wxT("00")); + m_hour->Append(wxT("01")); + m_hour->Append(wxT("02")); + m_hour->Append(wxT("03")); + m_hour->Append(wxT("04")); + m_hour->Append(wxT("05")); + m_hour->Append(wxT("06")); + m_hour->Append(wxT("07")); + m_hour->Append(wxT("08")); + m_hour->Append(wxT("09")); + m_hour->Append(wxT("10")); + m_hour->Append(wxT("11")); + m_hour->Append(wxT("12")); + m_hour->Append(wxT("13")); + m_hour->Append(wxT("14")); + m_hour->Append(wxT("15")); + m_hour->Append(wxT("16")); + m_hour->Append(wxT("17")); + m_hour->Append(wxT("18")); + m_hour->Append(wxT("19")); + m_hour->Append(wxT("20")); + m_hour->Append(wxT("21")); + m_hour->Append(wxT("22")); + m_hour->Append(wxT("23")); + sizer2a->Add(m_hour, 0, wxALL, BORDER_SIZE); + m_hour->SetSelection(0); + + wxStaticText* dummy1Label = new wxStaticText(this, -1, wxT(":")); + sizer2a->Add(dummy1Label, 0, wxALL, BORDER_SIZE); + + m_minute = new wxChoice(this, -1, wxDefaultPosition, wxSize(MINUTE_WIDTH, -1)); + m_minute->Append(wxT("00")); + m_minute->Append(wxT("05")); + m_minute->Append(wxT("10")); + m_minute->Append(wxT("15")); + m_minute->Append(wxT("20")); + m_minute->Append(wxT("25")); + m_minute->Append(wxT("30")); + m_minute->Append(wxT("35")); + m_minute->Append(wxT("40")); + m_minute->Append(wxT("45")); + m_minute->Append(wxT("50")); + m_minute->Append(wxT("55")); + sizer2a->Add(m_minute, 0, wxALL, BORDER_SIZE); + m_minute->SetSelection(0); + + sizer2->Add(sizer2a); + + wxStaticText* dummy2Label = new wxStaticText(this, -1, wxEmptyString); + sizer2->Add(dummy2Label, 0, wxALL, BORDER_SIZE); + + wxBoxSizer* sizer3 = new wxBoxSizer(wxHORIZONTAL); + + m_reflector = new wxChoice(this, -1, wxDefaultPosition, wxSize(REFLECTOR_WIDTH, -1)); + m_reflector->Append(_("None")); + + wxFileName fileName1(wxFileName::GetHomeDir(), DPLUS_HOSTS_FILE_NAME); + if (fileName1.IsFileReadable()) { + CHostFile file(fileName1.GetFullPath(), false); + + for (unsigned int i = 0U; i < file.getCount(); i++) + m_reflector->Append(file.getName(i).Trim()); + } + +#if defined(__WINDOWS__) + wxFileName fileName4(::wxGetCwd(), DPLUS_HOSTS_FILE_NAME); +#else + wxFileName fileName4(wxT(DATA_DIR), DPLUS_HOSTS_FILE_NAME); +#endif + if (fileName4.IsFileReadable()) { + CHostFile file(fileName4.GetFullPath(), false); + + for (unsigned int i = 0U; i < file.getCount(); i++) { + wxString name = file.getName(i).Trim(); + if (m_reflector->FindString(name) == wxNOT_FOUND) + m_reflector->Append(name); + } + } + + wxFileName fileName2(wxFileName::GetHomeDir(), DEXTRA_HOSTS_FILE_NAME); + if (fileName2.IsFileReadable()) { + CHostFile file(fileName2.GetFullPath(), false); + + for (unsigned int i = 0U; i < file.getCount(); i++) + m_reflector->Append(file.getName(i).Trim()); + } + +#if defined(__WINDOWS__) + wxFileName fileName5(::wxGetCwd(), DEXTRA_HOSTS_FILE_NAME); +#else + wxFileName fileName5(wxT(DATA_DIR), DEXTRA_HOSTS_FILE_NAME); +#endif + if (fileName5.IsFileReadable()) { + CHostFile file(fileName5.GetFullPath(), false); + + for (unsigned int i = 0U; i < file.getCount(); i++) { + wxString name = file.getName(i).Trim(); + if (m_reflector->FindString(name) == wxNOT_FOUND) + m_reflector->Append(name); + } + } + + wxFileName fileName3(wxFileName::GetHomeDir(), DCS_HOSTS_FILE_NAME); + if (fileName3.IsFileReadable()) { + CHostFile file(fileName3.GetFullPath(), false); + + for (unsigned int i = 0U; i < file.getCount(); i++) + m_reflector->Append(file.getName(i).Trim()); + } + +#if defined(__WINDOWS__) + wxFileName fileName6(::wxGetCwd(), DCS_HOSTS_FILE_NAME); +#else + wxFileName fileName6(wxT(DATA_DIR), DCS_HOSTS_FILE_NAME); +#endif + if (fileName6.IsFileReadable()) { + CHostFile file(fileName6.GetFullPath(), false); + + for (unsigned int i = 0U; i < file.getCount(); i++) { + wxString name = file.getName(i).Trim(); + if (m_reflector->FindString(name) == wxNOT_FOUND) + m_reflector->Append(name); + } + } + + sizer3->Add(m_reflector, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_reflector->SetSelection(0); + + m_channel = new wxChoice(this, -1, wxDefaultPosition, wxSize(CHANNEL_WIDTH, -1)); + m_channel->Append(wxT("A")); + m_channel->Append(wxT("B")); + m_channel->Append(wxT("C")); + m_channel->Append(wxT("D")); + m_channel->Append(wxT("E")); + m_channel->Append(wxT("F")); + m_channel->Append(wxT("G")); + m_channel->Append(wxT("H")); + m_channel->Append(wxT("I")); + m_channel->Append(wxT("J")); + m_channel->Append(wxT("K")); + m_channel->Append(wxT("L")); + m_channel->Append(wxT("M")); + m_channel->Append(wxT("N")); + m_channel->Append(wxT("O")); + m_channel->Append(wxT("P")); + m_channel->Append(wxT("Q")); + m_channel->Append(wxT("R")); + m_channel->Append(wxT("S")); + m_channel->Append(wxT("T")); + m_channel->Append(wxT("U")); + m_channel->Append(wxT("V")); + m_channel->Append(wxT("W")); + m_channel->Append(wxT("X")); + m_channel->Append(wxT("Y")); + m_channel->Append(wxT("Z")); + sizer3->Add(m_channel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_channel->SetSelection(0); + + sizer2->Add(sizer3); + + m_reconnect = new wxChoice(this, -1, wxDefaultPosition, wxSize(RECONNECT_WIDTH, -1)); + m_reconnect->Append(_("Never")); + m_reconnect->Append(_("Fixed")); + m_reconnect->Append(_("5 minutes")); + m_reconnect->Append(_("10 minutes")); + m_reconnect->Append(_("15 minutes")); + m_reconnect->Append(_("20 minutes")); + m_reconnect->Append(_("25 minutes")); + m_reconnect->Append(_("30 minutes")); + m_reconnect->Append(_("60 minutes")); + m_reconnect->Append(_("90 minutes")); + m_reconnect->Append(_("120 minutes")); + m_reconnect->Append(_("180 minutes")); + sizer2->Add(m_reconnect, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_reconnect->SetSelection(0); + + wxStaticText* dummy3Label = new wxStaticText(this, -1, wxEmptyString); + sizer2->Add(dummy3Label, 0, wxALL, BORDER_SIZE); + + wxButton* addButton = new wxButton(this, Button_Add, _("Add"), wxDefaultPosition, wxSize(BUTTON_WIDTH, -1)); + sizer2->Add(addButton, 0, wxALL, BORDER_SIZE); + + wxButton* modButton = new wxButton(this, Button_Modify, _("Modify"), wxDefaultPosition, wxSize(BUTTON_WIDTH, -1)); + sizer2->Add(modButton, 0, wxALL, BORDER_SIZE); + + wxButton* delButton = new wxButton(this, Button_Delete, _("Delete"), wxDefaultPosition, wxSize(BUTTON_WIDTH, -1)); + sizer2->Add(delButton, 0, wxALL, BORDER_SIZE); + + sizer1->Add(sizer2); + + SetAutoLayout(true); + + SetSizer(sizer1); +} + + +CTimerControlRepeaterPanel::~CTimerControlRepeaterPanel() +{ +} + +void CTimerControlRepeaterPanel::load(CTimerControlItem** items, unsigned int count) +{ + for (unsigned int i = 0U; i < count; i++) { + if (items[i]->m_repeater.IsSameAs(m_callsign)) + insertItem(items[i]); + } + + reload(); +} + +unsigned int CTimerControlRepeaterPanel::getCount() const +{ + return (unsigned int)m_list->GetItemCount(); +} + +CTimerControlItem** CTimerControlRepeaterPanel::getItems() const +{ + unsigned int count = getCount(); + + CTimerControlItem** items = new CTimerControlItem*[count]; + + for (unsigned int i = 0U; i < count; i++) + items[i] = (CTimerControlItem*)m_list->GetItemData(i); + + return items; +} + +void CTimerControlRepeaterPanel::insertItem(CTimerControlItem* item) +{ + switch (item->m_day) { + case 0U: m_list->InsertItem(0L, _("Sunday")); break; + case 1U: m_list->InsertItem(0L, _("Monday")); break; + case 2U: m_list->InsertItem(0L, _("Tuesday")); break; + case 3U: m_list->InsertItem(0L, _("Wednesday")); break; + case 4U: m_list->InsertItem(0L, _("Thursday")); break; + case 5U: m_list->InsertItem(0L, _("Friday")); break; + case 6U: m_list->InsertItem(0L, _("Saturday")); break; + case 7U: m_list->InsertItem(0L, _("Every day")); break; + case 8U: m_list->InsertItem(0L, _("Mon-Fri")); break; + case 9U: m_list->InsertItem(0L, _("Sat-Sun")); break; + default: m_list->InsertItem(0L, wxT("??????")); break; + } + + wxString text; + text.Printf(wxT("%02u:%02u"), item->m_hour, item->m_minute); + m_list->SetItem(0L, 1, text); + + switch (item->m_reconnect) { + case RECONNECT_NEVER: m_list->SetItem(0L, 2, _("Never")); break; + case RECONNECT_FIXED: m_list->SetItem(0L, 2, _("Fixed")); break; + case RECONNECT_5MINS: m_list->SetItem(0L, 2, _("5 minutes")); break; + case RECONNECT_10MINS: m_list->SetItem(0L, 2, _("10 minutes")); break; + case RECONNECT_15MINS: m_list->SetItem(0L, 2, _("15 minutes")); break; + case RECONNECT_20MINS: m_list->SetItem(0L, 2, _("20 minutes")); break; + case RECONNECT_25MINS: m_list->SetItem(0L, 2, _("25 minutes")); break; + case RECONNECT_30MINS: m_list->SetItem(0L, 2, _("30 minutes")); break; + case RECONNECT_60MINS: m_list->SetItem(0L, 2, _("60 minutes")); break; + case RECONNECT_90MINS: m_list->SetItem(0L, 2, _("90 minutes")); break; + case RECONNECT_120MINS: m_list->SetItem(0L, 2, _("120 minutes")); break; + case RECONNECT_180MINS: m_list->SetItem(0L, 2, _("180 minutes")); break; + default: m_list->SetItem(0L, 2, wxT("????????")); break; + } + + if (item->m_reflector.IsEmpty()) + m_list->SetItem(0L, 3, _("None")); + else + m_list->SetItem(0L, 3, item->m_reflector); + + m_list->SetItemPtrData(0L, wxUIntPtr(item)); +} + +void CTimerControlRepeaterPanel::onSelect(wxListEvent& event) +{ + m_n = event.GetIndex(); + m_item = (CTimerControlItem*)m_list->GetItemData(m_n); + if (m_item == NULL) + return; + + m_day->SetSelection(int(m_item->m_day)); + m_hour->SetSelection(int(m_item->m_hour)); + m_minute->SetSelection(int(m_item->m_minute / 5U)); + + if (m_item->m_reflector.IsEmpty()) { + m_reflector->SetSelection(0); + m_channel->SetSelection(0); + } else { + m_reflector->SetStringSelection(m_item->m_reflector.Left(7U).Trim()); + m_channel->SetStringSelection(m_item->m_reflector.Mid(7U).Trim()); + } + + m_reconnect->SetSelection(int(m_item->m_reconnect)); +} + +void CTimerControlRepeaterPanel::onAdd(wxCommandEvent&) +{ + int day = m_day->GetSelection(); + int hour = m_hour->GetSelection(); + int minute = m_minute->GetSelection(); + int reflector = m_reflector->GetSelection(); + int channel = m_channel->GetSelection(); + int reconnect = m_reconnect->GetSelection(); + + if (day == wxNOT_FOUND || hour == wxNOT_FOUND || minute == wxNOT_FOUND || reflector == wxNOT_FOUND || channel == wxNOT_FOUND || reconnect == wxNOT_FOUND) + return; + + CTimerControlItem* item = new CTimerControlItem; + + item->m_repeater = m_callsign; + item->m_day = (unsigned int)day; + item->m_hour = (unsigned int)hour; + item->m_minute = (unsigned int)minute * 5U; + + if (reflector != 0) { + wxString callsign = m_reflector->GetStringSelection(); + callsign.Append(wxT(" ")); + callsign.Truncate(7U); + callsign.Append(m_channel->GetStringSelection()); + item->m_reflector = callsign; + } + + item->m_reconnect = RECONNECT(reconnect); + + m_day->SetSelection(0); + m_hour->SetSelection(0); + m_minute->SetSelection(0); + m_reflector->SetSelection(0); + m_channel->SetSelection(0); + m_reconnect->SetSelection(0); + + reload(item); + + ::wxGetApp().writeItems(); +} + +void CTimerControlRepeaterPanel::onModify(wxCommandEvent&) +{ + if (m_item == NULL) + return; + + int day = m_day->GetSelection(); + int hour = m_hour->GetSelection(); + int minute = m_minute->GetSelection(); + int reflector = m_reflector->GetSelection(); + int channel = m_channel->GetSelection(); + int reconnect = m_reconnect->GetSelection(); + + if (day == wxNOT_FOUND || hour == wxNOT_FOUND || minute == wxNOT_FOUND || reflector == wxNOT_FOUND || channel == wxNOT_FOUND || reconnect == wxNOT_FOUND) + return; + + CTimerControlItem* item = new CTimerControlItem; + + item->m_repeater = m_callsign; + item->m_day = (unsigned int)day; + item->m_hour = (unsigned int)hour; + item->m_minute = (unsigned int)minute * 5U; + + if (reflector != 0) { + wxString callsign = m_reflector->GetStringSelection(); + callsign.Append(wxT(" ")); + callsign.Truncate(7U); + callsign.Append(m_channel->GetStringSelection()); + item->m_reflector = callsign; + } + + item->m_reconnect = RECONNECT(reconnect); + + m_list->DeleteItem(m_n); + + delete m_item; + m_item = NULL; + + m_day->SetSelection(0); + m_hour->SetSelection(0); + m_minute->SetSelection(0); + m_reflector->SetSelection(0); + m_channel->SetSelection(0); + m_reconnect->SetSelection(0); + + reload(item); + + ::wxGetApp().writeItems(); +} + +void CTimerControlRepeaterPanel::onDelete(wxCommandEvent&) +{ + if (m_item == NULL) + return; + + m_list->DeleteItem(m_n); + + delete m_item; + m_item = NULL; + + m_day->SetSelection(0); + m_hour->SetSelection(0); + m_minute->SetSelection(0); + m_reflector->SetSelection(0); + m_channel->SetSelection(0); + m_reconnect->SetSelection(0); + + ::wxGetApp().writeItems(); +} + +void CTimerControlRepeaterPanel::reload(CTimerControlItem* item) +{ + int n = m_list->GetItemCount(); + + CTimerControlItem** items = new CTimerControlItem*[n + 1]; + + unsigned int count = 0U; + for (int i = 0; i < n; i++) { + CTimerControlItem* listItem = (CTimerControlItem*)m_list->GetItemData(i); + + // Reject any that have the same day, hour and minute settings + if (item != NULL) { + // Match the exact day, hour, and minute + if (listItem->m_day == item->m_day && listItem->m_hour == item->m_hour && listItem->m_minute == item->m_minute) { + delete listItem; + // Match every day, hour, and minute + } else if (7U == item->m_day && listItem->m_hour == item->m_hour && listItem->m_minute == item->m_minute) { + delete listItem; + // Match every day, hour, and minute + } else if (listItem->m_day == 7U && listItem->m_hour == item->m_hour && listItem->m_minute == item->m_minute) { + delete listItem; + // Match week day, hour, and minute + } else if (8U == item->m_day && ((listItem->m_day >= 1U && listItem->m_day <= 5U) || listItem->m_day == 7U) && listItem->m_hour == item->m_hour && listItem->m_minute == item->m_minute) { + delete listItem; + // Match week day, hour, and minute + } else if (listItem->m_day == 8U && ((item->m_day >= 1U && item->m_day <= 5U) || item->m_day == 7U) && listItem->m_hour == item->m_hour && listItem->m_minute == item->m_minute) { + delete listItem; + // Match weekend, hour, and minute + } else if (9U == item->m_day && (listItem->m_day == 0U || listItem->m_day == 6U || listItem->m_day == 7U) && listItem->m_hour == item->m_hour && listItem->m_minute == item->m_minute) { + delete listItem; + // Match weekend, hour, and minute + } else if (listItem->m_day == 9U && (item->m_day == 0U || item->m_day == 6U || item->m_day == 7U) && listItem->m_hour == item->m_hour && listItem->m_minute == item->m_minute) { + delete listItem; + } else { + items[count] = listItem; + count++; + } + } else { + items[count] = listItem; + count++; + } + } + + // Put the new one at the end of the list + if (item != NULL) { + items[count] = item; + count++; + } + + m_list->DeleteAllItems(); + + ::qsort(items, count, sizeof(CTimerControlItem*), itemCompare); + + for (unsigned int i = 0U; i < count; i++) + insertItem(items[i]); + + delete[] items; +} diff --git a/TimerControl/TimerControlRepeaterPanel.h b/TimerControl/TimerControlRepeaterPanel.h new file mode 100644 index 0000000..9e5ac27 --- /dev/null +++ b/TimerControl/TimerControlRepeaterPanel.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2011 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 TimerControlRepeaterPanel_H +#define TimerControlRepeaterPanel_H + +#include "TimerControlItem.h" + +#include +#include + +class CTimerControlRepeaterPanel : public wxPanel { +public: + CTimerControlRepeaterPanel(wxWindow* parent, int id, const wxString& callsign); + virtual ~CTimerControlRepeaterPanel(); + + virtual void onSelect(wxListEvent& event); + + virtual void onAdd(wxCommandEvent& event); + virtual void onModify(wxCommandEvent& event); + virtual void onDelete(wxCommandEvent& event); + + virtual void load(CTimerControlItem** items, unsigned int count); + + virtual unsigned int getCount() const; + virtual CTimerControlItem** getItems() const; + +private: + wxString m_callsign; + wxListCtrl* m_list; + wxChoice* m_day; + wxChoice* m_hour; + wxChoice* m_minute; + wxChoice* m_reflector; + wxChoice* m_channel; + wxChoice* m_reconnect; + CTimerControlItem* m_item; + int m_n; + + DECLARE_EVENT_TABLE() + + void insertItem(CTimerControlItem* item); + void reload(CTimerControlItem* item = NULL); +}; + +#endif diff --git a/TimerControl/TimerControlThread.cpp b/TimerControl/TimerControlThread.cpp new file mode 100644 index 0000000..d5182fd --- /dev/null +++ b/TimerControl/TimerControlThread.cpp @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2011,2012 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 "TimerControlItemFile.h" +#include "TimerControlThread.h" +#include "SHA256.h" + + +CTimerControlThread::CTimerControlThread() : +m_password(), +m_fileName(), +m_delay(false), +m_count(0U), +m_items(NULL), +m_mutex(), +m_handler(NULL), +m_killed(false) +{ +} + +CTimerControlThread::~CTimerControlThread() +{ + if (m_items != NULL) { + for (unsigned int i = 0U; i < m_count; i++) + delete m_items[i]; + + delete[] m_items; + } + + delete m_handler; +} + +void CTimerControlThread::run() +{ + if (m_delay) { + for (unsigned int i = 0U; i < 20U; i++) { + if (m_killed) + return; + + ::wxMilliSleep(1000UL); + } + } else { + ::wxMilliSleep(100UL); + } + + int lastDay = 0; + int lastHour = 0; + int lastMin = 0; + + while (!m_killed) { + time_t t; + ::time(&t); + + struct tm* tm = ::localtime(&t); + int day = tm->tm_wday; + int hour = tm->tm_hour; + int min = tm->tm_min; + + if (day != lastDay || hour != lastHour || min != lastMin) { + m_mutex.Lock(); + + bool open = false; + for (unsigned int i = 0U; i < m_count; i++) { + // Check the day matching + if (int(m_items[i]->m_day) == day || // Exact day match + m_items[i]->m_day == 7U || // Every day match + (m_items[i]->m_day == 8U && day >= 1 && day <= 5) || // Weekday match + (m_items[i]->m_day == 9U && (day == 0 || day == 6))) { // Weekend match + + // Check the time matching + if (int(m_items[i]->m_hour) == hour && int(m_items[i]->m_minute) == min) { + if (m_items[i]->m_reflector.IsEmpty()) + wxLogMessage(wxT("Linking \"%s\" to \"None\" with reconnect %d"), m_items[i]->m_repeater.c_str(), m_items[i]->m_reconnect); + else + wxLogMessage(wxT("Linking \"%s\" to \"%s\" with reconnect %d"), m_items[i]->m_repeater.c_str(), m_items[i]->m_reflector.c_str(), m_items[i]->m_reconnect); + + if (!open) + open = login(); + + if (open) + link(m_items[i]->m_repeater, m_items[i]->m_reconnect, m_items[i]->m_reflector); + } + } + } + + m_mutex.Unlock(); + + if (open) + logoff(); + + lastDay = day; + lastHour = hour; + lastMin = min; + } + + ::wxMilliSleep(5000UL); + } +} + +void CTimerControlThread::setGateway(const wxString& address, unsigned int port, const wxString& password) +{ + m_password = password; + + delete m_handler; + + m_handler = new CTimerControlRemoteControlHandler(address, port); +} + +void CTimerControlThread::setDelay(bool enabled) +{ + m_delay = enabled; +} + +void CTimerControlThread::setFileName(const wxString& fileName) +{ + m_fileName = fileName; +} + +void CTimerControlThread::reload() +{ + wxMutexLocker lock(m_mutex); + + wxLogMessage(wxT("Reloading the schedule file")); + + // Remove the old schedule + if (m_items != NULL) { + for (unsigned int i = 0U; i < m_count; i++) + delete m_items[i]; + + delete[] m_items; + m_items = NULL; + } + + CTimerControlItemFile file(m_fileName); + m_items = file.readItems(m_count); +} + +void CTimerControlThread::kill() +{ + m_killed = true; +} + +bool CTimerControlThread::login() +{ + wxASSERT(m_handler != NULL); + + wxLogMessage(wxT("Logging into the gateway")); + + bool ret = m_handler->open(); + if (!ret) { + wxLogError(wxT("Error opening the port")); + return false; + } + + TC_TYPE type; + + do { + ret = m_handler->login(); + if (!ret) { + wxLogError(wxT("Error when sending the login command")); + m_handler->close(); + return false; + } + + ::wxMilliSleep(100UL); + + type = m_handler->readType(); + switch (type) { + case TCT_RANDOM: + wxLogMessage(wxT("Read the random number")); + break; + case TCT_NAK: + wxLogError(wxT("Received a NAK when asking for the random number")); + m_handler->setLoggedIn(false); + m_handler->readNAK(); + m_handler->close(); + return false; + default: + ::wxMilliSleep(100UL); + break; + } + } while (type != TCT_RANDOM); + + unsigned int rnd = m_handler->readRandom(); + + do { + ret = sendHash(rnd); + if (!ret) { + wxLogError(wxT("Error when sending the hash")); + m_handler->close(); + return false; + } + + ::wxMilliSleep(100UL); + + type = m_handler->readType(); + switch (type) { + case TCT_ACK: + wxLogMessage(wxT("Logged in to the gateway")); + m_handler->setLoggedIn(true); + break; + case TCT_NAK: + wxLogError(wxT("Received a NAK after sending the hash")); + m_handler->setLoggedIn(false); + m_handler->readNAK(); + m_handler->close(); + return false; + default: + ::wxMilliSleep(100UL); + break; + } + } while (type != TCT_ACK); + + return true; +} + +bool CTimerControlThread::link(const wxString& repeater, RECONNECT reconnect, const wxString& reflector) +{ + wxASSERT(m_handler != NULL); + + for (;;) { + bool ret = m_handler->link(repeater, reconnect, reflector); + if (!ret) { + wxLogError(wxT("Error when sending the link command")); + return false; + } + + ::wxMilliSleep(100UL); + + TC_TYPE type = m_handler->readType(); + switch (type) { + case TCT_ACK: + wxLogMessage(wxT("Sent the link command OK")); + return true; + case TCT_NAK: + wxLogError(wxT("Received a NAK after sending the link command")); + m_handler->readNAK(); + return false; + default: + ::wxMilliSleep(100UL); + break; + } + } +} + +void CTimerControlThread::logoff() +{ + wxASSERT(m_handler != NULL); + + wxLogMessage(wxT("Logging out of the gateway")); + + m_handler->logout(); + m_handler->setLoggedIn(false); + m_handler->close(); +} + +bool CTimerControlThread::sendHash(unsigned int rnd) +{ + unsigned int len = m_password.Len() + sizeof(unsigned int); + unsigned char* in = new unsigned char[len]; + unsigned char* out = new unsigned char[32U]; + + ::memcpy(in, &rnd, sizeof(unsigned int)); + for (unsigned int i = 0U; i < m_password.Len(); i++) + in[i + sizeof(unsigned int)] = m_password.GetChar(i); + + CSHA256 sha256; + sha256.buffer(in, len, out); + + bool ret = m_handler->sendHash(out, 32U); + + delete[] in; + delete[] out; + + return ret; +} diff --git a/TimerControl/TimerControlThread.h b/TimerControl/TimerControlThread.h new file mode 100644 index 0000000..e090cd7 --- /dev/null +++ b/TimerControl/TimerControlThread.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2011,2012 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 TimerControlThread_H +#define TimerControlThread_H + +#include "TimerControlRemoteControlHandler.h" +#include "TimerControlItem.h" +#include "Defs.h" + +#include + +class CTimerControlThread { +public: + CTimerControlThread(); + virtual ~CTimerControlThread(); + + virtual void setGateway(const wxString& address, unsigned int port, const wxString& password); + virtual void setDelay(bool enabled); + virtual void setFileName(const wxString& fileName); + + virtual void reload(); + + virtual void run(); + virtual void kill(); + +private: + wxString m_password; + wxString m_fileName; + bool m_delay; + unsigned int m_count; + CTimerControlItem** m_items; + wxMutex m_mutex; + CTimerControlRemoteControlHandler* m_handler; + bool m_killed; + + bool login(); + bool link(const wxString& repeater, RECONNECT reconnect, const wxString& reflector); + void logoff(); + + bool sendHash(unsigned int rnd); +}; + +#endif diff --git a/TimerControl/TimerControlThreadHelper.cpp b/TimerControl/TimerControlThreadHelper.cpp new file mode 100644 index 0000000..181ce3a --- /dev/null +++ b/TimerControl/TimerControlThreadHelper.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2012 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 "TimerControlThreadHelper.h" + +CTimerControlThreadHelper::CTimerControlThreadHelper(CTimerControlThread* thread) : +wxThread(wxTHREAD_JOINABLE), +m_thread(thread) +{ + wxASSERT(thread != NULL); +} + +CTimerControlThreadHelper::~CTimerControlThreadHelper() +{ + delete m_thread; +} + +void CTimerControlThreadHelper::start() +{ + Create(); + + SetPriority(100U); + + Run(); +} + +void* CTimerControlThreadHelper::Entry() +{ + wxASSERT(m_thread != NULL); + + m_thread->run(); + + return NULL; +} + +void CTimerControlThreadHelper::kill() +{ + wxASSERT(m_thread != NULL); + + m_thread->kill(); + + Wait(); +} + +void CTimerControlThreadHelper::reload() +{ + wxASSERT(m_thread != NULL); + + m_thread->reload(); +} diff --git a/TimerControl/TimerControlThreadHelper.h b/TimerControl/TimerControlThreadHelper.h new file mode 100644 index 0000000..fdb47c0 --- /dev/null +++ b/TimerControl/TimerControlThreadHelper.h @@ -0,0 +1,44 @@ +/* + * 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 + * 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 TimerControlThreadHelper_H +#define TimerControlThreadHelper_H + +#include "TimerControlThread.h" + +#include + +class CTimerControlThreadHelper : public wxThread { + +public: + CTimerControlThreadHelper(CTimerControlThread* thread); + virtual ~CTimerControlThreadHelper(); + + virtual void start(); + + virtual void* Entry(); + + virtual void kill(); + + virtual void reload(); + +private: + CTimerControlThread* m_thread; +}; + +#endif diff --git a/VoiceTransmit/VoiceStore.cpp b/VoiceTransmit/VoiceStore.cpp new file mode 100644 index 0000000..01ca126 --- /dev/null +++ b/VoiceTransmit/VoiceStore.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (C) 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 "DStarDefines.h" +#include "VoiceStore.h" + +CVoiceStore::CVoiceStore(const wxArrayString& filenames) : +m_filenames(filenames), +m_header(NULL), +m_fileNumber(0U), +m_file() +{ +} + +CVoiceStore::~CVoiceStore() +{ +} + +bool CVoiceStore::open() +{ + wxString fileName = m_filenames.Item(0U); + + bool ret = m_file.open(fileName); + if (!ret) + return false; + + DVTFR_TYPE type = m_file.read(); + if (type != DVTFR_HEADER) { + m_file.close(); + return false; + } + + m_header = m_file.readHeader(); + m_fileNumber = 0U; + + return true; +} + +CHeaderData* CVoiceStore::getHeader() +{ + CHeaderData* header = m_header; + + m_header = NULL; + + return header; +} + +CAMBEData* CVoiceStore::getAMBE() +{ + DVTFR_TYPE type = m_file.read(); + if (type == DVTFR_DATA) { + CAMBEData* ambe = m_file.readData(); + if (ambe != NULL) + return ambe; + } + + for (;;) { + m_file.close(); + m_fileNumber++; + + // The end of the last file? + if (m_fileNumber == m_filenames.GetCount()) + return NULL; + + wxString filename = m_filenames.Item(m_fileNumber); + + bool ret = m_file.open(filename); + if (!ret) + return NULL; + + // This should get the header + type = m_file.read(); + + if (type == DVTFR_HEADER) { + // Throw the header away + CHeaderData* header = m_file.readHeader(); + delete header; + } else if (type == DVTFR_DATA) { + // Shouldn't usually happen + CAMBEData* ambe = m_file.readData(); + if (ambe != NULL) + return ambe; + else + continue; + } else { + // !!!! + continue; + } + + // This should get the first data record + type = m_file.read(); + + if (type == DVTFR_DATA) { + CAMBEData* ambe = m_file.readData(); + if (ambe != NULL) + return ambe; + } + } +} + +void CVoiceStore::close() +{ + m_file.close(); +} diff --git a/VoiceTransmit/VoiceStore.h b/VoiceTransmit/VoiceStore.h new file mode 100644 index 0000000..a87b3d0 --- /dev/null +++ b/VoiceTransmit/VoiceStore.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 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. + */ + +#ifndef VoiceStore_H +#define VoiceStore_H + +#include "DVTOOLFileReader.h" +#include "HeaderData.h" +#include "AMBEData.h" + +#include + +class CVoiceStore { +public: + CVoiceStore(const wxArrayString& filenames); + ~CVoiceStore(); + + bool open(); + + CHeaderData* getHeader(); + + CAMBEData* getAMBE(); + + void close(); + +private: + wxArrayString m_filenames; + CHeaderData* m_header; + unsigned int m_fileNumber; + CDVTOOLFileReader m_file; +}; + +#endif diff --git a/VoiceTransmit/VoiceTransmit.cpp b/VoiceTransmit/VoiceTransmit.cpp new file mode 100644 index 0000000..2b35cbf --- /dev/null +++ b/VoiceTransmit/VoiceTransmit.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (C) 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 "DStarDefines.h" +#include "VoiceTransmit.h" + +int main(int argc, char** argv) +{ + bool res = ::wxInitialize(); + if (!res) { + ::fprintf(stderr, "voicetransmit: failed to initialise the wxWidgets library, exiting\n"); + return 1; + } + + if (argc < 3) { + ::fprintf(stderr, "voicetransmit: invalid command line usage: voicetransmit ..., exiting\n"); + ::wxUninitialize(); + return 1; + } + + wxString repeater = wxString(argv[1], wxConvLocal); + repeater.Replace(wxT("_"), wxT(" ")); + repeater.resize(LONG_CALLSIGN_LENGTH, wxT(' ')); + repeater.MakeUpper(); + + wxArrayString filenames; + for (int i = 2; i < argc; i++) + filenames.Add(wxString(argv[i], wxConvLocal)); + + CVoiceStore store(filenames); + bool opened = store.open(); + if (!opened) { + ::fprintf(stderr, "voicetransmit: unable to open one of the files, exiting\n"); + ::wxUninitialize(); + return 1; + } + + CVoiceTransmit tt(repeater, &store); + bool ret = tt.run(); + + store.close(); + + ::wxUninitialize(); + + return ret ? 0 : 1; +} + +CVoiceTransmit::CVoiceTransmit(const wxString& callsign, CVoiceStore* store) : +m_socket(wxEmptyString, 0U), +m_callsign(callsign), +m_store(store) +{ + wxASSERT(store != NULL); +} + +CVoiceTransmit::~CVoiceTransmit() +{ +} + +bool CVoiceTransmit::run() +{ + bool opened = m_socket.open(); + if (!opened) + return false; + + in_addr address = CUDPReaderWriter::lookup(wxT("127.0.0.1")); + + unsigned int id = CHeaderData::createId(); + + wxString callsignG = m_callsign.Left(LONG_CALLSIGN_LENGTH - 1U); + callsignG.Append(wxT("G")); + + CHeaderData* header = m_store->getHeader(); + if (header == NULL) { + m_socket.close(); + return false; + } + + header->setId(id); + header->setRptCall1(callsignG); + header->setRptCall2(m_callsign); + header->setDestination(address, G2_DV_PORT); + + sendHeader(header); + + delete header; + + wxStopWatch timer; + timer.Start(); + + unsigned int out = 0U; + unsigned int seqNo = 0U; + + for (;;) { + unsigned int needed = timer.Time() / DSTAR_FRAME_TIME_MS; + + while (out < needed) { + CAMBEData* ambe = m_store->getAMBE(); + + if (ambe == NULL) { + seqNo++; + if (seqNo >= 21U) + seqNo = 0U; + + CAMBEData data; + data.setData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES); + data.setDestination(address, G2_DV_PORT); + data.setId(id); + data.setSeq(seqNo); + data.setEnd(true); + + sendData(&data); + + m_socket.close(); + + return true; + } + + seqNo = ambe->getSeq(); + + ambe->setDestination(address, G2_DV_PORT); + ambe->setEnd(false); + ambe->setId(id); + + sendData(ambe); + + delete ambe; + + out++; + } + + ::wxMilliSleep(10UL); + } +} + +bool CVoiceTransmit::sendHeader(CHeaderData* header) +{ + wxASSERT(header != NULL); + + unsigned char buffer[60U]; + unsigned int length = header->getG2Data(buffer, 60U, true); + + for (unsigned int i = 0U; i < 2U; i++) { + bool res = m_socket.write(buffer, length, header->getYourAddress(), header->getYourPort()); + if (!res) + return false; + } + + return true; +} + +bool CVoiceTransmit::sendData(CAMBEData* data) +{ + wxASSERT(data != NULL); + + unsigned char buffer[40U]; + unsigned int length = data->getG2Data(buffer, 40U); + + return m_socket.write(buffer, length, data->getYourAddress(), data->getYourPort()); +} diff --git a/VoiceTransmit/VoiceTransmit.h b/VoiceTransmit/VoiceTransmit.h new file mode 100644 index 0000000..868292d --- /dev/null +++ b/VoiceTransmit/VoiceTransmit.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 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. + */ + +#ifndef VoiceTransmit_H +#define VoiceTransmit_H + +#include "UDPReaderWriter.h" +#include "VoiceStore.h" +#include "HeaderData.h" +#include "AMBEData.h" + +class CVoiceTransmit { +public: + CVoiceTransmit(const wxString& callsign, CVoiceStore* store); + ~CVoiceTransmit(); + + bool run(); + +private: + CUDPReaderWriter m_socket; + wxString m_callsign; + CVoiceStore* m_store; + + bool sendHeader(CHeaderData* header); + bool sendData(CAMBEData* data); +}; + +#endif diff --git a/VoiceTransmit/VoiceTransmit.vcxproj b/VoiceTransmit/VoiceTransmit.vcxproj new file mode 100644 index 0000000..c0af277 --- /dev/null +++ b/VoiceTransmit/VoiceTransmit.vcxproj @@ -0,0 +1,187 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {27D44C4F-6849-4E15-A5BB-0B54BA296D98} + VoiceTransmit + Win32Proj + 10.0.16299.0 + + + + Application + v141 + Unicode + true + + + Application + v141 + Unicode + true + + + Application + v141 + Unicode + + + Application + v141 + Unicode + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>14.0.24720.0 + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + $(WXWIN)\include;$(WXWIN)\lib\vc_dll\mswud;$(IncludePath) + + + true + $(WXWIN)\include;$(WXWIN)\lib\vc_x64_dll\mswud;$(IncludePath) + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + + + false + + + + Disabled + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATEWIN32;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + Level3 + EditAndContinue + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_dll;%(AdditionalLibraryDirectories) + true + Console + MachineX86 + + + + + Disabled + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATEWIN32;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + ProgramDatabase + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_x64_dll;%(AdditionalLibraryDirectories) + true + Console + + + + + MaxSpeed + true + $(WXWIN)\include\msvc;$(WXWIN)\include;../ircDDB;../Common;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + Level3 + ProgramDatabase + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_dll;%(AdditionalLibraryDirectories) + true + Console + true + true + MachineX86 + + + + + MaxSpeed + true + $(WXWIN)\include\msvc;$(WXWIN)\include;../ircDDB;../Common;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_x64_dll;%(AdditionalLibraryDirectories) + true + Console + true + true + + + + + + + + + + + + + {e793cb8e-2ac9-431a-bbfc-3f52537bb3cf} + false + + + + + + \ No newline at end of file diff --git a/VoiceTransmit/VoiceTransmit.vcxproj.filters b/VoiceTransmit/VoiceTransmit.vcxproj.filters new file mode 100644 index 0000000..91f12c6 --- /dev/null +++ b/VoiceTransmit/VoiceTransmit.vcxproj.filters @@ -0,0 +1,29 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/debian/README b/debian/README new file mode 100644 index 0000000..d774460 --- /dev/null +++ b/debian/README @@ -0,0 +1,6 @@ +The Debian Package ircddbgateway +---------------------------- + +Comments regarding the Package + + -- Jeremy McDermond Thu, 31 Mar 2016 12:47:42 -0700 diff --git a/debian/README.Debian b/debian/README.Debian new file mode 100644 index 0000000..0d1e5a9 --- /dev/null +++ b/debian/README.Debian @@ -0,0 +1,6 @@ +ircddbgateway for Debian +------------------------ + + + + -- Jeremy McDermond Thu, 31 Mar 2016 12:47:42 -0700 diff --git a/debian/README.source b/debian/README.source new file mode 100644 index 0000000..a292090 --- /dev/null +++ b/debian/README.source @@ -0,0 +1,10 @@ +ircddbgateway for Debian +------------------------ + + + + + + -- Jeremy McDermond Thu, 31 Mar 2016 12:47:42 -0700 + diff --git a/debian/aprstransmitd.aprstransmitd@.service b/debian/aprstransmitd.aprstransmitd@.service new file mode 100644 index 0000000..2ecad69 --- /dev/null +++ b/debian/aprstransmitd.aprstransmitd@.service @@ -0,0 +1,14 @@ +[Unit] +Description=D-STAR APRS Transmit Daemon +Wants=ircddbgatewayd.service +After=ircddbgatewayd.service + +[Service] +User=opendv +EnvironmentFile=/etc/opendv/aprstransmitd_%i.conf +ExecStart=/usr/sbin/aprstransmitd %i -host $APRS_SERVER -port $APRS_PORT -filter ${FILTER} +Restart=on-abort + +[Install] +WantedBy=multi-user.target +DefaultInstance=1 diff --git a/debian/aprstransmitd.conf b/debian/aprstransmitd.conf new file mode 100644 index 0000000..20e49e0 --- /dev/null +++ b/debian/aprstransmitd.conf @@ -0,0 +1,3 @@ +FILTER="m/50" +APRS_SERVER=rotate.aprs2.net +APRS_PORT=14580 diff --git a/debian/aprstransmitd.install b/debian/aprstransmitd.install new file mode 100644 index 0000000..5357072 --- /dev/null +++ b/debian/aprstransmitd.install @@ -0,0 +1,2 @@ +usr/sbin/aprstransmitd +../aprstransmitd.conf etc/opendv diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..96d755e --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +ircddbgateway (1.20160331-1) unstable; urgency=low + + * Initial Release. + + -- Jeremy McDermond Thu, 31 Mar 2016 12:47:42 -0700 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..ec63514 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +9 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..2ed1d4f --- /dev/null +++ b/debian/control @@ -0,0 +1,91 @@ +Source: ircddbgateway +Section: hamradio +Priority: optional +Maintainer: Jeremy McDermond +Build-Depends: debhelper (>= 9), autotools-dev , pkg-config, libwxgtk3.0-dev +Standards-Version: 3.9.6 +Homepage: https://github.com/dl5di/OpenDV + +Package: opendv-base +Architecture: all +Description: D-STAR Digital Voice Base Components + + +Package: ircddbgatewayd +Architecture: armhf armel i386 amd64 +Depends: ${shlibs:Depends}, ${misc:Depends}, opendv-base +Description: D-STAR Digital Voice Internet Gateway Daemon Components + + +Package: ircddbgateway +Architecture: armhf armel i386 amd64 +Depends: ${shlibs:Depends}, ${misc:Depends}, opendv-base +Description: D-STAR Digital Voice Internet Gateway GUI Components + + +Package: starnetserverd +Architecture: armhf armel i386 amd64 +Depends: ${shlibs:Depends}, ${misc:Depends}, opendv-base +Description: D-STAR Digital Voice StarNet Daemon Components + + +Package: starnetserver +Architecture: armhf armel i386 amd64 +Depends: ${shlibs:Depends}, ${misc:Depends}, opendv-base +Description: D-STAR Digital Voice StarNet GUI Components + + +Package: remotecontrol +Architecture: armhf armel i386 amd64 +Depends: ${shlibs:Depends}, ${misc:Depends}, opendv-base +Description: D-STAR Digital Voice Internet Gateway Remote Control + Used to control either an ircDDBGateway or a STARnet Digital Server. + +Package: remotecontrold +Architecture: armhf armel i386 amd64 +Depends: ${shlibs:Depends}, ${misc:Depends}, opendv-base +Description: D-STAR Digital Voice Internet Gateway Remote Control - Command Line + Used to control either an ircDDBGateway or a STARnet Digital Server. + Command line version. + +Package: timercontrol +Architecture: armhf armel i386 amd64 +Depends: ${shlibs:Depends}, ${misc:Depends}, opendv-base +Description: D-STAR Digital Voice Internet Gateway Timer Control GUI Components + The Timer Control application and daemon are used to control the reflector + links of the ircDDB Gateway. It allows a time schedule to be entered, so + that access to the various reflectors can depend on the day and time. + +Package: timercontrold +Architecture: armhf armel i386 amd64 +Depends: ${shlibs:Depends}, ${misc:Depends}, opendv-base +Description: D-STAR Digital Voice Internet Gateway Timer Control Daemon Components + The Timer Control application and daemon are used to control the reflector + links of the ircDDB Gateway. It allows a time schedule to be entered, so + that access to the various reflectors can depend on the day and time. + +Package: timeserver +Architecture: armhf armel i386 amd64 +Depends: ${shlibs:Depends}, ${misc:Depends}, opendv-base +Description: D-STAR Digital Voice Internet Gateway Time Server GUI Components + The Time Server is used with the ircDDB Gateway to add time announcements to + the system. + +Package: timeserverd +Architecture: armhf armel i386 amd64 +Depends: ${shlibs:Depends}, ${misc:Depends}, opendv-base +Description: D-STAR Digital Voice Internet Gateway Time Server Daemon Components + The Time Server is used with the ircDDB Gateway to add time announcements to + the system. + +Package: ircddbgateway-tools +Architecture: armhf armel i386 amd64 +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: D-STAR Digital Voice Internet Gateway Transmit Tools + These tools are used to transmit by hand to APRS, D-STAR text or voice. + +Package: aprstransmitd +Architecture: armhf armel i386 amd64 +Depends: ${shlibs:Depends}, ${misc:Depends}, opendv-base +Description: D-STAR Digital Voice Internet Gateway APRS Tansmit Daemon + This daemon is used to echo APRS data into a D-STAR gateway. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..d6ff572 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,34 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: ircddbgateway +Source: + +Files: * +Copyright: + +License: GPL-2.0+ + +Files: debian/* +Copyright: 2016 Jeremy McDermond +License: GPL-2.0+ + +License: GPL-2.0+ + This package 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 package 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, see + . + On Debian systems, the complete text of the GNU General + Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". + +# Please also look if there are files or directories which have a +# different copyright/license attached and list them here. +# Please avoid picking licenses with terms that are more restrictive than the +# packaged work, as it may make Debian's contributions unacceptable upstream. diff --git a/debian/docs b/debian/docs new file mode 100644 index 0000000..b9eb9fd --- /dev/null +++ b/debian/docs @@ -0,0 +1,3 @@ +BUILD.txt +CHANGES.txt +COPYING.txt diff --git a/debian/ircddbgateway-tools.install b/debian/ircddbgateway-tools.install new file mode 100644 index 0000000..516b271 --- /dev/null +++ b/debian/ircddbgateway-tools.install @@ -0,0 +1,3 @@ +usr/bin/voicetransmit +usr/bin/aprstransmit +usr/bin/texttransmit diff --git a/debian/ircddbgateway.install b/debian/ircddbgateway.install new file mode 100644 index 0000000..28cd041 --- /dev/null +++ b/debian/ircddbgateway.install @@ -0,0 +1,2 @@ +usr/bin/ircddbgateway +usr/bin/ircddbgatewayconfig diff --git a/debian/ircddbgateway.logrotate b/debian/ircddbgateway.logrotate new file mode 100644 index 0000000..247f063 --- /dev/null +++ b/debian/ircddbgateway.logrotate @@ -0,0 +1,7 @@ +/var/log/opendv/ircddbgateway.log { + rotate 14 + daily + compress + missingok + copytruncate +} diff --git a/debian/ircddbgatewayd.install b/debian/ircddbgatewayd.install new file mode 100644 index 0000000..8b9572d --- /dev/null +++ b/debian/ircddbgatewayd.install @@ -0,0 +1 @@ +usr/sbin/ircddbgatewayd diff --git a/debian/ircddbgatewayd.ircddbgatewayd.service b/debian/ircddbgatewayd.ircddbgatewayd.service new file mode 100644 index 0000000..48010dd --- /dev/null +++ b/debian/ircddbgatewayd.ircddbgatewayd.service @@ -0,0 +1,11 @@ +[Unit] +Description=D-STAR Gateway Daemon +After=network.target + +[Service] +User=opendv +ExecStart=/usr/sbin/ircddbgatewayd +Restart=on-abort + +[Install] +WantedBy=multi-user.target diff --git a/debian/ircddbgatewayd.logrotate b/debian/ircddbgatewayd.logrotate new file mode 100644 index 0000000..a2754f5 --- /dev/null +++ b/debian/ircddbgatewayd.logrotate @@ -0,0 +1,7 @@ +/var/log/opendv/ircddbgatewayd.log { + rotate 14 + daily + compress + missingok + copytruncate +} diff --git a/debian/opendv-base.dirs b/debian/opendv-base.dirs new file mode 100644 index 0000000..ae9d91a --- /dev/null +++ b/debian/opendv-base.dirs @@ -0,0 +1,2 @@ +etc/opendv +var/log/opendv diff --git a/debian/opendv-base.install b/debian/opendv-base.install new file mode 100644 index 0000000..d14edc5 --- /dev/null +++ b/debian/opendv-base.install @@ -0,0 +1,34 @@ +usr/share/opendv/CCS_Hosts.txt +usr/share/opendv/DCS_Hosts.txt +usr/share/opendv/de_DE.ambe +usr/share/opendv/de_DE.indx +usr/share/opendv/DExtra_Hosts.txt +usr/share/opendv/dk_DK.ambe +usr/share/opendv/dk_DK.indx +usr/share/opendv/DPlus_Hosts.txt +usr/share/opendv/en_GB.ambe +usr/share/opendv/en_GB.indx +usr/share/opendv/en_US.ambe +usr/share/opendv/en_US.indx +usr/share/opendv/es_ES.ambe +usr/share/opendv/es_ES.indx +usr/share/opendv/fr_FR.ambe +usr/share/opendv/fr_FR.indx +usr/share/opendv/it_IT.ambe +usr/share/opendv/it_IT.indx +usr/share/opendv/no_NO.ambe +usr/share/opendv/no_NO.indx +usr/share/opendv/pl_PL.ambe +usr/share/opendv/pl_PL.indx +usr/share/opendv/se_SE.ambe +usr/share/opendv/se_SE.indx +usr/share/opendv/TIME_de_DE.ambe +usr/share/opendv/TIME_de_DE.indx +usr/share/opendv/TIME_en_GB.ambe +usr/share/opendv/TIME_en_GB.indx +usr/share/opendv/TIME_en_US.ambe +usr/share/opendv/TIME_en_US.indx +usr/share/opendv/TIME_fr_FR.ambe +usr/share/opendv/TIME_fr_FR.indx +usr/share/opendv/TIME_se_SE.ambe +usr/share/opendv/TIME_se_SE.indx diff --git a/debian/opendv-base.postinst b/debian/opendv-base.postinst new file mode 100644 index 0000000..7ef22e3 --- /dev/null +++ b/debian/opendv-base.postinst @@ -0,0 +1,48 @@ +#!/bin/sh +# postinst script for dstarrepeater +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-remove' +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see https://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + configure) + useradd --user-group -M --system opendv --shell /bin/false || true + + # Add the opendv user to the audio group so that it can open audio + # devices when using the audio based drivers such as the Sound Card + # one. + usermod --groups audio --append opendv || true + usermod --groups dialout --append opendv || true + usermod --groups gpio --append opendv || true + chown opendv:opendv /var/log/opendv + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/debian/remotecontrol.install b/debian/remotecontrol.install new file mode 100644 index 0000000..d95c5f4 --- /dev/null +++ b/debian/remotecontrol.install @@ -0,0 +1 @@ +usr/bin/remotecontrol diff --git a/debian/remotecontrold.install b/debian/remotecontrold.install new file mode 100644 index 0000000..6155b51 --- /dev/null +++ b/debian/remotecontrold.install @@ -0,0 +1 @@ +usr/bin/remotecontrold diff --git a/debian/rules b/debian/rules new file mode 100644 index 0000000..e87d57a --- /dev/null +++ b/debian/rules @@ -0,0 +1,36 @@ +#!/usr/bin/make -f +# See debhelper(7) (uncomment to enable) +# output every command that modifies files on the build system. +#export DH_VERBOSE = 1 + +# see EXAMPLES in dpkg-buildflags(1) and read /usr/share/dpkg/* +DPKG_EXPORT_BUILDFLAGS = 1 +include /usr/share/dpkg/default.mk + +# see FEATURE AREAS in dpkg-buildflags(1) +#export DEB_BUILD_MAINT_OPTIONS = hardening=+all + +# see ENVIRONMENT in dpkg-buildflags(1) +# package maintainers to append CFLAGS +#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic +# package maintainers to append LDFLAGS +#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed + + +# main packaging script based on dh7 syntax +%: + dh $@ --with autotools-dev --parallel + +# dh_make generated override targets +# This is example for Cmake (See https://bugs.debian.org/641051 ) +#override_dh_auto_configure: +# dh_auto_configure -- \ +# -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH) + +override_dh_installinit: + dh_installinit + dh_installinit --name=ircddbgatewayd + dh_installinit --name=starnetserverd + dh_installinit --name=timercontrold + dh_installinit --name=timeserverd + dh_installinit --name=aprstransmitd@ diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..89ae9db --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/debian/starnetserver.install b/debian/starnetserver.install new file mode 100644 index 0000000..8d43e0d --- /dev/null +++ b/debian/starnetserver.install @@ -0,0 +1 @@ +usr/bin/starnetserver diff --git a/debian/starnetserver.logrotate b/debian/starnetserver.logrotate new file mode 100644 index 0000000..d251afc --- /dev/null +++ b/debian/starnetserver.logrotate @@ -0,0 +1,7 @@ +/var/log/opendv/starnetserver.log { + rotate 14 + daily + compress + missingok + copytruncate +} diff --git a/debian/starnetserverd.install b/debian/starnetserverd.install new file mode 100644 index 0000000..d42003a --- /dev/null +++ b/debian/starnetserverd.install @@ -0,0 +1 @@ +usr/sbin/starnetserverd diff --git a/debian/starnetserverd.logrotate b/debian/starnetserverd.logrotate new file mode 100644 index 0000000..b50cf56 --- /dev/null +++ b/debian/starnetserverd.logrotate @@ -0,0 +1,7 @@ +/var/log/opendv/starnetserverd.log { + rotate 14 + daily + compress + missingok + copytruncate +} diff --git a/debian/starnetserverd.starnetserverd.service b/debian/starnetserverd.starnetserverd.service new file mode 100644 index 0000000..ce5db1d --- /dev/null +++ b/debian/starnetserverd.starnetserverd.service @@ -0,0 +1,11 @@ +[Unit] +Description=D-STAR Gateway Daemon +After=network.target + +[Service] +User=opendv +ExecStart=/usr/sbin/starnetserverd +Restart=on-abort + +[Install] +WantedBy=multi-user.target diff --git a/debian/timercontrol.install b/debian/timercontrol.install new file mode 100644 index 0000000..38f4093 --- /dev/null +++ b/debian/timercontrol.install @@ -0,0 +1 @@ +usr/bin/timercontrol diff --git a/debian/timercontrol.logrotate b/debian/timercontrol.logrotate new file mode 100644 index 0000000..84d4a4b --- /dev/null +++ b/debian/timercontrol.logrotate @@ -0,0 +1,7 @@ +/var/log/opendv/timercontrol.log { + rotate 14 + daily + compress + missingok + copytruncate +} diff --git a/debian/timercontrold.install b/debian/timercontrold.install new file mode 100644 index 0000000..efe8d13 --- /dev/null +++ b/debian/timercontrold.install @@ -0,0 +1 @@ +usr/sbin/timercontrold diff --git a/debian/timercontrold.logrotate b/debian/timercontrold.logrotate new file mode 100644 index 0000000..97972f8 --- /dev/null +++ b/debian/timercontrold.logrotate @@ -0,0 +1,7 @@ +/var/log/opendv/timercontrold.log { + rotate 14 + daily + compress + missingok + copytruncate +} diff --git a/debian/timercontrold.timercontrold.service b/debian/timercontrold.timercontrold.service new file mode 100644 index 0000000..58e5b8c --- /dev/null +++ b/debian/timercontrold.timercontrold.service @@ -0,0 +1,12 @@ +[Unit] +Description=D-STAR Timer Control Daemon +Wants=ircddbgatewayd.service +After=ircddbgatewayd.service + +[Service] +User=opendv +ExecStart=/usr/sbin/timercontrold +Restart=on-abort + +[Install] +WantedBy=multi-user.target diff --git a/debian/timeserver.install b/debian/timeserver.install new file mode 100644 index 0000000..311e8a2 --- /dev/null +++ b/debian/timeserver.install @@ -0,0 +1 @@ +usr/bin/timeserver diff --git a/debian/timeserver.logrotate b/debian/timeserver.logrotate new file mode 100644 index 0000000..541a80d --- /dev/null +++ b/debian/timeserver.logrotate @@ -0,0 +1,7 @@ +/var/log/opendv/timeserver.log { + rotate 14 + daily + compress + missingok + copytruncate +} diff --git a/debian/timeserverd.install b/debian/timeserverd.install new file mode 100644 index 0000000..bfc53bf --- /dev/null +++ b/debian/timeserverd.install @@ -0,0 +1 @@ +usr/sbin/timeserverd diff --git a/debian/timeserverd.logrotate b/debian/timeserverd.logrotate new file mode 100644 index 0000000..1cdfe67 --- /dev/null +++ b/debian/timeserverd.logrotate @@ -0,0 +1,7 @@ +/var/log/opendv/timeserverd.log { + rotate 14 + daily + compress + missingok + copytruncate +} diff --git a/debian/timeserverd.timeserverd.service b/debian/timeserverd.timeserverd.service new file mode 100644 index 0000000..4425358 --- /dev/null +++ b/debian/timeserverd.timeserverd.service @@ -0,0 +1,12 @@ +[Unit] +Description=D-STAR Timer Control Daemon +Wants=ircddbgatewayd.service +After=ircddbgatewayd.service + +[Service] +User=opendv +ExecStart=/usr/sbin/timeserverd +Restart=on-abort + +[Install] +WantedBy=multi-user.target diff --git a/ircDDB/IRCApplication.h b/ircDDB/IRCApplication.h new file mode 100644 index 0000000..3e2a19e --- /dev/null +++ b/ircDDB/IRCApplication.h @@ -0,0 +1,50 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010 Michael Dirska, DL1BFF (dl1bff@mdx.de) + +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, see . + +*/ + +#if !defined(_IRCAPPLICATION_H) +#define _IRCAPPLICATION_H + +#include + +#include "IRCMessageQueue.h" + +class IRCApplication +{ +public: + virtual void userJoin (const wxString& nick, const wxString& name, const wxString& host) = 0; + virtual void userLeave (const wxString& nick) = 0; + virtual void userChanOp (const wxString& nick, bool op) = 0; + virtual void userListReset(void) = 0; + + virtual void msgChannel (IRCMessage * m) = 0; + virtual void msgQuery (IRCMessage * m) = 0; + + virtual void setCurrentNick(const wxString& nick) = 0; + virtual void setTopic(const wxString& topic) = 0; + + virtual void setBestServer(const wxString& ircUser) = 0; + + virtual void setSendQ( IRCMessageQueue * s ) = 0; + virtual IRCMessageQueue * getSendQ (void) = 0; +}; + + +#endif diff --git a/ircDDB/IRCClient.cpp b/ircDDB/IRCClient.cpp new file mode 100644 index 0000000..751231f --- /dev/null +++ b/ircDDB/IRCClient.cpp @@ -0,0 +1,515 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de) +Copyright (C) 2012 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, see . + +*/ + + +#if defined(WIN32) + +#define WIN32_LEAN_AND_MEAN + +#include +#include + +#else + +#include +#include +#include + +#endif + + +#include "IRCClient.h" +#include "IRCutils.h" + +#include + + + +#include +#include + + + + + + +IRCClient::IRCClient( IRCApplication * app, const wxString& update_channel, + const wxString& hostName, unsigned int port, const wxString& callsign, const wxString& password, + const wxString& versionInfo, const wxString& localAddr ) + : wxThread(wxTHREAD_JOINABLE) +{ +#if defined(__WINDOWS__) + WSAData data; + int wsaRet = ::WSAStartup(MAKEWORD(2, 2), &data); + if (wsaRet != 0) + wxLogError(wxT("IRCClient::IRCClient: Error from WSAStartup")); +#endif + + safeStringCopy(host_name, hostName.mb_str(), sizeof host_name); + + this -> callsign = callsign.Lower(); + this -> port = port; + this -> password = password; + + this -> app = app; + + if (localAddr.IsEmpty()) + { + safeStringCopy(local_addr, "0.0.0.0", sizeof local_addr); + } + else + { + safeStringCopy(local_addr, localAddr.mb_str(), sizeof local_addr); + } + + proto = new IRCProtocol ( app, this->callsign, password, update_channel, versionInfo ); + + recvQ = NULL; + sendQ = NULL; + + recv = NULL; +} + +IRCClient::~IRCClient() +{ + delete proto; + +#if defined(__WINDOWS__) + ::WSACleanup(); +#endif +} + +void IRCClient::startWork() +{ + terminateThread = false; + + Create(); + Run(); +} + +void IRCClient::stopWork() +{ + terminateThread = true; + + Wait(); +} + +wxThread::ExitCode IRCClient::Entry () +{ + unsigned int numAddr; + +#define MAXIPV4ADDR 10 + struct sockaddr_in addr[MAXIPV4ADDR]; + + struct sockaddr_in myaddr; + + int state = 0; + int timer = 0; + int sock = 0; + unsigned int currentAddr = 0; + + int result; + + + numAddr = 0; + + result = getAllIPV4Addresses(local_addr, 0, &numAddr, &myaddr, 1); + + if ((result != 0) || (numAddr != 1)) + { + wxLogVerbose(wxT("IRCClient::Entry: local address not parseable, using 0.0.0.0")); + memset(&myaddr, 0x00, sizeof(struct sockaddr_in)); + } + + while (true) + { + + if (timer > 0) + { + timer --; + } + + switch (state) + { + case 0: + if (terminateThread) + { + wxLogVerbose(wxT("IRCClient::Entry: thread terminated at state=%d"), state); + return 0; + } + + if (timer == 0) + { + timer = 30; + + if (getAllIPV4Addresses(host_name, port, &numAddr, addr, MAXIPV4ADDR) == 0) + { + wxLogVerbose(wxT("IRCClient::Entry: number of DNS entries %d"), numAddr); + if (numAddr > 0) + { + currentAddr = 0; + state = 1; + timer = 0; + } + } + } + break; + + case 1: + if (terminateThread) + { + wxLogVerbose(wxT("IRCClient::Entry: thread terminated at state=%d"), state); + return 0; + } + + if (timer == 0) + { + sock = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (sock < 0) + { + wxLogSysError(wxT("IRCClient::Entry: socket")); + timer = 30; + state = 0; + } + else + { +#if defined(__WINDOWS__) + u_long nonBlock = 1UL; + if (ioctlsocket( sock, FIONBIO, &nonBlock ) != 0) + { + wxLogSysError(wxT("IRCClient::Entry: ioctlsocket")); + closesocket(sock); + timer = 30; + state = 0; + } +#else + if (fcntl( sock, F_SETFL, O_NONBLOCK ) < 0) + { + wxLogSysError(wxT("IRCClient::Entry: fcntl")); + close(sock); + timer = 30; + state = 0; + } +#endif + else + { + unsigned char * h = (unsigned char *) &(myaddr.sin_addr); + int res; + + if ((h[0] != 0) || (h[1] != 0) || (h[2] != 0) || (h[3] != 0)) + { + wxLogVerbose(wxT("IRCClient::Entry: bind: local address %d.%d.%d.%d"), + h[0], h[1], h[2], h[3]); + } + + res = bind(sock, (struct sockaddr *) &myaddr, sizeof (struct sockaddr_in)); + + if (res != 0) + { + + wxLogSysError(wxT("IRCClient::Entry: bind")); +#if defined(__WINDOWS__) + closesocket(sock); +#else + close(sock); +#endif + state = 0; + timer = 30; + break; + } + + + h = (unsigned char *) &(addr[currentAddr].sin_addr); + wxLogVerbose(wxT("IRCClient::Entry: trying to connect to %d.%d.%d.%d"), + h[0], h[1], h[2], h[3]); + + res = connect(sock, (struct sockaddr *) (addr + currentAddr), sizeof (struct sockaddr_in)); + + if (res == 0) + { + wxLogVerbose(wxT("IRCClient::Entry: connected")); + state = 4; + } + else + { +#if defined(__WINDOWS__) + if (WSAGetLastError() == WSAEWOULDBLOCK) +#else + if (errno == EINPROGRESS) +#endif + { + wxLogVerbose(wxT("IRCClient::Entry: connect in progress")); + state = 3; + timer = 10; // 5 second timeout + } + else + { + wxLogSysError(wxT("IRCClient::Entry: connect")); +#if defined(__WINDOWS__) + closesocket(sock); +#else + close(sock); +#endif + currentAddr ++; + if (currentAddr >= numAddr) + { + state = 0; + timer = 30; + } + else + { + state = 1; + timer = 4; + } + } + } + } // connect + } + } + break; + + case 3: + { + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 0; + + fd_set myset; + FD_ZERO(&myset); +#if defined(__WINDOWS__) + FD_SET((unsigned int)sock, &myset); +#else + FD_SET(sock, &myset); +#endif + + int res = select(sock+1, NULL, &myset, NULL, &tv); + if (res < 0) + { + wxLogSysError(wxT("IRCClient::Entry: select")); +#if defined(__WINDOWS__) + closesocket(sock); +#else + close(sock); +#endif + state = 0; + timer = 30; + } + else if (res > 0) // connect is finished + { +#if defined(__WINDOWS__) + int val_len; +#else + socklen_t val_len; +#endif + int value; + + val_len = sizeof value; + + if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *) &value, &val_len) < 0) + { + wxLogSysError(wxT("IRCClient::Entry: getsockopt")); +#if defined(__WINDOWS__) + closesocket(sock); +#else + close(sock); +#endif + state = 0; + timer = 30; + } + else + { + if (value != 0) + { + wxLogWarning(wxT("IRCClient::Entry: SO_ERROR=%d"), value); +#if defined(__WINDOWS__) + closesocket(sock); +#else + close(sock); +#endif + currentAddr ++; + if (currentAddr >= numAddr) + { + state = 0; + timer = 30; + } + else + { + state = 1; + timer = 2; + } + } + else + { + wxLogVerbose(wxT("IRCClient::Entry: connected2")); + state = 4; + } + } + + } + else if (timer == 0) + { // select timeout and timer timeout + wxLogVerbose(wxT("IRCClient::Entry: connect timeout")); +#if defined(__WINDOWS__) + closesocket(sock); +#else + close(sock); +#endif + currentAddr ++; + if (currentAddr >= numAddr) + { + state = 0; + timer = 30; + } + else + { + state = 1; // open new socket + timer = 2; + } + } + + } + break; + + case 4: + { + recvQ = new IRCMessageQueue(); + sendQ = new IRCMessageQueue(); + + recv = new IRCReceiver(sock, recvQ); + recv->startWork(); + + proto->setNetworkReady(true); + state = 5; + timer = 0; + + } + break; + + + case 5: + if (terminateThread) + { + state = 6; + } + else + { + + if (recvQ -> isEOF()) + { + timer = 0; + state = 6; + } + else if (proto -> processQueues(recvQ, sendQ) == false) + { + timer = 0; + state = 6; + } + + while ((state == 5) && sendQ->messageAvailable()) + { + IRCMessage * m = sendQ -> getMessage(); + + wxString out; + + m -> composeMessage ( out ); + + char buf[200]; + safeStringCopy(buf, out.mb_str(wxConvUTF8), sizeof buf); + int len = strlen(buf); + + if (buf[len - 1] == 10) // is there a NL char at the end? + { + int r = send(sock, buf, len, 0); + + if (r != len) + { + wxLogVerbose(wxT("IRCClient::Entry: short write %d < %d"), r, len); + + timer = 0; + state = 6; + } +/* else + { + wxLogVerbose(wxT("write %d bytes (") + out + wxT(")"), len ); + } */ + } + else + { + wxLogVerbose(wxT("IRCClient::Entry: no NL at end, len=%d"), len); + + timer = 0; + state = 6; + } + + delete m; + } + } + break; + + case 6: + { + if (app != NULL) + { + app->setSendQ(NULL); + app->userListReset(); + } + + proto->setNetworkReady(false); + recv->stopWork(); + + wxThread::Sleep(2000); + + delete recv; + delete recvQ; + delete sendQ; + +#if defined(__WINDOWS__) + closesocket(sock); +#else + close(sock); +#endif + + if (terminateThread) // request to end the thread + { + wxLogVerbose(wxT("IRCClient::Entry: thread terminated at state=%d"), state); + return 0; + } + + timer = 30; + state = 0; // reconnect to IRC server + } + break; + + } + + wxThread::Sleep(500); + + } + + return 0; +} + + + + + diff --git a/ircDDB/IRCClient.h b/ircDDB/IRCClient.h new file mode 100644 index 0000000..521f900 --- /dev/null +++ b/ircDDB/IRCClient.h @@ -0,0 +1,77 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de) +Copyright (C) 2011,2012 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, see . + +*/ + + +#if !defined(_IRCCLIENT_H) +#define _IRCCLIENT_H + +#include "IRCReceiver.h" +#include "IRCMessageQueue.h" +#include "IRCProtocol.h" +#include "IRCApplication.h" + +#include + + +class IRCClient : public wxThread +{ + public: + + IRCClient( IRCApplication * app, const wxString& update_channel, + const wxString& hostName, unsigned int port, const wxString& callsign, const wxString& password, + const wxString& versionInfo, const wxString& localAddr ); + + virtual ~IRCClient(); + + + virtual void startWork(); + + virtual void stopWork(); + + + protected: + + virtual wxThread::ExitCode Entry(); + + + + private: + + char host_name[100]; + char local_addr[100]; + unsigned int port; + wxString callsign; + wxString password; + + bool terminateThread; + + IRCReceiver * recv; + IRCMessageQueue * recvQ; + IRCMessageQueue * sendQ; + IRCProtocol * proto; + + IRCApplication * app; + +}; + + +#endif diff --git a/ircDDB/IRCDDB.cpp b/ircDDB/IRCDDB.cpp new file mode 100644 index 0000000..afec4c1 --- /dev/null +++ b/ircDDB/IRCDDB.cpp @@ -0,0 +1,32 @@ +/* + +CIRCDDBClient - ircDDB client library in C++ + +Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de) +Copyright (C) 2011,2012 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, see . + +*/ + +#include "IRCDDB.h" + +CIRCDDB::CIRCDDB() +{ +} + + +CIRCDDB::~CIRCDDB() +{ +} diff --git a/ircDDB/IRCDDB.h b/ircDDB/IRCDDB.h new file mode 100644 index 0000000..328f6dc --- /dev/null +++ b/ircDDB/IRCDDB.h @@ -0,0 +1,163 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de) +Copyright (C) 2011,2012 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, see . + +*/ + +#include + +#if !defined(_IRCDDB_H) +#define _IRCDDB_H + +enum IRCDDB_RESPONSE_TYPE { + IDRT_NONE, + IDRT_USER, + IDRT_GATEWAY, + IDRT_REPEATER +}; + + +class CIRCDDB +{ +public: + CIRCDDB(); + virtual ~CIRCDDB(); + + // A false return implies a network error, or unable to log in + virtual bool open() = 0; + + + // rptrQTH can be called multiple times if necessary + // callsign The callsign of the repeater + // latitude WGS84 position of antenna in degrees, positive value -> NORTH + // longitude WGS84 position of antenna in degrees, positive value -> EAST + // desc1, desc2 20-character description of QTH + // infoURL URL of a web page with information about the repeater + + virtual void rptrQTH(const wxString& callsign, double latitude, double longitude, const wxString& desc1, + const wxString& desc2, const wxString& infoURL) = 0; + + + + // rptrQRG can be called multiple times if necessary + // callsign callsign of the repeater + // txFrequency repeater TX frequency in MHz + // duplexShift duplex shift in MHz (positive or negative value): RX_freq = txFrequency + duplexShift + // range range of the repeater in meters (meters = miles * 1609.344) + // agl height of the antenna above ground in meters (meters = feet * 0.3048) + + virtual void rptrQRG(const wxString& callsign, double txFrequency, double duplexShift, double range, double agl) = 0; + + + // If you call this method once, watchdog messages will be sent to the + // to the ircDDB network every 15 minutes. Invoke this method every 1-2 minutes to indicate + // that the gateway is working properly. After activating the watchdog, a red LED will be displayed + // on the ircDDB web page if this method is not called within a period of about 30 minutes. + // The string wdInfo should contain information about the source of the alive messages, e.g., + // version of the RF decoding software. For example, the ircDDB java software sets this + // to "rpm_ircddbmhd-x.z-z". The string wdInfo must contain at least one non-space character. + + virtual void kickWatchdog(const wxString& callsign, const wxString& wdInfo) = 0; + + + + // get internal network status + virtual int getConnectionState() = 0; + // one of these values is returned: + // 0 = not (yet) connected to the IRC server + // 1-6 = a new connection was established, download of repeater info etc. is + // in progress + // 7 = the ircDDB connection is fully operational + // 10 = some network error occured, next state is "0" (new connection attempt) + + // Send heard data, a false return implies a network error + virtual bool sendHeard(const wxString& myCall, const wxString& myCallExt, + const wxString& yourCall, const wxString& rpt1, + const wxString& rpt2, unsigned char flag1, + unsigned char flag2, unsigned char flag3) = 0; + + + // same as sendHeard with two new fields: + // network_destination: empty string or 8-char call sign of the repeater + // or reflector, where this transmission is relayed to. + // tx_message: 20-char TX message or empty string, if the user did not + // send a TX message + virtual bool sendHeardWithTXMsg(const wxString& myCall, const wxString& myCallExt, + const wxString& yourCall, const wxString& rpt1, + const wxString& rpt2, unsigned char flag1, + unsigned char flag2, unsigned char flag3, + const wxString& network_destination, + const wxString& tx_message) = 0; + + // this method should be called at the end of a transmission + // num_dv_frames: number of DV frames sent out (96 bit frames, 20ms) + // num_dv_silent_frames: number of DV silence frames sent out in the + // last transmission, or -1 if the information is not available + // num_bit_errors: number of bit errors of the received data. This should + // be the derived from the first Golay block of the voice data. This + // error correction code only looks at 24 bits of the 96 bit frame. + // So, the overall bit error rate is calculated like this: + // BER = num_bit_errors / (num_dv_frames * 24) + // Set num_bit_errors = -1, if the error information is not available. + virtual bool sendHeardWithTXStats(const wxString& myCall, const wxString& myCallExt, + const wxString& yourCall, const wxString& rpt1, + const wxString& rpt2, unsigned char flag1, + unsigned char flag2, unsigned char flag3, + int num_dv_frames, + int num_dv_silent_frames, + int num_bit_errors) = 0; + + // The following three functions don't block waiting for a reply, they just send the data + + // Send query for a gateway/reflector, a false return implies a network error + virtual bool findGateway(const wxString& gatewayCallsign) = 0; + + // Send query for a repeater module, a false return implies a network error + virtual bool findRepeater(const wxString& repeaterCallsign) = 0; + + // Send query for a user, a false return implies a network error + virtual bool findUser(const wxString& userCallsign) = 0; + + // The following functions are for processing received messages + + // Get the waiting message type + virtual IRCDDB_RESPONSE_TYPE getMessageType() = 0; + + // Get a gateway message, as a result of IDRT_REPEATER returned from getMessageType() + // A false return implies a network error + virtual bool receiveRepeater(wxString& repeaterCallsign, wxString& gatewayCallsign, wxString& address) = 0; + + // Get a gateway message, as a result of IDRT_GATEWAY returned from getMessageType() + // A false return implies a network error + virtual bool receiveGateway(wxString& gatewayCallsign, wxString& address) = 0; + + // Get a user message, as a result of IDRT_USER returned from getMessageType() + // A false return implies a network error + virtual bool receiveUser(wxString& userCallsign, wxString& repeaterCallsign, wxString& gatewayCallsign, wxString& address) = 0; + + virtual bool receiveUser(wxString& userCallsign, wxString& repeaterCallsign, wxString& gatewayCallsign, wxString& address, + wxString& timeStamp) = 0; + + virtual void close() = 0; // Implictely kills any threads in the IRC code + +}; + +WX_DEFINE_ARRAY_PTR(CIRCDDB*, CIRCDDB_Array); +#endif + diff --git a/ircDDB/IRCDDBApp.cpp b/ircDDB/IRCDDBApp.cpp new file mode 100644 index 0000000..78b8c9f --- /dev/null +++ b/ircDDB/IRCDDBApp.cpp @@ -0,0 +1,1353 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de) +Copyright (C) 2012 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, see . + +*/ + +#if defined(WIN32) + +#define WIN32_LEAN_AND_MEAN + +#include +#include + +#else + +#include +#include +#include + +#endif + + +#include "IRCDDBApp.h" +#include "IRCutils.h" + +#include +#include +#include + +class IRCDDBAppUserObject +{ + public: + + wxString nick; + wxString name; + wxString host; + bool op; + unsigned int usn; + + IRCDDBAppUserObject () + { + IRCDDBAppUserObject (wxT(""), wxT(""), wxT("")); + } + + IRCDDBAppUserObject ( const wxString& n, const wxString& nm, const wxString& h ) + { + nick = n; + name = nm; + host = h; + op = false; + usn = 0; + //usn = counter; + //counter ++; + } + + //static unsigned int counter; +}; + +//unsigned int IRCDDBAppUserObject::counter = 0; + + +WX_DECLARE_STRING_HASH_MAP( IRCDDBAppUserObject, IRCDDBAppUserMap ); + + +class IRCDDBAppRptrObject +{ + public: + + wxString arearp_cs; + wxDateTime lastChanged; + wxString zonerp_cs; + + IRCDDBAppRptrObject () + { + } + + IRCDDBAppRptrObject (wxDateTime& dt, wxString& repeaterCallsign, wxString& gatewayCallsign, wxDateTime& maxTime) + { + arearp_cs = repeaterCallsign; + lastChanged = dt; + zonerp_cs = gatewayCallsign; + + if (dt.IsLaterThan(maxTime)) + { + maxTime = dt; + } + } + + //static wxDateTime maxTime; +}; + +//wxDateTime IRCDDBAppRptrObject::maxTime((time_t) 950000000); // February 2000 + + +WX_DECLARE_STRING_HASH_MAP( IRCDDBAppRptrObject, IRCDDBAppRptrMap ); + +WX_DECLARE_STRING_HASH_MAP( wxString, IRCDDBAppModuleQRG ); +WX_DECLARE_STRING_HASH_MAP( wxString, IRCDDBAppModuleQTH ); +WX_DECLARE_STRING_HASH_MAP( wxString, IRCDDBAppModuleURL ); +WX_DECLARE_STRING_HASH_MAP( wxString, IRCDDBAppModuleWD ); + +class IRCDDBAppPrivate +{ + public: + + IRCDDBAppPrivate() + : tablePattern(wxT("^[0-9]$")), + datePattern(wxT("^20[0-9][0-9]-((1[0-2])|(0[1-9]))-((3[01])|([12][0-9])|(0[1-9]))$")), + timePattern(wxT("^((2[0-3])|([01][0-9])):[0-5][0-9]:[0-5][0-9]$")), + dbPattern(wxT("^[0-9A-Z_]{8}$")) + { + } + + IRCMessageQueue * sendQ; + + IRCDDBAppUserMap user; + wxMutex userMapMutex; + + wxString currentServer; + wxString myNick; + + wxRegEx tablePattern; + wxRegEx datePattern; + wxRegEx timePattern; + wxRegEx dbPattern; + + int state; + int timer; + int infoTimer; + + wxString updateChannel; + wxString channelTopic; + wxString bestServer; + + bool initReady; + + bool terminateThread; + + IRCDDBAppRptrMap rptrMap; + wxMutex rptrMapMutex; + + IRCMessageQueue replyQ; + + IRCDDBAppModuleQRG moduleQRG; + wxMutex moduleQRGMutex; + + IRCDDBAppModuleQTH moduleQTH; + IRCDDBAppModuleURL moduleURL; + wxMutex moduleQTHURLMutex; + + IRCDDBAppModuleWD moduleWD; + wxMutex moduleWDMutex; + + int wdTimer; +}; + + +IRCDDBApp::IRCDDBApp( const wxString& u_chan ) + : wxThread(wxTHREAD_JOINABLE), + d(new IRCDDBAppPrivate), + m_maxTime((time_t)950000000)//februray 2000 +{ + + d->sendQ = NULL; + d->initReady = false; + + userListReset(); + + d->state = 0; + d->timer = 0; + d->myNick = wxT("none"); + + d->updateChannel = u_chan; + + d->terminateThread = false; + +} + +IRCDDBApp::~IRCDDBApp() +{ + delete d->sendQ; + delete d; +} + + +void IRCDDBApp::rptrQTH(const wxString& callsign, double latitude, double longitude, const wxString& desc1, const wxString& desc2, const wxString& infoURL) +{ + wxString pos; + pos.Printf(wxT("%+09.5f %+010.5f"), latitude, longitude); + + wxString cs = callsign; + wxString d1 = desc1; + wxString d2 = desc2; + + d1.Append(wxT(' '), 20); + d2.Append(wxT(' '), 20); + + wxRegEx nonValid(wxT("[^a-zA-Z0-9 +&(),./'-]")); + nonValid.Replace(&d1, wxEmptyString); + nonValid.Replace(&d2, wxEmptyString); + + pos.Replace(wxT(","), wxT(".")); + d1.Replace(wxT(" "), wxT("_")); + d2.Replace(wxT(" "), wxT("_")); + cs.Replace(wxT(" "), wxT("_")); + + wxMutexLocker lock(d->moduleQTHURLMutex); + + d->moduleQTH[cs] = cs + wxT(" ") + pos + wxT(" ") + d1.Mid(0, 20) + wxT(" ") + d2.Mid(0, 20); + + wxLogVerbose(wxT("QTH: ") + d->moduleQTH[cs]); + + wxString url = infoURL; + + wxRegEx urlNonValid(wxT("[^[:graph:]]")); + urlNonValid.Replace(&url, wxEmptyString); + + if (!url.IsEmpty()) { + d->moduleURL[cs] = cs + wxT(" ") + url; + + wxLogVerbose(wxT("URL: ") + d->moduleURL[cs]); + } + + d->infoTimer = 5; // send info in 5 seconds +} + + +void IRCDDBApp::rptrQRG(const wxString& callsign, double txFrequency, double duplexShift, double range, double agl) +{ + wxString cs = callsign; + cs.Replace(wxT(" "), wxT("_")); + + wxString f; + f.Printf(wxT("%011.5f %+010.5f %06.2f %06.1f"), txFrequency, duplexShift, range / 1609.344, agl); + f.Replace(wxT(","), wxT(".")); + + wxMutexLocker lock(d->moduleQRGMutex); + + d->moduleQRG[cs] = cs + wxT(" ") + f; + + wxLogVerbose(wxT("QRG: ") + d->moduleQRG[cs]); + + d->infoTimer = 5; // send info in 5 seconds +} + +void IRCDDBApp::kickWatchdog(const wxString& callsign, const wxString& s) +{ + wxString text = s; + + wxRegEx nonValid(wxT("[^[:graph:]]")); + nonValid.Replace(&text, wxEmptyString); + + if (!text.IsEmpty()) { + wxString cs = callsign; + cs.Replace(wxT(" "), wxT("_")); + + wxMutexLocker lock(d->moduleWDMutex); + + d->moduleWD[cs] = cs + wxT(" ") + text; + + d->wdTimer = 60; + } +} + + +int IRCDDBApp::getConnectionState() +{ + return d->state; +} + +IRCDDB_RESPONSE_TYPE IRCDDBApp::getReplyMessageType() +{ + IRCMessage* m = d->replyQ.peekFirst(); + if (m == NULL) + return IDRT_NONE; + + wxString msgType = m->getCommand(); + + if (msgType.IsSameAs(wxT("IDRT_USER"))) + return IDRT_USER; + + if (msgType.IsSameAs(wxT("IDRT_REPEATER"))) + return IDRT_REPEATER; + + if (msgType.IsSameAs(wxT("IDRT_GATEWAY"))) + return IDRT_GATEWAY; + + wxLogError(wxT("IRCDDBApp::getMessageType: unknown msg type: %s"), msgType.c_str()); + + return IDRT_NONE; +} + + +IRCMessage* IRCDDBApp::getReplyMessage() +{ + return d->replyQ.getMessage(); +} + + + +void IRCDDBApp::startWork() +{ + d->terminateThread = false; + + Create(); + Run(); +} + +void IRCDDBApp::stopWork() +{ + d->terminateThread = true; + + Wait(); +} + +unsigned int IRCDDBApp::calculateUsn(const wxString& nick) +{ + wxString lnick = nick.BeforeLast('-'); + unsigned int maxUsn = 0; + for (int i = 1; i <= 4; i++) { + wxString ircUser = lnick + wxString::Format(wxT("-%d"), i); + + if (d->user.count(ircUser) == 1) { + IRCDDBAppUserObject obj = d->user[ircUser]; + if (obj.usn > maxUsn) + maxUsn = obj.usn; + } + } + + return maxUsn + 1; +} + +void IRCDDBApp::userJoin (const wxString& nick, const wxString& name, const wxString& host) +{ + wxMutexLocker lock(d->userMapMutex); + + wxString lnick = nick; + lnick.MakeLower(); + + IRCDDBAppUserObject u( lnick, name, host ); + u.usn = calculateUsn(lnick); + + d->user[lnick] = u; + + // wxLogVerbose(wxT("add %d: (") + u.nick + wxT(") (") + u.host + wxT(")"), d->user.size()); + + if (d->initReady) + { + int hyphenPos = nick.Find(wxT('-')); + + if ((hyphenPos >= 4) && (hyphenPos <= 6)) + { + wxString gatewayCallsign = nick.Mid(0, hyphenPos).Upper(); + + while (gatewayCallsign.Length() < 7) + { + gatewayCallsign.Append(wxT(' ')); + } + + gatewayCallsign.Append(wxT('G')); + + IRCMessage * m2 = new IRCMessage(wxT( "IDRT_GATEWAY")); + m2->addParam(gatewayCallsign); + m2->addParam(host); + d->replyQ.putMessage(m2); + } + } + + // wxLogVerbose(wxT("user %d"), u.usn ); +} + + + + +void IRCDDBApp::userLeave (const wxString& nick) +{ + wxMutexLocker lock(d->userMapMutex); + + wxString lnick = nick; + lnick.MakeLower(); + + d->user.erase(lnick); + + // wxLogVerbose(wxT("rm %d: ") + nick, d->user.size()); + + if (d->currentServer.Len() > 0) + { + if (d->user.count(d->myNick) != 1) + { + wxLogVerbose(wxT("IRCDDBApp::userLeave: could not find own nick")); + return; + } + + IRCDDBAppUserObject me = d->user[d->myNick]; + + if (me.op == false) + { + // if I am not op, then look for new server + + if (d->currentServer.IsSameAs(lnick)) + { + // currentServer = null; + d->state = 2; // choose new server + d->timer = 200; + d->initReady = false; + } + } + } +} + +void IRCDDBApp::userListReset() +{ + wxMutexLocker lock(d->userMapMutex); + + d->user.clear(); +} + +void IRCDDBApp::setCurrentNick(const wxString& nick) +{ + d->myNick = nick; + wxLogVerbose(wxT("IRCDDBApp::setCurrentNick ") + nick); +} + +void IRCDDBApp::setBestServer(const wxString& ircUser) +{ + d->bestServer = ircUser; + wxLogVerbose(wxT("IRCDDBApp::setBestServer ") + ircUser); +} + +void IRCDDBApp::setTopic(const wxString& topic) +{ + d->channelTopic = topic; +} + +bool IRCDDBApp::findServerUser() +{ + wxMutexLocker lock(d->userMapMutex); + + bool found = false; + + IRCDDBAppUserMap::iterator it; + + for( it = d->user.begin(); it != d->user.end(); ++it ) + { + IRCDDBAppUserObject u = it->second; + + if (u.nick.StartsWith(wxT("s-")) && u.op && !d->myNick.IsSameAs(u.nick) + && u.nick.IsSameAs(d->bestServer)) + { + d->currentServer = u.nick; + found = true; + break; + } + } + + if (found) + return true; + + if (d->bestServer.Len() == 8) + { + for( it = d->user.begin(); it != d->user.end(); ++it ) + { + IRCDDBAppUserObject u = it->second; + + if (u.nick.StartsWith(d->bestServer.Mid(0,7)) && u.op && + !d->myNick.IsSameAs(u.nick) ) + { + d->currentServer = u.nick; + found = true; + break; + } + } + } + + if (found) + return true; + + for( it = d->user.begin(); it != d->user.end(); ++it ) + { + IRCDDBAppUserObject u = it->second; + + if (u.nick.StartsWith(wxT("s-")) && u.op && !d->myNick.IsSameAs(u.nick)) + { + d->currentServer = u.nick; + found = true; + break; + } + } + + return found; +} + +void IRCDDBApp::userChanOp (const wxString& nick, bool op) +{ + wxMutexLocker lock(d->userMapMutex); + + wxString lnick = nick; + lnick.MakeLower(); + + if (d->user.count(lnick) == 1) + { + d->user[lnick].op = op; + } +} + + + +static const int numberOfTables = 2; + + + + + +wxString IRCDDBApp::getIPAddress(wxString& zonerp_cs) +{ + wxMutexLocker lock(d->userMapMutex); + wxString gw = zonerp_cs; + + gw.Replace(wxT("_"), wxT(" ")); + gw.MakeLower(); + + unsigned int max_usn = 0; + wxString ipAddr; + + int j; + + for (j=1; j <= 4; j++) + { + // int i = 0; + wxString ircUser = gw.Strip() + wxString::Format(wxT("-%d"), j); + + if (d->user.count(ircUser) == 1) + { + IRCDDBAppUserObject o = d->user[ ircUser ]; + + if (o.usn >= max_usn) + { + max_usn = o.usn; + ipAddr = o.host.c_str(); // make a deep copy of the string and do not use wxString reference counting! + // This is important, because the reference counting feature + // in wxString is not thread-safe. + } + // i = 1; + } + // wxLogVerbose(wxT("getIP %d (") + ircUser + wxT(") (") + ipAddr + wxT(")"), i); + + } + + return ipAddr; +} + + +bool IRCDDBApp::findGateway(const wxString& gwCall) +{ + wxString s = gwCall.Mid(0,6); + + IRCMessage * m2 = new IRCMessage(wxT( "IDRT_GATEWAY")); + m2->addParam(gwCall); + m2->addParam(getIPAddress(s)); + d->replyQ.putMessage(m2); + + return true; +} + +static void findReflector( const wxString& rptrCall, IRCDDBAppPrivate * d ) +{ + wxString zonerp_cs; + wxString ipAddr; + +#define MAXIPV4ADDR 5 + struct sockaddr_in addr[MAXIPV4ADDR]; + unsigned int numAddr = 0; + + char host_name[80]; + + wxString host = rptrCall.Mid(0,6) + wxT(".reflector.ircddb.net"); + + safeStringCopy(host_name, host.mb_str(wxConvUTF8), sizeof host_name); + + if (getAllIPV4Addresses(host_name, 0, &numAddr, addr, MAXIPV4ADDR) == 0) + { + if (numAddr > 0) + { + unsigned char * a = (unsigned char *) &addr[0].sin_addr; + + ipAddr = wxString::Format(wxT("%d.%d.%d.%d"), a[0], a[1], a[2], a[3]); + zonerp_cs = rptrCall; + zonerp_cs.SetChar(7, wxT('G')); + } + } + + + IRCMessage * m2 = new IRCMessage(wxT("IDRT_REPEATER")); + m2->addParam(rptrCall); + m2->addParam(zonerp_cs); + m2->addParam(ipAddr); + d->replyQ.putMessage(m2); +} + +bool IRCDDBApp::findRepeater(const wxString& rptrCall) +{ + + if (rptrCall.StartsWith(wxT("XRF")) || rptrCall.StartsWith(wxT("REF"))) + { + findReflector(rptrCall, d); + return true; + } + + wxString arearp_cs = rptrCall; + arearp_cs.Replace(wxT(" "), wxT("_")); + + wxString zonerp_cs; + + wxMutexLocker lock(d->rptrMapMutex); + + wxString s = wxT("NONE"); + + if (d->rptrMap.count(arearp_cs) == 1) + { + IRCDDBAppRptrObject o = d->rptrMap[arearp_cs]; + zonerp_cs = o.zonerp_cs; + zonerp_cs.Replace(wxT("_"), wxT(" ")); + zonerp_cs.SetChar(7, wxT('G')); + s = o.zonerp_cs; + } + + IRCMessage * m2 = new IRCMessage(wxT("IDRT_REPEATER")); + m2->addParam(rptrCall); + m2->addParam(zonerp_cs); + m2->addParam(getIPAddress(s)); + d->replyQ.putMessage(m2); + + return true; +} + + +bool IRCDDBApp::sendHeard(const wxString& myCall, const wxString& myCallExt, + const wxString& yourCall, const wxString& rpt1, + const wxString& rpt2, unsigned char flag1, + unsigned char flag2, unsigned char flag3, + const wxString& destination, const wxString& tx_msg, + const wxString& tx_stats ) +{ + + wxString my = myCall; + wxString myext = myCallExt; + wxString ur = yourCall; + wxString r1 = rpt1; + wxString r2 = rpt2; + wxString dest = destination; + + wxRegEx nonValid(wxT("[^A-Z0-9/]")); + wxString underScore = wxT("_"); + + nonValid.Replace(&my, underScore); + nonValid.Replace(&myext, underScore); + nonValid.Replace(&ur, underScore); + nonValid.Replace(&r1, underScore); + nonValid.Replace(&r2, underScore); + nonValid.Replace(&dest, underScore); + + bool statsMsg = (tx_stats.Len() > 0); + + wxString srv = d->currentServer; + IRCMessageQueue * q = getSendQ(); + + if ((srv.Len() > 0) && (d->state >= 6) && (q != NULL)) + { + wxString cmd = wxT("UPDATE "); + + cmd.Append( getCurrentTime() ); + + cmd.Append(wxT(" ")); + + cmd.Append(my); + cmd.Append(wxT(" ")); + cmd.Append(r1); + cmd.Append(wxT(" ")); + if (!statsMsg) + { + cmd.Append(wxT("0 ")); + } + cmd.Append(r2); + cmd.Append(wxT(" ")); + cmd.Append(ur); + cmd.Append(wxT(" ")); + + wxString flags = wxString::Format(wxT("%02X %02X %02X"), flag1, flag2, flag3); + + cmd.Append(flags); + cmd.Append(wxT(" ")); + cmd.Append(myext); + + if (statsMsg) + { + cmd.Append(wxT(" # ")); + cmd.Append(tx_stats); + } + else + { + cmd.Append(wxT(" 00 ")); + cmd.Append(dest); + + if (tx_msg.Len() == 20) + { + cmd.Append(wxT(" ")); + cmd.Append(tx_msg); + } + } + + + IRCMessage * m = new IRCMessage(srv, cmd); + + q->putMessage(m); + return true; + } + else + { + return false; + } +} + +bool IRCDDBApp::findUser(const wxString& usrCall) +{ + wxString srv = d->currentServer; + IRCMessageQueue * q = getSendQ(); + + if ((srv.Len() > 0) && (d->state >= 6) && (q != NULL)) + { + wxString usr = usrCall; + + usr.Replace(wxT(" "), wxT("_")); + + IRCMessage * m = new IRCMessage(srv, + wxT("FIND ") + usr ); + + q->putMessage(m); + } + else + { + IRCMessage * m2 = new IRCMessage(wxT("IDRT_USER")); + m2->addParam(usrCall); + m2->addParam(wxT("")); + m2->addParam(wxT("")); + m2->addParam(wxT("")); + m2->addParam(wxT("")); + d->replyQ.putMessage(m2); + } + + return true; +} + + +void IRCDDBApp::msgChannel (IRCMessage * m) +{ + if (m->getPrefixNick().StartsWith(wxT("s-")) && (m->numParams >= 2)) // server msg + { + doUpdate(m->params[1]); + } +} + + +void IRCDDBApp::doNotFound ( wxString& msg, wxString& retval ) +{ + int tableID = 0; + + wxStringTokenizer tkz(msg); + + if (!tkz.HasMoreTokens()) + { + return; // no text in message + } + + wxString tk = tkz.GetNextToken(); + + + if (d->tablePattern.Matches(tk)) + { + long i; + + if (tk.ToLong(&i)) + { + tableID = i; + if ((tableID < 0) || (tableID >= numberOfTables)) + { + wxLogVerbose(wxT("invalid table ID %d"), tableID); + return; + } + } + else + { + return; // not a valid number + } + + if (!tkz.HasMoreTokens()) + { + return; // received nothing but the tableID + } + + tk = tkz.GetNextToken(); + } + + if (tableID == 0) + { + if (! d->dbPattern.Matches(tk)) + { + return; // no valid key + } + + retval = tk; + } +} + +void IRCDDBApp::doUpdate ( wxString& msg ) +{ + int tableID = 0; + + wxStringTokenizer tkz(msg); + + if (!tkz.HasMoreTokens()) + { + return; // no text in message + } + + wxString tk = tkz.GetNextToken(); + + + if (d->tablePattern.Matches(tk)) + { + long i; + + if (tk.ToLong(&i)) + { + tableID = i; + if ((tableID < 0) || (tableID >= numberOfTables)) + { + wxLogVerbose(wxT("invalid table ID %d"), tableID); + return; + } + } + else + { + return; // not a valid number + } + + if (!tkz.HasMoreTokens()) + { + return; // received nothing but the tableID + } + + tk = tkz.GetNextToken(); + } + + if (d->datePattern.Matches(tk)) + { + if (!tkz.HasMoreTokens()) + { + return; // nothing after date string + } + + wxString timeToken = tkz.GetNextToken(); + + if (! d->timePattern.Matches(timeToken)) + { + return; // no time string after date string + } + + wxDateTime dt; + +#if wxMAJOR_VERSION == 3 + if (dt.ParseFormat(tk + wxT(" ") + timeToken, wxT("%Y-%m-%d %H:%M:%S")) == false) +#else + if (dt.ParseFormat(tk + wxT(" ") + timeToken, wxT("%Y-%m-%d %H:%M:%S")) == NULL) +#endif + { + return; // date+time parsing failed + } + + if ((tableID == 0) || (tableID == 1)) + { + if (!tkz.HasMoreTokens()) + { + return; // nothing after time string + } + + wxString key = tkz.GetNextToken(); + + if (! d->dbPattern.Matches(key)) + { + return; // no valid key + } + + if (!tkz.HasMoreTokens()) + { + return; // nothing after time string + } + + wxString value = tkz.GetNextToken(); + + if (! d->dbPattern.Matches(value)) + { + return; // no valid key + } + + //wxLogVerbose(wxT("TABLE %d ") + key + wxT(" ") + value, tableID ); + + + if (tableID == 1) + { + wxMutexLocker lock(d->rptrMapMutex); + + IRCDDBAppRptrObject newRptr(dt, key, value, m_maxTime); + + d->rptrMap[key] = newRptr; + + if (d->initReady) + { + wxString arearp_cs = key; + wxString zonerp_cs = value; + + arearp_cs.Replace(wxT("_"), wxT(" ")); + zonerp_cs.Replace(wxT("_"), wxT(" ")); + zonerp_cs.SetChar(7, wxT('G')); + + IRCMessage * m2 = new IRCMessage(wxT("IDRT_REPEATER")); + m2->addParam(arearp_cs); + m2->addParam(zonerp_cs); + m2->addParam(getIPAddress(value)); + d->replyQ.putMessage(m2); + } + } + else if ((tableID == 0) && d->initReady) + { + wxMutexLocker lock(d->rptrMapMutex); + + wxString userCallsign = key; + wxString arearp_cs = value; + wxString zonerp_cs; + wxString ip_addr; + + userCallsign.Replace(wxT("_"), wxT(" ")); + arearp_cs.Replace(wxT("_"), wxT(" ")); + + if (d->rptrMap.count(value) == 1) + { + IRCDDBAppRptrObject o = d->rptrMap[value]; + zonerp_cs = o.zonerp_cs; + zonerp_cs.Replace(wxT("_"), wxT(" ")); + zonerp_cs.SetChar(7, wxT('G')); + + ip_addr = getIPAddress(o.zonerp_cs); + } + + IRCMessage * m2 = new IRCMessage(wxT("IDRT_USER")); + m2->addParam(userCallsign); + m2->addParam(arearp_cs); + m2->addParam(zonerp_cs); + m2->addParam(ip_addr); + m2->addParam(tk + wxT(" ") + timeToken); + d->replyQ.putMessage(m2); + + } + + + } + } + +} + + +static wxString getTableIDString( int tableID, bool spaceBeforeNumber ) +{ + if (tableID == 0) + { + return wxT(""); + } + else if ((tableID > 0) && (tableID < numberOfTables)) + { + if (spaceBeforeNumber) + { + return wxString::Format(wxT(" %d"),tableID); + } + else + { + return wxString::Format(wxT("%d "),tableID); + } + } + else + { + return wxT(" TABLE_ID_OUT_OF_RANGE "); + } +} + + +void IRCDDBApp::msgQuery (IRCMessage * m) +{ + + if (m->getPrefixNick().StartsWith(wxT("s-")) && (m->numParams >= 2)) // server msg + { + wxString msg = m->params[1]; + wxStringTokenizer tkz(msg); + + if (!tkz.HasMoreTokens()) + { + return; // no text in message + } + + wxString cmd = tkz.GetNextToken(); + + if (cmd.IsSameAs(wxT("UPDATE"))) + { + wxString restOfLine = tkz.GetString(); + doUpdate(restOfLine); + } + else if (cmd.IsSameAs(wxT("LIST_END"))) + { + if (d->state == 5) // if in sendlist processing state + { + d->state = 3; // get next table + } + } + else if (cmd.IsSameAs(wxT("LIST_MORE"))) + { + if (d->state == 5) // if in sendlist processing state + { + d->state = 4; // send next SENDLIST + } + } + else if (cmd.IsSameAs(wxT("NOT_FOUND"))) + { + wxString callsign; + wxString restOfLine = tkz.GetString(); + doNotFound(restOfLine, callsign); + + if (callsign.Len() > 0) + { + callsign.Replace(wxT("_"), wxT(" ")); + + IRCMessage * m2 = new IRCMessage(wxT("IDRT_USER")); + m2->addParam(callsign); + m2->addParam(wxT("")); + m2->addParam(wxT("")); + m2->addParam(wxT("")); + m2->addParam(wxT("")); + d->replyQ.putMessage(m2); + } + } + } +} + + +void IRCDDBApp::setSendQ( IRCMessageQueue * s ) +{ + d->sendQ = s; +} + +IRCMessageQueue * IRCDDBApp::getSendQ() +{ + return d->sendQ; +} + + +wxString IRCDDBApp::getLastEntryTime(int tableID) +{ + + if (tableID == 1) + { + wxString max = /*IRCDDBAppRptrObject::maxTime*/m_maxTime.Format( wxT("%Y-%m-%d %H:%M:%S") ); + return max; + } + + return wxT("DBERROR"); +} + + +static bool needsDatabaseUpdate( int tableID ) +{ + return (tableID == 1); +} + + +wxThread::ExitCode IRCDDBApp::Entry() +{ + + int sendlistTableID = 0; + + while (!d->terminateThread) + { + + if (d->timer > 0) + { + d->timer --; + } + + switch(d->state) + { + case 0: // wait for network to start + + if (getSendQ() != NULL) + { + d->state = 1; + } + break; + + case 1: + // connect to db + d->state = 2; + d->timer = 200; + break; + + case 2: // choose server + wxLogVerbose(wxT("IRCDDBApp: state=2 choose new 's-'-user")); + if (getSendQ() == NULL) + { + d->state = 10; + } + else + { + if (findServerUser()) + { + sendlistTableID = numberOfTables; + + d->state = 3; // next: send "SENDLIST" + } + else if (d->timer == 0) + { + d->state = 10; + IRCMessage * m = new IRCMessage(wxT("QUIT")); + + m->addParam(wxT("no op user with 's-' found.")); + + IRCMessageQueue * q = getSendQ(); + if (q != NULL) + { + q->putMessage(m); + } + } + } + break; + + case 3: + if (getSendQ() == NULL) + { + d->state = 10; // disconnect DB + } + else + { + sendlistTableID --; + if (sendlistTableID < 0) + { + d->state = 6; // end of sendlist + } + else + { + wxLogVerbose(wxT("IRCDDBApp: state=3 tableID=%d"), sendlistTableID); + d->state = 4; // send "SENDLIST" + d->timer = 900; // 15 minutes max for update + } + } + break; + + case 4: + if (getSendQ() == NULL) + { + d->state = 10; // disconnect DB + } + else + { + if (needsDatabaseUpdate(sendlistTableID)) + { + IRCMessage * m = new IRCMessage(d->currentServer, + wxT("SENDLIST") + getTableIDString(sendlistTableID, true) + + wxT(" ") + getLastEntryTime(sendlistTableID) ); + + IRCMessageQueue * q = getSendQ(); + if (q != NULL) + { + q->putMessage(m); + } + + d->state = 5; // wait for answers + } + else + { + d->state = 3; // don't send SENDLIST for this table, go to next table + } + } + break; + + case 5: // sendlist processing + if (getSendQ() == NULL) + { + d->state = 10; // disconnect DB + } + else if (d->timer == 0) + { + d->state = 10; // disconnect DB + IRCMessage * m = new IRCMessage(wxT("QUIT")); + + m->addParam(wxT("timeout SENDLIST")); + + IRCMessageQueue * q = getSendQ(); + if (q != NULL) + { + q->putMessage(m); + } + + } + break; + + case 6: + if (getSendQ() == NULL) + { + d->state = 10; // disconnect DB + } + else + { + wxLogVerbose(wxT( "IRCDDBApp: state=6 initialization completed")); + + d->infoTimer = 2; + + d->initReady = true; + d->state = 7; + } + break; + + + case 7: // standby state after initialization + if (getSendQ() == NULL) + { + d->state = 10; // disconnect DB + } + + if (d->infoTimer > 0) + { + d->infoTimer --; + + if (d->infoTimer == 0) + { + d->moduleQTHURLMutex.Lock(); + + for (IRCDDBAppModuleQTH::iterator it = d->moduleQTH.begin(); it != d->moduleQTH.end(); ++it) + { + wxString value = it->second; + IRCMessage* m = new IRCMessage(d->currentServer, wxT("IRCDDB RPTRQTH: ") + value); + + IRCMessageQueue* q = getSendQ(); + if (q != NULL) + { + q->putMessage(m); + } + } + + d->moduleQTH.clear(); + + for (IRCDDBAppModuleURL::iterator it = d->moduleURL.begin(); it != d->moduleURL.end(); ++it) + { + wxString value = it->second; + IRCMessage* m = new IRCMessage(d->currentServer, wxT("IRCDDB RPTRURL: ") + value); + + IRCMessageQueue* q = getSendQ(); + if (q != NULL) + { + q->putMessage(m); + } + } + + d->moduleURL.clear(); + + d->moduleQTHURLMutex.Unlock(); + + d->moduleQRGMutex.Lock(); + + for (IRCDDBAppModuleQRG::iterator it = d->moduleQRG.begin(); it != d->moduleQRG.end(); ++it) + { + wxString value = it->second; + IRCMessage* m = new IRCMessage(d->currentServer, wxT("IRCDDB RPTRQRG: ") + value); + + IRCMessageQueue* q = getSendQ(); + if (q != NULL) + { + q->putMessage(m); + } + } + + d->moduleQRG.clear(); + + d->moduleQRGMutex.Unlock(); + + } + } + + if (d->wdTimer > 0) + { + d->wdTimer --; + + if (d->wdTimer == 0) + { + d->moduleWDMutex.Lock(); + + for (IRCDDBAppModuleWD::iterator it = d->moduleWD.begin(); it != d->moduleWD.end(); ++it) + { + wxString value = it->second; + IRCMessage* m = new IRCMessage(d->currentServer, wxT("IRCDDB RPTRSW: ") + value); + + IRCMessageQueue* q = getSendQ(); + if (q != NULL) + { + q->putMessage(m); + } + } + + d->moduleWD.clear(); + + d->moduleWDMutex.Unlock(); + } + } + break; + + case 10: + // disconnect db + d->state = 0; + d->timer = 0; + d->initReady = false; + break; + + } + + wxThread::Sleep(1000); + + + + + } // while + + return 0; +} // Entry() diff --git a/ircDDB/IRCDDBApp.h b/ircDDB/IRCDDBApp.h new file mode 100644 index 0000000..d4f4812 --- /dev/null +++ b/ircDDB/IRCDDBApp.h @@ -0,0 +1,103 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de) +Copyright (C) 2012 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, see . + +*/ + +#if !defined(_IRCDDBAPP_H) +#define _IRCDDBAPP_H + + +#include "IRCDDB.h" +#include "IRCApplication.h" + +#include +#include + +class IRCDDBAppPrivate; + +class IRCDDBApp : public IRCApplication, wxThread +{ + public: + IRCDDBApp(const wxString& update_channel); + + virtual ~IRCDDBApp(); + + virtual void userJoin (const wxString& nick, const wxString& name, const wxString& host); + + virtual void userLeave (const wxString& nick); + + virtual void userChanOp (const wxString& nick, bool op); + virtual void userListReset(); + + virtual void msgChannel (IRCMessage * m); + virtual void msgQuery (IRCMessage * m); + + virtual void setCurrentNick(const wxString& nick); + virtual void setTopic(const wxString& topic); + + virtual void setBestServer(const wxString& ircUser); + + virtual void setSendQ( IRCMessageQueue * s ); + virtual IRCMessageQueue * getSendQ (); + + void startWork(); + void stopWork(); + + IRCDDB_RESPONSE_TYPE getReplyMessageType(); + + IRCMessage * getReplyMessage(); + + bool findUser ( const wxString& s ); + bool findRepeater ( const wxString& s ); + bool findGateway ( const wxString& s ); + + bool sendHeard(const wxString& myCall, const wxString& myCallExt, + const wxString& yourCall, const wxString& rpt1, + const wxString& rpt2, unsigned char flag1, + unsigned char flag2, unsigned char flag3, + const wxString& destination, const wxString& tx_msg, + const wxString& tx_stats); + + int getConnectionState(); + + void rptrQRG( const wxString& callsign, double txFrequency, double duplexShift, + double range, double agl ); + + void rptrQTH( const wxString& callsign, double latitude, double longitude, const wxString& desc1, + const wxString& desc2, const wxString& infoURL ); + + void kickWatchdog( const wxString& callsign, const wxString& wdInfo ); + + protected: + virtual wxThread::ExitCode Entry(); + + private: + void doUpdate ( wxString& msg ); + void doNotFound ( wxString& msg, wxString& retval ); + wxString getIPAddress( wxString& zonerp_cs ); + bool findServerUser(); + unsigned int calculateUsn(const wxString& nick); + wxString getLastEntryTime(int tableID); + IRCDDBAppPrivate * d; + wxDateTime m_maxTime; +}; + + +#endif diff --git a/ircDDB/IRCDDBClient.cpp b/ircDDB/IRCDDBClient.cpp new file mode 100644 index 0000000..359a73a --- /dev/null +++ b/ircDDB/IRCDDBClient.cpp @@ -0,0 +1,487 @@ +/* + +CIRCDDBClient - ircDDB client library in C++ + +Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de) +Copyright (C) 2011,2012 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, see . + +*/ + + +#include "IRCDDBClient.h" + +#include "IRCClient.h" +#include "IRCDDBApp.h" + +#include + + +struct CIRCDDBClientPrivate +{ + IRCClient * client; + IRCDDBApp * app; +}; + + +CIRCDDBClient::CIRCDDBClient(const wxString& hostName, unsigned int port, + const wxString& callsign, const wxString& password, + const wxString& versionInfo, const wxString& localAddr ) : d( new CIRCDDBClientPrivate ) + +{ + wxString update_channel = wxT("#dstar"); + + d->app = new IRCDDBApp(update_channel); + + d->client = new IRCClient( d->app, update_channel, hostName, port, callsign, + password, versionInfo, localAddr ); +} + +CIRCDDBClient::~CIRCDDBClient() +{ + delete d->client; + delete d->app; + delete d; +} + + + // A false return implies a network error, or unable to log in +bool CIRCDDBClient::open() +{ + wxLogVerbose(wxT("start")); + + d->client->startWork(); + d->app->startWork(); + + return true; +} + + +int CIRCDDBClient::getConnectionState() +{ + return d->app->getConnectionState(); +} + + +void CIRCDDBClient::rptrQTH(const wxString& callsign, double latitude, double longitude, const wxString& desc1, const wxString& desc2, const wxString& infoURL) +{ + d->app->rptrQTH(callsign, latitude, longitude, desc1, desc2, infoURL); +} + + +void CIRCDDBClient::rptrQRG(const wxString& callsign, double txFrequency, double duplexShift, double range, double agl) +{ + d->app->rptrQRG(callsign, txFrequency, duplexShift, range, agl); +} + + +void CIRCDDBClient::kickWatchdog(const wxString& callsign, const wxString& wdInfo) +{ + d->app->kickWatchdog(callsign, wdInfo); +} + + + +// Send heard data, a false return implies a network error +bool CIRCDDBClient::sendHeard( const wxString& myCall, const wxString& myCallExt, + const wxString& yourCall, const wxString& rpt1, + const wxString& rpt2, unsigned char flag1, + unsigned char flag2, unsigned char flag3 ) +{ + if (myCall.Len() != 8) + { + wxLogVerbose(wxT("CIRCDDBClient::sendHeard:myCall: len != 8")); + return false; + } + + if (myCallExt.Len() != 4) + { + wxLogVerbose(wxT("CIRCDDBClient::sendHeard:myCallExt: len != 4")); + return false; + } + + if (yourCall.Len() != 8) + { + wxLogVerbose(wxT("CIRCDDBClient::sendHeard:yourCall: len != 8")); + return false; + } + + if (rpt1.Len() != 8) + { + wxLogVerbose(wxT("CIRCDDBClient::sendHeard:rpt1: len != 8")); + return false; + } + + if (rpt2.Len() != 8) + { + wxLogVerbose(wxT("CIRCDDBClient::sendHeard:rpt2: len != 8")); + return false; + } + + return d->app->sendHeard( myCall, myCallExt, yourCall, rpt1, rpt2, flag1, flag2, flag3, + wxT(" "), wxT(""), wxT("")); +} + + +// Send heard data, a false return implies a network error +bool CIRCDDBClient::sendHeardWithTXMsg( const wxString& myCall, const wxString& myCallExt, + const wxString& yourCall, const wxString& rpt1, + const wxString& rpt2, unsigned char flag1, + unsigned char flag2, unsigned char flag3, + const wxString& network_destination, + const wxString& tx_message ) +{ + if (myCall.Len() != 8) + { + wxLogVerbose(wxT("CIRCDDBClient::sendHeard:myCall: len != 8")); + return false; + } + + if (myCallExt.Len() != 4) + { + wxLogVerbose(wxT("CIRCDDBClient::sendHeard:myCallExt: len != 4")); + return false; + } + + if (yourCall.Len() != 8) + { + wxLogVerbose(wxT("CIRCDDBClient::sendHeard:yourCall: len != 8")); + return false; + } + + if (rpt1.Len() != 8) + { + wxLogVerbose(wxT("CIRCDDBClient::sendHeard:rpt1: len != 8")); + return false; + } + + if (rpt2.Len() != 8) + { + wxLogVerbose(wxT("CIRCDDBClient::sendHeard:rpt2: len != 8")); + return false; + } + + wxString dest = network_destination; + + if (dest.Len() == 0) + { + dest = wxT(" "); + } + + if (dest.Len() != 8) + { + wxLogVerbose(wxT("CIRCDDBClient::sendHeard:network_destination: len != 8")); + return false; + } + + wxString msg; + + if (tx_message.Len() == 20) + { + unsigned int i; + for (i=0; i < tx_message.Len(); i++) + { + wxChar ch = tx_message.GetChar(i); + + if ((ch > 32) && (ch < 127)) + { + msg.Append(ch); + } + else + { + msg.Append(wxT('_')); + } + } + } + + return d->app->sendHeard( myCall, myCallExt, yourCall, rpt1, rpt2, flag1, flag2, flag3, + dest, msg, wxT("")); +} + + + +bool CIRCDDBClient::sendHeardWithTXStats( const wxString& myCall, const wxString& myCallExt, + const wxString& yourCall, const wxString& rpt1, + const wxString& rpt2, unsigned char flag1, + unsigned char flag2, unsigned char flag3, + int num_dv_frames, + int num_dv_silent_frames, + int num_bit_errors ) +{ + if ((num_dv_frames <= 0) || (num_dv_frames > 65535)) + { + wxLogVerbose(wxT("CIRCDDBClient::sendHeard:num_dv_frames not in range 1-65535")); + return false; + } + + if (num_dv_silent_frames > num_dv_frames) + { + wxLogVerbose(wxT("CIRCDDBClient::sendHeard:num_dv_silent_frames > num_dv_frames")); + return false; + } + + if (num_bit_errors > (4*num_dv_frames)) // max 4 bit errors per frame + { + wxLogVerbose(wxT("CIRCDDBClient::sendHeard:num_bit_errors > (4*num_dv_frames)")); + return false; + } + + if (myCall.Len() != 8) + { + wxLogVerbose(wxT("CIRCDDBClient::sendHeard:myCall: len != 8")); + return false; + } + + if (myCallExt.Len() != 4) + { + wxLogVerbose(wxT("CIRCDDBClient::sendHeard:myCallExt: len != 4")); + return false; + } + + if (yourCall.Len() != 8) + { + wxLogVerbose(wxT("CIRCDDBClient::sendHeard:yourCall: len != 8")); + return false; + } + + if (rpt1.Len() != 8) + { + wxLogVerbose(wxT("CIRCDDBClient::sendHeard:rpt1: len != 8")); + return false; + } + + if (rpt2.Len() != 8) + { + wxLogVerbose(wxT("CIRCDDBClient::sendHeard:rpt2: len != 8")); + return false; + } + + wxString stats = wxString::Format(wxT("%04x"), num_dv_frames); + + if (num_dv_silent_frames >= 0) + { + wxString s = wxString::Format(wxT("%02x"), (num_dv_silent_frames * 100) / num_dv_frames); + stats.Append(s); + + if (num_bit_errors >= 0) + { + s = wxString::Format(wxT("%02x"), (num_bit_errors * 125) / (num_dv_frames * 3)); + stats.Append(s); + } + else + { + stats.Append(wxT("__")); + } + } + else + { + stats.Append(wxT("____")); + } + + stats.Append(wxT("____________")); // stats string should have 20 chars + + return d->app->sendHeard( myCall, myCallExt, yourCall, rpt1, rpt2, flag1, flag2, flag3, + wxT(" "), wxT(""), stats); +} + + + +// Send query for a gateway/reflector, a false return implies a network error +bool CIRCDDBClient::findGateway(const wxString& gatewayCallsign) +{ + if (gatewayCallsign.Len() != 8) + { + wxLogVerbose(wxT("CIRCDDBClient::findGateway: len != 8")); + return false; + } + + return d->app->findGateway( gatewayCallsign.Upper()); +} + + +bool CIRCDDBClient::findRepeater(const wxString& repeaterCallsign) +{ + if (repeaterCallsign.Len() != 8) + { + wxLogVerbose(wxT("CIRCDDBClient::findRepeater: len != 8")); + return false; + } + + return d->app->findRepeater( repeaterCallsign.Upper()); +} + +// Send query for a user, a false return implies a network error +bool CIRCDDBClient::findUser(const wxString& userCallsign) +{ + if (userCallsign.Len() != 8) + { + wxLogVerbose(wxT("CIRCDDBClient::findUser: len != 8")); + return false; + } + + return d->app->findUser( userCallsign.Upper()); +} + +// The following functions are for processing received messages + +// Get the waiting message type +IRCDDB_RESPONSE_TYPE CIRCDDBClient::getMessageType() +{ + return d->app->getReplyMessageType(); +} + +// Get a gateway message, as a result of IDRT_REPEATER returned from getMessageType() +// A false return implies a network error +bool CIRCDDBClient::receiveRepeater(wxString& repeaterCallsign, wxString& gatewayCallsign, wxString& address) +{ + IRCDDB_RESPONSE_TYPE rt = d->app->getReplyMessageType(); + + if (rt != IDRT_REPEATER) + { + wxLogError(wxT("CIRCDDBClient::receiveRepeater: unexpected response type")); + return false; + } + + IRCMessage * m = d->app->getReplyMessage(); + + if (m == NULL) + { + wxLogError(wxT("CIRCDDBClient::receiveRepeater: no message")); + return false; + } + + if (!m->getCommand().IsSameAs(wxT("IDRT_REPEATER"))) + { + wxLogError(wxT("CIRCDDBClient::receiveRepeater: wrong message type")); + delete m; + return false; + } + + if (m->getParamCount() != 3) + { + wxLogError(wxT("CIRCDDBClient::receiveRepeater: unexpected number of message parameters")); + delete m; + return false; + } + + repeaterCallsign = m->getParam(0); + gatewayCallsign = m->getParam(1); + address = m->getParam(2); + + delete m; + + return true; +} + +// Get a gateway message, as a result of IDRT_GATEWAY returned from getMessageType() +// A false return implies a network error +bool CIRCDDBClient::receiveGateway(wxString& gatewayCallsign, wxString& address) +{ + IRCDDB_RESPONSE_TYPE rt = d->app->getReplyMessageType(); + + if (rt != IDRT_GATEWAY) + { + wxLogError(wxT("CIRCDDBClient::receiveGateway: unexpected response type")); + return false; + } + + IRCMessage * m = d->app->getReplyMessage(); + + if (m == NULL) + { + wxLogError(wxT("CIRCDDBClient::receiveGateway: no message")); + return false; + } + + if (!m->getCommand().IsSameAs(wxT("IDRT_GATEWAY"))) + { + wxLogError(wxT("CIRCDDBClient::receiveGateway: wrong message type")); + delete m; + return false; + } + + if (m->getParamCount() != 2) + { + wxLogError(wxT("CIRCDDBClient::receiveGateway: unexpected number of message parameters")); + delete m; + return false; + } + + gatewayCallsign = m->getParam(0); + address = m->getParam(1); + + delete m; + + return true; +} + +// Get a user message, as a result of IDRT_USER returned from getMessageType() +// A false return implies a network error +bool CIRCDDBClient::receiveUser(wxString& userCallsign, wxString& repeaterCallsign, wxString& gatewayCallsign, wxString& address) +{ + wxString dummy; + return receiveUser(userCallsign, repeaterCallsign, gatewayCallsign, address, dummy); +} + +bool CIRCDDBClient::receiveUser(wxString& userCallsign, wxString& repeaterCallsign, wxString& gatewayCallsign, wxString& address, + wxString& timeStamp) +{ + IRCDDB_RESPONSE_TYPE rt = d->app->getReplyMessageType(); + + if (rt != IDRT_USER) + { + wxLogError(wxT("CIRCDDBClient::receiveUser: unexpected response type")); + return false; + } + + IRCMessage * m = d->app->getReplyMessage(); + + if (m == NULL) + { + wxLogError(wxT("CIRCDDBClient::receiveUser: no message")); + return false; + } + + if (!m->getCommand().IsSameAs(wxT("IDRT_USER"))) + { + wxLogError(wxT("CIRCDDBClient::receiveUser: wrong message type")); + delete m; + return false; + } + + if (m->getParamCount() != 5) + { + wxLogError(wxT("CIRCDDBClient::receiveUser: unexpected number of message parameters")); + delete m; + return false; + } + + userCallsign = m->getParam(0); + repeaterCallsign = m->getParam(1); + gatewayCallsign = m->getParam(2); + address = m->getParam(3); + timeStamp = m->getParam(4); + + delete m; + + return true; +} + +void CIRCDDBClient::close() // Implictely kills any threads in the IRC code +{ + d->client -> stopWork(); + d->app -> stopWork(); +} + diff --git a/ircDDB/IRCDDBClient.h b/ircDDB/IRCDDBClient.h new file mode 100644 index 0000000..f8c8650 --- /dev/null +++ b/ircDDB/IRCDDBClient.h @@ -0,0 +1,162 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de) +Copyright (C) 2011,2012 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, see . + +*/ + + +#if !defined(_IRCDDBCLIENT_H) +#define _IRCDDBCLIENT_H + +#include "IRCDDB.h" +#include + +struct CIRCDDBPrivate; + +class CIRCDDBClient : public CIRCDDB{ +public: + CIRCDDBClient(const wxString& hostName, unsigned int port, const wxString& callsign, const wxString& password, + const wxString& versionInfo, const wxString& localAddr = wxEmptyString ); + ~CIRCDDBClient(); + + // A false return implies a network error, or unable to log in + bool open(); + + + // rptrQTH can be called multiple times if necessary + // callsign The callsign of the repeater + // latitude WGS84 position of antenna in degrees, positive value -> NORTH + // longitude WGS84 position of antenna in degrees, positive value -> EAST + // desc1, desc2 20-character description of QTH + // infoURL URL of a web page with information about the repeater + + void rptrQTH( const wxString& callsign, double latitude, double longitude, const wxString& desc1, + const wxString& desc2, const wxString& infoURL ); + + + + // rptrQRG can be called multiple times if necessary + // callsign callsign of the repeater + // txFrequency repeater TX frequency in MHz + // duplexShift duplex shift in MHz (positive or negative value): RX_freq = txFrequency + duplexShift + // range range of the repeater in meters (meters = miles * 1609.344) + // agl height of the antenna above ground in meters (meters = feet * 0.3048) + + void rptrQRG( const wxString& callsign, double txFrequency, double duplexShift, double range, double agl ); + + + // If you call this method once, watchdog messages will be sent to the + // to the ircDDB network every 15 minutes. Invoke this method every 1-2 minutes to indicate + // that the gateway is working properly. After activating the watchdog, a red LED will be displayed + // on the ircDDB web page if this method is not called within a period of about 30 minutes. + // The string wdInfo should contain information about the source of the alive messages, e.g., + // version of the RF decoding software. For example, the ircDDB java software sets this + // to "rpm_ircddbmhd-x.z-z". The string wdInfo must contain at least one non-space character. + + void kickWatchdog(const wxString& callsign, const wxString& wdInfo); + + + + // get internal network status + int getConnectionState(); + // one of these values is returned: + // 0 = not (yet) connected to the IRC server + // 1-6 = a new connection was established, download of repeater info etc. is + // in progress + // 7 = the ircDDB connection is fully operational + // 10 = some network error occured, next state is "0" (new connection attempt) + + // Send heard data, a false return implies a network error + bool sendHeard(const wxString& myCall, const wxString& myCallExt, + const wxString& yourCall, const wxString& rpt1, + const wxString& rpt2, unsigned char flag1, + unsigned char flag2, unsigned char flag3 ); + + + // same as sendHeard with two new fields: + // network_destination: empty string or 8-char call sign of the repeater + // or reflector, where this transmission is relayed to. + // tx_message: 20-char TX message or empty string, if the user did not + // send a TX message + bool sendHeardWithTXMsg(const wxString& myCall, const wxString& myCallExt, + const wxString& yourCall, const wxString& rpt1, + const wxString& rpt2, unsigned char flag1, + unsigned char flag2, unsigned char flag3, + const wxString& network_destination, + const wxString& tx_message ); + + // this method should be called at the end of a transmission + // num_dv_frames: number of DV frames sent out (96 bit frames, 20ms) + // num_dv_silent_frames: number of DV silence frames sent out in the + // last transmission, or -1 if the information is not available + // num_bit_errors: number of bit errors of the received data. This should + // be the derived from the first Golay block of the voice data. This + // error correction code only looks at 24 bits of the 96 bit frame. + // So, the overall bit error rate is calculated like this: + // BER = num_bit_errors / (num_dv_frames * 24) + // Set num_bit_errors = -1, if the error information is not available. + bool sendHeardWithTXStats(const wxString& myCall, const wxString& myCallExt, + const wxString& yourCall, const wxString& rpt1, + const wxString& rpt2, unsigned char flag1, + unsigned char flag2, unsigned char flag3, + int num_dv_frames, + int num_dv_silent_frames, + int num_bit_errors ); + + // The following three functions don't block waiting for a reply, they just send the data + + // Send query for a gateway/reflector, a false return implies a network error + bool findGateway(const wxString& gatewayCallsign); + + // Send query for a repeater module, a false return implies a network error + bool findRepeater(const wxString& repeaterCallsign); + + // Send query for a user, a false return implies a network error + bool findUser(const wxString& userCallsign); + + // The following functions are for processing received messages + + // Get the waiting message type + IRCDDB_RESPONSE_TYPE getMessageType(); + + // Get a gateway message, as a result of IDRT_REPEATER returned from getMessageType() + // A false return implies a network error + bool receiveRepeater(wxString& repeaterCallsign, wxString& gatewayCallsign, wxString& address); + + // Get a gateway message, as a result of IDRT_GATEWAY returned from getMessageType() + // A false return implies a network error + bool receiveGateway(wxString& gatewayCallsign, wxString& address); + + // Get a user message, as a result of IDRT_USER returned from getMessageType() + // A false return implies a network error + bool receiveUser(wxString& userCallsign, wxString& repeaterCallsign, wxString& gatewayCallsign, wxString& address); + + bool receiveUser(wxString& userCallsign, wxString& repeaterCallsign, wxString& gatewayCallsign, wxString& address, + wxString& timeStamp ); + + void close(); // Implictely kills any threads in the IRC code + + +private: + struct CIRCDDBClientPrivate * const d; + +}; + +#endif // _IRCDDB_H + diff --git a/ircDDB/IRCDDBMultiClient.cpp b/ircDDB/IRCDDBMultiClient.cpp new file mode 100644 index 0000000..f7feb11 --- /dev/null +++ b/ircDDB/IRCDDBMultiClient.cpp @@ -0,0 +1,354 @@ +/* + +CIRCDDBClient - ircDDB client library in C++ + +Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de) +Copyright (C) 2011,2012 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, see . + +*/ + +#include "IRCDDBMultiClient.h" +#include + +CIRCDDBMultiClient::CIRCDDBMultiClient(const CIRCDDB_Array& clients) : +m_clients(), +m_queriesLock(), +m_responseQueueLock() +{ + for (unsigned int i = 0; i < clients.Count(); i++) { + if (clients[i] != NULL) + m_clients.Add(clients[i]); + } +} + +CIRCDDBMultiClient::~CIRCDDBMultiClient() +{ + for (unsigned int i = 0; i < m_clients.Count(); i++) { + delete m_clients[i]; + } + + while (m_responseQueue.Count() > 0) { + delete m_responseQueue[0]; + m_responseQueue.RemoveAt(0); + } + + for (CIRCDDBMultiClientQuery_HashMap::iterator it = m_userQueries.begin(); it != m_userQueries.end(); it++) + delete it->second; + m_userQueries.clear(); + + for (CIRCDDBMultiClientQuery_HashMap::iterator it = m_repeaterQueries.begin(); it != m_repeaterQueries.end(); it++) + delete it->second; + m_repeaterQueries.clear(); + + for (CIRCDDBMultiClientQuery_HashMap::iterator it = m_gatewayQueries.begin(); it != m_gatewayQueries.end(); it++) + delete it->second; + m_gatewayQueries.clear(); +} + +bool CIRCDDBMultiClient::open() +{ + bool result = true; + + for (unsigned int i = 0; i < m_clients.Count(); i++) { + result = m_clients[i]->open() && result; + } + + if (!result) close(); + + return result; +} + +void CIRCDDBMultiClient::rptrQTH(const wxString & callsign, double latitude, double longitude, const wxString & desc1, const wxString & desc2, const wxString & infoURL) +{ + for (unsigned int i = 0; i < m_clients.Count(); i++) { + m_clients[i]->rptrQTH(callsign, latitude, longitude, desc1, desc2, infoURL); + } +} + +void CIRCDDBMultiClient::rptrQRG(const wxString & callsign, double txFrequency, double duplexShift, double range, double agl) +{ + for (unsigned int i = 0; i < m_clients.Count(); i++) { + m_clients[i]->rptrQRG(callsign, txFrequency, duplexShift, range, agl); + } +} + +void CIRCDDBMultiClient::kickWatchdog(const wxString & callsign, const wxString & wdInfo) +{ + for (unsigned int i = 0; i < m_clients.Count(); i++) { + m_clients[i]->kickWatchdog(callsign, wdInfo); + } +} + +int CIRCDDBMultiClient::getConnectionState() +{ + for (unsigned int i = 0; i < m_clients.Count(); i++) { + int state = m_clients[i]->getConnectionState(); + if (state != 7) + return state; + } + + return 7; +} + +bool CIRCDDBMultiClient::sendHeard(const wxString & myCall, const wxString & myCallExt, const wxString & yourCall, const wxString & rpt1, const wxString & rpt2, unsigned char flag1, unsigned char flag2, unsigned char flag3) +{ + bool result = true; + + for (unsigned int i = 0; i < m_clients.Count(); i++) { + result = m_clients[i]->sendHeard(myCall, myCallExt, yourCall, rpt1, rpt2, flag1, flag2, flag3) && result; + } + + return result; +} + +bool CIRCDDBMultiClient::sendHeardWithTXMsg(const wxString & myCall, const wxString & myCallExt, const wxString & yourCall, const wxString & rpt1, const wxString & rpt2, unsigned char flag1, unsigned char flag2, unsigned char flag3, const wxString & network_destination, const wxString & tx_message) +{ + bool result = true; + + for (unsigned int i = 0; i < m_clients.Count(); i++) { + result = m_clients[i]->sendHeardWithTXMsg(myCall, myCallExt, yourCall, rpt1, rpt2, flag1, flag2, flag3, network_destination, tx_message) && result; + } + + return result; +} + +bool CIRCDDBMultiClient::sendHeardWithTXStats(const wxString & myCall, const wxString & myCallExt, const wxString & yourCall, const wxString & rpt1, const wxString & rpt2, unsigned char flag1, unsigned char flag2, unsigned char flag3, int num_dv_frames, int num_dv_silent_frames, int num_bit_errors) +{ + bool result = true; + + for (unsigned int i = 0; i < m_clients.Count(); i++) { + result = m_clients[i]->sendHeardWithTXStats(myCall, myCallExt, yourCall, rpt1, rpt2, flag1, flag2, flag3, num_dv_frames, num_dv_silent_frames, num_bit_errors) && result; + } + + return result; +} + +bool CIRCDDBMultiClient::findGateway(const wxString & gatewayCallsign) +{ + pushQuery(IDRT_GATEWAY, gatewayCallsign, new CIRCDDBMultiClientQuery(wxEmptyString, wxEmptyString, gatewayCallsign, wxEmptyString, wxEmptyString, IDRT_GATEWAY)); + bool result = true; + for (unsigned int i = 0; i < m_clients.Count(); i++) { + result = m_clients[i]->findGateway(gatewayCallsign) && result; + } + + return result; +} + +bool CIRCDDBMultiClient::findRepeater(const wxString & repeaterCallsign) +{ + pushQuery(IDRT_REPEATER, repeaterCallsign, new CIRCDDBMultiClientQuery(wxEmptyString, repeaterCallsign, wxEmptyString, wxEmptyString, wxEmptyString, IDRT_REPEATER)); + bool result = true; + for (unsigned int i = 0; i < m_clients.Count(); i++) { + result = m_clients[i]->findRepeater(repeaterCallsign) && result; + } + + return result; +} + +bool CIRCDDBMultiClient::findUser(const wxString & userCallsign) +{ + pushQuery(IDRT_USER, userCallsign, new CIRCDDBMultiClientQuery(userCallsign, wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString, IDRT_USER)); + bool result = true; + for (unsigned int i = 0; i < m_clients.Count(); i++) { + result = m_clients[i]->findUser(userCallsign) && result; + } + + return result; +} + +IRCDDB_RESPONSE_TYPE CIRCDDBMultiClient::getMessageType() +{ + //procees the inner clients at each call + for (unsigned int i = 0; i < m_clients.Count(); i++) { + wxString user = wxEmptyString, repeater = wxEmptyString, gateway = wxEmptyString, address = wxEmptyString, timestamp = wxEmptyString, key = wxEmptyString; + IRCDDB_RESPONSE_TYPE type = m_clients[i]->getMessageType(); + + switch (type) { + case IDRT_USER: { + if (!m_clients[i]->receiveUser(user, repeater, gateway, address, timestamp)) + type = IDRT_NONE; + key = user; + //wxLogMessage(wxT("After receive user : %s %s %s %s %s client idx %d"), user, repeater, gateway, address, timestamp, i); + break; + } + case IDRT_GATEWAY: { + if (!m_clients[i]->receiveGateway(gateway, address)) + type = IDRT_NONE; + key = gateway; + break; + } + case IDRT_REPEATER: { + if (!m_clients[i]->receiveRepeater(repeater, gateway, address)) + type = IDRT_NONE; + key = repeater; + break; + } + case IDRT_NONE: { + default: + break; + } + } + + if (type != IDRT_NONE) + { + wxMutexLocker locker(m_queriesLock); + bool canAddToQueue = false; + bool wasQuery = false; + CIRCDDBMultiClientQuery * item = popQuery(type, key); + if (item != NULL) {//is this a response to a query we've sent ? + item->Update(user, repeater, gateway, address, timestamp);//update item (if needed) + canAddToQueue = (item->incrementResponseCount() >= m_clients.Count()); //did all the clients respond or did we have an answer ? + wasQuery = true; + } + else { + item = new CIRCDDBMultiClientQuery(user, repeater, gateway, address, timestamp, type); + canAddToQueue = true; + } + + //wxLogMessage(wxT("After process : %s %s %s %s %s canAdd %d wasQuery %d"), user, repeater, gateway, address, timestamp, (int)canAddToQueue, (int)wasQuery); + if (canAddToQueue) { + wxMutexLocker responselocker(m_responseQueueLock); + m_responseQueue.Add(item); + } + else if (wasQuery) + pushQuery(type, key, item); + } + } + + { //this is an artificial scope, I'm not sure if compiler optimization would move instantiation of locker or not hence this + //finally send the first item we queued + wxMutexLocker locker(m_responseQueueLock); + if (m_responseQueue.Count() == 0) + return IDRT_NONE; + + return m_responseQueue[0]->getType(); + } +} + +bool CIRCDDBMultiClient::receiveRepeater(wxString & repeaterCallsign, wxString & gatewayCallsign, wxString & address) +{ + CIRCDDBMultiClientQuery * item = checkAndGetNextResponse(IDRT_REPEATER, wxT("CIRCDDBMultiClient::receiveRepeater: unexpected response type")); + if (item == NULL) + return false; + + repeaterCallsign = item->getRepeater(); + gatewayCallsign = item->getGateway(); + address = item->getAddress(); + delete item; + return true; +} + +bool CIRCDDBMultiClient::receiveGateway(wxString & gatewayCallsign, wxString & address) +{ + CIRCDDBMultiClientQuery * item = checkAndGetNextResponse(IDRT_GATEWAY, wxT("CIRCDDBMultiClient::receiveGateway: unexpected response type")); + if (item == NULL) + return false; + + gatewayCallsign = item->getGateway(); + address = item->getAddress(); + delete item; + return true; +} + +bool CIRCDDBMultiClient::receiveUser(wxString & userCallsign, wxString & repeaterCallsign, wxString & gatewayCallsign, wxString & address) +{ + wxString dummy; + return receiveUser(userCallsign, repeaterCallsign, gatewayCallsign, address, dummy); +} + +bool CIRCDDBMultiClient::receiveUser(wxString & userCallsign, wxString & repeaterCallsign, wxString & gatewayCallsign, wxString & address, wxString & timeStamp) +{ + CIRCDDBMultiClientQuery * item = checkAndGetNextResponse(IDRT_USER, wxT("CIRCDDBMultiClient::receiveUser: unexpected response type")); + if (item == NULL) { + //wxLogMessage(wxT("CIRCDDBMultiClient::receiveUser NO USER IN QUEUE")); + return false; + } + + //wxLogMessage(wxT("CIRCDDBMultiClient::receiveUser : %s"), item->toString()); + + userCallsign = item->getUser().Clone(); + repeaterCallsign = item->getRepeater().Clone(); + gatewayCallsign = item->getGateway().Clone(); + address = item->getAddress().Clone(); + timeStamp = item->getTimestamp().Clone(); + delete item; + return true; +} + +void CIRCDDBMultiClient::close() +{ + for (unsigned int i = 0; i < m_clients.Count(); i++) { + m_clients[i]->close(); + } +} + +CIRCDDBMultiClientQuery * CIRCDDBMultiClient::checkAndGetNextResponse(IRCDDB_RESPONSE_TYPE expectedType, wxString errorMessage) +{ + CIRCDDBMultiClientQuery * item = NULL; + wxMutexLocker locker(m_responseQueueLock); + + if (m_responseQueue.Count() == 0 || m_responseQueue[0]->getType() != expectedType) { + wxLogError(errorMessage); + } + else { + item = m_responseQueue[0]; + m_responseQueue.RemoveAt(0); + } + + return item; +} + +void CIRCDDBMultiClient::pushQuery(IRCDDB_RESPONSE_TYPE type, const wxString& key, CIRCDDBMultiClientQuery * query) +{ + CIRCDDBMultiClientQuery_HashMap * queries = getQueriesHashMap(type); + wxMutexLocker locker(m_queriesLock); + if (queries != NULL && (*queries)[key] == NULL) + (*queries)[key] = query; + else + delete query; +} + + +CIRCDDBMultiClientQuery * CIRCDDBMultiClient::popQuery(IRCDDB_RESPONSE_TYPE type, const wxString & key) +{ + CIRCDDBMultiClientQuery_HashMap * queries = getQueriesHashMap(type); + wxMutexLocker locker(m_queriesLock); + + CIRCDDBMultiClientQuery * item = NULL; + + if (queries != NULL && (item = (*queries)[key]) != NULL) + queries->erase(key); + + return item; +} + +CIRCDDBMultiClientQuery_HashMap * CIRCDDBMultiClient::getQueriesHashMap(IRCDDB_RESPONSE_TYPE type) +{ + switch (type) + { + case IDRT_USER: + return &m_userQueries; + case IDRT_GATEWAY: + return &m_gatewayQueries; + case IDRT_REPEATER: + return &m_repeaterQueries; + case IDRT_NONE: + default: + return NULL; + } +} + + diff --git a/ircDDB/IRCDDBMultiClient.h b/ircDDB/IRCDDBMultiClient.h new file mode 100644 index 0000000..42f81ae --- /dev/null +++ b/ircDDB/IRCDDBMultiClient.h @@ -0,0 +1,172 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de) +Copyright (C) 2011,2012 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, see . + +*/ + + +#if !defined(_IRCDDBMULTICLIENT_H) +#define _IRCDDBMULTICLIENT_H + +#include "IRCDDB.h" +#include +#include + +//Small data container to keep track of queries with sent to the inner clients +class CIRCDDBMultiClientQuery +{ +public: + CIRCDDBMultiClientQuery(const wxString& user, + const wxString& repeater, + const wxString& gateway, + const wxString& address, + const wxString& timestamp, + IRCDDB_RESPONSE_TYPE type) : + m_user(user.Clone()), + m_repeater(repeater.Clone()), + m_gateway(gateway.Clone()), + m_address(address.Clone()), + m_timestamp(timestamp.Clone()), + m_type(type), + m_responseCount(0) + { + + } + + wxString getUser() const + { + return m_user; + } + + wxString getRepeater() const + { + return m_repeater; + } + + wxString getGateway() const + { + return m_gateway; + } + + wxString getAddress() const + { + return m_address; + } + + wxString getTimestamp() const + { + return m_timestamp; + } + + unsigned int getResponseCount() + { + return m_responseCount; + } + + unsigned int incrementResponseCount() + { + ++m_responseCount; + //wxLogMessage(wxT("Resp Count : %s %d"), toString(), m_responseCount); + return m_responseCount; + } + + /* + Updates the entry, but only if the timestamp is newer. if an address was already specified it is kept. + */ + void Update(const wxString& user, const wxString& repeater, const wxString& gateway, const wxString& address, const wxString& timestamp) + { + //wxLogMessage(wxT("Before : %s"), toString()); + if (timestamp.IsEmpty() || timestamp.Cmp(m_timestamp) >= 0) { + m_user = user.Clone(); + m_repeater = repeater.Clone(); + m_gateway = gateway.Clone(); + m_timestamp = timestamp.Clone(); + + if(m_address.IsEmpty() && !address.IsEmpty()) + m_address = address.Clone(); + } + //wxLogMessage(wxT("After : %s"), toString()); + } + + IRCDDB_RESPONSE_TYPE getType() + { + return m_type; + } + + wxString toString() + { + return wxString::Format(wxT("%s %s %s %s %s"), m_user, m_repeater, m_gateway, m_address, m_timestamp); + } + +private: + wxString m_user; + wxString m_repeater; + wxString m_gateway; + wxString m_address; + wxString m_timestamp; + IRCDDB_RESPONSE_TYPE m_type; + unsigned int m_responseCount; +}; + +WX_DECLARE_STRING_HASH_MAP(CIRCDDBMultiClientQuery*, CIRCDDBMultiClientQuery_HashMap); +WX_DEFINE_ARRAY_PTR(CIRCDDBMultiClientQuery*, CIRCDDBMultiClientQuery_Array); + +class CIRCDDBMultiClient : public CIRCDDB +{ +public: + CIRCDDBMultiClient(const CIRCDDB_Array& clients); + ~CIRCDDBMultiClient(); + + // Inherited via CIRCDDB + virtual bool open(); + virtual void rptrQTH(const wxString & callsign, double latitude, double longitude, const wxString & desc1, const wxString & desc2, const wxString & infoURL); + virtual void rptrQRG(const wxString & callsign, double txFrequency, double duplexShift, double range, double agl); + virtual void kickWatchdog(const wxString & callsign, const wxString & wdInfo); + virtual int getConnectionState() ; + virtual bool sendHeard(const wxString & myCall, const wxString & myCallExt, const wxString & yourCall, const wxString & rpt1, const wxString & rpt2, unsigned char flag1, unsigned char flag2, unsigned char flag3); + virtual bool sendHeardWithTXMsg(const wxString & myCall, const wxString & myCallExt, const wxString & yourCall, const wxString & rpt1, const wxString & rpt2, unsigned char flag1, unsigned char flag2, unsigned char flag3, const wxString & network_destination, const wxString & tx_message); + virtual bool sendHeardWithTXStats(const wxString & myCall, const wxString & myCallExt, const wxString & yourCall, const wxString & rpt1, const wxString & rpt2, unsigned char flag1, unsigned char flag2, unsigned char flag3, int num_dv_frames, int num_dv_silent_frames, int num_bit_errors); + virtual bool findGateway(const wxString & gatewayCallsign); + virtual bool findRepeater(const wxString & repeaterCallsign); + virtual bool findUser(const wxString & userCallsign); + virtual IRCDDB_RESPONSE_TYPE getMessageType(); + virtual bool receiveRepeater(wxString & repeaterCallsign, wxString & gatewayCallsign, wxString & address); + virtual bool receiveGateway(wxString & gatewayCallsign, wxString & address); + virtual bool receiveUser(wxString & userCallsign, wxString & repeaterCallsign, wxString & gatewayCallsign, wxString & address); + virtual bool receiveUser(wxString & userCallsign, wxString & repeaterCallsign, wxString & gatewayCallsign, wxString & address, wxString & timeStamp); + virtual void close(); + + // + +private : + CIRCDDB_Array m_clients; + wxMutex m_queriesLock, m_responseQueueLock; + + CIRCDDBMultiClientQuery_HashMap m_userQueries; + CIRCDDBMultiClientQuery_HashMap m_repeaterQueries; + CIRCDDBMultiClientQuery_HashMap m_gatewayQueries; + CIRCDDBMultiClientQuery_Array m_responseQueue; + + CIRCDDBMultiClientQuery * checkAndGetNextResponse(IRCDDB_RESPONSE_TYPE expectedType, wxString errorMessage); + void pushQuery(IRCDDB_RESPONSE_TYPE type, const wxString& key, CIRCDDBMultiClientQuery * query); + CIRCDDBMultiClientQuery * popQuery(IRCDDB_RESPONSE_TYPE type, const wxString& key); + CIRCDDBMultiClientQuery_HashMap * getQueriesHashMap(IRCDDB_RESPONSE_TYPE type); +}; +#endif + diff --git a/ircDDB/IRCMessage.cpp b/ircDDB/IRCMessage.cpp new file mode 100644 index 0000000..b8a9ebd --- /dev/null +++ b/ircDDB/IRCMessage.cpp @@ -0,0 +1,177 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de) + +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, see . + +*/ + +#include "IRCMessage.h" + + + +IRCMessage::IRCMessage () +{ + numParams = 0; + prefixParsed = false; +} + +IRCMessage::IRCMessage ( const wxString& toNick, const wxString& msg ) +{ + command = wxT("PRIVMSG"); + numParams = 2; + params.Add( toNick ); + params.Add( msg ); + prefixParsed = false; +} + +IRCMessage::IRCMessage ( const wxString& cmd ) +{ + command = cmd; + numParams = 0; + prefixParsed = false; +} + +IRCMessage::~IRCMessage() +{ +} + + +void IRCMessage::addParam( const wxString& p ) +{ + params.Add( p ); + numParams = params.GetCount(); +} + +int IRCMessage::getParamCount() +{ + return params.GetCount(); +} + +wxString IRCMessage::getParam( int pos ) +{ + return params[pos]; +} + +wxString IRCMessage::getCommand() +{ + return command; +} + + +void IRCMessage::parsePrefix() +{ + unsigned int i; + + for (i=0; i < 3; i++) + { + prefixComponents.Add(wxT("")); + } + + int state = 0; + + for (i=0; i < prefix.Len(); i++) + { + wxChar c = prefix.GetChar(i); + + switch (c) + { + case wxT('!'): + state = 1; // next is name + break; + + case wxT('@'): + state = 2; // next is host + break; + + default: + prefixComponents[state].Append(c); + break; + } + } + + prefixParsed = true; +} + +wxString& IRCMessage::getPrefixNick() +{ + if (!prefixParsed) + { + parsePrefix(); + } + + return prefixComponents[0]; +} + +wxString& IRCMessage::getPrefixName() +{ + if (!prefixParsed) + { + parsePrefix(); + } + + return prefixComponents[1]; +} + +wxString& IRCMessage::getPrefixHost() +{ + if (!prefixParsed) + { + parsePrefix(); + } + + return prefixComponents[2]; +} + +void IRCMessage::composeMessage ( wxString& output ) +{ +#if defined(DEBUG_IRC) + wxString d = wxT("T [") + prefix + wxT("] [") + command + wxT("]"); + for (int i=0; i < numParams; i++) + { + d.Append(wxT(" [") + params[i] + wxT("]") ); + } + d.Replace(wxT("%"), wxT("%%"), true); + d.Replace(wxT("\\"), wxT("\\\\"), true); + wxLogVerbose(d); +#endif + + wxString o; + + if (prefix.Len() > 0) + { + o = wxT(":") + prefix + wxT(" "); + } + + o.Append(command); + + for (int i=0; i < numParams; i++) + { + if (i == (numParams - 1)) + { + o.Append(wxT(" :") + params[i]); + } + else + { + o.Append(wxT(" ") + params[i]); + } + } + + o.Append(wxT("\r\n")); + + output = o; +} + diff --git a/ircDDB/IRCMessage.h b/ircDDB/IRCMessage.h new file mode 100644 index 0000000..25c33af --- /dev/null +++ b/ircDDB/IRCMessage.h @@ -0,0 +1,71 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010 Michael Dirska, DL1BFF (dl1bff@mdx.de) + +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, see . + +*/ + +#if !defined(_IRC_MESSAGE_H) +#define _IRC_MESSAGE_H + +#include + + +class IRCMessage +{ + public: + + IRCMessage(); + + IRCMessage( const wxString& toNick, const wxString& msg ); + + IRCMessage( const wxString& command ); + + ~IRCMessage(); + + + + wxString prefix; + wxString command; + wxArrayString params; + + int numParams; + + wxString& getPrefixNick(); + wxString& getPrefixName(); + wxString& getPrefixHost(); + + void composeMessage ( wxString& output ); + + void addParam( const wxString& p ); + + wxString getCommand(); + + wxString getParam( int pos ); + + int getParamCount(); + + private: + + void parsePrefix(); + + wxArrayString prefixComponents; + bool prefixParsed; + +}; + +#endif diff --git a/ircDDB/IRCMessageQueue.cpp b/ircDDB/IRCMessageQueue.cpp new file mode 100644 index 0000000..b42d798 --- /dev/null +++ b/ircDDB/IRCMessageQueue.cpp @@ -0,0 +1,137 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010 Michael Dirska, DL1BFF (dl1bff@mdx.de) + +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, see . + +*/ + +#include "IRCMessageQueue.h" + + +IRCMessageQueue::IRCMessageQueue() +{ + eof = false; + first = NULL; + last = NULL; + +} + +IRCMessageQueue::~IRCMessageQueue() +{ + while (messageAvailable()) + { + IRCMessage * m = getMessage(); + + delete m; + } +} + + +bool IRCMessageQueue::isEOF() +{ + return eof; +} + + +void IRCMessageQueue::signalEOF() +{ + eof = true; +} + + +bool IRCMessageQueue::messageAvailable() +{ + wxMutexLocker lock(accessMutex); + + return (first != NULL); +} + + +IRCMessage * IRCMessageQueue::peekFirst() +{ + wxMutexLocker lock(accessMutex); + + IRCMessageQueueItem * k = first; + + if ( k == NULL ) + { + return NULL; + } + + return k->msg; +} + + +IRCMessage * IRCMessageQueue::getMessage() +{ + wxMutexLocker lock(accessMutex); + + IRCMessageQueueItem * k; + + if (first == NULL) + { + return NULL; + } + + k = first; + + first = k -> next; + + if (k -> next == NULL) + { + last = NULL; + } + else + { + k -> next -> prev = NULL; + } + + + IRCMessage * msg = k -> msg; + + delete k; + + return msg; +} + + +void IRCMessageQueue::putMessage( IRCMessage * m ) +{ + wxMutexLocker lock(accessMutex); + + // wxLogVerbose(wxT("IRCMessageQueue::putMessage")); + + IRCMessageQueueItem * k = new IRCMessageQueueItem(m); + + k -> prev = last; + k -> next = NULL; + + if (last == NULL) + { + first = k; + } + else + { + last -> next = k; + } + + last = k; +} + + + + diff --git a/ircDDB/IRCMessageQueue.h b/ircDDB/IRCMessageQueue.h new file mode 100644 index 0000000..b405a40 --- /dev/null +++ b/ircDDB/IRCMessageQueue.h @@ -0,0 +1,79 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010 Michael Dirska, DL1BFF (dl1bff@mdx.de) + +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, see . + +*/ + +#if !defined(_IRCMESSAGEQUEUE_H) +#define _IRCMESSAGEQUEUE_H + +#include + +#include "IRCMessage.h" + + +class IRCMessageQueueItem +{ + public: + IRCMessageQueueItem( IRCMessage * m ) + { + msg = m; + } + + ~IRCMessageQueueItem() + { + } + + IRCMessage * msg; + + IRCMessageQueueItem * prev; + IRCMessageQueueItem * next; +}; + + +class IRCMessageQueue +{ + public: + IRCMessageQueue(); + + ~IRCMessageQueue(); + + bool isEOF(); + + void signalEOF(); + + bool messageAvailable(); + + IRCMessage * getMessage(); + + IRCMessage * peekFirst(); + + void putMessage ( IRCMessage * m ); + + private: + + bool eof; + + IRCMessageQueueItem * first; + IRCMessageQueueItem * last; + + wxMutex accessMutex; + +}; + +#endif diff --git a/ircDDB/IRCProtocol.cpp b/ircDDB/IRCProtocol.cpp new file mode 100644 index 0000000..122b687 --- /dev/null +++ b/ircDDB/IRCProtocol.cpp @@ -0,0 +1,447 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de) + +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, see . + +*/ + + +#include "IRCProtocol.h" + +#include + +#define CIRCDDB_VERSION "1.2.4" + +IRCProtocol::IRCProtocol ( IRCApplication * app, + const wxString& callsign, const wxString& password, const wxString& channel, + const wxString& versionInfo ) +{ + this -> password = password; + this -> channel = channel; + this -> app = app; + + this->versionInfo = wxT("CIRCDDB:"); + this->versionInfo.Append(wxT(CIRCDDB_VERSION)); + + if (versionInfo.Len() > 0) + { + this->versionInfo.Append(wxT(" ")); + this->versionInfo.Append(versionInfo); + } + + + int hyphenPos = callsign.find(wxT('-')); + + if (hyphenPos == wxNOT_FOUND) + { + wxString n; + + n = callsign + wxT("-1"); + nicks.Add(n); + n = callsign + wxT("-2"); + nicks.Add(n); + n = callsign + wxT("-3"); + nicks.Add(n); + n = callsign + wxT("-4"); + nicks.Add(n); + } + else + { + nicks.Add(callsign); + } + + name = callsign; + + pingTimer = 60; // 30 seconds + state = 0; + timer = 0; + + + chooseNewNick(); +} + +IRCProtocol::~IRCProtocol() +{ +} + +void IRCProtocol::chooseNewNick() +{ + int r = rand() % nicks.GetCount(); + + currentNick = nicks[r]; +} + +void IRCProtocol::setNetworkReady( bool b ) +{ + if (b == true) + { + if (state != 0) + { + wxLogError(wxT("IRCProtocol::setNetworkReady: unexpected state")); + } + + state = 1; + chooseNewNick(); + } + else + { + state = 0; + } +} + + +bool IRCProtocol::processQueues ( IRCMessageQueue * recvQ, IRCMessageQueue * sendQ ) +{ + if (timer > 0) + { + timer --; + } + + while (recvQ->messageAvailable()) + { + IRCMessage * m = recvQ -> getMessage(); + +#if defined(DEBUG_IRC) + wxString d = wxT("R [") + m->prefix + wxT("] [") + m->command + wxT("]"); + for (int i=0; i < m->numParams; i++) + { + d.Append(wxT(" [") + m->params[i] + wxT("]") ); + } + d.Replace(wxT("%"), wxT("%%"), true); + d.Replace(wxT("\\"), wxT("\\\\"), true); + wxLogVerbose(d); +#endif + + if (m->command.IsSameAs(wxT("004"))) + { + if (state == 4) + { + if (m->params.GetCount() > 1) + { + wxRegEx serverNamePattern(wxT("^grp[1-9]s[1-9].ircDDB$")); + + if (serverNamePattern.Matches( m->params[1] )) + { + app->setBestServer(wxT("s-") + m->params[1].Mid(0,6)); + } + } + state = 5; // next: JOIN + app->setCurrentNick(currentNick); + } + } + else if (m->command.IsSameAs(wxT("PING"))) + { + IRCMessage * m2 = new IRCMessage(); + m2->command = wxT("PONG"); + if (m->params.GetCount() > 0) + { + m2->numParams = 1; + m2->params.Add( m->params[0] ); + } + sendQ -> putMessage(m2); + } + else if (m->command.IsSameAs(wxT("JOIN"))) + { + if ((m->numParams >= 1) && m->params[0].IsSameAs(channel)) + { + if (m->getPrefixNick().IsSameAs(currentNick) && (state == 6)) + { + if (debugChannel.Len() > 0) + { + state = 7; // next: join debug_channel + } + else + { + state = 10; // next: WHO * + } + } + else if (app != NULL) + { + app->userJoin( m->getPrefixNick(), m->getPrefixName(), m->getPrefixHost()); + } + } + + if ((m->numParams >= 1) && m->params[0].IsSameAs(debugChannel)) + { + if (m->getPrefixNick().IsSameAs(currentNick) && (state == 8)) + { + state = 10; // next: WHO * + } + } + } + else if (m->command.IsSameAs(wxT("PONG"))) + { + if (state == 12) + { + timer = pingTimer; + state = 11; + } + } + else if (m->command.IsSameAs(wxT("PART"))) + { + if ((m->numParams >= 1) && m->params[0].IsSameAs(channel)) + { + if (app != NULL) + { + app->userLeave( m->getPrefixNick() ); + } + } + } + else if (m->command.IsSameAs(wxT("KICK"))) + { + if ((m->numParams >= 2) && m->params[0].IsSameAs(channel)) + { + if (m->params[1].IsSameAs(currentNick)) + { + // i was kicked!! + delete m; + return false; + } + else if (app != NULL) + { + app->userLeave( m->params[1] ); + } + } + } + else if (m->command.IsSameAs(wxT("QUIT"))) + { + if (app != NULL) + { + app->userLeave( m->getPrefixNick() ); + } + } + else if (m->command.IsSameAs(wxT("MODE"))) + { + if ((m->numParams >= 3) && m->params[0].IsSameAs(channel)) + { + if (app != NULL) + { + size_t i; + wxString mode = m->params[1]; + + for (i = 1; (i < mode.Len()) && ((size_t) m->numParams >= (i+2)); i++) + { + if ( mode[i] == wxT('o') ) + { + if ( mode[0] == wxT('+') ) + { + app->userChanOp(m->params[i+1], true); + } + else if ( mode[0] == wxT('-') ) + { + app->userChanOp(m->params[i+1], false); + } + } + } // for + } + } + } + else if (m->command.IsSameAs(wxT("PRIVMSG"))) + { + if ((m->numParams == 2) && (app != NULL)) + { + if (m->params[0].IsSameAs(channel)) + { + app->msgChannel(m); + } + else if (m->params[0].IsSameAs(currentNick)) + { + app->msgQuery(m); + } + } + } + else if (m->command.IsSameAs(wxT("352"))) // WHO list + { + if ((m->numParams >= 7) && m->params[0].IsSameAs(currentNick) + && m->params[1].IsSameAs(channel)) + { + if (app != NULL) + { + app->userJoin( m->params[5], m->params[2], m->params[3]); + app->userChanOp ( m->params[5], m->params[6].IsSameAs(wxT("H@"))); + } + } + } + else if (m->command.IsSameAs(wxT("433"))) // nick collision + { + if (state == 2) + { + state = 3; // nick collision, choose new nick + timer = 10; // wait 5 seconds.. + } + } + else if (m->command.IsSameAs(wxT("332")) || + m->command.IsSameAs(wxT("TOPIC"))) // topic + { + if ((m->numParams == 2) && (app != NULL) && + m->params[0].IsSameAs(channel) ) + { + app->setTopic(m->params[1]); + } + } + + delete m; + } + + IRCMessage * m; + + switch (state) + { + case 1: + m = new IRCMessage(); + m->command = wxT("PASS"); + m->numParams = 1; + m->params.Add(password); + sendQ->putMessage(m); + + m = new IRCMessage(); + m->command = wxT("NICK"); + m->numParams = 1; + m->params.Add(currentNick); + sendQ->putMessage(m); + + timer = 10; // wait for possible nick collision message + state = 2; + break; + + case 2: + if (timer == 0) + { + m = new IRCMessage(); + m->command = wxT("USER"); + m->numParams = 4; + m->params.Add(name); + m->params.Add(wxT("0")); + m->params.Add(wxT("*")); + m->params.Add(versionInfo); + sendQ->putMessage(m); + + timer = 30; + state = 4; // wait for login message + } + break; + + case 3: + if (timer == 0) + { + chooseNewNick(); + m = new IRCMessage(); + m->command = wxT("NICK"); + m->numParams = 1; + m->params.Add(currentNick); + sendQ->putMessage(m); + + timer = 10; // wait for possible nick collision message + state = 2; + } + break; + + case 4: + if (timer == 0) + { + // no login message received -> disconnect + return false; + } + break; + + case 5: + m = new IRCMessage(); + m->command = wxT("JOIN"); + m->numParams = 1; + m->params.Add(channel); + sendQ->putMessage(m); + + timer = 30; + state = 6; // wait for join message + break; + + case 6: + if (timer == 0) + { + // no join message received -> disconnect + return false; + } + break; + + case 7: + if (debugChannel.Len() == 0) + { + return false; // this state cannot be processed if there is no debug_channel + } + + m = new IRCMessage(); + m->command = wxT("JOIN"); + m->numParams = 1; + m->params.Add(debugChannel); + sendQ->putMessage(m); + + timer = 30; + state = 8; // wait for join message + break; + + case 8: + if (timer == 0) + { + // no join message received -> disconnect + return false; + } + break; + + case 10: + m = new IRCMessage(); + m->command = wxT("WHO"); + m->numParams = 2; + m->params.Add(channel); + m->params.Add(wxT("*")); + sendQ->putMessage(m); + + timer = pingTimer; + state = 11; // wait for timer and then send ping + + if (app != NULL) + { + app->setSendQ(sendQ); // this switches the application on + } + break; + + case 11: + if (timer == 0) + { + m = new IRCMessage(); + m->command = wxT("PING"); + m->numParams = 1; + m->params.Add(currentNick); + sendQ->putMessage(m); + + timer = pingTimer; + state = 12; // wait for pong + } + break; + + case 12: + if (timer == 0) + { + // no pong message received -> disconnect + return false; + } + break; + } + + return true; +} + + diff --git a/ircDDB/IRCProtocol.h b/ircDDB/IRCProtocol.h new file mode 100644 index 0000000..cf144aa --- /dev/null +++ b/ircDDB/IRCProtocol.h @@ -0,0 +1,65 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010 Michael Dirska, DL1BFF (dl1bff@mdx.de) + +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, see . + +*/ + +#if !defined(_IRCPROTOCOL_H) +#define _IRCPROTOCOL_H + +#include + +#include "IRCMessageQueue.h" +#include "IRCApplication.h" + + +class IRCProtocol +{ + public: + IRCProtocol ( IRCApplication * app, + const wxString& callsign, const wxString& password, const wxString& channel, + const wxString& versionInfo ); + + ~IRCProtocol(); + + void setNetworkReady( bool state ); + + bool processQueues ( IRCMessageQueue * recvQ, IRCMessageQueue * sendQ ); + + private: + void chooseNewNick(); + + wxArrayString nicks; + wxString password; + wxString channel; + wxString name; + wxString currentNick; + wxString versionInfo; + + int state; + int timer; + int pingTimer; + + wxString debugChannel; + + IRCApplication * app; + +}; + + +#endif diff --git a/ircDDB/IRCReceiver.cpp b/ircDDB/IRCReceiver.cpp new file mode 100644 index 0000000..dc0df95 --- /dev/null +++ b/ircDDB/IRCReceiver.cpp @@ -0,0 +1,237 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010 Michael Dirska, DL1BFF (dl1bff@mdx.de) + +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, see . + +*/ + +#if defined(WIN32) + +#define WIN32_LEAN_AND_MEAN + +#include +#include + +#else + +#include +#include + +#endif + + + +#include "IRCReceiver.h" + +#include + +IRCReceiver::IRCReceiver(int sock, IRCMessageQueue * q ) + : wxThread(wxTHREAD_JOINABLE) +{ + this->sock = sock; + + recvQ = q; +} + +IRCReceiver::~IRCReceiver() +{ +} + +void IRCReceiver::startWork() +{ + terminateThread = false; + + Create(); + Run(); +} + +void IRCReceiver::stopWork() +{ + terminateThread = true; + + Wait(); +} + +static int doRead( int sock, char * buf, int buf_size ) +{ + struct timeval tv; + tv.tv_sec = 1; + tv.tv_usec = 0; + fd_set rdset; + fd_set errset; + + FD_ZERO(&rdset); + FD_ZERO(&errset); + FD_SET(sock, &rdset); + FD_SET(sock, &errset); + + int res; + + res = select(sock+1, &rdset, NULL, &errset, &tv); + + if ( res < 0 ) + { + wxLogSysError(wxT("IRCReceiver::doRead: select")); + return -1; + } + else if ( res > 0 ) + { + if (FD_ISSET(sock, &errset)) + { + wxLogVerbose(wxT("IRCReceiver::doRead: select (FD_ISSET(sock, exceptfds))")); + return -1; + } + + if (FD_ISSET(sock, &rdset)) + { + res = recv(sock, buf, buf_size, 0); + + if (res < 0) + { + wxLogSysError(wxT("IRCReceiver::doRead: read")); + return -1; + } + else if (res == 0) + { + wxLogVerbose(wxT("IRCReceiver::doRead: EOF read==0")); + return -1; + } + else + { + return res; + } + } + + } + + return 0; +} + +wxThread::ExitCode IRCReceiver::Entry () +{ + IRCMessage * m = new IRCMessage(); + + int i; + int state = 0; + + while (!terminateThread) + { + + // wxLogVerbose(wxT("IRCReceiver: tick")); + + char buf[200]; + int r = doRead( sock, buf, sizeof buf ); + + if (r < 0) + { + recvQ -> signalEOF(); + delete m; // delete unfinished IRCMessage + break; + } + + for (i=0; i < r; i++) + { + char b = buf[i]; + + if (b > 0) + { + if (b == 10) + { + recvQ -> putMessage(m); + m = new IRCMessage(); + state = 0; + } + else if (b == 13) + { + // do nothing + } + else switch (state) + { + case 0: + if (b == ':') + { + state = 1; // prefix + } + else if (b == 32) + { + // do nothing + } + else + { + m -> command.Append(wxChar(b)); + state = 2; // command + } + break; + + case 1: + if (b == 32) + { + state = 2; // command is next + } + else + { + m -> prefix.Append(wxChar(b)); + } + break; + + case 2: + if (b == 32) + { + state = 3; // params + m -> numParams = 1; + m -> params.Add(wxT("")); + } + else + { + m -> command.Append(wxChar(b)); + } + break; + + case 3: + if (b == 32) + { + m -> numParams ++; + if (m -> numParams >= 15) + { + state = 5; // ignore the rest + } + + m -> params.Add(wxT("")); + } + else if ((b == ':') && (m -> params[ m -> numParams-1 ].Len() == 0)) + { + state = 4; // rest of line is this param + } + else + { + m -> params[ m -> numParams-1 ].Append(wxChar(b)); + } + break; + + case 4: + m -> params[ m -> numParams-1 ].Append(wxChar(b)); + break; + + + } // switch + } // if + } // for + } // while + + return 0; +} + diff --git a/ircDDB/IRCReceiver.h b/ircDDB/IRCReceiver.h new file mode 100644 index 0000000..547a9b3 --- /dev/null +++ b/ircDDB/IRCReceiver.h @@ -0,0 +1,63 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010 Michael Dirska, DL1BFF (dl1bff@mdx.de) +Copyright (C) 2012 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, see . + +*/ + + + +#if !defined(_IRCRECEIVER_H) +#define _IRCRECEIVER_H + +#include + +#include "IRCMessageQueue.h" + + +class IRCReceiver : public wxThread +{ + public: + + IRCReceiver(int sock, IRCMessageQueue * q); + + virtual ~IRCReceiver(); + + + virtual void startWork(); + + virtual void stopWork(); + + + protected: + + virtual wxThread::ExitCode Entry(); + + + + private: + + + bool terminateThread; + int sock; + IRCMessageQueue * recvQ; + +}; + + +#endif diff --git a/ircDDB/IRCutils.cpp b/ircDDB/IRCutils.cpp new file mode 100644 index 0000000..fafda59 --- /dev/null +++ b/ircDDB/IRCutils.cpp @@ -0,0 +1,179 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de) + +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, see . + +*/ + + +#if defined(WIN32) + +#define WIN32_LEAN_AND_MEAN + +#include +#include + +#else + +#include +#include +#include + +#endif + + + +#include + +#include "IRCutils.h" + + +int getAllIPV4Addresses ( const char * name, unsigned short port, + unsigned int * num, struct sockaddr_in * addr, unsigned int max_addr ) +{ + + struct addrinfo hints; + struct addrinfo * res; + + memset(&hints, 0x00, sizeof(struct addrinfo)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + + int r = getaddrinfo( name, NULL, &hints, &res ); + + if (r == 0) + { + struct addrinfo * rp; + unsigned int numAddr = 0; + + for (rp = res; rp != NULL; rp = rp->ai_next) + { + if (rp->ai_family == AF_INET) + { + numAddr ++; + } + } + + if (numAddr > 0) + { + if (numAddr > max_addr) + { + numAddr = max_addr; + } + + int * shuffle = new int[numAddr]; + + unsigned int i; + + for (i=0; i < numAddr; i++) + { + shuffle[i] = i; + } + + for (i=0; i < (numAddr - 1); i++) + { + if (rand() & 1) + { + int tmp; + tmp = shuffle[i]; + shuffle[i] = shuffle[i+1]; + shuffle[i+1] = tmp; + } + } + + for (i=(numAddr - 1); i > 0; i--) + { + if (rand() & 1) + { + int tmp; + tmp = shuffle[i]; + shuffle[i] = shuffle[i-1]; + shuffle[i-1] = tmp; + } + } + + for (rp = res, i=0 ; (rp != NULL) && (i < numAddr); rp = rp->ai_next) + { + if (rp->ai_family == AF_INET) + { + memcpy( addr+shuffle[i], rp->ai_addr, sizeof (struct sockaddr_in) ); + + addr[shuffle[i]].sin_port = htons(port); + + i++; + } + } + + delete[] shuffle; + } + + *num = numAddr; + + freeaddrinfo(res); + + return 0; + + } + else + { + wxString e( gai_strerror(r), wxConvUTF8); + + wxLogWarning(wxT("getaddrinfo: ") + e ); + + return 1; + } + + +} + + + + + +void safeStringCopy (char * dest, const char * src, unsigned int buf_size) +{ + unsigned int i = 0; + + while (i < (buf_size - 1) && (src[i] != 0)) + { + dest[i] = src[i]; + i++; + } + + dest[i] = 0; +} + + +wxString getCurrentTime(void) +{ + time_t now = time(NULL); + struct tm* tm; + struct tm tm_buf; + char buffer[25]; + +#if defined(__WINDOWS__) + gmtime_s( &tm_buf, &now ); + tm = &tm_buf; +#else + tm = gmtime_r(&now, &tm_buf); +#endif + + strftime(buffer, sizeof buffer, "%Y-%m-%d %H:%M:%S", tm); + + return wxString(buffer, wxConvLocal); +} + diff --git a/ircDDB/IRCutils.h b/ircDDB/IRCutils.h new file mode 100644 index 0000000..f2e44a6 --- /dev/null +++ b/ircDDB/IRCutils.h @@ -0,0 +1,31 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010 Michael Dirska, DL1BFF (dl1bff@mdx.de) + +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, see . + +*/ + + + +int getAllIPV4Addresses ( const char * name, unsigned short port, + unsigned int * num, struct sockaddr_in * addr, unsigned int max_addr ); + + +void safeStringCopy (char * dest, const char * src, unsigned int buf_size); + +wxString getCurrentTime(void); + diff --git a/ircDDB/LICENSE b/ircDDB/LICENSE new file mode 100644 index 0000000..08ddefd --- /dev/null +++ b/ircDDB/LICENSE @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. + diff --git a/ircDDB/README b/ircDDB/README new file mode 100644 index 0000000..e69de29 diff --git a/ircDDB/ircDDB.vcxproj b/ircDDB/ircDDB.vcxproj new file mode 100644 index 0000000..75e4f54 --- /dev/null +++ b/ircDDB/ircDDB.vcxproj @@ -0,0 +1,171 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {276BC54D-5581-4A0C-AFD5-A5BDC947F0AD} + ircDDB + Win32Proj + 10.0.16299.0 + + + + StaticLibrary + v141 + Unicode + true + + + StaticLibrary + v141 + Unicode + true + + + StaticLibrary + v141 + Unicode + + + StaticLibrary + v141 + Unicode + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>14.0.24720.0 + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + $(WXWIN)\include;$(WXWIN)\lib\vc_dll\mswud;$(IncludePath) + + + $(WXWIN)\include;$(WXWIN)\lib\vc_dll\mswud;$(IncludePath) + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + + + + Disabled + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_LIB;DEXTRA_LINK;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + Level4 + EditAndContinue + + + + + Disabled + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_LIB;DEXTRA_LINK;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + + + Level4 + ProgramDatabase + + + + + MaxSpeed + true + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_LIB;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + Level3 + ProgramDatabase + + + + + MaxSpeed + true + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_LIB;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {e793cb8e-2ac9-431a-bbfc-3f52537bb3cf} + false + + + + + + + + + \ No newline at end of file diff --git a/ircDDB/ircDDB.vcxproj.filters b/ircDDB/ircDDB.vcxproj.filters new file mode 100644 index 0000000..49eb42e --- /dev/null +++ b/ircDDB/ircDDB.vcxproj.filters @@ -0,0 +1,83 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + + \ No newline at end of file diff --git a/ircDDBGateway.sln b/ircDDBGateway.sln new file mode 100644 index 0000000..b080f45 --- /dev/null +++ b/ircDDBGateway.sln @@ -0,0 +1,151 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27703.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ircDDBGateway", "ircDDBGateway\ircDDBGateway.vcxproj", "{6DDA3497-66A3-4856-BAD4-1DDCDBDFF959}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ircDDB", "ircDDB\ircDDB.vcxproj", "{276BC54D-5581-4A0C-AFD5-A5BDC947F0AD}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Common", "Common\Common.vcxproj", "{E793CB8E-2AC9-431A-BBFC-3F52537BB3CF}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StarNetServer", "StarNetServer\StarNetServer.vcxproj", "{4E9989C8-80D4-4F6E-BCA1-754DCED3AF5F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RemoteControl", "RemoteControl\RemoteControl.vcxproj", "{F7756875-1F58-4006-AD55-5C963AB682C0}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TimerControl", "TimerControl\TimerControl.vcxproj", "{68CFAB6D-AED3-4B8A-BCB3-EE4136CA00A3}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TimeServer", "TimeServer\TimeServer.vcxproj", "{99F336A5-6E33-4919-BF75-D6D8BA26345E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ircDDBGatewayConfig", "ircDDBGatewayConfig\ircDDBGatewayConfig.vcxproj", "{D0A32505-822B-4936-A61B-A5151966AEAA}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TextTransmit", "TextTransmit\TextTransmit.vcxproj", "{AFB9BCE5-0D37-4707-93A1-DA14E39F2773}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VoiceTransmit", "VoiceTransmit\VoiceTransmit.vcxproj", "{27D44C4F-6849-4E15-A5BB-0B54BA296D98}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GUICommon", "GUICommon\GUICommon.vcxproj", "{02D03515-0BBE-4553-8C40-566A597478F8}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "APRSTransmit", "APRSTransmit\APRSTransmit.vcxproj", "{F26EA1DB-74CF-4C52-A425-00235C8ABED2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "APRSTransmitD", "APRSTransmit\APRSTransmitD.vcxproj", "{C706EF5D-3917-4796-8BEB-823498A1B13C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6DDA3497-66A3-4856-BAD4-1DDCDBDFF959}.Debug|Win32.ActiveCfg = Debug|Win32 + {6DDA3497-66A3-4856-BAD4-1DDCDBDFF959}.Debug|Win32.Build.0 = Debug|Win32 + {6DDA3497-66A3-4856-BAD4-1DDCDBDFF959}.Debug|x64.ActiveCfg = Debug|x64 + {6DDA3497-66A3-4856-BAD4-1DDCDBDFF959}.Debug|x64.Build.0 = Debug|x64 + {6DDA3497-66A3-4856-BAD4-1DDCDBDFF959}.Release|Win32.ActiveCfg = Release|Win32 + {6DDA3497-66A3-4856-BAD4-1DDCDBDFF959}.Release|Win32.Build.0 = Release|Win32 + {6DDA3497-66A3-4856-BAD4-1DDCDBDFF959}.Release|x64.ActiveCfg = Release|x64 + {6DDA3497-66A3-4856-BAD4-1DDCDBDFF959}.Release|x64.Build.0 = Release|x64 + {276BC54D-5581-4A0C-AFD5-A5BDC947F0AD}.Debug|Win32.ActiveCfg = Debug|Win32 + {276BC54D-5581-4A0C-AFD5-A5BDC947F0AD}.Debug|Win32.Build.0 = Debug|Win32 + {276BC54D-5581-4A0C-AFD5-A5BDC947F0AD}.Debug|x64.ActiveCfg = Debug|x64 + {276BC54D-5581-4A0C-AFD5-A5BDC947F0AD}.Debug|x64.Build.0 = Debug|x64 + {276BC54D-5581-4A0C-AFD5-A5BDC947F0AD}.Release|Win32.ActiveCfg = Release|Win32 + {276BC54D-5581-4A0C-AFD5-A5BDC947F0AD}.Release|Win32.Build.0 = Release|Win32 + {276BC54D-5581-4A0C-AFD5-A5BDC947F0AD}.Release|x64.ActiveCfg = Release|x64 + {276BC54D-5581-4A0C-AFD5-A5BDC947F0AD}.Release|x64.Build.0 = Release|x64 + {E793CB8E-2AC9-431A-BBFC-3F52537BB3CF}.Debug|Win32.ActiveCfg = Debug|Win32 + {E793CB8E-2AC9-431A-BBFC-3F52537BB3CF}.Debug|Win32.Build.0 = Debug|Win32 + {E793CB8E-2AC9-431A-BBFC-3F52537BB3CF}.Debug|x64.ActiveCfg = Debug|x64 + {E793CB8E-2AC9-431A-BBFC-3F52537BB3CF}.Debug|x64.Build.0 = Debug|x64 + {E793CB8E-2AC9-431A-BBFC-3F52537BB3CF}.Release|Win32.ActiveCfg = Release|Win32 + {E793CB8E-2AC9-431A-BBFC-3F52537BB3CF}.Release|Win32.Build.0 = Release|Win32 + {E793CB8E-2AC9-431A-BBFC-3F52537BB3CF}.Release|x64.ActiveCfg = Release|x64 + {E793CB8E-2AC9-431A-BBFC-3F52537BB3CF}.Release|x64.Build.0 = Release|x64 + {4E9989C8-80D4-4F6E-BCA1-754DCED3AF5F}.Debug|Win32.ActiveCfg = Debug|Win32 + {4E9989C8-80D4-4F6E-BCA1-754DCED3AF5F}.Debug|Win32.Build.0 = Debug|Win32 + {4E9989C8-80D4-4F6E-BCA1-754DCED3AF5F}.Debug|x64.ActiveCfg = Debug|x64 + {4E9989C8-80D4-4F6E-BCA1-754DCED3AF5F}.Debug|x64.Build.0 = Debug|x64 + {4E9989C8-80D4-4F6E-BCA1-754DCED3AF5F}.Release|Win32.ActiveCfg = Release|Win32 + {4E9989C8-80D4-4F6E-BCA1-754DCED3AF5F}.Release|Win32.Build.0 = Release|Win32 + {4E9989C8-80D4-4F6E-BCA1-754DCED3AF5F}.Release|x64.ActiveCfg = Release|x64 + {4E9989C8-80D4-4F6E-BCA1-754DCED3AF5F}.Release|x64.Build.0 = Release|x64 + {F7756875-1F58-4006-AD55-5C963AB682C0}.Debug|Win32.ActiveCfg = Debug|Win32 + {F7756875-1F58-4006-AD55-5C963AB682C0}.Debug|Win32.Build.0 = Debug|Win32 + {F7756875-1F58-4006-AD55-5C963AB682C0}.Debug|x64.ActiveCfg = Debug|x64 + {F7756875-1F58-4006-AD55-5C963AB682C0}.Debug|x64.Build.0 = Debug|x64 + {F7756875-1F58-4006-AD55-5C963AB682C0}.Release|Win32.ActiveCfg = Release|Win32 + {F7756875-1F58-4006-AD55-5C963AB682C0}.Release|Win32.Build.0 = Release|Win32 + {F7756875-1F58-4006-AD55-5C963AB682C0}.Release|x64.ActiveCfg = Release|x64 + {F7756875-1F58-4006-AD55-5C963AB682C0}.Release|x64.Build.0 = Release|x64 + {68CFAB6D-AED3-4B8A-BCB3-EE4136CA00A3}.Debug|Win32.ActiveCfg = Debug|Win32 + {68CFAB6D-AED3-4B8A-BCB3-EE4136CA00A3}.Debug|Win32.Build.0 = Debug|Win32 + {68CFAB6D-AED3-4B8A-BCB3-EE4136CA00A3}.Debug|x64.ActiveCfg = Debug|x64 + {68CFAB6D-AED3-4B8A-BCB3-EE4136CA00A3}.Debug|x64.Build.0 = Debug|x64 + {68CFAB6D-AED3-4B8A-BCB3-EE4136CA00A3}.Release|Win32.ActiveCfg = Release|Win32 + {68CFAB6D-AED3-4B8A-BCB3-EE4136CA00A3}.Release|Win32.Build.0 = Release|Win32 + {68CFAB6D-AED3-4B8A-BCB3-EE4136CA00A3}.Release|x64.ActiveCfg = Release|x64 + {68CFAB6D-AED3-4B8A-BCB3-EE4136CA00A3}.Release|x64.Build.0 = Release|x64 + {99F336A5-6E33-4919-BF75-D6D8BA26345E}.Debug|Win32.ActiveCfg = Debug|Win32 + {99F336A5-6E33-4919-BF75-D6D8BA26345E}.Debug|Win32.Build.0 = Debug|Win32 + {99F336A5-6E33-4919-BF75-D6D8BA26345E}.Debug|x64.ActiveCfg = Debug|x64 + {99F336A5-6E33-4919-BF75-D6D8BA26345E}.Debug|x64.Build.0 = Debug|x64 + {99F336A5-6E33-4919-BF75-D6D8BA26345E}.Release|Win32.ActiveCfg = Release|Win32 + {99F336A5-6E33-4919-BF75-D6D8BA26345E}.Release|Win32.Build.0 = Release|Win32 + {99F336A5-6E33-4919-BF75-D6D8BA26345E}.Release|x64.ActiveCfg = Release|x64 + {99F336A5-6E33-4919-BF75-D6D8BA26345E}.Release|x64.Build.0 = Release|x64 + {D0A32505-822B-4936-A61B-A5151966AEAA}.Debug|Win32.ActiveCfg = Debug|Win32 + {D0A32505-822B-4936-A61B-A5151966AEAA}.Debug|Win32.Build.0 = Debug|Win32 + {D0A32505-822B-4936-A61B-A5151966AEAA}.Debug|x64.ActiveCfg = Debug|x64 + {D0A32505-822B-4936-A61B-A5151966AEAA}.Debug|x64.Build.0 = Debug|x64 + {D0A32505-822B-4936-A61B-A5151966AEAA}.Release|Win32.ActiveCfg = Release|Win32 + {D0A32505-822B-4936-A61B-A5151966AEAA}.Release|Win32.Build.0 = Release|Win32 + {D0A32505-822B-4936-A61B-A5151966AEAA}.Release|x64.ActiveCfg = Release|x64 + {D0A32505-822B-4936-A61B-A5151966AEAA}.Release|x64.Build.0 = Release|x64 + {AFB9BCE5-0D37-4707-93A1-DA14E39F2773}.Debug|Win32.ActiveCfg = Debug|Win32 + {AFB9BCE5-0D37-4707-93A1-DA14E39F2773}.Debug|Win32.Build.0 = Debug|Win32 + {AFB9BCE5-0D37-4707-93A1-DA14E39F2773}.Debug|x64.ActiveCfg = Debug|x64 + {AFB9BCE5-0D37-4707-93A1-DA14E39F2773}.Debug|x64.Build.0 = Debug|x64 + {AFB9BCE5-0D37-4707-93A1-DA14E39F2773}.Release|Win32.ActiveCfg = Release|Win32 + {AFB9BCE5-0D37-4707-93A1-DA14E39F2773}.Release|Win32.Build.0 = Release|Win32 + {AFB9BCE5-0D37-4707-93A1-DA14E39F2773}.Release|x64.ActiveCfg = Release|x64 + {AFB9BCE5-0D37-4707-93A1-DA14E39F2773}.Release|x64.Build.0 = Release|x64 + {27D44C4F-6849-4E15-A5BB-0B54BA296D98}.Debug|Win32.ActiveCfg = Debug|Win32 + {27D44C4F-6849-4E15-A5BB-0B54BA296D98}.Debug|Win32.Build.0 = Debug|Win32 + {27D44C4F-6849-4E15-A5BB-0B54BA296D98}.Debug|x64.ActiveCfg = Debug|x64 + {27D44C4F-6849-4E15-A5BB-0B54BA296D98}.Debug|x64.Build.0 = Debug|x64 + {27D44C4F-6849-4E15-A5BB-0B54BA296D98}.Release|Win32.ActiveCfg = Release|Win32 + {27D44C4F-6849-4E15-A5BB-0B54BA296D98}.Release|Win32.Build.0 = Release|Win32 + {27D44C4F-6849-4E15-A5BB-0B54BA296D98}.Release|x64.ActiveCfg = Release|x64 + {27D44C4F-6849-4E15-A5BB-0B54BA296D98}.Release|x64.Build.0 = Release|x64 + {02D03515-0BBE-4553-8C40-566A597478F8}.Debug|Win32.ActiveCfg = Debug|Win32 + {02D03515-0BBE-4553-8C40-566A597478F8}.Debug|Win32.Build.0 = Debug|Win32 + {02D03515-0BBE-4553-8C40-566A597478F8}.Debug|x64.ActiveCfg = Debug|x64 + {02D03515-0BBE-4553-8C40-566A597478F8}.Debug|x64.Build.0 = Debug|x64 + {02D03515-0BBE-4553-8C40-566A597478F8}.Release|Win32.ActiveCfg = Release|Win32 + {02D03515-0BBE-4553-8C40-566A597478F8}.Release|Win32.Build.0 = Release|Win32 + {02D03515-0BBE-4553-8C40-566A597478F8}.Release|x64.ActiveCfg = Release|x64 + {02D03515-0BBE-4553-8C40-566A597478F8}.Release|x64.Build.0 = Release|x64 + {F26EA1DB-74CF-4C52-A425-00235C8ABED2}.Debug|Win32.ActiveCfg = Debug|Win32 + {F26EA1DB-74CF-4C52-A425-00235C8ABED2}.Debug|Win32.Build.0 = Debug|Win32 + {F26EA1DB-74CF-4C52-A425-00235C8ABED2}.Debug|x64.ActiveCfg = Debug|x64 + {F26EA1DB-74CF-4C52-A425-00235C8ABED2}.Debug|x64.Build.0 = Debug|x64 + {F26EA1DB-74CF-4C52-A425-00235C8ABED2}.Release|Win32.ActiveCfg = Release|Win32 + {F26EA1DB-74CF-4C52-A425-00235C8ABED2}.Release|Win32.Build.0 = Release|Win32 + {F26EA1DB-74CF-4C52-A425-00235C8ABED2}.Release|x64.ActiveCfg = Release|x64 + {F26EA1DB-74CF-4C52-A425-00235C8ABED2}.Release|x64.Build.0 = Release|x64 + {C706EF5D-3917-4796-8BEB-823498A1B13C}.Debug|Win32.ActiveCfg = Debug|Win32 + {C706EF5D-3917-4796-8BEB-823498A1B13C}.Debug|Win32.Build.0 = Debug|Win32 + {C706EF5D-3917-4796-8BEB-823498A1B13C}.Debug|x64.ActiveCfg = Debug|x64 + {C706EF5D-3917-4796-8BEB-823498A1B13C}.Debug|x64.Build.0 = Debug|x64 + {C706EF5D-3917-4796-8BEB-823498A1B13C}.Release|Win32.ActiveCfg = Release|Win32 + {C706EF5D-3917-4796-8BEB-823498A1B13C}.Release|Win32.Build.0 = Release|Win32 + {C706EF5D-3917-4796-8BEB-823498A1B13C}.Release|x64.ActiveCfg = Release|x64 + {C706EF5D-3917-4796-8BEB-823498A1B13C}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A992CB62-4EA0-4731-B9A4-6CC00564AB56} + EndGlobalSection +EndGlobal diff --git a/ircDDBGateway/IRCDDBGatewayApp.cpp b/ircDDBGateway/IRCDDBGatewayApp.cpp new file mode 100644 index 0000000..e6107aa --- /dev/null +++ b/ircDDBGateway/IRCDDBGatewayApp.cpp @@ -0,0 +1,923 @@ +/* + * 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 + * 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 "DummyRepeaterProtocolHandler.h" +#include "IcomRepeaterProtocolHandler.h" +#include "HBRepeaterProtocolHandler.h" +#include "IRCDDBGatewayLogRedirect.h" +#include "IRCDDBGatewayThread.h" +#include "IRCDDBGatewayDefs.h" +#include "IRCDDBGatewayApp.h" +#include "CallsignList.h" +#include "APRSWriter.h" +#include "Version.h" +#include "Logger.h" +#include "IRCDDBClient.h" +#include "IRCDDBMultiClient.h" +#include "Utils.h" +#include "XLXHostsFileDownloader.h" + +#include +#include +#include + +IMPLEMENT_APP(CIRCDDBGatewayApp) + +const wxChar* NAME_PARAM = wxT("Gateway Name"); +const wxChar* NOLOGGING_SWITCH = wxT("nolog"); +const wxChar* GUI_SWITCH = wxT("gui"); +const wxChar* LOGDIR_OPTION = wxT("logdir"); +const wxChar* CONFDIR_OPTION = wxT("confdir"); + +const wxString LOG_BASE_NAME = wxT("ircddbgateway"); + +CIRCDDBGatewayApp::CIRCDDBGatewayApp() : +wxApp(), +m_name(), +m_nolog(false), +m_gui(false), +m_logDir(), +m_confDir(), +m_frame(NULL), +m_thread(NULL), +m_config(NULL), +m_checker(NULL), +m_logChain(NULL) +{ +} + +CIRCDDBGatewayApp::~CIRCDDBGatewayApp() +{ +} + +bool CIRCDDBGatewayApp::OnInit() +{ + SetVendorName(VENDOR_NAME); + + if (!wxApp::OnInit()) + return false; + + if (!m_nolog) { + wxString logBaseName = LOG_BASE_NAME; + if (!m_name.IsEmpty()) { + logBaseName.Append(wxT("_")); + logBaseName.Append(m_name); + } + +#if defined(__WINDOWS__) + if (m_logDir.IsEmpty()) + m_logDir = ::wxGetHomeDir(); +#else + if (m_logDir.IsEmpty()) + m_logDir = wxT(LOG_DIR); +#endif + + wxLog* log = new CLogger(m_logDir, logBaseName); + wxLog::SetActiveTarget(log); + } else { + new wxLogNull; + } + + m_logChain = new wxLogChain(new CIRCDDBGatewayLogRedirect); + + wxString appName; + if (!m_name.IsEmpty()) + appName = APPLICATION_NAME + wxT(" ") + m_name; + else + appName = APPLICATION_NAME; + +#if !defined(__WINDOWS__) + appName.Replace(wxT(" "), wxT("_")); + m_checker = new wxSingleInstanceChecker(appName, wxT("/tmp")); +#else + m_checker = new wxSingleInstanceChecker(appName); +#endif + + bool ret = m_checker->IsAnotherRunning(); + if (ret) { + wxLogError(wxT("Another copy of the ircDDB Gateway is running, exiting")); + return false; + } + +#if defined(__WINDOWS__) + if (m_confDir.IsEmpty()) + m_confDir = ::wxGetHomeDir(); + + m_config = new CIRCDDBGatewayConfig(new wxConfig(APPLICATION_NAME), m_confDir, CONFIG_FILE_NAME, m_name); +#else + if (m_confDir.IsEmpty()) + m_confDir = wxT(CONF_DIR); + + m_config = new CIRCDDBGatewayConfig(m_confDir, CONFIG_FILE_NAME, m_name); +#endif + + wxString frameName = APPLICATION_NAME + wxT(" - "); + if (!m_name.IsEmpty()) { + frameName.Append(m_name); + frameName.Append(wxT(" - ")); + } + frameName.Append(VERSION); + + wxPoint position = wxDefaultPosition; + + int x, y; + m_config->getPosition(x, y); + if (x >= 0 && y >= 0) + position = wxPoint(x, y); + + m_frame = new CIRCDDBGatewayFrame(frameName, position, m_gui); + m_frame->Show(); + + SetTopWindow(m_frame); + + wxLogInfo(wxT("Starting ") + APPLICATION_NAME + wxT(" - ") + VERSION); + + // Log the version of wxWidgets and the Operating System + wxLogInfo(wxT("Using wxWidgets %d.%d.%d on %s"), wxMAJOR_VERSION, wxMINOR_VERSION, wxRELEASE_NUMBER, ::wxGetOsDescription().c_str()); + + createThread(); + + return true; +} + +int CIRCDDBGatewayApp::OnExit() +{ + m_logChain->SetLog(NULL); + + wxLogInfo(APPLICATION_NAME + wxT(" is exiting")); + + //m_thread->kill(); + wxGetApp().GetTopWindow()->Close(); + + delete m_config; + + delete m_checker; + + return 0; +} + +void CIRCDDBGatewayApp::OnInitCmdLine(wxCmdLineParser& parser) +{ + parser.AddSwitch(NOLOGGING_SWITCH, wxEmptyString, wxEmptyString, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddSwitch(GUI_SWITCH, wxEmptyString, wxEmptyString, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption(LOGDIR_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption(CONFDIR_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddParam(NAME_PARAM, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + + wxApp::OnInitCmdLine(parser); +} + +bool CIRCDDBGatewayApp::OnCmdLineParsed(wxCmdLineParser& parser) +{ + if (!wxApp::OnCmdLineParsed(parser)) + return false; + + m_nolog = parser.Found(NOLOGGING_SWITCH); + m_gui = parser.Found(GUI_SWITCH); + + wxString logDir; + bool found = parser.Found(LOGDIR_OPTION, &logDir); + if (found) + m_logDir = logDir; + + wxString confDir; + found = parser.Found(CONFDIR_OPTION, &confDir); + if (found) + m_confDir = confDir; + + if (parser.GetParamCount() > 0U) + m_name = parser.GetParam(0U); + + return true; +} + +#if defined(__WXDEBUG__) +void CIRCDDBGatewayApp::OnAssertFailure(const wxChar* file, int line, const wxChar* func, const wxChar* cond, const wxChar* msg) +{ + wxLogFatalError(wxT("Assertion failed on line %d in file %s and function %s: %s %s"), line, file, func, cond, msg); +} +#endif + +void CIRCDDBGatewayApp::showLog(const wxString& text) +{ + m_frame->showLog(text); +} + +CIRCDDBGatewayStatusData* CIRCDDBGatewayApp::getStatus() const +{ + return m_thread->getStatus(); +} + +void CIRCDDBGatewayApp::setPosition(int x, int y) +{ + m_config->setPosition(x, y); + m_config->write(); +} + +void CIRCDDBGatewayApp::createThread() +{ + wxASSERT(m_config != NULL); + + CIRCDDBGatewayThread* thread = new CIRCDDBGatewayThread(m_logDir, m_name); + + GATEWAY_TYPE gatewayType; + wxString gatewayCallsign, gatewayAddress, icomAddress, hbAddress, description1, description2, url; + unsigned int icomPort, hbPort; + double latitude, longitude; + m_config->getGateway(gatewayType, gatewayCallsign, gatewayAddress, icomAddress, icomPort, hbAddress, hbPort, latitude, longitude, description1, description2, url); + + gatewayCallsign.MakeUpper(); + gatewayCallsign.Append(wxT(" ")); + gatewayCallsign.Truncate(LONG_CALLSIGN_LENGTH - 1U); + + wxString callsign = gatewayCallsign; + callsign.Append(wxT(" ")); + + gatewayCallsign.Append(wxT("G")); + + wxLogInfo(wxT("Gateway type: %d, callsign: \"%s\", address: %s, Icom address: %s:%u, homebrew address: %s:%u, latitude: %lf, longitude: %lf, description: \"%s %s\", URL: \"%s\""), int(gatewayType), gatewayCallsign.c_str(), gatewayAddress.c_str(), icomAddress.c_str(), icomPort, hbAddress.c_str(), hbPort, latitude, longitude, description1.c_str(), description2.c_str(), url.c_str()); + + thread->setGateway(gatewayType, gatewayCallsign, gatewayAddress); + + wxString aprsHostname; + unsigned int aprsPort; + bool aprsEnabled; + m_config->getDPRS(aprsEnabled, aprsHostname, aprsPort); + wxLogInfo(wxT("APRS enabled: %d, host: %s:%u"), int(aprsEnabled), aprsHostname.c_str(), aprsPort); + + CAPRSWriter* aprs = NULL; + if (aprsEnabled && !gatewayCallsign.IsEmpty() && !aprsHostname.IsEmpty() && aprsPort != 0U) { + aprs = new CAPRSWriter(aprsHostname, aprsPort, gatewayCallsign, gatewayAddress); + + bool res = aprs->open(); + if (!res) + wxLogError(wxT("Cannot initialise the APRS data writer")); + else + thread->setAPRSWriter(aprs); + } + + TEXT_LANG language; + bool infoEnabled, echoEnabled, logEnabled, dratsEnabled, dtmfEnabled; + m_config->getMiscellaneous(language, infoEnabled, echoEnabled, logEnabled, dratsEnabled, dtmfEnabled); + wxLogInfo(wxT("Language: %d, info enabled: %d, echo enabled: %d, log enabled : %d, D-RATS enabled: %d, DTMF control enabled: %d"), int(language), int(infoEnabled), int(echoEnabled), int(logEnabled), int(dratsEnabled), int(dtmfEnabled)); + + CIcomRepeaterProtocolHandler* icomRepeaterHandler = NULL; + CHBRepeaterProtocolHandler* hbRepeaterHandler = NULL; + CDummyRepeaterProtocolHandler* dummyRepeaterHandler = NULL; + + unsigned int icomCount = 0U; + + wxString repeaterCall1, repeaterBand1, repeaterAddress1, reflector1, description11, description12, url1; + double frequency1, offset1, range1, latitude1, longitude1, agl1; + unsigned char band11, band12, band13; + unsigned int repeaterPort1; + HW_TYPE repeaterType1; + bool atStartup1; + RECONNECT reconnect1; + m_config->getRepeater1(repeaterCall1, repeaterBand1, repeaterType1, repeaterAddress1, repeaterPort1, band11, band12, band13, reflector1, atStartup1, reconnect1, frequency1, offset1, range1, latitude1, longitude1, agl1, description11, description12, url1); + + CUtils::clean(description11, wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,&*()-+=@/?:;")); + CUtils::clean(description12, wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,&*()-+=@/?:;")); + CUtils::clean(url1, wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,&*()-+=@/?:;")); + + wxString callsign1 = callsign; + if (!repeaterBand1.IsSameAs(wxT(" "))) { + if (!repeaterCall1.IsEmpty()) { + callsign1 = repeaterCall1; + callsign1.Append(wxT(" ")); + callsign1.Truncate(LONG_CALLSIGN_LENGTH); + } + + wxLogInfo(wxT("Repeater 1 callsign: \"%.7s%s\", hardware type: %d, address: %s:%u"), callsign1.c_str(), repeaterBand1.c_str(), int(repeaterType1), repeaterAddress1.c_str(), repeaterPort1); + wxLogInfo(wxT("Repeater 1 reflector: %s, at startup: %d, reconnect: %d"), reflector1.c_str(), atStartup1, reconnect1); + wxLogInfo(wxT("Repeater 1 latitude: %lf, longitude: %lf, range: %.0lf kms, height: %.0lf m, frequency: %.4lf MHz, offset: %.4lf MHz"), latitude1, longitude1, range1, agl1, frequency1, offset1); + wxLogInfo(wxT("Repeater 1 description: \"%s %s\", URL: \"%s\""), description11.c_str(), description12.c_str(), url1.c_str()); + + if (repeaterType1 == HW_ICOM && !icomAddress.IsEmpty()) { + icomRepeaterHandler = new CIcomRepeaterProtocolHandler(icomAddress, icomPort, repeaterAddress1, repeaterPort1); + bool res = icomRepeaterHandler->open(); + if (!res) { + wxLogError(wxT("Cannot open the Icom repeater protocol handler")); + delete icomRepeaterHandler; + icomRepeaterHandler = NULL; + } + } else if (repeaterType1 == HW_HOMEBREW && !hbAddress.IsEmpty()) { + hbRepeaterHandler = new CHBRepeaterProtocolHandler(hbAddress, hbPort); + bool res = hbRepeaterHandler->open(); + if (!res) { + wxLogError(wxT("Cannot open the Homebrew repeater protocol handler")); + delete hbRepeaterHandler; + hbRepeaterHandler = NULL; + } + } else if (repeaterType1 == HW_DUMMY && !hbAddress.IsEmpty()) { + dummyRepeaterHandler = new CDummyRepeaterProtocolHandler; + bool res = dummyRepeaterHandler->open(); + if (!res) { + wxLogError(wxT("Cannot open the Dummy repeater protocol handler")); + delete dummyRepeaterHandler; + dummyRepeaterHandler = NULL; + } + } + + if (latitude1 == 0.0 && longitude1 == 0.0) { + latitude1 = latitude; + longitude1 = longitude; + } + + if (description11.IsEmpty()) + description11 = description1; + if (description12.IsEmpty()) + description12 = description2; + + if (url1.IsEmpty()) + url1 = url; + + if (repeaterType1 == HW_ICOM && icomRepeaterHandler != NULL) { + wxLogInfo(wxT("Repeater 1 bands: %u %u %u"), band11, band12, band13); + thread->addRepeater(callsign1, repeaterBand1, repeaterAddress1, repeaterPort1, repeaterType1, reflector1, atStartup1, reconnect1, dratsEnabled, frequency1, offset1, range1, latitude1, longitude1, agl1, description11, description12, url1, icomRepeaterHandler, band11, band12, band13); + + if (aprs != NULL) + aprs->setPort(callsign1, repeaterBand1, frequency1, offset1, range1, latitude1, longitude1, agl1); + + icomCount++; + } else if (repeaterType1 == HW_HOMEBREW && hbRepeaterHandler != NULL) { + thread->addRepeater(callsign1, repeaterBand1, repeaterAddress1, repeaterPort1, repeaterType1, reflector1, atStartup1, reconnect1, dratsEnabled, frequency1, offset1, range1, latitude1, longitude1, agl1, description11, description12, url1, hbRepeaterHandler); + + if (aprs != NULL) + aprs->setPort(callsign1, repeaterBand1, frequency1, offset1, range1, latitude1, longitude1, agl1); + } else if (repeaterType1 == HW_DUMMY && dummyRepeaterHandler != NULL) { + thread->addRepeater(callsign1, repeaterBand1, repeaterAddress1, repeaterPort1, repeaterType1, reflector1, atStartup1, reconnect1, dratsEnabled, frequency1, offset1, range1, latitude1, longitude1, agl1, description11, description12, url1, dummyRepeaterHandler); + } + } + + wxString repeaterCall2, repeaterBand2, repeaterAddress2, reflector2, description21, description22, url2; + double frequency2, offset2, range2, latitude2, longitude2, agl2; + unsigned char band21, band22, band23; + unsigned int repeaterPort2; + HW_TYPE repeaterType2; + bool atStartup2; + RECONNECT reconnect2; + m_config->getRepeater2(repeaterCall2, repeaterBand2, repeaterType2, repeaterAddress2, repeaterPort2, band21, band22, band23, reflector2, atStartup2, reconnect2, frequency2, offset2, range2, latitude2, longitude2, agl2, description21, description22, url2); + + CUtils::clean(description21, wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,&*()-+=@/?:;")); + CUtils::clean(description22, wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,&*()-+=@/?:;")); + CUtils::clean(url2, wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,&*()-+=@/?:;")); + + wxString callsign2 = callsign; + if (!repeaterBand2.IsSameAs(wxT(" "))) { + if (!repeaterCall2.IsEmpty()) { + callsign2 = repeaterCall2; + callsign2.Append(wxT(" ")); + callsign2.Truncate(LONG_CALLSIGN_LENGTH); + } + + wxLogInfo(wxT("Repeater 2 callsign: \"%.7s%s\", hardware type: %d, address: %s:%u"), callsign2.c_str(), repeaterBand2.c_str(), int(repeaterType2), repeaterAddress2.c_str(), repeaterPort2); + wxLogInfo(wxT("Repeater 2 reflector: %s, at startup: %d, reconnect: %d"), reflector2.c_str(), atStartup2, reconnect2); + wxLogInfo(wxT("Repeater 2 latitude: %lf, longitude: %lf, range: %.0lf kms, height: %.0lf m, frequency: %.4lf MHz, offset: %.4lf MHz"), latitude2, longitude2, range2, agl2, frequency2, offset2); + wxLogInfo(wxT("Repeater 2 description: \"%s %s\", URL: \"%s\""), description21.c_str(), description22.c_str(), url2.c_str()); + + if (callsign1.IsSameAs(callsign2) && repeaterBand1.IsSameAs(repeaterBand2)) { + wxLogError(wxT("Repeater 2 has the same callsign and module as repeater 1, exiting")); + return; + } + + if (repeaterType2 == HW_ICOM && !icomAddress.IsEmpty() && icomRepeaterHandler == NULL) { + icomRepeaterHandler = new CIcomRepeaterProtocolHandler(icomAddress, icomPort, repeaterAddress2, repeaterPort2); + bool res = icomRepeaterHandler->open(); + if (!res) { + wxLogError(wxT("Cannot open the Icom repeater protocol handler")); + delete icomRepeaterHandler; + icomRepeaterHandler = NULL; + } + } else if (repeaterType2 == HW_HOMEBREW && !hbAddress.IsEmpty() && hbRepeaterHandler == NULL) { + hbRepeaterHandler = new CHBRepeaterProtocolHandler(hbAddress, hbPort); + bool res = hbRepeaterHandler->open(); + if (!res) { + wxLogError(wxT("Cannot open the Homebrew repeater protocol handler")); + delete hbRepeaterHandler; + hbRepeaterHandler = NULL; + } + } else if (repeaterType2 == HW_DUMMY && !hbAddress.IsEmpty() && dummyRepeaterHandler == NULL) { + dummyRepeaterHandler = new CDummyRepeaterProtocolHandler; + bool res = dummyRepeaterHandler->open(); + if (!res) { + wxLogError(wxT("Cannot open the Dummy repeater protocol handler")); + delete dummyRepeaterHandler; + dummyRepeaterHandler = NULL; + } + } + + if (latitude2 == 0.0 && longitude2 == 0.0) { + latitude2 = latitude; + longitude2 = longitude; + } + + if (description21.IsEmpty()) + description21 = description1; + if (description22.IsEmpty()) + description22 = description2; + + if (url2.IsEmpty()) + url2 = url; + + if (repeaterType2 == HW_ICOM && icomRepeaterHandler != NULL) { + wxLogInfo(wxT("Repeater 2 bands: %u %u %u"), band21, band22, band23); + thread->addRepeater(callsign2, repeaterBand2, repeaterAddress2, repeaterPort2, repeaterType2, reflector2, atStartup2, reconnect2, dratsEnabled, frequency2, offset2, range2, latitude2, longitude2, agl2, description21, description22, url2, icomRepeaterHandler, band21, band22, band23); + + if (aprs != NULL) + aprs->setPort(callsign2, repeaterBand2, frequency2, offset2, range2, latitude2, longitude2, agl2); + + icomCount++; + } else if (repeaterType2 == HW_HOMEBREW && hbRepeaterHandler != NULL) { + thread->addRepeater(callsign2, repeaterBand2, repeaterAddress2, repeaterPort2, repeaterType2, reflector2, atStartup2, reconnect2, dratsEnabled, frequency2, offset2, range2, latitude2, longitude2, agl2, description21, description22, url2, hbRepeaterHandler); + + if (aprs != NULL) + aprs->setPort(callsign2, repeaterBand2, frequency2, offset2, range2, latitude2, longitude2, agl2); + } else if (repeaterType2 == HW_DUMMY && dummyRepeaterHandler != NULL) { + thread->addRepeater(callsign2, repeaterBand2, repeaterAddress2, repeaterPort2, repeaterType2, reflector2, atStartup2, reconnect2, dratsEnabled, frequency2, offset2, range2, latitude2, longitude2, agl2, description21, description22, url2, dummyRepeaterHandler); + } + } + + wxString repeaterCall3, repeaterBand3, repeaterAddress3, reflector3, description31, description32, url3; + double frequency3, offset3, range3, latitude3, longitude3, agl3; + unsigned char band31, band32, band33; + unsigned int repeaterPort3; + HW_TYPE repeaterType3; + bool atStartup3; + RECONNECT reconnect3; + m_config->getRepeater3(repeaterCall3, repeaterBand3, repeaterType3, repeaterAddress3, repeaterPort3, band31, band32, band33, reflector3, atStartup3, reconnect3, frequency3, offset3, range3, latitude3, longitude3, agl3, description31, description32, url3); + + CUtils::clean(description31, wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,&*()-+=@/?:;")); + CUtils::clean(description32, wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,&*()-+=@/?:;")); + CUtils::clean(url3, wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,&*()-+=@/?:;")); + + wxString callsign3 = callsign; + if (!repeaterBand3.IsSameAs(wxT(" "))) { + if (!repeaterCall3.IsEmpty()) { + callsign3 = repeaterCall3; + callsign3.Append(wxT(" ")); + callsign3.Truncate(LONG_CALLSIGN_LENGTH); + } + + wxLogInfo(wxT("Repeater 3 callsign: \"%.7s%s\", hardware type: %d, address: %s:%u"), callsign3.c_str(), repeaterBand3.c_str(), int(repeaterType3), repeaterAddress3.c_str(), repeaterPort3); + wxLogInfo(wxT("Repeater 3 reflector: %s, at startup: %d, reconnect: %d"), reflector3.c_str(), atStartup3, reconnect3); + wxLogInfo(wxT("Repeater 3 latitude: %lf, longitude: %lf, range: %.0lf kms, height: %.0lf m, frequency: %.4lf MHz, offset: %.4lf MHz"), latitude3, longitude3, range3, agl3, frequency3, offset3); + wxLogInfo(wxT("Repeater 3 description: \"%s %s\", URL: \"%s\""), description31.c_str(), description32.c_str(), url3.c_str()); + + if (callsign1.IsSameAs(callsign3) && repeaterBand1.IsSameAs(repeaterBand3)) { + wxLogError(wxT("Repeater 3 has the same callsign and module as repeater 1, exiting")); + return; + } + if (callsign2.IsSameAs(callsign3) && repeaterBand2.IsSameAs(repeaterBand3)) { + wxLogError(wxT("Repeater 3 has the same callsign and module as repeater 2, exiting")); + return; + } + + if (repeaterType3 == HW_ICOM && !icomAddress.IsEmpty() && icomRepeaterHandler == NULL) { + icomRepeaterHandler = new CIcomRepeaterProtocolHandler(icomAddress, icomPort, repeaterAddress3, repeaterPort3); + bool res = icomRepeaterHandler->open(); + if (!res) { + wxLogError(wxT("Cannot open the Icom repeater protocol handler")); + delete icomRepeaterHandler; + icomRepeaterHandler = NULL; + } + } else if (repeaterType3 == HW_HOMEBREW && !hbAddress.IsEmpty() && hbRepeaterHandler == NULL) { + hbRepeaterHandler = new CHBRepeaterProtocolHandler(hbAddress, hbPort); + bool res = hbRepeaterHandler->open(); + if (!res) { + wxLogError(wxT("Cannot open the Homebrew repeater protocol handler")); + delete hbRepeaterHandler; + hbRepeaterHandler = NULL; + } + } else if (repeaterType3 == HW_DUMMY && !hbAddress.IsEmpty() && dummyRepeaterHandler == NULL) { + dummyRepeaterHandler = new CDummyRepeaterProtocolHandler; + bool res = dummyRepeaterHandler->open(); + if (!res) { + wxLogError(wxT("Cannot open the Dummy repeater protocol handler")); + delete dummyRepeaterHandler; + dummyRepeaterHandler = NULL; + } + } + + if (latitude3 == 0.0 && longitude3 == 0.0) { + latitude3 = latitude; + longitude3 = longitude; + } + + if (description31.IsEmpty()) + description31 = description1; + if (description32.IsEmpty()) + description32 = description2; + + if (url3.IsEmpty()) + url3 = url; + + if (repeaterType3 == HW_ICOM && icomRepeaterHandler != NULL) { + wxLogInfo(wxT("Repeater 3 bands: %u %u %u"), band31, band32, band33); + thread->addRepeater(callsign3, repeaterBand3, repeaterAddress3, repeaterPort3, repeaterType3, reflector3, atStartup3, reconnect3, dratsEnabled, frequency3, offset3, range3, latitude3, longitude3, agl3, description31, description32, url3, icomRepeaterHandler, band31, band32, band33); + + if (aprs != NULL) + aprs->setPort(callsign3, repeaterBand3, frequency3, offset3, range3, latitude3, longitude3, agl3); + + icomCount++; + } else if (repeaterType3 == HW_HOMEBREW && hbRepeaterHandler != NULL) { + thread->addRepeater(callsign3, repeaterBand3, repeaterAddress3, repeaterPort3, repeaterType3, reflector3, atStartup3, reconnect3, dratsEnabled, frequency3, offset3, range3, latitude3, longitude3, agl3, description31, description32, url3, hbRepeaterHandler); + + if (aprs != NULL) + aprs->setPort(callsign3, repeaterBand3, frequency3, offset3, range3, latitude3, longitude3, agl3); + } else if (repeaterType3 == HW_DUMMY && dummyRepeaterHandler != NULL) { + thread->addRepeater(callsign3, repeaterBand3, repeaterAddress3, repeaterPort3, repeaterType3, reflector3, atStartup3, reconnect3, dratsEnabled, frequency3, offset3, range3, latitude3, longitude3, agl3, description31, description32, url3, dummyRepeaterHandler); + } + } + + wxString repeaterCall4, repeaterBand4, repeaterAddress4, reflector4, description41, description42, url4; + double frequency4, offset4, range4, latitude4, longitude4, agl4; + unsigned char band41, band42, band43; + unsigned int repeaterPort4; + HW_TYPE repeaterType4; + bool atStartup4; + RECONNECT reconnect4; + m_config->getRepeater4(repeaterCall4, repeaterBand4, repeaterType4, repeaterAddress4, repeaterPort4, band41, band42, band43, reflector4, atStartup4, reconnect4, frequency4, offset4, range4, latitude4, longitude4, agl4, description41, description42, url4); + + CUtils::clean(description41, wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,&*()-+=@/?:;")); + CUtils::clean(description42, wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,&*()-+=@/?:;")); + CUtils::clean(url4, wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,&*()-+=@/?:;")); + + wxString callsign4 = callsign; + if (!repeaterBand4.IsSameAs(wxT(" "))) { + if (!repeaterCall4.IsEmpty()) { + callsign4 = repeaterCall4; + callsign4.Append(wxT(" ")); + callsign4.Truncate(LONG_CALLSIGN_LENGTH); + } + + wxLogInfo(wxT("Repeater 4 callsign: \"%.7s%s\", hardware type: %d, address: %s:%u"), callsign4.c_str(), repeaterBand4.c_str(), int(repeaterType4), repeaterAddress4.c_str(), repeaterPort4); + wxLogInfo(wxT("Repeater 4 reflector: %s, at startup: %d, reconnect: %d"), reflector4.c_str(), atStartup4, reconnect4); + wxLogInfo(wxT("Repeater 4 latitude: %lf, longitude: %lf, range: %.0lf kms, height: %.0lf m, frequency: %.4lf MHz, offset: %.4lf MHz"), latitude4, longitude4, range4, agl4, frequency4, offset4); + wxLogInfo(wxT("Repeater 4 description: \"%s %s\", URL: \"%s\""), description41.c_str(), description42.c_str(), url4.c_str()); + + if (callsign1.IsSameAs(callsign4) && repeaterBand1.IsSameAs(repeaterBand4)) { + wxLogError(wxT("Repeater 4 has the same callsign and module as repeater 1, exiting")); + return; + } + if (callsign2.IsSameAs(callsign4) && repeaterBand2.IsSameAs(repeaterBand4)) { + wxLogError(wxT("Repeater 4 has the same callsign and module as repeater 2, exiting")); + return; + } + if (callsign3.IsSameAs(callsign4) && repeaterBand3.IsSameAs(repeaterBand4)) { + wxLogError(wxT("Repeater 4 has the same callsign and module as repeater 3, exiting")); + return; + } + + if (repeaterType4 == HW_ICOM && !icomAddress.IsEmpty() && icomRepeaterHandler == NULL) { + icomRepeaterHandler = new CIcomRepeaterProtocolHandler(icomAddress, icomPort, repeaterAddress4, repeaterPort4); + bool res = icomRepeaterHandler->open(); + if (!res) { + wxLogError(wxT("Cannot open the Icom repeater protocol handler")); + delete icomRepeaterHandler; + icomRepeaterHandler = NULL; + } + } else if (repeaterType4 == HW_HOMEBREW && !hbAddress.IsEmpty() && hbRepeaterHandler == NULL) { + hbRepeaterHandler = new CHBRepeaterProtocolHandler(hbAddress, hbPort); + bool res = hbRepeaterHandler->open(); + if (!res) { + wxLogError(wxT("Cannot open the Homebrew repeater protocol handler")); + delete hbRepeaterHandler; + hbRepeaterHandler = NULL; + } + } else if (repeaterType4 == HW_DUMMY && !hbAddress.IsEmpty() && dummyRepeaterHandler == NULL) { + dummyRepeaterHandler = new CDummyRepeaterProtocolHandler; + bool res = dummyRepeaterHandler->open(); + if (!res) { + wxLogError(wxT("Cannot open the Dummy repeater protocol handler")); + delete dummyRepeaterHandler; + dummyRepeaterHandler = NULL; + } + } + + if (latitude4 == 0.0 && longitude4 == 0.0) { + latitude4 = latitude; + longitude4 = longitude; + } + + if (description41.IsEmpty()) + description41 = description1; + if (description42.IsEmpty()) + description42 = description2; + + if (url4.IsEmpty()) + url4 = url; + + if (repeaterType4 == HW_ICOM && icomRepeaterHandler != NULL) { + wxLogInfo(wxT("Repeater 4 bands: %u %u %u"), band41, band42, band43); + thread->addRepeater(callsign4, repeaterBand4, repeaterAddress4, repeaterPort4, repeaterType4, reflector4, atStartup4, reconnect4, dratsEnabled, frequency4, offset4, range4, latitude4, longitude4, agl4, description41, description42, url4, icomRepeaterHandler, band41, band42, band43); + + if (aprs != NULL) + aprs->setPort(callsign4, repeaterBand4, frequency4, offset4, range4, latitude4, longitude4, agl4); + + icomCount++; + } else if (repeaterType4 == HW_HOMEBREW && hbRepeaterHandler != NULL) { + thread->addRepeater(callsign4, repeaterBand4, repeaterAddress4, repeaterPort4, repeaterType4, reflector4, atStartup4, reconnect4, dratsEnabled, frequency4, offset4, range4, latitude4, longitude4, agl4, description41, description42, url4, hbRepeaterHandler); + + if (aprs != NULL) + aprs->setPort(callsign4, repeaterBand4, frequency4, offset4, range4, latitude4, longitude4, agl4); + } else if (repeaterType4 == HW_DUMMY && dummyRepeaterHandler != NULL) { + thread->addRepeater(callsign4, repeaterBand4, repeaterAddress4, repeaterPort4, repeaterType4, reflector4, atStartup4, reconnect4, dratsEnabled, frequency4, offset4, range4, latitude4, longitude4, agl4, description41, description42, url4, dummyRepeaterHandler); + } + } + + if (icomRepeaterHandler != NULL) + icomRepeaterHandler->setCount(icomCount); + + bool ircDDBAtLeastOneEnabled, ircDDBEnabled1, ircDDBEnabled2, ircDDBEnabled3, ircDDBEnabled4; + wxString ircDDBHostname1, ircDDBUsername1, ircDDBPassword1; + wxString ircDDBHostname2, ircDDBUsername2, ircDDBPassword2; + wxString ircDDBHostname3, ircDDBUsername3, ircDDBPassword3; + wxString ircDDBHostname4, ircDDBUsername4, ircDDBPassword4; + + m_config->getIrcDDB(ircDDBEnabled1, ircDDBHostname1, ircDDBUsername1, ircDDBPassword1); + wxLogInfo(wxT("ircDDB 1 enabled: %d, host: %s, username: %s"), int(ircDDBEnabled1), ircDDBHostname1.c_str(), ircDDBUsername1.c_str()); + + m_config->getIrcDDB2(ircDDBEnabled2, ircDDBHostname2, ircDDBUsername2, ircDDBPassword2); + wxLogInfo(wxT("ircDDB 2 enabled: %d, host: %s, username: %s"), int(ircDDBEnabled2), ircDDBHostname2.c_str(), ircDDBUsername2.c_str()); + + m_config->getIrcDDB3(ircDDBEnabled3, ircDDBHostname3, ircDDBUsername3, ircDDBPassword3); + wxLogInfo(wxT("ircDDB 3 enabled: %d, host: %s, username: %s"), int(ircDDBEnabled3), ircDDBHostname3.c_str(), ircDDBUsername3.c_str()); + + m_config->getIrcDDB4(ircDDBEnabled4, ircDDBHostname4, ircDDBUsername4, ircDDBPassword4); + wxLogInfo(wxT("ircDDB 4 enabled: %d, host: %s, username: %s"), int(ircDDBEnabled4), ircDDBHostname4.c_str(), ircDDBUsername4.c_str()); + + ircDDBAtLeastOneEnabled = ircDDBEnabled1 || ircDDBEnabled2 || ircDDBEnabled3 || ircDDBEnabled4; + + if (ircDDBAtLeastOneEnabled) { +#if defined(__WINDOWS__) + wxString versionInfo = wxT("win_") + LOG_BASE_NAME + wxT("-") + VERSION; +#else + wxString versionInfo = wxT("linux_") + LOG_BASE_NAME + wxT("-") + VERSION; +#endif + CIRCDDB_Array ircDDBClients; + if(ircDDBEnabled1) + ircDDBClients.Add(new CIRCDDBClient(ircDDBHostname1, 9007U, ircDDBUsername1, ircDDBPassword1, versionInfo, gatewayAddress)); + if (ircDDBEnabled2) + ircDDBClients.Add(new CIRCDDBClient(ircDDBHostname2, 9007U, ircDDBUsername2, ircDDBPassword2, versionInfo, gatewayAddress)); + if (ircDDBEnabled3) + ircDDBClients.Add(new CIRCDDBClient(ircDDBHostname3, 9007U, ircDDBUsername3, ircDDBPassword3, versionInfo, gatewayAddress)); + if (ircDDBEnabled4) + ircDDBClients.Add(new CIRCDDBClient(ircDDBHostname4, 9007U, ircDDBUsername4, ircDDBPassword4, versionInfo, gatewayAddress)); + + CIRCDDBMultiClient * ircDDB = new CIRCDDBMultiClient(ircDDBClients); + + bool res = ircDDB->open(); + if (!res) { + wxLogError(wxT("Cannot initialise the ircDDB protocol handler")); + ircDDBAtLeastOneEnabled = false; + } else { + thread->setIRC(ircDDB); + } + } + + if (ircDDBAtLeastOneEnabled) { + wxString starNetBand1, starNetCallsign1, starNetLogoff1, starNetInfo1, starNetPermanent1, link1; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout1, starNetGroupTimeout1; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch1; + bool starNetTXMsgSwitch1; + m_config->getStarNet1(starNetBand1, starNetCallsign1, starNetLogoff1, starNetInfo1, starNetPermanent1, starNetUserTimeout1, starNetGroupTimeout1, starNetCallsignSwitch1, starNetTXMsgSwitch1 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,link1 +#endif + ); + + if (!starNetCallsign1.IsEmpty() && !starNetCallsign1.IsSameAs(wxT(" "))) { + wxString repeater = gatewayCallsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand1); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + thread->addStarNet(starNetCallsign1, starNetLogoff1, repeater, starNetInfo1, starNetPermanent1, starNetUserTimeout1, starNetGroupTimeout1, starNetCallsignSwitch1, starNetTXMsgSwitch1, link1); + wxLogInfo(wxT("STARnet group 1 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign1.c_str(), starNetLogoff1.c_str(), repeater.c_str(), starNetInfo1.c_str(), starNetPermanent1.c_str(), starNetUserTimeout1, starNetGroupTimeout1, int(starNetCallsignSwitch1), int(starNetTXMsgSwitch1), link1.c_str()); +#else + thread->addStarNet(starNetCallsign1, starNetLogoff1, repeater, starNetInfo1, starNetPermanent1, starNetUserTimeout1, starNetGroupTimeout1, starNetCallsignSwitch1, starNetTXMsgSwitch1); + wxLogInfo(wxT("STARnet group 1 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign1.c_str(), starNetLogoff1.c_str(), repeater.c_str(), starNetInfo1.c_str(), starNetPermanent1.c_str(), starNetUserTimeout1, starNetGroupTimeout1, int(starNetCallsignSwitch1), int(starNetTXMsgSwitch1)); +#endif + } + + wxString starNetBand2, starNetCallsign2, starNetLogoff2, starNetInfo2, starNetPermanent2, link2; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout2, starNetGroupTimeout2; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch2; + bool starNetTXMsgSwitch2; + m_config->getStarNet2(starNetBand2, starNetCallsign2, starNetLogoff2, starNetInfo2, starNetPermanent2, starNetUserTimeout2, starNetGroupTimeout2, starNetCallsignSwitch2, starNetTXMsgSwitch2 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,link2 +#endif + ); + + if (!starNetCallsign2.IsEmpty() && !starNetCallsign2.IsSameAs(wxT(" "))) { + wxString repeater = gatewayCallsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand2); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + thread->addStarNet(starNetCallsign2, starNetLogoff2, repeater, starNetInfo2, starNetPermanent2, starNetUserTimeout2, starNetGroupTimeout2, starNetCallsignSwitch2, starNetTXMsgSwitch2, link2); + wxLogInfo(wxT("STARnet group 2 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign2.c_str(), starNetLogoff2.c_str(), repeater.c_str(), starNetInfo2.c_str(), starNetPermanent2.c_str(), starNetUserTimeout2, starNetGroupTimeout2, int(starNetCallsignSwitch2), int(starNetTXMsgSwitch2), link2.c_str()); +#else + thread->addStarNet(starNetCallsign2, starNetLogoff2, repeater, starNetInfo2, starNetPermanent2, starNetUserTimeout2, starNetGroupTimeout2, starNetCallsignSwitch2, starNetTXMsgSwitch2); + wxLogInfo(wxT("STARnet group 2 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign2.c_str(), starNetLogoff2.c_str(), repeater.c_str(), starNetInfo2.c_str(), starNetPermanent2.c_str(), starNetUserTimeout2, starNetGroupTimeout2, int(starNetCallsignSwitch2), int(starNetTXMsgSwitch2)); +#endif + } + + wxString starNetBand3, starNetCallsign3, starNetLogoff3, starNetInfo3, starNetPermanent3, link3; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout3, starNetGroupTimeout3; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch3; + bool starNetTXMsgSwitch3; + m_config->getStarNet3(starNetBand3, starNetCallsign3, starNetLogoff3, starNetInfo3, starNetPermanent3, starNetUserTimeout3, starNetGroupTimeout3, starNetCallsignSwitch3, starNetTXMsgSwitch3 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,link3 +#endif + ); + + if (!starNetCallsign3.IsEmpty() && !starNetCallsign3.IsSameAs(wxT(" "))) { + wxString repeater = gatewayCallsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand3); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + thread->addStarNet(starNetCallsign3, starNetLogoff3, repeater, starNetInfo3, starNetPermanent3, starNetUserTimeout3, starNetGroupTimeout3, starNetCallsignSwitch3, starNetTXMsgSwitch3, link3); + wxLogInfo(wxT("STARnet group 3 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign3.c_str(), starNetLogoff3.c_str(), repeater.c_str(), starNetInfo3.c_str(), starNetPermanent3.c_str(), starNetUserTimeout3, starNetGroupTimeout3, int(starNetCallsignSwitch3), int(starNetTXMsgSwitch3), link3.c_str()); +#else + thread->addStarNet(starNetCallsign3, starNetLogoff3, repeater, starNetInfo3, starNetPermanent3, starNetUserTimeout3, starNetGroupTimeout3, starNetCallsignSwitch3, starNetTXMsgSwitch3); + wxLogInfo(wxT("STARnet group 3 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign3.c_str(), starNetLogoff3.c_str(), repeater.c_str(), starNetInfo3.c_str(), starNetPermanent3.c_str(), starNetUserTimeout3, starNetGroupTimeout3, int(starNetCallsignSwitch3), int(starNetTXMsgSwitch3)); +#endif + } + + wxString starNetBand4, starNetCallsign4, starNetLogoff4, starNetInfo4, starNetPermanent4, link4; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout4, starNetGroupTimeout4; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch4; + bool starNetTXMsgSwitch4; + m_config->getStarNet4(starNetBand4, starNetCallsign4, starNetLogoff4, starNetInfo4, starNetPermanent4, starNetUserTimeout4, starNetGroupTimeout4, starNetCallsignSwitch4, starNetTXMsgSwitch4 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,link4 +#endif + ); + + if (!starNetCallsign4.IsEmpty() && !starNetCallsign4.IsSameAs(wxT(" "))) { + wxString repeater = gatewayCallsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand4); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + thread->addStarNet(starNetCallsign4, starNetLogoff4, repeater, starNetInfo4, starNetPermanent4, starNetUserTimeout4, starNetGroupTimeout4, starNetCallsignSwitch4, starNetTXMsgSwitch4, link4); + wxLogInfo(wxT("STARnet group 4 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign4.c_str(), starNetLogoff4.c_str(), repeater.c_str(), starNetInfo4.c_str(), starNetPermanent4.c_str(), starNetUserTimeout4, starNetGroupTimeout4, int(starNetCallsignSwitch4), int(starNetTXMsgSwitch4), link4.c_str()); +#else + thread->addStarNet(starNetCallsign4, starNetLogoff4, repeater, starNetInfo4, starNetPermanent4, starNetUserTimeout4, starNetGroupTimeout4, starNetCallsignSwitch4, starNetTXMsgSwitch4); + wxLogInfo(wxT("STARnet group 4 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign4.c_str(), starNetLogoff4.c_str(), repeater.c_str(), starNetInfo4.c_str(), starNetPermanent4.c_str(), starNetUserTimeout4, starNetGroupTimeout4, int(starNetCallsignSwitch4), int(starNetTXMsgSwitch4)); +#endif + } + + wxString starNetBand5, starNetCallsign5, starNetLogoff5, starNetInfo5, starNetPermanent5, link5; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout5, starNetGroupTimeout5; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch5; + bool starNetTXMsgSwitch5; + m_config->getStarNet5(starNetBand5, starNetCallsign5, starNetLogoff5, starNetInfo5, starNetPermanent5, starNetUserTimeout5, starNetGroupTimeout5, starNetCallsignSwitch5, starNetTXMsgSwitch5 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,link5 +#endif + ); + + if (!starNetCallsign5.IsEmpty() && !starNetCallsign5.IsSameAs(wxT(" "))) { + wxString repeater = gatewayCallsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand5); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + thread->addStarNet(starNetCallsign5, starNetLogoff5, repeater, starNetInfo5, starNetPermanent5, starNetUserTimeout5, starNetGroupTimeout5, starNetCallsignSwitch5, starNetTXMsgSwitch5, link5); + wxLogInfo(wxT("STARnet group 5 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign5.c_str(), starNetLogoff5.c_str(), repeater.c_str(), starNetInfo5.c_str(), starNetPermanent5.c_str(), starNetUserTimeout5, starNetGroupTimeout5, int(starNetCallsignSwitch5), int(starNetTXMsgSwitch5), link5.c_str()); +#else + thread->addStarNet(starNetCallsign5, starNetLogoff5, repeater, starNetInfo5, starNetPermanent5, starNetUserTimeout5, starNetGroupTimeout5, starNetCallsignSwitch5, starNetTXMsgSwitch5); + wxLogInfo(wxT("STARnet group 5 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign5.c_str(), starNetLogoff5.c_str(), repeater.c_str(), starNetInfo5.c_str(), starNetPermanent5.c_str(), starNetUserTimeout5, starNetGroupTimeout5, int(starNetCallsignSwitch5), int(starNetTXMsgSwitch5)); +#endif + } + } + + bool dextraEnabled; + unsigned int dextraMaxDongles; + m_config->getDExtra(dextraEnabled, dextraMaxDongles); + wxLogInfo(wxT("DExtra enabled: %d, max. dongles: %u"), int(dextraEnabled), dextraMaxDongles); + + bool remoteEnabled; + wxString remotePassword; + unsigned int remotePort; + m_config->getRemote(remoteEnabled, remotePassword, remotePort); + wxLogInfo(wxT("Remote enabled: %d, port: %u"), int(remoteEnabled), remotePort); + thread->setRemote(remoteEnabled, remotePassword, remotePort); + + wxString dplusLogin; + unsigned int dplusMaxDongles; + bool dplusEnabled; + m_config->getDPlus(dplusEnabled, dplusMaxDongles, dplusLogin); + wxLogInfo(wxT("D-Plus enabled: %d, max. dongles; %u, login: %s"), int(dplusEnabled), dplusMaxDongles, dplusLogin.c_str()); + + bool dcsEnabled, ccsEnabled; + wxString ccsHost; + m_config->getDCS(dcsEnabled, ccsEnabled, ccsHost); + wxLogInfo(wxT("DCS enabled: %d, CCS enabled: %d, server: %s"), int(dcsEnabled), int(ccsEnabled), ccsHost.c_str()); + + bool xlxEnabled; + bool xlxOverrideLocal; + wxString xlxHostsFileUrl; + m_config->getXLX(xlxEnabled, xlxOverrideLocal, xlxHostsFileUrl); + wxLogInfo(wxT("XLX enabled: %d, Override Local %d, Hosts file url: %s"), int(xlxEnabled), int(xlxOverrideLocal), xlxHostsFileUrl.c_str()); + + if (repeaterBand1.Len() > 1U || repeaterBand2.Len() > 1U || + repeaterBand3.Len() > 1U || repeaterBand4.Len() > 1U) { + wxLogInfo(wxT("DD mode enabled")); + thread->setDDModeEnabled(true); + } + + wxFileName wlFilename(wxFileName::GetHomeDir(), PRIMARY_WHITELIST_FILE_NAME); + bool exists = wlFilename.FileExists(); + + if (!exists) { + wlFilename.Assign(wxFileName::GetHomeDir(), SECONDARY_WHITELIST_FILE_NAME); + exists = wlFilename.FileExists(); + } + + if (exists) { + CCallsignList* list = new CCallsignList(wlFilename.GetFullPath()); + bool res = list->load(); + if (!res) { + wxLogError(wxT("Unable to open the white list file - %s"), wlFilename.GetFullPath().c_str()); + delete list; + } else { + wxLogInfo(wxT("%u callsigns loaded into the white list"), list->getCount()); + thread->setWhiteList(list); + } + } + + wxFileName blFilename(wxFileName::GetHomeDir(), PRIMARY_BLACKLIST_FILE_NAME); + exists = blFilename.FileExists(); + + if (!exists) { + blFilename.Assign(wxFileName::GetHomeDir(), SECONDARY_BLACKLIST_FILE_NAME); + exists = blFilename.FileExists(); + } + + if (exists) { + CCallsignList* list = new CCallsignList(blFilename.GetFullPath()); + bool res = list->load(); + if (!res) { + wxLogError(wxT("Unable to open the black list file - %s"), blFilename.GetFullPath().c_str()); + delete list; + } else { + wxLogInfo(wxT("%u callsigns loaded into the black list"), list->getCount()); + thread->setBlackList(list); + } + } + + wxFileName rlFilename(wxFileName::GetHomeDir(), RESTRICT_FILE_NAME); + exists = rlFilename.FileExists(); + if (exists) { + CCallsignList* list = new CCallsignList(rlFilename.GetFullPath()); + bool res = list->load(); + if (!res) { + wxLogError(wxT("Unable to open the restrict list file - %s"), rlFilename.GetFullPath().c_str()); + delete list; + } else { + wxLogInfo(wxT("%u callsigns loaded into the restrict list"), list->getCount()); + thread->setRestrictList(list); + } + } + + thread->setIcomRepeaterHandler(icomRepeaterHandler); + thread->setHBRepeaterHandler(hbRepeaterHandler); + thread->setDummyRepeaterHandler(dummyRepeaterHandler); + thread->setLanguage(language); + thread->setDPlus(dplusEnabled, dplusMaxDongles, dplusLogin); + thread->setDExtra(dextraEnabled, dextraMaxDongles); + thread->setDCS(dcsEnabled); + thread->setCCS(ccsEnabled, ccsHost); + thread->setXLX(xlxEnabled, xlxOverrideLocal, xlxEnabled ? CXLXHostsFileDownloader::Download(xlxHostsFileUrl) : wxString(wxEmptyString)); + thread->setInfoEnabled(infoEnabled); + thread->setEchoEnabled(echoEnabled); + thread->setDTMFEnabled(dtmfEnabled); + thread->setLog(logEnabled); + thread->setLocation(latitude, longitude); + + // Convert the worker class into a thread + m_thread = new CIRCDDBGatewayThreadHelper(thread); + m_thread->start(); +} diff --git a/ircDDBGateway/IRCDDBGatewayApp.h b/ircDDBGateway/IRCDDBGatewayApp.h new file mode 100644 index 0000000..6486931 --- /dev/null +++ b/ircDDBGateway/IRCDDBGatewayApp.h @@ -0,0 +1,71 @@ +/* + * 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 + * 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 IRCDDBGatewayApp_H +#define IRCDDBGatewayApp_H + +#include "IRCDDBGatewayThreadHelper.h" +#include "IRCDDBGatewayStatusData.h" +#include "IRCDDBGatewayConfig.h" +#include "IRCDDBGatewayFrame.h" +#include "Defs.h" + +#include +#include + +class CIRCDDBGatewayApp : public wxApp { + +public: + CIRCDDBGatewayApp(); + virtual ~CIRCDDBGatewayApp(); + + virtual bool OnInit(); + virtual int OnExit(); + + virtual void OnInitCmdLine(wxCmdLineParser& parser); + virtual bool OnCmdLineParsed(wxCmdLineParser& parser); + + // This is overridden because dialog boxes from threads are bad news +#if defined(__WXDEBUG__) + virtual void OnAssertFailure(const wxChar* file, int line, const wxChar* func, const wxChar* cond, const wxChar* msg); +#endif + + virtual void showLog(const wxString& text); + + virtual CIRCDDBGatewayStatusData* getStatus() const; + + virtual void setPosition(int x, int y); + +private: + wxString m_name; + bool m_nolog; + bool m_gui; + wxString m_logDir; + wxString m_confDir; + CIRCDDBGatewayFrame* m_frame; + CIRCDDBGatewayThreadHelper* m_thread; + CIRCDDBGatewayConfig* m_config; + wxSingleInstanceChecker* m_checker; + wxLogChain* m_logChain; + + void createThread(); +}; + +DECLARE_APP(CIRCDDBGatewayApp) + +#endif diff --git a/ircDDBGateway/IRCDDBGatewayAppD.cpp b/ircDDBGateway/IRCDDBGatewayAppD.cpp new file mode 100644 index 0000000..ced3177 --- /dev/null +++ b/ircDDBGateway/IRCDDBGatewayAppD.cpp @@ -0,0 +1,916 @@ +/* + * Copyright (C) 2010-2013,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 + * 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 "DummyRepeaterProtocolHandler.h" +#include "IcomRepeaterProtocolHandler.h" +#include "HBRepeaterProtocolHandler.h" +#include "IRCDDBGatewayConfig.h" +#include "IRCDDBGatewayAppD.h" +#include "IRCDDBGatewayDefs.h" +#include "CallsignList.h" +#include "APRSWriter.h" +#include "Version.h" +#include "Logger.h" +#include "IRCDDB.h" +#include "IRCDDBClient.h" +#include "IRCDDBMultiClient.h" +#include "Utils.h" +#include "XLXHostsFileDownloader.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +const wxChar* NAME_PARAM = wxT("Gateway Name"); +const wxChar* NOLOGGING_SWITCH = wxT("nolog"); +const wxChar* LOGDIR_OPTION = wxT("logdir"); +const wxChar* CONFDIR_OPTION = wxT("confdir"); +const wxChar* DAEMON_SWITCH = wxT("daemon"); + +const wxString LOG_BASE_NAME = wxT("ircddbgatewayd"); + +static CIRCDDBGatewayAppD* m_gateway = NULL; + +static void handler(int signum) +{ + m_gateway->kill(); +} + +int main(int argc, char** argv) +{ + bool res = ::wxInitialize(); + if (!res) { + ::fprintf(stderr, "ircddbgatewayd: failed to initialise the wxWidgets library, exiting\n"); + return -1; + } + + wxCmdLineParser parser(argc, argv); + parser.AddSwitch(NOLOGGING_SWITCH, wxEmptyString, wxEmptyString, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddSwitch(DAEMON_SWITCH, wxEmptyString, wxEmptyString, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption(LOGDIR_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption(CONFDIR_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddParam(NAME_PARAM, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + + int cmd = parser.Parse(); + if (cmd != 0) { + ::wxUninitialize(); + return 0; + } + + bool nolog = parser.Found(NOLOGGING_SWITCH); + bool daemon = parser.Found(DAEMON_SWITCH); + + wxString logDir; + bool found = parser.Found(LOGDIR_OPTION, &logDir); + if (!found) + logDir.Clear(); + + wxString confDir; + found = parser.Found(CONFDIR_OPTION, &confDir); + if (!found) + confDir = wxT(CONF_DIR); + + wxString name; + if (parser.GetParamCount() > 0U) + name = parser.GetParam(0U); + + if (daemon) { + pid_t pid = ::fork(); + + if (pid < 0) { + ::fprintf(stderr, "ircddbgatewayd: error in fork(), exiting\n"); + ::wxUninitialize(); + return 1; + } + + // If this is the parent, exit + if (pid > 0) + return 0; + + // We are the child from here onwards + ::setsid(); + + if(::chdir("/") == -1) + ::fprintf(stderr, "ircddbgatewayd: error changing directory %s\n", strerror(errno)); + + ::umask(0); + } + + wxString pidFileName; + if (!name.IsEmpty()) + pidFileName.Printf(wxT("/var/run/ircddbgateway_%s.pid"), name.c_str()); + else + pidFileName = wxT("/var/run/ircddbgateway.pid"); + pidFileName.Replace(wxT(" "), wxT("_")); + + char fileName[128U]; + ::memset(fileName, 0x00U, 128U); + for (unsigned int i = 0U; i < pidFileName.Len(); i++) + fileName[i] = pidFileName.GetChar(i); + + FILE* fp = ::fopen(fileName, "wt"); + if (fp != NULL) { + ::fprintf(fp, "%u\n", ::getpid()); + ::fclose(fp); + } + + m_gateway = new CIRCDDBGatewayAppD(nolog, logDir, confDir, name); + if (!m_gateway->init()) { + ::wxUninitialize(); + return 1; + } + + ::signal(SIGUSR1, handler); + + m_gateway->run(); + + delete m_gateway; + + ::unlink(fileName); + + ::wxUninitialize(); + + return 0; +} + +CIRCDDBGatewayAppD::CIRCDDBGatewayAppD(bool nolog, const wxString& logDir, const wxString& confDir, const wxString& name) : +m_name(name), +m_nolog(nolog), +m_logDir(logDir), +m_confDir(confDir), +m_thread(NULL), +m_checker(NULL) +{ +} + +CIRCDDBGatewayAppD::~CIRCDDBGatewayAppD() +{ +} + +bool CIRCDDBGatewayAppD::init() +{ + if (!m_nolog) { + wxString logBaseName = LOG_BASE_NAME; + if (!m_name.IsEmpty()) { + logBaseName.Append(wxT("_")); + logBaseName.Append(m_name); + } + + if (m_logDir.IsEmpty()) + m_logDir = wxT(LOG_DIR); + + wxLog* log = new CLogger(m_logDir, logBaseName); + wxLog::SetActiveTarget(log); + } else { + new wxLogNull; + } + + wxString appName; + if (!m_name.IsEmpty()) + appName = APPLICATION_NAME + wxT(" ") + m_name; + else + appName = APPLICATION_NAME; + appName.Replace(wxT(" "), wxT("_")); + + m_checker = new wxSingleInstanceChecker(appName, wxT("/tmp")); + bool ret = m_checker->IsAnotherRunning(); + if (ret) { + wxLogError(wxT("Another copy of the ircDDB Gateway is running, exiting")); + return false; + } + + wxLogInfo(wxT("Starting ") + APPLICATION_NAME + wxT(" daemon - ") + VERSION); + + // Log the version of wxWidgets and the Operating System + wxLogInfo(wxT("Using wxWidgets %d.%d.%d on %s"), wxMAJOR_VERSION, wxMINOR_VERSION, wxRELEASE_NUMBER, ::wxGetOsDescription().c_str()); + + return createThread(); +} + +void CIRCDDBGatewayAppD::run() +{ + m_thread->run(); + + delete m_checker; + + wxLogInfo(APPLICATION_NAME + wxT(" is exiting")); +} + +bool CIRCDDBGatewayAppD::createThread() +{ + CIRCDDBGatewayConfig config(m_confDir, CONFIG_FILE_NAME, m_name); + + m_thread = new CIRCDDBGatewayThread(m_logDir, m_name); + + GATEWAY_TYPE gatewayType; + wxString gatewayCallsign, gatewayAddress, icomAddress, hbAddress, description1, description2, url; + unsigned int icomPort, hbPort; + double latitude, longitude; + config.getGateway(gatewayType, gatewayCallsign, gatewayAddress, icomAddress, icomPort, hbAddress, hbPort, latitude, longitude, description1, description2, url); + + gatewayCallsign.MakeUpper(); + gatewayCallsign.Append(wxT(" ")); + gatewayCallsign.Truncate(LONG_CALLSIGN_LENGTH - 1U); + + wxString callsign = gatewayCallsign; + callsign.Append(wxT(" ")); + + gatewayCallsign.Append(wxT("G")); + + wxLogInfo(wxT("Gateway type: %d, callsign: \"%s\", address: %s, Icom address: %s:%u, homebrew address: %s:%u, latitude: %lf, longitude: %lf, description: \"%s %s\", URL: \"%s\""), int(gatewayType), gatewayCallsign.c_str(), gatewayAddress.c_str(), icomAddress.c_str(), icomPort, hbAddress.c_str(), hbPort, latitude, longitude, description1.c_str(), description2.c_str(), url.c_str()); + + m_thread->setGateway(gatewayType, gatewayCallsign, gatewayAddress); + + wxString aprsHostname; + unsigned int aprsPort; + bool aprsEnabled; + config.getDPRS(aprsEnabled, aprsHostname, aprsPort); + wxLogInfo(wxT("APRS enabled: %d, host: %s:%u"), int(aprsEnabled), aprsHostname.c_str(), aprsPort); + + CAPRSWriter* aprs = NULL; + if (aprsEnabled && !gatewayCallsign.IsEmpty() && !aprsHostname.IsEmpty() && aprsPort != 0U) { + aprs = new CAPRSWriter(aprsHostname, aprsPort, gatewayCallsign, gatewayAddress); + + bool res = aprs->open(); + if (!res) + wxLogError(wxT("Cannot initialise the APRS data writer")); + else + m_thread->setAPRSWriter(aprs); + } + + TEXT_LANG language; + bool infoEnabled, echoEnabled, logEnabled, dratsEnabled, dtmfEnabled; + config.getMiscellaneous(language, infoEnabled, echoEnabled, logEnabled, dratsEnabled, dtmfEnabled); + wxLogInfo(wxT("Language: %d, info enabled: %d, echo enabled: %d, log enabled : %d, D-RATS enabled: %d, DTMF control enabled: %d"), int(language), int(infoEnabled), int(echoEnabled), int(logEnabled), int(dratsEnabled), int(dtmfEnabled)); + + CIcomRepeaterProtocolHandler* icomRepeaterHandler = NULL; + CHBRepeaterProtocolHandler* hbRepeaterHandler = NULL; + CDummyRepeaterProtocolHandler* dummyRepeaterHandler = NULL; + + unsigned int icomCount = 0U; + + wxString repeaterCall1, repeaterBand1, repeaterAddress1, reflector1, description11, description12, url1; + double frequency1, offset1, range1, latitude1, longitude1, agl1; + unsigned char band11, band12, band13; + unsigned int repeaterPort1; + HW_TYPE repeaterType1; + bool atStartup1; + RECONNECT reconnect1; + config.getRepeater1(repeaterCall1, repeaterBand1, repeaterType1, repeaterAddress1, repeaterPort1, band11, band12, band13, reflector1, atStartup1, reconnect1, frequency1, offset1, range1, latitude1, longitude1, agl1, description11, description12, url1); + + CUtils::clean(description11, wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,&*()-+=@/?:;")); + CUtils::clean(description12, wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,&*()-+=@/?:;")); + CUtils::clean(url1, wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,&*()-+=@/?:;")); + + wxString callsign1 = callsign; + if (!repeaterBand1.IsSameAs(wxT(" "))) { + if (!repeaterCall1.IsEmpty()) { + callsign1 = repeaterCall1; + callsign1.Append(wxT(" ")); + callsign1.Truncate(LONG_CALLSIGN_LENGTH); + } + + wxLogInfo(wxT("Repeater 1 callsign: \"%.7s%s\", hardware type: %d, address: %s:%u"), callsign1.c_str(), repeaterBand1.c_str(), int(repeaterType1), repeaterAddress1.c_str(), repeaterPort1); + wxLogInfo(wxT("Repeater 1 reflector: %s, at startup: %d, reconnect: %d"), reflector1.c_str(), atStartup1, reconnect1); + wxLogInfo(wxT("Repeater 1 latitude: %lf, longitude: %lf, range: %.0lf kms, height: %.0lf m, frequency: %.4lf MHz, offset: %.4lf MHz"), latitude1, longitude1, range1, agl1, frequency1, offset1); + wxLogInfo(wxT("Repeater 1 description: \"%s %s\", URL: \"%s\""), description11.c_str(), description12.c_str(), url1.c_str()); + + if (repeaterType1 == HW_ICOM && !icomAddress.IsEmpty()) { + icomRepeaterHandler = new CIcomRepeaterProtocolHandler(icomAddress, icomPort, repeaterAddress1, repeaterPort1); + bool res = icomRepeaterHandler->open(); + if (!res) { + wxLogError(wxT("Cannot open the Icom repeater protocol handler")); + delete icomRepeaterHandler; + icomRepeaterHandler = NULL; + } + } else if (repeaterType1 == HW_HOMEBREW && !hbAddress.IsEmpty()) { + hbRepeaterHandler = new CHBRepeaterProtocolHandler(hbAddress, hbPort); + bool res = hbRepeaterHandler->open(); + if (!res) { + wxLogError(wxT("Cannot open the Homebrew repeater protocol handler")); + delete hbRepeaterHandler; + hbRepeaterHandler = NULL; + } + } else if (repeaterType1 == HW_DUMMY && !hbAddress.IsEmpty()) { + dummyRepeaterHandler = new CDummyRepeaterProtocolHandler; + bool res = dummyRepeaterHandler->open(); + if (!res) { + wxLogError(wxT("Cannot open the Dummy repeater protocol handler")); + delete dummyRepeaterHandler; + dummyRepeaterHandler = NULL; + } + } + + if (latitude1 == 0.0 && longitude1 == 0.0) { + latitude1 = latitude; + longitude1 = longitude; + } + + if (description11.IsEmpty()) + description11 = description1; + if (description12.IsEmpty()) + description12 = description2; + + if (url1.IsEmpty()) + url1 = url; + + if (repeaterType1 == HW_ICOM && icomRepeaterHandler != NULL) { + wxLogInfo(wxT("Repeater 1 bands: %u %u %u"), band11, band12, band13); + m_thread->addRepeater(callsign1, repeaterBand1, repeaterAddress1, repeaterPort1, repeaterType1, reflector1, atStartup1, reconnect1, dratsEnabled, frequency1, offset1, range1, latitude1, longitude1, agl1, description11, description12, url1, icomRepeaterHandler, band11, band12, band13); + + if (aprs != NULL) + aprs->setPort(callsign1, repeaterBand1, frequency1, offset1, range1, latitude1, longitude1, agl1); + + icomCount++; + } else if (repeaterType1 == HW_HOMEBREW && hbRepeaterHandler != NULL) { + m_thread->addRepeater(callsign1, repeaterBand1, repeaterAddress1, repeaterPort1, repeaterType1, reflector1, atStartup1, reconnect1, dratsEnabled, frequency1, offset1, range1, latitude1, longitude1, agl1, description11, description12, url1, hbRepeaterHandler); + + if (aprs != NULL) + aprs->setPort(callsign1, repeaterBand1, frequency1, offset1, range1, latitude1, longitude1, agl1); + } else if (repeaterType1 == HW_DUMMY && dummyRepeaterHandler != NULL) { + m_thread->addRepeater(callsign1, repeaterBand1, repeaterAddress1, repeaterPort1, repeaterType1, reflector1, atStartup1, reconnect1, dratsEnabled, frequency1, offset1, range1, latitude1, longitude1, agl1, description11, description12, url1, dummyRepeaterHandler); + } + } + + wxString repeaterCall2, repeaterBand2, repeaterAddress2, reflector2, description21, description22, url2; + double frequency2, offset2, range2, latitude2, longitude2, agl2; + unsigned char band21, band22, band23; + unsigned int repeaterPort2; + HW_TYPE repeaterType2; + bool atStartup2; + RECONNECT reconnect2; + config.getRepeater2(repeaterCall2, repeaterBand2, repeaterType2, repeaterAddress2, repeaterPort2, band21, band22, band23, reflector2, atStartup2, reconnect2, frequency2, offset2, range2, latitude2, longitude2, agl2, description21, description22, url2); + + CUtils::clean(description21, wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,&*()-+=@/?:;")); + CUtils::clean(description22, wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,&*()-+=@/?:;")); + CUtils::clean(url2, wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,&*()-+=@/?:;")); + + wxString callsign2 = callsign; + if (!repeaterBand2.IsSameAs(wxT(" "))) { + if (!repeaterCall2.IsEmpty()) { + callsign2 = repeaterCall2; + callsign2.Append(wxT(" ")); + callsign2.Truncate(LONG_CALLSIGN_LENGTH); + } + + wxLogInfo(wxT("Repeater 2 callsign: \"%.7s%s\", hardware type: %d, address: %s:%u"), callsign2.c_str(), repeaterBand2.c_str(), int(repeaterType2), repeaterAddress2.c_str(), repeaterPort2); + wxLogInfo(wxT("Repeater 2 reflector: %s, at startup: %d, reconnect: %d"), reflector2.c_str(), atStartup2, reconnect2); + wxLogInfo(wxT("Repeater 2 latitude: %lf, longitude: %lf, range: %.0lf kms, height: %.0lf m, frequency: %.4lf MHz, offset: %.4lf MHz"), latitude2, longitude2, range2, agl2, frequency2, offset2); + wxLogInfo(wxT("Repeater 2 description: \"%s %s\", URL: \"%s\""), description21.c_str(), description22.c_str(), url2.c_str()); + + if (callsign1.IsSameAs(callsign2) && repeaterBand1.IsSameAs(repeaterBand2)) { + wxLogError(wxT("Repeater 2 has the same callsign and module as repeater 1, exiting")); + return false; + } + + if (repeaterType2 == HW_ICOM && !icomAddress.IsEmpty() && icomRepeaterHandler == NULL) { + icomRepeaterHandler = new CIcomRepeaterProtocolHandler(icomAddress, icomPort, repeaterAddress2, repeaterPort2); + bool res = icomRepeaterHandler->open(); + if (!res) { + wxLogError(wxT("Cannot open the Icom repeater protocol handler")); + delete icomRepeaterHandler; + icomRepeaterHandler = NULL; + } + } else if (repeaterType2 == HW_HOMEBREW && !hbAddress.IsEmpty() && hbRepeaterHandler == NULL) { + hbRepeaterHandler = new CHBRepeaterProtocolHandler(hbAddress, hbPort); + bool res = hbRepeaterHandler->open(); + if (!res) { + wxLogError(wxT("Cannot open the Homebrew repeater protocol handler")); + delete hbRepeaterHandler; + hbRepeaterHandler = NULL; + } + } else if (repeaterType2 == HW_DUMMY && !hbAddress.IsEmpty() && dummyRepeaterHandler == NULL) { + dummyRepeaterHandler = new CDummyRepeaterProtocolHandler; + bool res = dummyRepeaterHandler->open(); + if (!res) { + wxLogError(wxT("Cannot open the Dummy repeater protocol handler")); + delete dummyRepeaterHandler; + dummyRepeaterHandler = NULL; + } + } + + if (latitude2 == 0.0 && longitude2 == 0.0) { + latitude2 = latitude; + longitude2 = longitude; + } + + if (description21.IsEmpty()) + description21 = description1; + if (description22.IsEmpty()) + description22 = description2; + + if (url2.IsEmpty()) + url2 = url; + + if (repeaterType2 == HW_ICOM && icomRepeaterHandler != NULL) { + wxLogInfo(wxT("Repeater 2 bands: %u %u %u"), band21, band22, band23); + m_thread->addRepeater(callsign2, repeaterBand2, repeaterAddress2, repeaterPort2, repeaterType2, reflector2, atStartup2, reconnect2, dratsEnabled, frequency2, offset2, range2, latitude2, longitude2, agl2, description21, description22, url2, icomRepeaterHandler, band21, band22, band23); + + if (aprs != NULL) + aprs->setPort(callsign2, repeaterBand2, frequency2, offset2, range2, latitude2, longitude2, agl2); + + icomCount++; + } else if (repeaterType2 == HW_HOMEBREW && hbRepeaterHandler != NULL) { + m_thread->addRepeater(callsign2, repeaterBand2, repeaterAddress2, repeaterPort2, repeaterType2, reflector2, atStartup2, reconnect2, dratsEnabled, frequency2, offset2, range2, latitude2, longitude2, agl2, description21, description22, url2, hbRepeaterHandler); + + if (aprs != NULL) + aprs->setPort(callsign2, repeaterBand2, frequency2, offset2, range2, latitude2, longitude2, agl2); + } else if (repeaterType2 == HW_DUMMY && dummyRepeaterHandler != NULL) { + m_thread->addRepeater(callsign2, repeaterBand2, repeaterAddress2, repeaterPort2, repeaterType2, reflector2, atStartup2, reconnect2, dratsEnabled, frequency2, offset2, range2, latitude2, longitude2, agl2, description21, description22, url2, dummyRepeaterHandler); + } + } + + wxString repeaterCall3, repeaterBand3, repeaterAddress3, reflector3, description31, description32, url3; + double frequency3, offset3, range3, latitude3, longitude3, agl3; + unsigned char band31, band32, band33; + unsigned int repeaterPort3; + HW_TYPE repeaterType3; + bool atStartup3; + RECONNECT reconnect3; + config.getRepeater3(repeaterCall3, repeaterBand3, repeaterType3, repeaterAddress3, repeaterPort3, band31, band32, band33, reflector3, atStartup3, reconnect3, frequency3, offset3, range3, latitude3, longitude3, agl3, description31, description32, url3); + + CUtils::clean(description31, wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,&*()-+=@/?:;")); + CUtils::clean(description32, wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,&*()-+=@/?:;")); + CUtils::clean(url3, wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,&*()-+=@/?:;")); + + wxString callsign3 = callsign; + if (!repeaterBand3.IsSameAs(wxT(" "))) { + if (!repeaterCall3.IsEmpty()) { + callsign3 = repeaterCall3; + callsign3.Append(wxT(" ")); + callsign3.Truncate(LONG_CALLSIGN_LENGTH); + } + + wxLogInfo(wxT("Repeater 3 callsign: \"%.7s%s\", hardware type: %d, address: %s:%u"), callsign3.c_str(), repeaterBand3.c_str(), int(repeaterType3), repeaterAddress3.c_str(), repeaterPort3); + wxLogInfo(wxT("Repeater 3 reflector: %s, at startup: %d, reconnect: %d"), reflector3.c_str(), atStartup3, reconnect3); + wxLogInfo(wxT("Repeater 3 latitude: %lf, longitude: %lf, range: %.0lf kms, height: %.0lf m, frequency: %.4lf MHz, offset: %.4lf MHz"), latitude3, longitude3, range3, agl3, frequency3, offset3); + wxLogInfo(wxT("Repeater 3 description: \"%s %s\", URL: \"%s\""), description31.c_str(), description32.c_str(), url3.c_str()); + + if (callsign1.IsSameAs(callsign3) && repeaterBand1.IsSameAs(repeaterBand3)) { + wxLogError(wxT("Repeater 3 has the same callsign and module as repeater 1, exiting")); + return false; + } + if (callsign2.IsSameAs(callsign3) && repeaterBand2.IsSameAs(repeaterBand3)) { + wxLogError(wxT("Repeater 3 has the same callsign and module as repeater 2, exiting")); + return false; + } + + if (repeaterType3 == HW_ICOM && !icomAddress.IsEmpty() && icomRepeaterHandler == NULL) { + icomRepeaterHandler = new CIcomRepeaterProtocolHandler(icomAddress, icomPort, repeaterAddress3, repeaterPort3); + bool res = icomRepeaterHandler->open(); + if (!res) { + wxLogError(wxT("Cannot open the Icom repeater protocol handler")); + delete icomRepeaterHandler; + icomRepeaterHandler = NULL; + } + } else if (repeaterType3 == HW_HOMEBREW && !hbAddress.IsEmpty() && hbRepeaterHandler == NULL) { + hbRepeaterHandler = new CHBRepeaterProtocolHandler(hbAddress, hbPort); + bool res = hbRepeaterHandler->open(); + if (!res) { + wxLogError(wxT("Cannot open the Homebrew repeater protocol handler")); + delete hbRepeaterHandler; + hbRepeaterHandler = NULL; + } + } else if (repeaterType3 == HW_DUMMY && !hbAddress.IsEmpty() && dummyRepeaterHandler == NULL) { + dummyRepeaterHandler = new CDummyRepeaterProtocolHandler; + bool res = dummyRepeaterHandler->open(); + if (!res) { + wxLogError(wxT("Cannot open the Dummy repeater protocol handler")); + delete dummyRepeaterHandler; + dummyRepeaterHandler = NULL; + } + } + + if (latitude3 == 0.0 && longitude3 == 0.0) { + latitude3 = latitude; + longitude3 = longitude; + } + + if (description31.IsEmpty()) + description31 = description1; + if (description32.IsEmpty()) + description32 = description2; + + if (url3.IsEmpty()) + url3 = url; + + if (repeaterType3 == HW_ICOM && icomRepeaterHandler != NULL) { + wxLogInfo(wxT("Repeater 3 bands: %u %u %u"), band31, band32, band33); + m_thread->addRepeater(callsign3, repeaterBand3, repeaterAddress3, repeaterPort3, repeaterType3, reflector3, atStartup3, reconnect3, dratsEnabled, frequency3, offset3, range3, latitude3, longitude3, agl3, description31, description32, url3, icomRepeaterHandler, band31, band32, band33); + + if (aprs != NULL) + aprs->setPort(callsign3, repeaterBand3, frequency3, offset3, range3, latitude3, longitude3, agl3); + + icomCount++; + } else if (repeaterType3 == HW_HOMEBREW && hbRepeaterHandler != NULL) { + m_thread->addRepeater(callsign3, repeaterBand3, repeaterAddress3, repeaterPort3, repeaterType3, reflector3, atStartup3, reconnect3, dratsEnabled, frequency3, offset3, range3, latitude3, longitude3, agl3, description31, description32, url3, hbRepeaterHandler); + + if (aprs != NULL) + aprs->setPort(callsign3, repeaterBand3, frequency3, offset3, range3, latitude3, longitude3, agl3); + } else if (repeaterType3 == HW_DUMMY && dummyRepeaterHandler != NULL) { + m_thread->addRepeater(callsign3, repeaterBand3, repeaterAddress3, repeaterPort3, repeaterType3, reflector3, atStartup3, reconnect3, dratsEnabled, frequency3, offset3, range3, latitude3, longitude3, agl3, description31, description32, url3, dummyRepeaterHandler); + } + } + + wxString repeaterCall4, repeaterBand4, repeaterAddress4, reflector4, description41, description42, url4; + double frequency4, offset4, range4, latitude4, longitude4, agl4; + unsigned char band41, band42, band43; + unsigned int repeaterPort4; + HW_TYPE repeaterType4; + bool atStartup4; + RECONNECT reconnect4; + config.getRepeater4(repeaterCall4, repeaterBand4, repeaterType4, repeaterAddress4, repeaterPort4, band41, band42, band43, reflector4, atStartup4, reconnect4, frequency4, offset4, range4, latitude4, longitude4, agl4, description41, description42, url4); + + CUtils::clean(description41, wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,&*()-+=@/?:;")); + CUtils::clean(description42, wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,&*()-+=@/?:;")); + CUtils::clean(url4, wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,&*()-+=@/?:;")); + + wxString callsign4 = callsign; + if (!repeaterBand4.IsSameAs(wxT(" "))) { + if (!repeaterCall4.IsEmpty()) { + callsign4 = repeaterCall4; + callsign4.Append(wxT(" ")); + callsign4.Truncate(LONG_CALLSIGN_LENGTH); + } + + wxLogInfo(wxT("Repeater 4 callsign: \"%.7s%s\", hardware type: %d, address: %s:%u"), callsign4.c_str(), repeaterBand4.c_str(), int(repeaterType4), repeaterAddress4.c_str(), repeaterPort4); + wxLogInfo(wxT("Repeater 4 reflector: %s, at startup: %d, reconnect: %d"), reflector4.c_str(), atStartup4, reconnect4); + wxLogInfo(wxT("Repeater 4 latitude: %lf, longitude: %lf, range: %.0lf kms, height: %.0lf m, frequency: %.4lf MHz, offset: %.4lf MHz"), latitude4, longitude4, range4, agl4, frequency4, offset4); + wxLogInfo(wxT("Repeater 4 description: \"%s %s\", URL: \"%s\""), description41.c_str(), description42.c_str(), url4.c_str()); + + if (callsign1.IsSameAs(callsign4) && repeaterBand1.IsSameAs(repeaterBand4)) { + wxLogError(wxT("Repeater 4 has the same callsign and module as repeater 1, exiting")); + return false; + } + if (callsign2.IsSameAs(callsign4) && repeaterBand2.IsSameAs(repeaterBand4)) { + wxLogError(wxT("Repeater 4 has the same callsign and module as repeater 2, exiting")); + return false; + } + if (callsign3.IsSameAs(callsign4) && repeaterBand3.IsSameAs(repeaterBand4)) { + wxLogError(wxT("Repeater 4 has the same callsign and module as repeater 3, exiting")); + return false; + } + + if (repeaterType4 == HW_ICOM && !icomAddress.IsEmpty() && icomRepeaterHandler == NULL) { + icomRepeaterHandler = new CIcomRepeaterProtocolHandler(icomAddress, icomPort, repeaterAddress4, repeaterPort4); + bool res = icomRepeaterHandler->open(); + if (!res) { + wxLogError(wxT("Cannot open the Icom repeater protocol handler")); + delete icomRepeaterHandler; + icomRepeaterHandler = NULL; + } + } else if (repeaterType4 == HW_HOMEBREW && !hbAddress.IsEmpty() && hbRepeaterHandler == NULL) { + hbRepeaterHandler = new CHBRepeaterProtocolHandler(hbAddress, hbPort); + bool res = hbRepeaterHandler->open(); + if (!res) { + wxLogError(wxT("Cannot open the Homebrew repeater protocol handler")); + delete hbRepeaterHandler; + hbRepeaterHandler = NULL; + } + } else if (repeaterType4 == HW_DUMMY && !hbAddress.IsEmpty() && dummyRepeaterHandler == NULL) { + dummyRepeaterHandler = new CDummyRepeaterProtocolHandler; + bool res = dummyRepeaterHandler->open(); + if (!res) { + wxLogError(wxT("Cannot open the Dummy repeater protocol handler")); + delete dummyRepeaterHandler; + dummyRepeaterHandler = NULL; + } + } + + if (latitude4 == 0.0 && longitude4 == 0.0) { + latitude4 = latitude; + longitude4 = longitude; + } + + if (description41.IsEmpty()) + description41 = description1; + if (description42.IsEmpty()) + description42 = description2; + + if (url4.IsEmpty()) + url4 = url; + + if (repeaterType4 == HW_ICOM && icomRepeaterHandler != NULL) { + wxLogInfo(wxT("Repeater 4 bands: %u %u %u"), band41, band42, band43); + m_thread->addRepeater(callsign4, repeaterBand4, repeaterAddress4, repeaterPort4, repeaterType4, reflector4, atStartup4, reconnect4, dratsEnabled, frequency4, offset4, range4, latitude4, longitude4, agl4, description41, description42, url4, icomRepeaterHandler, band41, band42, band43); + + if (aprs != NULL) + aprs->setPort(callsign4, repeaterBand4, frequency4, offset4, range4, latitude4, longitude4, agl4); + + icomCount++; + } else if (repeaterType4 == HW_HOMEBREW && hbRepeaterHandler != NULL) { + m_thread->addRepeater(callsign4, repeaterBand4, repeaterAddress4, repeaterPort4, repeaterType4, reflector4, atStartup4, reconnect4, dratsEnabled, frequency4, offset4, range4, latitude4, longitude4, agl4, description41, description42, url4, hbRepeaterHandler); + + if (aprs != NULL) + aprs->setPort(callsign4, repeaterBand4, frequency4, offset4, range4, latitude4, longitude4, agl4); + } else if (repeaterType4 == HW_DUMMY && dummyRepeaterHandler != NULL) { + m_thread->addRepeater(callsign4, repeaterBand4, repeaterAddress4, repeaterPort4, repeaterType4, reflector4, atStartup4, reconnect4, dratsEnabled, frequency4, offset4, range4, latitude4, longitude4, agl4, description41, description42, url4, dummyRepeaterHandler); + } + } + + if (icomRepeaterHandler != NULL) + icomRepeaterHandler->setCount(icomCount); + + bool ircDDBAtLeastOneEnabled, ircDDBEnabled1, ircDDBEnabled2, ircDDBEnabled3, ircDDBEnabled4; + wxString ircDDBHostname1, ircDDBUsername1, ircDDBPassword1; + wxString ircDDBHostname2, ircDDBUsername2, ircDDBPassword2; + wxString ircDDBHostname3, ircDDBUsername3, ircDDBPassword3; + wxString ircDDBHostname4, ircDDBUsername4, ircDDBPassword4; + + config.getIrcDDB(ircDDBEnabled1, ircDDBHostname1, ircDDBUsername1, ircDDBPassword1); + wxLogInfo(wxT("ircDDB 1 enabled: %d, host: %s, username: %s"), int(ircDDBEnabled1), ircDDBHostname1.c_str(), ircDDBUsername1.c_str()); + + config.getIrcDDB2(ircDDBEnabled2, ircDDBHostname2, ircDDBUsername2, ircDDBPassword2); + wxLogInfo(wxT("ircDDB 2 enabled: %d, host: %s, username: %s"), int(ircDDBEnabled2), ircDDBHostname2.c_str(), ircDDBUsername2.c_str()); + + config.getIrcDDB3(ircDDBEnabled3, ircDDBHostname3, ircDDBUsername3, ircDDBPassword3); + wxLogInfo(wxT("ircDDB 3 enabled: %d, host: %s, username: %s"), int(ircDDBEnabled3), ircDDBHostname3.c_str(), ircDDBUsername3.c_str()); + + config.getIrcDDB4(ircDDBEnabled4, ircDDBHostname4, ircDDBUsername4, ircDDBPassword4); + wxLogInfo(wxT("ircDDB 4 enabled: %d, host: %s, username: %s"), int(ircDDBEnabled4), ircDDBHostname4.c_str(), ircDDBUsername4.c_str()); + + ircDDBAtLeastOneEnabled = ircDDBEnabled1 || ircDDBEnabled2 || ircDDBEnabled3 || ircDDBEnabled4; + + if (ircDDBAtLeastOneEnabled) { +#if defined(__WINDOWS__) + wxString versionInfo = wxT("win_") + LOG_BASE_NAME + wxT("-") + VERSION; +#else + wxString versionInfo = wxT("linux_") + LOG_BASE_NAME + wxT("-") + VERSION; +#endif + CIRCDDB_Array ircDDBClients; + if (ircDDBEnabled1) + ircDDBClients.Add(new CIRCDDBClient(ircDDBHostname1, 9007U, ircDDBUsername1, ircDDBPassword1, versionInfo, gatewayAddress)); + if (ircDDBEnabled2) + ircDDBClients.Add(new CIRCDDBClient(ircDDBHostname2, 9007U, ircDDBUsername2, ircDDBPassword2, versionInfo, gatewayAddress)); + if (ircDDBEnabled3) + ircDDBClients.Add(new CIRCDDBClient(ircDDBHostname3, 9007U, ircDDBUsername3, ircDDBPassword3, versionInfo, gatewayAddress)); + if (ircDDBEnabled4) + ircDDBClients.Add(new CIRCDDBClient(ircDDBHostname4, 9007U, ircDDBUsername4, ircDDBPassword4, versionInfo, gatewayAddress)); + + CIRCDDBMultiClient * ircDDB = new CIRCDDBMultiClient(ircDDBClients); + + bool res = ircDDB->open(); + if (!res) { + wxLogError(wxT("Cannot initialise the ircDDB protocol handler")); + ircDDBAtLeastOneEnabled = false; + } + else { + m_thread->setIRC(ircDDB); + } + } + + if (ircDDBAtLeastOneEnabled) { + wxString starNetBand1, starNetCallsign1, starNetLogoff1, starNetInfo1, starNetPermanent1, link1; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout1, starNetGroupTimeout1; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch1; + bool starNetTXMsgSwitch1; + config.getStarNet1(starNetBand1, starNetCallsign1, starNetLogoff1, starNetInfo1, starNetPermanent1, starNetUserTimeout1, starNetGroupTimeout1, starNetCallsignSwitch1, starNetTXMsgSwitch1 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,link1 +#endif + ); + + if (!starNetCallsign1.IsEmpty() && !starNetCallsign1.IsSameAs(wxT(" "))) { + wxString repeater = gatewayCallsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand1); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_thread->addStarNet(starNetCallsign1, starNetLogoff1, repeater, starNetInfo1, starNetPermanent1, starNetUserTimeout1, starNetGroupTimeout1, starNetCallsignSwitch1, starNetTXMsgSwitch1, link1); + wxLogInfo(wxT("STARnet group 1 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign1.c_str(), starNetLogoff1.c_str(), repeater.c_str(), starNetInfo1.c_str(), starNetPermanent1.c_str(), starNetUserTimeout1, starNetGroupTimeout1, int(starNetCallsignSwitch1), int(starNetTXMsgSwitch1), link1.c_str()); +#else + m_thread->addStarNet(starNetCallsign1, starNetLogoff1, repeater, starNetInfo1, starNetPermanent1, starNetUserTimeout1, starNetGroupTimeout1, starNetCallsignSwitch1, starNetTXMsgSwitch1); + wxLogInfo(wxT("STARnet group 1 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign1.c_str(), starNetLogoff1.c_str(), repeater.c_str(), starNetInfo1.c_str(), starNetPermanent1.c_str(), starNetUserTimeout1, starNetGroupTimeout1, int(starNetCallsignSwitch1), int(starNetTXMsgSwitch1)); +#endif + } + + wxString starNetBand2, starNetCallsign2, starNetLogoff2, starNetInfo2, starNetPermanent2, link2; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout2, starNetGroupTimeout2; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch2; + bool starNetTXMsgSwitch2; + config.getStarNet2(starNetBand2, starNetCallsign2, starNetLogoff2, starNetInfo2, starNetPermanent2, starNetUserTimeout2, starNetGroupTimeout2, starNetCallsignSwitch2, starNetTXMsgSwitch2 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,link2 +#endif + ); + + if (!starNetCallsign2.IsEmpty() && !starNetCallsign2.IsSameAs(wxT(" "))) { + wxString repeater = gatewayCallsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand2); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_thread->addStarNet(starNetCallsign2, starNetLogoff2, repeater, starNetInfo2, starNetPermanent2, starNetUserTimeout2, starNetGroupTimeout2, starNetCallsignSwitch2, starNetTXMsgSwitch2, link2); + wxLogInfo(wxT("STARnet group 2 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign2.c_str(), starNetLogoff2.c_str(), repeater.c_str(), starNetInfo2.c_str(), starNetPermanent2.c_str(), starNetUserTimeout2, starNetGroupTimeout2, int(starNetCallsignSwitch2), int(starNetTXMsgSwitch2), link2.c_str()); +#else + m_thread->addStarNet(starNetCallsign2, starNetLogoff2, repeater, starNetInfo2, starNetPermanent2, starNetUserTimeout2, starNetGroupTimeout2, starNetCallsignSwitch2, starNetTXMsgSwitch2); + wxLogInfo(wxT("STARnet group 2 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign2.c_str(), starNetLogoff2.c_str(), repeater.c_str(), starNetInfo2.c_str(), starNetPermanent2.c_str(), starNetUserTimeout2, starNetGroupTimeout2, int(starNetCallsignSwitch2), int(starNetTXMsgSwitch2)); +#endif + } + + wxString starNetBand3, starNetCallsign3, starNetLogoff3, starNetInfo3, starNetPermanent3, link3; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout3, starNetGroupTimeout3; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch3; + bool starNetTXMsgSwitch3; + config.getStarNet3(starNetBand3, starNetCallsign3, starNetLogoff3, starNetInfo3, starNetPermanent3, starNetUserTimeout3, starNetGroupTimeout3, starNetCallsignSwitch3, starNetTXMsgSwitch3 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,link3 +#endif + ); + + if (!starNetCallsign3.IsEmpty() && !starNetCallsign3.IsSameAs(wxT(" "))) { + wxString repeater = gatewayCallsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand3); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_thread->addStarNet(starNetCallsign3, starNetLogoff3, repeater, starNetInfo3, starNetPermanent3, starNetUserTimeout3, starNetGroupTimeout3, starNetCallsignSwitch3, starNetTXMsgSwitch3, link3); + wxLogInfo(wxT("STARnet group 3 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign3.c_str(), starNetLogoff3.c_str(), repeater.c_str(), starNetInfo3.c_str(), starNetPermanent3.c_str(), starNetUserTimeout3, starNetGroupTimeout3, int(starNetCallsignSwitch3), int(starNetTXMsgSwitch3), link3.c_str()); +#else + m_thread->addStarNet(starNetCallsign3, starNetLogoff3, repeater, starNetInfo3, starNetPermanent3, starNetUserTimeout3, starNetGroupTimeout3, starNetCallsignSwitch3, starNetTXMsgSwitch3); + wxLogInfo(wxT("STARnet group 3 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign3.c_str(), starNetLogoff3.c_str(), repeater.c_str(), starNetInfo3.c_str(), starNetPermanent3.c_str(), starNetUserTimeout3, starNetGroupTimeout3, int(starNetCallsignSwitch3), int(starNetTXMsgSwitch3)); +#endif + } + + wxString starNetBand4, starNetCallsign4, starNetLogoff4, starNetInfo4, starNetPermanent4, link4; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout4, starNetGroupTimeout4; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch4; + bool starNetTXMsgSwitch4; + config.getStarNet4(starNetBand4, starNetCallsign4, starNetLogoff4, starNetInfo4, starNetPermanent4, starNetUserTimeout4, starNetGroupTimeout4, starNetCallsignSwitch4, starNetTXMsgSwitch4 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,link4 +#endif + ); + + if (!starNetCallsign4.IsEmpty() && !starNetCallsign4.IsSameAs(wxT(" "))) { + wxString repeater = gatewayCallsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand4); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_thread->addStarNet(starNetCallsign4, starNetLogoff4, repeater, starNetInfo4, starNetPermanent4, starNetUserTimeout4, starNetGroupTimeout4, starNetCallsignSwitch4, starNetTXMsgSwitch4, link4); + wxLogInfo(wxT("STARnet group 4 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign4.c_str(), starNetLogoff4.c_str(), repeater.c_str(), starNetInfo4.c_str(), starNetPermanent4.c_str(), starNetUserTimeout4, starNetGroupTimeout4, int(starNetCallsignSwitch4), int(starNetTXMsgSwitch4), link4.c_str()); +#else + m_thread->addStarNet(starNetCallsign4, starNetLogoff4, repeater, starNetInfo4, starNetPermanent4, starNetUserTimeout4, starNetGroupTimeout4, starNetCallsignSwitch4, starNetTXMsgSwitch4); + wxLogInfo(wxT("STARnet group 4 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign4.c_str(), starNetLogoff4.c_str(), repeater.c_str(), starNetInfo4.c_str(), starNetPermanent4.c_str(), starNetUserTimeout4, starNetGroupTimeout4, int(starNetCallsignSwitch4), int(starNetTXMsgSwitch4)); +#endif + } + + wxString starNetBand5, starNetCallsign5, starNetLogoff5, starNetInfo5, starNetPermanent5, link5; // DEXTRA_LINK || DCS_LINK + unsigned int starNetUserTimeout5, starNetGroupTimeout5; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch5; + bool starNetTXMsgSwitch5; + config.getStarNet5(starNetBand5, starNetCallsign5, starNetLogoff5, starNetInfo5, starNetPermanent5, starNetUserTimeout5, starNetGroupTimeout5, starNetCallsignSwitch5, starNetTXMsgSwitch5 +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + ,link5 +#endif + ); + + if (!starNetCallsign5.IsEmpty() && !starNetCallsign5.IsSameAs(wxT(" "))) { + wxString repeater = gatewayCallsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(starNetBand5); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + m_thread->addStarNet(starNetCallsign5, starNetLogoff5, repeater, starNetInfo5, starNetPermanent5, starNetUserTimeout5, starNetGroupTimeout5, starNetCallsignSwitch5, starNetTXMsgSwitch5, link5); + wxLogInfo(wxT("STARnet group 5 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d, reflector: %s"), starNetCallsign5.c_str(), starNetLogoff5.c_str(), repeater.c_str(), starNetInfo5.c_str(), starNetPermanent5.c_str(), starNetUserTimeout5, starNetGroupTimeout5, int(starNetCallsignSwitch5), int(starNetTXMsgSwitch5), link5.c_str()); +#else + m_thread->addStarNet(starNetCallsign5, starNetLogoff5, repeater, starNetInfo5, starNetPermanent5, starNetUserTimeout5, starNetGroupTimeout5, starNetCallsignSwitch5, starNetTXMsgSwitch5); + wxLogInfo(wxT("STARnet group 5 set to %s/%s on repeater %s, info: \"%s\", permanent: %s, user: %u mins, group: %u mins, callsign switch: %d, tx msg switch: %d"), starNetCallsign5.c_str(), starNetLogoff5.c_str(), repeater.c_str(), starNetInfo5.c_str(), starNetPermanent5.c_str(), starNetUserTimeout5, starNetGroupTimeout5, int(starNetCallsignSwitch5), int(starNetTXMsgSwitch5)); +#endif + } + } + + bool dextraEnabled; + unsigned int dextraMaxDongles; + config.getDExtra(dextraEnabled, dextraMaxDongles); + wxLogInfo(wxT("DExtra enabled: %d, max. dongles: %u"), int(dextraEnabled), dextraMaxDongles); + + bool remoteEnabled; + wxString remotePassword; + unsigned int remotePort; + config.getRemote(remoteEnabled, remotePassword, remotePort); + wxLogInfo(wxT("Remote enabled: %d, port: %u"), int(remoteEnabled), remotePort); + m_thread->setRemote(remoteEnabled, remotePassword, remotePort); + + wxString dplusLogin; + unsigned int dplusMaxDongles; + bool dplusEnabled; + config.getDPlus(dplusEnabled, dplusMaxDongles, dplusLogin); + wxLogInfo(wxT("D-Plus enabled: %d, max. dongles; %u, login: %s"), int(dplusEnabled), dplusMaxDongles, dplusLogin.c_str()); + + bool dcsEnabled, ccsEnabled; + wxString ccsHost; + config.getDCS(dcsEnabled, ccsEnabled, ccsHost); + wxLogInfo(wxT("DCS enabled: %d, CCS enabled: %d, server: %s"), int(dcsEnabled), int(ccsEnabled), ccsHost.c_str()); + + bool xlxEnabled; + bool xlxOverrideLocal; + wxString xlxHostsFileUrl; + config.getXLX(xlxEnabled, xlxOverrideLocal, xlxHostsFileUrl); + wxLogInfo(wxT("XLX enabled: %d, Override Local %d, Hosts file url: %s"), int(xlxEnabled), int(xlxOverrideLocal), xlxHostsFileUrl.c_str()); + + if (repeaterBand1.Len() > 1U || repeaterBand2.Len() > 1U || + repeaterBand3.Len() > 1U || repeaterBand4.Len() > 1U) { + wxLogInfo(wxT("DD mode enabled")); + m_thread->setDDModeEnabled(true); + } + + wxFileName wlFilename(wxFileName::GetHomeDir(), PRIMARY_WHITELIST_FILE_NAME); + bool exists = wlFilename.FileExists(); + + if (!exists) { + wlFilename.Assign(wxFileName::GetHomeDir(), SECONDARY_WHITELIST_FILE_NAME); + exists = wlFilename.FileExists(); + } + + if (exists) { + CCallsignList* list = new CCallsignList(wlFilename.GetFullPath()); + bool res = list->load(); + if (!res) { + wxLogError(wxT("Unable to open the white list file - %s"), wlFilename.GetFullPath().c_str()); + delete list; + } else { + wxLogInfo(wxT("%u callsigns loaded into the white list"), list->getCount()); + m_thread->setWhiteList(list); + } + } + + wxFileName blFilename(wxFileName::GetHomeDir(), PRIMARY_BLACKLIST_FILE_NAME); + exists = blFilename.FileExists(); + + if (!exists) { + blFilename.Assign(wxFileName::GetHomeDir(), SECONDARY_BLACKLIST_FILE_NAME); + exists = blFilename.FileExists(); + } + + if (exists) { + CCallsignList* list = new CCallsignList(blFilename.GetFullPath()); + bool res = list->load(); + if (!res) { + wxLogError(wxT("Unable to open the black list file - %s"), blFilename.GetFullPath().c_str()); + delete list; + } else { + wxLogInfo(wxT("%u callsigns loaded into the black list"), list->getCount()); + m_thread->setBlackList(list); + } + } + + wxFileName rlFilename(wxFileName::GetHomeDir(), RESTRICT_FILE_NAME); + exists = rlFilename.FileExists(); + if (exists) { + CCallsignList* list = new CCallsignList(rlFilename.GetFullPath()); + bool res = list->load(); + if (!res) { + wxLogError(wxT("Unable to open the restrict list file - %s"), rlFilename.GetFullPath().c_str()); + delete list; + } else { + wxLogInfo(wxT("%u callsigns loaded into the restrict list"), list->getCount()); + m_thread->setRestrictList(list); + } + } + m_thread->setIcomRepeaterHandler(icomRepeaterHandler); + m_thread->setHBRepeaterHandler(hbRepeaterHandler); + m_thread->setDummyRepeaterHandler(dummyRepeaterHandler); + m_thread->setLanguage(language); + m_thread->setDPlus(dplusEnabled, dplusMaxDongles, dplusLogin); + m_thread->setDExtra(dextraEnabled, dextraMaxDongles); + m_thread->setDCS(dcsEnabled); + m_thread->setCCS(ccsEnabled, ccsHost); + m_thread->setXLX(xlxEnabled, xlxOverrideLocal, xlxEnabled ? CXLXHostsFileDownloader::Download(xlxHostsFileUrl): wxString(wxEmptyString)); + m_thread->setInfoEnabled(infoEnabled); + m_thread->setEchoEnabled(echoEnabled); + m_thread->setDTMFEnabled(dtmfEnabled); + m_thread->setLog(logEnabled); + m_thread->setLocation(latitude, longitude); + + return true; +} + +void CIRCDDBGatewayAppD::kill() +{ + wxASSERT(m_thread != NULL); + + m_thread->kill(); +} diff --git a/ircDDBGateway/IRCDDBGatewayAppD.h b/ircDDBGateway/IRCDDBGatewayAppD.h new file mode 100644 index 0000000..bee1aa4 --- /dev/null +++ b/ircDDBGateway/IRCDDBGatewayAppD.h @@ -0,0 +1,51 @@ +/* + * 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 + * 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 IRCDDBGatewayAppD_H +#define IRCDDBGatewayAppD_H + +#include "IRCDDBGatewayThread.h" + +#include +#include +#include + +class CIRCDDBGatewayAppD { + +public: + CIRCDDBGatewayAppD(bool nolog, const wxString& logDir, const wxString& confDir, const wxString& name); + ~CIRCDDBGatewayAppD(); + + bool init(); + + void run(); + + void kill(); + +private: + wxString m_name; + bool m_nolog; + wxString m_logDir; + wxString m_confDir; + CIRCDDBGatewayThread* m_thread; + wxSingleInstanceChecker* m_checker; + + bool createThread(); +}; + +#endif diff --git a/ircDDBGateway/IRCDDBGatewayDefs.h b/ircDDBGateway/IRCDDBGatewayDefs.h new file mode 100644 index 0000000..9eac8a0 --- /dev/null +++ b/ircDDBGateway/IRCDDBGatewayDefs.h @@ -0,0 +1,49 @@ +/* + * 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 + * 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 IRCDDBGatewayDefs_H +#define IRCDDBGatewayDefs_H + +#include + +const wxString APPLICATION_NAME = wxT("ircDDB Gateway"); + +const wxString CONFIG_FILE_NAME = wxT("ircddbgateway"); + +const wxString STATUS1_FILE_NAME = wxT("status1.txt"); +const wxString STATUS2_FILE_NAME = wxT("status2.txt"); +const wxString STATUS3_FILE_NAME = wxT("status3.txt"); +const wxString STATUS4_FILE_NAME = wxT("status4.txt"); +const wxString STATUS5_FILE_NAME = wxT("status5.txt"); + +const wxString PRIMARY_WHITELIST_FILE_NAME = wxT("gw_whitelist.dat"); +const wxString SECONDARY_WHITELIST_FILE_NAME = wxT("whitelist.dat"); +const wxString PRIMARY_BLACKLIST_FILE_NAME = wxT("gw_blacklist.dat"); +const wxString SECONDARY_BLACKLIST_FILE_NAME = wxT("blacklist.dat"); +const wxString RESTRICT_FILE_NAME = wxT("restrict.txt"); + +const unsigned int MAX_OUTGOING = 6U; +const unsigned int MAX_REPEATERS = 4U; +const unsigned int MAX_DEXTRA_LINKS = 5U; +const unsigned int MAX_DPLUS_LINKS = 5U; +const unsigned int MAX_DCS_LINKS = 5U; +const unsigned int MAX_STARNETS = 5U; +const unsigned int MAX_ROUTES = MAX_REPEATERS + 5U; +const unsigned int MAX_DD_ROUTES = 20U; + +#endif diff --git a/ircDDBGateway/IRCDDBGatewayFrame.cpp b/ircDDBGateway/IRCDDBGatewayFrame.cpp new file mode 100644 index 0000000..6fe3c7d --- /dev/null +++ b/ircDDBGateway/IRCDDBGatewayFrame.cpp @@ -0,0 +1,322 @@ +/* + * 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 + * 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 "IRCDDBGatewayStatusData.h" +#include "IRCDDBGatewayFrame.h" +#include "IRCDDBGatewayDefs.h" +#include "IRCDDBGatewayApp.h" +#include "LogEvent.h" +#include "Version.h" + +const unsigned int BORDER_SIZE = 5U; + +#if defined(__WINDOWS__) +const unsigned int IRCDDB_WIDTH = 230U; +const unsigned int LINKED_WIDTH = 160U; +const unsigned int INCOMING_WIDTH = 320U; +const unsigned int DONGLES_WIDTH = 560U; +const unsigned int LOGTEXT_WIDTH = 560U; +#else +const unsigned int IRCDDB_WIDTH = 288U; +const unsigned int LINKED_WIDTH = 220U; +const unsigned int INCOMING_WIDTH = 385U; +const unsigned int DONGLES_WIDTH = 700U; +const unsigned int LOGTEXT_WIDTH = 700U; +#endif + +#include +#include + +DEFINE_EVENT_TYPE(LOG_EVENT) + +enum { + Menu_Edit_Preferences = 6000, + Menu_View_Updates +}; + +BEGIN_EVENT_TABLE(CIRCDDBGatewayFrame, wxFrame) + EVT_MENU(wxID_EXIT, CIRCDDBGatewayFrame::onQuit) + EVT_MENU(Menu_View_Updates, CIRCDDBGatewayFrame::onUpdates) + EVT_MENU(wxID_ABOUT, CIRCDDBGatewayFrame::onAbout) + + EVT_TIMER(-1, CIRCDDBGatewayFrame::onTimer) + + EVT_CLOSE(CIRCDDBGatewayFrame::onClose) + + EVT_CUSTOM(LOG_EVENT, wxID_ANY, CIRCDDBGatewayFrame::onLog) +END_EVENT_TABLE() + +CIRCDDBGatewayFrame::CIRCDDBGatewayFrame(const wxString& title, const wxPoint& position, bool gui) : +wxFrame(NULL, -1, title, position), +m_timer(this), +m_ircDDBStatus(NULL), +m_dprsStatus(NULL), +#if defined(__WXDEBUG__) +m_updates(true) +#else +m_updates(gui) +#endif +{ + SetMenuBar(createMenuBar()); + + wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); + + wxPanel* panel = new wxPanel(this); + + wxBoxSizer* panelSizer = new wxBoxSizer(wxVERTICAL); + + wxStaticBoxSizer* status1Sizer = new wxStaticBoxSizer(new wxStaticBox(panel, -1, _("Status")), wxVERTICAL); + wxFlexGridSizer* status2Sizer = new wxFlexGridSizer(6); + + wxStaticText* ircDDBStatusLabel = new wxStaticText(panel, -1, _("ircDDB:")); + status2Sizer->Add(ircDDBStatusLabel, 0, wxALL, BORDER_SIZE); + + m_ircDDBStatus = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(IRCDDB_WIDTH, -1)); + status2Sizer->Add(m_ircDDBStatus, 0, wxALL, BORDER_SIZE); + + wxStaticText* dprsStatusLabel = new wxStaticText(panel, -1, _("D-PRS:")); + status2Sizer->Add(dprsStatusLabel, 0, wxALL, BORDER_SIZE); + + m_dprsStatus = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(IRCDDB_WIDTH, -1)); + status2Sizer->Add(m_dprsStatus, 0, wxALL, BORDER_SIZE); + + status1Sizer->Add(status2Sizer); + panelSizer->Add(status1Sizer, 0, wxALL, BORDER_SIZE); + + wxStaticBoxSizer* links1Sizer = new wxStaticBoxSizer(new wxStaticBox(panel, -1, _("Links")), wxVERTICAL); + wxFlexGridSizer* links2Sizer = new wxFlexGridSizer(3); + + for (unsigned int i = 0U; i < 4U; i++) { + wxString text; + text.Printf(_("Repeater %u:"), i + 1U); + + wxStaticText* repeaterLabel = new wxStaticText(panel, -1, text); + links2Sizer->Add(repeaterLabel, 0, wxALL, BORDER_SIZE); + + m_linkStatus[i] = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(LINKED_WIDTH, -1)); + links2Sizer->Add(m_linkStatus[i], 0, wxALL, BORDER_SIZE); + + m_incoming[i] = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(INCOMING_WIDTH, -1)); + links2Sizer->Add(m_incoming[i], 0, wxALL, BORDER_SIZE); + } + + links1Sizer->Add(links2Sizer); + panelSizer->Add(links1Sizer, 0, wxALL, BORDER_SIZE); + + wxStaticBoxSizer* dongles1Sizer = new wxStaticBoxSizer(new wxStaticBox(panel, -1, _("Dongles")), wxVERTICAL); + wxFlexGridSizer* dongles2Sizer = new wxFlexGridSizer(5); + + m_dongles = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(DONGLES_WIDTH, -1)); + dongles2Sizer->Add(m_dongles, 0, wxALL, BORDER_SIZE); + + dongles1Sizer->Add(dongles2Sizer); + panelSizer->Add(dongles1Sizer, 0, wxALL, BORDER_SIZE); + + wxStaticBoxSizer* log1Sizer = new wxStaticBoxSizer(new wxStaticBox(panel, -1, _("Log")), wxVERTICAL); + wxBoxSizer* log2Sizer = new wxBoxSizer(wxVERTICAL); + + for (unsigned int i = 0U; i < 7U; i++) { + m_logLine[i] = new wxStaticText(panel, -1, wxEmptyString, wxDefaultPosition, wxSize(LOGTEXT_WIDTH, -1)); + m_logLine[i]->Wrap(LOGTEXT_WIDTH); + log2Sizer->Add(m_logLine[i], 0, wxTOP | wxLEFT | wxRIGHT, BORDER_SIZE); + } + + log1Sizer->Add(log2Sizer); + panelSizer->Add(log1Sizer, 0, wxALL, BORDER_SIZE); + + panel->SetSizer(panelSizer); + panelSizer->SetSizeHints(panel); + + mainSizer->Add(panel); + + SetSizer(mainSizer); + mainSizer->SetSizeHints(this); + + m_timer.Start(5000); +} + +CIRCDDBGatewayFrame::~CIRCDDBGatewayFrame() +{ +} + +wxMenuBar* CIRCDDBGatewayFrame::createMenuBar() +{ + wxMenu* fileMenu = new wxMenu(); + fileMenu->Append(wxID_EXIT, _("Exit")); + + wxMenu* viewMenu = new wxMenu(); + viewMenu->AppendCheckItem(Menu_View_Updates, _("GUI Updates")); + viewMenu->Check(Menu_View_Updates, m_updates); + + wxMenu* helpMenu = new wxMenu(); + helpMenu->Append(wxID_ABOUT, _("About ircDDB Gateway")); + + wxMenuBar* menuBar = new wxMenuBar(); + menuBar->Append(fileMenu, _("File")); + menuBar->Append(viewMenu, _("View")); + menuBar->Append(helpMenu, _("Help")); + + return menuBar; +} + +void CIRCDDBGatewayFrame::onQuit(wxCommandEvent&) +{ + Close(false); +} + +void CIRCDDBGatewayFrame::onClose(wxCloseEvent&) +{ + int x, y; + GetPosition(&x, &y); + if (x >= 0 && y >= 0) + ::wxGetApp().setPosition(x, y); + + Destroy(); +} + +void CIRCDDBGatewayFrame::onUpdates(wxCommandEvent& event) +{ + m_updates = event.IsChecked(); +} + +void CIRCDDBGatewayFrame::onAbout(wxCommandEvent&) +{ + wxAboutDialogInfo info; + info.AddDeveloper(wxT("Jonathan Naylor, G4KLX")); + info.AddDeveloper(wxT("Michael Dirska, DL1BFF")); + info.SetCopyright(wxT("(C) 2010-2015 using GPL v2 or later")); + info.SetName(APPLICATION_NAME); + info.SetVersion(VERSION); + info.SetDescription(_("This program allows a computer running homebrew repeaters\nto access the ircDDB network for G2 callsign and repeater routing,\nas well as D-Plus and DExtra reflectors. It includes a StarNet\nDigital server.")); + + ::wxAboutBox(info); +} + +void CIRCDDBGatewayFrame::onLog(wxEvent& event) +{ + CLogEvent& logEvent = dynamic_cast(event); + + wxString text; + + text = m_logLine[1U]->GetLabel(); + m_logLine[0U]->SetLabel(text); + + text = m_logLine[2U]->GetLabel(); + m_logLine[1U]->SetLabel(text); + + text = m_logLine[3U]->GetLabel(); + m_logLine[2U]->SetLabel(text); + + text = m_logLine[4U]->GetLabel(); + m_logLine[3U]->SetLabel(text); + + text = m_logLine[5U]->GetLabel(); + m_logLine[4U]->SetLabel(text); + + text = m_logLine[6U]->GetLabel(); + m_logLine[5U]->SetLabel(text); + + text = logEvent.getText(); + m_logLine[6U]->SetLabel(text); +} + +void CIRCDDBGatewayFrame::showLog(const wxString& text) +{ + if (!m_updates) + return; + + CLogEvent event(text, LOG_EVENT); + + AddPendingEvent(event); +} + +void CIRCDDBGatewayFrame::onTimer(wxTimerEvent&) +{ + if (!m_updates) + return; + + CIRCDDBGatewayStatusData* status = ::wxGetApp().getStatus(); + if (status == NULL) + return; + + IRCDDB_STATUS ircDDBStatus = status->getIrcDDBStatus(); + switch (ircDDBStatus) { + case IS_DISABLED: + m_ircDDBStatus->SetLabel(_("Disabled")); + break; + case IS_DISCONNECTED: + m_ircDDBStatus->SetLabel(_("Disconnected")); + break; + case IS_CONNECTING: + m_ircDDBStatus->SetLabel(_("Connecting")); + break; + default: + m_ircDDBStatus->SetLabel(_("Connected")); + break; + } + + bool dprsStatus = status->getDPRSStatus(); + if (dprsStatus) { + if (ircDDBStatus == IS_CONNECTED || ircDDBStatus == IS_DISABLED) + m_dprsStatus->SetLabel(_("Active")); + else + m_dprsStatus->SetLabel(_("Waiting")); + } else { + m_dprsStatus->SetLabel(_("Inactive")); + } + + for (unsigned int i = 0U; i < 4U; i++) { + wxString callsign = status->getCallsign(i); + if (!callsign.IsEmpty()) { + wxString text = callsign; + + wxString linkCallsign = status->getLinkCallsign(i); + + switch (status->getLinkStatus(i)) { + case LS_NONE: + text.Append(_(" Not linked")); + break; + case LS_LINKED_LOOPBACK: + case LS_LINKED_DEXTRA: + case LS_LINKED_DPLUS: + case LS_LINKED_DCS: + case LS_LINKED_CCS: + text.Append(_(" Linked to ")); + text.Append(linkCallsign); + break; + default: + text.Append(_(" Linking to ")); + text.Append(linkCallsign); + break; + } + m_linkStatus[i]->SetLabel(text); + + wxString incoming = status->getIncoming(i); + if (!incoming.IsEmpty()) { + incoming.Prepend(wxT("[ ")); + incoming.Append(wxT("]")); + } + m_incoming[i]->SetLabel(incoming); + } + } + + wxString dongles = status->getDongles(); + m_dongles->SetLabel(dongles); + + delete status; +} diff --git a/ircDDBGateway/IRCDDBGatewayFrame.h b/ircDDBGateway/IRCDDBGatewayFrame.h new file mode 100644 index 0000000..2d0b7dc --- /dev/null +++ b/ircDDBGateway/IRCDDBGatewayFrame.h @@ -0,0 +1,56 @@ +/* + * 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 + * 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 IRCDDBGatewayFrame_H +#define IRCDDBGatewayFrame_H + +#include "Defs.h" + +#include +#include + +class CIRCDDBGatewayFrame : public wxFrame { +public: + CIRCDDBGatewayFrame(const wxString& title, const wxPoint& position, bool gui); + virtual ~CIRCDDBGatewayFrame(); + + virtual void onQuit(wxCommandEvent& event); + virtual void onUpdates(wxCommandEvent& event); + virtual void onAbout(wxCommandEvent& event); + virtual void onClose(wxCloseEvent& event); + virtual void onLog(wxEvent& event); + virtual void onTimer(wxTimerEvent& event); + + virtual void showLog(const wxString& text); + +private: + wxTimer m_timer; + wxStaticText* m_ircDDBStatus; + wxStaticText* m_dprsStatus; + wxStaticText* m_linkStatus[4]; + wxStaticText* m_incoming[4]; + wxStaticText* m_dongles; + wxStaticText* m_logLine[7]; + bool m_updates; + + DECLARE_EVENT_TABLE() + + wxMenuBar* createMenuBar(); +}; + +#endif diff --git a/ircDDBGateway/IRCDDBGatewayLogRedirect.cpp b/ircDDBGateway/IRCDDBGatewayLogRedirect.cpp new file mode 100644 index 0000000..357a59a --- /dev/null +++ b/ircDDBGateway/IRCDDBGatewayLogRedirect.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2002,2003,2009-2013,2018 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 "IRCDDBGatewayLogRedirect.h" +#include "IRCDDBGatewayApp.h" + +CIRCDDBGatewayLogRedirect::CIRCDDBGatewayLogRedirect() : +wxLog() +{ +} + +CIRCDDBGatewayLogRedirect::~CIRCDDBGatewayLogRedirect() +{ +} + +void CIRCDDBGatewayLogRedirect::DoLogRecord(wxLogLevel level, const wxString& msg, const wxLogRecordInfo& info) +{ + wxString letter; + + switch (level) { + case wxLOG_FatalError: letter = wxT("F"); break; + case wxLOG_Error: letter = wxT("E"); break; + case wxLOG_Warning: letter = wxT("W"); break; + case wxLOG_Info: letter = wxT("I"); break; + case wxLOG_Message: letter = wxT("M"); break; + case wxLOG_Status: letter = wxT("M"); break; + case wxLOG_Trace: letter = wxT("T"); break; + case wxLOG_Debug: letter = wxT("D"); break; + default: letter = wxT("U"); break; + } + + struct tm* tm = ::gmtime(&info.timestamp); + + wxString message; + message.Printf(wxT("%s: %04d-%02d-%02d %02d:%02d:%02d: %s\n"), letter.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, msg.c_str()); + + ::wxGetApp().showLog(message); + + if (level == wxLOG_FatalError) + ::abort(); +} diff --git a/ircDDBGateway/IRCDDBGatewayLogRedirect.h b/ircDDBGateway/IRCDDBGatewayLogRedirect.h new file mode 100644 index 0000000..a7d0d16 --- /dev/null +++ b/ircDDBGateway/IRCDDBGatewayLogRedirect.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2002,2003,2009-2011,2018 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 IRCDDBGatewayLogRedirect_H +#define IRCDDBGatewayLogRedirect_H + +#include +#include + +class CIRCDDBGatewayLogRedirect : public wxLog { +public: + CIRCDDBGatewayLogRedirect(); + virtual ~CIRCDDBGatewayLogRedirect(); + + virtual void DoLogRecord(wxLogLevel level, const wxString& msg, const wxLogRecordInfo& info); + +private: +}; + +#endif diff --git a/ircDDBGateway/IRCDDBGatewayStatusData.cpp b/ircDDBGateway/IRCDDBGatewayStatusData.cpp new file mode 100644 index 0000000..17bf722 --- /dev/null +++ b/ircDDBGateway/IRCDDBGatewayStatusData.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2010,2011,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 "IRCDDBGatewayStatusData.h" + +CIRCDDBGatewayStatusData::CIRCDDBGatewayStatusData(IRCDDB_STATUS ircDDBStatus, bool dprsStatus) : +m_ircDDBStatus(ircDDBStatus), +m_dprsStatus(dprsStatus) +{ +} + +CIRCDDBGatewayStatusData::~CIRCDDBGatewayStatusData() +{ +} + +void CIRCDDBGatewayStatusData::setRepeater(unsigned int n, const wxString& callsign, LINK_STATUS linkStatus, const wxString& linkCallsign, const wxString& incoming) +{ + wxASSERT(n < 4U); + + m_callsign[n] = callsign; + m_linkStatus[n] = linkStatus; + m_linkCallsign[n] = linkCallsign; + m_incoming[n] = incoming; +} + +void CIRCDDBGatewayStatusData::setDongles(const wxString& dongles) +{ + m_dongles = dongles; +} + +IRCDDB_STATUS CIRCDDBGatewayStatusData::getIrcDDBStatus() const +{ + return m_ircDDBStatus; +} + +bool CIRCDDBGatewayStatusData::getDPRSStatus() const +{ + return m_dprsStatus; +} + +wxString CIRCDDBGatewayStatusData::getCallsign(unsigned int n) const +{ + wxASSERT(n < 4U); + + return m_callsign[n]; +} + +LINK_STATUS CIRCDDBGatewayStatusData::getLinkStatus(unsigned int n) const +{ + wxASSERT(n < 4U); + + return m_linkStatus[n]; +} + +wxString CIRCDDBGatewayStatusData::getLinkCallsign(unsigned int n) const +{ + wxASSERT(n < 4U); + + return m_linkCallsign[n]; +} + +wxString CIRCDDBGatewayStatusData::getIncoming(unsigned int n) const +{ + wxASSERT(n < 4U); + + return m_incoming[n]; +} + +wxString CIRCDDBGatewayStatusData::getDongles() const +{ + return m_dongles; +} diff --git a/ircDDBGateway/IRCDDBGatewayStatusData.h b/ircDDBGateway/IRCDDBGatewayStatusData.h new file mode 100644 index 0000000..d308337 --- /dev/null +++ b/ircDDBGateway/IRCDDBGatewayStatusData.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2010,2011,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 IRCDDBGatewayStatusData_H +#define IRCDDBGatewayStatusData_H + +#include "Defs.h" + +#include + +class CIRCDDBGatewayStatusData { +public: + CIRCDDBGatewayStatusData(IRCDDB_STATUS ircDDBStatus, bool dprsStatus); + ~CIRCDDBGatewayStatusData(); + + void setRepeater(unsigned int n, const wxString& callsign, LINK_STATUS linkStatus, const wxString& linkCallsign, const wxString& incoming); + + void setDongles(const wxString& dongles); + + IRCDDB_STATUS getIrcDDBStatus() const; + bool getDPRSStatus() const; + + wxString getCallsign(unsigned int n) const; + LINK_STATUS getLinkStatus(unsigned int n) const; + wxString getLinkCallsign(unsigned int n) const; + wxString getIncoming(unsigned int n) const; + + wxString getDongles() const; + +private: + IRCDDB_STATUS m_ircDDBStatus; + bool m_dprsStatus; + wxString m_callsign[4]; + LINK_STATUS m_linkStatus[4]; + wxString m_linkCallsign[4]; + wxString m_incoming[4]; + wxString m_dongles; +}; + +#endif diff --git a/ircDDBGateway/IRCDDBGatewayThread.cpp b/ircDDBGateway/IRCDDBGatewayThread.cpp new file mode 100644 index 0000000..9b9f830 --- /dev/null +++ b/ircDDBGateway/IRCDDBGatewayThread.cpp @@ -0,0 +1,1389 @@ +/* + * 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 + * 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 "IRCDDBGatewayThread.h" +#include "IRCDDBGatewayDefs.h" +#include "RepeaterHandler.h" +#include "StarNetHandler.h" +#include "CallsignServer.h" +#include "DExtraHandler.h" +#include "DPlusHandler.h" +#include "HeaderLogger.h" +#include "ConnectData.h" +#include "CCSHandler.h" +#include "HeaderData.h" +#include "StatusData.h" +#include "DCSHandler.h" +#include "DDHandler.h" +#include "G2Handler.h" +#include "HeardData.h" +#include "PollData.h" +#include "AMBEData.h" +#include "HostFile.h" +#include "CCSData.h" +#include "DDData.h" +#include "Utils.h" +#include "Defs.h" + +#include +#include +#include +#include +#include + + +#if defined(__WINDOWS__) +#include "Inaddr.h" +#else +#include +#endif + +const wxString LOOPBACK_ADDRESS = wxT("127.0.0.1"); + +const unsigned int REMOTE_DUMMY_PORT = 65016U; + +CIRCDDBGatewayThread::CIRCDDBGatewayThread(const wxString& logDir, const wxString& name) : +m_logDir(logDir), +m_name(name), +m_killed(false), +m_stopped(true), +m_gatewayType(GT_REPEATER), +m_gatewayCallsign(), +m_gatewayAddress(), +m_icomRepeaterHandler(NULL), +m_hbRepeaterHandler(NULL), +m_dummyRepeaterHandler(NULL), +m_dextraPool(NULL), +m_dplusPool(NULL), +m_dcsPool(NULL), +m_g2Handler(NULL), +m_aprsWriter(NULL), +m_irc(NULL), +m_cache(), +m_language(TL_ENGLISH_UK), +m_dextraEnabled(true), +m_dextraMaxDongles(0U), +m_dplusEnabled(false), +m_dplusMaxDongles(0U), +m_dplusLogin(), +m_dcsEnabled(true), +m_xlxEnabled(true), +m_xlxHostsFileName(), +m_ccsEnabled(true), +m_ccsHost(), +m_infoEnabled(true), +m_echoEnabled(true), +m_dtmfEnabled(true), +m_logEnabled(false), +m_ddModeEnabled(false), +m_lastStatus(IS_DISABLED), +m_statusTimer1(1000U, 1U), // 1 second +m_statusTimer2(1000U, 1U), // 1 second +m_remoteEnabled(false), +m_remotePassword(), +m_remotePort(0U), +m_remote(NULL), +m_statusFileTimer(1000U, 2U * 60U), // 2 minutes +m_status1(), +m_status2(), +m_status3(), +m_status4(), +m_status5(), +m_latitude(0.0), +m_longitude(0.0), +m_whiteList(NULL), +m_blackList(NULL), +m_restrictList(NULL) +{ + CHeaderData::initialise(); + CG2Handler::initialise(MAX_ROUTES); + CDExtraHandler::initialise(MAX_DEXTRA_LINKS); + CDPlusHandler::initialise(MAX_DPLUS_LINKS); + CDCSHandler::initialise(MAX_DCS_LINKS); + CRepeaterHandler::initialise(MAX_REPEATERS); + CStarNetHandler::initialise(MAX_STARNETS, m_name); + CCCSHandler::initialise(MAX_REPEATERS); + CAudioUnit::initialise(); +} + +CIRCDDBGatewayThread::~CIRCDDBGatewayThread() +{ + CHeaderData::finalise(); + CG2Handler::finalise(); + CDExtraHandler::finalise(); + CDPlusHandler::finalise(); + CDCSHandler::finalise(); + CRepeaterHandler::finalise(); + CStarNetHandler::finalise(); + CCCSHandler::finalise(); + CAudioUnit::finalise(); +} + +void CIRCDDBGatewayThread::run() +{ + // Truncate the old Links.log file + wxString fullName = LINKS_BASE_NAME; + + if (!m_name.IsEmpty()) { + fullName.Append(wxT("_")); + fullName.Append(m_name); + } + + wxFileName fileName1(m_logDir, fullName, wxT("log")); + wxLogMessage(wxT("Truncating %s"), fileName1.GetFullPath().c_str()); + + wxFFile file; + bool ret = file.Open(fileName1.GetFullPath(), wxT("wt")); + if (ret) + file.Close(); + + // Truncate the old StarNet.log file + fullName = STARNET_BASE_NAME; + + if (!m_name.IsEmpty()) { + fullName.Append(wxT("_")); + fullName.Append(m_name); + } + + wxFileName fileName2(m_logDir, fullName, wxT("log")); + wxLogMessage(wxT("Truncating %s"), fileName2.GetFullPath().c_str()); + + ret = file.Open(fileName2.GetFullPath(), wxT("wt")); + if (ret) + file.Close(); + + wxString dextraAddress = m_dextraEnabled ? m_gatewayAddress : LOOPBACK_ADDRESS; + m_dextraPool = new CDExtraProtocolHandlerPool(MAX_OUTGOING + 1U, DEXTRA_PORT, dextraAddress); + ret = m_dextraPool->open(); + if (!ret) { + wxLogError(wxT("Could not open the DExtra protocol pool")); + delete m_dextraPool; + m_dextraPool = NULL; + } else { + // Allocate the incoming port + CDExtraProtocolHandler* handler = m_dextraPool->getHandler(DEXTRA_PORT); + CDExtraHandler::setDExtraProtocolIncoming(handler); + CDExtraHandler::setDExtraProtocolHandlerPool(m_dextraPool); + } + + wxString dplusAddress = m_dplusEnabled ? m_gatewayAddress : LOOPBACK_ADDRESS; + m_dplusPool = new CDPlusProtocolHandlerPool(MAX_OUTGOING + 1U, DPLUS_PORT, dplusAddress); + ret = m_dplusPool->open(); + if (!ret) { + wxLogError(wxT("Could not open the D-Plus protocol pool")); + delete m_dplusPool; + m_dplusPool = NULL; + } else { + // Allocate the incoming port + CDPlusProtocolHandler* handler = m_dplusPool->getHandler(DPLUS_PORT); + CDPlusHandler::setDPlusProtocolIncoming(handler); + CDPlusHandler::setDPlusProtocolHandlerPool(m_dplusPool); + } + + wxString dcsAddress = m_dcsEnabled ? m_gatewayAddress : LOOPBACK_ADDRESS; + m_dcsPool = new CDCSProtocolHandlerPool(MAX_OUTGOING + 1U, DCS_PORT, dcsAddress); + ret = m_dcsPool->open(); + if (!ret) { + wxLogError(wxT("Could not open the DCS protocol pool")); + delete m_dcsPool; + m_dcsPool = NULL; + } else { + // Allocate the incoming port + CDCSProtocolHandler* handler = m_dcsPool->getHandler(DCS_PORT); + CDCSHandler::setDCSProtocolIncoming(handler); + CDCSHandler::setDCSProtocolHandlerPool(m_dcsPool); + } + + m_g2Handler = new CG2ProtocolHandler(G2_DV_PORT, m_gatewayAddress); + ret = m_g2Handler->open(); + if (!ret) { + wxLogError(wxT("Could not open the G2 protocol handler")); + delete m_g2Handler; + m_g2Handler = NULL; + } + + // Wait here until we have the essentials to run + while (!m_killed && (m_dextraPool == NULL || m_dplusPool == NULL || m_dcsPool == NULL || m_g2Handler == NULL || (m_icomRepeaterHandler == NULL && m_hbRepeaterHandler == NULL && m_dummyRepeaterHandler == NULL) || m_gatewayCallsign.IsEmpty())) + ::wxMilliSleep(500UL); // 1/2 sec + + if (m_killed) + return; + + m_stopped = false; + + wxLogMessage(wxT("Starting the ircDDB Gateway thread")); + + CHeaderLogger* headerLogger = NULL; + if (m_logEnabled) { + m_statusTimer1.start(); + + headerLogger = new CHeaderLogger(m_logDir, m_name); + ret = headerLogger->open(); + if (!ret) { + delete headerLogger; + headerLogger = NULL; + } + } + + loadGateways(); + loadReflectors(); + + CG2Handler::setG2ProtocolHandler(m_g2Handler); + CG2Handler::setHeaderLogger(headerLogger); + + CDExtraHandler::setCallsign(m_gatewayCallsign); + CDExtraHandler::setHeaderLogger(headerLogger); + CDExtraHandler::setMaxDongles(m_dextraMaxDongles); + + CDPlusHandler::setCallsign(m_gatewayCallsign); + CDPlusHandler::setDPlusLogin(m_dplusLogin); + CDPlusHandler::setHeaderLogger(headerLogger); + CDPlusHandler::setMaxDongles(m_dplusMaxDongles); + if (m_dplusEnabled) + CDPlusHandler::startAuthenticator(m_gatewayAddress, &m_cache); + + CDCSHandler::setGatewayType(m_gatewayType); + CDCSHandler::setHeaderLogger(headerLogger); + + CRepeaterHandler::setLocalAddress(m_gatewayAddress); + CRepeaterHandler::setG2Handler(m_g2Handler); + + if (m_irc != NULL) + CRepeaterHandler::setIRC(m_irc); + + CRepeaterHandler::setCache(&m_cache); + CRepeaterHandler::setGateway(m_gatewayCallsign); + CRepeaterHandler::setLanguage(m_language); + CRepeaterHandler::setDExtraEnabled(m_dextraEnabled); + CRepeaterHandler::setDPlusEnabled(m_dplusEnabled); + CRepeaterHandler::setDCSEnabled(m_dcsEnabled); + CRepeaterHandler::setHeaderLogger(headerLogger); + CRepeaterHandler::setAPRSWriter(m_aprsWriter); + CRepeaterHandler::setInfoEnabled(m_infoEnabled); + CRepeaterHandler::setEchoEnabled(m_echoEnabled); + CRepeaterHandler::setDTMFEnabled(m_dtmfEnabled); + if (m_whiteList != NULL) { + CDExtraHandler::setWhiteList(m_whiteList); + CDPlusHandler::setWhiteList(m_whiteList); + CDCSHandler::setWhiteList(m_whiteList); + } + if (m_blackList != NULL) { + CDExtraHandler::setBlackList(m_blackList); + CDPlusHandler::setBlackList(m_blackList); + CDCSHandler::setBlackList(m_blackList); + } + if (m_restrictList != NULL) + CRepeaterHandler::setRestrictList(m_restrictList); + + CAudioUnit::setLanguage(m_language); + + CStarNetHandler::setCache(&m_cache); + CStarNetHandler::setGateway(m_gatewayCallsign); + CStarNetHandler::setG2Handler(m_g2Handler); + + if (m_irc != NULL) + CStarNetHandler::setIRC(m_irc); + + CStarNetHandler::setLogging(m_logEnabled, m_logDir); +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + CStarNetHandler::link(); +#endif + + if (m_ddModeEnabled) { + CDDHandler::initialise(MAX_DD_ROUTES, m_name); + CDDHandler::setLogging(m_logEnabled, m_logDir); + CDDHandler::setHeaderLogger(headerLogger); + + if (m_irc != NULL) + CDDHandler::setIRC(m_irc); + } + + wxString ccsAddress = m_ccsEnabled ? m_gatewayAddress : LOOPBACK_ADDRESS; + CCCSHandler::setLocalAddress(ccsAddress); + CCCSHandler::setHeaderLogger(headerLogger); + CCCSHandler::setHost(m_ccsHost); + + // If no ircDDB then APRS is started immediately + if (m_aprsWriter != NULL && m_irc == NULL) + m_aprsWriter->setEnabled(true); + + if (m_remoteEnabled && !m_remotePassword.IsEmpty() && m_remotePort > 0U) { + m_remote = new CRemoteHandler(m_remotePassword, m_remotePort, m_gatewayAddress); + bool res = m_remote->open(); + if (!res) { + delete m_remote; + m_remote = NULL; + } + } + + CRepeaterHandler::startup(); + + m_statusFileTimer.start(); + + CCallsignServer* server = NULL; + if (m_dextraEnabled || m_dcsEnabled) { + server = new CCallsignServer(m_gatewayCallsign, m_gatewayAddress, &m_cache); + server->start(); + } + + wxStopWatch stopWatch; + stopWatch.Start(); + + m_statusTimer2.start(); + + try { + while (!m_killed) { + if (m_icomRepeaterHandler != NULL) + processRepeater(m_icomRepeaterHandler); + + if (m_hbRepeaterHandler != NULL) + processRepeater(m_hbRepeaterHandler); + + if (m_dummyRepeaterHandler != NULL) + processRepeater(m_dummyRepeaterHandler); + + if (m_irc != NULL) + processIrcDDB(); + + processDExtra(); + processDPlus(); + processDCS(); + processG2(); + CCCSHandler::process(); + + if (m_ddModeEnabled) + processDD(); + + if (m_remote != NULL) + m_remote->process(); + + unsigned long ms = stopWatch.Time(); + stopWatch.Start(); + + CRepeaterHandler::clock(ms); + CG2Handler::clock(ms); + CDExtraHandler::clock(ms); + CDPlusHandler::clock(ms); + CDCSHandler::clock(ms); + CStarNetHandler::clock(ms); + CDDHandler::clock(ms); + CCCSHandler::clock(ms); + + m_statusTimer2.clock(ms); + + m_statusFileTimer.clock(ms); + if (m_statusFileTimer.hasExpired()) { + readStatusFiles(); + m_statusFileTimer.start(); + } + + if (m_aprsWriter != NULL) + m_aprsWriter->clock(ms); + + if (m_logEnabled) { + m_statusTimer1.clock(ms); + if (m_statusTimer1.hasExpired()) { + bool ret1 = CDExtraHandler::stateChange(); + bool ret2 = CDPlusHandler::stateChange(); + bool ret3 = CDCSHandler::stateChange(); + bool ret4 = CCCSHandler::stateChange(); + if (ret1 || ret2 || ret3 || ret4) + writeStatus(); + + m_statusTimer1.start(); + } + } + + ::wxMilliSleep(TIME_PER_TIC_MS); + } + } + catch (std::exception& e) { + wxString message(e.what(), wxConvLocal); + wxLogError(wxT("Exception raised in the main thread - \"%s\""), message.c_str()); + } + catch (...) { + wxLogError(wxT("Unknown exception raised in the main thread")); + } + + wxLogMessage(wxT("Stopping the ircDDB Gateway thread")); + + // Unlink from all reflectors + CDExtraHandler::unlink(); + CDPlusHandler::unlink(); + CDCSHandler::unlink(); + CCCSHandler::disconnect(); + + if (m_ddModeEnabled) + CDDHandler::finalise(); + + if (server != NULL) + server->stop(); + + m_dextraPool->close(); + delete m_dextraPool; + + m_dplusPool->close(); + delete m_dplusPool; + + m_dcsPool->close(); + delete m_dcsPool; + + m_g2Handler->close(); + delete m_g2Handler; + + if (m_irc != NULL) { + m_irc->close(); + delete m_irc; + } + + if (m_icomRepeaterHandler != NULL) { + m_icomRepeaterHandler->close(); + delete m_icomRepeaterHandler; + } + + if (m_hbRepeaterHandler != NULL) { + m_hbRepeaterHandler->close(); + delete m_hbRepeaterHandler; + } + + if (m_dummyRepeaterHandler != NULL) { + m_dummyRepeaterHandler->close(); + delete m_dummyRepeaterHandler; + } + + if (m_remote != NULL) { + m_remote->close(); + delete m_remote; + } + + if (headerLogger != NULL) { + headerLogger->close(); + delete headerLogger; + } +} + +void CIRCDDBGatewayThread::kill() +{ + m_killed = true; +} + +void CIRCDDBGatewayThread::setGateway(GATEWAY_TYPE gatewayType, const wxString& gatewayCallsign, const wxString& gatewayAddress) +{ + if (!m_stopped) + return; + + m_gatewayType = gatewayType; + m_gatewayCallsign = gatewayCallsign; + m_gatewayAddress = gatewayAddress; +} + +void CIRCDDBGatewayThread::addRepeater(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) +{ + CRepeaterHandler::add(callsign, band, address, port, hwType, reflector, atStartup, reconnect, dratsEnabled, frequency, offset, range, latitude, longitude, agl, description1, description2, url, handler, band1, band2, band3); + + wxString repeater = callsign; + repeater.Truncate(LONG_CALLSIGN_LENGTH - 1U); + repeater.Append(band); + + // Add a fixed address and protocol for the local repeaters + m_cache.updateRepeater(repeater, m_gatewayCallsign, wxT("127.0.0.1"), DP_LOOPBACK, true, true); + + wxLogMessage(wxT("Adding %s to the cache as a local repeater"), repeater.c_str()); +} + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) +void CIRCDDBGatewayThread::addStarNet(const wxString& callsign, const wxString& logoff, const wxString& repeater, const wxString& infoText, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector) +{ + CStarNetHandler::add(callsign, logoff, repeater, infoText, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch, reflector); +} +#else +void CIRCDDBGatewayThread::addStarNet(const wxString& callsign, const wxString& logoff, const wxString& repeater, const wxString& infoText, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch) +{ + CStarNetHandler::add(callsign, logoff, repeater, infoText, permanent, userTimeout, groupTimeout, callsignSwitch, txMsgSwitch); +} +#endif + +void CIRCDDBGatewayThread::setIcomRepeaterHandler(CIcomRepeaterProtocolHandler* handler) +{ + m_icomRepeaterHandler = handler; +} + +void CIRCDDBGatewayThread::setHBRepeaterHandler(CHBRepeaterProtocolHandler* handler) +{ + m_hbRepeaterHandler = handler; +} + +void CIRCDDBGatewayThread::setDummyRepeaterHandler(CDummyRepeaterProtocolHandler* handler) +{ + m_dummyRepeaterHandler = handler; +} + +void CIRCDDBGatewayThread::setIRC(CIRCDDB* irc) +{ + wxASSERT(irc != NULL); + + m_irc = irc; + + m_lastStatus = IS_DISCONNECTED; +} + +void CIRCDDBGatewayThread::setLanguage(TEXT_LANG language) +{ + m_language = language; +} + +void CIRCDDBGatewayThread::setDExtra(bool enabled, unsigned int maxDongles) +{ + if (enabled) { + m_dextraEnabled = true; + m_dextraMaxDongles = maxDongles; + } else { + m_dextraEnabled = false; + m_dextraMaxDongles = 0U; + } +} + +void CIRCDDBGatewayThread::setDPlus(bool enabled, unsigned int maxDongles, const wxString& login) +{ + if (enabled) { + m_dplusEnabled = true; + m_dplusMaxDongles = maxDongles; + } else { + m_dplusEnabled = false; + m_dplusMaxDongles = 0U; + } + + m_dplusLogin = login; +} + +void CIRCDDBGatewayThread::setDCS(bool enabled) +{ + m_dcsEnabled = enabled; +} + +void CIRCDDBGatewayThread::setXLX(bool enabled, bool overrideLocal, const wxString& xlxHostsFileName) +{ + m_xlxEnabled = enabled; + m_xlxHostsFileName = xlxHostsFileName; + m_xlxOverrideLocal = overrideLocal; +} + +void CIRCDDBGatewayThread::setCCS(bool enabled, const wxString& host) +{ + m_ccsEnabled = enabled; + + wxFileName fileName(wxFileName::GetHomeDir(), CCS_HOSTS_FILE_NAME); + + if (!fileName.IsFileReadable()) { + wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str()); +#if defined(__WINDOWS__) + fileName.Assign(::wxGetCwd(), CCS_HOSTS_FILE_NAME); +#else + fileName.Assign(wxT(DATA_DIR), CCS_HOSTS_FILE_NAME); +#endif + if (!fileName.IsFileReadable()) { + wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str()); + m_ccsEnabled = false; + return; + } + } + + CHostFile hostFile(fileName.GetFullPath(), true); + + m_ccsHost = hostFile.getAddress(host); +} + +void CIRCDDBGatewayThread::setLog(bool enabled) +{ + m_logEnabled = enabled; +} + +void CIRCDDBGatewayThread::setAPRSWriter(CAPRSWriter* writer) +{ + m_aprsWriter = writer; +} + +void CIRCDDBGatewayThread::setInfoEnabled(bool enabled) +{ + m_infoEnabled = enabled; +} + +void CIRCDDBGatewayThread::setEchoEnabled(bool enabled) +{ + m_echoEnabled = enabled; +} + +void CIRCDDBGatewayThread::setDTMFEnabled(bool enabled) +{ + m_dtmfEnabled = enabled; +} + +void CIRCDDBGatewayThread::setDDModeEnabled(bool enabled) +{ + m_ddModeEnabled = enabled; +} + +void CIRCDDBGatewayThread::setLocation(double latitude, double longitude) +{ + m_latitude = latitude; + m_longitude = longitude; +} + +void CIRCDDBGatewayThread::setRemote(bool enabled, const wxString& password, unsigned int port) +{ + if (enabled) { + m_remoteEnabled = true; + m_remotePassword = password; + m_remotePort = port; + } else { + m_remoteEnabled = false; + m_remotePassword = password; + m_remotePort = REMOTE_DUMMY_PORT; + } +} + +void CIRCDDBGatewayThread::setWhiteList(CCallsignList* list) +{ + wxASSERT(list != NULL); + + m_whiteList = list; +} + +void CIRCDDBGatewayThread::setBlackList(CCallsignList* list) +{ + wxASSERT(list != NULL); + + m_blackList = list; +} + +void CIRCDDBGatewayThread::setRestrictList(CCallsignList* list) +{ + wxASSERT(list != NULL); + + m_restrictList = list; +} + +void CIRCDDBGatewayThread::processIrcDDB() +{ + // Once per second + if (m_statusTimer2.hasExpired()) { + int status = m_irc->getConnectionState(); + switch (status) { + case 0: + case 10: + if (m_lastStatus != IS_DISCONNECTED) { + wxLogInfo(wxT("Disconnected from ircDDB")); + m_lastStatus = IS_DISCONNECTED; + if (m_aprsWriter != NULL) + m_aprsWriter->setEnabled(false); + } + break; + case 7: + if (m_lastStatus != IS_CONNECTED) { + wxLogInfo(wxT("Connected to ircDDB")); + m_lastStatus = IS_CONNECTED; + if (m_aprsWriter != NULL) + m_aprsWriter->setEnabled(true); + } + break; + default: + if (m_lastStatus != IS_CONNECTING) { + wxLogInfo(wxT("Connecting to ircDDB")); + m_lastStatus = IS_CONNECTING; + if (m_aprsWriter != NULL) + m_aprsWriter->setEnabled(false); + } + break; + } + + m_statusTimer2.start(); + } + + // Process incoming ircDDB messages, updating the caches + for (;;) { + IRCDDB_RESPONSE_TYPE type = m_irc->getMessageType(); + + switch (type) { + case IDRT_USER: { + wxString user, repeater, gateway, address, timestamp; + bool res = m_irc->receiveUser(user, repeater, gateway, address, timestamp); + if (!res) + break; + + if (!address.IsEmpty()) { + wxLogMessage(wxT("USER: %s %s %s %s"), user.c_str(), repeater.c_str(), gateway.c_str(), address.c_str()); + m_cache.updateUser(user, repeater, gateway, address, timestamp, DP_DEXTRA, false, false); + } else { + wxLogMessage(wxT("USER: %s NOT FOUND"), user.c_str()); + } + } + break; + + case IDRT_REPEATER: { + wxString repeater, gateway, address; + bool res = m_irc->receiveRepeater(repeater, gateway, address); + if (!res) + break; + + CRepeaterHandler::resolveRepeater(repeater, gateway, address, DP_DEXTRA); + if (!address.IsEmpty()) { + wxLogMessage(wxT("REPEATER: %s %s %s"), repeater.c_str(), gateway.c_str(), address.c_str()); + m_cache.updateRepeater(repeater, gateway, address, DP_DEXTRA, false, false); + } else { + wxLogMessage(wxT("REPEATER: %s NOT FOUND"), repeater.c_str()); + } + } + break; + + case IDRT_GATEWAY: { + wxString gateway, address; + bool res = m_irc->receiveGateway(gateway, address); + if (!res) + break; + + CDExtraHandler::gatewayUpdate(gateway, address); + CDPlusHandler::gatewayUpdate(gateway, address); + if (!address.IsEmpty()) { + wxLogMessage(wxT("GATEWAY: %s %s"), gateway.c_str(), address.c_str()); + m_cache.updateGateway(gateway, address, DP_DEXTRA, false, false); + } else { + wxLogMessage(wxT("GATEWAY: %s NOT FOUND"), gateway.c_str()); + } + } + break; + + default: + return; + } + } +} + +void CIRCDDBGatewayThread::processRepeater(IRepeaterProtocolHandler* handler) +{ + for (;;) { + REPEATER_TYPE type = handler->read(); + + switch (type) { + case RT_POLL: { + CPollData* poll = handler->readPoll(); + if (poll != NULL) { + CRepeaterHandler* handler = CRepeaterHandler::findRepeater(*poll); + if (handler != NULL) + handler->processRepeater(*poll); + else + CRepeaterHandler::pollAllIcom(*poll); + + delete poll; + } + } + break; + + case RT_HEARD: { + CHeardData* heard = handler->readHeard(); + if (heard != NULL) { + wxString user = heard->getUser(); + wxString repeater = heard->getRepeater(); + + // Internal controller heard have identical user and repeater values + if (!repeater.IsSameAs(user)) { + CRepeaterHandler* handler = CRepeaterHandler::findDVRepeater(repeater); + if (handler == NULL) + wxLogMessage(wxT("Heard received from unknown repeater, %s"), repeater.c_str()); + else + handler->processRepeater(*heard); + + delete heard; + } + } + } + break; + + case RT_HEADER: { + CHeaderData* header = handler->readHeader(); + if (header != NULL) { + // wxLogMessage(wxT("Repeater header - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); + + CRepeaterHandler* repeater = CRepeaterHandler::findDVRepeater(*header); + if (repeater == NULL) + wxLogMessage(wxT("Header received from unknown repeater, %s"), header->getRptCall1().c_str()); + else + repeater->processRepeater(*header); + + delete header; + } + } + break; + + case RT_AMBE: { + CAMBEData* data = handler->readAMBE(); + if (data != NULL) { + CRepeaterHandler* repeater = CRepeaterHandler::findDVRepeater(*data, false); + if (repeater != NULL) + repeater->processRepeater(*data); + + delete data; + } + } + break; + + case RT_BUSY_HEADER: { + CHeaderData* header = handler->readBusyHeader(); + if (header != NULL) { + // wxLogMessage(wxT("Repeater busy header - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); + + CRepeaterHandler* repeater = CRepeaterHandler::findDVRepeater(*header); + if (repeater == NULL) + wxLogMessage(wxT("Busy header received from unknown repeater, %s"), header->getRptCall1().c_str()); + else + repeater->processBusy(*header); + + delete header; + } + } + break; + + case RT_BUSY_AMBE: { + CAMBEData* data = handler->readBusyAMBE(); + if (data != NULL) { + CRepeaterHandler* repeater = CRepeaterHandler::findDVRepeater(*data, true); + if (repeater != NULL) + repeater->processBusy(*data); + + delete data; + } + } + break; + + case RT_DD: { + CDDData* data = handler->readDD(); + if (data != NULL) { + // wxLogMessage(wxT("DD header - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X"), data->getMyCall1().c_str(), data->getMyCall2().c_str(), data->getYourCall().c_str(), data->getRptCall1().c_str(), data->getRptCall2().c_str(), data->getFlag1(), data->getFlag2(), data->getFlag3()); + + CRepeaterHandler* repeater = CRepeaterHandler::findDDRepeater(); + if (repeater == NULL) + wxLogMessage(wxT("DD data received from unknown DD repeater, %s"), data->getRptCall1().c_str()); + else + repeater->processRepeater(*data); + + delete data; + } + } + break; + + default: + return; + } + } +} + +void CIRCDDBGatewayThread::processDExtra() +{ + for (;;) { + DEXTRA_TYPE type = m_dextraPool->read(); + + switch (type) { + case DE_POLL: { + CPollData* poll = m_dextraPool->readPoll(); + if (poll != NULL) { + CDExtraHandler::process(*poll); + delete poll; + } + } + break; + + case DE_CONNECT: { + CConnectData* connect = m_dextraPool->readConnect(); + if (connect != NULL) { + CDExtraHandler::process(*connect); + delete connect; + } + } + break; + + case DE_HEADER: { + CHeaderData* header = m_dextraPool->readHeader(); + if (header != NULL) { + // wxLogMessage(wxT("DExtra header - My: %s/%s Your: %s Rpt1: %s Rpt2: %s"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str()); + CDExtraHandler::process(*header); + delete header; + } + } + break; + + case DE_AMBE: { + CAMBEData* data = m_dextraPool->readAMBE(); + if (data != NULL) { + CDExtraHandler::process(*data); + delete data; + } + } + break; + + default: + return; + } + } +} + +void CIRCDDBGatewayThread::processDPlus() +{ + for (;;) { + DPLUS_TYPE type = m_dplusPool->read(); + + switch (type) { + case DP_POLL: { + CPollData* poll = m_dplusPool->readPoll(); + if (poll != NULL) { + CDPlusHandler::process(*poll); + delete poll; + } + } + break; + + case DP_CONNECT: { + CConnectData* connect = m_dplusPool->readConnect(); + if (connect != NULL) { + CDPlusHandler::process(*connect); + delete connect; + } + } + break; + + case DP_HEADER: { + CHeaderData* header = m_dplusPool->readHeader(); + if (header != NULL) { + // wxLogMessage(wxT("D-Plus header - My: %s/%s Your: %s Rpt1: %s Rpt2: %s"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str()); + CDPlusHandler::process(*header); + delete header; + } + } + break; + + case DP_AMBE: { + CAMBEData* data = m_dplusPool->readAMBE(); + if (data != NULL) { + CDPlusHandler::process(*data); + delete data; + } + } + break; + + default: + return; + } + } +} + +void CIRCDDBGatewayThread::processDCS() +{ + for (;;) { + DCS_TYPE type = m_dcsPool->read(); + + switch (type) { + case DC_POLL: { + CPollData* poll = m_dcsPool->readPoll(); + if (poll != NULL) { + CDCSHandler::process(*poll); + delete poll; + } + } + break; + + case DC_CONNECT: { + CConnectData* connect = m_dcsPool->readConnect(); + if (connect != NULL) { + CDCSHandler::process(*connect); + delete connect; + } + } + break; + + case DC_DATA: { + CAMBEData* data = m_dcsPool->readData(); + if (data != NULL) { + // wxLogMessage(wxT("DCS header - My: %s/%s Your: %s Rpt1: %s Rpt2: %s"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str()); + CDCSHandler::process(*data); + delete data; + } + } + break; + + default: + return; + } + } +} + +void CIRCDDBGatewayThread::processG2() +{ + for (;;) { + G2_TYPE type = m_g2Handler->read(); + + switch (type) { + case GT_HEADER: { + CHeaderData* header = m_g2Handler->readHeader(); + if (header != NULL) { + // wxLogMessage(wxT("G2 header - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X"), header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); + CG2Handler::process(*header); + delete header; + } + } + break; + + case GT_AMBE: { + CAMBEData* data = m_g2Handler->readAMBE(); + if (data != NULL) { + CG2Handler::process(*data); + delete data; + } + } + break; + + default: + return; + } + } +} + +void CIRCDDBGatewayThread::processDD() +{ + for (;;) { + CDDData* data = CDDHandler::read(); + if (data == NULL) + return; + + // wxLogMessage(wxT("DD header - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X"), data->getMyCall1().c_str(), data->getMyCall2().c_str(), data->getYourCall().c_str(), data->getRptCall1().c_str(), data->getRptCall2().c_str(), data->getFlag1(), data->getFlag2(), data->getFlag3()); + + delete data; + } +} + +void CIRCDDBGatewayThread::loadGateways() +{ + wxFileName fileName(wxFileName::GetHomeDir(), GATEWAY_HOSTS_FILE_NAME); + if (!fileName.IsFileReadable()) + return; + + unsigned int count = 0U; + + CHostFile hostFile(fileName.GetFullPath(), false); + for (unsigned int i = 0U; i < hostFile.getCount(); i++) { + wxString gateway = hostFile.getName(i); + in_addr address = CUDPReaderWriter::lookup(hostFile.getAddress(i)); + + if (address.s_addr != INADDR_NONE) { + unsigned char* ucp = (unsigned char*)&address; + + wxString addrText; + addrText.Printf(wxT("%u.%u.%u.%u"), ucp[0U] & 0xFFU, ucp[1U] & 0xFFU, ucp[2U] & 0xFFU, ucp[3U] & 0xFFU); + + wxLogMessage(wxT("Locking %s to %s"), gateway.c_str(), addrText.c_str()); + + gateway.Append(wxT(" ")); + gateway.Truncate(LONG_CALLSIGN_LENGTH - 1U); + gateway.Append(wxT("G")); + m_cache.updateGateway(gateway, addrText, DP_DEXTRA, true, false); + + count++; + } + } + + wxLogMessage(wxT("Loaded %u of %u gateways from %s"), count, hostFile.getCount(), fileName.GetFullPath().c_str()); +} + +void CIRCDDBGatewayThread::loadReflectors() +{ + if(m_xlxEnabled && !m_xlxOverrideLocal) { + loadXLXReflectors(); + } + + if (m_dplusEnabled) { + wxFileName fileName(wxFileName::GetHomeDir(), DPLUS_HOSTS_FILE_NAME); + if (fileName.IsFileReadable()) + loadDPlusReflectors(fileName.GetFullPath()); + +#if defined(__WINDOWS__) + fileName.Assign(::wxGetCwd(), DPLUS_HOSTS_FILE_NAME); +#else + fileName.Assign(wxT(DATA_DIR), DPLUS_HOSTS_FILE_NAME); +#endif + if (fileName.IsFileReadable()) + loadDPlusReflectors(fileName.GetFullPath()); + } + + if (m_dextraEnabled) { + wxFileName fileName(wxFileName::GetHomeDir(), DEXTRA_HOSTS_FILE_NAME); + if (fileName.IsFileReadable()) + loadDExtraReflectors(fileName.GetFullPath()); + +#if defined(__WINDOWS__) + fileName.Assign(::wxGetCwd(), DEXTRA_HOSTS_FILE_NAME); +#else + fileName.Assign(wxT(DATA_DIR), DEXTRA_HOSTS_FILE_NAME); +#endif + if (fileName.IsFileReadable()) + loadDExtraReflectors(fileName.GetFullPath()); + } + + if (m_dcsEnabled) { + wxFileName fileName(wxFileName::GetHomeDir(), DCS_HOSTS_FILE_NAME); + if (fileName.IsFileReadable()) + loadDCSReflectors(fileName.GetFullPath()); + +#if defined(__WINDOWS__) + fileName.Assign(::wxGetCwd(), DCS_HOSTS_FILE_NAME); +#else + fileName.Assign(wxT(DATA_DIR), DCS_HOSTS_FILE_NAME); +#endif + if (fileName.IsFileReadable()) + loadDCSReflectors(fileName.GetFullPath()); + } + + if(m_xlxEnabled && m_xlxOverrideLocal) { + loadXLXReflectors(); + } +} + +void CIRCDDBGatewayThread::loadDExtraReflectors(const wxString& fileName) +{ + unsigned int count = 0U; + + CHostFile hostFile(fileName, false); + for (unsigned int i = 0U; i < hostFile.getCount(); i++) { + wxString reflector = hostFile.getName(i); + in_addr address = CUDPReaderWriter::lookup(hostFile.getAddress(i)); + bool lock = hostFile.getLock(i); + + if (address.s_addr != INADDR_NONE) { + unsigned char* ucp = (unsigned char*)&address; + + wxString addrText; + addrText.Printf(wxT("%u.%u.%u.%u"), ucp[0U] & 0xFFU, ucp[1U] & 0xFFU, ucp[2U] & 0xFFU, ucp[3U] & 0xFFU); + + if (lock) + wxLogMessage(wxT("Locking %s to %s"), reflector.c_str(), addrText.c_str()); + + reflector.Append(wxT(" ")); + reflector.Truncate(LONG_CALLSIGN_LENGTH - 1U); + reflector.Append(wxT("G")); + m_cache.updateGateway(reflector, addrText, DP_DEXTRA, lock, true); + + count++; + } + } + + wxLogMessage(wxT("Loaded %u of %u DExtra reflectors from %s"), count, hostFile.getCount(), fileName.c_str()); +} + +void CIRCDDBGatewayThread::loadDPlusReflectors(const wxString& fileName) +{ + unsigned int count = 0U; + + CHostFile hostFile(fileName, false); + for (unsigned int i = 0U; i < hostFile.getCount(); i++) { + wxString reflector = hostFile.getName(i); + in_addr address = CUDPReaderWriter::lookup(hostFile.getAddress(i)); + bool lock = hostFile.getLock(i); + + if (address.s_addr != INADDR_NONE) { + unsigned char* ucp = (unsigned char*)&address; + + wxString addrText; + addrText.Printf(wxT("%u.%u.%u.%u"), ucp[0U] & 0xFFU, ucp[1U] & 0xFFU, ucp[2U] & 0xFFU, ucp[3U] & 0xFFU); + + if (lock) + wxLogMessage(wxT("Locking %s to %s"), reflector.c_str(), addrText.c_str()); + + reflector.Append(wxT(" ")); + reflector.Truncate(LONG_CALLSIGN_LENGTH - 1U); + reflector.Append(wxT("G")); + m_cache.updateGateway(reflector, addrText, DP_DPLUS, lock, true); + + count++; + } + } + + wxLogMessage(wxT("Loaded %u of %u D-Plus reflectors from %s"), count, hostFile.getCount(), fileName.c_str()); +} + +void CIRCDDBGatewayThread::loadDCSReflectors(const wxString& fileName) +{ + unsigned int count = 0U; + + CHostFile hostFile(fileName, false); + for (unsigned int i = 0U; i < hostFile.getCount(); i++) { + wxString reflector = hostFile.getName(i); + in_addr address = CUDPReaderWriter::lookup(hostFile.getAddress(i)); + bool lock = hostFile.getLock(i); + + if (address.s_addr != INADDR_NONE) { + unsigned char* ucp = (unsigned char*)&address; + + wxString addrText; + addrText.Printf(wxT("%u.%u.%u.%u"), ucp[0U] & 0xFFU, ucp[1U] & 0xFFU, ucp[2U] & 0xFFU, ucp[3U] & 0xFFU); + + if (lock) + wxLogMessage(wxT("Locking %s to %s"), reflector.c_str(), addrText.c_str()); + + reflector.Append(wxT(" ")); + reflector.Truncate(LONG_CALLSIGN_LENGTH - 1U); + reflector.Append(wxT("G")); + m_cache.updateGateway(reflector, addrText, DP_DCS, lock, true); + + count++; + } + } + + wxLogMessage(wxT("Loaded %u of %u DCS reflectors from %s"), count, hostFile.getCount(), fileName.c_str()); +} + +void CIRCDDBGatewayThread::loadXLXReflectors() +{ + unsigned int count = 0U; + CHostFile hostFile = CHostFile(m_xlxHostsFileName, true); + for (unsigned int i = 0U; i < hostFile.getCount(); i++) { + wxString reflector = hostFile.getName(i); + in_addr address = CUDPReaderWriter::lookup(hostFile.getAddress(i)); + bool lock = hostFile.getLock(i); + + if (address.s_addr != INADDR_NONE) { + unsigned char* ucp = (unsigned char*)&address; + + wxString addrText; + addrText.Printf(wxT("%u.%u.%u.%u"), ucp[0U] & 0xFFU, ucp[1U] & 0xFFU, ucp[2U] & 0xFFU, ucp[3U] & 0xFFU); + + if (lock) + wxLogMessage(wxT("Locking %s to %s"), reflector.c_str(), addrText.c_str()); + + reflector.Append(wxT(" ")); + reflector.Truncate(LONG_CALLSIGN_LENGTH - 1U); + reflector.Append(wxT("G")); + + if(m_dcsEnabled && reflector.StartsWith(wxT("DCS"))) + m_cache.updateGateway(reflector, addrText, DP_DCS, lock, true); + else if(m_dextraEnabled && reflector.StartsWith(wxT("XRF"))) + m_cache.updateGateway(reflector, addrText, DP_DEXTRA, lock, true); + + count++; + } + } + + wxLogMessage(wxT("Loaded %u of %u XLX reflectors from %s"), count, hostFile.getCount(), m_xlxHostsFileName.c_str()); +} + +void CIRCDDBGatewayThread::writeStatus() +{ + wxString fullName = LINKS_BASE_NAME; + + if (!m_name.IsEmpty()) { + fullName.Append(wxT("_")); + fullName.Append(m_name); + } + + wxFileName fileName(m_logDir, fullName, wxT("log")); + + wxFFile file; + bool ret = file.Open(fileName.GetFullPath(), wxT("wt")); + if (!ret) { + wxLogError(wxT("Unable to open %s for writing"), fileName.GetFullPath().c_str()); + return; + } + + CDExtraHandler::writeStatus(file); + CDPlusHandler::writeStatus(file); + CDCSHandler::writeStatus(file); + CCCSHandler::writeStatus(file); + + file.Close(); +} + +CIRCDDBGatewayStatusData* CIRCDDBGatewayThread::getStatus() const +{ + bool aprsStatus = false; + if (m_aprsWriter != NULL) + aprsStatus = m_aprsWriter->isConnected(); + + CIRCDDBGatewayStatusData* status = new CIRCDDBGatewayStatusData(m_lastStatus, aprsStatus); + + for (unsigned int i = 0U; i < 4U; i++) { + wxString callsign, linkCallsign; + LINK_STATUS linkStatus; + bool ret = CRepeaterHandler::getRepeater(i, callsign, linkStatus, linkCallsign); + if (ret) { + wxString incoming1 = CDExtraHandler::getIncoming(callsign); + wxString incoming2 = CDCSHandler::getIncoming(callsign); + wxString incoming3 = CCCSHandler::getIncoming(callsign); + + wxString incoming; + if (!incoming1.IsEmpty()) { + incoming.Append(incoming1); + incoming.Append(wxT(" ")); + } + if (!incoming2.IsEmpty()) { + incoming.Append(incoming2); + incoming.Append(wxT(" ")); + } + if (!incoming3.IsEmpty()) { + incoming.Append(incoming3); + incoming.Append(wxT(" ")); + } + + status->setRepeater(i, callsign, linkStatus, linkCallsign, incoming); + } + } + + wxString dongles; + dongles.Append(CDExtraHandler::getDongles()); + dongles.Append(CDPlusHandler::getDongles()); + status->setDongles(dongles); + + return status; +} + +void CIRCDDBGatewayThread::readStatusFiles() +{ + readStatusFile(STATUS1_FILE_NAME, 0U, m_status1); + readStatusFile(STATUS2_FILE_NAME, 1U, m_status2); + readStatusFile(STATUS3_FILE_NAME, 2U, m_status3); + readStatusFile(STATUS4_FILE_NAME, 3U, m_status4); + readStatusFile(STATUS5_FILE_NAME, 4U, m_status5); +} + +void CIRCDDBGatewayThread::readStatusFile(const wxString& filename, unsigned int n, wxString& var) +{ + wxFileName name(wxFileName::GetHomeDir(), filename); + + wxString text; + + bool res = wxFile::Exists(name.GetFullPath()); + if (res) { + wxTextFile textFile(name.GetFullPath()); + res = textFile.Open(); + if (res) { + text = textFile.GetFirstLine(); + textFile.Close(); + } + } + + if (!var.IsSameAs(text)) { + wxLogMessage(wxT("Status %u message set to \"%s\""), n + 1U, text.c_str()); + CStatusData statusData(text, n); + CRepeaterHandler::writeStatus(statusData); + var = text; + } +} diff --git a/ircDDBGateway/IRCDDBGatewayThread.h b/ircDDBGateway/IRCDDBGatewayThread.h new file mode 100644 index 0000000..e3cc863 --- /dev/null +++ b/ircDDBGateway/IRCDDBGatewayThread.h @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2010-2013,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 + * 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 IRCDDBGatewayThread_H +#define IRCDDBGatewayThread_H + +#include "DummyRepeaterProtocolHandler.h" +#include "IcomRepeaterProtocolHandler.h" +#include "HBRepeaterProtocolHandler.h" +#include "DExtraProtocolHandlerPool.h" +#include "DPlusProtocolHandlerPool.h" +#include "RepeaterProtocolHandler.h" +#include "IRCDDBGatewayStatusData.h" +#include "DCSProtocolHandlerPool.h" +#include "G2ProtocolHandler.h" +#include "RemoteHandler.h" +#include "CacheManager.h" +#include "CallsignList.h" +#include "APRSWriter.h" +#include "IRCDDB.h" +#include "Timer.h" +#include "Defs.h" + +#include + +class CIRCDDBGatewayThread { +public: + CIRCDDBGatewayThread(const wxString& logDir, const wxString& name); + virtual ~CIRCDDBGatewayThread(); + + virtual void setGateway(GATEWAY_TYPE type, const wxString& callsign, const wxString& address); + virtual void addRepeater(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 = 0x00U, unsigned char band2 = 0x00U, unsigned char band3 = 0x00U); +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + virtual void addStarNet(const wxString& callsign, const wxString& logoff, const wxString& repeater, const wxString& infoText, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const wxString& reflector); +#else + virtual void addStarNet(const wxString& callsign, const wxString& logoff, const wxString& repeater, const wxString& infoText, const wxString& permanent, unsigned int userTimeout, unsigned int groupTimeout, STARNET_CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch); +#endif + virtual void setIcomRepeaterHandler(CIcomRepeaterProtocolHandler* handler); + virtual void setHBRepeaterHandler(CHBRepeaterProtocolHandler* handler); + virtual void setDummyRepeaterHandler(CDummyRepeaterProtocolHandler* handler); + virtual void setIRC(CIRCDDB* irc); + virtual void setLanguage(TEXT_LANG language); + virtual void setDExtra(bool enabled, unsigned int maxDongles); + virtual void setDPlus(bool enabled, unsigned int maxDongles, const wxString& login); + virtual void setDCS(bool enabled); + virtual void setXLX(bool enabled, bool overrideLocal, const wxString& fileName); + virtual void setCCS(bool enabled, const wxString& host); + virtual void setLog(bool enabled); + virtual void setAPRSWriter(CAPRSWriter* writer); + virtual void setInfoEnabled(bool enabled); + virtual void setEchoEnabled(bool enabled); + virtual void setDTMFEnabled(bool enabled); + virtual void setDDModeEnabled(bool enabled); + virtual void setRemote(bool enabled, const wxString& password, unsigned int port); + virtual void setLocation(double latitude, double longitude); + virtual void setWhiteList(CCallsignList* list); + virtual void setBlackList(CCallsignList* list); + virtual void setRestrictList(CCallsignList* list); + + virtual CIRCDDBGatewayStatusData* getStatus() const; + + virtual void run(); + virtual void kill(); + +private: + wxString m_logDir; + wxString m_name; + bool m_killed; + bool m_stopped; + GATEWAY_TYPE m_gatewayType; + wxString m_gatewayCallsign; + wxString m_gatewayAddress; + CIcomRepeaterProtocolHandler* m_icomRepeaterHandler; + CHBRepeaterProtocolHandler* m_hbRepeaterHandler; + CDummyRepeaterProtocolHandler* m_dummyRepeaterHandler; + CDExtraProtocolHandlerPool* m_dextraPool; + CDPlusProtocolHandlerPool* m_dplusPool; + CDCSProtocolHandlerPool* m_dcsPool; + CG2ProtocolHandler* m_g2Handler; + CAPRSWriter* m_aprsWriter; + CIRCDDB* m_irc; + CCacheManager m_cache; + TEXT_LANG m_language; + bool m_dextraEnabled; + unsigned int m_dextraMaxDongles; + bool m_dplusEnabled; + unsigned int m_dplusMaxDongles; + wxString m_dplusLogin; + bool m_dcsEnabled; + bool m_xlxEnabled; + bool m_xlxOverrideLocal; + wxString m_xlxHostsFileName; + bool m_ccsEnabled; + wxString m_ccsHost; + bool m_infoEnabled; + bool m_echoEnabled; + bool m_dtmfEnabled; + bool m_logEnabled; + bool m_ddModeEnabled; + IRCDDB_STATUS m_lastStatus; + CTimer m_statusTimer1; + CTimer m_statusTimer2; + bool m_remoteEnabled; + wxString m_remotePassword; + unsigned int m_remotePort; + CRemoteHandler* m_remote; + CTimer m_statusFileTimer; + wxString m_status1; + wxString m_status2; + wxString m_status3; + wxString m_status4; + wxString m_status5; + double m_latitude; + double m_longitude; + CCallsignList* m_whiteList; + CCallsignList* m_blackList; + CCallsignList* m_restrictList; + + void processIrcDDB(); + void processRepeater(IRepeaterProtocolHandler* handler); + void processDExtra(); + void processDPlus(); + void processDCS(); + void processG2(); + void processDD(); + + void loadGateways(); + void loadReflectors(); + void loadDExtraReflectors(const wxString& fileName); + void loadDPlusReflectors(const wxString& fileName); + void loadDCSReflectors(const wxString& fileName); + void loadXLXReflectors(); + + void writeStatus(); + + void readStatusFiles(); + void readStatusFile(const wxString& filename, unsigned int n, wxString& var); +}; + +#endif diff --git a/ircDDBGateway/IRCDDBGatewayThreadHelper.cpp b/ircDDBGateway/IRCDDBGatewayThreadHelper.cpp new file mode 100644 index 0000000..d1ff6b4 --- /dev/null +++ b/ircDDBGateway/IRCDDBGatewayThreadHelper.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2012 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 "IRCDDBGatewayThreadHelper.h" + +CIRCDDBGatewayThreadHelper::CIRCDDBGatewayThreadHelper(CIRCDDBGatewayThread* thread) : +wxThread(wxTHREAD_JOINABLE), +m_thread(thread) +{ + wxASSERT(thread != NULL); +} + +CIRCDDBGatewayThreadHelper::~CIRCDDBGatewayThreadHelper() +{ + delete m_thread; +} + +void CIRCDDBGatewayThreadHelper::start() +{ + Create(); + + SetPriority(100U); + + Run(); +} + +void* CIRCDDBGatewayThreadHelper::Entry() +{ + wxASSERT(m_thread != NULL); + + m_thread->run(); + + return NULL; +} + +void CIRCDDBGatewayThreadHelper::kill() +{ + wxASSERT(m_thread != NULL); + + m_thread->kill(); + + Wait(); +} + +CIRCDDBGatewayStatusData* CIRCDDBGatewayThreadHelper::getStatus() +{ + wxASSERT(m_thread != NULL); + + return m_thread->getStatus(); +} diff --git a/ircDDBGateway/IRCDDBGatewayThreadHelper.h b/ircDDBGateway/IRCDDBGatewayThreadHelper.h new file mode 100644 index 0000000..4b0b3f8 --- /dev/null +++ b/ircDDBGateway/IRCDDBGatewayThreadHelper.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2012 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 IRCDDBGatewayThreadHelper_H +#define IRCDDBGatewayThreadHelper_H + +#include "IRCDDBGatewayStatusData.h" +#include "IRCDDBGatewayThread.h" + +#include + +class CIRCDDBGatewayThreadHelper : public wxThread { + +public: + CIRCDDBGatewayThreadHelper(CIRCDDBGatewayThread* thread); + virtual ~CIRCDDBGatewayThreadHelper(); + + virtual void start(); + + virtual void* Entry(); + + virtual void kill(); + + virtual CIRCDDBGatewayStatusData* getStatus(); + +private: + CIRCDDBGatewayThread* m_thread; +}; + +#endif diff --git a/ircDDBGateway/ircDDBGateway.vcxproj b/ircDDBGateway/ircDDBGateway.vcxproj new file mode 100644 index 0000000..991b1d1 --- /dev/null +++ b/ircDDBGateway/ircDDBGateway.vcxproj @@ -0,0 +1,200 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {6DDA3497-66A3-4856-BAD4-1DDCDBDFF959} + ircDDBGateway + Win32Proj + 10.0.16299.0 + + + + Application + v141 + Unicode + true + + + Application + v141 + Unicode + true + + + Application + v141 + Unicode + + + Application + v141 + Unicode + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>14.0.24720.0 + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + $(WXWIN)\include;$(WXWIN)\lib\vc_dll\mswud;$(IncludePath) + + + true + $(WXWIN)\include;$(WXWIN)\lib\vc_x64_dll\mswud;$(IncludePath) + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + + + false + + + + Disabled + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;../ircDDB;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;DCS_LINK;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + Level4 + EditAndContinue + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_dll;%(AdditionalLibraryDirectories) + true + Windows + MachineX86 + + + + + Disabled + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;../ircDDB;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;DCS_LINK;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + + + Level4 + ProgramDatabase + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_x64_dll;%(AdditionalLibraryDirectories) + true + Windows + + + + + MaxSpeed + true + $(WXWIN)\include\msvc;$(WXWIN)\include;../ircDDB;../Common;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + Level3 + ProgramDatabase + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_dll;%(AdditionalLibraryDirectories) + true + Windows + true + true + MachineX86 + + + + + MaxSpeed + true + $(WXWIN)\include\msvc;$(WXWIN)\include;../ircDDB;../Common;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_x64_dll;%(AdditionalLibraryDirectories) + true + Windows + true + true + + + + + + + + + + + + + + + + + + + + + + {e793cb8e-2ac9-431a-bbfc-3f52537bb3cf} + false + + + {276bc54d-5581-4a0c-afd5-a5bdc947f0ad} + false + + + + + + \ No newline at end of file diff --git a/ircDDBGateway/ircDDBGateway.vcxproj.filters b/ircDDBGateway/ircDDBGateway.vcxproj.filters new file mode 100644 index 0000000..acace74 --- /dev/null +++ b/ircDDBGateway/ircDDBGateway.vcxproj.filters @@ -0,0 +1,56 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/ircDDBGatewayConfig/IRCDDBGatewayConfigApp.cpp b/ircDDBGatewayConfig/IRCDDBGatewayConfigApp.cpp new file mode 100644 index 0000000..6ab9c8f --- /dev/null +++ b/ircDDBGatewayConfig/IRCDDBGatewayConfigApp.cpp @@ -0,0 +1,94 @@ +/* + * 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 + * 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 "IRCDDBGatewayConfigDefs.h" +#include "IRCDDBGatewayConfigApp.h" +#include "Version.h" +#include "Utils.h" + +#include +#include +#include + +IMPLEMENT_APP(CIRCDDBGatewayConfigApp) + +const wxChar* NAME_PARAM = wxT("Gateway Name"); +const wxChar* CONFDIR_OPTION = wxT("confdir"); + +CIRCDDBGatewayConfigApp::CIRCDDBGatewayConfigApp() : +wxApp(), +m_name(), +m_confDir(), +m_frame(NULL) +{ +} + +CIRCDDBGatewayConfigApp::~CIRCDDBGatewayConfigApp() +{ +} + +bool CIRCDDBGatewayConfigApp::OnInit() +{ + SetVendorName(VENDOR_NAME); + + if (!wxApp::OnInit()) + return false; + + wxString frameName = APPLICATION_NAME + wxT(" - "); + if (!m_name.IsEmpty()) { + frameName.Append(m_name); + frameName.Append(wxT(" - ")); + } + frameName.Append(VERSION); + + m_frame = new CIRCDDBGatewayConfigFrame(frameName, m_confDir, m_name); + m_frame->Show(); + + SetTopWindow(m_frame); + + return true; +} + +int CIRCDDBGatewayConfigApp::OnExit() +{ + return 0; +} + +void CIRCDDBGatewayConfigApp::OnInitCmdLine(wxCmdLineParser& parser) +{ + parser.AddOption(CONFDIR_OPTION, wxEmptyString, wxEmptyString, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddParam(NAME_PARAM, wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + + wxApp::OnInitCmdLine(parser); +} + +bool CIRCDDBGatewayConfigApp::OnCmdLineParsed(wxCmdLineParser& parser) +{ + if (!wxApp::OnCmdLineParsed(parser)) + return false; + + wxString confDir; + bool found = parser.Found(CONFDIR_OPTION, &confDir); + if (found) + m_confDir = confDir; + + if (parser.GetParamCount() > 0U) + m_name = parser.GetParam(0U); + + return true; +} diff --git a/ircDDBGatewayConfig/IRCDDBGatewayConfigApp.h b/ircDDBGatewayConfig/IRCDDBGatewayConfigApp.h new file mode 100644 index 0000000..20ee352 --- /dev/null +++ b/ircDDBGatewayConfig/IRCDDBGatewayConfigApp.h @@ -0,0 +1,47 @@ +/* + * 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 + * 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 IRCDDBGatewayConfigApp_H +#define IRCDDBGatewayConfigApp_H + +#include "IRCDDBGatewayConfigFrame.h" +#include "Defs.h" + +#include + +class CIRCDDBGatewayConfigApp : public wxApp { + +public: + CIRCDDBGatewayConfigApp(); + virtual ~CIRCDDBGatewayConfigApp(); + + virtual bool OnInit(); + virtual int OnExit(); + + virtual void OnInitCmdLine(wxCmdLineParser& parser); + virtual bool OnCmdLineParsed(wxCmdLineParser& parser); + +private: + wxString m_name; + wxString m_confDir; + CIRCDDBGatewayConfigFrame* m_frame; +}; + +DECLARE_APP(CIRCDDBGatewayConfigApp) + +#endif diff --git a/ircDDBGatewayConfig/IRCDDBGatewayConfigDefs.h b/ircDDBGatewayConfig/IRCDDBGatewayConfigDefs.h new file mode 100644 index 0000000..5bd7fed --- /dev/null +++ b/ircDDBGatewayConfig/IRCDDBGatewayConfigDefs.h @@ -0,0 +1,39 @@ +/* + * 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 + * 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 IRCDDBGatewayConfigDefs_H +#define IRCDDBGatewayConfigDefs_H + +#include + +const wxString APPLICATION_NAME = wxT("ircDDB Gateway"); + +const wxString LOG_BASE_NAME = wxT("ircDDBGateway"); + +const wxString CONFIG_FILE_NAME = wxT("ircddbgateway"); + +const unsigned int MAX_OUTGOING = 6U; +const unsigned int MAX_REPEATERS = 4U; +const unsigned int MAX_DEXTRA_LINKS = 5U; +const unsigned int MAX_DPLUS_LINKS = 5U; +const unsigned int MAX_DCS_LINKS = 5U; +const unsigned int MAX_STARNETS = 5U; +const unsigned int MAX_ROUTES = MAX_REPEATERS + 5U; +const unsigned int MAX_DD_ROUTES = 20U; + +#endif diff --git a/ircDDBGatewayConfig/IRCDDBGatewayConfigFrame.cpp b/ircDDBGatewayConfig/IRCDDBGatewayConfigFrame.cpp new file mode 100644 index 0000000..40ffb31 --- /dev/null +++ b/ircDDBGatewayConfig/IRCDDBGatewayConfigFrame.cpp @@ -0,0 +1,636 @@ +/* + * 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 + * 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 "IRCDDBGatewayConfigFrame.h" +#include "IRCDDBGatewayConfigDefs.h" +#include "Version.h" + +const unsigned int BORDER_SIZE = 5U; + +#include +#include +#include + +enum { + Menu_File_Save = 6000 +}; + +BEGIN_EVENT_TABLE(CIRCDDBGatewayConfigFrame, wxFrame) + EVT_MENU(wxID_EXIT, CIRCDDBGatewayConfigFrame::onQuit) + EVT_MENU(Menu_File_Save, CIRCDDBGatewayConfigFrame::onSave) + EVT_MENU(wxID_ABOUT, CIRCDDBGatewayConfigFrame::onAbout) + + EVT_CLOSE(CIRCDDBGatewayConfigFrame::onClose) +END_EVENT_TABLE() + +CIRCDDBGatewayConfigFrame::CIRCDDBGatewayConfigFrame(const wxString& title, const wxString& confDir, const wxString& name) : +wxFrame(NULL, -1, title), +m_config(NULL), +m_gateway(NULL), +m_repeaterData1(NULL), +m_repeaterInfo1(NULL), +m_repeaterData2(NULL), +m_repeaterInfo2(NULL), +m_repeaterData3(NULL), +m_repeaterInfo3(NULL), +m_repeaterData4(NULL), +m_repeaterInfo4(NULL), +m_ircDDB(NULL), +m_ircDDB2(NULL), +m_ircDDB3(NULL), +m_ircDDB4(NULL), +m_dprs(NULL), +m_dextra(NULL), +m_dplus(NULL), +m_dcs(NULL), +m_xlx(NULL), +m_starNet1(NULL), +m_starNet2(NULL), +m_starNet3(NULL), +m_starNet4(NULL), +m_starNet5(NULL), +m_remote(NULL), +m_miscellaneous(NULL) +{ + SetMenuBar(createMenuBar()); + + wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); + + wxPanel* panel = new wxPanel(this, -1); + +#if defined(__WINDOWS__) + if (confDir.IsEmpty()) + m_config = new CIRCDDBGatewayConfig(new wxConfig(APPLICATION_NAME), ::wxGetHomeDir(), CONFIG_FILE_NAME, name); + else + m_config = new CIRCDDBGatewayConfig(new wxConfig(APPLICATION_NAME), confDir, CONFIG_FILE_NAME, name); +#else + if (confDir.IsEmpty()) + m_config = new CIRCDDBGatewayConfig(wxT(CONF_DIR), CONFIG_FILE_NAME, name); + else + m_config = new CIRCDDBGatewayConfig(confDir, CONFIG_FILE_NAME, name); +#endif + + wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); + + wxNotebook* noteBook = new wxNotebook(panel, -1); + + bool dextraEnabled; + unsigned int maxDExtraDongles; + m_config->getDExtra(dextraEnabled, maxDExtraDongles); + + wxString dplusLogin; + unsigned int maxDPlusDongles; + bool dplusEnabled; + m_config->getDPlus(dplusEnabled, maxDPlusDongles, dplusLogin); + + bool dcsEnabled, ccsEnabled; + wxString ccsHost; + m_config->getDCS(dcsEnabled, ccsEnabled, ccsHost); + + bool xlxEnabled; + bool xlxOverrideLocal; + wxString xlxHostsFileUrl; + m_config->getXLX(xlxEnabled, xlxOverrideLocal, xlxHostsFileUrl); + + GATEWAY_TYPE gatewayType; + wxString gatewayCallsign, gatewayAddress, icomAddress, hbAddress, description1, description2, url; + unsigned int icomPort, hbPort; + double latitude, longitude; + m_config->getGateway(gatewayType, gatewayCallsign, gatewayAddress, icomAddress, icomPort, hbAddress, hbPort, latitude, longitude, description1, description2, url); + + m_gateway = new CIRCDDBGatewayConfigGatewaySet(noteBook, -1, APPLICATION_NAME, gatewayType, gatewayCallsign, gatewayAddress, icomAddress, icomPort, hbAddress, hbPort, latitude, longitude, description1, description2, url); + noteBook->AddPage(m_gateway, _("Gateway"), true); + + wxString repeaterCall1, repeaterBand1, repeaterAddress1, reflector1, description11, description12, url1; + double frequency1, offset1, range1, latitude1, longitude1, agl1; + unsigned char band11, band12, band13; + unsigned int repeaterPort1; + HW_TYPE repeaterType1; + bool atStartup1; + RECONNECT reconnect1; + m_config->getRepeater1(repeaterCall1, repeaterBand1, repeaterType1, repeaterAddress1, repeaterPort1, band11, band12, band13, reflector1, atStartup1, reconnect1, frequency1, offset1, range1, latitude1, longitude1, agl1, description11, description12, url1); + + m_repeaterData1 = new CRepeaterDataSet(noteBook, -1, APPLICATION_NAME, repeaterBand1, repeaterType1, repeaterAddress1, repeaterPort1, band11, band12, band13, dplusEnabled, dextraEnabled, dcsEnabled, reflector1, atStartup1, reconnect1); + noteBook->AddPage(m_repeaterData1, _("Repeater 1"), false); + + m_repeaterInfo1 = new CRepeaterInfoSet(noteBook, -1, APPLICATION_NAME, frequency1, offset1, range1, latitude1, longitude1, agl1, description11, description12, url1); + noteBook->AddPage(m_repeaterInfo1, _("Repeater 1"), false); + + wxString repeaterCall2, repeaterBand2, repeaterAddress2, reflector2, description21, description22, url2; + double frequency2, offset2, range2, latitude2, longitude2, agl2; + unsigned char band21, band22, band23; + unsigned int repeaterPort2; + HW_TYPE repeaterType2; + bool atStartup2; + RECONNECT reconnect2; + m_config->getRepeater2(repeaterCall2, repeaterBand2, repeaterType2, repeaterAddress2, repeaterPort2, band21, band22, band23, reflector2, atStartup2, reconnect2, frequency2, offset2, range2, latitude2, longitude2, agl2, description21, description22, url2); + + m_repeaterData2 = new CRepeaterDataSet(noteBook, -1, APPLICATION_NAME, repeaterBand2, repeaterType2, repeaterAddress2, repeaterPort2, band21, band22, band23, dplusEnabled, dextraEnabled, dcsEnabled, reflector2, atStartup2, reconnect2); + noteBook->AddPage(m_repeaterData2, _("Repeater 2"), false); + + m_repeaterInfo2 = new CRepeaterInfoSet(noteBook, -1, APPLICATION_NAME, frequency2, offset2, range2, latitude2, longitude2, agl2, description21, description22, url2); + noteBook->AddPage(m_repeaterInfo2, _("Repeater 2"), false); + + wxString repeaterCall3, repeaterBand3, repeaterAddress3, reflector3, description31, description32, url3; + double frequency3, offset3, range3, latitude3, longitude3, agl3; + unsigned char band31, band32, band33; + unsigned int repeaterPort3; + HW_TYPE repeaterType3; + bool atStartup3; + RECONNECT reconnect3; + m_config->getRepeater3(repeaterCall3, repeaterBand3, repeaterType3, repeaterAddress3, repeaterPort3, band31, band32, band33, reflector3, atStartup3, reconnect3, frequency3, offset3, range3, latitude3, longitude3, agl3, description31, description32, url3); + + m_repeaterData3 = new CRepeaterDataSet(noteBook, -1, APPLICATION_NAME, repeaterBand3, repeaterType3, repeaterAddress3, repeaterPort3, band31, band32, band33, dplusEnabled, dextraEnabled, dcsEnabled, reflector3, atStartup3, reconnect3); + noteBook->AddPage(m_repeaterData3, _("Repeater 3"), false); + + m_repeaterInfo3 = new CRepeaterInfoSet(noteBook, -1, APPLICATION_NAME, frequency3, offset3, range3, latitude3, longitude3, agl3, description31, description32, url3); + noteBook->AddPage(m_repeaterInfo3, _("Repeater 3"), false); + + wxString repeaterCall4, repeaterBand4, repeaterAddress4, reflector4, description41, description42, url4; + double frequency4, offset4, range4, latitude4, longitude4, agl4; + unsigned char band41, band42, band43; + unsigned int repeaterPort4; + HW_TYPE repeaterType4; + bool atStartup4; + RECONNECT reconnect4; + m_config->getRepeater4(repeaterCall4, repeaterBand4, repeaterType4, repeaterAddress4, repeaterPort4, band41, band42, band43, reflector4, atStartup4, reconnect4, frequency4, offset4, range4, latitude4, longitude4, agl4, description41, description42, url4); + + m_repeaterData4 = new CRepeaterDataSet(noteBook, -1, APPLICATION_NAME, repeaterBand4, repeaterType4, repeaterAddress4, repeaterPort4, band41, band42, band43, dplusEnabled, dextraEnabled, dcsEnabled, reflector4, atStartup4, reconnect4); + noteBook->AddPage(m_repeaterData4, _("Repeater 4"), false); + + m_repeaterInfo4 = new CRepeaterInfoSet(noteBook, -1, APPLICATION_NAME, frequency4, offset4, range4, latitude4, longitude4, agl4, description41, description42, url4); + noteBook->AddPage(m_repeaterInfo4, _("Repeater 4"), false); + + bool ircDDBEnabled; + wxString ircDDBHostname, ircDDBUsername, ircDDBPassword; + m_config->getIrcDDB(ircDDBEnabled, ircDDBHostname, ircDDBUsername, ircDDBPassword); + m_ircDDB = new CIRCDDBGatewayConfigIrcDDBSet(noteBook, -1, APPLICATION_NAME, ircDDBEnabled, ircDDBHostname, ircDDBUsername, ircDDBPassword); + noteBook->AddPage(m_ircDDB, wxT("ircDDB 1st Network"), false); + + m_config->getIrcDDB2(ircDDBEnabled, ircDDBHostname, ircDDBUsername, ircDDBPassword); + m_ircDDB2 = new CIRCDDBGatewayConfigIrcDDBSet(noteBook, -1, APPLICATION_NAME, ircDDBEnabled, ircDDBHostname, ircDDBUsername, ircDDBPassword); + noteBook->AddPage(m_ircDDB2, wxT("ircDDB 2nd Network"), false); + + m_config->getIrcDDB3(ircDDBEnabled, ircDDBHostname, ircDDBUsername, ircDDBPassword); + m_ircDDB3 = new CIRCDDBGatewayConfigIrcDDBSet(noteBook, -1, APPLICATION_NAME, ircDDBEnabled, ircDDBHostname, ircDDBUsername, ircDDBPassword); + noteBook->AddPage(m_ircDDB3, wxT("ircDDB 3rd Network"), false); + + m_config->getIrcDDB4(ircDDBEnabled, ircDDBHostname, ircDDBUsername, ircDDBPassword); + m_ircDDB4 = new CIRCDDBGatewayConfigIrcDDBSet(noteBook, -1, APPLICATION_NAME, ircDDBEnabled, ircDDBHostname, ircDDBUsername, ircDDBPassword); + noteBook->AddPage(m_ircDDB4, wxT("ircDDB 4th Network"), false); + + wxString aprsHostname; + unsigned int aprsPort; + bool aprsEnabled; + m_config->getDPRS(aprsEnabled, aprsHostname, aprsPort); + + m_dprs = new CDPRSSet(noteBook, -1, APPLICATION_NAME, aprsEnabled, aprsHostname, aprsPort); + noteBook->AddPage(m_dprs, wxT("D-PRS"), false); + + m_dextra = new CDExtraSet(noteBook, -1, APPLICATION_NAME, dextraEnabled, maxDExtraDongles, MAX_DEXTRA_LINKS); + noteBook->AddPage(m_dextra, wxT("DExtra"), false); + + m_dplus = new CDPlusSet(noteBook, -1, APPLICATION_NAME, dplusEnabled, maxDPlusDongles, MAX_DPLUS_LINKS, dplusLogin); + noteBook->AddPage(m_dplus, wxT("D-Plus"), false); + + m_dcs = new CDCSSet(noteBook, -1, APPLICATION_NAME, dcsEnabled, ccsEnabled, ccsHost); + noteBook->AddPage(m_dcs, _("DCS and CCS"), false); + + m_xlx = new CXLXSet(noteBook, -1, APPLICATION_NAME, xlxEnabled, xlxOverrideLocal, xlxHostsFileUrl); + noteBook->AddPage(m_xlx, _("XLX Hosts File"), false); + +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + wxString starNetBand1, starNetCallsign1, starNetLogoff1, starNetInfo1, starNetLink1, starNetPermanent1; + unsigned int starNetUserTimeout1, starNetGroupTimeout1; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch1; + bool starNetTXMsgSwitch1; + m_config->getStarNet1(starNetBand1, starNetCallsign1, starNetLogoff1, starNetInfo1, starNetPermanent1, starNetUserTimeout1, starNetGroupTimeout1, starNetCallsignSwitch1, starNetTXMsgSwitch1, starNetLink1); + + m_starNet1 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand1, starNetCallsign1, starNetLogoff1, starNetInfo1, starNetPermanent1, starNetUserTimeout1, starNetGroupTimeout1, starNetCallsignSwitch1, starNetTXMsgSwitch1, starNetLink1); + noteBook->AddPage(m_starNet1, wxT("StarNet 1"), false); + + wxString starNetBand2, starNetCallsign2, starNetLogoff2, starNetInfo2, starNetLink2, starNetPermanent2; + unsigned int starNetUserTimeout2, starNetGroupTimeout2; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch2; + bool starNetTXMsgSwitch2; + m_config->getStarNet2(starNetBand2, starNetCallsign2, starNetLogoff2, starNetInfo2, starNetPermanent2, starNetUserTimeout2, starNetGroupTimeout2, starNetCallsignSwitch2, starNetTXMsgSwitch2, starNetLink2); + + m_starNet2 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand2, starNetCallsign2, starNetLogoff2, starNetInfo2, starNetPermanent2, starNetUserTimeout2, starNetGroupTimeout2, starNetCallsignSwitch2, starNetTXMsgSwitch2, starNetLink2); + noteBook->AddPage(m_starNet2, wxT("StarNet 2"), false); + + wxString starNetBand3, starNetCallsign3, starNetLogoff3, starNetInfo3, starNetLink3, starNetPermanent3; + unsigned int starNetUserTimeout3, starNetGroupTimeout3; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch3; + bool starNetTXMsgSwitch3; + m_config->getStarNet3(starNetBand3, starNetCallsign3, starNetLogoff3, starNetInfo3, starNetPermanent3, starNetUserTimeout3, starNetGroupTimeout3, starNetCallsignSwitch3, starNetTXMsgSwitch3, starNetLink3); + + m_starNet3 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand3, starNetCallsign3, starNetLogoff3, starNetInfo3, starNetPermanent3, starNetUserTimeout3, starNetGroupTimeout3, starNetCallsignSwitch3, starNetTXMsgSwitch3, starNetLink3); + noteBook->AddPage(m_starNet3, wxT("StarNet 3"), false); + + wxString starNetBand4, starNetCallsign4, starNetLogoff4, starNetInfo4, starNetLink4, starNetPermanent4; + unsigned int starNetUserTimeout4, starNetGroupTimeout4; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch4; + bool starNetTXMsgSwitch4; + m_config->getStarNet4(starNetBand4, starNetCallsign4, starNetLogoff4, starNetInfo4, starNetPermanent4, starNetUserTimeout4, starNetGroupTimeout4, starNetCallsignSwitch4, starNetTXMsgSwitch4, starNetLink4); + + m_starNet4 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand4, starNetCallsign4, starNetLogoff4, starNetInfo4, starNetPermanent4, starNetUserTimeout4, starNetGroupTimeout4, starNetCallsignSwitch4, starNetTXMsgSwitch4, starNetLink4); + noteBook->AddPage(m_starNet4, wxT("StarNet 4"), false); + + wxString starNetBand5, starNetCallsign5, starNetLogoff5, starNetInfo5, starNetLink5, starNetPermanent5; + unsigned int starNetUserTimeout5, starNetGroupTimeout5; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch5; + bool starNetTXMsgSwitch5; + m_config->getStarNet5(starNetBand5, starNetCallsign5, starNetLogoff5, starNetInfo5, starNetPermanent5, starNetUserTimeout5, starNetGroupTimeout5, starNetCallsignSwitch5, starNetTXMsgSwitch5, starNetLink5); + + m_starNet5 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand5, starNetCallsign5, starNetLogoff5, starNetInfo5, starNetPermanent5, starNetUserTimeout5, starNetGroupTimeout5, starNetCallsignSwitch5, starNetTXMsgSwitch5, starNetLink5); + noteBook->AddPage(m_starNet5, wxT("StarNet 5"), false); +#else + wxString starNetBand1, starNetCallsign1, starNetLogoff1, starNetInfo1, starNetPermanent1; + unsigned int starNetUserTimeout1, starNetGroupTimeout1; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch1; + bool starNetTXMsgSwitch1; + m_config->getStarNet1(starNetBand1, starNetCallsign1, starNetLogoff1, starNetInfo1, starNetPermanent1, starNetUserTimeout1, starNetGroupTimeout1, starNetCallsignSwitch1, starNetTXMsgSwitch1); + + m_starNet1 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand1, starNetCallsign1, starNetLogoff1, starNetInfo1, starNetPermanent1, starNetUserTimeout1, starNetGroupTimeout1, starNetCallsignSwitch1, starNetTXMsgSwitch1); + noteBook->AddPage(m_starNet1, wxT("StarNet 1"), false); + + wxString starNetBand2, starNetCallsign2, starNetLogoff2, starNetInfo2, starNetPermanent2; + unsigned int starNetUserTimeout2, starNetGroupTimeout2; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch2; + bool starNetTXMsgSwitch2; + m_config->getStarNet2(starNetBand2, starNetCallsign2, starNetLogoff2, starNetInfo2, starNetPermanent2, starNetUserTimeout2, starNetGroupTimeout2, starNetCallsignSwitch2, starNetTXMsgSwitch2); + + m_starNet2 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand2, starNetCallsign2, starNetLogoff2, starNetInfo2, starNetPermanent2, starNetUserTimeout2, starNetGroupTimeout2, starNetCallsignSwitch2, starNetTXMsgSwitch2); + noteBook->AddPage(m_starNet2, wxT("StarNet 2"), false); + + wxString starNetBand3, starNetCallsign3, starNetLogoff3, starNetInfo3, starNetPermanent3; + unsigned int starNetUserTimeout3, starNetGroupTimeout3; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch3; + bool starNetTXMsgSwitch3; + m_config->getStarNet3(starNetBand3, starNetCallsign3, starNetLogoff3, starNetInfo3, starNetPermanent3, starNetUserTimeout3, starNetGroupTimeout3, starNetCallsignSwitch3, starNetTXMsgSwitch3); + + m_starNet3 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand3, starNetCallsign3, starNetLogoff3, starNetInfo3, starNetPermanent3, starNetUserTimeout3, starNetGroupTimeout3, starNetCallsignSwitch3, starNetTXMsgSwitch3); + noteBook->AddPage(m_starNet3, wxT("StarNet 3"), false); + + wxString starNetBand4, starNetCallsign4, starNetLogoff4, starNetInfo4, starNetPermanent4; + unsigned int starNetUserTimeout4, starNetGroupTimeout4; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch4; + bool starNetTXMsgSwitch4; + m_config->getStarNet4(starNetBand4, starNetCallsign4, starNetLogoff4, starNetInfo4, starNetPermanent4, starNetUserTimeout4, starNetGroupTimeout4, starNetCallsignSwitch4, starNetTXMsgSwitch4); + + m_starNet4 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand4, starNetCallsign4, starNetLogoff4, starNetInfo4, starNetPermanent4, starNetUserTimeout4, starNetGroupTimeout4, starNetCallsignSwitch4, starNetTXMsgSwitch4); + noteBook->AddPage(m_starNet4, wxT("StarNet 4"), false); + + wxString starNetBand5, starNetCallsign5, starNetLogoff5, starNetInfo5, starNetPermanent5; + unsigned int starNetUserTimeout5, starNetGroupTimeout5; + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch5; + bool starNetTXMsgSwitch5; + m_config->getStarNet5(starNetBand5, starNetCallsign5, starNetLogoff5, starNetInfo5, starNetPermanent5, starNetUserTimeout5, starNetGroupTimeout5, starNetCallsignSwitch5, starNetTXMsgSwitch5); + + m_starNet5 = new CStarNetSet(noteBook, -1, APPLICATION_NAME, starNetBand5, starNetCallsign5, starNetLogoff5, starNetInfo5, starNetPermanent5, starNetUserTimeout5, starNetGroupTimeout5, starNetCallsignSwitch5, starNetTXMsgSwitch5); + noteBook->AddPage(m_starNet5, wxT("StarNet 5"), false); +#endif + + unsigned int remotePort; + wxString remotePassword; + bool remoteEnabled; + m_config->getRemote(remoteEnabled, remotePassword, remotePort); + + m_remote = new CRemoteSet(noteBook, -1, APPLICATION_NAME, remoteEnabled, remotePassword, remotePort); + noteBook->AddPage(m_remote, wxT("Remote"), false); + + TEXT_LANG language; + bool infoEnabled, echoEnabled, logEnabled, dratsEnabled, dtmfEnabled; + m_config->getMiscellaneous(language, infoEnabled, echoEnabled, logEnabled, dratsEnabled, dtmfEnabled); + + m_miscellaneous = new CIRCDDBGatewayConfigMiscellaneousSet(noteBook, -1, APPLICATION_NAME, language, infoEnabled, echoEnabled, logEnabled, dratsEnabled, dtmfEnabled); + noteBook->AddPage(m_miscellaneous, wxT("Misc"), false); + + sizer->Add(noteBook, 0, wxEXPAND | wxALL, BORDER_SIZE); + + panel->SetSizer(sizer); + + mainSizer->Add(panel, 0, wxEXPAND | wxALL, BORDER_SIZE); + + mainSizer->SetSizeHints(this); + + SetSizer(mainSizer); +} + +CIRCDDBGatewayConfigFrame::~CIRCDDBGatewayConfigFrame() +{ + delete m_config; +} + +wxMenuBar* CIRCDDBGatewayConfigFrame::createMenuBar() +{ + wxMenu* fileMenu = new wxMenu(); + fileMenu->Append(Menu_File_Save, _("Save")); + fileMenu->AppendSeparator(); + fileMenu->Append(wxID_EXIT, _("Exit")); + + wxMenu* helpMenu = new wxMenu(); + helpMenu->Append(wxID_ABOUT, _("About ircDDB Gateway Config")); + + wxMenuBar* menuBar = new wxMenuBar(); + menuBar->Append(fileMenu, _("File")); + menuBar->Append(helpMenu, _("Help")); + + return menuBar; +} + +void CIRCDDBGatewayConfigFrame::onQuit(wxCommandEvent&) +{ + Close(false); +} + +void CIRCDDBGatewayConfigFrame::onClose(wxCloseEvent&) +{ + Destroy(); +} + +void CIRCDDBGatewayConfigFrame::onSave(wxCommandEvent&) +{ + if (!m_gateway->Validate() || !m_repeaterData1->Validate() || !m_repeaterInfo1->Validate() || !m_repeaterData2->Validate() || + !m_repeaterInfo2->Validate() || !m_repeaterData3->Validate() || !m_repeaterInfo3->Validate() || !m_repeaterData4->Validate() || + !m_repeaterInfo4->Validate() || + !m_ircDDB->Validate() || !m_ircDDB2->Validate() || !m_ircDDB3->Validate() || !m_ircDDB4->Validate() || !m_dprs->Validate() || !m_dplus->Validate() || !m_dcs->Validate() || !m_xlx->Validate() || + !m_starNet1->Validate() || !m_starNet2->Validate() || !m_starNet3->Validate() || !m_starNet4->Validate() || + !m_starNet5->Validate() || !m_remote->Validate() || !m_miscellaneous->Validate()) + return; + + GATEWAY_TYPE gatewayType = m_gateway->getType(); + wxString gatewayCallsign = m_gateway->getCallsign(); + wxString gatewayAddress = m_gateway->getAddress(); + wxString icomAddress = m_gateway->getIcomAddress(); + unsigned int icomPort = m_gateway->getIcomPort(); + wxString hbAddress = m_gateway->getHBAddress(); + unsigned int hbPort = m_gateway->getHBPort(); + double latitude = m_gateway->getLatitude(); + double longitude = m_gateway->getLongitude(); + wxString description1 = m_gateway->getDescription1(); + wxString description2 = m_gateway->getDescription2(); + wxString url = m_gateway->getURL(); + m_config->setGateway(gatewayType, gatewayCallsign, gatewayAddress, icomAddress, icomPort, hbAddress, hbPort, latitude, longitude, description1, description2, url); + + wxString repeaterBand1 = m_repeaterData1->getBand(); + HW_TYPE repeaterType1 = m_repeaterData1->getType(); + wxString repeaterAddress1 = m_repeaterData1->getAddress(); + unsigned int repeaterPort1 = m_repeaterData1->getPort(); + unsigned char band11 = m_repeaterData1->getBand1(); + unsigned char band12 = m_repeaterData1->getBand2(); + unsigned char band13 = m_repeaterData1->getBand3(); + wxString reflector1 = m_repeaterData1->getReflector(); + bool atStartup1 = m_repeaterData1->atStartup(); + RECONNECT reconnect1 = m_repeaterData1->getReconnect(); + double frequency1 = m_repeaterInfo1->getFrequency(); + double offset1 = m_repeaterInfo1->getOffset(); + double range1 = m_repeaterInfo1->getRange(); + double latitude1 = m_repeaterInfo1->getLatitude(); + double longitude1 = m_repeaterInfo1->getLongitude(); + double agl1 = m_repeaterInfo1->getAGL(); + wxString description11 = m_repeaterInfo1->getDescription1(); + wxString description12 = m_repeaterInfo1->getDescription2(); + wxString url1 = m_repeaterInfo1->getURL(); + m_config->setRepeater1(repeaterBand1, repeaterType1, repeaterAddress1, repeaterPort1, band11, band12, band13, reflector1, atStartup1, reconnect1, frequency1, offset1, range1, latitude1, longitude1, agl1, description11, description12, url1); + + wxString repeaterBand2 = m_repeaterData2->getBand(); + HW_TYPE repeaterType2 = m_repeaterData2->getType(); + wxString repeaterAddress2 = m_repeaterData2->getAddress(); + unsigned int repeaterPort2 = m_repeaterData2->getPort(); + unsigned char band21 = m_repeaterData2->getBand1(); + unsigned char band22 = m_repeaterData2->getBand2(); + unsigned char band23 = m_repeaterData2->getBand3(); + wxString reflector2 = m_repeaterData2->getReflector(); + bool atStartup2 = m_repeaterData2->atStartup(); + RECONNECT reconnect2 = m_repeaterData2->getReconnect(); + double frequency2 = m_repeaterInfo2->getFrequency(); + double offset2 = m_repeaterInfo2->getOffset(); + double range2 = m_repeaterInfo2->getRange(); + double latitude2 = m_repeaterInfo2->getLatitude(); + double longitude2 = m_repeaterInfo2->getLongitude(); + double agl2 = m_repeaterInfo2->getAGL(); + wxString description21 = m_repeaterInfo2->getDescription1(); + wxString description22 = m_repeaterInfo2->getDescription2(); + wxString url2 = m_repeaterInfo2->getURL(); + m_config->setRepeater2(repeaterBand2, repeaterType2, repeaterAddress2, repeaterPort2, band21, band22, band23, reflector2, atStartup2, reconnect2, frequency2, offset2, range2, latitude2, longitude2, agl2, description21, description22, url2); + + wxString repeaterBand3 = m_repeaterData3->getBand(); + HW_TYPE repeaterType3 = m_repeaterData3->getType(); + wxString repeaterAddress3 = m_repeaterData3->getAddress(); + unsigned int repeaterPort3 = m_repeaterData3->getPort(); + unsigned char band31 = m_repeaterData3->getBand1(); + unsigned char band32 = m_repeaterData3->getBand2(); + unsigned char band33 = m_repeaterData3->getBand3(); + wxString reflector3 = m_repeaterData3->getReflector(); + bool atStartup3 = m_repeaterData3->atStartup(); + RECONNECT reconnect3 = m_repeaterData3->getReconnect(); + double frequency3 = m_repeaterInfo3->getFrequency(); + double offset3 = m_repeaterInfo3->getOffset(); + double range3 = m_repeaterInfo3->getRange(); + double latitude3 = m_repeaterInfo3->getLatitude(); + double longitude3 = m_repeaterInfo3->getLongitude(); + double agl3 = m_repeaterInfo3->getAGL(); + wxString description31 = m_repeaterInfo3->getDescription1(); + wxString description32 = m_repeaterInfo3->getDescription2(); + wxString url3 = m_repeaterInfo3->getURL(); + m_config->setRepeater3(repeaterBand3, repeaterType3, repeaterAddress3, repeaterPort3, band31, band32, band33, reflector3, atStartup3, reconnect3, frequency3, offset3, range3, latitude3, longitude3, agl3, description31, description32, url3); + + wxString repeaterBand4 = m_repeaterData4->getBand(); + HW_TYPE repeaterType4 = m_repeaterData4->getType(); + wxString repeaterAddress4 = m_repeaterData4->getAddress(); + unsigned int repeaterPort4 = m_repeaterData4->getPort(); + unsigned char band41 = m_repeaterData4->getBand1(); + unsigned char band42 = m_repeaterData4->getBand2(); + unsigned char band43 = m_repeaterData4->getBand3(); + wxString reflector4 = m_repeaterData4->getReflector(); + bool atStartup4 = m_repeaterData4->atStartup(); + RECONNECT reconnect4 = m_repeaterData4->getReconnect(); + double frequency4 = m_repeaterInfo4->getFrequency(); + double offset4 = m_repeaterInfo4->getOffset(); + double range4 = m_repeaterInfo4->getRange(); + double latitude4 = m_repeaterInfo4->getLatitude(); + double longitude4 = m_repeaterInfo4->getLongitude(); + double agl4 = m_repeaterInfo4->getAGL(); + wxString description41 = m_repeaterInfo4->getDescription1(); + wxString description42 = m_repeaterInfo4->getDescription2(); + wxString url4 = m_repeaterInfo4->getURL(); + m_config->setRepeater4(repeaterBand4, repeaterType4, repeaterAddress4, repeaterPort4, band41, band42, band43, reflector4, atStartup4, reconnect4, frequency4, offset4, range4, latitude4, longitude4, agl4, description41, description42, url4); + + bool ircDDBEnabled = m_ircDDB->getEnabled(); + wxString ircDDBHostname = m_ircDDB->getHostname(); + wxString ircDDBUsername = m_ircDDB->getUsername(); + wxString ircDDBPassword = m_ircDDB->getPassword(); + m_config->setIrcDDB(ircDDBEnabled, ircDDBHostname, ircDDBUsername, ircDDBPassword); + + ircDDBEnabled = m_ircDDB2->getEnabled(); + ircDDBHostname = m_ircDDB2->getHostname(); + ircDDBUsername = m_ircDDB2->getUsername(); + ircDDBPassword = m_ircDDB2->getPassword(); + m_config->setIrcDDB2(ircDDBEnabled, ircDDBHostname, ircDDBUsername, ircDDBPassword); + + ircDDBEnabled = m_ircDDB3->getEnabled(); + ircDDBHostname = m_ircDDB3->getHostname(); + ircDDBUsername = m_ircDDB3->getUsername(); + ircDDBPassword = m_ircDDB3->getPassword(); + m_config->setIrcDDB3(ircDDBEnabled, ircDDBHostname, ircDDBUsername, ircDDBPassword); + + ircDDBEnabled = m_ircDDB4->getEnabled(); + ircDDBHostname = m_ircDDB4->getHostname(); + ircDDBUsername = m_ircDDB4->getUsername(); + ircDDBPassword = m_ircDDB4->getPassword(); + m_config->setIrcDDB4(ircDDBEnabled, ircDDBHostname, ircDDBUsername, ircDDBPassword); + + bool aprsEnabled = m_dprs->getEnabled(); + wxString aprsHostname = m_dprs->getHostname(); + unsigned int aprsPort = m_dprs->getPort(); + m_config->setDPRS(aprsEnabled, aprsHostname, aprsPort); + + bool dextraEnabled = m_dextra->getEnabled(); + unsigned int maxDExtraDongles = m_dextra->getMaxDongles(); + m_config->setDExtra(dextraEnabled, maxDExtraDongles); + + bool dplusEnabled = m_dplus->getEnabled(); + unsigned int maxDPlusDongles = m_dplus->getMaxDongles(); + wxString dplusLogin = m_dplus->getLogin(); + m_config->setDPlus(dplusEnabled, maxDPlusDongles, dplusLogin); + + bool dcsEnabled = m_dcs->getDCSEnabled(); + bool ccsEnabled = m_dcs->getCCSEnabled(); + wxString ccsHost = m_dcs->getCCSHost(); + m_config->setDCS(dcsEnabled, ccsEnabled, ccsHost); + + bool xlxEnabled = m_xlx->getXLXEnabled(); + bool xlxOverrideLocal = m_xlx->getXLXOverrideLocal(); + wxString xlxHostsFileUrl = m_xlx->getXLXHostsFileUrl(); + m_config->setXLX(xlxEnabled, xlxOverrideLocal, xlxHostsFileUrl); + + wxString starNetBand1 = m_starNet1->getBand(); + wxString starNetCallsign1 = m_starNet1->getCallsign(); + wxString starNetLogoff1 = m_starNet1->getLogoff(); + wxString starNetInfo1 = m_starNet1->getInfo(); + wxString starNetPermanent1 = m_starNet1->getPermanent(); + unsigned int starNetUserTimeout1 = m_starNet1->getUserTimeout(); + unsigned int starNetGroupTimeout1 = m_starNet1->getGroupTimeout(); + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch1 = m_starNet1->getCallsignSwitch(); + bool starNetTXMsgSwitch1 = m_starNet1->getTXMsgSwitch(); +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + wxString starNetLink1 = m_starNet1->getReflector(); + m_config->setStarNet1(starNetBand1, starNetCallsign1, starNetLogoff1, starNetInfo1, starNetPermanent1, starNetUserTimeout1, starNetGroupTimeout1, starNetCallsignSwitch1, starNetTXMsgSwitch1, starNetLink1); +#else + m_config->setStarNet1(starNetBand1, starNetCallsign1, starNetLogoff1, starNetInfo1, starNetPermanent1, starNetUserTimeout1, starNetGroupTimeout1, starNetCallsignSwitch1, starNetTXMsgSwitch1); +#endif + + wxString starNetBand2 = m_starNet2->getBand(); + wxString starNetCallsign2 = m_starNet2->getCallsign(); + wxString starNetLogoff2 = m_starNet2->getLogoff(); + wxString starNetInfo2 = m_starNet2->getInfo(); + wxString starNetPermanent2 = m_starNet2->getPermanent(); + unsigned int starNetUserTimeout2 = m_starNet2->getUserTimeout(); + unsigned int starNetGroupTimeout2 = m_starNet2->getGroupTimeout(); + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch2 = m_starNet2->getCallsignSwitch(); + bool starNetTXMsgSwitch2 = m_starNet2->getTXMsgSwitch(); +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + wxString starNetLink2 = m_starNet2->getReflector(); + m_config->setStarNet2(starNetBand2, starNetCallsign2, starNetLogoff2, starNetInfo2, starNetPermanent2, starNetUserTimeout2, starNetGroupTimeout2, starNetCallsignSwitch2, starNetTXMsgSwitch2, starNetLink2); +#else + m_config->setStarNet2(starNetBand2, starNetCallsign2, starNetLogoff2, starNetInfo2, starNetPermanent2, starNetUserTimeout2, starNetGroupTimeout2, starNetCallsignSwitch2, starNetTXMsgSwitch2); +#endif + + wxString starNetBand3 = m_starNet3->getBand(); + wxString starNetCallsign3 = m_starNet3->getCallsign(); + wxString starNetLogoff3 = m_starNet3->getLogoff(); + wxString starNetInfo3 = m_starNet3->getInfo(); + wxString starNetPermanent3 = m_starNet3->getPermanent(); + unsigned int starNetUserTimeout3 = m_starNet3->getUserTimeout(); + unsigned int starNetGroupTimeout3 = m_starNet3->getGroupTimeout(); + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch3 = m_starNet3->getCallsignSwitch(); + bool starNetTXMsgSwitch3 = m_starNet3->getTXMsgSwitch(); +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + wxString starNetLink3 = m_starNet3->getReflector(); + m_config->setStarNet3(starNetBand3, starNetCallsign3, starNetLogoff3, starNetInfo3, starNetPermanent3, starNetUserTimeout3, starNetGroupTimeout3, starNetCallsignSwitch3, starNetTXMsgSwitch3, starNetLink3); +#else + m_config->setStarNet3(starNetBand3, starNetCallsign3, starNetLogoff3, starNetInfo3, starNetPermanent3, starNetUserTimeout3, starNetGroupTimeout3, starNetCallsignSwitch3, starNetTXMsgSwitch3); +#endif + + wxString starNetBand4 = m_starNet4->getBand(); + wxString starNetCallsign4 = m_starNet4->getCallsign(); + wxString starNetLogoff4 = m_starNet4->getLogoff(); + wxString starNetInfo4 = m_starNet4->getInfo(); + wxString starNetPermanent4 = m_starNet4->getPermanent(); + unsigned int starNetUserTimeout4 = m_starNet4->getUserTimeout(); + unsigned int starNetGroupTimeout4 = m_starNet4->getGroupTimeout(); + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch4 = m_starNet4->getCallsignSwitch(); + bool starNetTXMsgSwitch4 = m_starNet4->getTXMsgSwitch(); +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + wxString starNetLink4 = m_starNet4->getReflector(); + m_config->setStarNet4(starNetBand4, starNetCallsign4, starNetLogoff4, starNetInfo4, starNetPermanent4, starNetUserTimeout4, starNetGroupTimeout4, starNetCallsignSwitch4, starNetTXMsgSwitch4, starNetLink4); +#else + m_config->setStarNet4(starNetBand4, starNetCallsign4, starNetLogoff4, starNetInfo4, starNetPermanent4, starNetUserTimeout4, starNetGroupTimeout4, starNetCallsignSwitch4, starNetTXMsgSwitch4); +#endif + + wxString starNetBand5 = m_starNet5->getBand(); + wxString starNetCallsign5 = m_starNet5->getCallsign(); + wxString starNetLogoff5 = m_starNet5->getLogoff(); + wxString starNetInfo5 = m_starNet5->getInfo(); + wxString starNetPermanent5 = m_starNet5->getPermanent(); + unsigned int starNetUserTimeout5 = m_starNet5->getUserTimeout(); + unsigned int starNetGroupTimeout5 = m_starNet5->getGroupTimeout(); + STARNET_CALLSIGN_SWITCH starNetCallsignSwitch5 = m_starNet5->getCallsignSwitch(); + bool starNetTXMsgSwitch5 = m_starNet5->getTXMsgSwitch(); +#if defined(DEXTRA_LINK) || defined(DCS_LINK) + wxString starNetLink5 = m_starNet5->getReflector(); + m_config->setStarNet5(starNetBand5, starNetCallsign5, starNetLogoff5, starNetInfo5, starNetPermanent5, starNetUserTimeout5, starNetGroupTimeout5, starNetCallsignSwitch5, starNetTXMsgSwitch5, starNetLink5); +#else + m_config->setStarNet5(starNetBand5, starNetCallsign5, starNetLogoff5, starNetInfo5, starNetPermanent5, starNetUserTimeout5, starNetGroupTimeout5, starNetCallsignSwitch5, starNetTXMsgSwitch5); +#endif + + bool remoteEnabled = m_remote->getEnabled(); + wxString remotePassword = m_remote->getPassword(); + unsigned int remotePort = m_remote->getPort(); + m_config->setRemote(remoteEnabled, remotePassword, remotePort); + + TEXT_LANG language = m_miscellaneous->getLanguage(); + bool infoEnabled = m_miscellaneous->getInfoEnabled(); + bool echoEnabled = m_miscellaneous->getEchoEnabled(); + bool logEnabled = m_miscellaneous->getLogEnabled(); + bool dratsEnabled = m_miscellaneous->getDRATSEnabled(); + bool dtmfEnabled = m_miscellaneous->getDTMFEnabled(); + m_config->setMiscellaneous(language, infoEnabled, echoEnabled, logEnabled, dratsEnabled, dtmfEnabled); + + bool ret = m_config->write(); + if (!ret) { + wxMessageDialog dialog(this, _("There was an error when writing the ircDDB Gateway configuration file"), _("Error"), wxICON_ERROR); + dialog.ShowModal(); + } else { + wxMessageDialog dialog(this, _("The changes made will not take effect\nuntil the ircDDB Gateway is (re)started"), _("Information"), wxICON_INFORMATION); + dialog.ShowModal(); + } +} + +void CIRCDDBGatewayConfigFrame::onAbout(wxCommandEvent&) +{ + wxAboutDialogInfo info; + info.AddDeveloper(wxT("Jonathan Naylor, G4KLX")); + info.SetCopyright(wxT("(C) 2010-2015 using GPL v2 or later")); + info.SetName(APPLICATION_NAME); + info.SetVersion(VERSION); + info.SetDescription(_("This program configures the ircDDB Gateway.")); + + ::wxAboutBox(info); +} diff --git a/ircDDBGatewayConfig/IRCDDBGatewayConfigFrame.h b/ircDDBGatewayConfig/IRCDDBGatewayConfigFrame.h new file mode 100644 index 0000000..15032c0 --- /dev/null +++ b/ircDDBGatewayConfig/IRCDDBGatewayConfigFrame.h @@ -0,0 +1,82 @@ +/* + * 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 + * 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 IRCDDBGatewayConfigFrame_H +#define IRCDDBGatewayConfigFrame_H + +#include "IRCDDBGatewayConfigMiscellaneousSet.h" +#include "IRCDDBGatewayConfigGatewaySet.h" +#include "IRCDDBGatewayConfigIrcDDBSet.h" +#include "IRCDDBGatewayConfig.h" +#include "RepeaterInfoSet.h" +#include "RepeaterDataSet.h" +#include "StarNetSet.h" +#include "RemoteSet.h" +#include "DExtraSet.h" +#include "DPlusSet.h" +#include "DPRSSet.h" +#include "DCSSet.h" +#include "XLXSet.h" +#include "Defs.h" + +#include + +class CIRCDDBGatewayConfigFrame : public wxFrame { +public: + CIRCDDBGatewayConfigFrame(const wxString& title, const wxString& confDir, const wxString& name); + virtual ~CIRCDDBGatewayConfigFrame(); + + virtual void onQuit(wxCommandEvent& event); + virtual void onSave(wxCommandEvent& event); + virtual void onAbout(wxCommandEvent& event); + virtual void onClose(wxCloseEvent& event); + +private: + CIRCDDBGatewayConfig* m_config; + CIRCDDBGatewayConfigGatewaySet* m_gateway; + CRepeaterDataSet* m_repeaterData1; + CRepeaterInfoSet* m_repeaterInfo1; + CRepeaterDataSet* m_repeaterData2; + CRepeaterInfoSet* m_repeaterInfo2; + CRepeaterDataSet* m_repeaterData3; + CRepeaterInfoSet* m_repeaterInfo3; + CRepeaterDataSet* m_repeaterData4; + CRepeaterInfoSet* m_repeaterInfo4; + CIRCDDBGatewayConfigIrcDDBSet* m_ircDDB; + CIRCDDBGatewayConfigIrcDDBSet* m_ircDDB2; + CIRCDDBGatewayConfigIrcDDBSet* m_ircDDB3; + CIRCDDBGatewayConfigIrcDDBSet* m_ircDDB4; + CDPRSSet* m_dprs; + CDExtraSet* m_dextra; + CDPlusSet* m_dplus; + CDCSSet* m_dcs; + CXLXSet* m_xlx; + CStarNetSet* m_starNet1; + CStarNetSet* m_starNet2; + CStarNetSet* m_starNet3; + CStarNetSet* m_starNet4; + CStarNetSet* m_starNet5; + CRemoteSet* m_remote; + CIRCDDBGatewayConfigMiscellaneousSet* m_miscellaneous; + + DECLARE_EVENT_TABLE() + + wxMenuBar* createMenuBar(); +}; + +#endif diff --git a/ircDDBGatewayConfig/IRCDDBGatewayConfigGatewaySet.cpp b/ircDDBGatewayConfig/IRCDDBGatewayConfigGatewaySet.cpp new file mode 100644 index 0000000..4f469a9 --- /dev/null +++ b/ircDDBGatewayConfig/IRCDDBGatewayConfigGatewaySet.cpp @@ -0,0 +1,320 @@ +/* + * 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 + * 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 "IRCDDBGatewayConfigGatewaySet.h" +#include + +const unsigned int DESCRIPTION_WIDTH = 120U; +const unsigned int ADDRESS_WIDTH = 120U; +const unsigned int PORT_WIDTH = 80U; + +const unsigned int DESCRIPTION_LENGTH = 20U; +const unsigned int ADDRESS_LENGTH = 15U; +const unsigned int PORT_LENGTH = 5U; + +const unsigned int BORDER_SIZE = 5U; + +CIRCDDBGatewayConfigGatewaySet::CIRCDDBGatewayConfigGatewaySet(wxWindow* parent, int id, const wxString& title, GATEWAY_TYPE type, const wxString& callsign, const wxString& address, const wxString& icomAddress, unsigned int icomPort, const wxString& hbAddress, unsigned int hbPort, double latitude, double longitude, const wxString& description1, const wxString& description2, const wxString& url) : +wxPanel(parent, id), +m_title(title), +m_type(NULL), +m_callsign(NULL), +m_address(NULL), +m_icomAddress(NULL), +m_icomPort(NULL), +m_hbAddress(NULL), +m_hbPort(NULL), +m_latitude(NULL), +m_longitude(NULL), +m_description1(NULL), +m_description2(NULL), +m_url(NULL) +{ + wxFlexGridSizer* sizer = new wxFlexGridSizer(3); + + wxStaticText* typeLabel = new wxStaticText(this, -1, _("Type")); + sizer->Add(typeLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_type = new wxChoice(this, -1); + m_type->Append(_("Repeater")); + m_type->Append(_("Hotspot")); + m_type->Append(_("Dongle")); + sizer->Add(m_type, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_type->SetSelection(int(type)); + + wxStaticText* dummy10Label = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummy10Label, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxStaticText* callsignLabel = new wxStaticText(this, -1, _("Callsign")); + sizer->Add(callsignLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxString call = callsign; + call.Truncate(LONG_CALLSIGN_LENGTH - 1U); + call.Trim(); + + m_callsign = new CCallsignTextCtrl(this, -1, call, wxDefaultPosition, wxSize(ADDRESS_WIDTH, -1)); + m_callsign->SetMaxLength(LONG_CALLSIGN_LENGTH); + sizer->Add(m_callsign, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* gLabel = new wxStaticText(this, -1, wxT("G")); + sizer->Add(gLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxStaticText* addressLabel = new wxStaticText(this, -1, _("Gateway Address")); + sizer->Add(addressLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_address = new CAddressTextCtrl(this, -1, address, wxDefaultPosition, wxSize(ADDRESS_WIDTH, -1)); + m_address->SetMaxLength(ADDRESS_LENGTH); + sizer->Add(m_address, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* dummy0Label = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummy0Label, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxStaticText* icomAddressLabel = new wxStaticText(this, -1, _("Local Icom Address")); + sizer->Add(icomAddressLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_icomAddress = new CAddressTextCtrl(this, -1, icomAddress, wxDefaultPosition, wxSize(ADDRESS_WIDTH, -1)); + m_icomAddress->SetMaxLength(ADDRESS_LENGTH); + sizer->Add(m_icomAddress, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* dummy1Label = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummy1Label, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxStaticText* icomPortLabel = new wxStaticText(this, -1, _("Local Icom Port")); + sizer->Add(icomPortLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxString buffer; + buffer.Printf(wxT("%u"), icomPort); + + m_icomPort = new CPortTextCtrl(this, -1, buffer, wxDefaultPosition, wxSize(PORT_WIDTH, -1)); + m_icomPort->SetMaxLength(PORT_LENGTH); + sizer->Add(m_icomPort, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* dummy2Label = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummy2Label, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxStaticText* hbAddressLabel = new wxStaticText(this, -1, _("Local HB Address")); + sizer->Add(hbAddressLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_hbAddress = new CAddressTextCtrl(this, -1, hbAddress, wxDefaultPosition, wxSize(ADDRESS_WIDTH, -1)); + m_hbAddress->SetMaxLength(ADDRESS_LENGTH); + sizer->Add(m_hbAddress, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* dummy3Label = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummy3Label, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxStaticText* hbPortLabel = new wxStaticText(this, -1, _("Local HB Port")); + sizer->Add(hbPortLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + buffer.Printf(wxT("%u"), hbPort); + + m_hbPort = new CPortTextCtrl(this, -1, buffer, wxDefaultPosition, wxSize(PORT_WIDTH, -1)); + m_hbPort->SetMaxLength(PORT_LENGTH); + sizer->Add(m_hbPort, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* dummy4Label = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummy4Label, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxStaticText* latitudeLabel = new wxStaticText(this, -1, _("Latitude")); + sizer->Add(latitudeLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + buffer.Printf(wxT("%lf"), latitude); + + m_latitude = new wxTextCtrl(this, -1, buffer, wxDefaultPosition, wxSize(PORT_WIDTH, -1)); + sizer->Add(m_latitude, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* dummy5Label = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummy5Label, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxStaticText* longitudeLabel = new wxStaticText(this, -1, _("Longitude")); + sizer->Add(longitudeLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + buffer.Printf(wxT("%lf"), longitude); + + m_longitude = new wxTextCtrl(this, -1, buffer, wxDefaultPosition, wxSize(PORT_WIDTH, -1)); + sizer->Add(m_longitude, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* dummy6Label = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummy6Label, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxStaticText* descriptionLabel = new wxStaticText(this, -1, _("QTH")); + sizer->Add(descriptionLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_description1 = new CDescriptionTextCtrl(this, -1, description1, wxDefaultPosition, wxSize(DESCRIPTION_WIDTH, -1)); + m_description1->SetMaxLength(DESCRIPTION_LENGTH); + sizer->Add(m_description1, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* dummy7Label = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummy7Label, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxStaticText* dummy8Label = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummy8Label, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_description2 = new CDescriptionTextCtrl(this, -1, description2, wxDefaultPosition, wxSize(DESCRIPTION_WIDTH, -1)); + m_description2->SetMaxLength(DESCRIPTION_LENGTH); + sizer->Add(m_description2, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* dummy9Label = new wxStaticText(this, -1, wxEmptyString); + sizer->Add(dummy9Label, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + wxStaticText* urlLabel = new wxStaticText(this, -1, _("URL")); + sizer->Add(urlLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_url = new CDescriptionTextCtrl(this, -1, url, wxDefaultPosition, wxSize(DESCRIPTION_WIDTH, -1)); + sizer->Add(m_url, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + SetAutoLayout(true); + + SetSizer(sizer); +} + + +CIRCDDBGatewayConfigGatewaySet::~CIRCDDBGatewayConfigGatewaySet() +{ +} + +bool CIRCDDBGatewayConfigGatewaySet::Validate() +{ + int n = m_type->GetCurrentSelection(); + if (n == wxNOT_FOUND) + return false; + + wxString callsign = getCallsign(); + + if (callsign.IsEmpty()) { + wxMessageDialog dialog(this, _("The Gateway Callsign is not valid"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + unsigned int port = getIcomPort(); + + if (port == 0U || port > 65535U) { + wxMessageDialog dialog(this, _("The Icom Port is not valid"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + port = getHBPort(); + + if (port == 0U || port > 65535U) { + wxMessageDialog dialog(this, _("The Homebrew Port is not valid"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + double latitude = getLatitude(); + + if (latitude < -90.0 || latitude > 90.0) { + wxMessageDialog dialog(this, _("The Latitude is invalid"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + double longitude = getLongitude(); + + if (longitude < -180.0 || longitude > 180.0) { + wxMessageDialog dialog(this, _("The Longitude is invalid"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + return true; +} + +GATEWAY_TYPE CIRCDDBGatewayConfigGatewaySet::getType() const +{ + int n = m_type->GetCurrentSelection(); + if (n == wxNOT_FOUND) + return GT_REPEATER; + + return GATEWAY_TYPE(n); +} + +wxString CIRCDDBGatewayConfigGatewaySet::getCallsign() const +{ + wxString callsign = m_callsign->GetValue(); + + callsign.MakeUpper(); + + return callsign; +} + +wxString CIRCDDBGatewayConfigGatewaySet::getAddress() const +{ + return m_address->GetValue(); +} + +wxString CIRCDDBGatewayConfigGatewaySet::getIcomAddress() const +{ + return m_icomAddress->GetValue(); +} + +unsigned int CIRCDDBGatewayConfigGatewaySet::getIcomPort() const +{ + unsigned long n; + m_icomPort->GetValue().ToULong(&n); + + return n; +} + +wxString CIRCDDBGatewayConfigGatewaySet::getHBAddress() const +{ + return m_hbAddress->GetValue(); +} + +unsigned int CIRCDDBGatewayConfigGatewaySet::getHBPort() const +{ + unsigned long n; + m_hbPort->GetValue().ToULong(&n); + + return n; +} + +double CIRCDDBGatewayConfigGatewaySet::getLatitude() const +{ + double val; + + m_latitude->GetValue().ToDouble(&val); + + return val; +} + +double CIRCDDBGatewayConfigGatewaySet::getLongitude() const +{ + double val; + + m_longitude->GetValue().ToDouble(&val); + + return val; +} + +wxString CIRCDDBGatewayConfigGatewaySet::getDescription1() const +{ + return m_description1->GetValue(); +} + +wxString CIRCDDBGatewayConfigGatewaySet::getDescription2() const +{ + return m_description2->GetValue(); +} + +wxString CIRCDDBGatewayConfigGatewaySet::getURL() const +{ + return m_url->GetValue(); +} diff --git a/ircDDBGatewayConfig/IRCDDBGatewayConfigGatewaySet.h b/ircDDBGatewayConfig/IRCDDBGatewayConfigGatewaySet.h new file mode 100644 index 0000000..658bf40 --- /dev/null +++ b/ircDDBGatewayConfig/IRCDDBGatewayConfigGatewaySet.h @@ -0,0 +1,66 @@ +/* + * 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 + * 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 IRCDDBGatewayConfigGatewaySet_H +#define IRCDDBGatewayConfigGatewaySet_H + +#include "DescriptionTextCtrl.h" +#include "CallsignTextCtrl.h" +#include "AddressTextCtrl.h" +#include "PortTextCtrl.h" +#include "Defs.h" + +#include + +class CIRCDDBGatewayConfigGatewaySet : public wxPanel { +public: + CIRCDDBGatewayConfigGatewaySet(wxWindow* parent, int id, const wxString& title, GATEWAY_TYPE type, const wxString& callsign, const wxString& address, const wxString& icomAddress, unsigned int icomPort, const wxString& hbAddress, unsigned int hbPort, double latitude, double longitude, const wxString& description1, const wxString& description2, const wxString& url); + virtual ~CIRCDDBGatewayConfigGatewaySet(); + + virtual bool Validate(); + + virtual GATEWAY_TYPE getType() const; + virtual wxString getCallsign() const; + virtual wxString getAddress() const; + virtual wxString getIcomAddress() const; + virtual unsigned int getIcomPort() const; + virtual wxString getHBAddress() const; + virtual unsigned int getHBPort() const; + virtual double getLatitude() const; + virtual double getLongitude() const; + virtual wxString getDescription1() const; + virtual wxString getDescription2() const; + virtual wxString getURL() const; + +private: + wxString m_title; + wxChoice* m_type; + CCallsignTextCtrl* m_callsign; + CAddressTextCtrl* m_address; + CAddressTextCtrl* m_icomAddress; + CPortTextCtrl* m_icomPort; + CAddressTextCtrl* m_hbAddress; + CPortTextCtrl* m_hbPort; + wxTextCtrl* m_latitude; + wxTextCtrl* m_longitude; + CDescriptionTextCtrl* m_description1; + CDescriptionTextCtrl* m_description2; + CDescriptionTextCtrl* m_url; +}; + +#endif diff --git a/ircDDBGatewayConfig/IRCDDBGatewayConfigIrcDDBSet.cpp b/ircDDBGatewayConfig/IRCDDBGatewayConfigIrcDDBSet.cpp new file mode 100644 index 0000000..af4fd83 --- /dev/null +++ b/ircDDBGatewayConfig/IRCDDBGatewayConfigIrcDDBSet.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2010,2012,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 "IRCDDBGatewayConfigIrcDDBSet.h" + +const unsigned int BORDER_SIZE = 5U; +const unsigned int CONTROL_WIDTH1 = 200U; +const unsigned int CONTROL_WIDTH2 = 80U; + +const unsigned int PORT_LENGTH = 5U; + +CIRCDDBGatewayConfigIrcDDBSet::CIRCDDBGatewayConfigIrcDDBSet(wxWindow* parent, int id, const wxString& title, bool enabled, const wxString& hostname, const wxString& username, const wxString& password) : +wxPanel(parent, id), +m_title(title), +m_enabled(NULL), +m_hostname(NULL), +m_username(NULL), +m_password(NULL) +{ + wxFlexGridSizer* sizer = new wxFlexGridSizer(2); + + wxStaticText* enabledLabel = new wxStaticText(this, -1, _("ircDDB")); + sizer->Add(enabledLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_enabled = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_enabled->Append(_("Disabled")); + m_enabled->Append(_("Enabled")); + sizer->Add(m_enabled, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_enabled->SetSelection(enabled ? 1 : 0); + + wxStaticText* hostnameLabel = new wxStaticText(this, -1, _("Hostname")); + sizer->Add(hostnameLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_hostname = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1)); + m_hostname->Append(wxT("group1-irc.ircddb.net")); + m_hostname->Append(wxT("group2-irc.ircddb.net")); + m_hostname->Append(wxT("rr.openquad.net")); + m_hostname->Append(wxT("freestar-irc-cluster.ve3lsr.ca")); + m_hostname->Append(wxT("server1-ik2xyp.free-dstar.org")); + m_hostname->Append(wxT("ircddb.dstar.su")); + sizer->Add(m_hostname, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_hostname->SetStringSelection(hostname); + + wxStaticText* usernameLabel = new wxStaticText(this, -1, _("Username")); + sizer->Add(usernameLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_username = new wxTextCtrl(this, -1, username, wxDefaultPosition, wxSize(CONTROL_WIDTH2, -1)); + sizer->Add(m_username, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + wxStaticText* passwordLabel = new wxStaticText(this, -1, _("Password")); + sizer->Add(passwordLabel, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + m_password = new wxTextCtrl(this, -1, password, wxDefaultPosition, wxSize(CONTROL_WIDTH1, -1), wxTE_PASSWORD); + sizer->Add(m_password, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + SetAutoLayout(true); + + SetSizer(sizer); +} + + +CIRCDDBGatewayConfigIrcDDBSet::~CIRCDDBGatewayConfigIrcDDBSet() +{ +} + +bool CIRCDDBGatewayConfigIrcDDBSet::Validate() +{ + int n = m_enabled->GetCurrentSelection(); + if (n == wxNOT_FOUND) + return false; + + bool enabled = getEnabled(); + if (!enabled) + return true; + + bool res = getHostname().IsEmpty(); + if (res) { + wxMessageDialog dialog(this, _("The ircDDB Hostname may not be empty"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + res = getUsername().IsEmpty(); + if (res) { + wxMessageDialog dialog(this, _("The ircDDB Username may not be empty"), m_title + _(" Error"), wxICON_ERROR); + dialog.ShowModal(); + return false; + } + + return true; +} + +bool CIRCDDBGatewayConfigIrcDDBSet::getEnabled() const +{ + int c = m_enabled->GetCurrentSelection(); + if (c == wxNOT_FOUND) + return false; + + return c == 1; +} + +wxString CIRCDDBGatewayConfigIrcDDBSet::getHostname() const +{ + return m_hostname->GetStringSelection(); +} + +wxString CIRCDDBGatewayConfigIrcDDBSet::getUsername() const +{ + return m_username->GetValue(); +} + +wxString CIRCDDBGatewayConfigIrcDDBSet::getPassword() const +{ + return m_password->GetValue(); +} diff --git a/ircDDBGatewayConfig/IRCDDBGatewayConfigIrcDDBSet.h b/ircDDBGatewayConfig/IRCDDBGatewayConfigIrcDDBSet.h new file mode 100644 index 0000000..7b1936e --- /dev/null +++ b/ircDDBGatewayConfig/IRCDDBGatewayConfigIrcDDBSet.h @@ -0,0 +1,44 @@ +/* + * 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 + * 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 IRCDDBGatewayConfigIrcDDBSet_H +#define IRCDDBGatewayConfigIrcDDBSet_H + +#include + +class CIRCDDBGatewayConfigIrcDDBSet : public wxPanel { +public: + CIRCDDBGatewayConfigIrcDDBSet(wxWindow* parent, int id, const wxString& title, bool enabled, const wxString& hostname, const wxString& username, const wxString& password); + virtual ~CIRCDDBGatewayConfigIrcDDBSet(); + + virtual bool Validate(); + + virtual bool getEnabled() const; + virtual wxString getHostname() const; + virtual wxString getUsername() const; + virtual wxString getPassword() const; + +private: + wxString m_title; + wxChoice* m_enabled; + wxChoice* m_hostname; + wxTextCtrl* m_username; + wxTextCtrl* m_password; +}; + +#endif diff --git a/ircDDBGatewayConfig/IRCDDBGatewayConfigMiscellaneousSet.cpp b/ircDDBGatewayConfig/IRCDDBGatewayConfigMiscellaneousSet.cpp new file mode 100644 index 0000000..fa6cf95 --- /dev/null +++ b/ircDDBGatewayConfig/IRCDDBGatewayConfigMiscellaneousSet.cpp @@ -0,0 +1,185 @@ +/* + * 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 + * 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 "IRCDDBGatewayConfigMiscellaneousSet.h" +#include "DStarDefines.h" + +const unsigned int CONTROL_WIDTH = 130U; + +const unsigned int BORDER_SIZE = 5U; + +CIRCDDBGatewayConfigMiscellaneousSet::CIRCDDBGatewayConfigMiscellaneousSet(wxWindow* parent, int id, const wxString& title, TEXT_LANG language, bool infoEnabled, bool echoEnabled, bool logEnabled, bool dratsEnabled, bool dtmfEnabled) : +wxPanel(parent, id), +m_title(title), +m_language(NULL), +m_infoEnabled(NULL), +m_echoEnabled(NULL), +m_logEnabled(NULL), +m_dratsEnabled(NULL), +m_dtmfEnabled(NULL) +{ + wxFlexGridSizer* sizer = new wxFlexGridSizer(2); + + wxStaticText* languageLabel = new wxStaticText(this, -1, _("Language")); + sizer->Add(languageLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_language = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + m_language->Append(wxT("English (UK)")); + m_language->Append(wxT("Deutsch")); + m_language->Append(wxT("Dansk")); + m_language->Append(wxT("Francais")); + m_language->Append(wxT("Italiano")); + m_language->Append(wxT("Polski")); + m_language->Append(wxT("English (US)")); + m_language->Append(wxT("Espanol")); + m_language->Append(wxT("Svenska")); + m_language->Append(wxT("Nederlands (NL)")); + m_language->Append(wxT("Nederlands (BE)")); + m_language->Append(wxT("Norsk")); + m_language->Append(wxT("Portugues")); + sizer->Add(m_language, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_language->SetSelection(int(language)); + + wxStaticText* infoEnabledLabel = new wxStaticText(this, -1, _("Info Command")); + sizer->Add(infoEnabledLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_infoEnabled = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + m_infoEnabled->Append(_("Disabled")); + m_infoEnabled->Append(_("Enabled")); + sizer->Add(m_infoEnabled, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_infoEnabled->SetSelection(infoEnabled ? 1 : 0); + + wxStaticText* echoEnabledLabel = new wxStaticText(this, -1, _("Echo Command")); + sizer->Add(echoEnabledLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_echoEnabled = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + m_echoEnabled->Append(_("Disabled")); + m_echoEnabled->Append(_("Enabled")); + sizer->Add(m_echoEnabled, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_echoEnabled->SetSelection(echoEnabled ? 1 : 0); + + wxStaticText* logEnabledLabel = new wxStaticText(this, -1, _("GUI Log")); + sizer->Add(logEnabledLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_logEnabled = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + m_logEnabled->Append(_("Disabled")); + m_logEnabled->Append(_("Enabled")); + sizer->Add(m_logEnabled, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_logEnabled->SetSelection(logEnabled ? 1 : 0); + + wxStaticText* dratsEnabledLabel = new wxStaticText(this, -1, wxT("D-RATS")); + sizer->Add(dratsEnabledLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_dratsEnabled = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + m_dratsEnabled->Append(_("Disabled")); + m_dratsEnabled->Append(_("Enabled")); + sizer->Add(m_dratsEnabled, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_dratsEnabled->SetSelection(dratsEnabled ? 1 : 0); + + wxStaticText* dtmfEnabledLabel = new wxStaticText(this, -1, wxT("DTMF Control")); + sizer->Add(dtmfEnabledLabel, 0, wxALL | wxALIGN_RIGHT, BORDER_SIZE); + + m_dtmfEnabled = new wxChoice(this, -1, wxDefaultPosition, wxSize(CONTROL_WIDTH, -1)); + m_dtmfEnabled->Append(_("Disabled")); + m_dtmfEnabled->Append(_("Enabled")); + sizer->Add(m_dtmfEnabled, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + m_dtmfEnabled->SetSelection(dtmfEnabled ? 1 : 0); + + SetAutoLayout(true); + + SetSizer(sizer); +} + + +CIRCDDBGatewayConfigMiscellaneousSet::~CIRCDDBGatewayConfigMiscellaneousSet() +{ +} + +bool CIRCDDBGatewayConfigMiscellaneousSet::Validate() +{ + if (m_language->GetCurrentSelection() == wxNOT_FOUND) + return false; + + if (m_logEnabled->GetCurrentSelection() == wxNOT_FOUND) + return false; + + if (m_infoEnabled->GetCurrentSelection() == wxNOT_FOUND) + return false; + + if (m_echoEnabled->GetCurrentSelection() == wxNOT_FOUND) + return false; + + if (m_dratsEnabled->GetCurrentSelection() == wxNOT_FOUND) + return false; + + return m_dtmfEnabled->GetCurrentSelection() != wxNOT_FOUND; +} + +TEXT_LANG CIRCDDBGatewayConfigMiscellaneousSet::getLanguage() const +{ + int c = m_language->GetCurrentSelection(); + if (c == wxNOT_FOUND) + return TL_ENGLISH_UK; + + return TEXT_LANG(c); +} + +bool CIRCDDBGatewayConfigMiscellaneousSet::getInfoEnabled() const +{ + int c = m_infoEnabled->GetCurrentSelection(); + if (c == wxNOT_FOUND) + return false; + + return c == 1; +} + +bool CIRCDDBGatewayConfigMiscellaneousSet::getEchoEnabled() const +{ + int c = m_echoEnabled->GetCurrentSelection(); + if (c == wxNOT_FOUND) + return false; + + return c == 1; +} + +bool CIRCDDBGatewayConfigMiscellaneousSet::getLogEnabled() const +{ + int c = m_logEnabled->GetCurrentSelection(); + if (c == wxNOT_FOUND) + return false; + + return c == 1; +} + +bool CIRCDDBGatewayConfigMiscellaneousSet::getDRATSEnabled() const +{ + int c = m_dratsEnabled->GetCurrentSelection(); + if (c == wxNOT_FOUND) + return false; + + return c == 1; +} + +bool CIRCDDBGatewayConfigMiscellaneousSet::getDTMFEnabled() const +{ + int c = m_dtmfEnabled->GetCurrentSelection(); + if (c == wxNOT_FOUND) + return false; + + return c == 1; +} diff --git a/ircDDBGatewayConfig/IRCDDBGatewayConfigMiscellaneousSet.h b/ircDDBGatewayConfig/IRCDDBGatewayConfigMiscellaneousSet.h new file mode 100644 index 0000000..c5c81fd --- /dev/null +++ b/ircDDBGatewayConfig/IRCDDBGatewayConfigMiscellaneousSet.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2010,2011,2012 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 IRCDDBGatewayConfigMiscellaneousSet_H +#define IRCDDBGatewayConfigMiscellaneousSet_H + +#include "Defs.h" + +#include + +class CIRCDDBGatewayConfigMiscellaneousSet : public wxPanel { +public: + CIRCDDBGatewayConfigMiscellaneousSet(wxWindow* parent, int id, const wxString& title, TEXT_LANG language, bool infoEnabled, bool echoEnabled, bool logEnabled, bool dratsEnabled, bool dtmfEnabled); + virtual ~CIRCDDBGatewayConfigMiscellaneousSet(); + + virtual bool Validate(); + + virtual TEXT_LANG getLanguage() const; + + virtual bool getInfoEnabled() const; + + virtual bool getEchoEnabled() const; + + virtual bool getLogEnabled() const; + + virtual bool getDRATSEnabled() const; + + virtual bool getDTMFEnabled() const; + +private: + wxString m_title; + wxChoice* m_language; + wxChoice* m_infoEnabled; + wxChoice* m_echoEnabled; + wxChoice* m_logEnabled; + wxChoice* m_dratsEnabled; + wxChoice* m_dtmfEnabled; +}; + +#endif diff --git a/ircDDBGatewayConfig/ircDDBGatewayConfig.vcxproj b/ircDDBGatewayConfig/ircDDBGatewayConfig.vcxproj new file mode 100644 index 0000000..1d6186c --- /dev/null +++ b/ircDDBGatewayConfig/ircDDBGatewayConfig.vcxproj @@ -0,0 +1,198 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {D0A32505-822B-4936-A61B-A5151966AEAA} + ircDDBGatewayConfig + Win32Proj + 10.0.16299.0 + + + + Application + v141 + Unicode + true + + + Application + v141 + Unicode + true + + + Application + v141 + Unicode + + + Application + v141 + Unicode + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>14.0.24720.0 + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + $(WXWIN)\include;$(WXWIN)\lib\vc_dll\mswud;$(IncludePath) + + + true + $(WXWIN)\include;$(WXWIN)\lib\vc_x64_dll\mswud;$(IncludePath) + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + + + false + + + + Disabled + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;../GUICommon;../ircDDB;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;DCS_LINK;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + Level4 + EditAndContinue + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_dll;%(AdditionalLibraryDirectories) + true + Windows + MachineX86 + + + + + Disabled + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;../GUICommon;../ircDDB;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;__WXDEBUG__;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;DCS_LINK;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + + + Level4 + ProgramDatabase + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_x64_dll;%(AdditionalLibraryDirectories) + true + Windows + + + + + MaxSpeed + true + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;../GUICommon;../ircDDB;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + Level3 + ProgramDatabase + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_dll;%(AdditionalLibraryDirectories) + true + Windows + true + true + MachineX86 + + + + + MaxSpeed + true + $(WXWIN)\include\msvc;$(WXWIN)\include;../Common;../GUICommon;../ircDDB;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;WINVER=0x0400;__WXMSW__;WXUSINGDLL;wxUSE_GUI=1;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + + + ws2_32.lib;%(AdditionalDependencies) + $(WXWIN)\lib\vc_x64_dll;%(AdditionalLibraryDirectories) + true + Windows + true + true + + + + + + + + + + + + + + + + + + + + {e793cb8e-2ac9-431a-bbfc-3f52537bb3cf} + false + + + {02d03515-0bbe-4553-8c40-566a597478f8} + false + + + + + + \ No newline at end of file diff --git a/ircDDBGatewayConfig/ircDDBGatewayConfig.vcxproj.filters b/ircDDBGatewayConfig/ircDDBGatewayConfig.vcxproj.filters new file mode 100644 index 0000000..3d00a5e --- /dev/null +++ b/ircDDBGatewayConfig/ircDDBGatewayConfig.vcxproj.filters @@ -0,0 +1,50 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file