[DMR] implement appropriate support to perform payload timeslot activation; [DMR] handle appropriate transmission of various payload, TSCC and payload TSCC short LC messages; [DMR] implement REST API callbacks to trigger payload activation for remote channels; [DMR] properly transmit channel grant twice for "redundancy" per ETSI spec; [DMR/P25/NXDN] implement more verbose messages for failures to command remote voice channesl to perform actions;

pull/24/head
Bryan Biedenkapp 3 years ago
parent a1dd5193cf
commit f216304970

@ -148,21 +148,21 @@ void Control::setOptions(yaml::Node& conf, bool controlPermitTG, const std::vect
Slot::setSiteData(voiceChNo, voiceChData, netId, siteId, channelId, channelNo, dedicatedTSCC); Slot::setSiteData(voiceChNo, voiceChData, netId, siteId, channelId, channelNo, dedicatedTSCC);
Slot::setAlohaConfig(nRandWait, backOff); Slot::setAlohaConfig(nRandWait, backOff);
m_tsccSlotNo = (uint8_t)control["slot"].as<uint32_t>(0U); if (enableTSCC) {
switch (m_tsccSlotNo) { m_tsccSlotNo = (uint8_t)control["slot"].as<uint32_t>(0U);
case 1U: switch (m_tsccSlotNo) {
m_slot1->setTSCC(enableTSCC, dedicatedTSCC); case 1U:
m_slot1->setControlPermitTG(m_controlPermitTG); m_slot1->setTSCC(enableTSCC, dedicatedTSCC);
//m_slot2->setTSCCPayload(true); // this is not the correct way this should be done m_slot1->setControlPermitTG(m_controlPermitTG);
break; break;
case 2U: case 2U:
m_slot2->setTSCC(enableTSCC, dedicatedTSCC); m_slot2->setTSCC(enableTSCC, dedicatedTSCC);
m_slot2->setControlPermitTG(m_controlPermitTG); m_slot2->setControlPermitTG(m_controlPermitTG);
//m_slot1->setTSCCPayload(true); // this is not the correct way this should be done break;
break; default:
default: LogError(LOG_DMR, "DMR, invalid slot, TSCC disabled, slotNo = %u", m_tsccSlotNo);
LogError(LOG_DMR, "DMR, invalid slot, TSCC disabled, slotNo = %u", m_tsccSlotNo); break;
break; }
} }
uint32_t silenceThreshold = dmrProtocol["silenceThreshold"].as<uint32_t>(dmr::DEFAULT_SILENCE_THRESHOLD); uint32_t silenceThreshold = dmrProtocol["silenceThreshold"].as<uint32_t>(dmr::DEFAULT_SILENCE_THRESHOLD);
@ -398,6 +398,55 @@ Slot* Control::getTSCCSlot() const
} }
} }
/// <summary>
/// Helper to payload activate the slot carrying granted payload traffic.
/// </summary>
/// <param name="slotNo">DMR slot number.</param>
/// <param name="dstId"></param>
/// <param name="group"></param>
void Control::tsccActivateSlot(uint32_t slotNo, uint32_t dstId, bool group)
{
if (m_verbose) {
LogMessage(LOG_DMR, "DMR Slot %u, payload activation, group = %u, dstId = %u",
group, dstId);
}
switch (slotNo) {
case 1U:
m_slot1->setTSCCActivated(dstId, group);
break;
case 2U:
m_slot2->setTSCCActivated(dstId, group);
break;
default:
LogError(LOG_DMR, "DMR, invalid slot, TSCC payload activation, slotNo = %u", slotNo);
break;
}
}
/// <summary>
/// Helper to clear an activated payload slot.
/// </summary>
/// <param name="slotNo">DMR slot number.</param>
void Control::tsccClearActivatedSlot(uint32_t slotNo)
{
if (m_verbose) {
LogMessage(LOG_DMR, "DMR Slot %u, payload activation clear");
}
switch (slotNo) {
case 1U:
m_slot1->clearTSCCActivated();
break;
case 2U:
m_slot2->clearTSCCActivated();
break;
default:
LogError(LOG_DMR, "DMR, invalid slot, TSCC payload activation, slotNo = %u", slotNo);
break;
}
}
/// <summary> /// <summary>
/// Helper to write a DMR extended function packet on the RF interface. /// Helper to write a DMR extended function packet on the RF interface.
/// </summary> /// </summary>

