Resolve conflicts

pull/8/head
Doug McLain 3 years ago
parent 0d29254024
commit 5ed10a3633

@ -2,43 +2,25 @@
The URF Multiprotocol Gateway Reflector Server, ***urfd***, is part of the software system for a Digital Voice Network. The sources are published under GPL Licenses.
# Information about this fork
This fork of URFD supports *all* modes currently used in ham radio: D-Star, DMR, YSF, P25, NXDN, M17, and USRP (for connections to AllStar nodes, etc). All transcoding is centralized so there is no double transcoding to/from any mode. This fork of urfd, along with the swambe2 branch of my tcd repo, contains many changes from the original:
Integraded P25 Reflector with software imbe vocoder.
Integrated NXDN Reflector
Inegrated USRP Reflector
Optional software vocoding of AMBE+2(DMR/YSF/NXDN) can be done using md380_vocoder library. This means that only 1 USB dv dongle is required per module. This also makes an ARM platform (like Rpi) a reqirement. See the tcd README for details.
Numerous fixes like late entry recognition from modes like YSF that are otherwise ignored by the original reflector when no header has been received.
The USRP Clients are read from a file defined in Main.h. The format of this file is ipaddr;port;callsign; one host per line, ex:
```bash
192.168.1.100;32000;CALLSIGN1;
192.168.1.101;32001;CALLSIGN2;
```
The rest of this README is unchanged from the original.
## Introduction
This will build a new kind of digital voice reflector. Based on N7TAE's [new-xlxd](https://github.com/n7tae/new-xlxd), which, in turn, is based on the first multi-protocol reflector, [xlxd](https://github.com/LX3JL/xlxd), **urfd** supports all protocols of it's predecessors, as well as both M17 protocols, **voice-only** and **voice+data**! A key part of this is the hybrid transcoder, [tcd](https://github.com/n7tae/tcd), which is in a seperate repository. URFd is not compatible with either new-xlxd or xlxd. You can't interlink urfd with xlxd. This reflector can be built without a transcoder, but clients will only hear other clients using the same protocol. Please note that currently, urfd only supports the tcd transcoder when run locally. For best performance, urfd and tcd uses UNIX DGRAM sockets for interprocess communications. These kernal-base sockets are signifantly faster than conventional UDP/IP sockets. It should be noted that tcd supports DVSI-3003 nad DVSI-3000 devices, which it uses for AMBE vocoding.
This will build a new kind of digital voice reflector. A *urfd* supports DStar protocols (DPlus, DCS, DExtra and G3) DMR protocols (MMDVMHost, DMR+ and NXDN), M17, YSF, P25 (using IMBE) and USRP (Allstar). A key part of this is the hybrid transcoder, [tcd](https://github.com/n7tae/tcd), which is in a seperate repository. You can't interlink urfd with xlxd. This reflector can be built without a transcoder, but clients will only hear other clients using the same codec. Please note that currently, urfd only supports the tcd transcoder when run locally. As a local device, urfd and tcd uses UNIX DGRAM sockets for interprocess communications. These kernel-base sockets are signifantly faster than conventional UDP/IP sockets. It should be noted that tcd supports DVSI-3003 nad DVSI-3000 devices, which it uses for AMBE vocoding.
This build support *dual-stack* operation, so the server on which it's running, must have both an IPv4 and IPv6 routable address if you are going to configure a dual-stack reflector.
This build support *dual-stack* operation, so the server on which it's running, must have both an IPv4 and IPv6 routable address if you are going to configure a dual-stack reflector. URF can support out-going DExtra links, by adding a new DExtra Peer type *and* it has many changes designed to increase reliability and stability.
There are many improvements previous multi-mode reflectors:
There are many improvements of urfd over xlxd, some of which were inherited from new-xlxd:
- Nearly all std::vector containers have been replaced with more appropriate containers.
- No classes are derived from any standard containers.
- For concurancy, *i.e.*, thread management, the standard thread (std::thread) library calls have been replaced with std::future.
- Managed memory, std::unique_ptr and std::shared_ptr, is used replacing the need for calls to *new* and *delete*.
- Your reflector can be configured with up to 26 modules, *A* through *Z* and as few as one module. For other choices, the configure modules don't have to be contigious. For example, you could configure modules A, B, C and E.
- An integraded P25 Reflector with software imbe vocoder.
- An integrated NXDN Reflector
- An inegrated USRP Reflector
Only systemd-based operating systems are supported. Debian or Ubuntu is recommended. If you want to install this on a non-systemd based OS, you are on your own. Also, by default, tcd and urfd are built without gdb support. You can add debugging support in the configuration script, `./rconfig`. Finally, this repository is designed so that you don't have to modify any file in the repository when you build your system. Any file you need to modify to properly configure your reflector will be a file you copy from you locally cloned repo. This makes it easier to update the source code when this repository is updated. Follow the instructions below to build your transcoding URF reflector.
Only systemd-based operating systems are supported. Debian or Ubuntu is recommended. If you want to install this on a non-systemd based OS, you are on your own. Finally, this repository is designed so that you don't have to modify any file in the repository when you build your system. Any file you need to modify to properly configure your reflector will be a file you copy from you locally cloned repo. This makes it easier to update the source code when this repository is updated. Follow the instructions below to build your transcoding URF reflector.
## Usage
@ -66,47 +48,35 @@ sudo apt upgrade
sudo apt install git
sudo apt install apache2 php5
sudo apt install build-essential
sudo apt install libmariadb-dev-compat
sudo apt install nlohmann-json3-dev
```
### YSF direct connection support
The following is needed if you plan on supporting local YSF frequency registration database for those YSF-clients that want to directly connect to URF. You will also need to install the client frequency registration pages on your web server. This is because the WiresX protocol supplies the operational frequency to connecting clients.
```bash
sudo apt install php-mysql mariadb-server mariadb-client
```
### Download the repository(s)
### Download and build the repository and
```bash
git clone https://github.com/n7tae/urfd.git
cd urfd/reflector
```
And, if needed, the hybrid transcoder:
```bash
git clone https://github.com/n7tae/tcd.git
```
### Create and edit your blacklist, whitelist and linking files
### Create and edit your configuration files
First, move to the reflector build directory:
First, move to the reflector build directory and create your configuration file:
```bash
cd urfd/reflector
cp ../config/* .
```
The blacklist file defines callsigns that are blocked from linking or transmitting. The whitelist file defines callsigns that are allowed to link and transmit. Both of these files support the astrisk as a wild-card. The supplied blacklist file is empty and the supplied whitelist file contains a single definition, \*, which will allow any callsign to link and transmit, blocking no one. The interlink file defines possible Brandmeister, XRF and URF linking. The terminal file defines operations for Icom's Terminal and Access Point mode, sometimes called *G3*. This protocol requires significantly higher connection resources than any other mode, so it is possible to build a URF reflector without G3 support.
This will create seven files:
1. The `urfd.mk` file contains compile-time options for *urfd*. If you change the `BINDIR`, you'll need to update how `urfd.service` starts *urfd*.
2. The `urfd.ini` file contains the run-time options for *urfd* and will be discussed below.
3. The `urfd.blacklist` file defines callsigns that are blocked from linking or transmitting.
4. The `urfd.whitelist` file defines callsigns that are allowed to link and transmit. Both of these files support the astrisk as a wild-card. The supplied blacklist and whitelist file are empty, which will allow any callsign to link and transmit, blocking no one. Both files support a limited wildcard feature.
5. The `urfd.interlink` file defines possible Brandmeister and URF linking.
6. The `urfd.terminal` file defines operations for Icom's Terminal and Access Point mode, sometimes called *G3*. This protocol requires significantly higher connection resources than any other mode, so it is possible to build a URF reflector without G3 support.
7. The `urfd.service` file is a systemd file that will start and stop *urfd*. Importantly, it contains the only reference to where the *urfd* ini file is located. Be sure to set a fully qualified path to your urfd.ini file on the `ExecStart` line.
```bash
cp ../config/urfd.blacklist .
cp ../config/urfd.whitelist .
cp ../config/urfd.interlink .
cp ../config/urfd.terminal .
```
You can acutally put the blacklist, whitelist, interlink, terminal and ini file anyplace and even rename them. Just make sure your ini file and service file have the proper, fully-qualified paths. The service file and the mk file need to remain in your `urfd/reflector` directory.
If you are not going to support G3 linking, you don't need to copy the .terminal file. Use your favorite editor to modify each of these files. If you want a totally open network, the blacklist and whitelist files are ready to go. The blacklist determine which callsigns can't use the reflector. The whitelist determines which callsigns can use the reflector. The interlink file sets up the URF<--->URF inter-linking and/or out-going XRF peer linking.
When you are done with the configuration files and ready to start the installation process, you can return to the main repository directory:
@ -114,31 +84,61 @@ When you are done with the configuration files and ready to start the installati
cd ..
```
### Build *urfd*
After possibly editing `urfd.mk`, you can build your reflector: `make` . Besides building *urfd*, this will also build two helper applications that will be discussed below.
### Configuring your reflector
Configuring, compiling and maintaining your reflector build is easy! Start the configuration script in the base directory of you cloned repo:
Use your favorite text editor to set your run-time configuration in your copy of `urfd.ini`.
There are only a few things that need to be specified. Most important are, the reflector callsign and the IP addresses for the IPv4 and IPv6 listen ports and a transcoder port, if there is a transcoder. Dual-stack operation is enabled by specifying both an IPv4 and IPv6 address. IPv4-only single stack can be specified by leaving the IPv6 address undefined.
You can configure any modules, from **A** to **Z**. They don't have to be contigious. If your reflector is configured with a transcoder, you can specify which configured modules will be transcoded. Up to three modules can be transcoded if you have the necessary hardware.
Three protocols, BrandMeister, G3 and USRP should be disabled if you aren't going to use them.
There are three databases needed by *urfd*:
1. The *DMR ID* database maps a DMR ID to a callsign and *vis versa*.
2. The *NXDN ID* database maps an NXDN ID to a callsign and *vis versa*.
3. The *YSF Tx/Rx* database maps a callsign to a transmit/receive RF frequencies.
These databases can come from a URL or a file, or both. If you specify "both", then the file will be read after the URL.
#### Special *USRP* configuration
If configured, a *USRP* client is very unique. A *USRP* client (an AllStar node) doesn't support any connect or disconnect protocol. The USRP client simply sends and receives *USRP* voice packets. That means, if *USRP* is enabled, the client is created during initialization using the configured callsign, IP address and Tx/Rx ports.
If `FilePath` is defined, this should point to a text file listing special, listen-only client(s), one per line. Each line defining a read-only client contains an IP address, a port number, and a callsign. Here is an example:
```bash
./rconfig
1.2.3.1;34001;ALLSTR1;
1.2.3.4;34004;ALLSTR4;
```
There are only a few things that need to be specified. Most important are, the reflector callsign and the IP addresses for the IPv4 and IPv6 listen ports and a transcoder port, if there is a transcoder. Dual-stack operation is enabled by specifying both an IPv4 and IPv6 address. IPv4-only single stack can be specified by leaving the IPv6 address set to `none`. It's even possible to operate in an IPv6-only configuration by leaving the IPv4 address to the default `none`. For most users, you can define the IP addresses as "any", but you can specify specific IPv4 and IPv6 addresses, if this is required for you installation site.
If you want to create listen-only clients, but you don't need a configured read/write client, then set its `Callsign` to `NONE`.
### Helper apps
You can configure any modules, from **A** to **Z**. They don't have to be contigious. If your reflector is configured with a transcoder, you can specify which configured modules will be transcoded. Up to three modules can be transcoded. There are also true/false flags to prevent G3 support and so that you can build executables that will support gdb debugging.
There are two, very useful helper applications, *inicheck* and *dbutil*. Both apps will show you a usage message if you execuate them without any arguments.
You can support your own YSF frequency database. This is very useful for hot-spots that use YSF linking. These linked hot-spots can then use the *WiresX* command on their radios to be able to connect to any configured URF module. Users can register their TX and RX frequency (typically the same for most hot-spot configurations) on http:<*urf url*>/wiresx/login.php. Once their hot-spot is registered, URF will return the correct frequency for their hot-spot when a *WiresX* command is sent to the reflector. You'll need to enable YSF auto-linking, specify a default module and define a database name, user and user password. When you write you URF configuration, a database **configure.sql** script will be built to not only create the database and database user, but also the table for the hot-spot frequency data.
The *inicheck* app will use the exact same code that urfd uses to validate your `urfd.ini` file. Do `./inicheck -q mrefd.ini` to check your infile for errors. If you see any messages containing `ERROR`, that means that *urfd* won't start. You'll have to fix the errors described in the message(s). If you only see messages containing `WARNING`, *urfd* will start, but it may not perform as expected. You will have to decide if the warning should be fixed. If you don't see any messages, it means that your ini file is syntactly correct.
Be sure to write out the configuration files and look over the up to seven different configration files that are created. The first file, reflector.cfg is the memory file for rconfig so that if you start that script again, it will remember how you left things. There are one or two `.h` files for the reflector and tcd and there are one or two `.mk` files for the reflector and tcd makefiles. You should **not** modify these files by hand unless you really know exactly how they work. The rconfig script will not start if it detects that an URF server is already running. You can override this behavior in expert mode: `./rconfig expert`. If you do change the configuration after you have already compiled the code, it is safest if you clean the repo and then recompile.
The *dbutil* app can be used for serveral tasks relating to the three databases that *urfd* uses. The usage is: `./dbutil DATABASE SOURCE ACTION INIFILE`, where:
- DATABASE is "dmr", "nxdn" or "ysf"
- SOURCE is "html" or "file"
- ACTION is "parse" or "errors"
- INIFLILE is the path to the infile that defines the location of the http and file sources for these three databases.
One at a time, *dbutil* can work with any of the three DATABASEs. It can read either the http or the file SOURCE. It can either show you the data entries that are syntactically correct or incorrect (ACTION).
### Compling and installing your system
### Installing your system
After you have written your configutation files, you can build and install your system:
After you have written your configutation files, you can install your system:
```bash
./radmin
```
Use this command to compile and install your system. It can also be used to uninstall your system. It will use the information in reflector.cfg to perform each task. This radmin menu can also perform other tasks like restarting the reflector or transcoder process. It can also be used to update the software, if the system is uninstalled.
You can use this interactive shell script to install and uninstall your system. This can also perform other tasks like restarting the reflector or transcoder process, or be used to view the reflector or transcoder log in real time.
### Stoping and starting the services manually
@ -163,39 +163,26 @@ Please note that your www root directory might be some place else. There is one
**DO NOT** enable the "calling home" feature unless you are sure that you will not be infringing on an existing XLX or XRF reflector with the same callsign suffix. If you don't understand what this means, don't set `$CallingHome['Active']` to true!
If you have configured support of hot-spot frequency registation, recursively copy the **wiresx** directory where the index.php file is for your dashboard. Also from the build directory, create the database and database user and hot-spot frequency table:
```bash
sudo mysql < configure.sql
```
The configure.sql file will be generated automatically by the rconfig script **if** you have enabled the **YSF Local Database**.
## Updating urfd and tcd
Updating can be performed entirely in the radmin script, but just in case there is a new version of the radmin script, you can start first with a simple `git pull`. If any .h or .cpp fiiles have updates, you can then start radmin and do a clean and compile and then uninstall and install: `cl, co, us, is`. Follow that with a `rl` to watch the reflector log, or an `rt` to watch the transcoder while it comes up.
If rconfig was updated with the `git pull`, it might be wise to run it first to see if there have been any new options added to the code base. If so, be sure to write out the new configuration files before exiting rconfig. THen you can rebuild and reinstall your reflector.
If you change any configuration after your reflector has been compiled, be sure to do a clean/compile/uninstall/reinstall to sync your system to the new configuration.
## Firewall settings
URF Server requires the following ports to be open and forwarded properly for in- and outgoing network traffic:
URF Server requires the following ports to be open and forwarded properly for in- and outgoing network traffic. Obviously you don't need to open ports for G3, USRP and BrandMeister if they are not enabled:
```text
TCP port 80 (http) optional TCP port 443 (https)
UDP port 8880 (DMR+ DMO mode)
UDP port 10002 (BM connection)
UDP port 10017 (URF interlinking)
UDP port 42000 (YSF protocol)
UDP port 12345 - 12346 (G3 Icom Terminal presence and request port)
UDP port 17000 (M17 protocol)
UDP port 30001 (DExtra protocol)
UPD port 20001 (DPlus protocol)
UDP port 30001 (DExtra protocol)
UDP port 30051 (DCS protocol)
UDP port 8880 (DMR+ DMO mode)
UDP port 32000 (USRP protocol)
UDP port 40000 (G3 Icom Terminal port)
UDP port 41000 (P25 port)
UDP port 41400 (NXDN port)
UDP port 42000 (YSF protocol)
UDP port 62030 (MMDVM protocol)
UDP port 12345 - 12346 (Icom Terminal presence and request port)
UDP port 40000 (Icom Terminal dv port)
```
## YSF Master Server
@ -205,11 +192,11 @@ It has nothing to do with the regular YSFReflector network, hence you dont ne
## To-dos
I will eventually support a remote transcoder option, so that you can, for example, run urfd in a data center, and then run the transcoder somewhere you have physical access to it so you can plug in your AMBE vocoders. I don't recommend this as it will add unnessary and variable latency to your reflector.
I will eventually support a remote transcoder option, so that you can, for example, run *urfd* in a data center, and then run the transcoder somewhere you have physical access so you can plug in your AMBE vocoders. I don't recommend this as it will add unnessary and variable latency to your reflector.
The M17 team will be working on big changes for the dashboard. I can't wait to see what they come up with!
A new dashboard is on the to-do list!
## Copyright
- Copyright © 2016 Jean-Luc Deltombe LX3JL and Luc Engelmann LX1IQ
- Copyright © 2021 Thomas A. Early N7TAE
- Copyright © 2022 Doug McLain AD8DP and Thomas A. Early N7TAE

@ -16,13 +16,10 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Main.h"
#include <string.h>
#include <cctype>
#include "DMRIdDirFile.h"
#include "DMRIdDirHttp.h"
#include "NXDNIdDirFile.h"
#include "NXDNIdDirHttp.h"
#include "Global.h"
#include "Callsign.h"
// if a client is using special characters '.', '-' or '/', he's out of luck!
@ -34,98 +31,104 @@
CCallsign::CCallsign()
{
// blank all
memset(m_Callsign, ' ', CALLSIGN_LEN);
memset(m_Suffix, ' ', CALLSUFFIX_LEN);
m_Callsign.l = 0x2020202020202020ul;
m_Suffix.u = 0x20202020u;
m_Module = ' ';
m_uiDmrid = 0;
m_coded = 0;
}
CCallsign::CCallsign(const char *sz, uint32_t dmrid, uint16_t nxdnid)
CCallsign::CCallsign(const std::string &cs, uint32_t dmrid, uint16_t nxdnid) : CCallsign()
{
// blank all
memset(m_Callsign, ' ', CALLSIGN_LEN);
memset(m_Suffix, ' ', CALLSUFFIX_LEN);
m_Module = ' ';
// and populate
m_uiDmrid = dmrid;
m_uiNXDNid = nxdnid;
// and populate
auto len = strlen(sz);
auto len = cs.size();
if ( len > 0 )
{
// callsign valid
memcpy(m_Callsign, sz, MIN(len, CALLSIGN_LEN-1));
if ( len > CALLSIGN_LEN )
memcpy(m_Callsign.c, cs.c_str(), MIN(len, CALLSIGN_LEN-1));
if ( len >= CALLSIGN_LEN )
{
m_Module = sz[len-1];
m_Module = cs.back();
}
// Calculate the M17 coded callsign
CSIn();
// dmrid ok ?
if ( m_uiDmrid == 0 )
auto key = GetKey();
if (0 == m_uiDmrid)
{
g_DmridDir.Lock();
{
m_uiDmrid = g_DmridDir.FindDmrid(*this);
}
g_DmridDir.Unlock();
g_LDid.Lock();
m_uiDmrid = g_LDid.FindDmrid(key);
g_LDid.Unlock();
}
if ( m_uiNXDNid == 0 )
if (0 == m_uiNXDNid)
{
g_NXDNidDir.Lock();
{
m_uiNXDNid = g_NXDNidDir.FindNXDNid(*this);
}
g_NXDNidDir.Unlock();
g_LNid.Lock();
m_uiNXDNid = g_LNid.FindNXDNid(key);
g_LNid.Unlock();
}
}
else if ( m_uiDmrid != 0 )
else if (dmrid)
{
g_DmridDir.Lock();
{
const CCallsign *callsign = g_DmridDir.FindCallsign(m_uiDmrid);
if ( callsign != nullptr )
{
memcpy(m_Callsign, callsign->m_Callsign, CALLSIGN_LEN);
}
}
g_DmridDir.Unlock();
g_LDid.Lock();
auto pItem = g_LDid.FindCallsign(dmrid);
if (pItem)
m_Callsign = *pItem;
g_LDid.Unlock();
if ( m_uiNXDNid == 0 )
if (m_Callsign.l && 0 == nxdnid)
{
g_NXDNidDir.Lock();
{
m_uiNXDNid = g_NXDNidDir.FindNXDNid(*this);
}
g_NXDNidDir.Unlock();
g_LNid.Lock();
m_uiNXDNid = g_LNid.FindNXDNid(GetKey());
g_LNid.Unlock();
}
CSIn();
}
else if ( m_uiNXDNid != 0 )
else if (nxdnid)
{
g_NXDNidDir.Lock();
{
const CCallsign *callsign = g_NXDNidDir.FindCallsign(m_uiNXDNid);
if ( callsign != nullptr )
{
memcpy(m_Callsign, callsign->m_Callsign, CALLSIGN_LEN);
}
}
g_NXDNidDir.Unlock();
g_LNid.Lock();
auto pItem = g_LNid.FindCallsign(nxdnid);
if (pItem)
m_Callsign = *pItem;
g_LNid.Unlock();
if ( m_uiDmrid == 0 )
if (m_Callsign.l && 0 == dmrid)
{
g_DmridDir.Lock();
{
m_uiDmrid = g_DmridDir.FindDmrid(*this);
}
g_DmridDir.Unlock();
g_LDid.Lock();
m_uiDmrid = g_LDid.FindDmrid(GetKey());
g_LDid.Unlock();
}
}
if (m_Callsign.l)
CSIn();
}
CCallsign::CCallsign(const CCallsign &cs)
{
m_Callsign.l = cs.m_Callsign.l;
m_Suffix.u = cs.m_Suffix.u;
m_Module = cs.m_Module;
m_uiDmrid = cs.m_uiDmrid;
m_uiNXDNid = cs.m_uiNXDNid;
m_coded = cs.m_coded;
}
CCallsign::CCallsign(const UCallsign &ucs) : CCallsign()
{
m_Callsign.l = ucs.l;
}
CCallsign &CCallsign::operator = (const CCallsign &cs)
{
if (this != &cs)
{
m_Callsign.l = cs.m_Callsign.l;
m_Suffix.u = cs.m_Suffix.u;
m_Module = cs.m_Module;
m_uiDmrid = cs.m_uiDmrid;
m_uiNXDNid = cs.m_uiNXDNid;
m_coded = cs.m_coded;
}
return *this;
}
////////////////////////////////////////////////////////////////////////////////////////
@ -141,32 +144,31 @@ bool CCallsign::IsValid(void) const
int iNum = 0;
for ( i = 0; i < 3; i++ )
{
valid &= IsLetter(m_Callsign[i]) || IsNumber(m_Callsign[i]);
if ( IsNumber(m_Callsign[i]) )
valid = valid && (IsLetter(m_Callsign.c[i]) || IsNumber(m_Callsign.c[i]));
if ( IsNumber(m_Callsign.c[i]) )
{
iNum++;
}
}
valid &= (iNum < 3);
valid = valid && (iNum < 3);
// all remaining char are letter, number or space
for ( ; i < CALLSIGN_LEN; i++)
{
valid &= IsLetter(m_Callsign[i]) || IsNumber(m_Callsign[i]) || IsSpace(m_Callsign[i]);
valid = valid && (IsLetter(m_Callsign.c[i]) || IsNumber(m_Callsign.c[i]) || IsSpace(m_Callsign.c[i]));
}
// prefix
// all chars are number, letter, special char, or space
for ( i = 0; i < CALLSUFFIX_LEN; i++ )
{
valid &= IsLetter(m_Suffix[i]) || IsNumber(m_Suffix[i]) || IsSpace(m_Suffix[i]) || IsLetterLC(m_Suffix[i]) || IsSpecialChar(m_Suffix[i]);
valid = valid && (IsLetter(m_Suffix.c[i]) || IsNumber(m_Suffix.c[i]) || IsSpace(m_Suffix.c[i]) || IsLetterLC(m_Suffix.c[i]) || IsSpecialChar(m_Suffix.c[i]));
}
// module
// is an letter or space
valid &= IsLetter(m_Module) || IsSpace(m_Module);
valid = valid && (IsLetter(m_Module) || IsSpace(m_Module));
// dmrid is not tested, as it can be nullptr
// if station does is not dmr registered
// dmr and nxdn id is not tested, as it can be 0 if station is not registered
// done
return valid;
@ -174,57 +176,53 @@ bool CCallsign::IsValid(void) const
bool CCallsign::HasSuffix(void) const
{
bool has = false;
for ( int i = 0; i < CALLSUFFIX_LEN; i++ )
{
has |= (m_Suffix[i] != ' ');
}
return has;
return 0x20202020u != m_Suffix.u;
}
////////////////////////////////////////////////////////////////////////////////////////
// set
void CCallsign::SetCallsign(const char *sz, bool UpdateDmrid)
void CCallsign::SetCallsign(const std::string &s, bool updateids)
{
// set callsign
memset(m_Callsign, ' ', CALLSIGN_LEN);
memset(m_Callsign.c, ' ', CALLSIGN_LEN);
m_Module = ' ';
auto len = strlen(sz);
memcpy(m_Callsign, sz, MIN(len, CALLSIGN_LEN-1));
if ( len > CALLSIGN_LEN )
auto len = s.size();
memcpy(m_Callsign.c, s.c_str(), MIN(len, CALLSIGN_LEN-1));
if ( len >= CALLSIGN_LEN )
{
m_Module = sz[len-1];
m_Module = s.back();
}
// update M17 coded callsign
CSIn();
// and update dmrid
if ( UpdateDmrid )
if (updateids)
{
g_DmridDir.Lock();
auto key = GetKey();
g_LDid.Lock();
{
m_uiDmrid = g_DmridDir.FindDmrid(*this);
m_uiDmrid = g_LDid.FindDmrid(key);
}
g_DmridDir.Unlock();
g_NXDNidDir.Lock();
g_LDid.Unlock();
g_LNid.Lock();
{
m_uiNXDNid = g_NXDNidDir.FindNXDNid(*this);
m_uiNXDNid = g_LNid.FindNXDNid(key);
}
g_NXDNidDir.Unlock();
g_LNid.Unlock();
}
}
void CCallsign::SetCallsign(const uint8_t *buffer, int len, bool UpdateDmrid)
void CCallsign::SetCallsign(const uint8_t *buffer, int len, bool updateids)
{
// set callsign
memset(m_Callsign, ' ', CALLSIGN_LEN);
memset(m_Callsign.c, ' ', CALLSIGN_LEN);
m_Module = ' ';
memcpy(m_Callsign, buffer, MIN(len, (int)CALLSIGN_LEN-1));
memcpy(m_Callsign.c, buffer, MIN(len, (int)CALLSIGN_LEN-1));
for ( unsigned i = 0; i < CALLSIGN_LEN; i++ )
{
if ( m_Callsign[i] == 0 )
if ( m_Callsign.c[i] == 0 )
{
m_Callsign[i] = ' ';
m_Callsign.c[i] = ' ';
}
}
if ( (len >= (int)CALLSIGN_LEN) && ((char)buffer[CALLSIGN_LEN-1] != 0) )
@ -232,18 +230,19 @@ void CCallsign::SetCallsign(const uint8_t *buffer, int len, bool UpdateDmrid)
m_Module = (char)buffer[CALLSIGN_LEN-1];
}
CSIn();
if ( UpdateDmrid )
if (updateids)
{
g_DmridDir.Lock();
auto key = GetKey();
g_LDid.Lock();
{
m_uiDmrid = g_DmridDir.FindDmrid(*this);
m_uiDmrid = g_LDid.FindDmrid(key);
}
g_DmridDir.Unlock();
g_NXDNidDir.Lock();
g_LDid.Unlock();
g_LNid.Lock();
{
m_uiNXDNid = g_NXDNidDir.FindNXDNid(*this);
m_uiNXDNid = g_LNid.FindNXDNid(key);
}
g_NXDNidDir.Unlock();
g_LNid.Unlock();
}
}
@ -252,15 +251,16 @@ void CCallsign::SetDmrid(uint32_t dmrid, bool UpdateCallsign)
m_uiDmrid = dmrid;
if ( UpdateCallsign )
{
g_DmridDir.Lock();
g_LDid.Lock();
{
const CCallsign *callsign = g_DmridDir.FindCallsign(dmrid);
auto callsign = g_LDid.FindCallsign(dmrid);
if ( callsign != nullptr )
{
memcpy(m_Callsign, callsign->m_Callsign, CALLSIGN_LEN);
m_Callsign.l = callsign->l;
}
}
g_DmridDir.Unlock();
g_LDid.Unlock();
CSIn();
}
}
@ -277,15 +277,16 @@ void CCallsign::SetNXDNid(uint16_t nxdnid, bool UpdateCallsign)
m_uiNXDNid = nxdnid;
if ( UpdateCallsign )
{
g_NXDNidDir.Lock();
g_LNid.Lock();
{
const CCallsign *callsign = g_NXDNidDir.FindCallsign(nxdnid);
auto callsign = g_LNid.FindCallsign(nxdnid);
if ( callsign != nullptr )
{
memcpy(m_Callsign, callsign->m_Callsign, CALLSIGN_LEN);
m_Callsign.l = callsign->l;
}
}
g_NXDNidDir.Unlock();
g_LNid.Unlock();
CSIn();
}
}
@ -304,17 +305,17 @@ void CCallsign::SetCSModule(char c)
}
void CCallsign::SetSuffix(const char *sz)
void CCallsign::SetSuffix(const std::string &s)
{
memset(m_Suffix, ' ', CALLSUFFIX_LEN);
memcpy(m_Suffix, sz, MIN(strlen(sz), CALLSUFFIX_LEN));
memset(m_Suffix.c, ' ', CALLSUFFIX_LEN);
memcpy(m_Suffix.c, s.c_str(), MIN(s.size(), CALLSUFFIX_LEN));
}
void CCallsign::SetSuffix(const uint8_t *buffer, int len)
{
len = MIN(len, (int)CALLSUFFIX_LEN);
memset(m_Suffix, ' ', CALLSUFFIX_LEN);
memcpy(m_Suffix, buffer, len);
memset(m_Suffix.c, ' ', CALLSUFFIX_LEN);
memcpy(m_Suffix.c, buffer, len);
}
////////////////////////////////////////////////////////////////////////////////////////
@ -324,7 +325,7 @@ void CCallsign::PatchCallsign(int off, const char *patch, int len)
{
if ( off < CALLSIGN_LEN )
{
memcpy(m_Callsign, patch, MIN(len, (int)CALLSIGN_LEN - off));
memcpy(m_Callsign.c, patch, MIN(len, (int)CALLSIGN_LEN - off));
}
CSIn();
}
@ -333,9 +334,24 @@ void CCallsign::PatchCallsign(int off, const char *patch, int len)
////////////////////////////////////////////////////////////////////////////////////////
// get
UCallsign CCallsign::GetKey() const
{
UCallsign rval;
rval.l = 0x2020202020202020ul;
for (unsigned i=0; i<CALLSIGN_LEN; i++)
{
auto c = m_Callsign.c[i];
if (IsLetter(c) || IsNumber(c))
rval.c[i] = c;
else
break;
}
return rval;
}
void CCallsign::GetCallsign(uint8_t *buffer) const
{
memcpy(buffer, m_Callsign, CALLSIGN_LEN);
memcpy(buffer, m_Callsign.c, CALLSIGN_LEN);
if ( HasModule() )
{
buffer[CALLSIGN_LEN-1] = m_Module;
@ -345,51 +361,52 @@ void CCallsign::GetCallsign(uint8_t *buffer) const
void CCallsign::GetCallsignString(char *sz) const
{
unsigned i;
for ( i = 0; (i < CALLSIGN_LEN) && (m_Callsign[i] != ' '); i++ )
for ( i = 0; (i < CALLSIGN_LEN) && (m_Callsign.c[i] != ' '); i++ )
{
sz[i] = m_Callsign[i];
sz[i] = m_Callsign.c[i];
}
sz[i] = 0;
}
const std::string CCallsign::GetCS(unsigned len) const
const std::string CCallsign::GetCS() const
{
std::string rval(m_Callsign, CALLSIGN_LEN);
rval.append(1, m_Module);
std::string rval(m_Callsign.c, CALLSIGN_LEN);
if (' ' != m_Module)
rval.append(1, m_Module);
return rval;
}
void CCallsign::GetSuffix(uint8_t *buffer) const
{
memcpy(buffer, m_Suffix, CALLSUFFIX_LEN);
memcpy(buffer, m_Suffix.c, CALLSUFFIX_LEN);
}
////////////////////////////////////////////////////////////////////////////////////////
// compare
bool CCallsign::HasSameCallsign(const CCallsign &Callsign) const
bool CCallsign::HasSameCallsign(const CCallsign &cs) const
{
return (memcmp(m_Callsign, Callsign.m_Callsign, CALLSIGN_LEN) == 0);
return (memcmp(m_Callsign.c, cs.m_Callsign.c, CALLSIGN_LEN) == 0);
}
bool CCallsign::HasSameCallsignWithWildcard(const CCallsign &callsign) const
bool CCallsign::HasSameCallsignWithWildcard(const CCallsign &cs) const
{
bool same = true;
bool done = false;
for ( unsigned i = 0; (i < CALLSIGN_LEN) && same && !done; i++ )
{
if ( !(done = ((m_Callsign[i] == '*') || (callsign[i] == '*'))) )
if ( !(done = ((m_Callsign.c[i] == '*') || (cs.m_Callsign.c[i] == '*'))) )
{
same &= (m_Callsign[i] == callsign[i]);
same = same && (m_Callsign.c[i] == cs.m_Callsign.c[i]);
}
}
return same;
}
bool CCallsign::HasLowerCallsign(const CCallsign &Callsign) const
bool CCallsign::HasLowerCallsign(const CCallsign &cs) const
{
return (memcmp(m_Callsign, Callsign.m_Callsign, CALLSIGN_LEN) < 0);
return (memcmp(m_Callsign.c, cs.m_Callsign.c, CALLSIGN_LEN) < 0);
}
bool CCallsign::HasSameModule(const CCallsign &Callsign) const
@ -401,34 +418,33 @@ bool CCallsign::HasSameModule(const CCallsign &Callsign) const
////////////////////////////////////////////////////////////////////////////////////////
// operators
bool CCallsign::operator ==(const CCallsign &callsign) const
bool CCallsign::operator ==(const CCallsign &cs) const
{
return ((memcmp(callsign.m_Callsign, m_Callsign, CALLSIGN_LEN) == 0) && (m_Module == callsign.m_Module)
&& (memcmp(callsign.m_Suffix, m_Suffix, CALLSUFFIX_LEN) == 0)
&& (m_uiDmrid == callsign.m_uiDmrid)
);
return (cs.m_Callsign.l == m_Callsign.l) && (m_Module == cs.m_Module) && (cs.m_Suffix.u == m_Suffix.u) && (m_uiDmrid == cs.m_uiDmrid) && (m_uiNXDNid == cs.m_uiNXDNid);
}
CCallsign::operator const char *() const
{
static char sz[CALLSIGN_LEN+CALLSUFFIX_LEN+5];
// empty
memset(m_sz, 0, sizeof(m_sz));
memset(sz, 0, sizeof(sz));
// callsign
memcpy(m_sz, m_Callsign, CALLSIGN_LEN);
memcpy(sz, m_Callsign.c, CALLSIGN_LEN);
// module
if ( HasModule() )
{
m_sz[CALLSIGN_LEN] = m_Module;
sz[CALLSIGN_LEN] = m_Module;
}
// suffix
if ( HasSuffix() )
{
::strcat(m_sz, " / ");
::strncat(m_sz, m_Suffix, CALLSUFFIX_LEN);
::strcat(sz, " / ");
::strncat(sz, m_Suffix.c, CALLSUFFIX_LEN);
}
// done
return m_sz;
return sz;
}
////////////////////////////////////////////////////////////////////////////////////////
@ -500,7 +516,7 @@ void CCallsign::CSIn()
m_coded = pos;
m_coded *= 40;
for( int i=CALLSIGN_LEN-2; i>=0; i-- ) {
pos = m17_alphabet.find(m_Callsign[i]);
pos = m17_alphabet.find(m_Callsign.c[i]);
if (pos == std::string::npos) {
pos = 0;
}

@ -16,26 +16,10 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Main.h"
#include <string.h>
#include "Reflector.h"
#include "GateKeeper.h"
#include "DMRIdDirFile.h"
#include "DMRIdDirHttp.h"
#include "NXDNIdDirFile.h"
#include "NXDNIdDirHttp.h"
#include "YSFNodeDirFile.h"
#include "YSFNodeDirHttp.h"
////////////////////////////////////////////////////////////////////////////////////////
// constructor
CReflector::CReflector() : m_Callsign(CALLSIGN), m_Modules(ACTIVE_MODULES), keep_running(true)
{
}
#include <string.h>
#include "Global.h"
////////////////////////////////////////////////////////////////////////////////////////
// destructor
@ -46,12 +30,7 @@ CReflector::~CReflector()
{
m_XmlReportFuture.get();
}
#ifdef JSON_MONITOR
if ( m_JsonReportFuture.valid() )
{
m_JsonReportFuture.get();
}
#endif
for (auto it=m_Modules.cbegin(); it!=m_Modules.cend(); it++)
{
if (m_RouterFuture[*it].valid())
@ -59,11 +38,6 @@ CReflector::~CReflector()
}
m_RouterFuture.clear();
m_Stream.clear();
#ifdef TRANSCODED_MODULES
m_TCReader.clear();
#endif
}
@ -72,6 +46,11 @@ CReflector::~CReflector()
bool CReflector::Start(void)
{
// get config stuff
m_Callsign = CCallsign(g_Configure.GetString(g_Keys.names.callsign).c_str(), false);
m_Modules.assign(g_Configure.GetString(g_Keys.modules.modules));
std::string tcmods(g_Configure.GetString(g_Keys.modules.tcmodules));
// let's go!
keep_running = true;
@ -79,48 +58,63 @@ bool CReflector::Start(void)
g_GateKeeper.Init();
// init dmrid directory. No need to check the return value.
g_DmridDir.Init();
g_LDid.LookupInit();
// init nxdnid directory. No need to check the return value.
g_NXDNidDir.Init();
// init dmrid directory. No need to check the return value.
g_LNid.LookupInit();
// init wiresx node directory. Likewise with the return vale.
g_YsfNodeDir.Init();
g_LYtr.LookupInit();
// create protocols
if (! m_Protocols.Init())
{
m_Protocols.Close();
return false;
return true;
}
// start one thread per reflector module
for (auto it=m_Modules.cbegin(); it!=m_Modules.cend(); it++)
for (auto c : m_Modules)
{
#ifdef TRANSCODED_MODULES
m_TCReader[*it] = std::make_shared<CUnixDgramReader>();
std::string readername(TC2REF);
readername.append(1, *it);
if (m_TCReader[*it]->Open(readername.c_str()))
auto stream = std::make_shared<CPacketStream>(c);
if (stream)
{
std::cerr << "ERROR: Reflector can't open " << readername << std::endl;
m_TCReader[*it].reset();
return false;
// if it's a transcoded module, then we need to initialize the codec stream
if (std::string::npos != tcmods.find(c))
{
if (stream->InitCodecStream())
return true;
}
m_Stream[c] = stream;
}
else
{
std::cerr << "Could not make a CPacketStream for module '" << c << "'" << std::endl;
return true;
}
try
{
m_RouterFuture[c] = std::async(std::launch::async, &CReflector::RouterThread, this, c);
}
catch(const std::exception& e)
{
std::cerr << "Cannot start module '" << c << "' thread: " << e.what() << '\n';
keep_running = false;
return true;
}
m_Stream[*it] = std::make_shared<CPacketStream>(m_TCReader[*it]);
#else
m_Stream[*it] = std::make_shared<CPacketStream>();
#endif
m_RouterFuture[*it] = std::async(std::launch::async, &CReflector::RouterThread, this, *it);
}
// start the reporting threads
m_XmlReportFuture = std::async(std::launch::async, &CReflector::XmlReportThread, this);
#ifdef JSON_MONITOR
m_JsonReportFuture = std::async(std::launch::async, &CReflector::JsonReportThread, this);
#endif
// start the reporting thread
try
{
m_XmlReportFuture = std::async(std::launch::async, &CReflector::XmlReportThread, this);
}
catch(const std::exception& e)
{
std::cerr << "Cannot start the dashboard data report thread: " << e.what() << '\n';
}
return true;
return false;
}
void CReflector::Stop(void)
@ -133,18 +127,12 @@ void CReflector::Stop(void)
{
m_XmlReportFuture.get();
}
#ifdef JSON_MONITOR
if ( m_JsonReportFuture.valid() )
{
m_JsonReportFuture.get();
}
#endif
// stop & delete all router thread
for (auto it=m_Modules.cbegin(); it!=m_Modules.cend(); it++)
for (auto c : m_Modules)
{
if (m_RouterFuture[*it].valid())
m_RouterFuture[*it].get();
if (m_RouterFuture[c].valid())
m_RouterFuture[c].get();
}
// close protocols
@ -154,9 +142,9 @@ void CReflector::Stop(void)
g_GateKeeper.Close();
// close databases
g_DmridDir.Close();
g_NXDNidDir.Close();
g_YsfNodeDir.Close();
g_LDid.LookupClose();
g_LNid.LookupClose();
g_LYtr.LookupClose();
}
////////////////////////////////////////////////////////////////////////////////////////
@ -202,7 +190,6 @@ std::shared_ptr<CPacketStream> CReflector::OpenStream(std::unique_ptr<CDvHeaderP
return nullptr;
}
stream->Lock();
// is it available ?
if ( stream->OpenPacketStream(*DvHeader, client) )
{
@ -225,7 +212,6 @@ std::shared_ptr<CPacketStream> CReflector::OpenStream(std::unique_ptr<CDvHeaderP
OnStreamOpen(stream->GetUserCallsign());
}
stream->Unlock();
return stream;
}
@ -234,22 +220,14 @@ void CReflector::CloseStream(std::shared_ptr<CPacketStream> stream)
if ( stream != nullptr )
{
// wait queue is empty. this waits forever
bool bEmpty = false;
do
bool bEmpty = stream->IsEmpty();
while (! bEmpty)
{
stream->Lock();
// do not use stream->IsEmpty() has this "may" never succeed
// and anyway, the DvLastFramPacket short-circuit the transcoder
// loop queues
bEmpty = stream->empty();
stream->Unlock();
if ( !bEmpty )
std::this_thread::sleep_for(std::chrono::milliseconds(10));
std::this_thread::sleep_for(std::chrono::milliseconds(10));
bEmpty = stream->IsEmpty();
}
while (!bEmpty);
GetClients(); // lock clients
stream->Lock(); // lock stream
// get and check the master
std::shared_ptr<CClient>client = stream->GetOwnerClient();
@ -267,11 +245,6 @@ void CReflector::CloseStream(std::shared_ptr<CPacketStream> stream)
// release clients
ReleaseClients();
// unlock before closing
// to avoid double lock in assiociated
// codecstream close/thread-join
stream->Unlock();
// and stop the queue
stream->ClosePacketStream();
}
@ -282,183 +255,99 @@ void CReflector::CloseStream(std::shared_ptr<CPacketStream> stream)
void CReflector::RouterThread(const char ThisModule)
{
auto pitem = m_Stream.find(ThisModule);
if (m_Stream.end() == pitem)
{
std::cerr << "Module '" << ThisModule << " CPacketStream doesn't exist! aborting RouterThread()" << std::endl;
return;
}
const auto streamIn = pitem->second;
while (keep_running)
{
std::unique_ptr<CPacket> packet;
auto streamIn = m_Stream[ThisModule];
// any packet in our input queue ?
streamIn->Lock();
if ( !streamIn->empty() )
{
// get the packet
packet = streamIn->pop();
}
else
{
packet = nullptr;
}
streamIn->Unlock();
// wait until something shows up
auto packet = streamIn->PopWait();
// route it
if ( packet != nullptr )
packet->SetPacketModule(ThisModule);
// iterate on all protocols
m_Protocols.Lock();
for ( auto it=m_Protocols.begin(); it!=m_Protocols.end(); it++ )
{
// set origin
packet->SetPacketModule(ThisModule);
auto copy = packet->Copy();
// iterate on all protocols
m_Protocols.Lock();
for ( auto it=m_Protocols.begin(); it!=m_Protocols.end(); it++ )
// if packet is header, update RPT2 according to protocol
if ( copy->IsDvHeader() )
{
// duplicate packet
auto packetClone = packet->Duplicate();
// if packet is header, update RPT2 according to protocol
if ( packetClone->IsDvHeader() )
{
// get our callsign
CCallsign csRPT = (*it)->GetReflectorCallsign();
csRPT.SetCSModule(ThisModule);
(dynamic_cast<CDvHeaderPacket *>(packetClone.get()))->SetRpt2Callsign(csRPT);
}
// and push it
CPacketQueue *queue = (*it)->GetQueue();
queue->push(packetClone);
(*it)->ReleaseQueue();
// make the protocol-patched reflector callsign
CCallsign csRPT = (*it)->GetReflectorCallsign();
csRPT.SetCSModule(ThisModule);
// and put it in the copy
(dynamic_cast<CDvHeaderPacket *>(copy.get()))->SetRpt2Callsign(csRPT);
}
m_Protocols.Unlock();
}
else
{
std::this_thread::sleep_for(std::chrono::milliseconds(10));
(*it)->Push(std::move(copy));
}
m_Protocols.Unlock();
}
}
////////////////////////////////////////////////////////////////////////////////////////
// report threads
#define XML_UPDATE_PERIOD 10
void CReflector::XmlReportThread()
{
while (keep_running)
{
// report to xml file
std::ofstream xmlFile;
xmlFile.open(XML_PATH, std::ios::out | std::ios::trunc);
if ( xmlFile.is_open() )
{
// write xml file
WriteXmlFile(xmlFile);
// and close file
xmlFile.close();
}
#ifndef DEBUG_NO_ERROR_ON_XML_OPEN_FAIL
else
{
std::cout << "Failed to open " << XML_PATH << std::endl;
}
#endif
// and wait a bit
for (int i=0; i< XML_UPDATE_PERIOD && keep_running; i++)
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}
std::string xmlpath, jsonpath;
#ifdef JSON_MONITOR
void CReflector::JsonReportThread()
{
CUdpSocket Socket;
CBuffer Buffer;
CIp Ip;
bool bOn;
if (g_Configure.Contains(g_Keys.files.xml))
xmlpath.assign(g_Configure.GetString(g_Keys.files.xml));
if (g_Configure.Contains(g_Keys.files.json))
jsonpath.assign(g_Configure.GetString(g_Keys.files.json));
// init variable
bOn = false;
if (xmlpath.empty() && jsonpath.empty())
return; // nothing to do
// create listening socket
if ( Socket.Open(JSON_PORT) )
while (keep_running)
{
// and loop
while (keep_running)
// report to xml file
if (! xmlpath.empty())
{
// any command ?
if ( Socket.Receive(Buffer, Ip, 50) )
std::ofstream xmlFile;
xmlFile.open(xmlpath, std::ios::out | std::ios::trunc);
if ( xmlFile.is_open() )
{
// check verb
if ( Buffer.Compare((uint8_t *)"hello", 5) == 0 )
{
std::cout << "Monitor socket connected with " << Ip << std::endl;
// connected
bOn = true;
// announce ourselves
SendJsonReflectorObject(Socket, Ip);
// dump tables
SendJsonNodesObject(Socket, Ip);
SendJsonStationsObject(Socket, Ip);
}
else if ( Buffer.Compare((uint8_t *)"bye", 3) == 0 )
{
std::cout << "Monitor socket disconnected" << std::endl;
// diconnected
bOn = false;
}
}
// write xml file
WriteXmlFile(xmlFile);
// any notifications ?
CNotification notification;
m_Notifications.Lock();
if ( !m_Notifications.empty() )
// and close file
xmlFile.close();
}
else
{
// get the packet
notification = m_Notifications.front();
m_Notifications.pop();
std::cout << "Failed to open " << xmlpath << std::endl;
}
m_Notifications.Unlock();
}
// handle it
if ( bOn )
// json report
if (! jsonpath.empty())
{
nlohmann::json jreport;
JsonReport(jreport);
std::ofstream jsonFile;
jsonFile.open(jsonpath, std::ios::out | std::ios::trunc);
if (jsonFile.is_open())
{
switch ( notification.GetId() )
{
case NOTIFICATION_CLIENTS:
case NOTIFICATION_PEERS:
//std::cout << "Monitor updating nodes table" << std::endl;
SendJsonNodesObject(Socket, Ip);
break;
case NOTIFICATION_USERS:
//std::cout << "Monitor updating stations table" << std::endl;
SendJsonStationsObject(Socket, Ip);
break;
case NOTIFICATION_STREAM_OPEN:
//std::cout << "Monitor notify station " << notification.GetCallsign() << "going ON air" << std::endl;
SendJsonStationsObject(Socket, Ip);
SendJsonOnairObject(Socket, Ip, notification.GetCallsign());
break;
case NOTIFICATION_STREAM_CLOSE:
//std::cout << "Monitor notify station " << notification.GetCallsign() << "going OFF air" << std::endl;
SendJsonOffairObject(Socket, Ip, notification.GetCallsign());
break;
case NOTIFICATION_NONE:
default:
// nothing to do, just sleep a bit
std::this_thread::sleep_for(std::chrono::milliseconds(250);
break;
}
jsonFile << jreport.dump();
jsonFile.close();
}
}
}
else
{
std::cout << "Error creating monitor socket" << std::endl;
// and wait a bit
for (int i=0; i< XML_UPDATE_PERIOD && keep_running; i++)
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}
#endif
////////////////////////////////////////////////////////////////////////////////////////
// notifications
@ -541,7 +430,34 @@ char CReflector::GetStreamModule(std::shared_ptr<CPacketStream> stream)
}
////////////////////////////////////////////////////////////////////////////////////////
// xml helpers
// report helpers
void CReflector::JsonReport(nlohmann::json &report)
{
for (auto &item : g_Configure.GetData().items())
{
if (isupper(item.key().at(0)))
report["Configure"][item.key()] = item.value();
}
report["Peers"] = nlohmann::json::array();
auto peers = GetPeers();
for (auto pit=peers->cbegin(); pit!=peers->cend(); pit++)
(*pit)->JsonReport(report);
ReleasePeers();
report["Clients"] = nlohmann::json::array();
auto clients = GetClients();
for (auto cit=clients->cbegin(); cit!=clients->cend(); cit++)
(*cit)->JsonReport(report);
ReleaseClients();
report["Users"] = nlohmann::json::array();
auto users = GetUsers();
for (auto uid=users->begin(); uid!=users->end(); uid++)
(*uid).JsonReport(report);
ReleaseUsers();
}
void CReflector::WriteXmlFile(std::ofstream &xmlFile)
{
@ -549,9 +465,7 @@ void CReflector::WriteXmlFile(std::ofstream &xmlFile)
xmlFile << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
// software version
char sz[64];
::sprintf(sz, "%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION);
xmlFile << "<Version>" << sz << "</Version>" << std::endl;
xmlFile << "<Version>" << g_Version << "</Version>" << std::endl;
CCallsign cs = m_Callsign;
cs.PatchCallsign(0, "XLX", 3);
@ -598,120 +512,3 @@ void CReflector::WriteXmlFile(std::ofstream &xmlFile)
ReleaseUsers();
xmlFile << "</" << cs << "heard users>" << std::endl;
}
#ifdef JSON_MONITOR
////////////////////////////////////////////////////////////////////////////////////////
// json helpers
void CReflector::SendJsonReflectorObject(CUdpSocket &Socket, CIp &Ip)
{
char Buffer[1024];
char cs[CALLSIGN_LEN+1];
char mod[8] = "\"A\"";
// get reflector callsign
m_Callsign.GetCallsign((uint8_t *)cs);
cs[CALLSIGN_LEN] = 0;
// build string
::sprintf(Buffer, "{\"reflector\":\"%s\",\"modules\":[", cs);
for ( int i = 0; i < NB_OF_MODULES; i++ )
{
::strcat(Buffer, mod);
mod[1]++;
if ( i < NB_OF_MODULES-1 )
{
::strcat(Buffer, ",");
}
}
::strcat(Buffer, "]}");
// and send
Socket.Send(Buffer, Ip);
}
#define JSON_NBMAX_NODES 250
void CReflector::SendJsonNodesObject(CUdpSocket &Socket, CIp &Ip)
{
char Buffer[12+(JSON_NBMAX_NODES*94)];
// nodes object table
::sprintf(Buffer, "{\"nodes\":[");
// lock
CClients *clients = GetClients();
// iterate on clients
for ( auto it=clients->cbegin(); it!=clients->cend(); )
{
(*it++)->GetJsonObject(Buffer);
if ( it != clients->cend() )
{
::strcat(Buffer, ",");
}
}
// unlock
ReleaseClients();
::strcat(Buffer, "]}");
// and send
//std::cout << Buffer << std::endl;
Socket.Send(Buffer, Ip);
}
void CReflector::SendJsonStationsObject(CUdpSocket &Socket, CIp &Ip)
{
char Buffer[15+(LASTHEARD_USERS_MAX_SIZE*94)];
// stations object table
::sprintf(Buffer, "{\"stations\":[");
// lock
CUsers *users = GetUsers();
// iterate on users
for ( auto it=users->begin(); it!=users->end(); )
{
(*it++).GetJsonObject(Buffer);
if ( it != users->end() )
{
::strcat(Buffer, ",");
}
}
// unlock
ReleaseUsers();
::strcat(Buffer, "]}");
// and send
//std::cout << Buffer << std::endl;
Socket.Send(Buffer, Ip);
}
void CReflector::SendJsonOnairObject(CUdpSocket &Socket, CIp &Ip, const CCallsign &Callsign)
{
char Buffer[128];
char sz[CALLSIGN_LEN+1];
// onair object
Callsign.GetCallsignString(sz);
::sprintf(Buffer, "{\"onair\":\"%s\"}", sz);
// and send
//std::cout << Buffer << std::endl;
Socket.Send(Buffer, Ip);
}
void CReflector::SendJsonOffairObject(CUdpSocket &Socket, CIp &Ip, const CCallsign &Callsign)
{
char Buffer[128];
char sz[CALLSIGN_LEN+1];
// offair object
Callsign.GetCallsignString(sz);
::sprintf(Buffer, "{\"offair\":\"%s\"}", sz);
// and send
//std::cout << Buffer << std::endl;
Socket.Send(Buffer, Ip);
}
#endif

@ -1,8 +1,8 @@
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
// urfd -- The universal reflector
// Copyright © 2021 Thomas A. Early N7TAE
// Copyright © 2021 Doug McLain AD8DP
// Copyright © 2023 Thomas A. Early N7TAE
// Copyright © 2023 Doug McLain AD8DP
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@ -17,13 +17,12 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Main.h"
#include "Defines.h"
#include <string.h>
#include "USRPClient.h"
#include "USRPProtocol.h"
#include "Reflector.h"
#include "GateKeeper.h"
#include "Global.h"
const uint8_t USRP_TYPE_VOICE = 0;
const uint8_t USRP_TYPE_TEXT = 2;
@ -40,15 +39,30 @@ bool CUSRPProtocol::Initialize(const char *type, const EProtocol ptype, const ui
{
CBuffer buffer;
m_uiStreamId = 0;
CClients *clients = g_Reflector.GetClients();
std::ifstream file;
std::streampos size;
// base class
// base class, create the listing port for the read-write client
if (! CProtocol::Initialize(type, ptype, port, has_ipv4, has_ipv6))
return false;
file.open(USRP_CLIENTS_PATH, std::ios::in | std::ios::binary | std::ios::ate);
m_Module = g_Configure.GetAutolinkModule(g_Keys.usrp.module);
// create the one special USRP Tx/Rx client
auto scs = g_Configure.GetString(g_Keys.usrp.callsign);
if (scs.compare("NONE"))
{
m_Callsign.SetCallsign(scs, false);
CIp ip(AF_INET, uint16_t(g_Configure.GetUnsigned(g_Keys.usrp.txport)), g_Configure.GetString(g_Keys.usrp.ip).c_str());
auto newclient = std::make_shared<CUSRPClient>(m_Callsign, ip);
newclient->SetReflectorModule(m_Module);
g_Reflector.GetClients()->AddClient(newclient);
g_Reflector.ReleaseClients();
}
// now create "listen-only" clients, as many as specified
if (g_Configure.Contains(g_Keys.usrp.filepath))
file.open(g_Configure.GetString(g_Keys.usrp.filepath), std::ios::in | std::ios::binary | std::ios::ate);
if ( file.is_open() )
{
// read file
@ -81,18 +95,19 @@ bool CUSRPProtocol::Initialize(const char *type, const EProtocol ptype, const ui
((port = ::strtok(nullptr, ";")) != nullptr) &&
((clientcs = ::strtok(nullptr, ";")) != nullptr) )
{
uint32_t ui = atoi(port);
uint16_t ui = atoi(port);
CIp Ip(AF_INET, ui, ip);
CCallsign cs(clientcs);
CCallsign cs;
cs.SetCallsign(clientcs, false);
auto newclient = std::make_shared<CUSRPClient>(cs, Ip);
newclient->SetReflectorModule(USRP_AUTOLINK_MODULE);
clients->AddClient(newclient);
newclient->SetReflectorModule(m_Module);
g_Reflector.GetClients()->AddClient(newclient);
g_Reflector.ReleaseClients();
}
ptr1 = ptr2+1;
}
}
g_Reflector.ReleaseClients();
// update time
m_LastKeepaliveTime.start();
@ -220,11 +235,10 @@ void CUSRPProtocol::OnDvHeaderPacketIn(std::unique_ptr<CDvHeaderPacket> &Header,
void CUSRPProtocol::HandleQueue(void)
{
m_Queue.Lock();
while ( !m_Queue.empty() )
while (! m_Queue.IsEmpty())
{
// get the packet
auto packet = m_Queue.pop();
auto packet = m_Queue.Pop();
// get our sender's id
const auto module = packet->GetPacketModule();
@ -263,7 +277,6 @@ void CUSRPProtocol::HandleQueue(void)
g_Reflector.ReleaseClients();
}
}
m_Queue.Unlock();
}
@ -278,10 +291,10 @@ bool CUSRPProtocol::IsValidDvPacket(const CIp &Ip, const CBuffer &Buffer, std::u
if ( !stream )
{
m_uiStreamId = static_cast<uint32_t>(::rand());
CCallsign csMY = CCallsign(USRP_DEFAULT_CALLSIGN);
CCallsign rpt1 = CCallsign(USRP_DEFAULT_CALLSIGN);
CCallsign csMY;
CCallsign rpt1 = m_Callsign;
CCallsign rpt2 = m_ReflectorCallsign;
rpt1.SetCSModule(USRP_MODULE_ID);
rpt1.SetCSModule(m_Module);
rpt2.SetCSModule(' ');
header = std::unique_ptr<CDvHeaderPacket>(new CDvHeaderPacket(csMY, CCallsign("CQCQCQ"), rpt1, rpt2, m_uiStreamId, true));
OnDvHeaderPacketIn(header, Ip);
@ -309,7 +322,7 @@ bool CUSRPProtocol::IsValidDvHeaderPacket(const CIp &Ip, const CBuffer &Buffer,
CCallsign csMY = CCallsign("", uiSrcId);
CCallsign rpt1 = CCallsign("", uiSrcId);
CCallsign rpt2 = m_ReflectorCallsign;
rpt1.SetCSModule(USRP_MODULE_ID);
rpt1.SetCSModule(m_Module);
rpt2.SetCSModule(' ');
header = std::unique_ptr<CDvHeaderPacket>(new CDvHeaderPacket(csMY, CCallsign("CQCQCQ"), rpt1, rpt2, m_uiStreamId, true));
}
@ -396,4 +409,3 @@ void CUSRPProtocol::HandleKeepalives(void)
}
g_Reflector.ReleaseClients();
}

Loading…
Cancel
Save

Powered by TurnKey Linux.