Fixing "dual" modes for Raspberry Pi build, adding logging script
This commit is contained in:
parent
21a6a2f676
commit
49c2ccd49a
9 changed files with 314 additions and 20 deletions
|
|
@ -309,6 +309,19 @@ Run one on each Pi. There should be no required start order for the dual-role te
|
|||
./microreticulum_306_rpi_arm64_dual_children
|
||||
```
|
||||
|
||||
Dual builds default to `--ble-dual-policy=first-path-wins`. Both BlueZ roles start, but once either central or peripheral establishes the first peer path, the opposite role is stopped. This avoids exposing two simultaneous Reticulum interfaces to the same peer.
|
||||
|
||||
Temporary policy overrides are available for debugging:
|
||||
|
||||
```bash
|
||||
./microreticulum_306_rpi_arm64_dual_little_boy_blue --ble-dual-policy=both
|
||||
./microreticulum_306_rpi_arm64_dual_little_boy_blue --ble-dual-policy=first-path-wins
|
||||
./microreticulum_306_rpi_arm64_dual_little_boy_blue --ble-dual-policy=central-only
|
||||
./microreticulum_306_rpi_arm64_dual_little_boy_blue --ble-dual-policy=peripheral-only
|
||||
```
|
||||
|
||||
`both` preserves the earlier Linux dual behavior and may produce Reticulum `Link-associated packet received on unexpected interface` errors if two BLE paths form at the same time. The `central-only` and `peripheral-only` policies are useful when isolating BlueZ adapter behavior without rebuilding split-role binaries.
|
||||
|
||||
You can copy those binaries to another Pi Zero 2W and rename them for clarity if both Pis use the same CPU architecture, OS bitness, and compatible runtime libraries. Check with:
|
||||
|
||||
```bash
|
||||
|
|
|
|||
|
|
@ -60,6 +60,8 @@
|
|||
<p class="note">
|
||||
Use two different payload binaries for a simple exchange test. Start one on
|
||||
each Pi; no fixed client/server start order is intended for the dual builds.
|
||||
The default BLE policy is first-path-wins, which stops the opposite BlueZ
|
||||
role after the first peer path is established.
|
||||
</p>
|
||||
|
||||
<h2>Downloads</h2>
|
||||
|
|
|
|||
|
|
@ -106,6 +106,8 @@ microreticulum_306_rpi_arm64_dual_little_boy_blue
|
|||
microreticulum_306_rpi_arm64_dual_children
|
||||
```
|
||||
|
||||
Dual binaries default to `--ble-dual-policy=first-path-wins`: both BlueZ roles start, but the first working peer path stops the opposite role. To reproduce the older experimental behavior, run with `--ble-dual-policy=both`.
|
||||
|
||||
Example scp command to a new server named "trixie1":
|
||||
```bash
|
||||
scp microreticulum_306_rpi_arm64_dual_children trixie1:~
|
||||
|
|
@ -162,6 +164,7 @@ Expected healthy startup:
|
|||
jlpoole@trixie1:~ $ ./microreticulum_306_rpi_arm64_dual_children
|
||||
Exercise 306 native BLE file transfer console
|
||||
Node=Node-PIZERO2-DUAL
|
||||
BLE dual policy=first-path-wins
|
||||
Selected file=children.txt bytes=1422 chunk=32 interval_ms=500 repeat_rest_ms=10000
|
||||
[ustore] Initializing PosixFileSystem
|
||||
[ustore] WARNING: FlashFSFileSystem check failed, reformatting!
|
||||
|
|
@ -175,6 +178,20 @@ BLE linux-dual waiting for peer
|
|||
BLE linux-dual waiting for peer
|
||||
|
||||
```
|
||||
|
||||
When one role wins, expect a line like:
|
||||
|
||||
```text
|
||||
BLE dual policy first-path-wins: peripheral path won peer=:1.42; stopping central role
|
||||
```
|
||||
|
||||
or:
|
||||
|
||||
```text
|
||||
BLE dual policy first-path-wins: central path won peer=/org/bluez/hci0/dev_D8_3A_DD_1D_CF_B5; stopping peripheral role
|
||||
```
|
||||
|
||||
The native BLE interfaces also emit temporary `BLE-FRAME` lines for Reticulum frame tracing. Those lines include timestamp, node label, BLE role, peer path/address, BlueZ object path, interface pointer, packet length, and the first packet byte.
|
||||
When you have a second unit running, your console will show something like:
|
||||
```bash
|
||||
[Startup stuff]
|
||||
|
|
|
|||
|
|
@ -48,6 +48,34 @@ static void log_error(const char* what, GError* error) {
|
|||
}
|
||||
}
|
||||
|
||||
static std::string peer_address_from_path(const std::string& path) {
|
||||
size_t pos = path.find("/dev_");
|
||||
if (pos == std::string::npos) {
|
||||
return path.empty() ? std::string("unknown") : path;
|
||||
}
|
||||
std::string addr = path.substr(pos + 5);
|
||||
std::replace(addr.begin(), addr.end(), '_', ':');
|
||||
return addr;
|
||||
}
|
||||
|
||||
static void log_reticulum_frame(const char* direction,
|
||||
const std::string& node_label,
|
||||
const std::string& peer_path,
|
||||
const std::string& object_path,
|
||||
const void* iface,
|
||||
const RNS::Bytes& packet) {
|
||||
unsigned first = packet.size() > 0 ? packet[0] : 0;
|
||||
std::printf("BLE-FRAME t=%llu node=%s role=central dir=%s peer=%s object=%s if=%p len=%zu packet0=0x%02X link_hint=unknown\n",
|
||||
(unsigned long long)now_ms(),
|
||||
node_label.c_str(),
|
||||
direction,
|
||||
peer_address_from_path(peer_path).c_str(),
|
||||
object_path.empty() ? "unknown" : object_path.c_str(),
|
||||
iface,
|
||||
packet.size(),
|
||||
first);
|
||||
}
|
||||
|
||||
HostBluezBleInterface::HostBluezBleInterface(const std::string& node_label, const char* name)
|
||||
: InterfaceImpl(name), node_label_(node_label) {
|
||||
_IN = true;
|
||||
|
|
@ -281,7 +309,6 @@ bool HostBluezBleInterface::connect_device(const std::string& device_path) {
|
|||
if (!result) {
|
||||
log_error("BLE linux-central: Device1.Connect failed", error);
|
||||
next_scan_ms_ = now_ms() + SCAN_RETRY_MS;
|
||||
start_discovery();
|
||||
return false;
|
||||
}
|
||||
g_variant_unref(result);
|
||||
|
|
@ -457,6 +484,8 @@ void HostBluezBleInterface::send_outgoing(const RNS::Bytes& data) {
|
|||
return;
|
||||
}
|
||||
|
||||
log_reticulum_frame("tx", node_label_, device_path_, rx_char_path_, this, data);
|
||||
|
||||
size_t total = (data.size() + BLE_PAYLOAD_SIZE - 1) / BLE_PAYLOAD_SIZE;
|
||||
if (total == 0 || total > 65535) {
|
||||
std::fprintf(stderr, "BLE linux-central: cannot fragment packet len=%zu fragments=%zu\n", data.size(), total);
|
||||
|
|
@ -617,6 +646,7 @@ void HostBluezBleInterface::handle_fragment(const uint8_t* data, size_t len) {
|
|||
}
|
||||
|
||||
void HostBluezBleInterface::enqueue_packet(const RNS::Bytes& packet) {
|
||||
log_reticulum_frame("rx", node_label_, device_path_, tx_char_path_, this, packet);
|
||||
incoming_packets_.push_back(packet);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ public:
|
|||
|
||||
bool connected() const { return connected_; }
|
||||
const char* role_name() const { return "linux-central"; }
|
||||
const std::string& peer_path() const { return device_path_; }
|
||||
|
||||
private:
|
||||
void send_outgoing(const RNS::Bytes& data) override;
|
||||
|
|
|
|||
|
|
@ -62,6 +62,34 @@ static GVariant* flags_variant(std::initializer_list<const char*> flags) {
|
|||
return g_variant_builder_end(&builder);
|
||||
}
|
||||
|
||||
static std::string peer_address_from_path(const std::string& path) {
|
||||
size_t pos = path.find("/dev_");
|
||||
if (pos == std::string::npos) {
|
||||
return path.empty() ? std::string("unknown") : path;
|
||||
}
|
||||
std::string addr = path.substr(pos + 5);
|
||||
std::replace(addr.begin(), addr.end(), '_', ':');
|
||||
return addr;
|
||||
}
|
||||
|
||||
static void log_reticulum_frame(const char* direction,
|
||||
const std::string& node_label,
|
||||
const std::string& peer_path,
|
||||
const char* object_path,
|
||||
const void* iface,
|
||||
const RNS::Bytes& packet) {
|
||||
unsigned first = packet.size() > 0 ? packet[0] : 0;
|
||||
std::printf("BLE-FRAME t=%llu node=%s role=peripheral dir=%s peer=%s object=%s if=%p len=%zu packet0=0x%02X link_hint=unknown\n",
|
||||
(unsigned long long)now_ms(),
|
||||
node_label.c_str(),
|
||||
direction,
|
||||
peer_address_from_path(peer_path).c_str(),
|
||||
object_path ? object_path : "unknown",
|
||||
iface,
|
||||
packet.size(),
|
||||
first);
|
||||
}
|
||||
|
||||
static const char* OBJECT_MANAGER_XML =
|
||||
"<node>"
|
||||
" <interface name='org.freedesktop.DBus.ObjectManager'>"
|
||||
|
|
@ -167,6 +195,7 @@ void HostBluezPeripheralInterface::stop() {
|
|||
online_ = false;
|
||||
connected_ = false;
|
||||
notifying_ = false;
|
||||
peer_path_.clear();
|
||||
_online = false;
|
||||
}
|
||||
|
||||
|
|
@ -381,6 +410,8 @@ void HostBluezPeripheralInterface::send_outgoing(const RNS::Bytes& data) {
|
|||
return;
|
||||
}
|
||||
|
||||
log_reticulum_frame("tx", node_label_, peer_path_, TX_PATH, this, data);
|
||||
|
||||
size_t total = (data.size() + BLE_PAYLOAD_SIZE - 1) / BLE_PAYLOAD_SIZE;
|
||||
if (total == 0 || total > 65535) {
|
||||
std::fprintf(stderr, "BLE linux-peripheral: cannot fragment packet len=%zu fragments=%zu\n", data.size(), total);
|
||||
|
|
@ -496,6 +527,7 @@ void HostBluezPeripheralInterface::handle_fragment(const uint8_t* data, size_t l
|
|||
}
|
||||
|
||||
void HostBluezPeripheralInterface::enqueue_packet(const RNS::Bytes& packet) {
|
||||
log_reticulum_frame("rx", node_label_, peer_path_, RX_PATH, this, packet);
|
||||
incoming_packets_.push_back(packet);
|
||||
}
|
||||
|
||||
|
|
@ -615,6 +647,7 @@ GVariant* HostBluezPeripheralInterface::get_property(const char* object_path,
|
|||
void HostBluezPeripheralInterface::handle_method_call(const char* object_path,
|
||||
const char* interface_name,
|
||||
const char* method_name,
|
||||
const char* sender,
|
||||
GVariant* parameters,
|
||||
GDBusMethodInvocation* invocation) {
|
||||
if (std::strcmp(interface_name, "org.freedesktop.DBus.ObjectManager") == 0 &&
|
||||
|
|
@ -661,6 +694,12 @@ void HostBluezPeripheralInterface::handle_method_call(const char* object_path,
|
|||
GVariant* value = nullptr;
|
||||
GVariant* options = nullptr;
|
||||
g_variant_get(parameters, "(@ay@a{sv})", &value, &options);
|
||||
const gchar* device = nullptr;
|
||||
if (options && g_variant_lookup(options, "device", "&o", &device) && device) {
|
||||
peer_path_ = device;
|
||||
} else if (sender && peer_path_.empty()) {
|
||||
peer_path_ = sender;
|
||||
}
|
||||
gsize len = 0;
|
||||
const uint8_t* data = static_cast<const uint8_t*>(g_variant_get_fixed_array(value, &len, sizeof(uint8_t)));
|
||||
if (data && len > 0) {
|
||||
|
|
@ -683,6 +722,9 @@ void HostBluezPeripheralInterface::handle_method_call(const char* object_path,
|
|||
}
|
||||
notifying_ = true;
|
||||
connected_ = true;
|
||||
if (sender && peer_path_.empty()) {
|
||||
peer_path_ = sender;
|
||||
}
|
||||
std::printf("BLE linux-peripheral: central subscribed to TX notifications\n");
|
||||
g_dbus_method_invocation_return_value(invocation, nullptr);
|
||||
return;
|
||||
|
|
@ -691,6 +733,7 @@ void HostBluezPeripheralInterface::handle_method_call(const char* object_path,
|
|||
if (std::strcmp(method_name, "StopNotify") == 0) {
|
||||
notifying_ = false;
|
||||
connected_ = false;
|
||||
peer_path_.clear();
|
||||
reset_reassembly();
|
||||
std::printf("BLE linux-peripheral: central unsubscribed from TX notifications\n");
|
||||
g_dbus_method_invocation_return_value(invocation, nullptr);
|
||||
|
|
@ -715,7 +758,7 @@ void HostBluezPeripheralInterface::method_call(GDBusConnection* connection,
|
|||
g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "No peripheral instance");
|
||||
return;
|
||||
}
|
||||
self->handle_method_call(object_path, interface_name, method_name, parameters, invocation);
|
||||
self->handle_method_call(object_path, interface_name, method_name, sender, parameters, invocation);
|
||||
}
|
||||
|
||||
GVariant* HostBluezPeripheralInterface::property_get(GDBusConnection* connection,
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ public:
|
|||
|
||||
bool connected() const { return connected_; }
|
||||
const char* role_name() const { return "linux-peripheral"; }
|
||||
const std::string& peer_path() const { return peer_path_; }
|
||||
|
||||
private:
|
||||
void send_outgoing(const RNS::Bytes& data) override;
|
||||
|
|
@ -44,6 +45,7 @@ private:
|
|||
void handle_method_call(const char* object_path,
|
||||
const char* interface_name,
|
||||
const char* method_name,
|
||||
const char* sender,
|
||||
GVariant* parameters,
|
||||
GDBusMethodInvocation* invocation);
|
||||
|
||||
|
|
@ -102,6 +104,7 @@ private:
|
|||
bool advertisement_registered_ = false;
|
||||
bool has_gatt_manager_ = false;
|
||||
bool has_advertising_manager_ = false;
|
||||
std::string peer_path_;
|
||||
uint32_t tx_message_id_ = 0;
|
||||
RNS::Bytes tx_value_;
|
||||
RNS::Bytes identity_value_;
|
||||
|
|
|
|||
|
|
@ -59,9 +59,18 @@ static constexpr uint32_t FNV1A_PRIME = 16777619UL;
|
|||
|
||||
static RNS::Reticulum reticulum({RNS::Type::NONE});
|
||||
#if defined(HOST_BLE_DUAL)
|
||||
enum class BleDualPolicy {
|
||||
Both,
|
||||
FirstPathWins,
|
||||
CentralOnly,
|
||||
PeripheralOnly,
|
||||
};
|
||||
|
||||
static std::vector<RNS::Interface> ble_interfaces;
|
||||
static HostBluezBleInterface* ble_central_impl = nullptr;
|
||||
static HostBluezPeripheralInterface* ble_peripheral_impl = nullptr;
|
||||
static BleDualPolicy ble_dual_policy = BleDualPolicy::FirstPathWins;
|
||||
static const char* ble_dual_winner = nullptr;
|
||||
#else
|
||||
static RNS::Interface ble_interface({RNS::Type::NONE});
|
||||
static HostBleInterface* ble_impl = nullptr;
|
||||
|
|
@ -79,6 +88,42 @@ static bool link_attempted = false;
|
|||
static std::string node_label = HOST_NODE_LABEL;
|
||||
static bool running = true;
|
||||
|
||||
#if defined(HOST_BLE_DUAL)
|
||||
static const char* ble_dual_policy_name(BleDualPolicy policy) {
|
||||
switch (policy) {
|
||||
case BleDualPolicy::Both:
|
||||
return "both";
|
||||
case BleDualPolicy::FirstPathWins:
|
||||
return "first-path-wins";
|
||||
case BleDualPolicy::CentralOnly:
|
||||
return "central-only";
|
||||
case BleDualPolicy::PeripheralOnly:
|
||||
return "peripheral-only";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
static bool set_ble_dual_policy(const char* value) {
|
||||
if (std::strcmp(value, "both") == 0) {
|
||||
ble_dual_policy = BleDualPolicy::Both;
|
||||
return true;
|
||||
}
|
||||
if (std::strcmp(value, "first-path-wins") == 0) {
|
||||
ble_dual_policy = BleDualPolicy::FirstPathWins;
|
||||
return true;
|
||||
}
|
||||
if (std::strcmp(value, "central-only") == 0) {
|
||||
ble_dual_policy = BleDualPolicy::CentralOnly;
|
||||
return true;
|
||||
}
|
||||
if (std::strcmp(value, "peripheral-only") == 0) {
|
||||
ble_dual_policy = BleDualPolicy::PeripheralOnly;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct RxTransferState {
|
||||
bool active = false;
|
||||
std::string sender;
|
||||
|
|
@ -433,21 +478,25 @@ static void setup_reticulum() {
|
|||
RNS::Utilities::OS::register_filesystem(filesystem);
|
||||
|
||||
#if defined(HOST_BLE_DUAL)
|
||||
auto central = std::shared_ptr<RNS::InterfaceImpl>(new HostBluezBleInterface(node_label));
|
||||
ble_central_impl = static_cast<HostBluezBleInterface*>(central.get());
|
||||
RNS::Interface central_interface(central);
|
||||
central_interface.mode(RNS::Type::Interface::MODE_GATEWAY);
|
||||
RNS::Transport::register_interface(central_interface);
|
||||
ble_interfaces.push_back(central_interface);
|
||||
central_interface.start();
|
||||
if (ble_dual_policy != BleDualPolicy::PeripheralOnly) {
|
||||
auto central = std::shared_ptr<RNS::InterfaceImpl>(new HostBluezBleInterface(node_label));
|
||||
ble_central_impl = static_cast<HostBluezBleInterface*>(central.get());
|
||||
RNS::Interface central_interface(central);
|
||||
central_interface.mode(RNS::Type::Interface::MODE_GATEWAY);
|
||||
RNS::Transport::register_interface(central_interface);
|
||||
ble_interfaces.push_back(central_interface);
|
||||
central_interface.start();
|
||||
}
|
||||
|
||||
auto peripheral = std::shared_ptr<RNS::InterfaceImpl>(new HostBluezPeripheralInterface(node_label));
|
||||
ble_peripheral_impl = static_cast<HostBluezPeripheralInterface*>(peripheral.get());
|
||||
RNS::Interface peripheral_interface(peripheral);
|
||||
peripheral_interface.mode(RNS::Type::Interface::MODE_GATEWAY);
|
||||
RNS::Transport::register_interface(peripheral_interface);
|
||||
ble_interfaces.push_back(peripheral_interface);
|
||||
peripheral_interface.start();
|
||||
if (ble_dual_policy != BleDualPolicy::CentralOnly) {
|
||||
auto peripheral = std::shared_ptr<RNS::InterfaceImpl>(new HostBluezPeripheralInterface(node_label));
|
||||
ble_peripheral_impl = static_cast<HostBluezPeripheralInterface*>(peripheral.get());
|
||||
RNS::Interface peripheral_interface(peripheral);
|
||||
peripheral_interface.mode(RNS::Type::Interface::MODE_GATEWAY);
|
||||
RNS::Transport::register_interface(peripheral_interface);
|
||||
ble_interfaces.push_back(peripheral_interface);
|
||||
peripheral_interface.start();
|
||||
}
|
||||
#else
|
||||
auto impl = std::shared_ptr<RNS::InterfaceImpl>(new HostBleInterface(node_label));
|
||||
ble_impl = static_cast<HostBleInterface*>(impl.get());
|
||||
|
|
@ -478,12 +527,12 @@ static void setup_reticulum() {
|
|||
|
||||
static void loop_ble_interfaces() {
|
||||
#if defined(HOST_BLE_DUAL)
|
||||
if (ble_central_impl) {
|
||||
ble_central_impl->loop();
|
||||
}
|
||||
if (ble_peripheral_impl) {
|
||||
ble_peripheral_impl->loop();
|
||||
}
|
||||
if (ble_central_impl) {
|
||||
ble_central_impl->loop();
|
||||
}
|
||||
#else
|
||||
if (ble_impl) {
|
||||
ble_impl->loop();
|
||||
|
|
@ -491,6 +540,33 @@ static void loop_ble_interfaces() {
|
|||
#endif
|
||||
}
|
||||
|
||||
#if defined(HOST_BLE_DUAL)
|
||||
static void enforce_ble_dual_policy() {
|
||||
if (ble_dual_policy != BleDualPolicy::FirstPathWins || ble_dual_winner) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ble_peripheral_impl && ble_peripheral_impl->connected()) {
|
||||
ble_dual_winner = "peripheral";
|
||||
std::printf("BLE dual policy first-path-wins: peripheral path won peer=%s; stopping central role\n",
|
||||
ble_peripheral_impl->peer_path().empty() ? "unknown" : ble_peripheral_impl->peer_path().c_str());
|
||||
if (ble_central_impl) {
|
||||
ble_central_impl->stop();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (ble_central_impl && ble_central_impl->connected()) {
|
||||
ble_dual_winner = "central";
|
||||
std::printf("BLE dual policy first-path-wins: central path won peer=%s; stopping peripheral role\n",
|
||||
ble_central_impl->peer_path().empty() ? "unknown" : ble_central_impl->peer_path().c_str());
|
||||
if (ble_peripheral_impl) {
|
||||
ble_peripheral_impl->stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool ble_connected() {
|
||||
#if defined(HOST_BLE_DUAL)
|
||||
return (ble_central_impl && ble_central_impl->connected()) ||
|
||||
|
|
@ -502,6 +578,9 @@ static bool ble_connected() {
|
|||
|
||||
static const char* ble_role_status() {
|
||||
#if defined(HOST_BLE_DUAL)
|
||||
if (ble_dual_winner) {
|
||||
return ble_dual_winner;
|
||||
}
|
||||
if (ble_central_impl && ble_central_impl->connected()) {
|
||||
return ble_central_impl->role_name();
|
||||
}
|
||||
|
|
@ -534,13 +613,37 @@ static void handle_signal(int signal) {
|
|||
running = false;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int main(int argc, char** argv) {
|
||||
std::signal(SIGINT, handle_signal);
|
||||
std::signal(SIGTERM, handle_signal);
|
||||
|
||||
#if defined(HOST_BLE_DUAL)
|
||||
static constexpr const char* policy_prefix = "--ble-dual-policy=";
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (std::strncmp(argv[i], policy_prefix, std::strlen(policy_prefix)) == 0) {
|
||||
const char* value = argv[i] + std::strlen(policy_prefix);
|
||||
if (!set_ble_dual_policy(value)) {
|
||||
std::fprintf(stderr,
|
||||
"Unknown --ble-dual-policy=%s; valid values: both, first-path-wins, central-only, peripheral-only\n",
|
||||
value);
|
||||
return 2;
|
||||
}
|
||||
} else {
|
||||
std::fprintf(stderr, "Unknown option: %s\n", argv[i]);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
#endif
|
||||
|
||||
RNS::loglevel(RNS::LOG_NOTICE);
|
||||
std::printf("Exercise 306 native BLE file transfer console\n");
|
||||
std::printf("Node=%s\n", node_label.c_str());
|
||||
#if defined(HOST_BLE_DUAL)
|
||||
std::printf("BLE dual policy=%s\n", ble_dual_policy_name(ble_dual_policy));
|
||||
#endif
|
||||
std::printf("Selected file=%s bytes=%u chunk=%u interval_ms=%lu repeat_rest_ms=%lu\n",
|
||||
SELECTED_TEXT_NAME, (unsigned)SELECTED_TEXT_SIZE,
|
||||
(unsigned)TRANSFER_CHUNK_SIZE,
|
||||
|
|
@ -557,6 +660,9 @@ int main() {
|
|||
while (running) {
|
||||
reticulum.loop();
|
||||
loop_ble_interfaces();
|
||||
#if defined(HOST_BLE_DUAL)
|
||||
enforce_ble_dual_policy();
|
||||
#endif
|
||||
|
||||
uint64_t now = millis64();
|
||||
if (!ble_connected()) {
|
||||
|
|
|
|||
79
tools/run_sync.sh
Executable file
79
tools/run_sync.sh
Executable file
|
|
@ -0,0 +1,79 @@
|
|||
#!/bin/bash
|
||||
# 20260522 ChatGPT
|
||||
# $Header$
|
||||
#
|
||||
# Example:
|
||||
# ./run_sync.sh little_boy_blue 14:40
|
||||
# ./run_sync.sh childrens_hour 14:40
|
||||
# ./run_sync.sh ./little_boy_blue 14:40
|
||||
#
|
||||
# Optional with timeout:
|
||||
# ./run_sync.sh little_boy_blue 14:40 300
|
||||
#
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if [ "$#" -lt 2 ] || [ "$#" -gt 3 ]; then
|
||||
echo "Usage: $0 binary_name HH:MM [run_seconds]"
|
||||
echo "Example: $0 little_boy_blue 14:40"
|
||||
echo "Example: $0 little_boy_blue 14:40 300"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BINARY="$1"
|
||||
START_TIME="$2"
|
||||
RUN_SECONDS="${3:-}"
|
||||
|
||||
# If the binary name does not contain a slash, assume it is in the current directory.
|
||||
case "$BINARY" in
|
||||
*/*) CMD="$BINARY" ;;
|
||||
*) CMD="./$BINARY" ;;
|
||||
esac
|
||||
|
||||
if [ ! -x "$CMD" ]; then
|
||||
echo "ERROR: command is not executable: $CMD"
|
||||
echo
|
||||
echo "Check with:"
|
||||
echo " ls -l $CMD"
|
||||
echo " chmod 755 $CMD"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TARGET_EPOCH=$(date -d "today ${START_TIME}" +%s)
|
||||
NOW_EPOCH=$(date +%s)
|
||||
|
||||
# If requested time already passed today, assume tomorrow.
|
||||
if [ "$TARGET_EPOCH" -le "$NOW_EPOCH" ]; then
|
||||
TARGET_EPOCH=$(date -d "tomorrow ${START_TIME}" +%s)
|
||||
fi
|
||||
|
||||
SLEEP_SECONDS=$(( TARGET_EPOCH - NOW_EPOCH ))
|
||||
|
||||
mkdir -p "$HOME/logs"
|
||||
|
||||
echo "Host: $(hostname)"
|
||||
echo "Binary: $CMD"
|
||||
echo "Now: $(date '+%Y%m%d_%H%M%S %Z')"
|
||||
echo "Target time: $(date -d "@${TARGET_EPOCH}" '+%Y%m%d_%H%M%S %Z')"
|
||||
echo "Sleeping for: ${SLEEP_SECONDS} seconds"
|
||||
|
||||
sleep "$SLEEP_SECONDS"
|
||||
|
||||
STAMP=$(date +%Y%m%d_%H%M%S)
|
||||
SAFE_BINARY=$(basename "$BINARY")
|
||||
LOG="$HOME/logs/$(hostname)_${SAFE_BINARY}_${STAMP}.log"
|
||||
|
||||
{
|
||||
echo "SYNC_START_HOST=$(hostname)"
|
||||
echo "SYNC_START_BINARY=$CMD"
|
||||
echo "SYNC_START_EPOCH=$(date +%s.%N)"
|
||||
echo "SYNC_START_LOCAL=$(date '+%Y%m%d_%H%M%S.%N %Z')"
|
||||
echo "SYNC_LOG_FILE=$LOG"
|
||||
echo "========================================"
|
||||
} | tee "$LOG"
|
||||
|
||||
if [ -n "$RUN_SECONDS" ]; then
|
||||
exec timeout "$RUN_SECONDS"s "$CMD" 2>&1 | tee -a "$LOG"
|
||||
else
|
||||
exec "$CMD" 2>&1 | tee -a "$LOG"
|
||||
fi
|
||||
Loading…
Add table
Add a link
Reference in a new issue