diff --git a/Conf.cpp b/Conf.cpp index b247556..13d9a62 100644 --- a/Conf.cpp +++ b/Conf.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2019 by Jonathan Naylor G4KLX + * Copyright (C) 2015-2020 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -37,7 +37,8 @@ enum SECTION { SECTION_DMR_NETWORK_3, SECTION_DMR_NETWORK_4, SECTION_DMR_NETWORK_5, - SECTION_XLX_NETWORK + SECTION_XLX_NETWORK, + SECTION_DYNAMIC_TG_CONTROL }; CConf::CConf(const std::string& file) : @@ -82,6 +83,7 @@ m_dmrNetwork1TGRewrites(), m_dmrNetwork1PCRewrites(), m_dmrNetwork1TypeRewrites(), m_dmrNetwork1SrcRewrites(), +m_dmrNetwork1TGDynRewrites(), m_dmrNetwork1IdRewrites(), m_dmrNetwork1PassAllPC(), m_dmrNetwork1PassAllTG(), @@ -99,6 +101,7 @@ m_dmrNetwork2TGRewrites(), m_dmrNetwork2PCRewrites(), m_dmrNetwork2TypeRewrites(), m_dmrNetwork2SrcRewrites(), +m_dmrNetwork2TGDynRewrites(), m_dmrNetwork2IdRewrites(), m_dmrNetwork2PassAllPC(), m_dmrNetwork2PassAllTG(), @@ -116,6 +119,7 @@ m_dmrNetwork3TGRewrites(), m_dmrNetwork3PCRewrites(), m_dmrNetwork3TypeRewrites(), m_dmrNetwork3SrcRewrites(), +m_dmrNetwork3TGDynRewrites(), m_dmrNetwork3IdRewrites(), m_dmrNetwork3PassAllPC(), m_dmrNetwork3PassAllTG(), @@ -133,6 +137,7 @@ m_dmrNetwork4TGRewrites(), m_dmrNetwork4PCRewrites(), m_dmrNetwork4TypeRewrites(), m_dmrNetwork4SrcRewrites(), +m_dmrNetwork4TGDynRewrites(), m_dmrNetwork4IdRewrites(), m_dmrNetwork4PassAllPC(), m_dmrNetwork4PassAllTG(), @@ -150,6 +155,7 @@ m_dmrNetwork5TGRewrites(), m_dmrNetwork5PCRewrites(), m_dmrNetwork5TypeRewrites(), m_dmrNetwork5SrcRewrites(), +m_dmrNetwork5TGDynRewrites(), m_dmrNetwork5IdRewrites(), m_dmrNetwork5PassAllPC(), m_dmrNetwork5PassAllTG(), @@ -167,7 +173,9 @@ m_xlxNetworkStartup(4000U), m_xlxNetworkRelink(0U), m_xlxNetworkDebug(false), m_xlxNetworkUserControl(true), -m_xlxNetworkModule() +m_xlxNetworkModule(), +m_dynamicTGControlEnabled(false), +m_dynamicTGControlPort(3769U) { } @@ -211,6 +219,8 @@ bool CConf::read() section = SECTION_DMR_NETWORK_4; else if (::strncmp(buffer, "[DMR Network 5]", 15U) == 0) section = SECTION_DMR_NETWORK_5; + else if (::strncmp(buffer, "[Dynamic TG Control]", 20U) == 0) + section = SECTION_DYNAMIC_TG_CONTROL; else section = SECTION_NONE; @@ -376,13 +386,15 @@ bool CConf::read() char* p1 = ::strtok(value, ", "); char* p2 = ::strtok(NULL, ", "); char* p3 = ::strtok(NULL, ", "); - char* p4 = ::strtok(NULL, " \r\n"); + char* p4 = ::strtok(NULL, ", \r\n"); if (p1 != NULL && p2 != NULL && p3 != NULL && p4 != NULL) { CTypeRewriteStruct rewrite; rewrite.m_fromSlot = ::atoi(p1); rewrite.m_fromTG = ::atoi(p2); rewrite.m_toSlot = ::atoi(p3); rewrite.m_toId = ::atoi(p4); + char* p5 = ::strtok(NULL, " \r\n"); + rewrite.m_range = p5 != NULL ? ::atoi(p5) : 1; m_dmrNetwork1TypeRewrites.push_back(rewrite); } } else if (::strncmp(key, "SrcRewrite", 10U) == 0) { @@ -400,6 +412,31 @@ bool CConf::read() rewrite.m_range = ::atoi(p5); m_dmrNetwork1SrcRewrites.push_back(rewrite); } + } else if (::strncmp(key, "TGDynRewrite", 12U) == 0) { + std::vector p7; + char* p1 = ::strtok(value, ", "); + char* p2 = ::strtok(NULL, ", "); + char* p3 = ::strtok(NULL, ", "); + char* p4 = ::strtok(NULL, ", "); + char* p5 = ::strtok(NULL, ", "); + char* p6 = ::strtok(NULL, ", \r\n"); + char* p; + while ((p = ::strtok(NULL, ", \r\n")) != NULL) + p7.push_back(p); + if (p1 != NULL && p2 != NULL && p3 != NULL && p4 != NULL && p5 != NULL && p6 != NULL) { + CTGDynRewriteStruct rewrite; + rewrite.m_slot = ::atoi(p1); + rewrite.m_fromTG = ::atoi(p2); + rewrite.m_discPC = ::atoi(p3); + rewrite.m_statusPC = ::atoi(p4); + rewrite.m_toTG = ::atoi(p5); + rewrite.m_range = ::atoi(p6); + for (std::vector::const_iterator it = p7.cbegin(); it != p7.cend(); ++it) { + unsigned int tg = ::atoi(*it); + rewrite.m_exclTGs.push_back(tg); + } + m_dmrNetwork1TGDynRewrites.push_back(rewrite); + } } else if (::strncmp(key, "IdRewrite", 9U) == 0) { char* rfId = ::strtok(value, ", "); char* netId = ::strtok(NULL, " \r\n"); @@ -471,13 +508,15 @@ bool CConf::read() char* p1 = ::strtok(value, ", "); char* p2 = ::strtok(NULL, ", "); char* p3 = ::strtok(NULL, ", "); - char* p4 = ::strtok(NULL, " \r\n"); + char* p4 = ::strtok(NULL, ", \r\n"); if (p1 != NULL && p2 != NULL && p3 != NULL && p4 != NULL) { CTypeRewriteStruct rewrite; rewrite.m_fromSlot = ::atoi(p1); rewrite.m_fromTG = ::atoi(p2); rewrite.m_toSlot = ::atoi(p3); rewrite.m_toId = ::atoi(p4); + char* p5 = ::strtok(NULL, " \r\n"); + rewrite.m_range = p5 != NULL ? ::atoi(p5) : 1; m_dmrNetwork2TypeRewrites.push_back(rewrite); } } else if (::strncmp(key, "SrcRewrite", 10U) == 0) { @@ -495,6 +534,31 @@ bool CConf::read() rewrite.m_range = ::atoi(p5); m_dmrNetwork2SrcRewrites.push_back(rewrite); } + } else if (::strncmp(key, "TGDynRewrite", 12U) == 0) { + std::vector p7; + char* p1 = ::strtok(value, ", "); + char* p2 = ::strtok(NULL, ", "); + char* p3 = ::strtok(NULL, ", "); + char* p4 = ::strtok(NULL, ", "); + char* p5 = ::strtok(NULL, ", "); + char* p6 = ::strtok(NULL, ", \r\n"); + char* p; + while ((p = ::strtok(NULL, ", \r\n")) != NULL) + p7.push_back(p); + if (p1 != NULL && p2 != NULL && p3 != NULL && p4 != NULL && p5 != NULL && p6 != NULL) { + CTGDynRewriteStruct rewrite; + rewrite.m_slot = ::atoi(p1); + rewrite.m_fromTG = ::atoi(p2); + rewrite.m_discPC = ::atoi(p3); + rewrite.m_statusPC = ::atoi(p4); + rewrite.m_toTG = ::atoi(p5); + rewrite.m_range = ::atoi(p6); + for (std::vector::const_iterator it = p7.cbegin(); it != p7.cend(); ++it) { + unsigned int tg = ::atoi(*it); + rewrite.m_exclTGs.push_back(tg); + } + m_dmrNetwork2TGDynRewrites.push_back(rewrite); + } } else if (::strncmp(key, "IdRewrite", 9U) == 0) { char* rfId = ::strtok(value, ", "); char* netId = ::strtok(NULL, " \r\n"); @@ -566,13 +630,15 @@ bool CConf::read() char* p1 = ::strtok(value, ", "); char* p2 = ::strtok(NULL, ", "); char* p3 = ::strtok(NULL, ", "); - char* p4 = ::strtok(NULL, " \r\n"); + char* p4 = ::strtok(NULL, ", \r\n"); if (p1 != NULL && p2 != NULL && p3 != NULL && p4 != NULL) { CTypeRewriteStruct rewrite; rewrite.m_fromSlot = ::atoi(p1); rewrite.m_fromTG = ::atoi(p2); rewrite.m_toSlot = ::atoi(p3); rewrite.m_toId = ::atoi(p4); + char* p5 = ::strtok(NULL, " \r\n"); + rewrite.m_range = p5 != NULL ? ::atoi(p5) : 1; m_dmrNetwork3TypeRewrites.push_back(rewrite); } } else if (::strncmp(key, "SrcRewrite", 10U) == 0) { @@ -590,6 +656,31 @@ bool CConf::read() rewrite.m_range = ::atoi(p5); m_dmrNetwork3SrcRewrites.push_back(rewrite); } + } else if (::strncmp(key, "TGDynRewrite", 12U) == 0) { + std::vector p7; + char* p1 = ::strtok(value, ", "); + char* p2 = ::strtok(NULL, ", "); + char* p3 = ::strtok(NULL, ", "); + char* p4 = ::strtok(NULL, ", "); + char* p5 = ::strtok(NULL, ", "); + char* p6 = ::strtok(NULL, ", \r\n"); + char* p; + while ((p = ::strtok(NULL, ", \r\n")) != NULL) + p7.push_back(p); + if (p1 != NULL && p2 != NULL && p3 != NULL && p4 != NULL && p5 != NULL && p6 != NULL) { + CTGDynRewriteStruct rewrite; + rewrite.m_slot = ::atoi(p1); + rewrite.m_fromTG = ::atoi(p2); + rewrite.m_discPC = ::atoi(p3); + rewrite.m_statusPC = ::atoi(p4); + rewrite.m_toTG = ::atoi(p5); + rewrite.m_range = ::atoi(p6); + for (std::vector::const_iterator it = p7.cbegin(); it != p7.cend(); ++it) { + unsigned int tg = ::atoi(*it); + rewrite.m_exclTGs.push_back(tg); + } + m_dmrNetwork3TGDynRewrites.push_back(rewrite); + } } else if (::strncmp(key, "IdRewrite", 9U) == 0) { char* rfId = ::strtok(value, ", "); char* netId = ::strtok(NULL, " \r\n"); @@ -661,13 +752,15 @@ bool CConf::read() char* p1 = ::strtok(value, ", "); char* p2 = ::strtok(NULL, ", "); char* p3 = ::strtok(NULL, ", "); - char* p4 = ::strtok(NULL, " \r\n"); + char* p4 = ::strtok(NULL, ", \r\n"); if (p1 != NULL && p2 != NULL && p3 != NULL && p4 != NULL) { CTypeRewriteStruct rewrite; rewrite.m_fromSlot = ::atoi(p1); rewrite.m_fromTG = ::atoi(p2); rewrite.m_toSlot = ::atoi(p3); rewrite.m_toId = ::atoi(p4); + char* p5 = ::strtok(NULL, " \r\n"); + rewrite.m_range = p5 != NULL ? ::atoi(p5) : 1; m_dmrNetwork4TypeRewrites.push_back(rewrite); } } else if (::strncmp(key, "SrcRewrite", 10U) == 0) { @@ -685,6 +778,31 @@ bool CConf::read() rewrite.m_range = ::atoi(p5); m_dmrNetwork4SrcRewrites.push_back(rewrite); } + } else if (::strncmp(key, "TGDynRewrite", 12U) == 0) { + std::vector p7; + char* p1 = ::strtok(value, ", "); + char* p2 = ::strtok(NULL, ", "); + char* p3 = ::strtok(NULL, ", "); + char* p4 = ::strtok(NULL, ", "); + char* p5 = ::strtok(NULL, ", "); + char* p6 = ::strtok(NULL, ", \r\n"); + char* p; + while ((p = ::strtok(NULL, ", \r\n")) != NULL) + p7.push_back(p); + if (p1 != NULL && p2 != NULL && p3 != NULL && p4 != NULL && p5 != NULL && p6 != NULL) { + CTGDynRewriteStruct rewrite; + rewrite.m_slot = ::atoi(p1); + rewrite.m_fromTG = ::atoi(p2); + rewrite.m_discPC = ::atoi(p3); + rewrite.m_statusPC = ::atoi(p4); + rewrite.m_toTG = ::atoi(p5); + rewrite.m_range = ::atoi(p6); + for (std::vector::const_iterator it = p7.cbegin(); it != p7.cend(); ++it) { + unsigned int tg = ::atoi(*it); + rewrite.m_exclTGs.push_back(tg); + } + m_dmrNetwork4TGDynRewrites.push_back(rewrite); + } } else if (::strncmp(key, "IdRewrite", 9U) == 0) { char* rfId = ::strtok(value, ", "); char* netId = ::strtok(NULL, " \r\n"); @@ -756,13 +874,15 @@ bool CConf::read() char* p1 = ::strtok(value, ", "); char* p2 = ::strtok(NULL, ", "); char* p3 = ::strtok(NULL, ", "); - char* p4 = ::strtok(NULL, " \r\n"); + char* p4 = ::strtok(NULL, ", \r\n"); if (p1 != NULL && p2 != NULL && p3 != NULL && p4 != NULL) { CTypeRewriteStruct rewrite; rewrite.m_fromSlot = ::atoi(p1); rewrite.m_fromTG = ::atoi(p2); rewrite.m_toSlot = ::atoi(p3); rewrite.m_toId = ::atoi(p4); + char* p5 = ::strtok(NULL, " \r\n"); + rewrite.m_range = p5 != NULL ? ::atoi(p5) : 1; m_dmrNetwork5TypeRewrites.push_back(rewrite); } } else if (::strncmp(key, "SrcRewrite", 10U) == 0) { @@ -780,6 +900,31 @@ bool CConf::read() rewrite.m_range = ::atoi(p5); m_dmrNetwork5SrcRewrites.push_back(rewrite); } + } else if (::strncmp(key, "TGDynRewrite", 12U) == 0) { + std::vector p7; + char* p1 = ::strtok(value, ", "); + char* p2 = ::strtok(NULL, ", "); + char* p3 = ::strtok(NULL, ", "); + char* p4 = ::strtok(NULL, ", "); + char* p5 = ::strtok(NULL, ", "); + char* p6 = ::strtok(NULL, ", \r\n"); + char* p; + while ((p = ::strtok(NULL, ", \r\n")) != NULL) + p7.push_back(p); + if (p1 != NULL && p2 != NULL && p3 != NULL && p4 != NULL && p5 != NULL && p6 != NULL) { + CTGDynRewriteStruct rewrite; + rewrite.m_slot = ::atoi(p1); + rewrite.m_fromTG = ::atoi(p2); + rewrite.m_discPC = ::atoi(p3); + rewrite.m_statusPC = ::atoi(p4); + rewrite.m_toTG = ::atoi(p5); + rewrite.m_range = ::atoi(p6); + for (std::vector::const_iterator it = p7.cbegin(); it != p7.cend(); ++it) { + unsigned int tg = ::atoi(*it); + rewrite.m_exclTGs.push_back(tg); + } + m_dmrNetwork5TGDynRewrites.push_back(rewrite); + } } else if (::strncmp(key, "IdRewrite", 9U) == 0) { char* rfId = ::strtok(value, ", "); char* netId = ::strtok(NULL, " \r\n"); @@ -796,6 +941,11 @@ bool CConf::read() unsigned int slotNo = (unsigned int)::atoi(value); m_dmrNetwork5PassAllTG.push_back(slotNo); } + } else if (section == SECTION_DYNAMIC_TG_CONTROL) { + if (::strcmp(key, "Enabled") == 0) + m_dynamicTGControlEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "Port") == 0) + m_dynamicTGControlPort = (unsigned int)::atoi(value); } } @@ -1080,6 +1230,11 @@ std::vector CConf::getDMRNetwork1SrcRewrites() const return m_dmrNetwork1SrcRewrites; } +std::vector CConf::getDMRNetwork1TGDynRewrites() const +{ + return m_dmrNetwork1TGDynRewrites; +} + std::vector CConf::getDMRNetwork1IdRewrites() const { return m_dmrNetwork1IdRewrites; @@ -1168,6 +1323,11 @@ std::vector CConf::getDMRNetwork2SrcRewrites() const return m_dmrNetwork2SrcRewrites; } +std::vector CConf::getDMRNetwork2TGDynRewrites() const +{ + return m_dmrNetwork2TGDynRewrites; +} + std::vector CConf::getDMRNetwork2IdRewrites() const { return m_dmrNetwork2IdRewrites; @@ -1256,6 +1416,11 @@ std::vector CConf::getDMRNetwork3SrcRewrites() const return m_dmrNetwork3SrcRewrites; } +std::vector CConf::getDMRNetwork3TGDynRewrites() const +{ + return m_dmrNetwork3TGDynRewrites; +} + std::vector CConf::getDMRNetwork3IdRewrites() const { return m_dmrNetwork3IdRewrites; @@ -1344,6 +1509,11 @@ std::vector CConf::getDMRNetwork4SrcRewrites() const return m_dmrNetwork4SrcRewrites; } +std::vector CConf::getDMRNetwork4TGDynRewrites() const +{ + return m_dmrNetwork4TGDynRewrites; +} + std::vector CConf::getDMRNetwork4IdRewrites() const { return m_dmrNetwork4IdRewrites; @@ -1432,6 +1602,11 @@ std::vector CConf::getDMRNetwork5SrcRewrites() const return m_dmrNetwork5SrcRewrites; } +std::vector CConf::getDMRNetwork5TGDynRewrites() const +{ + return m_dmrNetwork5TGDynRewrites; +} + std::vector CConf::getDMRNetwork5IdRewrites() const { return m_dmrNetwork5IdRewrites; @@ -1446,3 +1621,13 @@ std::vector CConf::getDMRNetwork5PassAllTG() const { return m_dmrNetwork5PassAllTG; } + +bool CConf::getDynamicTGControlEnabled() const +{ + return m_dynamicTGControlEnabled; +} + +unsigned int CConf::getDynamicTGControlPort() const +{ + return m_dynamicTGControlPort; +} diff --git a/Conf.h b/Conf.h index cc0f617..b038329 100644 --- a/Conf.h +++ b/Conf.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2017,2019 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,2019,2020 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -43,6 +43,7 @@ struct CTypeRewriteStruct { unsigned int m_fromTG; unsigned int m_toSlot; unsigned int m_toId; + unsigned int m_range; }; struct CSrcRewriteStruct { @@ -53,6 +54,16 @@ struct CSrcRewriteStruct { unsigned int m_range; }; +struct CTGDynRewriteStruct { + unsigned int m_slot; + unsigned int m_fromTG; + unsigned int m_discPC; + unsigned int m_statusPC; + unsigned int m_toTG; + unsigned int m_range; + std::vector m_exclTGs; +}; + struct CIdRewriteStruct { unsigned int m_rfId; unsigned int m_netId; @@ -111,13 +122,14 @@ public: std::string getDMRNetwork1Options() const; bool getDMRNetwork1Location() const; bool getDMRNetwork1Debug() const; - std::vector getDMRNetwork1TGRewrites() const; - std::vector getDMRNetwork1PCRewrites() const; - std::vector getDMRNetwork1TypeRewrites() const; - std::vector getDMRNetwork1SrcRewrites() const; - std::vector getDMRNetwork1IdRewrites() const; - std::vector getDMRNetwork1PassAllPC() const; - std::vector getDMRNetwork1PassAllTG() const; + std::vector getDMRNetwork1TGRewrites() const; + std::vector getDMRNetwork1PCRewrites() const; + std::vector getDMRNetwork1TypeRewrites() const; + std::vector getDMRNetwork1SrcRewrites() const; + std::vector getDMRNetwork1TGDynRewrites() const; + std::vector getDMRNetwork1IdRewrites() const; + std::vector getDMRNetwork1PassAllPC() const; + std::vector getDMRNetwork1PassAllTG() const; // The DMR Network 2 section bool getDMRNetwork2Enabled() const; @@ -130,13 +142,14 @@ public: std::string getDMRNetwork2Options() const; bool getDMRNetwork2Location() const; bool getDMRNetwork2Debug() const; - std::vector getDMRNetwork2TGRewrites() const; - std::vector getDMRNetwork2PCRewrites() const; - std::vector getDMRNetwork2TypeRewrites() const; - std::vector getDMRNetwork2SrcRewrites() const; - std::vector getDMRNetwork2IdRewrites() const; - std::vector getDMRNetwork2PassAllPC() const; - std::vector getDMRNetwork2PassAllTG() const; + std::vector getDMRNetwork2TGRewrites() const; + std::vector getDMRNetwork2PCRewrites() const; + std::vector getDMRNetwork2TypeRewrites() const; + std::vector getDMRNetwork2SrcRewrites() const; + std::vector getDMRNetwork2TGDynRewrites() const; + std::vector getDMRNetwork2IdRewrites() const; + std::vector getDMRNetwork2PassAllPC() const; + std::vector getDMRNetwork2PassAllTG() const; // The DMR Network 3 section bool getDMRNetwork3Enabled() const; @@ -149,13 +162,14 @@ public: std::string getDMRNetwork3Options() const; bool getDMRNetwork3Location() const; bool getDMRNetwork3Debug() const; - std::vector getDMRNetwork3TGRewrites() const; - std::vector getDMRNetwork3PCRewrites() const; - std::vector getDMRNetwork3TypeRewrites() const; - std::vector getDMRNetwork3SrcRewrites() const; - std::vector getDMRNetwork3IdRewrites() const; - std::vector getDMRNetwork3PassAllPC() const; - std::vector getDMRNetwork3PassAllTG() const; + std::vector getDMRNetwork3TGRewrites() const; + std::vector getDMRNetwork3PCRewrites() const; + std::vector getDMRNetwork3TypeRewrites() const; + std::vector getDMRNetwork3SrcRewrites() const; + std::vector getDMRNetwork3TGDynRewrites() const; + std::vector getDMRNetwork3IdRewrites() const; + std::vector getDMRNetwork3PassAllPC() const; + std::vector getDMRNetwork3PassAllTG() const; // The DMR Network 4 section bool getDMRNetwork4Enabled() const; @@ -168,13 +182,14 @@ public: std::string getDMRNetwork4Options() const; bool getDMRNetwork4Location() const; bool getDMRNetwork4Debug() const; - std::vector getDMRNetwork4TGRewrites() const; - std::vector getDMRNetwork4PCRewrites() const; - std::vector getDMRNetwork4TypeRewrites() const; - std::vector getDMRNetwork4SrcRewrites() const; - std::vector getDMRNetwork4IdRewrites() const; - std::vector getDMRNetwork4PassAllPC() const; - std::vector getDMRNetwork4PassAllTG() const; + std::vector getDMRNetwork4TGRewrites() const; + std::vector getDMRNetwork4PCRewrites() const; + std::vector getDMRNetwork4TypeRewrites() const; + std::vector getDMRNetwork4SrcRewrites() const; + std::vector getDMRNetwork4TGDynRewrites() const; + std::vector getDMRNetwork4IdRewrites() const; + std::vector getDMRNetwork4PassAllPC() const; + std::vector getDMRNetwork4PassAllTG() const; // The DMR Network 5 section bool getDMRNetwork5Enabled() const; @@ -187,13 +202,14 @@ public: std::string getDMRNetwork5Options() const; bool getDMRNetwork5Location() const; bool getDMRNetwork5Debug() const; - std::vector getDMRNetwork5TGRewrites() const; - std::vector getDMRNetwork5PCRewrites() const; - std::vector getDMRNetwork5TypeRewrites() const; - std::vector getDMRNetwork5SrcRewrites() const; - std::vector getDMRNetwork5IdRewrites() const; - std::vector getDMRNetwork5PassAllPC() const; - std::vector getDMRNetwork5PassAllTG() const; + std::vector getDMRNetwork5TGRewrites() const; + std::vector getDMRNetwork5PCRewrites() const; + std::vector getDMRNetwork5TypeRewrites() const; + std::vector getDMRNetwork5SrcRewrites() const; + std::vector getDMRNetwork5TGDynRewrites() const; + std::vector getDMRNetwork5IdRewrites() const; + std::vector getDMRNetwork5PassAllPC() const; + std::vector getDMRNetwork5PassAllTG() const; // The XLX Network section bool getXLXNetworkEnabled() const; @@ -212,6 +228,10 @@ public: bool getXLXNetworkUserControl() const; char getXLXNetworkModule() const; + // The Dynamic TG Control section + bool getDynamicTGControlEnabled() const; + unsigned int getDynamicTGControlPort() const; + private: std::string m_file; bool m_daemon; @@ -254,13 +274,14 @@ private: std::string m_dmrNetwork1Options; bool m_dmrNetwork1Location; bool m_dmrNetwork1Debug; - std::vector m_dmrNetwork1TGRewrites; - std::vector m_dmrNetwork1PCRewrites; - std::vector m_dmrNetwork1TypeRewrites; - std::vector m_dmrNetwork1SrcRewrites; - std::vector m_dmrNetwork1IdRewrites; - std::vector m_dmrNetwork1PassAllPC; - std::vector m_dmrNetwork1PassAllTG; + std::vector m_dmrNetwork1TGRewrites; + std::vector m_dmrNetwork1PCRewrites; + std::vector m_dmrNetwork1TypeRewrites; + std::vector m_dmrNetwork1SrcRewrites; + std::vector m_dmrNetwork1TGDynRewrites; + std::vector m_dmrNetwork1IdRewrites; + std::vector m_dmrNetwork1PassAllPC; + std::vector m_dmrNetwork1PassAllTG; bool m_dmrNetwork2Enabled; std::string m_dmrNetwork2Name; @@ -272,13 +293,14 @@ private: std::string m_dmrNetwork2Options; bool m_dmrNetwork2Location; bool m_dmrNetwork2Debug; - std::vector m_dmrNetwork2TGRewrites; - std::vector m_dmrNetwork2PCRewrites; - std::vector m_dmrNetwork2TypeRewrites; - std::vector m_dmrNetwork2SrcRewrites; - std::vector m_dmrNetwork2IdRewrites; - std::vector m_dmrNetwork2PassAllPC; - std::vector m_dmrNetwork2PassAllTG; + std::vector m_dmrNetwork2TGRewrites; + std::vector m_dmrNetwork2PCRewrites; + std::vector m_dmrNetwork2TypeRewrites; + std::vector m_dmrNetwork2SrcRewrites; + std::vector m_dmrNetwork2TGDynRewrites; + std::vector m_dmrNetwork2IdRewrites; + std::vector m_dmrNetwork2PassAllPC; + std::vector m_dmrNetwork2PassAllTG; bool m_dmrNetwork3Enabled; std::string m_dmrNetwork3Name; @@ -290,13 +312,14 @@ private: std::string m_dmrNetwork3Options; bool m_dmrNetwork3Location; bool m_dmrNetwork3Debug; - std::vector m_dmrNetwork3TGRewrites; - std::vector m_dmrNetwork3PCRewrites; - std::vector m_dmrNetwork3TypeRewrites; - std::vector m_dmrNetwork3SrcRewrites; - std::vector m_dmrNetwork3IdRewrites; - std::vector m_dmrNetwork3PassAllPC; - std::vector m_dmrNetwork3PassAllTG; + std::vector m_dmrNetwork3TGRewrites; + std::vector m_dmrNetwork3PCRewrites; + std::vector m_dmrNetwork3TypeRewrites; + std::vector m_dmrNetwork3SrcRewrites; + std::vector m_dmrNetwork3TGDynRewrites; + std::vector m_dmrNetwork3IdRewrites; + std::vector m_dmrNetwork3PassAllPC; + std::vector m_dmrNetwork3PassAllTG; bool m_dmrNetwork4Enabled; std::string m_dmrNetwork4Name; @@ -308,13 +331,14 @@ private: std::string m_dmrNetwork4Options; bool m_dmrNetwork4Location; bool m_dmrNetwork4Debug; - std::vector m_dmrNetwork4TGRewrites; - std::vector m_dmrNetwork4PCRewrites; - std::vector m_dmrNetwork4TypeRewrites; - std::vector m_dmrNetwork4SrcRewrites; - std::vector m_dmrNetwork4IdRewrites; - std::vector m_dmrNetwork4PassAllPC; - std::vector m_dmrNetwork4PassAllTG; + std::vector m_dmrNetwork4TGRewrites; + std::vector m_dmrNetwork4PCRewrites; + std::vector m_dmrNetwork4TypeRewrites; + std::vector m_dmrNetwork4SrcRewrites; + std::vector m_dmrNetwork4TGDynRewrites; + std::vector m_dmrNetwork4IdRewrites; + std::vector m_dmrNetwork4PassAllPC; + std::vector m_dmrNetwork4PassAllTG; bool m_dmrNetwork5Enabled; std::string m_dmrNetwork5Name; @@ -326,13 +350,14 @@ private: std::string m_dmrNetwork5Options; bool m_dmrNetwork5Location; bool m_dmrNetwork5Debug; - std::vector m_dmrNetwork5TGRewrites; - std::vector m_dmrNetwork5PCRewrites; - std::vector m_dmrNetwork5TypeRewrites; - std::vector m_dmrNetwork5SrcRewrites; - std::vector m_dmrNetwork5IdRewrites; - std::vector m_dmrNetwork5PassAllPC; - std::vector m_dmrNetwork5PassAllTG; + std::vector m_dmrNetwork5TGRewrites; + std::vector m_dmrNetwork5PCRewrites; + std::vector m_dmrNetwork5TypeRewrites; + std::vector m_dmrNetwork5SrcRewrites; + std::vector m_dmrNetwork5TGDynRewrites; + std::vector m_dmrNetwork5IdRewrites; + std::vector m_dmrNetwork5PassAllPC; + std::vector m_dmrNetwork5PassAllTG; bool m_xlxNetworkEnabled; unsigned int m_xlxNetworkId; @@ -349,6 +374,9 @@ private: bool m_xlxNetworkDebug; bool m_xlxNetworkUserControl; char m_xlxNetworkModule; + + bool m_dynamicTGControlEnabled; + unsigned int m_dynamicTGControlPort; }; #endif diff --git a/DMRGateway.cpp b/DMRGateway.cpp index dce4d57..4f961d6 100644 --- a/DMRGateway.cpp +++ b/DMRGateway.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2019 by Jonathan Naylor G4KLX + * Copyright (C) 2015-2020 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,10 +27,10 @@ #include "PassAllPC.h" #include "PassAllTG.h" #include "DMRFullLC.h" +#include "XLXVoice.h" #include "Version.h" #include "Thread.h" #include "DMRLC.h" -#include "Voice.h" #include "Sync.h" #include "Log.h" #include "GitVersion.h" @@ -71,7 +71,7 @@ static void sigHandler(int signum) const char* HEADER1 = "This software is for use on amateur radio networks only,"; const char* HEADER2 = "it is to be used for educational purposes only. Its use on"; const char* HEADER3 = "commercial networks is strictly prohibited."; -const char* HEADER4 = "Copyright(C) 2017 by Jonathan Naylor, G4KLX and others"; +const char* HEADER4 = "Copyright(C) 2017-2020 by Jonathan Naylor, G4KLX and others"; int main(int argc, char** argv) { @@ -178,7 +178,10 @@ m_dmr1Passalls(), m_dmr2Passalls(), m_dmr3Passalls(), m_dmr4Passalls(), -m_dmr5Passalls() +m_dmr5Passalls(), +m_dynVoices(), +m_dynRF(), +m_socket(NULL) { m_status = new DMRGW_STATUS[3U]; m_status[1U] = DMRGWS_NONE; @@ -199,10 +202,10 @@ CDMRGateway::~CDMRGateway() delete *it; for (std::vector::iterator it = m_dmr2NetRewrites.begin(); it != m_dmr2NetRewrites.end(); ++it) - delete *it; + delete *it; for (std::vector::iterator it = m_dmr2RFRewrites.begin(); it != m_dmr2RFRewrites.end(); ++it) - delete *it; + delete *it; for (std::vector::iterator it = m_dmr2SrcRewrites.begin(); it != m_dmr2SrcRewrites.end(); ++it) delete *it; @@ -249,6 +252,9 @@ CDMRGateway::~CDMRGateway() for (std::vector::iterator it = m_dmr5Passalls.begin(); it != m_dmr5Passalls.end(); ++it) delete *it; + for (std::vector::iterator it = m_dynVoices.begin(); it != m_dynVoices.end(); ++it) + delete* it; + delete m_rptRewrite; delete m_xlxRewrite; @@ -368,6 +374,32 @@ int CDMRGateway::run() bool ruleTrace = m_conf.getRuleTrace(); LogInfo("Rule trace: %s", ruleTrace ? "yes" : "no"); + if (m_conf.getXLXNetworkEnabled()) { + ret = createXLXNetwork(); + if (!ret) + return 1; + } + + CXLXVoice* xlxVoice = NULL; + if (m_conf.getVoiceEnabled()) { + std::string language = m_conf.getVoiceLanguage(); + std::string directory = m_conf.getVoiceDirectory(); + + LogInfo("Voice Parameters"); + LogInfo(" Enabled: yes"); + LogInfo(" Language: %s", language.c_str()); + LogInfo(" Directory: %s", directory.c_str()); + + if (m_xlxNetwork != NULL) { + xlxVoice = new CXLXVoice(directory, language, m_repeater->getId(), m_xlxSlot, m_xlxTG); + bool ret = xlxVoice->open(); + if (!ret) { + delete xlxVoice; + xlxVoice = NULL; + } + } + } + if (m_conf.getDMRNetwork1Enabled()) { ret = createDMRNetwork1(); if (!ret) @@ -398,8 +430,8 @@ int CDMRGateway::run() return 1; } - if (m_conf.getXLXNetworkEnabled()) { - ret = createXLXNetwork(); + if (m_conf.getDynamicTGControlEnabled()) { + bool ret = createDynamicTGControl(); if (!ret) return 1; } @@ -407,24 +439,6 @@ int CDMRGateway::run() unsigned int rfTimeout = m_conf.getRFTimeout(); unsigned int netTimeout = m_conf.getNetTimeout(); - CVoice* voice = NULL; - if (m_conf.getVoiceEnabled() && m_xlxNetwork != NULL) { - std::string language = m_conf.getVoiceLanguage(); - std::string directory = m_conf.getVoiceDirectory(); - - LogInfo("Voice Parameters"); - LogInfo(" Enabled: yes"); - LogInfo(" Language: %s", language.c_str()); - LogInfo(" Directory: %s", directory.c_str()); - - voice = new CVoice(directory, language, m_repeater->getId(), m_xlxSlot, m_xlxTG); - bool ret = voice->open(); - if (!ret) { - delete voice; - voice = NULL; - } - } - CTimer* timer[3U]; timer[1U] = new CTimer(1000U); timer[2U] = new CTimer(1000U); @@ -466,18 +480,18 @@ int CDMRGateway::run() writeXLXLink(m_xlxId, m_xlxReflector, m_xlxNetwork); char c = ('A' + (m_xlxReflector % 100U)) - 1U; LogMessage("XLX, Linking to reflector XLX%03u %c", m_xlxNumber, c); - if (voice != NULL) - voice->linkedTo(m_xlxNumber, m_xlxReflector); + if (xlxVoice != NULL) + xlxVoice->linkedTo(m_xlxNumber, m_xlxReflector); } else if (m_xlxRoom >= 4001U && m_xlxRoom <= 4026U) { writeXLXLink(m_xlxId, m_xlxRoom, m_xlxNetwork); char c = ('A' + (m_xlxRoom % 100U)) - 1U; LogMessage("XLX, Linking to reflector XLX%03u %c", m_xlxNumber, c); - if (voice != NULL) - voice->linkedTo(m_xlxNumber, m_xlxRoom); + if (xlxVoice != NULL) + xlxVoice->linkedTo(m_xlxNumber, m_xlxRoom); m_xlxReflector = m_xlxRoom; } else { - if (voice != NULL) - voice->linkedTo(m_xlxNumber, 0U); + if (xlxVoice != NULL) + xlxVoice->linkedTo(m_xlxNumber, 0U); } m_xlxConnected = true; @@ -489,8 +503,8 @@ int CDMRGateway::run() } else if (!connected && m_xlxConnected) { LogMessage("XLX, Unlinking from XLX%03u due to loss of connection", m_xlxNumber); - if (voice != NULL) - voice->unlinked(); + if (xlxVoice != NULL) + xlxVoice->unlinked(); m_xlxConnected = false; m_xlxRelink.stop(); @@ -521,11 +535,11 @@ int CDMRGateway::run() } m_xlxReflector = m_xlxRoom; - if (voice != NULL) { + if (xlxVoice != NULL) { if (m_xlxReflector < 4001U || m_xlxReflector > 4026U) - voice->linkedTo(m_xlxNumber, 0U); + xlxVoice->linkedTo(m_xlxNumber, 0U); else - voice->linkedTo(m_xlxNumber, m_xlxReflector); + xlxVoice->linkedTo(m_xlxNumber, m_xlxReflector); } } } @@ -578,16 +592,16 @@ int CDMRGateway::run() timer[slotNo]->setTimeout(rfTimeout); timer[slotNo]->start(); - if (voice != NULL) { + if (xlxVoice != NULL) { unsigned char type = data.getDataType(); if (type == DT_TERMINATOR_WITH_LC) { if (m_xlxConnected) { if (m_xlxReflector != 4000U) - voice->linkedTo(m_xlxNumber, m_xlxReflector); + xlxVoice->linkedTo(m_xlxNumber, m_xlxReflector); else - voice->linkedTo(m_xlxNumber, 0U); + xlxVoice->linkedTo(m_xlxNumber, 0U); } else { - voice->unlinked(); + xlxVoice->unlinked(); } } } @@ -613,19 +627,19 @@ int CDMRGateway::run() if (trace) LogDebug("Rule Trace, RF transmission: Slot=%u Src=%u Dst=%s%u", slotNo, srcId, flco == FLCO_GROUP ? "TG" : "", dstId); - bool rewritten = false; + PROCESS_RESULT result = RESULT_UNMATCHED; if (m_dmrNetwork1 != NULL) { // Rewrite the slot and/or TG or neither for (std::vector::iterator it = m_dmr1RFRewrites.begin(); it != m_dmr1RFRewrites.end(); ++it) { - bool ret = (*it)->process(data, trace); - if (ret) { - rewritten = true; + PROCESS_RESULT res = (*it)->process(data, trace); + if (res != RESULT_UNMATCHED) { + result = res; break; } } - if (rewritten) { + if (result == RESULT_MATCHED) { if (m_status[slotNo] == DMRGWS_NONE || m_status[slotNo] == DMRGWS_DMRNETWORK1) { rewrite(m_dmr1SrcRewrites, data, trace); m_dmrNetwork1->write(data); @@ -636,18 +650,18 @@ int CDMRGateway::run() } } - if (!rewritten) { + if (result == RESULT_UNMATCHED) { if (m_dmrNetwork2 != NULL) { // Rewrite the slot and/or TG or neither for (std::vector::iterator it = m_dmr2RFRewrites.begin(); it != m_dmr2RFRewrites.end(); ++it) { - bool ret = (*it)->process(data, trace); - if (ret) { - rewritten = true; + PROCESS_RESULT res = (*it)->process(data, trace); + if (res != RESULT_UNMATCHED) { + result = res; break; } } - if (rewritten) { + if (result == RESULT_MATCHED) { if (m_status[slotNo] == DMRGWS_NONE || m_status[slotNo] == DMRGWS_DMRNETWORK2) { rewrite(m_dmr2SrcRewrites, data, trace); m_dmrNetwork2->write(data); @@ -659,18 +673,18 @@ int CDMRGateway::run() } } - if (!rewritten) { + if (result == RESULT_UNMATCHED) { if (m_dmrNetwork3 != NULL) { // Rewrite the slot and/or TG or neither for (std::vector::iterator it = m_dmr3RFRewrites.begin(); it != m_dmr3RFRewrites.end(); ++it) { - bool ret = (*it)->process(data, trace); - if (ret) { - rewritten = true; + PROCESS_RESULT res = (*it)->process(data, trace); + if (res != RESULT_UNMATCHED) { + result = res; break; } } - if (rewritten) { + if (result == RESULT_MATCHED) { if (m_status[slotNo] == DMRGWS_NONE || m_status[slotNo] == DMRGWS_DMRNETWORK3) { rewrite(m_dmr3SrcRewrites, data, trace); m_dmrNetwork3->write(data); @@ -682,18 +696,18 @@ int CDMRGateway::run() } } - if (!rewritten) { + if (result == RESULT_UNMATCHED) { if (m_dmrNetwork4 != NULL) { // Rewrite the slot and/or TG or neither for (std::vector::iterator it = m_dmr4RFRewrites.begin(); it != m_dmr4RFRewrites.end(); ++it) { - bool ret = (*it)->process(data, trace); - if (ret) { - rewritten = true; + PROCESS_RESULT res = (*it)->process(data, trace); + if (res != RESULT_UNMATCHED) { + result = res; break; } } - if (rewritten) { + if (result == RESULT_MATCHED) { if (m_status[slotNo] == DMRGWS_NONE || m_status[slotNo] == DMRGWS_DMRNETWORK4) { rewrite(m_dmr4SrcRewrites, data, trace); m_dmrNetwork4->write(data); @@ -705,18 +719,18 @@ int CDMRGateway::run() } } - if (!rewritten) { + if (result == RESULT_UNMATCHED) { if (m_dmrNetwork5 != NULL) { // Rewrite the slot and/or TG or neither for (std::vector::iterator it = m_dmr5RFRewrites.begin(); it != m_dmr5RFRewrites.end(); ++it) { - bool ret = (*it)->process(data, trace); - if (ret) { - rewritten = true; + PROCESS_RESULT res = (*it)->process(data, trace); + if (res != RESULT_UNMATCHED) { + result = res; break; } } - if (rewritten) { + if (result == RESULT_MATCHED) { if (m_status[slotNo] == DMRGWS_NONE || m_status[slotNo] == DMRGWS_DMRNETWORK5) { rewrite(m_dmr5SrcRewrites, data, trace); m_dmrNetwork5->write(data); @@ -728,17 +742,17 @@ int CDMRGateway::run() } } - if (!rewritten) { + if (result == RESULT_UNMATCHED) { if (m_dmrNetwork1 != NULL) { for (std::vector::iterator it = m_dmr1Passalls.begin(); it != m_dmr1Passalls.end(); ++it) { - bool ret = (*it)->process(data, trace); - if (ret) { - rewritten = true; + PROCESS_RESULT res = (*it)->process(data, trace); + if (res != RESULT_UNMATCHED) { + result = res; break; } } - if (rewritten) { + if (result == RESULT_MATCHED) { if (m_status[slotNo] == DMRGWS_NONE || m_status[slotNo] == DMRGWS_DMRNETWORK1) { rewrite(m_dmr1SrcRewrites, data, trace); m_dmrNetwork1->write(data); @@ -750,17 +764,17 @@ int CDMRGateway::run() } } - if (!rewritten) { + if (result == RESULT_UNMATCHED) { if (m_dmrNetwork2 != NULL) { for (std::vector::iterator it = m_dmr2Passalls.begin(); it != m_dmr2Passalls.end(); ++it) { - bool ret = (*it)->process(data, trace); - if (ret) { - rewritten = true; + PROCESS_RESULT res = (*it)->process(data, trace); + if (res != RESULT_UNMATCHED) { + result = res; break; } } - if (rewritten) { + if (result == RESULT_MATCHED) { if (m_status[slotNo] == DMRGWS_NONE || m_status[slotNo] == DMRGWS_DMRNETWORK2) { rewrite(m_dmr2SrcRewrites, data, trace); m_dmrNetwork2->write(data); @@ -772,17 +786,17 @@ int CDMRGateway::run() } } - if (!rewritten) { + if (result == RESULT_UNMATCHED) { if (m_dmrNetwork3 != NULL) { for (std::vector::iterator it = m_dmr3Passalls.begin(); it != m_dmr3Passalls.end(); ++it) { - bool ret = (*it)->process(data, trace); - if (ret) { - rewritten = true; + PROCESS_RESULT res = (*it)->process(data, trace); + if (res != RESULT_UNMATCHED) { + result = res; break; } } - if (rewritten) { + if (result == RESULT_MATCHED) { if (m_status[slotNo] == DMRGWS_NONE || m_status[slotNo] == DMRGWS_DMRNETWORK3) { rewrite(m_dmr3SrcRewrites, data, trace); m_dmrNetwork3->write(data); @@ -794,17 +808,17 @@ int CDMRGateway::run() } } - if (!rewritten) { + if (result == RESULT_UNMATCHED) { if (m_dmrNetwork4 != NULL) { for (std::vector::iterator it = m_dmr4Passalls.begin(); it != m_dmr4Passalls.end(); ++it) { - bool ret = (*it)->process(data, trace); - if (ret) { - rewritten = true; + PROCESS_RESULT res = (*it)->process(data, trace); + if (res != RESULT_UNMATCHED) { + result = res; break; } } - if (rewritten) { + if (result == RESULT_MATCHED) { if (m_status[slotNo] == DMRGWS_NONE || m_status[slotNo] == DMRGWS_DMRNETWORK4) { rewrite(m_dmr4SrcRewrites, data, trace); m_dmrNetwork4->write(data); @@ -816,17 +830,17 @@ int CDMRGateway::run() } } - if (!rewritten) { + if (result == RESULT_UNMATCHED) { if (m_dmrNetwork5 != NULL) { for (std::vector::iterator it = m_dmr5Passalls.begin(); it != m_dmr5Passalls.end(); ++it) { - bool ret = (*it)->process(data, trace); - if (ret) { - rewritten = true; + PROCESS_RESULT res = (*it)->process(data, trace); + if (res != RESULT_UNMATCHED) { + result = res; break; } } - if (rewritten) { + if (result == RESULT_MATCHED) { if (m_status[slotNo] == DMRGWS_NONE || m_status[slotNo] == DMRGWS_DMRNETWORK5) { rewrite(m_dmr5SrcRewrites, data, trace); m_dmrNetwork5->write(data); @@ -838,7 +852,7 @@ int CDMRGateway::run() } } - if (!rewritten && trace) + if (result == RESULT_UNMATCHED && trace) LogDebug("Rule Trace,\tnot matched so rejected"); } } @@ -895,6 +909,8 @@ int CDMRGateway::run() // Check that the rewritten slot is free to use. slotNo = data.getSlotNo(); if (m_status[slotNo] == DMRGWS_NONE || m_status[slotNo] == DMRGWS_DMRNETWORK1) { + for (std::vector::iterator it = m_dynRF.begin(); it != m_dynRF.end(); ++it) + (*it)->stopVoice(slotNo); m_repeater->write(data); m_status[slotNo] = DMRGWS_DMRNETWORK1; timer[slotNo]->setTimeout(netTimeout); @@ -943,6 +959,8 @@ int CDMRGateway::run() // Check that the rewritten slot is free to use. slotNo = data.getSlotNo(); if (m_status[slotNo] == DMRGWS_NONE || m_status[slotNo] == DMRGWS_DMRNETWORK2) { + for (std::vector::iterator it = m_dynRF.begin(); it != m_dynRF.end(); ++it) + (*it)->stopVoice(slotNo); m_repeater->write(data); m_status[slotNo] = DMRGWS_DMRNETWORK2; timer[slotNo]->setTimeout(netTimeout); @@ -991,6 +1009,8 @@ int CDMRGateway::run() // Check that the rewritten slot is free to use. slotNo = data.getSlotNo(); if (m_status[slotNo] == DMRGWS_NONE || m_status[slotNo] == DMRGWS_DMRNETWORK3) { + for (std::vector::iterator it = m_dynRF.begin(); it != m_dynRF.end(); ++it) + (*it)->stopVoice(slotNo); m_repeater->write(data); m_status[slotNo] = DMRGWS_DMRNETWORK3; timer[slotNo]->setTimeout(netTimeout); @@ -1039,6 +1059,8 @@ int CDMRGateway::run() // Check that the rewritten slot is free to use. slotNo = data.getSlotNo(); if (m_status[slotNo] == DMRGWS_NONE || m_status[slotNo] == DMRGWS_DMRNETWORK4) { + for (std::vector::iterator it = m_dynRF.begin(); it != m_dynRF.end(); ++it) + (*it)->stopVoice(slotNo); m_repeater->write(data); m_status[slotNo] = DMRGWS_DMRNETWORK4; timer[slotNo]->setTimeout(netTimeout); @@ -1087,6 +1109,8 @@ int CDMRGateway::run() // Check that the rewritten slot is free to use. slotNo = data.getSlotNo(); if (m_status[slotNo] == DMRGWS_NONE || m_status[slotNo] == DMRGWS_DMRNETWORK5) { + for (std::vector::iterator it = m_dynRF.begin(); it != m_dynRF.end(); ++it) + (*it)->stopVoice(slotNo); m_repeater->write(data); m_status[slotNo] = DMRGWS_DMRNETWORK5; timer[slotNo]->setTimeout(netTimeout); @@ -1109,8 +1133,8 @@ int CDMRGateway::run() processHomePosition(); - if (voice != NULL) { - ret = voice->read(data); + if (xlxVoice != NULL) { + ret = xlxVoice->read(data); if (ret) { m_repeater->write(data); m_status[m_xlxSlot] = DMRGWS_XLXREFLECTOR; @@ -1119,6 +1143,15 @@ int CDMRGateway::run() } } + for (std::vector::iterator it = m_dynVoices.begin(); it != m_dynVoices.end(); ++it) { + ret = (*it)->read(data); + if (ret) + m_repeater->write(data); + } + + if (m_socket != NULL) + processDynamicTGControl(); + unsigned int ms = stopWatch.elapsed(); stopWatch.start(); @@ -1147,8 +1180,11 @@ int CDMRGateway::run() if (m_xlxReflectors != NULL) m_xlxReflectors->clock(ms); - if (voice != NULL) - voice->clock(ms); + if (xlxVoice != NULL) + xlxVoice->clock(ms); + + for (std::vector::iterator it = m_dynVoices.begin(); it != m_dynVoices.end(); ++it) + (*it)->clock(ms); for (unsigned int i = 1U; i < 3U; i++) { timer[i]->clock(ms); @@ -1162,7 +1198,7 @@ int CDMRGateway::run() CThread::sleep(10U); } - delete voice; + delete xlxVoice; m_repeater->close(); delete m_repeater; @@ -1197,6 +1233,11 @@ int CDMRGateway::run() delete m_xlxNetwork; } + if (m_socket != NULL) { + m_socket->close(); + delete m_socket; + } + delete timer[1U]; delete timer[2U]; @@ -1314,9 +1355,12 @@ bool CDMRGateway::createDMRNetwork1() std::vector typeRewrites = m_conf.getDMRNetwork1TypeRewrites(); for (std::vector::const_iterator it = typeRewrites.begin(); it != typeRewrites.end(); ++it) { - LogInfo(" Rewrite RF: %u:TG%u -> %u:%u", (*it).m_fromSlot, (*it).m_fromTG, (*it).m_toSlot, (*it).m_toId); + if ((*it).m_range == 1) + LogInfo(" Rewrite RF: %u:TG%u -> %u:%u", (*it).m_fromSlot, (*it).m_fromTG, (*it).m_toSlot, (*it).m_toId); + else + LogInfo(" Rewrite RF: %u:TG%u-%u -> %u:%u-%u", (*it).m_fromSlot, (*it).m_fromTG, (*it).m_fromTG + (*it).m_range - 1U, (*it).m_toSlot, (*it).m_toId, (*it).m_toId + (*it).m_range - 1U); - CRewriteType* rewrite = new CRewriteType(m_dmr1Name, (*it).m_fromSlot, (*it).m_fromTG, (*it).m_toSlot, (*it).m_toId); + CRewriteType* rewrite = new CRewriteType(m_dmr1Name, (*it).m_fromSlot, (*it).m_fromTG, (*it).m_toSlot, (*it).m_toId, (*it).m_range); m_dmr1RFRewrites.push_back(rewrite); } @@ -1333,6 +1377,33 @@ bool CDMRGateway::createDMRNetwork1() m_dmr1NetRewrites.push_back(rewrite); } + std::vector dynRewrites = m_conf.getDMRNetwork1TGDynRewrites(); + for (std::vector::const_iterator it = dynRewrites.begin(); it != dynRewrites.end(); ++it) { + LogInfo(" Dyn Rewrite: %u:TG%u-%u:TG%u <-> %u:TG%u (disc %u:%u) (status %u:%u) (%u exclusions)", (*it).m_slot, (*it).m_fromTG, (*it).m_slot, (*it).m_fromTG + (*it).m_range - 1U, (*it).m_slot, (*it).m_toTG, (*it).m_slot, (*it).m_discPC, (*it).m_slot, (*it).m_statusPC, (*it).m_exclTGs.size()); + + CDynVoice* voice = NULL; + if (m_conf.getVoiceEnabled()) { + std::string language = m_conf.getVoiceLanguage(); + std::string directory = m_conf.getVoiceDirectory(); + + voice = new CDynVoice(directory, language, m_repeater->getId(), (*it).m_slot, (*it).m_toTG); + bool ret = voice->open(); + if (!ret) { + delete voice; + voice = NULL; + } else { + m_dynVoices.push_back(voice); + } + } + + CRewriteDynTGNet* netRewriteDynTG = new CRewriteDynTGNet(m_dmr1Name, (*it).m_slot, (*it).m_toTG); + CRewriteDynTGRF* rfRewriteDynTG = new CRewriteDynTGRF(m_dmr1Name, (*it).m_slot, (*it).m_fromTG, (*it).m_toTG, (*it).m_discPC, (*it).m_statusPC, (*it).m_range, (*it).m_exclTGs, netRewriteDynTG, voice); + + m_dmr1RFRewrites.push_back(rfRewriteDynTG); + m_dmr1NetRewrites.push_back(netRewriteDynTG); + m_dynRF.push_back(rfRewriteDynTG); + } + std::vector idRewrites = m_conf.getDMRNetwork1IdRewrites(); for (std::vector::const_iterator it = idRewrites.begin(); it != idRewrites.end(); ++it) { LogInfo(" Rewrite Id: %u <-> %u", (*it).m_rfId, (*it).m_netId); @@ -1452,9 +1523,12 @@ bool CDMRGateway::createDMRNetwork2() std::vector typeRewrites = m_conf.getDMRNetwork2TypeRewrites(); for (std::vector::const_iterator it = typeRewrites.begin(); it != typeRewrites.end(); ++it) { - LogInfo(" Rewrite RF: %u:TG%u -> %u:%u", (*it).m_fromSlot, (*it).m_fromTG, (*it).m_toSlot, (*it).m_toId); + if ((*it).m_range == 1) + LogInfo(" Rewrite RF: %u:TG%u -> %u:%u", (*it).m_fromSlot, (*it).m_fromTG, (*it).m_toSlot, (*it).m_toId); + else + LogInfo(" Rewrite RF: %u:TG%u-%u -> %u:%u-%u", (*it).m_fromSlot, (*it).m_fromTG, (*it).m_fromTG + (*it).m_range - 1U, (*it).m_toSlot, (*it).m_toId, (*it).m_toId + (*it).m_range - 1U); - CRewriteType* rewrite = new CRewriteType(m_dmr2Name, (*it).m_fromSlot, (*it).m_fromTG, (*it).m_toSlot, (*it).m_toId); + CRewriteType* rewrite = new CRewriteType(m_dmr2Name, (*it).m_fromSlot, (*it).m_fromTG, (*it).m_toSlot, (*it).m_toId, (*it).m_range); m_dmr2RFRewrites.push_back(rewrite); } @@ -1471,6 +1545,33 @@ bool CDMRGateway::createDMRNetwork2() m_dmr2NetRewrites.push_back(rewrite); } + std::vector dynRewrites = m_conf.getDMRNetwork2TGDynRewrites(); + for (std::vector::const_iterator it = dynRewrites.begin(); it != dynRewrites.end(); ++it) { + LogInfo(" Dyn Rewrite: %u:TG%u-%u:TG%u <-> %u:TG%u (disc %u:%u) (status %u:%u) (%u exclusions)", (*it).m_slot, (*it).m_fromTG, (*it).m_slot, (*it).m_fromTG + (*it).m_range - 1U, (*it).m_slot, (*it).m_toTG, (*it).m_slot, (*it).m_discPC, (*it).m_slot, (*it).m_statusPC, (*it).m_exclTGs.size()); + + CDynVoice* voice = NULL; + if (m_conf.getVoiceEnabled()) { + std::string language = m_conf.getVoiceLanguage(); + std::string directory = m_conf.getVoiceDirectory(); + + voice = new CDynVoice(directory, language, m_repeater->getId(), (*it).m_slot, (*it).m_toTG); + bool ret = voice->open(); + if (!ret) { + delete voice; + voice = NULL; + } else { + m_dynVoices.push_back(voice); + } + } + + CRewriteDynTGNet* netRewriteDynTG = new CRewriteDynTGNet(m_dmr2Name, (*it).m_slot, (*it).m_toTG); + CRewriteDynTGRF* rfRewriteDynTG = new CRewriteDynTGRF(m_dmr2Name, (*it).m_slot, (*it).m_fromTG, (*it).m_toTG, (*it).m_discPC, (*it).m_statusPC, (*it).m_range, (*it).m_exclTGs, netRewriteDynTG, voice); + + m_dmr2RFRewrites.push_back(rfRewriteDynTG); + m_dmr2NetRewrites.push_back(netRewriteDynTG); + m_dynRF.push_back(rfRewriteDynTG); + } + std::vector idRewrites = m_conf.getDMRNetwork2IdRewrites(); for (std::vector::const_iterator it = idRewrites.begin(); it != idRewrites.end(); ++it) { LogInfo(" Rewrite Id: %u <-> %u", (*it).m_rfId, (*it).m_netId); @@ -1590,9 +1691,12 @@ bool CDMRGateway::createDMRNetwork3() std::vector typeRewrites = m_conf.getDMRNetwork3TypeRewrites(); for (std::vector::const_iterator it = typeRewrites.begin(); it != typeRewrites.end(); ++it) { - LogInfo(" Rewrite RF: %u:TG%u -> %u:%u", (*it).m_fromSlot, (*it).m_fromTG, (*it).m_toSlot, (*it).m_toId); + if ((*it).m_range == 1) + LogInfo(" Rewrite RF: %u:TG%u -> %u:%u", (*it).m_fromSlot, (*it).m_fromTG, (*it).m_toSlot, (*it).m_toId); + else + LogInfo(" Rewrite RF: %u:TG%u-%u -> %u:%u-%u", (*it).m_fromSlot, (*it).m_fromTG, (*it).m_fromTG + (*it).m_range - 1U, (*it).m_toSlot, (*it).m_toId, (*it).m_toId + (*it).m_range - 1U); - CRewriteType* rewrite = new CRewriteType(m_dmr3Name, (*it).m_fromSlot, (*it).m_fromTG, (*it).m_toSlot, (*it).m_toId); + CRewriteType* rewrite = new CRewriteType(m_dmr3Name, (*it).m_fromSlot, (*it).m_fromTG, (*it).m_toSlot, (*it).m_toId, (*it).m_range); m_dmr3RFRewrites.push_back(rewrite); } @@ -1609,6 +1713,33 @@ bool CDMRGateway::createDMRNetwork3() m_dmr3NetRewrites.push_back(rewrite); } + std::vector dynRewrites = m_conf.getDMRNetwork3TGDynRewrites(); + for (std::vector::const_iterator it = dynRewrites.begin(); it != dynRewrites.end(); ++it) { + LogInfo(" Dyn Rewrite: %u:TG%u-%u:TG%u <-> %u:TG%u (disc %u:%u) (status %u:%u) (%u exclusions)", (*it).m_slot, (*it).m_fromTG, (*it).m_slot, (*it).m_fromTG + (*it).m_range - 1U, (*it).m_slot, (*it).m_toTG, (*it).m_slot, (*it).m_discPC, (*it).m_slot, (*it).m_statusPC, (*it).m_exclTGs.size()); + + CDynVoice* voice = NULL; + if (m_conf.getVoiceEnabled()) { + std::string language = m_conf.getVoiceLanguage(); + std::string directory = m_conf.getVoiceDirectory(); + + voice = new CDynVoice(directory, language, m_repeater->getId(), (*it).m_slot, (*it).m_toTG); + bool ret = voice->open(); + if (!ret) { + delete voice; + voice = NULL; + } else { + m_dynVoices.push_back(voice); + } + } + + CRewriteDynTGNet* netRewriteDynTG = new CRewriteDynTGNet(m_dmr3Name, (*it).m_slot, (*it).m_toTG); + CRewriteDynTGRF* rfRewriteDynTG = new CRewriteDynTGRF(m_dmr3Name, (*it).m_slot, (*it).m_fromTG, (*it).m_toTG, (*it).m_discPC, (*it).m_statusPC, (*it).m_range, (*it).m_exclTGs, netRewriteDynTG, voice); + + m_dmr3RFRewrites.push_back(rfRewriteDynTG); + m_dmr3NetRewrites.push_back(netRewriteDynTG); + m_dynRF.push_back(rfRewriteDynTG); + } + std::vector idRewrites = m_conf.getDMRNetwork3IdRewrites(); for (std::vector::const_iterator it = idRewrites.begin(); it != idRewrites.end(); ++it) { LogInfo(" Rewrite Id: %u <-> %u", (*it).m_rfId, (*it).m_netId); @@ -1728,9 +1859,12 @@ bool CDMRGateway::createDMRNetwork4() std::vector typeRewrites = m_conf.getDMRNetwork4TypeRewrites(); for (std::vector::const_iterator it = typeRewrites.begin(); it != typeRewrites.end(); ++it) { - LogInfo(" Rewrite RF: %u:TG%u -> %u:%u", (*it).m_fromSlot, (*it).m_fromTG, (*it).m_toSlot, (*it).m_toId); + if ((*it).m_range == 1) + LogInfo(" Rewrite RF: %u:TG%u -> %u:%u", (*it).m_fromSlot, (*it).m_fromTG, (*it).m_toSlot, (*it).m_toId); + else + LogInfo(" Rewrite RF: %u:TG%u-%u -> %u:%u-%u", (*it).m_fromSlot, (*it).m_fromTG, (*it).m_fromTG + (*it).m_range - 1U, (*it).m_toSlot, (*it).m_toId, (*it).m_toId + (*it).m_range - 1U); - CRewriteType* rewrite = new CRewriteType(m_dmr4Name, (*it).m_fromSlot, (*it).m_fromTG, (*it).m_toSlot, (*it).m_toId); + CRewriteType* rewrite = new CRewriteType(m_dmr4Name, (*it).m_fromSlot, (*it).m_fromTG, (*it).m_toSlot, (*it).m_toId, (*it).m_range); m_dmr4RFRewrites.push_back(rewrite); } @@ -1747,6 +1881,33 @@ bool CDMRGateway::createDMRNetwork4() m_dmr4NetRewrites.push_back(rewrite); } + std::vector dynRewrites = m_conf.getDMRNetwork4TGDynRewrites(); + for (std::vector::const_iterator it = dynRewrites.begin(); it != dynRewrites.end(); ++it) { + LogInfo(" Dyn Rewrite: %u:TG%u-%u:TG%u <-> %u:TG%u (disc %u:%u) (status %u:%u) (%u exclusions)", (*it).m_slot, (*it).m_fromTG, (*it).m_slot, (*it).m_fromTG + (*it).m_range - 1U, (*it).m_slot, (*it).m_toTG, (*it).m_slot, (*it).m_discPC, (*it).m_slot, (*it).m_statusPC, (*it).m_exclTGs.size()); + + CDynVoice* voice = NULL; + if (m_conf.getVoiceEnabled()) { + std::string language = m_conf.getVoiceLanguage(); + std::string directory = m_conf.getVoiceDirectory(); + + voice = new CDynVoice(directory, language, m_repeater->getId(), (*it).m_slot, (*it).m_toTG); + bool ret = voice->open(); + if (!ret) { + delete voice; + voice = NULL; + } else { + m_dynVoices.push_back(voice); + } + } + + CRewriteDynTGNet* netRewriteDynTG = new CRewriteDynTGNet(m_dmr4Name, (*it).m_slot, (*it).m_toTG); + CRewriteDynTGRF* rfRewriteDynTG = new CRewriteDynTGRF(m_dmr4Name, (*it).m_slot, (*it).m_fromTG, (*it).m_toTG, (*it).m_discPC, (*it).m_statusPC, (*it).m_range, (*it).m_exclTGs, netRewriteDynTG, voice); + + m_dmr4RFRewrites.push_back(rfRewriteDynTG); + m_dmr4NetRewrites.push_back(netRewriteDynTG); + m_dynRF.push_back(rfRewriteDynTG); + } + std::vector idRewrites = m_conf.getDMRNetwork4IdRewrites(); for (std::vector::const_iterator it = idRewrites.begin(); it != idRewrites.end(); ++it) { LogInfo(" Rewrite Id: %u <-> %u", (*it).m_rfId, (*it).m_netId); @@ -1866,9 +2027,12 @@ bool CDMRGateway::createDMRNetwork5() std::vector typeRewrites = m_conf.getDMRNetwork5TypeRewrites(); for (std::vector::const_iterator it = typeRewrites.begin(); it != typeRewrites.end(); ++it) { - LogInfo(" Rewrite RF: %u:TG%u -> %u:%u", (*it).m_fromSlot, (*it).m_fromTG, (*it).m_toSlot, (*it).m_toId); + if ((*it).m_range == 1) + LogInfo(" Rewrite RF: %u:TG%u -> %u:%u", (*it).m_fromSlot, (*it).m_fromTG, (*it).m_toSlot, (*it).m_toId); + else + LogInfo(" Rewrite RF: %u:TG%u-%u -> %u:%u-%u", (*it).m_fromSlot, (*it).m_fromTG, (*it).m_fromTG + (*it).m_range - 1U, (*it).m_toSlot, (*it).m_toId, (*it).m_toId + (*it).m_range - 1U); - CRewriteType* rewrite = new CRewriteType(m_dmr5Name, (*it).m_fromSlot, (*it).m_fromTG, (*it).m_toSlot, (*it).m_toId); + CRewriteType* rewrite = new CRewriteType(m_dmr5Name, (*it).m_fromSlot, (*it).m_fromTG, (*it).m_toSlot, (*it).m_toId, (*it).m_range); m_dmr5RFRewrites.push_back(rewrite); } @@ -1885,6 +2049,33 @@ bool CDMRGateway::createDMRNetwork5() m_dmr5NetRewrites.push_back(rewrite); } + std::vector dynRewrites = m_conf.getDMRNetwork5TGDynRewrites(); + for (std::vector::const_iterator it = dynRewrites.begin(); it != dynRewrites.end(); ++it) { + LogInfo(" Dyn Rewrite: %u:TG%u-%u:TG%u <-> %u:TG%u (disc %u:%u) (status %u:%u) (%u exclusions)", (*it).m_slot, (*it).m_fromTG, (*it).m_slot, (*it).m_fromTG + (*it).m_range - 1U, (*it).m_slot, (*it).m_toTG, (*it).m_slot, (*it).m_discPC, (*it).m_slot, (*it).m_statusPC, (*it).m_exclTGs.size()); + + CDynVoice* voice = NULL; + if (m_conf.getVoiceEnabled()) { + std::string language = m_conf.getVoiceLanguage(); + std::string directory = m_conf.getVoiceDirectory(); + + voice = new CDynVoice(directory, language, m_repeater->getId(), (*it).m_slot, (*it).m_toTG); + bool ret = voice->open(); + if (!ret) { + delete voice; + voice = NULL; + } else { + m_dynVoices.push_back(voice); + } + } + + CRewriteDynTGNet* netRewriteDynTG = new CRewriteDynTGNet(m_dmr5Name, (*it).m_slot, (*it).m_toTG); + CRewriteDynTGRF* rfRewriteDynTG = new CRewriteDynTGRF(m_dmr5Name, (*it).m_slot, (*it).m_fromTG, (*it).m_toTG, (*it).m_discPC, (*it).m_statusPC, (*it).m_range, (*it).m_exclTGs, netRewriteDynTG, voice); + + m_dmr5RFRewrites.push_back(rfRewriteDynTG); + m_dmr5NetRewrites.push_back(netRewriteDynTG); + m_dynRF.push_back(rfRewriteDynTG); + } + std::vector idRewrites = m_conf.getDMRNetwork5IdRewrites(); for (std::vector::const_iterator it = idRewrites.begin(); it != idRewrites.end(); ++it) { LogInfo(" Rewrite Id: %u <-> %u", (*it).m_rfId, (*it).m_netId); @@ -1991,6 +2182,22 @@ bool CDMRGateway::createXLXNetwork() return true; } +bool CDMRGateway::createDynamicTGControl() +{ + unsigned int port = m_conf.getDynamicTGControlPort(); + + m_socket = new CUDPSocket(port); + + bool ret = m_socket->open(); + if (!ret) { + delete m_socket; + m_socket = NULL; + return false; + } + + return true; +} + bool CDMRGateway::linkXLX(unsigned int number) { CReflector* reflector = m_xlxReflectors->find(number); @@ -2233,3 +2440,34 @@ void CDMRGateway::processHomePosition() if (m_dmrNetwork5 != NULL) m_dmrNetwork5->writeHomePosition(buffer, length); } + +void CDMRGateway::processDynamicTGControl() +{ + unsigned char buffer[100U]; + in_addr address; + unsigned int port; + + int len = m_socket->read(buffer, 100U, address, port); + if (len <= 0) + return; + + buffer[len] = '\0'; + + if (::memcmp(buffer + 0U, "DynTG", 5U) == 0) { + char* pSlot = ::strtok((char*)(buffer + 5U), ", \r\n"); + char* pTG = ::strtok(NULL, ", \r\n"); + + if (pSlot == NULL || pTG == NULL) { + LogWarning("Malformed dynamic TG control message"); + return; + } + + unsigned int slot = (unsigned int)::atoi(pSlot); + unsigned int tg = (unsigned int)::atoi(pTG); + + for (std::vector::iterator it = m_dynRF.begin(); it != m_dynRF.end(); ++it) + (*it)->tgChange(slot, tg); + } else { + LogWarning("Unknown dynamic TG control message: %s", buffer); + } +} diff --git a/DMRGateway.h b/DMRGateway.h index becc3a7..0312e5b 100644 --- a/DMRGateway.h +++ b/DMRGateway.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2017,2019 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,2019,2020 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,10 +20,14 @@ #define DMRGateway_H #include "RepeaterProtocol.h" +#include "RewriteDynTGNet.h" +#include "RewriteDynTGRF.h" #include "MMDVMNetwork.h" #include "DMRNetwork.h" #include "Reflectors.h" +#include "UDPSocket.h" #include "RewriteTG.h" +#include "DynVoice.h" #include "Rewrite.h" #include "Timer.h" #include "Conf.h" @@ -104,6 +108,9 @@ private: std::vector m_dmr3Passalls; std::vector m_dmr4Passalls; std::vector m_dmr5Passalls; + std::vector m_dynVoices; + std::vector m_dynRF; + CUDPSocket* m_socket; bool createMMDVM(); bool createDMRNetwork1(); @@ -112,6 +119,7 @@ private: bool createDMRNetwork4(); bool createDMRNetwork5(); bool createXLXNetwork(); + bool createDynamicTGControl(); bool linkXLX(unsigned int number); void unlinkXLX(); @@ -124,6 +132,7 @@ private: void processRadioPosition(); void processTalkerAlias(); void processHomePosition(); + void processDynamicTGControl(); }; #endif diff --git a/DMRGateway.ini b/DMRGateway.ini index 8601468..675e5f9 100644 --- a/DMRGateway.ini +++ b/DMRGateway.ini @@ -70,6 +70,8 @@ TypeRewrite=1,9990,1,9990 SrcRewrite=1,9990,1,9990,1 # Reflector status returns SrcRewrite=2,4000,2,9,1001 +# Dynamic rewriting of slot 2 TGs 90-999999 to TG9 to emulate reflector behaviour +TGDynRewrite=2,90,4000,5000,9,999910,9990 # Pass all of the other private traffic on slot 1 and slot 2 PassAllPC=1 PassAllPC=2 @@ -134,3 +136,7 @@ TGRewrite=2,11,2,11,1 Password=PASSWORD Location=0 Debug=0 + +[Dynamic TG Control] +Enabled=1 +Port=3769 diff --git a/DMRGateway.vcxproj b/DMRGateway.vcxproj index 9108dcd..fb7a833 100644 --- a/DMRGateway.vcxproj +++ b/DMRGateway.vcxproj @@ -22,32 +22,32 @@ {862A3182-C71A-4B01-B58D-5F0725927A56} Win32Proj DMRGateway - 10.0.15063.0 + 10.0 Application true - v141 + v142 Unicode Application false - v141 + v142 true Unicode Application true - v141 + v142 Unicode Application false - v141 + v142 true Unicode @@ -166,6 +166,7 @@ + @@ -177,6 +178,8 @@ + + @@ -192,7 +195,7 @@ - + @@ -208,6 +211,7 @@ + @@ -219,6 +223,8 @@ + + @@ -232,7 +238,7 @@ - + diff --git a/DMRGateway.vcxproj.filters b/DMRGateway.vcxproj.filters index 126a6ab..63ad046 100644 --- a/DMRGateway.vcxproj.filters +++ b/DMRGateway.vcxproj.filters @@ -95,9 +95,6 @@ Header Files - - Header Files - Header Files @@ -134,6 +131,18 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + @@ -211,9 +220,6 @@ Source Files - - Source Files - Source Files @@ -250,5 +256,17 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + \ No newline at end of file diff --git a/DynVoice.cpp b/DynVoice.cpp new file mode 100644 index 0000000..7643163 --- /dev/null +++ b/DynVoice.cpp @@ -0,0 +1,349 @@ +/* +* Copyright (C) 2017,2020 by Jonathan Naylor G4KLX +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "DMRSlotType.h" +#include "DMRFullLC.h" +#include "DynVoice.h" +#include "DMREMB.h" +#include "Sync.h" +#include "Log.h" + +#include +#include + +#include + +const unsigned char SILENCE[] = {0xACU, 0xAAU, 0x40U, 0x20U, 0x00U, 0x44U, 0x40U, 0x80U, 0x80U}; + +const unsigned char COLOR_CODE = 3U; + +const unsigned int SILENCE_LENGTH = 9U; +const unsigned int AMBE_LENGTH = 9U; + +CDynVoice::CDynVoice(const std::string& directory, const std::string& language, unsigned int id, unsigned int slot, unsigned int tg) : +m_indxFile(), +m_ambeFile(), +m_id(id), +m_slot(slot), +m_lc(), +m_embeddedLC(), +m_status(DYNVS_NONE), +m_timer(1000U, 1U), +m_stopWatch(), +m_seqNo(0U), +m_streamId(0U), +m_sent(0U), +m_ambe(NULL), +m_positions(), +m_data(), +m_it() +{ +#if defined(_WIN32) || defined(_WIN64) + m_indxFile = directory + "\\" + language + ".indx"; + m_ambeFile = directory + "\\" + language + ".ambe"; +#else + m_indxFile = directory + "/" + language + ".indx"; + m_ambeFile = directory + "/" + language + ".ambe"; +#endif + + m_lc.setFLCO(FLCO_GROUP); + m_lc.setSrcId(id); + m_lc.setDstId(tg); + + m_embeddedLC.setLC(m_lc); +} + +CDynVoice::~CDynVoice() +{ + for (std::vector::iterator it = m_data.begin(); it != m_data.end(); ++it) + delete *it; + + for (std::unordered_map::iterator it = m_positions.begin(); it != m_positions.end(); ++it) + delete it->second; + + m_data.clear(); + m_positions.clear(); + + delete[] m_ambe; +} + +bool CDynVoice::open() +{ + FILE* fpindx = ::fopen(m_indxFile.c_str(), "rt"); + if (fpindx == NULL) { + LogError("Unable to open the index file - %s", m_indxFile.c_str()); + return false; + } + + struct stat statStruct; + int ret = ::stat(m_ambeFile.c_str(), &statStruct); + if (ret != 0) { + LogError("Unable to stat the AMBE file - %s", m_ambeFile.c_str()); + ::fclose(fpindx); + return false; + } + + FILE* fpambe = ::fopen(m_ambeFile.c_str(), "rb"); + if (fpambe == NULL) { + LogError("Unable to open the AMBE file - %s", m_ambeFile.c_str()); + ::fclose(fpindx); + return false; + } + + m_ambe = new unsigned char[statStruct.st_size]; + + size_t sizeRead = ::fread(m_ambe, 1U, statStruct.st_size, fpambe); + if (sizeRead != 0U) { + char buffer[80U]; + while (::fgets(buffer, 80, fpindx) != NULL) { + char* p1 = ::strtok(buffer, "\t\r\n"); + char* p2 = ::strtok(NULL, "\t\r\n"); + char* p3 = ::strtok(NULL, "\t\r\n"); + + if (p1 != NULL && p2 != NULL && p3 != NULL) { + std::string symbol = std::string(p1); + unsigned int start = ::atoi(p2) * AMBE_LENGTH; + unsigned int length = ::atoi(p3) * AMBE_LENGTH; + + CDynPositions* pos = new CDynPositions; + pos->m_start = start; + pos->m_length = length; + + m_positions[symbol] = pos; + } + } + } + + ::fclose(fpindx); + ::fclose(fpambe); + + return true; +} + +void CDynVoice::linkedTo(unsigned int number) +{ + char letters[10U]; + ::sprintf(letters, "%u", number); + + std::vector words; + if (m_positions.count("linkedto") == 0U) + words.push_back("linked"); + else + words.push_back("linkedto"); + + for (unsigned int i = 0U; letters[i] != '\0'; i++) + words.push_back(std::string(1U, letters[i])); + + createVoice(words); +} + +void CDynVoice::unlinked() +{ + std::vector words; + words.push_back("notlinked"); + + createVoice(words); +} + +void CDynVoice::abort() +{ + for (std::vector::iterator it = m_data.begin(); it != m_data.end(); ++it) + delete* it; + + m_data.clear(); + m_timer.stop(); + + m_status = DYNVS_NONE; +} + +void CDynVoice::createVoice(const std::vector& words) +{ + unsigned int ambeLength = 0U; + for (std::vector::const_iterator it = words.begin(); it != words.end(); ++it) { + if (m_positions.count(*it) > 0U) { + CDynPositions* position = m_positions.at(*it); + ambeLength += position->m_length; + } else { + LogWarning("Unable to find character/phrase \"%s\" in the index", (*it).c_str()); + } + } + + // Ensure that the AMBE is an integer number of DMR frames + if ((ambeLength % (3U * AMBE_LENGTH)) != 0U) { + unsigned int frames = ambeLength / (3U * AMBE_LENGTH); + frames++; + ambeLength = frames * (3U * AMBE_LENGTH); + } + + // Add space for silence before and after the voice + ambeLength += SILENCE_LENGTH * AMBE_LENGTH; + ambeLength += SILENCE_LENGTH * AMBE_LENGTH; + + unsigned char* ambeData = new unsigned char[ambeLength]; + + // Fill the AMBE data with silence + for (unsigned int i = 0U; i < ambeLength; i += AMBE_LENGTH) + ::memcpy(ambeData + i, SILENCE, AMBE_LENGTH); + + // Put offset in for silence at the beginning + unsigned int pos = SILENCE_LENGTH * AMBE_LENGTH; + for (std::vector::const_iterator it = words.begin(); it != words.end(); ++it) { + if (m_positions.count(*it) > 0U) { + CDynPositions* position = m_positions.at(*it); + unsigned int start = position->m_start; + unsigned int length = position->m_length; + ::memcpy(ambeData + pos, m_ambe + start, length); + pos += length; + } + } + + for (std::vector::iterator it = m_data.begin(); it != m_data.end(); ++it) + delete *it; + + m_data.clear(); + + m_streamId = ::rand() + 1U; + m_seqNo = 0U; + + createHeaderTerminator(DT_VOICE_LC_HEADER); + createHeaderTerminator(DT_VOICE_LC_HEADER); + createHeaderTerminator(DT_VOICE_LC_HEADER); + + unsigned char buffer[DMR_FRAME_LENGTH_BYTES]; + + unsigned int n = 0U; + for (unsigned int i = 0U; i < ambeLength; i += (3U * AMBE_LENGTH)) { + unsigned char* p = ambeData + i; + + CDMRData* data = new CDMRData; + + data->setSlotNo(m_slot); + data->setFLCO(FLCO_GROUP); + data->setSrcId(m_lc.getSrcId()); + data->setDstId(m_lc.getDstId()); + data->setN(n); + data->setSeqNo(m_seqNo++); + data->setStreamId(m_streamId); + + ::memcpy(buffer + 0U, p + 0U, AMBE_LENGTH); + ::memcpy(buffer + 9U, p + 9U, AMBE_LENGTH); + ::memcpy(buffer + 15U, p + 9U, AMBE_LENGTH); + ::memcpy(buffer + 24U, p + 18U, AMBE_LENGTH); + + if (n == 0U) { + CSync::addDMRAudioSync(buffer, true); + data->setDataType(DT_VOICE_SYNC); + } else { + unsigned char lcss = m_embeddedLC.getData(buffer, n); + + CDMREMB emb; + emb.setColorCode(COLOR_CODE); + emb.setPI(false); + emb.setLCSS(lcss); + emb.getData(buffer); + + data->setDataType(DT_VOICE); + } + + n++; + if (n >= 6U) + n = 0U; + + data->setData(buffer); + + m_data.push_back(data); + } + + createHeaderTerminator(DT_TERMINATOR_WITH_LC); + createHeaderTerminator(DT_TERMINATOR_WITH_LC); + + delete[] ambeData; + + m_status = DYNVS_WAITING; + m_timer.start(); +} + +bool CDynVoice::read(CDMRData& data) +{ + if (m_status != DYNVS_SENDING) + return false; + + unsigned int count = m_stopWatch.elapsed() / DMR_SLOT_TIME; + + if (m_sent < count) { + data = *(*m_it); + + ++m_sent; + ++m_it; + + if (m_it == m_data.end()) { + for (std::vector::iterator it = m_data.begin(); it != m_data.end(); ++it) + delete *it; + m_data.clear(); + m_timer.stop(); + m_status = DYNVS_NONE; + } + + return true; + } + + return false; +} + +void CDynVoice::clock(unsigned int ms) +{ + m_timer.clock(ms); + if (m_timer.isRunning() && m_timer.hasExpired()) { + if (m_status == DYNVS_WAITING) { + m_stopWatch.start(); + m_status = DYNVS_SENDING; + m_it = m_data.begin(); + m_sent = 0U; + } + } +} + +void CDynVoice::createHeaderTerminator(unsigned char type) +{ + CDMRData* data = new CDMRData; + + data->setSlotNo(m_slot); + data->setFLCO(FLCO_GROUP); + data->setSrcId(m_lc.getSrcId()); + data->setDstId(m_lc.getDstId()); + data->setDataType(type); + data->setN(0U); + data->setSeqNo(m_seqNo++); + data->setStreamId(m_streamId); + + unsigned char buffer[DMR_FRAME_LENGTH_BYTES]; + + CDMRFullLC fullLC; + fullLC.encode(m_lc, buffer, type); + + CDMRSlotType slotType; + slotType.setColorCode(COLOR_CODE); + slotType.setDataType(type); + slotType.getData(buffer); + + CSync::addDMRDataSync(buffer, true); + + data->setData(buffer); + + m_data.push_back(data); +} diff --git a/DynVoice.h b/DynVoice.h new file mode 100644 index 0000000..64abd2e --- /dev/null +++ b/DynVoice.h @@ -0,0 +1,81 @@ +/* +* Copyright (C) 2017,2020 by Jonathan Naylor G4KLX +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#if !defined(DynVoice_H) +#define DynVoice_H + +#include "DMREmbeddedData.h" +#include "StopWatch.h" +#include "DMRData.h" +#include "DMRLC.h" +#include "Timer.h" + +#include +#include +#include + +enum DYNVOICE_STATUS { + DYNVS_NONE, + DYNVS_WAITING, + DYNVS_SENDING +}; + +struct CDynPositions { + unsigned int m_start; + unsigned int m_length; +}; + +class CDynVoice { +public: + CDynVoice(const std::string& directory, const std::string& language, unsigned int id, unsigned int slot, unsigned int tg); + ~CDynVoice(); + + bool open(); + + void linkedTo(unsigned int number); + void unlinked(); + + bool read(CDMRData& data); + + void abort(); + + void clock(unsigned int ms); + +private: + std::string m_indxFile; + std::string m_ambeFile; + unsigned int m_id; + unsigned int m_slot; + CDMRLC m_lc; + CDMREmbeddedData m_embeddedLC; + DYNVOICE_STATUS m_status; + CTimer m_timer; + CStopWatch m_stopWatch; + unsigned int m_seqNo; + unsigned int m_streamId; + unsigned int m_sent; + unsigned char* m_ambe; + std::unordered_map m_positions; + std::vector m_data; + std::vector::const_iterator m_it; + + void createHeaderTerminator(unsigned char type); + void createVoice(const std::vector& words); +}; + +#endif diff --git a/Makefile b/Makefile index 2414e70..591b73f 100644 --- a/Makefile +++ b/Makefile @@ -5,8 +5,9 @@ LIBS = -lpthread LDFLAGS = -g OBJECTS = BPTC19696.o Conf.o CRC.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREmbeddedData.o DMREMB.o DMRFullLC.o DMRGateway.o DMRLC.o DMRNetwork.o DMRSlotType.o \ - Golay2087.o Hamming.o Log.o MMDVMNetwork.o PassAllPC.o PassAllTG.o QR1676.o Reflectors.o RepeaterProtocol.o Rewrite.o RewriteDstId.o RewritePC.o RewriteSrc.o \ - RewriteSrcId.o RewriteTG.o RewriteType.o RS129.o SHA256.o StopWatch.o Sync.o Thread.o Timer.o UDPSocket.o Utils.o Voice.o + DynVoice.o Golay2087.o Hamming.o Log.o MMDVMNetwork.o PassAllPC.o PassAllTG.o QR1676.o Reflectors.o RepeaterProtocol.o Rewrite.o RewriteDstId.o \ + RewriteDynTGNet.o RewriteDynTGRF.o RewritePC.o RewriteSrc.o RewriteSrcId.o RewriteTG.o RewriteType.o RS129.o SHA256.o StopWatch.o Sync.o Thread.o \ + Timer.o UDPSocket.o Utils.o XLXVoice.o all: DMRGateway diff --git a/PassAllPC.cpp b/PassAllPC.cpp index 342e988..08129d7 100644 --- a/PassAllPC.cpp +++ b/PassAllPC.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2017 by Jonathan Naylor G4KLX +* Copyright (C) 2017,2020 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -36,7 +36,7 @@ CPassAllPC::~CPassAllPC() { } -bool CPassAllPC::process(CDMRData& data, bool trace) +PROCESS_RESULT CPassAllPC::process(CDMRData& data, bool trace) { FLCO flco = data.getFLCO(); unsigned int slotNo = data.getSlotNo(); @@ -46,5 +46,5 @@ bool CPassAllPC::process(CDMRData& data, bool trace) if (trace) LogDebug("Rule Trace,\tPassAllPC %s Slot=%u: %s", m_name.c_str(), m_slot, ret ? "matched" : "not matched"); - return ret; + return ret ? RESULT_MATCHED : RESULT_UNMATCHED; } diff --git a/PassAllPC.h b/PassAllPC.h index 3b589b0..b48d5d4 100644 --- a/PassAllPC.h +++ b/PassAllPC.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2017 by Jonathan Naylor G4KLX +* Copyright (C) 2017,2020 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,7 +29,7 @@ public: CPassAllPC(const std::string& name, unsigned int slot); virtual ~CPassAllPC(); - virtual bool process(CDMRData& data, bool trace); + virtual PROCESS_RESULT process(CDMRData& data, bool trace); private: std::string m_name; diff --git a/PassAllTG.cpp b/PassAllTG.cpp index ccfb08e..7a72d33 100644 --- a/PassAllTG.cpp +++ b/PassAllTG.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2017 by Jonathan Naylor G4KLX +* Copyright (C) 2017,2020 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -36,7 +36,7 @@ CPassAllTG::~CPassAllTG() { } -bool CPassAllTG::process(CDMRData& data, bool trace) +PROCESS_RESULT CPassAllTG::process(CDMRData& data, bool trace) { FLCO flco = data.getFLCO(); unsigned int slotNo = data.getSlotNo(); @@ -46,5 +46,5 @@ bool CPassAllTG::process(CDMRData& data, bool trace) if (trace) LogDebug("Rule Trace,\tPassAllTG %s Slot=%u: %s", m_name.c_str(), m_slot, ret ? "matched" : "not matched"); - return ret; + return ret ? RESULT_MATCHED : RESULT_UNMATCHED; } diff --git a/PassAllTG.h b/PassAllTG.h index 01078bd..0974701 100644 --- a/PassAllTG.h +++ b/PassAllTG.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2017 by Jonathan Naylor G4KLX +* Copyright (C) 2017,2020 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,7 +29,7 @@ public: CPassAllTG(const std::string& name, unsigned int slot); virtual ~CPassAllTG(); - virtual bool process(CDMRData& data, bool trace); + virtual PROCESS_RESULT process(CDMRData& data, bool trace); private: std::string m_name; diff --git a/Rewrite.h b/Rewrite.h index f2789eb..ce0dbf4 100644 --- a/Rewrite.h +++ b/Rewrite.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2017 by Jonathan Naylor G4KLX +* Copyright (C) 2017,2020 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,12 +23,18 @@ #include "DMRData.h" #include "DMRLC.h" +enum PROCESS_RESULT { + RESULT_UNMATCHED, + RESULT_MATCHED, + RESULT_IGNORED +}; + class CRewrite { public: CRewrite(); virtual ~CRewrite(); - virtual bool process(CDMRData& data, bool trace) = 0; + virtual PROCESS_RESULT process(CDMRData& data, bool trace) = 0; protected: void processMessage(CDMRData& data); diff --git a/RewriteDstId.cpp b/RewriteDstId.cpp index ef796e3..e979965 100644 --- a/RewriteDstId.cpp +++ b/RewriteDstId.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2017 by Jonathan Naylor G4KLX +* Copyright (C) 2017,2020 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,7 +35,7 @@ CRewriteDstId::~CRewriteDstId() { } -bool CRewriteDstId::process(CDMRData& data, bool trace) +PROCESS_RESULT CRewriteDstId::process(CDMRData& data, bool trace) { FLCO flco = data.getFLCO(); unsigned int dstId = data.getDstId(); @@ -44,7 +44,7 @@ bool CRewriteDstId::process(CDMRData& data, bool trace) if (trace) LogDebug("Rule Trace,\tRewriteDstId from %s Src=%u: not matched", m_name.c_str(), m_fromId); - return false; + return RESULT_UNMATCHED; } data.setDstId(m_toId); @@ -56,5 +56,5 @@ bool CRewriteDstId::process(CDMRData& data, bool trace) LogDebug("Rule Trace,\tRewriteDstId to %s Src=%u", m_name.c_str(), m_toId); } - return true; + return RESULT_MATCHED; } diff --git a/RewriteDstId.h b/RewriteDstId.h index 8d066bd..388ffd6 100644 --- a/RewriteDstId.h +++ b/RewriteDstId.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2017 by Jonathan Naylor G4KLX +* Copyright (C) 2017,2020 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,7 +29,7 @@ public: CRewriteDstId(const std::string& name, unsigned int fromId, unsigned int toID); virtual ~CRewriteDstId(); - virtual bool process(CDMRData& data, bool trace); + virtual PROCESS_RESULT process(CDMRData& data, bool trace); private: std::string m_name; diff --git a/RewriteDynTGNet.cpp b/RewriteDynTGNet.cpp new file mode 100644 index 0000000..fde2ffc --- /dev/null +++ b/RewriteDynTGNet.cpp @@ -0,0 +1,67 @@ +/* +* Copyright (C) 2017,2020 by Jonathan Naylor G4KLX +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "RewriteDynTGNet.h" + +#include "DMRDefines.h" +#include "Log.h" + +#include +#include + +CRewriteDynTGNet::CRewriteDynTGNet(const std::string& name, unsigned int slot, unsigned int toTG) : +CRewrite(), +m_name(name), +m_slot(slot), +m_toTG(toTG), +m_currentTG(0U) +{ + assert(slot == 1U || slot == 2U); +} + +CRewriteDynTGNet::~CRewriteDynTGNet() +{ +} + +PROCESS_RESULT CRewriteDynTGNet::process(CDMRData& data, bool trace) +{ + FLCO flco = data.getFLCO(); + unsigned int dstId = data.getDstId(); + unsigned int slotNo = data.getSlotNo(); + + if (flco != FLCO_GROUP || slotNo != m_slot || dstId != m_currentTG) { + if (trace) + LogDebug("Rule Trace,\tRewriteDynTGNet from %s Slot=%u Dst=TG%u: not matched", m_name.c_str(), m_slot, m_currentTG); + + return RESULT_UNMATCHED; + } + + data.setDstId(m_toTG); + + processMessage(data); + + if (trace) + LogDebug("Rule Trace,\tRewriteDynTGNet from %s Slot=%u Dst=TG%u: matched", m_name.c_str(), m_slot, m_currentTG); + + return RESULT_MATCHED; +} + +void CRewriteDynTGNet::setCurrentTG(unsigned int currentTG) +{ + m_currentTG = currentTG; +} diff --git a/RewriteDynTGNet.h b/RewriteDynTGNet.h new file mode 100644 index 0000000..60b59c7 --- /dev/null +++ b/RewriteDynTGNet.h @@ -0,0 +1,43 @@ +/* +* Copyright (C) 2017,2020 by Jonathan Naylor G4KLX +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#if !defined(REWRITEDYNTGNET_H) +#define REWRITEDYNTGNET_H + +#include "Rewrite.h" +#include "DMRData.h" + +#include + +class CRewriteDynTGNet : public CRewrite { +public: + CRewriteDynTGNet(const std::string& name, unsigned int slot, unsigned int toTG); + virtual ~CRewriteDynTGNet(); + + virtual PROCESS_RESULT process(CDMRData& data, bool trace); + + void setCurrentTG(unsigned int tg); + +private: + std::string m_name; + unsigned int m_slot; + unsigned int m_toTG; + unsigned int m_currentTG; +}; + +#endif diff --git a/RewriteDynTGRF.cpp b/RewriteDynTGRF.cpp new file mode 100644 index 0000000..728b472 --- /dev/null +++ b/RewriteDynTGRF.cpp @@ -0,0 +1,180 @@ +/* +* Copyright (C) 2017,2020 by Jonathan Naylor G4KLX +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "RewriteDynTGRF.h" + +#include "DMRDefines.h" +#include "Log.h" + +#include +#include +#include + +CRewriteDynTGRF::CRewriteDynTGRF(const std::string& name, unsigned int slot, unsigned int fromTG, unsigned int toTG, unsigned int discPC, unsigned int statusPC, unsigned int range, const std::vector& exclTGs, CRewriteDynTGNet* rewriteNet, CDynVoice* voice) : +CRewrite(), +m_name(name), +m_slot(slot), +m_fromTGStart(fromTG), +m_fromTGEnd(fromTG + range - 1U), +m_toTG(toTG), +m_discPC(discPC), +m_statusPC(statusPC), +m_exclTGs(exclTGs), +m_rewriteNet(rewriteNet), +m_voice(voice), +m_currentTG(0U) +{ + assert(slot == 1U || slot == 2U); + assert(rewriteNet != NULL); +} + +CRewriteDynTGRF::~CRewriteDynTGRF() +{ +} + +PROCESS_RESULT CRewriteDynTGRF::process(CDMRData& data, bool trace) +{ + FLCO flco = data.getFLCO(); + unsigned int dstId = data.getDstId(); + unsigned int slotNo = data.getSlotNo(); + unsigned char type = data.getDataType(); + + if (flco == FLCO_GROUP && slotNo == m_slot && dstId == m_toTG) { + if (trace) + LogDebug("Rule Trace,\tRewriteDynTGRF from %s Slot=%u Dst=TG%u: matched", m_name.c_str(), m_slot, m_toTG); + + if (m_currentTG != 0U) { + data.setDstId(m_currentTG); + + processMessage(data); + + return RESULT_MATCHED; + } else { + return RESULT_IGNORED; + } + } + + if (slotNo == m_slot && dstId == m_discPC) { + if (trace) + LogDebug("Rule Trace,\tRewriteDynTGRF from %s Slot=%u Dst=%u: matched", m_name.c_str(), m_slot, m_discPC); + + if (m_currentTG != 0U) { + data.setFLCO(FLCO_GROUP); + + processMessage(data); + + if (type == DT_TERMINATOR_WITH_LC) { + m_rewriteNet->setCurrentTG(0U); + m_currentTG = 0U; + if (m_voice != NULL) + m_voice->unlinked(); + } + + return RESULT_MATCHED; + } else { + return RESULT_IGNORED; + } + } + + if (slotNo == m_slot && dstId == m_statusPC) { + if (trace) + LogDebug("Rule Trace,\tRewriteDynTGRF from %s Slot=%u Dst=%u: matched", m_name.c_str(), m_slot, m_statusPC); + + if (type == DT_TERMINATOR_WITH_LC && m_voice != NULL) { + if (m_currentTG == 0U) + m_voice->unlinked(); + else + m_voice->linkedTo(m_currentTG); + } + + return RESULT_IGNORED; + } + + if (slotNo == m_slot && std::find(m_exclTGs.cbegin(), m_exclTGs.cend(), dstId) != m_exclTGs.cend()) { + if (trace) + LogDebug("Rule Trace,\tRewriteDynTGRF from %s Slot=%u Dst=%u: not matched", m_name.c_str(), m_slot, dstId); + + return RESULT_UNMATCHED; + } + + if (slotNo == m_slot && dstId >= m_fromTGStart && dstId <= m_fromTGEnd) { + if (trace) { + if (m_fromTGStart == m_fromTGEnd) + LogDebug("Rule Trace,\tRewriteDynTGRF from %s Slot=%u Dst=%u: matched", m_name.c_str(), m_slot, m_fromTGStart); + else + LogDebug("Rule Trace,\tRewriteDynTGRF from %s Slot=%u Dst=%u-%u: matched", m_name.c_str(), m_slot, m_fromTGStart, m_fromTGEnd); + } + + data.setFLCO(FLCO_GROUP); + + processMessage(data); + + if (type == DT_TERMINATOR_WITH_LC) { + m_rewriteNet->setCurrentTG(dstId); + m_currentTG = dstId; + if (m_voice != NULL) + m_voice->linkedTo(dstId); + } + + return RESULT_MATCHED; + } + + if (trace) { + if (m_fromTGStart == m_fromTGEnd) + LogDebug("Rule Trace,\tRewriteDynTGRF from %s Slot=%u Dst=%u or Dst=TG%u or Dst=%u or Dst=%u: not matched", m_name.c_str(), m_slot, m_fromTGStart, m_toTG, m_discPC, m_statusPC); + else + LogDebug("Rule Trace,\tRewriteDynTGRF from %s Slot=%u Dst=%u-%u or Dst=TG%u or Dst=%u or Dst=%u: not matched", m_name.c_str(), m_slot, m_fromTGStart, m_fromTGEnd, m_toTG, m_discPC, m_statusPC); + } + + return RESULT_UNMATCHED; +} + +void CRewriteDynTGRF::tgChange(unsigned int slot, unsigned int tg) +{ + if (slot == m_slot && tg == m_discPC) { + if (m_currentTG != 0U) { + m_currentTG = 0U; + m_rewriteNet->setCurrentTG(0U); + if (m_voice != NULL) + m_voice->unlinked(); + } + return; + } + + if (slot == m_slot && tg == m_statusPC) + return; + + if (slot == m_slot && std::find(m_exclTGs.cbegin(), m_exclTGs.cend(), tg) != m_exclTGs.cend()) + return; + + if (slot == m_slot && tg >= m_fromTGStart && tg <= m_fromTGEnd) { + if (m_currentTG != tg) { + m_currentTG = tg; + m_rewriteNet->setCurrentTG(tg); + if (m_voice != NULL) + m_voice->linkedTo(tg); + } + return; + } +} + +void CRewriteDynTGRF::stopVoice(unsigned int slot) +{ + if (slot == m_slot && m_voice != NULL) + m_voice->abort(); +} diff --git a/RewriteDynTGRF.h b/RewriteDynTGRF.h new file mode 100644 index 0000000..d5a2b06 --- /dev/null +++ b/RewriteDynTGRF.h @@ -0,0 +1,57 @@ +/* +* Copyright (C) 2017,2020 by Jonathan Naylor G4KLX +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#if !defined(REWRITEDYNTGRF_H) +#define REWRITEDYNTGRF_H + +#include "DynVoice.h" +#include "Rewrite.h" +#include "DMRData.h" + +#include "RewriteDynTGNet.h" + +#include +#include + +class CRewriteDynTGRF : public CRewrite { +public: + CRewriteDynTGRF(const std::string& name, unsigned int slot, unsigned int fromTG, unsigned int toTG, unsigned int discPC, unsigned int statusPC, unsigned int range, const std::vector& exclTGs, CRewriteDynTGNet* rewriteNet, CDynVoice* voice); + virtual ~CRewriteDynTGRF(); + + virtual PROCESS_RESULT process(CDMRData& data, bool trace); + + void stopVoice(unsigned int slot); + + void tgChange(unsigned int slot, unsigned int tg); + +private: + std::string m_name; + unsigned int m_slot; + unsigned int m_fromTGStart; + unsigned int m_fromTGEnd; + unsigned int m_toTG; + unsigned int m_discPC; + unsigned int m_statusPC; + std::vector m_exclTGs; + CRewriteDynTGNet* m_rewriteNet; + CDynVoice* m_voice; + unsigned int m_currentTG; +}; + + +#endif diff --git a/RewritePC.cpp b/RewritePC.cpp index 9420945..bc2b579 100644 --- a/RewritePC.cpp +++ b/RewritePC.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2017 by Jonathan Naylor G4KLX +* Copyright (C) 2017,2020 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -42,7 +42,7 @@ CRewritePC::~CRewritePC() { } -bool CRewritePC::process(CDMRData& data, bool trace) +PROCESS_RESULT CRewritePC::process(CDMRData& data, bool trace) { FLCO flco = data.getFLCO(); unsigned int dstId = data.getDstId(); @@ -52,7 +52,7 @@ bool CRewritePC::process(CDMRData& data, bool trace) if (trace) LogDebug("Rule Trace,\tRewritePC from %s Slot=%u Dst=%u-%u: not matched", m_name.c_str(), m_fromSlot, m_fromIdStart, m_fromIdEnd); - return false; + return RESULT_UNMATCHED; } if (m_fromSlot != m_toSlot) @@ -70,5 +70,5 @@ bool CRewritePC::process(CDMRData& data, bool trace) LogDebug("Rule Trace,\tRewritePC to %s Slot=%u Dst=%u-%u", m_name.c_str(), m_toSlot, m_toIdStart, m_toIdEnd); } - return true; + return RESULT_MATCHED; } diff --git a/RewritePC.h b/RewritePC.h index 7d42432..d60b12b 100644 --- a/RewritePC.h +++ b/RewritePC.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2017 by Jonathan Naylor G4KLX +* Copyright (C) 2017,2020 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,7 +29,7 @@ public: CRewritePC(const std::string& name, unsigned int fromSlot, unsigned int fromId, unsigned int toSlot, unsigned int toId, unsigned int range); virtual ~CRewritePC(); - virtual bool process(CDMRData& data, bool trace); + virtual PROCESS_RESULT process(CDMRData& data, bool trace); private: std::string m_name; diff --git a/RewriteSrc.cpp b/RewriteSrc.cpp index dcd2152..ba61c25 100644 --- a/RewriteSrc.cpp +++ b/RewriteSrc.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2017 by Jonathan Naylor G4KLX +* Copyright (C) 2017,2020 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -41,7 +41,7 @@ CRewriteSrc::~CRewriteSrc() { } -bool CRewriteSrc::process(CDMRData& data, bool trace) +PROCESS_RESULT CRewriteSrc::process(CDMRData& data, bool trace) { FLCO flco = data.getFLCO(); unsigned int srcId = data.getSrcId(); @@ -51,7 +51,7 @@ bool CRewriteSrc::process(CDMRData& data, bool trace) if (trace) LogDebug("Rule Trace,\tRewriteSrc from %s Slot=%u Src=%u-%u: not matched", m_name.c_str(), m_fromSlot, m_fromIdStart, m_fromIdEnd); - return false; + return RESULT_UNMATCHED; } if (m_fromSlot != m_toSlot) @@ -67,5 +67,5 @@ bool CRewriteSrc::process(CDMRData& data, bool trace) LogDebug("Rule Trace,\tRewriteSrc to %s Slot=%u Dst=TG%u", m_name.c_str(), m_toSlot, m_toTG); } - return true; + return RESULT_MATCHED; } diff --git a/RewriteSrc.h b/RewriteSrc.h index d970f23..83321bc 100644 --- a/RewriteSrc.h +++ b/RewriteSrc.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2017 by Jonathan Naylor G4KLX +* Copyright (C) 2017,2020 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,7 +29,7 @@ public: CRewriteSrc(const std::string& name, unsigned int fromSlot, unsigned int fromId, unsigned int toSlot, unsigned int toTG, unsigned int range); virtual ~CRewriteSrc(); - virtual bool process(CDMRData& data, bool trace); + virtual PROCESS_RESULT process(CDMRData& data, bool trace); private: std::string m_name; diff --git a/RewriteSrcId.cpp b/RewriteSrcId.cpp index 2382c75..1255a65 100644 --- a/RewriteSrcId.cpp +++ b/RewriteSrcId.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2017 by Jonathan Naylor G4KLX +* Copyright (C) 2017,2020 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,7 +35,7 @@ CRewriteSrcId::~CRewriteSrcId() { } -bool CRewriteSrcId::process(CDMRData& data, bool trace) +PROCESS_RESULT CRewriteSrcId::process(CDMRData& data, bool trace) { unsigned int srcId = data.getSrcId(); @@ -43,7 +43,7 @@ bool CRewriteSrcId::process(CDMRData& data, bool trace) if (trace) LogDebug("Rule Trace,\tRewriteSrcId from %s Src=%u: not matched", m_name.c_str(), m_fromId); - return false; + return RESULT_UNMATCHED; } data.setSrcId(m_toId); @@ -55,5 +55,5 @@ bool CRewriteSrcId::process(CDMRData& data, bool trace) LogDebug("Rule Trace,\tRewriteSrcId to %s Src=%u", m_name.c_str(), m_toId); } - return true; + return RESULT_MATCHED; } diff --git a/RewriteSrcId.h b/RewriteSrcId.h index 76bd17e..ff33006 100644 --- a/RewriteSrcId.h +++ b/RewriteSrcId.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2017 by Jonathan Naylor G4KLX +* Copyright (C) 2017,2020 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,7 +29,7 @@ public: CRewriteSrcId(const std::string& name, unsigned int fromId, unsigned int toID); virtual ~CRewriteSrcId(); - virtual bool process(CDMRData& data, bool trace); + virtual PROCESS_RESULT process(CDMRData& data, bool trace); private: std::string m_name; diff --git a/RewriteTG.cpp b/RewriteTG.cpp index 73d7176..1741a33 100644 --- a/RewriteTG.cpp +++ b/RewriteTG.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2017 by Jonathan Naylor G4KLX +* Copyright (C) 2017,2020 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -42,7 +42,7 @@ CRewriteTG::~CRewriteTG() { } -bool CRewriteTG::process(CDMRData& data, bool trace) +PROCESS_RESULT CRewriteTG::process(CDMRData& data, bool trace) { FLCO flco = data.getFLCO(); unsigned int dstId = data.getDstId(); @@ -56,7 +56,7 @@ bool CRewriteTG::process(CDMRData& data, bool trace) LogDebug("Rule Trace,\tRewriteTG from %s Slot=%u Dst=TG%u-TG%u: not matched", m_name.c_str(), m_fromSlot, m_fromTGStart, m_fromTGEnd); } - return false; + return RESULT_UNMATCHED; } if (m_fromSlot != m_toSlot) @@ -81,5 +81,5 @@ bool CRewriteTG::process(CDMRData& data, bool trace) LogDebug("Rule Trace,\tRewriteTG to %s Slot=%u Dst=TG%u-TG%u", m_name.c_str(), m_toSlot, m_toTGStart, m_toTGEnd); } - return true; + return RESULT_MATCHED; } diff --git a/RewriteTG.h b/RewriteTG.h index 30c8984..88dd06f 100644 --- a/RewriteTG.h +++ b/RewriteTG.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2017 by Jonathan Naylor G4KLX +* Copyright (C) 2017,2020 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,7 +29,7 @@ public: CRewriteTG(const std::string& name, unsigned int fromSlot, unsigned int fromTG, unsigned int toSlot, unsigned int toTG, unsigned int range); virtual ~CRewriteTG(); - virtual bool process(CDMRData& data, bool trace); + virtual PROCESS_RESULT process(CDMRData& data, bool trace); private: std::string m_name; diff --git a/RewriteType.cpp b/RewriteType.cpp index c691fcc..0ceb1da 100644 --- a/RewriteType.cpp +++ b/RewriteType.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2017 by Jonathan Naylor G4KLX +* Copyright (C) 2017,2020 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,13 +24,15 @@ #include #include -CRewriteType::CRewriteType(const std::string& name, unsigned int fromSlot, unsigned int fromTG, unsigned int toSlot, unsigned int toId) : +CRewriteType::CRewriteType(const std::string& name, unsigned int fromSlot, unsigned int fromTG, unsigned int toSlot, unsigned int toId, unsigned int range) : CRewrite(), m_name(name), m_fromSlot(fromSlot), -m_fromTG(fromTG), +m_fromTGStart(fromTG), +m_fromTGEnd(fromTG + range - 1U), m_toSlot(toSlot), -m_toId(toId) +m_toIdStart(toId), +m_toIdEnd(toId + range - 1U) { assert(fromSlot == 1U || fromSlot == 2U); assert(toSlot == 1U || toSlot == 2U); @@ -40,29 +42,43 @@ CRewriteType::~CRewriteType() { } -bool CRewriteType::process(CDMRData& data, bool trace) +PROCESS_RESULT CRewriteType::process(CDMRData& data, bool trace) { FLCO flco = data.getFLCO(); unsigned int dstId = data.getDstId(); unsigned int slotNo = data.getSlotNo(); - if (flco != FLCO_GROUP || slotNo != m_fromSlot || dstId != m_fromTG) { - if (trace) - LogDebug("Rule Trace,\tRewriteType %s Slot=%u Dst=TG%u: not matched", m_name.c_str(), m_fromSlot, m_fromTG); - - return false; + if (flco != FLCO_GROUP || slotNo != m_fromSlot || dstId < m_fromTGStart || dstId > m_fromTGEnd) { + if (trace) { + if (m_fromTGStart == m_fromTGEnd) + LogDebug("Rule Trace,\tRewriteType from \"%s\" Slot=%u Dst=TG%u: not matched", m_name.c_str(), m_fromSlot, m_fromTGStart); + else + LogDebug("Rule Trace,\tRewriteType from \"%s\" Slot=%u Dst=TG%u-%u: not matched", m_name.c_str(), m_fromSlot, m_fromTGStart, m_fromTGEnd); + } + return RESULT_UNMATCHED; } if (m_fromSlot != m_toSlot) data.setSlotNo(m_toSlot); - data.setDstId(m_toId); + if (m_fromTGStart != m_toIdStart) { + unsigned int newDstId = dstId + m_toIdStart - m_fromTGStart; + data.setDstId(newDstId); + } data.setFLCO(FLCO_USER_USER); processMessage(data); - if (trace) - LogDebug("Rule Trace,\tRewriteType %s Slot=%u Dst=TG%u: matched", m_name.c_str(), m_fromSlot, m_fromTG); + if (trace) { + if (m_fromTGStart == m_fromTGEnd) + LogDebug("Rule Trace,\tRewriteType from \"%s\" Slot=%u Dst=TG%u: not matched", m_name.c_str(), m_fromSlot, m_fromTGStart); + else + LogDebug("Rule Trace,\tRewriteType from \"%s\" Slot=%u Dst=TG%u-%u: not matched", m_name.c_str(), m_fromSlot, m_fromTGStart, m_fromTGEnd); + if (m_toIdStart == m_toIdEnd) + LogDebug("Rule Trace,\tRewriteType to \"\%s\" Slot=%u Dst=%u: matched", m_name.c_str(), m_toSlot, m_toIdStart); + else + LogDebug("Rule Trace,\tRewriteType to \"\%s\" Slot=%u Dst=%u-%u: matched", m_name.c_str(), m_toSlot, m_toIdStart, m_toIdEnd); + } - return true; + return RESULT_MATCHED; } diff --git a/RewriteType.h b/RewriteType.h index 2cb8834..07e7edd 100644 --- a/RewriteType.h +++ b/RewriteType.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2017 by Jonathan Naylor G4KLX +* Copyright (C) 2017,2020 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,17 +26,19 @@ class CRewriteType : public CRewrite { public: - CRewriteType(const std::string& name, unsigned int fromSlot, unsigned int fromTG, unsigned int toSlot, unsigned int toId); + CRewriteType(const std::string& name, unsigned int fromSlot, unsigned int fromTG, unsigned int toSlot, unsigned int toId, unsigned int range); virtual ~CRewriteType(); - virtual bool process(CDMRData& data, bool trace); + virtual PROCESS_RESULT process(CDMRData& data, bool trace); private: std::string m_name; unsigned int m_fromSlot; - unsigned int m_fromTG; + unsigned int m_fromTGStart; + unsigned int m_fromTGEnd; unsigned int m_toSlot; - unsigned int m_toId; + unsigned int m_toIdStart; + unsigned int m_toIdEnd; }; diff --git a/Version.h b/Version.h index bd50711..aa4c644 100644 --- a/Version.h +++ b/Version.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2019 by Jonathan Naylor G4KLX + * Copyright (C) 2015-2020 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,6 @@ #if !defined(VERSION_H) #define VERSION_H -const char* VERSION = "20190717"; +const char* VERSION = "20200421"; #endif diff --git a/XLXHosts.txt b/XLXHosts.txt index ed1db42..a8092d2 100644 --- a/XLXHosts.txt +++ b/XLXHosts.txt @@ -127,11 +127,12 @@ 302;144.217.241.23;4004 307;72.21.76.154;4004 310;52.11.207.121;4004 -311;46.41.0.214;4003 +311;91.204.44.37;4003 312;192.241.160.183;4004 313;34.213.108.164;4001 315;65.101.7.50;4004 317;44.48.8.15;4004 +320;91.204.44.27;4003 328;212.237.33.114;4004 329;114.181.139.32;4004 332;188.213.168.99;4004 diff --git a/Voice.cpp b/XLXVoice.cpp similarity index 88% rename from Voice.cpp rename to XLXVoice.cpp index 63b650c..bcc3edb 100644 --- a/Voice.cpp +++ b/XLXVoice.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2017 by Jonathan Naylor G4KLX +* Copyright (C) 2017,2020 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,8 +18,8 @@ #include "DMRSlotType.h" #include "DMRFullLC.h" +#include "XLXVoice.h" #include "DMREMB.h" -#include "Voice.h" #include "Sync.h" #include "Log.h" @@ -35,13 +35,13 @@ const unsigned char COLOR_CODE = 3U; const unsigned int SILENCE_LENGTH = 9U; const unsigned int AMBE_LENGTH = 9U; -CVoice::CVoice(const std::string& directory, const std::string& language, unsigned int id, unsigned int slot, unsigned int tg) : +CXLXVoice::CXLXVoice(const std::string& directory, const std::string& language, unsigned int id, unsigned int slot, unsigned int tg) : m_indxFile(), m_ambeFile(), m_slot(slot), m_lc(FLCO_GROUP, id, tg), m_embeddedLC(), -m_status(VS_NONE), +m_status(XLXVS_NONE), m_timer(1000U, 1U), m_stopWatch(), m_seqNo(0U), @@ -63,12 +63,12 @@ m_it() #endif } -CVoice::~CVoice() +CXLXVoice::~CXLXVoice() { for (std::vector::iterator it = m_data.begin(); it != m_data.end(); ++it) delete *it; - for (std::unordered_map::iterator it = m_positions.begin(); it != m_positions.end(); ++it) + for (std::unordered_map::iterator it = m_positions.begin(); it != m_positions.end(); ++it) delete it->second; m_data.clear(); @@ -77,7 +77,7 @@ CVoice::~CVoice() delete[] m_ambe; } -bool CVoice::open() +bool CXLXVoice::open() { FILE* fpindx = ::fopen(m_indxFile.c_str(), "rt"); if (fpindx == NULL) { @@ -115,7 +115,7 @@ bool CVoice::open() unsigned int start = ::atoi(p2) * AMBE_LENGTH; unsigned int length = ::atoi(p3) * AMBE_LENGTH; - CPositions* pos = new CPositions; + CXLXPositions* pos = new CXLXPositions; pos->m_start = start; pos->m_length = length; @@ -130,7 +130,7 @@ bool CVoice::open() return true; } -void CVoice::linkedTo(unsigned int number, unsigned int room) +void CXLXVoice::linkedTo(unsigned int number, unsigned int room) { char letters[10U]; ::sprintf(letters, "%03u", number); @@ -158,7 +158,7 @@ void CVoice::linkedTo(unsigned int number, unsigned int room) createVoice(words); } -void CVoice::unlinked() +void CXLXVoice::unlinked() { std::vector words; words.push_back("notlinked"); @@ -166,12 +166,12 @@ void CVoice::unlinked() createVoice(words); } -void CVoice::createVoice(const std::vector& words) +void CXLXVoice::createVoice(const std::vector& words) { unsigned int ambeLength = 0U; for (std::vector::const_iterator it = words.begin(); it != words.end(); ++it) { if (m_positions.count(*it) > 0U) { - CPositions* position = m_positions.at(*it); + CXLXPositions* position = m_positions.at(*it); ambeLength += position->m_length; } else { LogWarning("Unable to find character/phrase \"%s\" in the index", (*it).c_str()); @@ -199,7 +199,7 @@ void CVoice::createVoice(const std::vector& words) unsigned int pos = SILENCE_LENGTH * AMBE_LENGTH; for (std::vector::const_iterator it = words.begin(); it != words.end(); ++it) { if (m_positions.count(*it) > 0U) { - CPositions* position = m_positions.at(*it); + CXLXPositions* position = m_positions.at(*it); unsigned int start = position->m_start; unsigned int length = position->m_length; ::memcpy(ambeData + pos, m_ambe + start, length); @@ -269,13 +269,13 @@ void CVoice::createVoice(const std::vector& words) delete[] ambeData; - m_status = VS_WAITING; + m_status = XLXVS_WAITING; m_timer.start(); } -bool CVoice::read(CDMRData& data) +bool CXLXVoice::read(CDMRData& data) { - if (m_status != VS_SENDING) + if (m_status != XLXVS_SENDING) return false; unsigned int count = m_stopWatch.elapsed() / DMR_SLOT_TIME; @@ -291,7 +291,7 @@ bool CVoice::read(CDMRData& data) delete *it; m_data.clear(); m_timer.stop(); - m_status = VS_NONE; + m_status = XLXVS_NONE; } return true; @@ -300,20 +300,20 @@ bool CVoice::read(CDMRData& data) return false; } -void CVoice::clock(unsigned int ms) +void CXLXVoice::clock(unsigned int ms) { m_timer.clock(ms); if (m_timer.isRunning() && m_timer.hasExpired()) { - if (m_status == VS_WAITING) { + if (m_status == XLXVS_WAITING) { m_stopWatch.start(); - m_status = VS_SENDING; + m_status = XLXVS_SENDING; m_it = m_data.begin(); m_sent = 0U; } } } -void CVoice::createHeaderTerminator(unsigned char type) +void CXLXVoice::createHeaderTerminator(unsigned char type) { CDMRData* data = new CDMRData; diff --git a/Voice.h b/XLXVoice.h similarity index 80% rename from Voice.h rename to XLXVoice.h index c27aadb..186d3f6 100644 --- a/Voice.h +++ b/XLXVoice.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2017 by Jonathan Naylor G4KLX +* Copyright (C) 2017,2020 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,8 +16,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#if !defined(Voice_H) -#define Voice_H +#if !defined(XLXVoice_H) +#define XLXVoice_H #include "DMREmbeddedData.h" #include "StopWatch.h" @@ -29,21 +29,21 @@ #include #include -enum VOICE_STATUS { - VS_NONE, - VS_WAITING, - VS_SENDING +enum XLXVOICE_STATUS { + XLXVS_NONE, + XLXVS_WAITING, + XLXVS_SENDING }; -struct CPositions { +struct CXLXPositions { unsigned int m_start; unsigned int m_length; }; -class CVoice { +class CXLXVoice { public: - CVoice(const std::string& directory, const std::string& language, unsigned int id, unsigned int slot, unsigned int tg); - ~CVoice(); + CXLXVoice(const std::string& directory, const std::string& language, unsigned int id, unsigned int slot, unsigned int tg); + ~CXLXVoice(); bool open(); @@ -60,14 +60,14 @@ private: unsigned int m_slot; CDMRLC m_lc; CDMREmbeddedData m_embeddedLC; - VOICE_STATUS m_status; + XLXVOICE_STATUS m_status; CTimer m_timer; CStopWatch m_stopWatch; unsigned int m_seqNo; unsigned int m_streamId; unsigned int m_sent; unsigned char* m_ambe; - std::unordered_map m_positions; + std::unordered_map m_positions; std::vector m_data; std::vector::const_iterator m_it; diff --git a/scripts/install-linker.sh b/scripts/install-linker.sh new file mode 100644 index 0000000..636d7a4 --- /dev/null +++ b/scripts/install-linker.sh @@ -0,0 +1,21 @@ +#!/bin/bash + + + + +cd / +rm -rf DMRGa* +cd /opt +rm -rf script +cd /var/log +rm -rf script + +cd /boot +cp DMRGateway*.tgz / +cd / +tar -xvf DMRGateway*.tgz +systemctl daemon-reload +echo -n "" > /var/log/script/static-slot1.txt +echo -n "" > /var/log/script/static-slot2.txt +echo -n "" > /var/log/script/slot1.txt +echo -n "" > /var/log/script/slot2.txt diff --git a/scripts/lib/systemd/system/relinker.service b/scripts/lib/systemd/system/relinker.service new file mode 100644 index 0000000..69cb9a4 --- /dev/null +++ b/scripts/lib/systemd/system/relinker.service @@ -0,0 +1,14 @@ +[Unit] +Description=relinker service +After=multi-user.target + +[Service] +Type=idle + +ExecStart=/bin/bash /opt/script/relink-repeater.sh + + + + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/scripts/lib/systemd/system/relinkerhot.service b/scripts/lib/systemd/system/relinkerhot.service new file mode 100644 index 0000000..4331bf1 --- /dev/null +++ b/scripts/lib/systemd/system/relinkerhot.service @@ -0,0 +1,12 @@ +[Unit] +Description=relinkerhot service +After=multi-user.target + +[Service] +Type=idle + +ExecStart=/bin/bash /opt/script/relink-hotspot.sh + + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/scripts/opt/script/README b/scripts/opt/script/README new file mode 100644 index 0000000..b0e5eb2 --- /dev/null +++ b/scripts/opt/script/README @@ -0,0 +1,213 @@ +DMR SUDO Reflector - TG + + + + + +This is the first release version of the relinker scripts 3.2 + + + + + + +These scripts are supplied as is and work in conjuction with DMRGateway Mid April 2020 Onwards .....If you don't understand the notes ask someone who does please + +Tested on rasp pi running MMDVMHost and DMRGateway Using SUDO-Reflector-TalkGroups + + +These scripts provide a means to setup via the config file default TG's for each slot on a repeater. These should be also setup as only Static TG setup via Brandmeister Selfcare, so that when +a slot is commanded to another dynamic TG by a user and then after use they forget to unlink. The BM Dynamic TG timer which is monitored by this script (expires 10 minutes after inactivity) +the slot will be returned to the configured Static default TG. + +Both slots can be monitored together and as they maybe taken off to other dynamics at the same time or individually it is worth getting users in the habit of sending a PC 4000 once they finish +using a different TG as it ensures the repeater when not in use is in the correct place for most of the time. + + +A default TG of choice which can be configured linking provided on on both slots. + + + +Installation + +Firstly DMRGateway should have the following lines minimum configuring if running both timeslots for SUDO Reflector on a repeater + + +# Dynamic rewriting of slot 2 TGs 90-999999 to TG9 to emulate reflector behaviour Private call manual dial commands PC 2351 for SUDO REF TG 2351 PC 4000 unlink PC 5000 Status PC 9990 echo BM + + +TGDynRewrite0=1,90,4000,5000,9,999910,9990 +TGDynRewrite1=2,90,4000,5000,9,999910,9990 + +Check you also have the following section added in your DMRGateway config file its usually at the bottom + +[Dynamic TG Control] +Enabled=1 +Port=3769 + + + + +1. Copy the package DMRGateway-TGDynRewrite-relinker-3.2.tgz to the folder/boot and also the install-relinker.sh script supplied (you can do this with a card reader from windows if you like) + + +2. run /boot#./install-relinker which should create folders /opt/script/script and /var/log/script/various working files and logfile. it also sets up services in systemd + +ready to run the script required + + + +3. Edit the config file /opt/script/script.conf repeater=235??? ID call callsign your defaults for slot linking + +also min relink timer minrelinktimer in seconds this is the time before reconnection on a slot after someone finishes RF and either the slot is unlinked by the user + +or the dynamic times out i.e BM timer + +it waits a further minlinktimer seconds before relinking + +slowdown=10 This is higher for faster rasp pi 10 for pi 3B + or 1 for original Pi B + +checkslot1=check_status_slot1 +checkslot2=check_status_slot2 +slot1notlinked=check_if_nothing_linked_slot1 +slot2notlinked=check_if_nothing_linked_slot2 + +Above checks all slots default for repeater with prefered config SUDO Reflectors both slots ............if in doubt include all these lines !!!! + + +checkslot1= +checkslot2=check_status_slot2 +slot1notlinked= +slot2notlinked=check_if_nothing_linked_slot2 + +Above checks only slot 2 typically a hotspot........ or a repeater that only has SUDO Reflectors enabled in DMRGateway on one of the slots + + +............if in doubt include all these lines in first example!!!! + + + + +4. Don't forget to set these static via Brandmeister selfcare + + + +5. Add the following line to crontab on the rasp pi you dont need to do anything else just save crontab and exit + +17 * * * * root echo -n "" > /var/log/script/checkBMAPI.log + + + + + + +6. Pi-star additional step + + +Also in same file etc/rc.local + +look for + +if [ ! -d /var/log/unattended-upgrades ]; then + mkdir -p /var/log/unattended-upgrades + + + + +fi + +add only the below lines just below the above block + +if [ ! -d /var/log/script ]; then + /opt/script/./start.sh +fi + + +7. start one of the services with following command + + +choose either Repeater + +systemctl start relinker.service + + + +or Hotspot ? + +systemctl start relinkerhot.service + + +systemctl status relinker.service + +or + +systemctl status relinkerhot.service + + +will show if its running if it is running ? + + +you can tail the log file at the command prompt + + +tail -f /var/log/script/checkBMAPI.log + + +once it is confirmed running and working + + +enable the service you chose to enable automatically at boot example + +systemctl enable relinker.service + +for repeater + +or + +systemctl enable relinkerhot.service + +for hotspot + + +reboot + + +once you have rebooted check all is auto starting + + +you can tail the log file at the command prompt + + +tail -f /var/log/script/checkBMAPI.log + + + + + + + + + +Have fun + +73, + +Jon G4TSN + +jon@g4tsn.com + + + + + + + + + + + + + + + + diff --git a/scripts/opt/script/relink-hotspot.sh b/scripts/opt/script/relink-hotspot.sh new file mode 100644 index 0000000..c175e9a --- /dev/null +++ b/scripts/opt/script/relink-hotspot.sh @@ -0,0 +1,142 @@ +#!/bin/bash + + +source /opt/script/script.conf + +exec 3>&1 4>&2 +trap 'exec 2>&4 1>&3' 0 1 2 3 +exec 1>/var/log/script/checkBMAPI.log 2>&1 + + + + + + +#check BM API + +check_BM_API(){ + + echo "Repeater $call $repeater" + + date + + curl "https://api.brandmeister.network/v1.0/repeater/?action=profile&q=$repeater" > /var/log/script/test1.txt + + sed -n -e 's/^.*tarantool"}],//p' /var/log/script/test1.txt > /var/log/script/test2.txt + + sed 's/}],"timedSubscriptions.*//' /var/log/script/test2.txt > /var/log/script/test3.txt + + sed -n -e 's/^.*slot":1,"timeout"://p' /var/log/script/test3.txt > /var/log/script/slot1.txt + + + + #sed -n -e 's/^.*slot":2,"timeout"://p' /var/log/script/test3.txt > /var/log/script/test4.txt + + sed -n -e 's/^.*slot":0,"timeout"://p' /var/log/script/test3.txt > /var/log/script/test4.txt + + + sed 's/},{"repeaterid.*//' /var/log/script/test4.txt > /var/log/script/slot2.txt + +} + + +check_status_slot1(){ + + if [ -s "/var/log/script/slot1.txt" ] + then + echo "Dynamic is linked slot 1." + echo -n "" > /var/log/script/static-slot1.txt + else + echo + #echo "Dynamic is not linked slot 1."./check + fi + if [ -s "/var/log/script/static-slot1.txt" ] + then + echo "Static is linked slot 1." + echo + else + #echo "Static is not linked slot 1." + echo + + fi +} + +check_status_slot2(){ + + if [ -s "/var/log/script/slot2.txt" ] + then + echo "Dynamic is linked slot 2." + echo -n "" > /var/log/script/static-slot2.txt + else + echo + #echo "Dynamic is not linked slot 2." + fi + if [ -s "/var/log/script/static-slot2.txt" ] + then + echo "Static is linked slot 2." + echo + else + #echo "Static is not linked slot 2." + echo + + fi +} + + +#check if static-slot1.txt and slot.txt are blank + +check_if_nothing_linked_slot1(){ + + if [ -s "/var/log/script/static-slot1.txt" ] + + then + echo "Linked to Static $defaultslot1 Slot 1" + + elif [ -s "/var/log/script/slot1.txt" ] + + then + echo "Dynamic is Linked slot 1" + else + echo "DMRGateway slot 1 is Unlinked" + echo "Linking slot 1 $defaultslot1" && sleep $minrelinktimerslot1; echo "DynTG1,$defaultslot1" > /dev/udp/127.0.0.1/3769 + echo -n "Linked to $defaultslot1" > /var/log/script/static-slot1.txt + + + fi + +} + + + +#check if static-slot2.txt and slot.txt are blank + +check_if_nothing_linked_slot2(){ + + if [ -s "/var/log/script/static-slot2.txt" ] + + then + echo "Linked to Static $defaultslot2 Slot 2" + + elif [ -s "/var/log/script/slot2.txt" ] + + then + echo "Dynamic is Linked slot 2" + else + echo "DMRGateway slot 2 is Unlinked" + echo "Linking slot 2 $defaultslot2" && sleep $minrelinktimerslot2; echo "DynTG2,$defaultslot2" > /dev/udp/127.0.0.1/3769 + echo -n "Linked to $defaultslot2" > /var/log/script/static-slot2.txt + fi + +} + +while : +do +check_BM_API +echo +$checkslot1 +$checkslot2 +$slot1notlinked +$slot2notlinked +echo +sleep $slowdown +done \ No newline at end of file diff --git a/scripts/opt/script/relink-repeater.sh b/scripts/opt/script/relink-repeater.sh new file mode 100644 index 0000000..6700195 --- /dev/null +++ b/scripts/opt/script/relink-repeater.sh @@ -0,0 +1,143 @@ +#!/bin/bash + + +source /opt/script/script.conf + +exec 3>&1 4>&2 +trap 'exec 2>&4 1>&3' 0 1 2 3 +exec 1>/var/log/script/checkBMAPI.log 2>&1 + + + + + +#check BM API + +check_BM_API(){ + + echo "Repeater $call $repeater" + + date + + curl "https://api.brandmeister.network/v1.0/repeater/?action=profile&q=$repeater" > /var/log/script/test1.txt + + sed -n -e 's/^.*tarantool"}],//p' /var/log/script/test1.txt > /var/log/script/test2.txt + + sed 's/}],"timedSubscriptions.*//' /var/log/script/test2.txt > /var/log/script/test3.txt + + sed -n -e 's/^.*slot":1,"timeout"://p' /var/log/script/test3.txt > /var/log/script/slot1.txt + + + + sed -n -e 's/^.*slot":2,"timeout"://p' /var/log/script/test3.txt > /var/log/script/test4.txt + + #sed -n -e 's/^.*slot":0,"timeout"://p' /Var/log/script/test3.txt > /var/log/script/test4.txt + + + sed 's/},{"repeaterid.*//' /var/log/script/test4.txt > /var/log/script/slot2.txt + +} + + +check_status_slot1(){ + + if [ -s "/var/log/script/slot1.txt" ] + then + echo "Dynamic is linked slot 1." + echo -n "" > /var/log/script/static-slot1.txt + else + echo + #echo "Dynamic is not linked slot 1."./check + fi + if [ -s "/var/log/script/static-slot1.txt" ] + then + echo "Static is linked slot 1." + echo + else + #echo "Static is not linked slot 1." + echo + + fi +} + +check_status_slot2(){ + + if [ -s "/var/log/script/slot2.txt" ] + then + echo "Dynamic is linked slot 2." + echo -n "" > /var/log/script/static-slot2.txt + else + echo + #echo "Dynamic is not linked slot 2." + fi + if [ -s "/var/log/script/static-slot2.txt" ] + then + echo "Static is linked slot 2." + echo + else + #echo "Static is not linked slot 2." + echo + + fi +} + + +#check if static-slot1.txt and slot.txt are blank + +check_if_nothing_linked_slot1(){ + + if [ -s "/var/log/script/static-slot1.txt" ] + + then + echo "Linked to Static $defaultslot1 Slot 1" + + elif [ -s "/var/log/script/slot1.txt" ] + + then + echo "Dynamic is Linked slot 1" + else + echo "DMRGateway slot 1 is Unlinked" + echo "Linking slot 1 $defaultslot1" && sleep $minrelinktimerslot1; echo "DynTG1,$defaultslot1" > /dev/udp/127.0.0.1/3769 + echo -n "Linked to $defaultslot1" > /var/log/script/static-slot1.txt + + + fi + +} + + + +#check if static-slot2.txt and slot.txt are blank + +check_if_nothing_linked_slot2(){ + + if [ -s "/var/log/script/static-slot2.txt" ] + + then + echo "Linked to Static $defaultslot2 Slot 2" + + elif [ -s "/var/log/script/slot2.txt" ] + + then + echo "Dynamic is Linked slot 2" + else + echo "DMRGateway slot 2 is Unlinked" + echo "Linking slot 2 $defaultslot2" && sleep $minrelinktimerslot2; echo "DynTG2,$defaultslot2" > /dev/udp/127.0.0.1/3769 + echo -n "Linked to $defaultslot2" > /var/log/script/static-slot2.txt + fi + +} + + + +while : +do +check_BM_API +echo +$checkslot1 +$checkslot2 +$slot1notlinked +$slot2notlinked +echo +sleep $slowdown +done diff --git a/scripts/opt/script/script.conf b/scripts/opt/script/script.conf new file mode 100644 index 0000000..8736739 --- /dev/null +++ b/scripts/opt/script/script.conf @@ -0,0 +1,20 @@ +#!/usr/bin/bash +repeater=235192 +call=GB3IN +defaultslot1=23590 +defaultslot2=91 +minrelinktimerslot1=30 +minrelinktimerslot2=30 +slowdown=0 +checkslot1=check_status_slot1 +checkslot2=check_status_slot2 +slot1notlinked=check_if_nothing_linked_slot1 +slot2notlinked=check_if_nothing_linked_slot2 + +#configuration above enabled to watch slot 2 only on a hotspot + +# add the lines as required you may only have 1 slot monitored on a repeater if thats + +# all using SUDO Reflectors Cant imagine why + + \ No newline at end of file diff --git a/scripts/opt/script/start.sh b/scripts/opt/script/start.sh new file mode 100644 index 0000000..5caca19 --- /dev/null +++ b/scripts/opt/script/start.sh @@ -0,0 +1,15 @@ +#!/bin/bash +mkdir /var/log/script +sleep 1 +cp /opt/script/var-log-script.tgz /var/log/script/ +sleep 1 +cd /var/log/script +sleep 1 +tar -xvf var-log-script.tgz +echo -n "" > /var/log/script/static-slot1.txt +echo -n "" > /var/log/script/static-slot2.txt +echo -n "" > /var/log/script/slot1.txt +echo -n "" > /var/log/script/slot2.txt + + + diff --git a/scripts/opt/script/var-log-script.tgz b/scripts/opt/script/var-log-script.tgz new file mode 100644 index 0000000..cac2dfb Binary files /dev/null and b/scripts/opt/script/var-log-script.tgz differ diff --git a/scripts/var/log/script/slot1.txt b/scripts/var/log/script/slot1.txt new file mode 100644 index 0000000..e69de29 diff --git a/scripts/var/log/script/slot2.txt b/scripts/var/log/script/slot2.txt new file mode 100644 index 0000000..e69de29 diff --git a/scripts/var/log/script/static-slot1.txt b/scripts/var/log/script/static-slot1.txt new file mode 100644 index 0000000..ddfdd29 --- /dev/null +++ b/scripts/var/log/script/static-slot1.txt @@ -0,0 +1 @@ +Linked to 23590 \ No newline at end of file diff --git a/scripts/var/log/script/static-slot2.txt b/scripts/var/log/script/static-slot2.txt new file mode 100644 index 0000000..2524e32 --- /dev/null +++ b/scripts/var/log/script/static-slot2.txt @@ -0,0 +1 @@ +Linked to 91 \ No newline at end of file diff --git a/scripts/var/log/script/test1.txt b/scripts/var/log/script/test1.txt new file mode 100644 index 0000000..e69de29 diff --git a/scripts/var/log/script/test2.txt b/scripts/var/log/script/test2.txt new file mode 100644 index 0000000..4a2dc73 --- /dev/null +++ b/scripts/var/log/script/test2.txt @@ -0,0 +1 @@ +"dynamicSubscriptions":[],"timedSubscriptions":[],"clusters":[]} \ No newline at end of file diff --git a/scripts/var/log/script/test3.txt b/scripts/var/log/script/test3.txt new file mode 100644 index 0000000..4a2dc73 --- /dev/null +++ b/scripts/var/log/script/test3.txt @@ -0,0 +1 @@ +"dynamicSubscriptions":[],"timedSubscriptions":[],"clusters":[]} \ No newline at end of file diff --git a/scripts/var/log/script/test4.txt b/scripts/var/log/script/test4.txt new file mode 100644 index 0000000..e69de29