REST -> RPC Migration (#84)
* migrate away from REST API for inter-dvmhost operations towards a custom UDP RPC framework; * replace config parameters for REST API in some places with properly named RPC; swap peer Network, FNE DiagNetwork and FNENetwork over from if-else-if ladders to switch statements (switch statements perform logically better after compilation because the compiler tends to optimize these into jump-tables which execute faster); * continued work on inter-dvmhost REST to RPC transition; * update build bumper for R04G20 to R04G21; * cleanup config file; * clean up doc/commenting;pull/85/head
parent
6b5c61009a
commit
3da4eb2d40
@ -0,0 +1,303 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* Digital Voice Modem - Common Library
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2025 Bryan Biedenkapp, N2PLL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include "Defines.h"
|
||||||
|
#include "common/edac/CRC.h"
|
||||||
|
#include "common/edac/SHA256.h"
|
||||||
|
#include "common/network/RPCHeader.h"
|
||||||
|
#include "common/network/json/json.h"
|
||||||
|
#include "common/Log.h"
|
||||||
|
#include "common/Utils.h"
|
||||||
|
#include "network/RPC.h"
|
||||||
|
|
||||||
|
using namespace network;
|
||||||
|
using namespace network::frame;
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Public Class Members
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/* Initializes a new instance of the RPC class. */
|
||||||
|
|
||||||
|
RPC::RPC(const std::string& address, uint16_t port, uint16_t localPort, const std::string& password, bool debug) :
|
||||||
|
m_address(address),
|
||||||
|
m_port(port),
|
||||||
|
m_debug(debug),
|
||||||
|
m_socket(nullptr),
|
||||||
|
m_frameQueue(nullptr),
|
||||||
|
m_password(password),
|
||||||
|
m_handlers()
|
||||||
|
{
|
||||||
|
assert(!address.empty());
|
||||||
|
assert(port > 0U);
|
||||||
|
assert(!password.empty());
|
||||||
|
|
||||||
|
m_socket = new udp::Socket(address, port);
|
||||||
|
m_frameQueue = new RawFrameQueue(m_socket, debug);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Finalizes a instance of the RPC class. */
|
||||||
|
|
||||||
|
RPC::~RPC()
|
||||||
|
{
|
||||||
|
if (m_frameQueue != nullptr) {
|
||||||
|
delete m_frameQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_socket != nullptr) {
|
||||||
|
delete m_socket;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Updates the timer by the passed number of milliseconds. */
|
||||||
|
|
||||||
|
void RPC::clock(uint32_t ms)
|
||||||
|
{
|
||||||
|
sockaddr_storage address;
|
||||||
|
uint32_t addrLen;
|
||||||
|
|
||||||
|
frame::RPCHeader rpcHeader;
|
||||||
|
int length = 0U;
|
||||||
|
|
||||||
|
// read message
|
||||||
|
UInt8Array buffer = m_frameQueue->read(length, address, addrLen);
|
||||||
|
uint8_t* raw = buffer.get();
|
||||||
|
if (length > 0) {
|
||||||
|
if (length < RPC_HEADER_LENGTH_BYTES) {
|
||||||
|
LogError(LOG_NET, "RPC::clock(), message received from network is malformed! %u bytes != %u bytes",
|
||||||
|
RPC_HEADER_LENGTH_BYTES, length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// decode RTP header
|
||||||
|
if (!rpcHeader.decode(buffer.get())) {
|
||||||
|
LogError(LOG_NET, "RPC::clock(), invalid RPC packet received from network");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_debug) {
|
||||||
|
LogDebugEx(LOG_NET, "RPC::clock()", "RPC, func = $%04X, messageLength = %u", rpcHeader.getFunction(), rpcHeader.getMessageLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy message
|
||||||
|
uint32_t messageLength = rpcHeader.getMessageLength();
|
||||||
|
UInt8Array message = std::unique_ptr<uint8_t[]>(new uint8_t[messageLength]);
|
||||||
|
::memcpy(message.get(), raw + RPC_HEADER_LENGTH_BYTES, messageLength);
|
||||||
|
|
||||||
|
uint16_t calc = edac::CRC::createCRC16(message.get(), messageLength * 8U);
|
||||||
|
if (m_debug) {
|
||||||
|
LogDebugEx(LOG_NET, "RPC::clock()", "RPC, calc = $%04X, crc = $%04X", calc, rpcHeader.getCRC());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (calc != rpcHeader.getCRC()) {
|
||||||
|
LogError(LOG_NET, "RPC::clock(), failed CRC CCITT-162 check");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse JSON body
|
||||||
|
std::string content = std::string((char*)message.get());
|
||||||
|
|
||||||
|
json::value v;
|
||||||
|
std::string err = json::parse(v, content);
|
||||||
|
if (!err.empty()) {
|
||||||
|
LogError(LOG_NET, "RPC::clock(), invalid RPC JSON payload, %s", err.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure parsed JSON is an object
|
||||||
|
if (!v.is<json::object>()) {
|
||||||
|
LogError(LOG_NET, "RPC::clock(), invalid RPC JSON payload, request was not a JSON object");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
json::object request = v.get<json::object>();
|
||||||
|
json::object response;
|
||||||
|
|
||||||
|
// find RPC function callback
|
||||||
|
if (m_handlers.find(rpcHeader.getFunction()) != m_handlers.end()) {
|
||||||
|
m_handlers[rpcHeader.getFunction()](request, response);
|
||||||
|
|
||||||
|
// remove the reply handler (these should be temporary)
|
||||||
|
if ((rpcHeader.getFunction() & RPC_REPLY_FUNC) == RPC_REPLY_FUNC) {
|
||||||
|
m_handlers.erase(rpcHeader.getFunction());
|
||||||
|
} else {
|
||||||
|
reply(rpcHeader.getFunction(), response, address, addrLen);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bool isReply = (rpcHeader.getFunction() & RPC_REPLY_FUNC) == RPC_REPLY_FUNC;
|
||||||
|
if (isReply) {
|
||||||
|
if (!request["status"].is<int>()) {
|
||||||
|
::LogError(LOG_NET, "RPC %s:%u, invalid RPC response", udp::Socket::address(address).c_str(), udp::Socket::port(address));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int status = request["status"].get<int>();
|
||||||
|
if (status != network::RPC::OK) {
|
||||||
|
if (request["message"].is<std::string>()) {
|
||||||
|
std::string retMsg = request["message"].get<std::string>();
|
||||||
|
::LogError(LOG_NET, "RPC %s:%u failed, %s", udp::Socket::address(address).c_str(), udp::Socket::port(address), retMsg.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
LogWarning(LOG_NET, "RPC::clock(), ignoring unhandled function, func = $%04X, reply = %u", rpcHeader.getFunction() & 0x3FFFU, isReply);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Writes an RPC request to the network. */
|
||||||
|
|
||||||
|
bool RPC::req(uint16_t func, const json::object& request, RPCType reply, std::string address, uint16_t port)
|
||||||
|
{
|
||||||
|
sockaddr_storage addr;
|
||||||
|
uint32_t addrLen = 0U;
|
||||||
|
if (udp::Socket::lookup(address, port, addr, addrLen) == 0) {
|
||||||
|
return req(func, request, reply, addr, addrLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Writes an RPC request to the network. */
|
||||||
|
|
||||||
|
bool RPC::req(uint16_t func, const json::object& request, RPCType reply, sockaddr_storage& address, uint32_t addrLen)
|
||||||
|
{
|
||||||
|
json::value v = json::value(request);
|
||||||
|
std::string json = v.serialize();
|
||||||
|
|
||||||
|
// generate RPC header
|
||||||
|
RPCHeader header = RPCHeader();
|
||||||
|
header.setFunction(func & 0x3FFFU);
|
||||||
|
header.setMessageLength(json.length() + 1U);
|
||||||
|
|
||||||
|
// generate message
|
||||||
|
CharArray __message = std::make_unique<char[]>(json.length() + 1U);
|
||||||
|
char* message = __message.get();
|
||||||
|
|
||||||
|
::snprintf(message, json.length() + 1U, "%s", json.c_str());
|
||||||
|
|
||||||
|
uint16_t crc = edac::CRC::createCRC16((uint8_t*)message, (json.length() + 1U) * 8U);
|
||||||
|
if (m_debug) {
|
||||||
|
LogDebugEx(LOG_NET, "RPC::req()", "RPC, crc = $%04X", crc);
|
||||||
|
}
|
||||||
|
|
||||||
|
header.setCRC(crc);
|
||||||
|
|
||||||
|
// generate RPC message
|
||||||
|
UInt8Array __buffer = std::make_unique<uint8_t[]>(json.length() + 9U);
|
||||||
|
uint8_t* buffer = __buffer.get();
|
||||||
|
|
||||||
|
header.encode(buffer);
|
||||||
|
::memcpy(buffer + 8U, message, json.length() + 1U);
|
||||||
|
|
||||||
|
// install reply handler
|
||||||
|
if (reply != nullptr)
|
||||||
|
m_handlers[func | RPC_REPLY_FUNC] = reply;
|
||||||
|
|
||||||
|
return m_frameQueue->write(buffer, json.length() + 9U, address, addrLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Helper to generate a default response error payload. */
|
||||||
|
|
||||||
|
void RPC::defaultResponse(json::object& reply, std::string message, StatusType status)
|
||||||
|
{
|
||||||
|
reply = json::object();
|
||||||
|
|
||||||
|
int s = (int)status;
|
||||||
|
reply["status"].set<int>(s);
|
||||||
|
reply["message"].set<std::string>(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Opens connection to the network. */
|
||||||
|
|
||||||
|
bool RPC::open()
|
||||||
|
{
|
||||||
|
if (m_debug)
|
||||||
|
LogMessage(LOG_NET, "Opening RPC network");
|
||||||
|
|
||||||
|
// generate AES256 key
|
||||||
|
size_t size = m_password.size();
|
||||||
|
|
||||||
|
uint8_t* in = new uint8_t[size];
|
||||||
|
for (size_t i = 0U; i < size; i++)
|
||||||
|
in[i] = m_password.at(i);
|
||||||
|
|
||||||
|
uint8_t passwordHash[32U];
|
||||||
|
::memset(passwordHash, 0x00U, 32U);
|
||||||
|
|
||||||
|
edac::SHA256 sha256;
|
||||||
|
sha256.buffer(in, (uint32_t)(size), passwordHash);
|
||||||
|
|
||||||
|
delete[] in;
|
||||||
|
|
||||||
|
m_socket->setPresharedKey(passwordHash);
|
||||||
|
|
||||||
|
return m_socket->open();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Closes connection to the network. */
|
||||||
|
|
||||||
|
void RPC::close()
|
||||||
|
{
|
||||||
|
if (m_debug)
|
||||||
|
LogMessage(LOG_NET, "Closing RPC network");
|
||||||
|
|
||||||
|
m_socket->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Private Class Members
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
/* Writes an RPC reply to the network. */
|
||||||
|
|
||||||
|
bool RPC::reply(uint16_t func, json::object& reply, sockaddr_storage& address, uint32_t addrLen)
|
||||||
|
{
|
||||||
|
json::value v = json::value(reply);
|
||||||
|
std::string json = v.serialize();
|
||||||
|
|
||||||
|
// generate RPC header
|
||||||
|
RPCHeader header = RPCHeader();
|
||||||
|
header.setFunction(func | RPC_REPLY_FUNC);
|
||||||
|
header.setMessageLength(json.length() + 1U);
|
||||||
|
|
||||||
|
// generate message
|
||||||
|
CharArray __message = std::make_unique<char[]>(json.length() + 1U);
|
||||||
|
char* message = __message.get();
|
||||||
|
|
||||||
|
::snprintf(message, json.length() + 1U, "%s", json.c_str());
|
||||||
|
|
||||||
|
uint16_t crc = edac::CRC::createCRC16((uint8_t*)message, (json.length() + 1U) * 8U);
|
||||||
|
header.setCRC(crc);
|
||||||
|
|
||||||
|
// generate RPC message
|
||||||
|
UInt8Array __buffer = std::make_unique<uint8_t[]>(json.length() + 9U);
|
||||||
|
uint8_t* buffer = __buffer.get();
|
||||||
|
|
||||||
|
header.encode(buffer);
|
||||||
|
::memcpy(buffer + 8U, message, json.length() + 1U);
|
||||||
|
|
||||||
|
return m_frameQueue->write(buffer, json.length() + 9U, address, addrLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Default status response handler. */
|
||||||
|
|
||||||
|
void RPC::defaultHandler(json::object& request, json::object& reply)
|
||||||
|
{
|
||||||
|
reply = json::object();
|
||||||
|
|
||||||
|
int s = (int)StatusType::UNHANDLED_REQUEST;
|
||||||
|
reply["status"].set<int>(s);
|
||||||
|
reply["message"].set<std::string>("unhandled request");
|
||||||
|
}
|
||||||
@ -0,0 +1,162 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/**
|
||||||
|
* Digital Voice Modem - Common Library
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2025 Bryan Biedenkapp, N2PLL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @file RPC.h
|
||||||
|
* @ingroup network_core
|
||||||
|
* @file RPC.cpp
|
||||||
|
* @ingroup network_core
|
||||||
|
*/
|
||||||
|
#if !defined(__RPC_H__)
|
||||||
|
#define __RPC_H__
|
||||||
|
|
||||||
|
#include "common/Defines.h"
|
||||||
|
#include "common/network/RawFrameQueue.h"
|
||||||
|
#include "common/network/json/json.h"
|
||||||
|
#include "common/network/udp/Socket.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <functional>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace network
|
||||||
|
{
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Macros
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#define RPC_FUNC_BIND(funcAddr, classInstance) std::bind(&funcAddr, classInstance, std::placeholders::_1, std::placeholders::_2)
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Declaration
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Implements the Remote Procedure Call networking logic.
|
||||||
|
* @ingroup network_core
|
||||||
|
*/
|
||||||
|
class HOST_SW_API RPC {
|
||||||
|
public:
|
||||||
|
typedef std::function<void(json::object& request, json::object& reply)> RPCType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Status/Response Codes
|
||||||
|
*/
|
||||||
|
enum StatusType {
|
||||||
|
OK = 200, //! OK 200
|
||||||
|
|
||||||
|
BAD_REQUEST = 400, //! Bad Request 400
|
||||||
|
INVALID_ARGS = 401, //! Invalid Arguments 401
|
||||||
|
UNHANDLED_REQUEST = 402, //! Unhandled Request 402
|
||||||
|
} status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initializes a new instance of the RPC class.
|
||||||
|
* @param address Network Hostname/IP address to connect to.
|
||||||
|
* @param port Network port number.
|
||||||
|
* @param localPort
|
||||||
|
* @param peerId Unique ID on the network.
|
||||||
|
* @param password Network authentication password.
|
||||||
|
* @param debug Flag indicating whether network debug is enabled.
|
||||||
|
*/
|
||||||
|
RPC(const std::string& address, uint16_t port, uint16_t localPort, const std::string& password, bool debug);
|
||||||
|
/**
|
||||||
|
* @brief Finalizes a instance of the RPC class.
|
||||||
|
*/
|
||||||
|
~RPC();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Updates the timer by the passed number of milliseconds.
|
||||||
|
* @param ms Number of milliseconds.
|
||||||
|
*/
|
||||||
|
void clock(uint32_t ms);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Writes an RPC request to the network.
|
||||||
|
* @param request JSON content body for request.
|
||||||
|
* @param reply Reply handler.
|
||||||
|
* @param address IP address to write data to.
|
||||||
|
* @param port Port number to write data to.
|
||||||
|
* @returns bool True, if message was written, otherwise false.
|
||||||
|
*/
|
||||||
|
bool req(uint16_t func, const json::object& request, RPCType reply, std::string address, uint16_t port);
|
||||||
|
/**
|
||||||
|
* @brief Writes an RPC request to the network.
|
||||||
|
* @param request JSON content body for request.
|
||||||
|
* @param reply Reply handler.
|
||||||
|
* @param address IP address to write data to.
|
||||||
|
* @param addrLen
|
||||||
|
* @returns bool True, if message was written, otherwise false.
|
||||||
|
*/
|
||||||
|
bool req(uint16_t func, const json::object& request, RPCType reply, sockaddr_storage& address, uint32_t addrLen);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Helper to generate a default response error payload.
|
||||||
|
* @param reply JSON reply.
|
||||||
|
* @param message Textual error message to send.
|
||||||
|
* @param status Status to send.
|
||||||
|
*/
|
||||||
|
void defaultResponse(json::object &reply, std::string message, StatusType status = StatusType::BAD_REQUEST);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Opens connection to the network.
|
||||||
|
* @returns bool True, if networking has started, otherwise false.
|
||||||
|
*/
|
||||||
|
bool open();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Closes connection to the network.
|
||||||
|
*/
|
||||||
|
void close();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Helper to register an RPC handler.
|
||||||
|
* @param func Function opcode.
|
||||||
|
* @param handler Function handler.
|
||||||
|
*/
|
||||||
|
void registerHandler(uint16_t func, RPCType handler) { m_handlers[func] = handler; }
|
||||||
|
/**
|
||||||
|
* @brief Helper to unregister an RPC handler.
|
||||||
|
* @param func Function opcode.
|
||||||
|
*/
|
||||||
|
void unregisterHandler(uint16_t func) { m_handlers.erase(func); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_address;
|
||||||
|
uint16_t m_port;
|
||||||
|
|
||||||
|
bool m_debug;
|
||||||
|
|
||||||
|
udp::Socket* m_socket;
|
||||||
|
RawFrameQueue* m_frameQueue;
|
||||||
|
|
||||||
|
std::string m_password;
|
||||||
|
|
||||||
|
std::map<uint16_t, RPCType> m_handlers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Writes an RPC reply to the network.
|
||||||
|
* @param request JSON content body for reply.
|
||||||
|
* @param address IP address to write data to.
|
||||||
|
* @param addrLen
|
||||||
|
* @returns bool True, if message was written, otherwise false.
|
||||||
|
*/
|
||||||
|
bool reply(uint16_t func, json::object& request, sockaddr_storage& address, uint32_t addrLen);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Default status response handler.
|
||||||
|
* @param request JSON request.
|
||||||
|
* @param reply JSON response.
|
||||||
|
*/
|
||||||
|
void defaultHandler(json::object& request, json::object& reply);
|
||||||
|
};
|
||||||
|
} // namespace network
|
||||||
|
|
||||||
|
#endif // __RPC_H__
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* Digital Voice Modem - Common Library
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2025 Bryan Biedenkapp, N2PLL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include "Defines.h"
|
||||||
|
#include "network/RPCHeader.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
|
using namespace network::frame;
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Public Class Members
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/* Initializes a new instance of the RPCHeader class. */
|
||||||
|
|
||||||
|
RPCHeader::RPCHeader() :
|
||||||
|
m_crc16(0U),
|
||||||
|
m_func(0U),
|
||||||
|
m_messageLength(0U)
|
||||||
|
{
|
||||||
|
/* stub */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Finalizes a instance of the RPCHeader class. */
|
||||||
|
|
||||||
|
RPCHeader::~RPCHeader() = default;
|
||||||
|
|
||||||
|
/* Decode a RTP header. */
|
||||||
|
|
||||||
|
bool RPCHeader::decode(const uint8_t* data)
|
||||||
|
{
|
||||||
|
assert(data != nullptr);
|
||||||
|
|
||||||
|
m_crc16 = (data[0U] << 8) | (data[1U] << 0); // CRC-16
|
||||||
|
m_func = __GET_UINT16B(data, 2U); // Function
|
||||||
|
m_messageLength = __GET_UINT32(data, 4U); // Message Length
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Encode a RTP header. */
|
||||||
|
|
||||||
|
void RPCHeader::encode(uint8_t* data)
|
||||||
|
{
|
||||||
|
assert(data != nullptr);
|
||||||
|
|
||||||
|
data[0U] = (m_crc16 >> 8) & 0xFFU; // CRC-16 MSB
|
||||||
|
data[1U] = (m_crc16 >> 0) & 0xFFU; // CRC-16 LSB
|
||||||
|
__SET_UINT16B(m_func, data, 2U); // Function
|
||||||
|
|
||||||
|
__SET_UINT32(m_messageLength, data, 4U); // Message Length
|
||||||
|
}
|
||||||
@ -0,0 +1,88 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* Digital Voice Modem - Common Library
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2025 Bryan Biedenkapp, N2PLL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @file RPCHeader.h
|
||||||
|
* @ingroup network_core
|
||||||
|
* @file RPCHeader.cpp
|
||||||
|
* @ingroup network_core
|
||||||
|
*/
|
||||||
|
#if !defined(__RPC_HEADER_H__)
|
||||||
|
#define __RPC_HEADER_H__
|
||||||
|
|
||||||
|
#include "common/Defines.h"
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Constants
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#define RPC_HEADER_LENGTH_BYTES 8
|
||||||
|
#define RPC_REPLY_FUNC 0x8000U
|
||||||
|
|
||||||
|
namespace network
|
||||||
|
{
|
||||||
|
namespace frame
|
||||||
|
{
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Declaration
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Represents the Remote Procedure Call network frame header.
|
||||||
|
* \code{.unparsed}
|
||||||
|
* Byte 0 1 2 3
|
||||||
|
* Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
|
||||||
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
* | Payload CRC-16 | Function |
|
||||||
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
* | Message Length |
|
||||||
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
* 8 bytes
|
||||||
|
* \endcode
|
||||||
|
*/
|
||||||
|
class HOST_SW_API RPCHeader {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Initializes a new instance of the RPCHeader class.
|
||||||
|
*/
|
||||||
|
RPCHeader();
|
||||||
|
/**
|
||||||
|
* @brief Finalizes a instance of the RPCHeader class.
|
||||||
|
*/
|
||||||
|
~RPCHeader();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Decode a RPC header.
|
||||||
|
* @param[in] data Buffer containing RPC header to decode.
|
||||||
|
*/
|
||||||
|
bool decode(const uint8_t* data);
|
||||||
|
/**
|
||||||
|
* @brief Encode a RPC header.
|
||||||
|
* @param[out] data Buffer to encode an RPC header.
|
||||||
|
*/
|
||||||
|
void encode(uint8_t* data);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Payload packet CRC-16.
|
||||||
|
*/
|
||||||
|
__PROPERTY(uint16_t, crc16, CRC);
|
||||||
|
/**
|
||||||
|
* @brief Function.
|
||||||
|
*/
|
||||||
|
__PROPERTY(uint16_t, func, Function);
|
||||||
|
/**
|
||||||
|
* @brief Message Length.
|
||||||
|
*/
|
||||||
|
__PROPERTY(uint32_t, messageLength, MessageLength);
|
||||||
|
};
|
||||||
|
} // namespace frame
|
||||||
|
} // namespace network
|
||||||
|
|
||||||
|
#endif // __RPC_HEADER_H__
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* Digital Voice Modem - Modem Host Software
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2025 Bryan Biedenkapp, N2PLL
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @defgroup host_rpc Host RPC
|
||||||
|
* @brief Implementation for the host RPC.
|
||||||
|
* @ingroup host
|
||||||
|
*
|
||||||
|
* @file RPCDefines.h
|
||||||
|
* @ingroup host_prc
|
||||||
|
*/
|
||||||
|
#if !defined(__RPC_DEFINES_H__)
|
||||||
|
#define __RPC_DEFINES_H__
|
||||||
|
|
||||||
|
#include "Defines.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup host_rpc
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Constants
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#define RPC_REGISTER_CC_VC 0x1000U
|
||||||
|
|
||||||
|
#define RPC_RELEASE_P25_TG 0x0101U
|
||||||
|
#define RPC_RELEASE_DMR_TG 0x0102U
|
||||||
|
#define RPC_RELEASE_NXDN_TG 0x0103U
|
||||||
|
#define RPC_TOUCH_P25_TG 0x0201U
|
||||||
|
#define RPC_TOUCH_DMR_TG 0x0202U
|
||||||
|
#define RPC_TOUCH_NXDN_TG 0x0203U
|
||||||
|
#define RPC_PERMIT_P25_TG 0x0001U
|
||||||
|
#define RPC_PERMIT_DMR_TG 0x0002U
|
||||||
|
#define RPC_PERMIT_NXDN_TG 0x0003U
|
||||||
|
|
||||||
|
#define RPC_DMR_TSCC_PAYLOAD_ACT 0x0010U
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
#endif // __RPC_DEFINES_H__
|
||||||
Loading…
Reference in new issue