@ -99,6 +99,10 @@ namespace dmr
/// <summary>Helper to return the slot carrying the TSCC.</summary> /// <summary>Helper to return the slot carrying the TSCC.</summary>
Slot* getTSCCSlot() const; Slot* getTSCCSlot() const;
/// <summary>Helper to payload activate the slot carrying granted payload traffic.</summary>
void tsccActivateSlot(uint32_t slotNo, uint32_t dstId, bool group);
/// <summary>Helper to clear an activated payload slot.</summary>
void tsccClearActivatedSlot(uint32_t slotNo);
/// <summary>Helper to write a DMR extended function packet on the RF interface.</summary> /// <summary>Helper to write a DMR extended function packet on the RF interface.</summary>
void writeRF_Ext_Func(uint32_t slotNo, uint32_t func, uint32_t arg, uint32_t dstId); void writeRF_Ext_Func(uint32_t slotNo, uint32_t func, uint32_t arg, uint32_t dstId);

@ -162,6 +162,10 @@ Slot::Slot(uint32_t slotNo, uint32_t timeout, uint32_t tgHang, uint32_t queueSiz
m_ccPrevRunning(false), m_ccPrevRunning(false),
m_ccHalted(false), m_ccHalted(false),
m_enableTSCC(false), m_enableTSCC(false),
m_dedicatedTSCC(false),
m_tsccActivated(false),
m_tsccPayloadDstId(0U),
m_tsccPayloadGroup(false),
m_verbose(verbose), m_verbose(verbose),
m_debug(debug) m_debug(debug)
{ {
@ -288,12 +292,11 @@ bool Slot::processFrame(uint8_t *data, uint32_t len)
if ((dataSync || voiceSync) && m_rfState != RS_RF_LISTENING) if ((dataSync || voiceSync) && m_rfState != RS_RF_LISTENING)
m_rfTGHang.start(); m_rfTGHang.start();
// write and process TSCC CSBKs and short LC if (dataSync) {
if (m_enableTSCC && m_dedicatedTSCC) uint8_t dataType = data[1U] & 0x0FU;
{
if (dataSync) {
uint8_t dataType = data[1U] & 0x0FU;
// write and process TSCC CSBKs and short LC
if (m_enableTSCC && m_dedicatedTSCC && m_slotNo == m_dmr->m_tsccSlotNo) {
switch (dataType) switch (dataType)
{ {
case DT_CSBK: case DT_CSBK:
@ -301,13 +304,9 @@ bool Slot::processFrame(uint8_t *data, uint32_t len)
default: default:
break; break;
} }
}
return false;
}
if (dataSync) { return false;
uint8_t dataType = data[1U] & 0x0FU; }
switch (dataType) switch (dataType)
{ {
@ -375,6 +374,12 @@ void Slot::processNetwork(const data::Data& dmrData)
uint8_t dataType = dmrData.getDataType(); uint8_t dataType = dmrData.getDataType();
// ignore non-CSBK data destined for the TSCC slot
if (m_enableTSCC && m_dedicatedTSCC && m_slotNo == m_dmr->m_tsccSlotNo &&
dataType != DT_CSBK) {
return;
}
switch (dataType) switch (dataType)
{ {
case DT_CSBK: case DT_CSBK:
@ -464,8 +469,36 @@ void Slot::clock()
} }
} }
if (m_tsccPayloadSlot) { // never allow the TSCC to become payload activated
setShortLC_Payload(m_siteData, m_tsccCnt); if (m_tsccActivated && m_enableTSCC) {
::LogDebug(LOG_DMR, "DMR Slot %u, BUG BUG tried to payload activate the TSCC slot", m_slotNo);
clearTSCCActivated();
}
// activate payload channel if requested from the TSCC
if (m_tsccActivated && !m_enableTSCC) {
if (m_rfState == RS_RF_LISTENING && m_netState == RS_NET_IDLE) {
if (m_tsccPayloadDstId > 0U) {
if (m_dmr->m_tsccSlotNo > 0U) {
// every 4 frames transmit the payload TSCC
if ((m_tsccCnt % 4) == 0) {
setShortLC_Payload(m_siteData, m_tsccCnt);
}
else {
// only transmit the payload short LC every 2 frames
if ((m_tsccCnt % 2) == 0) {
setShortLC(m_slotNo, m_tsccPayloadDstId, m_tsccPayloadGroup ? FLCO_GROUP : FLCO_PRIVATE, true);
}
}
}
else {
setShortLC(m_slotNo, m_tsccPayloadDstId, m_tsccPayloadGroup ? FLCO_GROUP : FLCO_PRIVATE, true);
}
}
}
else {
clearTSCCActivated();
}
} }
m_rfTimeoutTimer.clock(ms); m_rfTimeoutTimer.clock(ms);

@ -106,7 +106,7 @@ namespace dmr
/// <summary>Helper to enable and configure TSCC support for this slot.</summary> /// <summary>Helper to enable and configure TSCC support for this slot.</summary>
void setTSCC(bool enable, bool dedicated); void setTSCC(bool enable, bool dedicated);
/// <summary>Sets a flag indicating whether the slot is a TSCC payload slot.</summary> /// <summary>Sets a flag indicating whether the slot is a TSCC payload slot.</summary>
void setTSCCPayload(bool payload) { m_tsccPayloadSlot = payload; } void setTSCCActivated(uint32_t dstId, bool group) { m_tsccActivated = true; m_tsccPayloadDstId = dstId; m_tsccPayloadGroup = group; }
/// <summary>Sets a flag indicating whether the DMR control channel can send permit-tg to voice channels.</summary> /// <summary>Sets a flag indicating whether the DMR control channel can send permit-tg to voice channels.</summary>
void setControlPermitTG(bool controlPermitTG) { m_controlPermitTG = controlPermitTG; } void setControlPermitTG(bool controlPermitTG) { m_controlPermitTG = controlPermitTG; }
/// <summary>Helper to set the voice error silence threshold.</summary> /// <summary>Helper to set the voice error silence threshold.</summary>
@ -191,7 +191,10 @@ namespace dmr
bool m_enableTSCC; bool m_enableTSCC;
bool m_dedicatedTSCC; bool m_dedicatedTSCC;
bool m_tsccPayloadSlot;
bool m_tsccActivated;
uint32_t m_tsccPayloadDstId;
bool m_tsccPayloadGroup;
bool m_controlPermitTG; bool m_controlPermitTG;
@ -263,6 +266,9 @@ namespace dmr
/// <summary>Helper to write control channel packet data.</summary> /// <summary>Helper to write control channel packet data.</summary>
void writeRF_ControlData(uint16_t frameCnt, uint8_t n); void writeRF_ControlData(uint16_t frameCnt, uint8_t n);
/// <summary>Clears the flag indicating whether the slot is a TSCC payload slot.</summary>
void clearTSCCActivated() { m_tsccActivated = false; m_tsccPayloadDstId = 0U; m_tsccPayloadGroup = false; }
/// <summary>Helper to set the DMR short LC.</summary> /// <summary>Helper to set the DMR short LC.</summary>
static void setShortLC(uint32_t slotNo, uint32_t id, uint8_t flco = FLCO_GROUP, bool voice = true); static void setShortLC(uint32_t slotNo, uint32_t id, uint8_t flco = FLCO_GROUP, bool voice = true);
/// <summary>Helper to set the DMR short LC for TSCC.</summary> /// <summary>Helper to set the DMR short LC for TSCC.</summary>

@ -841,6 +841,26 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_
::ActivityLog("DMR", true, "Slot %u group grant request from %u to TG %u", m_tscc->m_slotNo, srcId, dstId); ::ActivityLog("DMR", true, "Slot %u group grant request from %u to TG %u", m_tscc->m_slotNo, srcId, dstId);
} }
// if the channel granted isn't the same as the TSCC; remote activate the payload channel
if (chNo != m_tscc->m_channelNo) {
::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->getRFChData(chNo);
if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) {
json::object req = json::object();
req["dstId"].set<uint32_t>(dstId);
req["slot"].set<uint8_t>(slot);
req["group"].set<bool>(grp);
RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(),
HTTP_PUT, PUT_PERMIT_TG, req, m_tscc->m_debug);
}
else {
::LogError(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), failed to activate payload channel, chNo = %u, slot = %u", m_tscc->m_slotNo, chNo, slot);
}
}
else {
m_slot->m_dmr->tsccActivateSlot(slot, dstId, grp);
}
// callback RCON to permit-tg on the specified voice channel // callback RCON to permit-tg on the specified voice channel
if (m_tscc->m_authoritative && m_tscc->m_controlPermitTG) { if (m_tscc->m_authoritative && m_tscc->m_controlPermitTG) {
::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->getRFChData(chNo); ::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->getRFChData(chNo);
@ -854,6 +874,9 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_
RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(), RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(),
HTTP_PUT, PUT_PERMIT_TG, req, m_tscc->m_debug); HTTP_PUT, PUT_PERMIT_TG, req, m_tscc->m_debug);
} }
else {
::LogError(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), failed to permit TG for use, chNo = %u, slot = %u", m_tscc->m_slotNo, chNo, slot);
}
} }
std::unique_ptr<CSBK_TV_GRANT> csbk = new_unique(CSBK_TV_GRANT); std::unique_ptr<CSBK_TV_GRANT> csbk = new_unique(CSBK_TV_GRANT);
@ -871,14 +894,35 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_
csbk->setSrcId(srcId); csbk->setSrcId(srcId);
csbk->setDstId(dstId); csbk->setDstId(dstId);
// transmit group grant // transmit group grant (x2)
writeRF_CSBK(csbk.get()); for (uint8_t i = 0; i < 2U; i++)
writeRF_CSBK(csbk.get());
} }
else { else {
if (!net) { if (!net) {
::ActivityLog("DMR", true, "Slot %u individual grant request from %u to TG %u", m_tscc->m_slotNo, srcId, dstId); ::ActivityLog("DMR", true, "Slot %u individual grant request from %u to TG %u", m_tscc->m_slotNo, srcId, dstId);
} }
// if the channel granted isn't the same as the TSCC; remote activate the payload channel
if (chNo != m_tscc->m_channelNo) {
::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->getRFChData(chNo);
if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) {
json::object req = json::object();
req["dstId"].set<uint32_t>(dstId);
req["slot"].set<uint8_t>(slot);
req["group"].set<bool>(grp);
RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(),
HTTP_PUT, PUT_PERMIT_TG, req, m_tscc->m_debug);
}
else {
::LogError(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), failed to activate payload channel, chNo = %u, slot = %u", m_tscc->m_slotNo, chNo, slot);
}
}
else {
m_slot->m_dmr->tsccActivateSlot(slot, dstId, grp);
}
// callback RCON to permit-tg on the specified voice channel // callback RCON to permit-tg on the specified voice channel
if (m_tscc->m_authoritative && m_tscc->m_controlPermitTG) { if (m_tscc->m_authoritative && m_tscc->m_controlPermitTG) {
::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->getRFChData(chNo); ::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->getRFChData(chNo);
@ -892,6 +936,9 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_
RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(), RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(),
HTTP_PUT, PUT_PERMIT_TG, req, m_tscc->m_debug); HTTP_PUT, PUT_PERMIT_TG, req, m_tscc->m_debug);
} }
else {
::LogError(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), failed to permit TG for use, chNo = %u, slot = %u", m_tscc->m_slotNo, chNo, slot);
}
} }
std::unique_ptr<CSBK_PV_GRANT> csbk = new_unique(CSBK_PV_GRANT); std::unique_ptr<CSBK_PV_GRANT> csbk = new_unique(CSBK_PV_GRANT);
@ -907,8 +954,9 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_
csbk->setSrcId(srcId); csbk->setSrcId(srcId);
csbk->setDstId(dstId); csbk->setDstId(dstId);
// transmit private grant // transmit private grant (x2)
writeRF_CSBK(csbk.get()); for (uint8_t i = 0; i < 2U; i++)
writeRF_CSBK(csbk.get());
} }
return true; return true;
@ -1026,6 +1074,26 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u
::ActivityLog("DMR", true, "Slot %u group grant request from %u to TG %u", m_tscc->m_slotNo, srcId, dstId); ::ActivityLog("DMR", true, "Slot %u group grant request from %u to TG %u", m_tscc->m_slotNo, srcId, dstId);
} }
// if the channel granted isn't the same as the TSCC; remote activate the payload channel
if (chNo != m_tscc->m_channelNo) {
::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->getRFChData(chNo);
if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) {
json::object req = json::object();
req["dstId"].set<uint32_t>(dstId);
req["slot"].set<uint8_t>(slot);
req["group"].set<bool>(grp);
RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(),
HTTP_PUT, PUT_PERMIT_TG, req, m_tscc->m_debug);
}
else {
::LogError(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), failed to activate payload channel, chNo = %u, slot = %u", m_tscc->m_slotNo, chNo, slot);
}
}
else {
m_slot->m_dmr->tsccActivateSlot(slot, dstId, grp);
}
std::unique_ptr<CSBK_TD_GRANT> csbk = new_unique(CSBK_TD_GRANT); std::unique_ptr<CSBK_TD_GRANT> csbk = new_unique(CSBK_TD_GRANT);
csbk->setLogicalCh1(chNo); csbk->setLogicalCh1(chNo);
csbk->setSlotNo(slot - 1U); csbk->setSlotNo(slot - 1U);
@ -1039,14 +1107,35 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u
csbk->setSrcId(srcId); csbk->setSrcId(srcId);
csbk->setDstId(dstId); csbk->setDstId(dstId);
// transmit group grant // transmit group grant (x2)
writeRF_CSBK(csbk.get()); for (uint8_t i = 0; i < 2U; i++)
writeRF_CSBK(csbk.get());
} }
else { else {
if (!net) { if (!net) {
::ActivityLog("DMR", true, "Slot %u individual grant request from %u to TG %u", m_tscc->m_slotNo, srcId, dstId); ::ActivityLog("DMR", true, "Slot %u individual grant request from %u to TG %u", m_tscc->m_slotNo, srcId, dstId);
} }
// if the channel granted isn't the same as the TSCC; remote activate the payload channel
if (chNo != m_tscc->m_channelNo) {
::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->getRFChData(chNo);
if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) {
json::object req = json::object();
req["dstId"].set<uint32_t>(dstId);
req["slot"].set<uint8_t>(slot);
req["group"].set<bool>(grp);
RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(),
HTTP_PUT, PUT_PERMIT_TG, req, m_tscc->m_debug);
}
else {
::LogError(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), failed to activate payload channel, chNo = %u, slot = %u", m_tscc->m_slotNo, chNo, slot);
}
}
else {
m_slot->m_dmr->tsccActivateSlot(slot, dstId, grp);
}
std::unique_ptr<CSBK_PD_GRANT> csbk = new_unique(CSBK_PD_GRANT); std::unique_ptr<CSBK_PD_GRANT> csbk = new_unique(CSBK_PD_GRANT);
csbk->setLogicalCh1(chNo); csbk->setLogicalCh1(chNo);
csbk->setSlotNo(slot - 1U); csbk->setSlotNo(slot - 1U);
@ -1060,8 +1149,9 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u
csbk->setSrcId(srcId); csbk->setSrcId(srcId);
csbk->setDstId(dstId); csbk->setDstId(dstId);
// transmit private grant // transmit private grant (x2)
writeRF_CSBK(csbk.get()); for (uint8_t i = 0; i < 2U; i++)
writeRF_CSBK(csbk.get());
} }
return true; return true;

