diff --git a/src/dmr/Control.cpp b/src/dmr/Control.cpp index ff12b93d..c8572a7c 100644 --- a/src/dmr/Control.cpp +++ b/src/dmr/Control.cpp @@ -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::setAlohaConfig(nRandWait, backOff); - m_tsccSlotNo = (uint8_t)control["slot"].as(0U); - switch (m_tsccSlotNo) { - case 1U: - m_slot1->setTSCC(enableTSCC, dedicatedTSCC); - m_slot1->setControlPermitTG(m_controlPermitTG); - //m_slot2->setTSCCPayload(true); // this is not the correct way this should be done - break; - case 2U: - m_slot2->setTSCC(enableTSCC, dedicatedTSCC); - m_slot2->setControlPermitTG(m_controlPermitTG); - //m_slot1->setTSCCPayload(true); // this is not the correct way this should be done - break; - default: - LogError(LOG_DMR, "DMR, invalid slot, TSCC disabled, slotNo = %u", m_tsccSlotNo); - break; + if (enableTSCC) { + m_tsccSlotNo = (uint8_t)control["slot"].as(0U); + switch (m_tsccSlotNo) { + case 1U: + m_slot1->setTSCC(enableTSCC, dedicatedTSCC); + m_slot1->setControlPermitTG(m_controlPermitTG); + break; + case 2U: + m_slot2->setTSCC(enableTSCC, dedicatedTSCC); + m_slot2->setControlPermitTG(m_controlPermitTG); + break; + default: + LogError(LOG_DMR, "DMR, invalid slot, TSCC disabled, slotNo = %u", m_tsccSlotNo); + break; + } } uint32_t silenceThreshold = dmrProtocol["silenceThreshold"].as(dmr::DEFAULT_SILENCE_THRESHOLD); @@ -398,6 +398,55 @@ Slot* Control::getTSCCSlot() const } } +/// +/// Helper to payload activate the slot carrying granted payload traffic. +/// +/// DMR slot number. +/// +/// +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; + } +} + +/// +/// Helper to clear an activated payload slot. +/// +/// DMR slot number. +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; + } +} + /// /// Helper to write a DMR extended function packet on the RF interface. /// diff --git a/src/dmr/Control.h b/src/dmr/Control.h index d9d79c10..5ff858c1 100644 --- a/src/dmr/Control.h +++ b/src/dmr/Control.h @@ -99,6 +99,10 @@ namespace dmr /// Helper to return the slot carrying the TSCC. Slot* getTSCCSlot() const; + /// Helper to payload activate the slot carrying granted payload traffic. + void tsccActivateSlot(uint32_t slotNo, uint32_t dstId, bool group); + /// Helper to clear an activated payload slot. + void tsccClearActivatedSlot(uint32_t slotNo); /// Helper to write a DMR extended function packet on the RF interface. void writeRF_Ext_Func(uint32_t slotNo, uint32_t func, uint32_t arg, uint32_t dstId); diff --git a/src/dmr/Slot.cpp b/src/dmr/Slot.cpp index 9e9599a3..a7ee5379 100644 --- a/src/dmr/Slot.cpp +++ b/src/dmr/Slot.cpp @@ -162,6 +162,10 @@ Slot::Slot(uint32_t slotNo, uint32_t timeout, uint32_t tgHang, uint32_t queueSiz m_ccPrevRunning(false), m_ccHalted(false), m_enableTSCC(false), + m_dedicatedTSCC(false), + m_tsccActivated(false), + m_tsccPayloadDstId(0U), + m_tsccPayloadGroup(false), m_verbose(verbose), m_debug(debug) { @@ -288,12 +292,11 @@ bool Slot::processFrame(uint8_t *data, uint32_t len) if ((dataSync || voiceSync) && m_rfState != RS_RF_LISTENING) m_rfTGHang.start(); - // write and process TSCC CSBKs and short LC - if (m_enableTSCC && m_dedicatedTSCC) - { - if (dataSync) { - 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) { case DT_CSBK: @@ -301,13 +304,9 @@ bool Slot::processFrame(uint8_t *data, uint32_t len) default: break; } - } - - return false; - } - if (dataSync) { - uint8_t dataType = data[1U] & 0x0FU; + return false; + } switch (dataType) { @@ -375,6 +374,12 @@ void Slot::processNetwork(const data::Data& dmrData) 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) { case DT_CSBK: @@ -464,8 +469,36 @@ void Slot::clock() } } - if (m_tsccPayloadSlot) { - setShortLC_Payload(m_siteData, m_tsccCnt); + // never allow the TSCC to become payload activated + 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); diff --git a/src/dmr/Slot.h b/src/dmr/Slot.h index 45b76565..183d4d90 100644 --- a/src/dmr/Slot.h +++ b/src/dmr/Slot.h @@ -106,7 +106,7 @@ namespace dmr /// Helper to enable and configure TSCC support for this slot. void setTSCC(bool enable, bool dedicated); /// Sets a flag indicating whether the slot is a TSCC payload slot. - void setTSCCPayload(bool payload) { m_tsccPayloadSlot = payload; } + void setTSCCActivated(uint32_t dstId, bool group) { m_tsccActivated = true; m_tsccPayloadDstId = dstId; m_tsccPayloadGroup = group; } /// Sets a flag indicating whether the DMR control channel can send permit-tg to voice channels. void setControlPermitTG(bool controlPermitTG) { m_controlPermitTG = controlPermitTG; } /// Helper to set the voice error silence threshold. @@ -191,7 +191,10 @@ namespace dmr bool m_enableTSCC; bool m_dedicatedTSCC; - bool m_tsccPayloadSlot; + + bool m_tsccActivated; + uint32_t m_tsccPayloadDstId; + bool m_tsccPayloadGroup; bool m_controlPermitTG; @@ -263,6 +266,9 @@ namespace dmr /// Helper to write control channel packet data. void writeRF_ControlData(uint16_t frameCnt, uint8_t n); + /// Clears the flag indicating whether the slot is a TSCC payload slot. + void clearTSCCActivated() { m_tsccActivated = false; m_tsccPayloadDstId = 0U; m_tsccPayloadGroup = false; } + /// Helper to set the DMR short LC. static void setShortLC(uint32_t slotNo, uint32_t id, uint8_t flco = FLCO_GROUP, bool voice = true); /// Helper to set the DMR short LC for TSCC. diff --git a/src/dmr/packet/ControlSignaling.cpp b/src/dmr/packet/ControlSignaling.cpp index 75b0b462..4b02432a 100644 --- a/src/dmr/packet/ControlSignaling.cpp +++ b/src/dmr/packet/ControlSignaling.cpp @@ -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); } + // 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(dstId); + req["slot"].set(slot); + req["group"].set(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 if (m_tscc->m_authoritative && m_tscc->m_controlPermitTG) { ::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(), 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 = 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->setDstId(dstId); - // transmit group grant - writeRF_CSBK(csbk.get()); + // transmit group grant (x2) + for (uint8_t i = 0; i < 2U; i++) + writeRF_CSBK(csbk.get()); } else { if (!net) { ::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(dstId); + req["slot"].set(slot); + req["group"].set(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 if (m_tscc->m_authoritative && m_tscc->m_controlPermitTG) { ::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(), 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 = 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->setDstId(dstId); - // transmit private grant - writeRF_CSBK(csbk.get()); + // transmit private grant (x2) + for (uint8_t i = 0; i < 2U; i++) + writeRF_CSBK(csbk.get()); } 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); } + // 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(dstId); + req["slot"].set(slot); + req["group"].set(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 = new_unique(CSBK_TD_GRANT); csbk->setLogicalCh1(chNo); 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->setDstId(dstId); - // transmit group grant - writeRF_CSBK(csbk.get()); + // transmit group grant (x2) + for (uint8_t i = 0; i < 2U; i++) + writeRF_CSBK(csbk.get()); } else { if (!net) { ::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(dstId); + req["slot"].set(slot); + req["group"].set(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 = new_unique(CSBK_PD_GRANT); csbk->setLogicalCh1(chNo); 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->setDstId(dstId); - // transmit private grant - writeRF_CSBK(csbk.get()); + // transmit private grant (x2) + for (uint8_t i = 0; i < 2U; i++) + writeRF_CSBK(csbk.get()); } return true; diff --git a/src/dmr/packet/Data.cpp b/src/dmr/packet/Data.cpp index d7bf4767..8aa5bd80 100644 --- a/src/dmr/packet/Data.cpp +++ b/src/dmr/packet/Data.cpp @@ -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%%", 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) { m_slot->writeEndRF(); 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%%", 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(); } else if (dataType == DT_DATA_HEADER) { diff --git a/src/network/RESTAPI.cpp b/src/network/RESTAPI.cpp index 69137133..fe563561 100644 --- a/src/network/RESTAPI.cpp +++ b/src/network/RESTAPI.cpp @@ -291,6 +291,7 @@ void RESTAPI::initializeEndpoints() 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_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 @@ -1378,6 +1379,80 @@ void RESTAPI::restAPI_GetDMRCCBroadcast(const HTTPPayload& request, HTTPPayload& #endif // defined(ENABLE_DMR) } +/// +/// +/// +/// +/// +/// +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()) { + errorPayload(reply, "slot was not valid"); + return; + } + + // validate clear flag is a boolean within the JSON blob + if (!req["clear"].is()) { + errorPayload(reply, "clear flag was not valid"); + return; + } + + uint8_t slot = req["slot"].get(); + bool clear = req["clear"].get(); + + 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()) { + errorPayload(reply, "destination ID was not valid"); + return; + } + + // validate group flag is a boolean within the JSON blob + if (!req["group"].is()) { + errorPayload(reply, "group flag was not valid"); + return; + } + + uint32_t dstId = req["dstId"].get(); + bool group = req["group"].get(); + + 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 */ diff --git a/src/network/RESTAPI.h b/src/network/RESTAPI.h index 93ff7a26..08c32767 100644 --- a/src/network/RESTAPI.h +++ b/src/network/RESTAPI.h @@ -152,6 +152,8 @@ private: void restAPI_GetDMRCCEnable(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); /// void restAPI_GetDMRCCBroadcast(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + /// + void restAPI_PutTSCCPayloadActivate(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); /* ** Project 25 diff --git a/src/network/RESTDefines.h b/src/network/RESTDefines.h index 77d47580..826e56fc 100644 --- a/src/network/RESTDefines.h +++ b/src/network/RESTDefines.h @@ -76,6 +76,7 @@ #define PUT_DMR_RID "/dmr/rid" #define GET_DMR_CC_DEDICATED "/dmr/cc-enable" #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_FALLBACK_BASE "/p25/cc-fallback/" diff --git a/src/nxdn/packet/Trunk.cpp b/src/nxdn/packet/Trunk.cpp index 2d67edce..37461b2f 100644 --- a/src/nxdn/packet/Trunk.cpp +++ b/src/nxdn/packet/Trunk.cpp @@ -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(), 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 = new_unique(rcch::MESSAGE_TYPE_VCALL_CONN); diff --git a/src/p25/packet/Trunk.cpp b/src/p25/packet/Trunk.cpp index e5557fe7..72d48328 100644 --- a/src/p25/packet/Trunk.cpp +++ b/src/p25/packet/Trunk.cpp @@ -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(), 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 = 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(), 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 = new_unique(IOSP_UU_VCH); diff --git a/src/remote/RESTClient.cpp b/src/remote/RESTClient.cpp index c2a2dbf2..84f391a2 100644 --- a/src/remote/RESTClient.cpp +++ b/src/remote/RESTClient.cpp @@ -54,6 +54,7 @@ using namespace network::rest::http; #define ERRNO_SOCK_OPEN 98 #define ERRNO_BAD_API_RESPONSE 97 #define ERRNO_API_CALL_TIMEOUT 96 +#define ERRNO_INTERNAL_ERROR 100 // --------------------------------------------------------------------------- // Static Class Members @@ -165,76 +166,82 @@ int RESTClient::send(const std::string& address, uint32_t port, const std::strin typedef network::rest::BasicRequestDispatcher RESTDispatcherType; RESTDispatcherType m_dispatcher(RESTClient::responseHandler); - HTTPClient client(address, port); - if (!client.open()) - return ERRNO_SOCK_OPEN; - client.setHandler(m_dispatcher); + try { + HTTPClient client(address, port); + if (!client.open()) + return ERRNO_SOCK_OPEN; + client.setHandler(m_dispatcher); - // generate password SHA hash - size_t size = password.size(); + // generate password SHA hash + size_t size = password.size(); - uint8_t* in = new uint8_t[size]; - for (size_t i = 0U; i < size; i++) - in[i] = password.at(i); + uint8_t* in = new uint8_t[size]; + for (size_t i = 0U; i < size; i++) + in[i] = password.at(i); - uint8_t out[32U]; - ::memset(out, 0x00U, 32U); + uint8_t out[32U]; + ::memset(out, 0x00U, 32U); - edac::SHA256 sha256; - sha256.buffer(in, (uint32_t)(size), out); + edac::SHA256 sha256; + sha256.buffer(in, (uint32_t)(size), out); - std::stringstream ss; - ss << std::hex; + std::stringstream ss; + ss << std::hex; - for (uint8_t i = 0; i < 32U; i++) - ss << std::setw(2) << std::setfill('0') << (int)out[i]; + for (uint8_t i = 0; i < 32U; i++) + ss << std::setw(2) << std::setfill('0') << (int)out[i]; - std::string hash = ss.str(); + std::string hash = ss.str(); - // send authentication API - json::object request = json::object(); - request["auth"].set(hash); + // send authentication API + json::object request = json::object(); + request["auth"].set(hash); - HTTPPayload httpPayload = HTTPPayload::requestPayload(HTTP_PUT, "/auth"); - httpPayload.payload(request); - client.request(httpPayload); + HTTPPayload httpPayload = HTTPPayload::requestPayload(HTTP_PUT, "/auth"); + httpPayload.payload(request); + client.request(httpPayload); - // wait for response and parse - if (wait()) { - client.close(); - return ERRNO_API_CALL_TIMEOUT; - } + // wait for response and parse + if (wait()) { + client.close(); + return ERRNO_API_CALL_TIMEOUT; + } - json::object rsp = json::object(); - if (!parseResponseBody(m_response, rsp)) { - return ERRNO_BAD_API_RESPONSE; - } + json::object rsp = json::object(); + if (!parseResponseBody(m_response, rsp)) { + return ERRNO_BAD_API_RESPONSE; + } - std::string token = ""; - int status = rsp["status"].get(); - if (status == HTTPPayload::StatusType::OK) { - token = rsp["token"].get(); - } - else { - client.close(); - return ERRNO_BAD_API_RESPONSE; - } + std::string token = ""; + int status = rsp["status"].get(); + if (status == HTTPPayload::StatusType::OK) { + token = rsp["token"].get(); + } + else { + client.close(); + return ERRNO_BAD_API_RESPONSE; + } - // send actual API request - httpPayload = HTTPPayload::requestPayload(method, endpoint); - httpPayload.headers.add("X-DVM-Auth-Token", token); - httpPayload.payload(payload); - client.request(httpPayload); + // send actual API request + httpPayload = HTTPPayload::requestPayload(method, endpoint); + httpPayload.headers.add("X-DVM-Auth-Token", token); + httpPayload.payload(payload); + client.request(httpPayload); - // wait for response and parse - if (wait()) { - client.close(); - return ERRNO_API_CALL_TIMEOUT; - } + // wait for response and parse + if (wait()) { + client.close(); + 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; }