add DHT Records #41

feature/OpenDHT_#41
Geoffrey Merck 2 months ago
parent 6510c2c755
commit 2545825ec6

@ -0,0 +1,89 @@
/*
* Copyright (c) 2026 by Geoffrey Merck F4FXL / KC3FRA
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "DHTRecord.h"
using nlohmann::json;
/*
* Serialize LastHeard to JSON
*/
void to_json(json& j, const LastHeard& lh)
{
j = json{
{"repeater", lh.repeater},
{"gateway", lh.gateway},
{"at", lh.at}
};
}
/*
* Deserialize LastHeard from JSON
*/
void from_json(const json& j, LastHeard& lh)
{
j.at("repeater").get_to(lh.repeater);
j.at("gateway").get_to(lh.gateway);
j.at("at").get_to(lh.at);
}
/*
* Serialize DHTRecord to JSON
*/
void to_json(json& j, const DHTRecord& rec)
{
j = json{
{"version", rec.version},
{"kind", rec.kind},
{"callsign", rec.callsign},
{"gateway", rec.gateway},
{"publisher", rec.publisher},
{"instance_id", rec.instance_id},
{"published_at", rec.published_at},
{"expires_at", rec.expires_at},
{"seq", rec.seq}
};
// Add optional last_heard block only if present
if (rec.last_heard.has_value()) {
j["last_heard"] = rec.last_heard.value();
}
}
/*
* Deserialize DHTRecord from JSON
*/
void from_json(const json& j, DHTRecord& rec)
{
j.at("version").get_to(rec.version);
j.at("kind").get_to(rec.kind);
j.at("callsign").get_to(rec.callsign);
j.at("gateway").get_to(rec.gateway);
j.at("publisher").get_to(rec.publisher);
j.at("instance_id").get_to(rec.instance_id);
j.at("published_at").get_to(rec.published_at);
j.at("expires_at").get_to(rec.expires_at);
j.at("seq").get_to(rec.seq);
// Optional field
if (j.contains("last_heard")) {
rec.last_heard = j.at("last_heard").get<LastHeard>();
} else {
rec.last_heard.reset();
}
}

@ -0,0 +1,73 @@
/*
* Copyright (c) 2026 by Geoffrey Merck F4FXL / KC3FRA
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#pragma once
#include <string>
#include <optional>
#include <cstdint>
#include <nlohmann/json.hpp>
/*
* Represents the "last heard" information for a user.
* This block is optional and only present for user records.
*/
struct LastHeard
{
std::string repeater; // Repeater callsign (e.g. "F1ZXX B")
std::string gateway; // Gateway callsign (e.g. "F1ZXX G")
std::string at; // ISO8601 timestamp (UTC)
};
/*
* Represents a generic DHT record.
*
* This structure is used for both:
* - repeater presence (kind = "repeater")
* - user presence (kind = "user")
*
* The difference is that "user" records include the optional last_heard block.
*/
struct DHTRecord
{
uint32_t version = 1; // Schema version
std::string kind; // "repeater" or "user"
std::string callsign; // Subject callsign (user or repeater)
std::string gateway; // Gateway associated to the subject
std::string publisher; // Gateway that published this record
std::string instance_id; // Unique instance identifier (per process)
std::string published_at; // ISO8601 timestamp (UTC)
std::string expires_at; // ISO8601 timestamp (UTC)
uint64_t seq = 0; // Monotonic sequence number
std::optional<LastHeard> last_heard; // Present only for user records
};
/*
* JSON serialization helpers (nlohmann::json)
*/
void to_json(nlohmann::json& j, const LastHeard& lh);
void from_json(const nlohmann::json& j, LastHeard& lh);
void to_json(nlohmann::json& j, const DHTRecord& rec);
void from_json(const nlohmann::json& j, DHTRecord& rec);

@ -0,0 +1,15 @@
SRCS = $(wildcard *.cpp)
OBJS = $(SRCS:.cpp=.o)
DEPS = $(SRCS:.cpp=.d)
DStarBase.a: $(OBJS)
$(AR) rcs DStarBase.a $(OBJS)
%.o : %.cpp
$(CC) -I../BaseCommon $(CPPFLAGS) -MMD -MD -c $< -o $@
-include $(DEPS)
clean:
$(RM) *.o *.d DHT.a
../BaseCommon/BaseCommon.a:

@ -0,0 +1,33 @@
Repeater
```
{
"version": 1,
"kind": "repeater",
"callsign": "F1ZXX B",
"gateway": "F1ZXX G",
"publisher": "F1ZXX G",
"instance_id": "1c8dfd1a-6ab5-4ef6-b2fc-fd089db1d7be",
"published_at": "2026-03-30T12:34:56Z",
"expires_at": "2026-03-30T12:37:56Z",
"seq": 1842
}
```
Last Heard
```
{
"version": 1,
"kind": "user",
"callsign": "F4FXL",
"publisher": "F1ZXX G",
"instance_id": "1c8dfd1a-6ab5-4ef6-b2fc-fd089db1d7be",
"published_at": "2026-03-30T12:35:12Z",
"expires_at": "2026-03-30T12:37:12Z",
"seq": 99231,
"last_heard": {
"repeater": "F1ZXX B",
"gateway": "F1ZXX G",
"at": "2026-03-30T12:35:10Z"
}
}
```

@ -68,6 +68,9 @@ DGWTimeServer/dgwtimeserver: VersionInfo/GitVersion.h $(OBJS) DStarBase/DStarBas
DGWVoiceTransmit/dgwvoicetransmit: VersionInfo/GitVersion.h $(OBJS) DStarBase/DStarBase.a BaseCommon/BaseCommon.a FORCE DGWVoiceTransmit/dgwvoicetransmit: VersionInfo/GitVersion.h $(OBJS) DStarBase/DStarBase.a BaseCommon/BaseCommon.a FORCE
$(MAKE) -C DGWVoiceTransmit $(MAKE) -C DGWVoiceTransmit
DHT/DHT.a: BaseCommon/BaseCommon.a FORCE
$(MAKE) -C DHT
IRCDDB/IRCDDB.a: VersionInfo/GitVersion.h BaseCommon/BaseCommon.a FORCE IRCDDB/IRCDDB.a: VersionInfo/GitVersion.h BaseCommon/BaseCommon.a FORCE
$(MAKE) -C IRCDDB $(MAKE) -C IRCDDB

Loading…
Cancel
Save

Powered by TurnKey Linux.