@ -148,6 +148,8 @@ bool Data::process(uint8_t* data, uint32_t len)
LogMessage(LOG_RF, "DMR Slot %u, total frames: %d, total bits: %d, errors: %d, BER: %.4f%%", LogMessage(LOG_RF, "DMR Slot %u, total frames: %d, total bits: %d, errors: %d, BER: %.4f%%",
m_slot->m_slotNo, m_slot->m_rfFrames, m_slot->m_rfBits, m_slot->m_rfErrs, float(m_slot->m_rfErrs * 100U) / float(m_slot->m_rfBits)); m_slot->m_slotNo, m_slot->m_rfFrames, m_slot->m_rfBits, m_slot->m_rfErrs, float(m_slot->m_rfErrs * 100U) / float(m_slot->m_rfBits));
m_slot->m_dmr->tsccClearActivatedSlot(m_slot->m_slotNo);
if (m_slot->m_rfTimeout) { if (m_slot->m_rfTimeout) {
m_slot->writeEndRF(); m_slot->writeEndRF();
return false; return false;
@ -371,6 +373,8 @@ void Data::processNetwork(const data::Data& dmrData)
::ActivityLog("DMR", false, "Slot %u network end of voice transmission, %.1f seconds, %u%% packet loss, BER: %.1f%%", ::ActivityLog("DMR", false, "Slot %u network end of voice transmission, %.1f seconds, %u%% packet loss, BER: %.1f%%",
m_slot->m_slotNo, float(m_slot->m_netFrames) / 16.667F, (m_slot->m_netLost * 100U) / m_slot->m_netFrames, float(m_slot->m_netErrs * 100U) / float(m_slot->m_netBits)); m_slot->m_slotNo, float(m_slot->m_netFrames) / 16.667F, (m_slot->m_netLost * 100U) / m_slot->m_netFrames, float(m_slot->m_netErrs * 100U) / float(m_slot->m_netBits));
m_slot->m_dmr->tsccClearActivatedSlot(m_slot->m_slotNo);
m_slot->writeEndNet(); m_slot->writeEndNet();
} }
else if (dataType == DT_DATA_HEADER) { else if (dataType == DT_DATA_HEADER) {

@ -291,6 +291,7 @@ void RESTAPI::initializeEndpoints()
m_dispatcher.match(PUT_DMR_RID).put(REST_API_BIND(RESTAPI::restAPI_PutDMRRID, this)); m_dispatcher.match(PUT_DMR_RID).put(REST_API_BIND(RESTAPI::restAPI_PutDMRRID, this));
m_dispatcher.match(GET_DMR_CC_DEDICATED).get(REST_API_BIND(RESTAPI::restAPI_GetDMRCCEnable, this)); m_dispatcher.match(GET_DMR_CC_DEDICATED).get(REST_API_BIND(RESTAPI::restAPI_GetDMRCCEnable, this));
m_dispatcher.match(GET_DMR_CC_BCAST).get(REST_API_BIND(RESTAPI::restAPI_GetDMRCCBroadcast, this)); m_dispatcher.match(GET_DMR_CC_BCAST).get(REST_API_BIND(RESTAPI::restAPI_GetDMRCCBroadcast, this));
m_dispatcher.match(PUT_DMR_TSCC_PAYLOAD_ACT).put(REST_API_BIND(RESTAPI::restAPI_PutTSCCPayloadActivate, this));
/* /*
** Project 25 ** Project 25
@ -1378,6 +1379,80 @@ void RESTAPI::restAPI_GetDMRCCBroadcast(const HTTPPayload& request, HTTPPayload&
#endif // defined(ENABLE_DMR) #endif // defined(ENABLE_DMR)
} }
/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="reply"></param>
/// <param name="match"></param>
void RESTAPI::restAPI_PutTSCCPayloadActivate(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match)
{
if (!validateAuth(request, reply)) {
return;
}
json::object req = json::object();
if (!parseRequestBody(request, reply, req)) {
return;
}
#if defined(ENABLE_DMR)
if (m_dmr == nullptr) {
errorPayload(reply, "DMR mode is not enabled", HTTPPayload::SERVICE_UNAVAILABLE);
return;
}
// validate destination ID is a integer within the JSON blob
if (!req["slot"].is<uint8_t>()) {
errorPayload(reply, "slot was not valid");
return;
}
// validate clear flag is a boolean within the JSON blob
if (!req["clear"].is<bool>()) {
errorPayload(reply, "clear flag was not valid");
return;
}
uint8_t slot = req["slot"].get<uint8_t>();
bool clear = req["clear"].get<bool>();
if (slot == 0U && slot >= 3U) {
errorPayload(reply, "invalid DMR slot number (slot == 0 or slot > 3)");
return;
}
errorPayload(reply, "OK", HTTPPayload::OK);
if (clear) {
m_dmr->tsccClearActivatedSlot(slot);
}
else {
// validate destination ID is a integer within the JSON blob
if (!req["dstId"].is<uint32_t>()) {
errorPayload(reply, "destination ID was not valid");
return;
}
// validate group flag is a boolean within the JSON blob
if (!req["group"].is<bool>()) {
errorPayload(reply, "group flag was not valid");
return;
}
uint32_t dstId = req["dstId"].get<uint32_t>();
bool group = req["group"].get<bool>();
if (dstId == 0U) {
errorPayload(reply, "destination ID was not valid");
return;
}
m_dmr->tsccActivateSlot(slot, dstId, group);
}
#else
errorPayload(reply, "DMR operations are unavailable", HTTPPayload::SERVICE_UNAVAILABLE);
#endif // defined(ENABLE_DMR)
}
/* /*
** Project 25 ** Project 25
*/ */

@ -152,6 +152,8 @@ private:
void restAPI_GetDMRCCEnable(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); void restAPI_GetDMRCCEnable(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match);
/// <summary></summary> /// <summary></summary>
void restAPI_GetDMRCCBroadcast(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); void restAPI_GetDMRCCBroadcast(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match);
/// <summary></summary>
void restAPI_PutTSCCPayloadActivate(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match);
/* /*
** Project 25 ** Project 25

@ -76,6 +76,7 @@
#define PUT_DMR_RID "/dmr/rid" #define PUT_DMR_RID "/dmr/rid"
#define GET_DMR_CC_DEDICATED "/dmr/cc-enable" #define GET_DMR_CC_DEDICATED "/dmr/cc-enable"
#define GET_DMR_CC_BCAST "/dmr/cc-broadcast" #define GET_DMR_CC_BCAST "/dmr/cc-broadcast"
#define PUT_DMR_TSCC_PAYLOAD_ACT "/dmr/payload-activate"
#define GET_P25_CC "/p25/cc" #define GET_P25_CC "/p25/cc"
#define GET_P25_CC_FALLBACK_BASE "/p25/cc-fallback/" #define GET_P25_CC_FALLBACK_BASE "/p25/cc-fallback/"

@ -522,6 +522,9 @@ bool Trunk::writeRF_Message_Grant(uint32_t srcId, uint32_t dstId, uint8_t servic
RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(), RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(),
HTTP_PUT, PUT_PERMIT_TG, req, m_nxdn->m_debug); HTTP_PUT, PUT_PERMIT_TG, req, m_nxdn->m_debug);
} }
else {
::LogError((net) ? LOG_NET : LOG_RF, "NXDN, " NXDN_RTCH_MSG_TYPE_VCALL_RESP ", failed to permit TG for use, chNo = %u", chNo);
}
} }
std::unique_ptr<rcch::MESSAGE_TYPE_VCALL_CONN> rcch = new_unique(rcch::MESSAGE_TYPE_VCALL_CONN); std::unique_ptr<rcch::MESSAGE_TYPE_VCALL_CONN> rcch = new_unique(rcch::MESSAGE_TYPE_VCALL_CONN);

@ -2247,6 +2247,9 @@ bool Trunk::writeRF_TSDU_Grant(uint32_t srcId, uint32_t dstId, uint8_t serviceOp
RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(), RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(),
HTTP_PUT, PUT_PERMIT_TG, req, m_p25->m_debug); HTTP_PUT, PUT_PERMIT_TG, req, m_p25->m_debug);
} }
else {
::LogError((net) ? LOG_NET : LOG_RF, P25_TSDU_STR ", TSBK_IOSP_GRP_VCH (Group Voice Channel Grant), failed to permit TG for use, chNo = %u", chNo);
}
} }
std::unique_ptr<IOSP_GRP_VCH> iosp = new_unique(IOSP_GRP_VCH); std::unique_ptr<IOSP_GRP_VCH> iosp = new_unique(IOSP_GRP_VCH);
@ -2284,6 +2287,9 @@ bool Trunk::writeRF_TSDU_Grant(uint32_t srcId, uint32_t dstId, uint8_t serviceOp
RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(), RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(),
HTTP_PUT, PUT_PERMIT_TG, req, m_p25->m_debug); HTTP_PUT, PUT_PERMIT_TG, req, m_p25->m_debug);
} }
else {
::LogError((net) ? LOG_NET : LOG_RF, P25_TSDU_STR ", TSBK_IOSP_GRP_VCH (Group Voice Channel Grant), failed to permit TG for use, chNo = %u", chNo);
}
} }
std::unique_ptr<IOSP_UU_VCH> iosp = new_unique(IOSP_UU_VCH); std::unique_ptr<IOSP_UU_VCH> iosp = new_unique(IOSP_UU_VCH);

@ -54,6 +54,7 @@ using namespace network::rest::http;
#define ERRNO_SOCK_OPEN 98 #define ERRNO_SOCK_OPEN 98
#define ERRNO_BAD_API_RESPONSE 97 #define ERRNO_BAD_API_RESPONSE 97
#define ERRNO_API_CALL_TIMEOUT 96 #define ERRNO_API_CALL_TIMEOUT 96
#define ERRNO_INTERNAL_ERROR 100
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Static Class Members // Static Class Members
@ -165,76 +166,82 @@ int RESTClient::send(const std::string& address, uint32_t port, const std::strin
typedef network::rest::BasicRequestDispatcher<network::rest::http::HTTPPayload, network::rest::http::HTTPPayload> RESTDispatcherType; typedef network::rest::BasicRequestDispatcher<network::rest::http::HTTPPayload, network::rest::http::HTTPPayload> RESTDispatcherType;
RESTDispatcherType m_dispatcher(RESTClient::responseHandler); RESTDispatcherType m_dispatcher(RESTClient::responseHandler);
HTTPClient<RESTDispatcherType> client(address, port); try {
if (!client.open()) HTTPClient<RESTDispatcherType> client(address, port);
return ERRNO_SOCK_OPEN; if (!client.open())
client.setHandler(m_dispatcher); return ERRNO_SOCK_OPEN;
client.setHandler(m_dispatcher);
// generate password SHA hash // generate password SHA hash
size_t size = password.size(); size_t size = password.size();
uint8_t* in = new uint8_t[size]; uint8_t* in = new uint8_t[size];
for (size_t i = 0U; i < size; i++) for (size_t i = 0U; i < size; i++)
in[i] = password.at(i); in[i] = password.at(i);
uint8_t out[32U]; uint8_t out[32U];
::memset(out, 0x00U, 32U); ::memset(out, 0x00U, 32U);
edac::SHA256 sha256; edac::SHA256 sha256;
sha256.buffer(in, (uint32_t)(size), out); sha256.buffer(in, (uint32_t)(size), out);
std::stringstream ss; std::stringstream ss;
ss << std::hex; ss << std::hex;
for (uint8_t i = 0; i < 32U; i++) for (uint8_t i = 0; i < 32U; i++)
ss << std::setw(2) << std::setfill('0') << (int)out[i]; ss << std::setw(2) << std::setfill('0') << (int)out[i];
std::string hash = ss.str(); std::string hash = ss.str();
// send authentication API // send authentication API
json::object request = json::object(); json::object request = json::object();
request["auth"].set<std::string>(hash); request["auth"].set<std::string>(hash);
HTTPPayload httpPayload = HTTPPayload::requestPayload(HTTP_PUT, "/auth"); HTTPPayload httpPayload = HTTPPayload::requestPayload(HTTP_PUT, "/auth");
httpPayload.payload(request); httpPayload.payload(request);
client.request(httpPayload); client.request(httpPayload);
// wait for response and parse // wait for response and parse
if (wait()) { if (wait()) {
client.close(); client.close();
return ERRNO_API_CALL_TIMEOUT; return ERRNO_API_CALL_TIMEOUT;
} }
json::object rsp = json::object(); json::object rsp = json::object();
if (!parseResponseBody(m_response, rsp)) { if (!parseResponseBody(m_response, rsp)) {
return ERRNO_BAD_API_RESPONSE; return ERRNO_BAD_API_RESPONSE;
} }
std::string token = ""; std::string token = "";
int status = rsp["status"].get<int>(); int status = rsp["status"].get<int>();
if (status == HTTPPayload::StatusType::OK) { if (status == HTTPPayload::StatusType::OK) {
token = rsp["token"].get<std::string>(); token = rsp["token"].get<std::string>();
} }
else { else {
client.close(); client.close();
return ERRNO_BAD_API_RESPONSE; return ERRNO_BAD_API_RESPONSE;
} }
// send actual API request // send actual API request
httpPayload = HTTPPayload::requestPayload(method, endpoint); httpPayload = HTTPPayload::requestPayload(method, endpoint);
httpPayload.headers.add("X-DVM-Auth-Token", token); httpPayload.headers.add("X-DVM-Auth-Token", token);
httpPayload.payload(payload); httpPayload.payload(payload);
client.request(httpPayload); client.request(httpPayload);
// wait for response and parse // wait for response and parse
if (wait()) { if (wait()) {
client.close(); client.close();
return ERRNO_API_CALL_TIMEOUT; return ERRNO_API_CALL_TIMEOUT;
} }
fprintf(stdout, "%s\r\n", m_response.content.c_str()); fprintf(stdout, "%s\r\n", m_response.content.c_str());
client.close(); client.close();
}
catch (std::exception&) {
return ERRNO_INTERNAL_ERROR;
}
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

Loading…
Cancel
Save

Powered by TurnKey Linux.