add DHT Records #41
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
Loading…
Reference in new issue