mirror of
https://github.com/meshtastic/firmware.git
synced 2025-10-27 15:02:41 +00:00
Merge branch 'develop' into multi-message-Storage
This commit is contained in:
commit
caff68f2f3
2
.github/workflows/stale_bot.yml
vendored
2
.github/workflows/stale_bot.yml
vendored
@ -20,5 +20,7 @@ jobs:
|
|||||||
uses: actions/stale@v10.1.0
|
uses: actions/stale@v10.1.0
|
||||||
with:
|
with:
|
||||||
days-before-stale: 45
|
days-before-stale: 45
|
||||||
|
stale-issue-message: This issue has not had any comment or update in the last month. If it is still relevant, please post update comments. If no comments are made, this issue will be closed automagically in 7 days.
|
||||||
|
close-issue-message: This issue has not had any comment since the last notice. It has been closed automatically. If this is incorrect, or the issue becomes relevant again, please request that it is reopened.
|
||||||
exempt-issue-labels: pinned,3.0
|
exempt-issue-labels: pinned,3.0
|
||||||
exempt-pr-labels: pinned,3.0
|
exempt-pr-labels: pinned,3.0
|
||||||
|
|||||||
116
bin/kill-github-actions.sh
Executable file
116
bin/kill-github-actions.sh
Executable file
@ -0,0 +1,116 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Script to cancel all running GitHub Actions workflows
|
||||||
|
# Requires GitHub CLI (gh) to be installed and authenticated
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Function to print colored output
|
||||||
|
print_status() {
|
||||||
|
echo -e "${GREEN}[INFO]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_warning() {
|
||||||
|
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if gh CLI is installed
|
||||||
|
if ! command -v gh &> /dev/null; then
|
||||||
|
print_error "GitHub CLI (gh) is not installed. Please install it first:"
|
||||||
|
echo " brew install gh"
|
||||||
|
echo " Or visit: https://cli.github.com/"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if authenticated
|
||||||
|
if ! gh auth status &> /dev/null; then
|
||||||
|
print_error "GitHub CLI is not authenticated. Please run:"
|
||||||
|
echo " gh auth login"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get repository info
|
||||||
|
REPO=$(gh repo view --json owner,name -q '.owner.login + "/" + .name')
|
||||||
|
if [[ -z "$REPO" ]]; then
|
||||||
|
print_error "Could not determine repository. Make sure you're in a GitHub repository."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_status "Working with repository: $REPO"
|
||||||
|
|
||||||
|
# Get all active workflows (both queued and in-progress)
|
||||||
|
print_status "Fetching active workflows (queued and in-progress)..."
|
||||||
|
QUEUED_WORKFLOWS=$(gh run list --status queued --json databaseId,displayTitle,headBranch,status --limit 100)
|
||||||
|
IN_PROGRESS_WORKFLOWS=$(gh run list --status in_progress --json databaseId,displayTitle,headBranch,status --limit 100)
|
||||||
|
|
||||||
|
# Combine both lists
|
||||||
|
ALL_WORKFLOWS=$(echo "$QUEUED_WORKFLOWS $IN_PROGRESS_WORKFLOWS" | jq -s 'add | unique_by(.databaseId)')
|
||||||
|
|
||||||
|
if [[ "$ALL_WORKFLOWS" == "[]" ]]; then
|
||||||
|
print_status "No active workflows found."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Parse and display active workflows
|
||||||
|
echo
|
||||||
|
print_warning "Found active workflows:"
|
||||||
|
echo "$ALL_WORKFLOWS" | jq -r '.[] | " - \(.displayTitle) (Branch: \(.headBranch), Status: \(.status), ID: \(.databaseId))"'
|
||||||
|
|
||||||
|
echo
|
||||||
|
read -p "Do you want to cancel ALL these workflows? (y/N): " -n 1 -r
|
||||||
|
echo
|
||||||
|
|
||||||
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
print_status "Cancelled by user."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Cancel each workflow
|
||||||
|
print_status "Cancelling workflows..."
|
||||||
|
CANCELLED_COUNT=0
|
||||||
|
FAILED_COUNT=0
|
||||||
|
|
||||||
|
while IFS= read -r WORKFLOW_ID; do
|
||||||
|
if [[ -n "$WORKFLOW_ID" ]]; then
|
||||||
|
print_status "Cancelling workflow ID: $WORKFLOW_ID"
|
||||||
|
if gh run cancel "$WORKFLOW_ID" 2>/dev/null; then
|
||||||
|
((CANCELLED_COUNT++))
|
||||||
|
else
|
||||||
|
print_error "Failed to cancel workflow ID: $WORKFLOW_ID"
|
||||||
|
((FAILED_COUNT++))
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done < <(echo "$ALL_WORKFLOWS" | jq -r '.[].databaseId')
|
||||||
|
|
||||||
|
echo
|
||||||
|
print_status "Summary:"
|
||||||
|
echo " - Cancelled: $CANCELLED_COUNT workflows"
|
||||||
|
if [[ $FAILED_COUNT -gt 0 ]]; then
|
||||||
|
echo " - Failed: $FAILED_COUNT workflows"
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_status "Done!"
|
||||||
|
|
||||||
|
# Optional: Show remaining active workflows
|
||||||
|
echo
|
||||||
|
print_status "Checking for any remaining active workflows..."
|
||||||
|
REMAINING_QUEUED=$(gh run list --status queued --json databaseId --limit 10)
|
||||||
|
REMAINING_IN_PROGRESS=$(gh run list --status in_progress --json databaseId --limit 10)
|
||||||
|
REMAINING_ALL=$(echo "$REMAINING_QUEUED $REMAINING_IN_PROGRESS" | jq -s 'add | unique_by(.databaseId)')
|
||||||
|
|
||||||
|
if [[ "$REMAINING_ALL" == "[]" ]]; then
|
||||||
|
print_status "All workflows successfully cancelled."
|
||||||
|
else
|
||||||
|
REMAINING_COUNT=$(echo "$REMAINING_ALL" | jq '. | length')
|
||||||
|
print_warning "Still $REMAINING_COUNT workflows active (may take a moment to update status)"
|
||||||
|
fi
|
||||||
@ -15,6 +15,7 @@
|
|||||||
#include "Router.h"
|
#include "Router.h"
|
||||||
#include "SPILock.h"
|
#include "SPILock.h"
|
||||||
#include "TypeConversions.h"
|
#include "TypeConversions.h"
|
||||||
|
#include "concurrency/LockGuard.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "xmodem.h"
|
#include "xmodem.h"
|
||||||
|
|
||||||
@ -71,8 +72,12 @@ void PhoneAPI::handleStartConfig()
|
|||||||
LOG_DEBUG("Got %d files in manifest", filesManifest.size());
|
LOG_DEBUG("Got %d files in manifest", filesManifest.size());
|
||||||
|
|
||||||
LOG_INFO("Start API client config");
|
LOG_INFO("Start API client config");
|
||||||
nodeInfoForPhone.num = 0; // Don't keep returning old nodeinfos
|
// Protect against concurrent BLE callbacks: they run in NimBLE's FreeRTOS task and also touch nodeInfoQueue.
|
||||||
nodeInfoQueue.clear();
|
{
|
||||||
|
concurrency::LockGuard guard(&nodeInfoMutex);
|
||||||
|
nodeInfoForPhone = {};
|
||||||
|
nodeInfoQueue.clear();
|
||||||
|
}
|
||||||
resetReadIndex();
|
resetReadIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,8 +99,12 @@ void PhoneAPI::close()
|
|||||||
onConnectionChanged(false);
|
onConnectionChanged(false);
|
||||||
fromRadioScratch = {};
|
fromRadioScratch = {};
|
||||||
toRadioScratch = {};
|
toRadioScratch = {};
|
||||||
nodeInfoForPhone = {};
|
// Clear cached node info under lock because NimBLE callbacks can still be draining it.
|
||||||
nodeInfoQueue.clear();
|
{
|
||||||
|
concurrency::LockGuard guard(&nodeInfoMutex);
|
||||||
|
nodeInfoForPhone = {};
|
||||||
|
nodeInfoQueue.clear();
|
||||||
|
}
|
||||||
packetForPhone = NULL;
|
packetForPhone = NULL;
|
||||||
filesManifest.clear();
|
filesManifest.clear();
|
||||||
fromRadioNum = 0;
|
fromRadioNum = 0;
|
||||||
@ -241,13 +250,20 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
|||||||
LOG_DEBUG("Send My NodeInfo");
|
LOG_DEBUG("Send My NodeInfo");
|
||||||
auto us = nodeDB->readNextMeshNode(readIndex);
|
auto us = nodeDB->readNextMeshNode(readIndex);
|
||||||
if (us) {
|
if (us) {
|
||||||
nodeInfoForPhone = TypeConversions::ConvertToNodeInfo(us);
|
auto info = TypeConversions::ConvertToNodeInfo(us);
|
||||||
nodeInfoForPhone.has_hops_away = false;
|
info.has_hops_away = false;
|
||||||
nodeInfoForPhone.is_favorite = true;
|
info.is_favorite = true;
|
||||||
|
{
|
||||||
|
concurrency::LockGuard guard(&nodeInfoMutex);
|
||||||
|
nodeInfoForPhone = info;
|
||||||
|
}
|
||||||
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_node_info_tag;
|
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_node_info_tag;
|
||||||
fromRadioScratch.node_info = nodeInfoForPhone;
|
fromRadioScratch.node_info = info;
|
||||||
// Should allow us to resume sending NodeInfo in STATE_SEND_OTHER_NODEINFOS
|
// Should allow us to resume sending NodeInfo in STATE_SEND_OTHER_NODEINFOS
|
||||||
nodeInfoForPhone.num = 0;
|
{
|
||||||
|
concurrency::LockGuard guard(&nodeInfoMutex);
|
||||||
|
nodeInfoForPhone.num = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (config_nonce == SPECIAL_NONCE_ONLY_NODES) {
|
if (config_nonce == SPECIAL_NONCE_ONLY_NODES) {
|
||||||
// If client only wants node info, jump directly to sending nodes
|
// If client only wants node info, jump directly to sending nodes
|
||||||
@ -434,23 +450,30 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
|||||||
|
|
||||||
case STATE_SEND_OTHER_NODEINFOS: {
|
case STATE_SEND_OTHER_NODEINFOS: {
|
||||||
LOG_DEBUG("Send known nodes");
|
LOG_DEBUG("Send known nodes");
|
||||||
if (nodeInfoForPhone.num == 0 && !nodeInfoQueue.empty()) {
|
meshtastic_NodeInfo infoToSend = {};
|
||||||
// Serve the next cached node without re-reading from the DB iterator.
|
{
|
||||||
nodeInfoForPhone = nodeInfoQueue.front();
|
concurrency::LockGuard guard(&nodeInfoMutex);
|
||||||
nodeInfoQueue.pop_front();
|
if (nodeInfoForPhone.num == 0 && !nodeInfoQueue.empty()) {
|
||||||
|
// Serve the next cached node without re-reading from the DB iterator.
|
||||||
|
nodeInfoForPhone = nodeInfoQueue.front();
|
||||||
|
nodeInfoQueue.pop_front();
|
||||||
|
}
|
||||||
|
infoToSend = nodeInfoForPhone;
|
||||||
|
if (infoToSend.num != 0)
|
||||||
|
nodeInfoForPhone = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nodeInfoForPhone.num != 0) {
|
if (infoToSend.num != 0) {
|
||||||
// Just in case we stored a different user.id in the past, but should never happen going forward
|
// Just in case we stored a different user.id in the past, but should never happen going forward
|
||||||
sprintf(nodeInfoForPhone.user.id, "!%08x", nodeInfoForPhone.num);
|
sprintf(infoToSend.user.id, "!%08x", infoToSend.num);
|
||||||
LOG_DEBUG("nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s", nodeInfoForPhone.num, nodeInfoForPhone.last_heard,
|
LOG_DEBUG("nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s", infoToSend.num, infoToSend.last_heard,
|
||||||
nodeInfoForPhone.user.id, nodeInfoForPhone.user.long_name);
|
infoToSend.user.id, infoToSend.user.long_name);
|
||||||
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_node_info_tag;
|
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_node_info_tag;
|
||||||
fromRadioScratch.node_info = nodeInfoForPhone;
|
fromRadioScratch.node_info = infoToSend;
|
||||||
nodeInfoForPhone = {};
|
|
||||||
prefetchNodeInfos();
|
prefetchNodeInfos();
|
||||||
} else {
|
} else {
|
||||||
LOG_DEBUG("Done sending nodeinfo");
|
LOG_DEBUG("Done sending nodeinfo");
|
||||||
|
concurrency::LockGuard guard(&nodeInfoMutex);
|
||||||
nodeInfoQueue.clear();
|
nodeInfoQueue.clear();
|
||||||
state = STATE_SEND_FILEMANIFEST;
|
state = STATE_SEND_FILEMANIFEST;
|
||||||
// Go ahead and send that ID right now
|
// Go ahead and send that ID right now
|
||||||
@ -559,20 +582,23 @@ void PhoneAPI::prefetchNodeInfos()
|
|||||||
{
|
{
|
||||||
bool added = false;
|
bool added = false;
|
||||||
// Keep the queue topped up so BLE reads stay responsive even if DB fetches take a moment.
|
// Keep the queue topped up so BLE reads stay responsive even if DB fetches take a moment.
|
||||||
while (nodeInfoQueue.size() < kNodePrefetchDepth) {
|
{
|
||||||
auto nextNode = nodeDB->readNextMeshNode(readIndex);
|
concurrency::LockGuard guard(&nodeInfoMutex);
|
||||||
if (!nextNode)
|
while (nodeInfoQueue.size() < kNodePrefetchDepth) {
|
||||||
break;
|
auto nextNode = nodeDB->readNextMeshNode(readIndex);
|
||||||
|
if (!nextNode)
|
||||||
|
break;
|
||||||
|
|
||||||
auto info = TypeConversions::ConvertToNodeInfo(nextNode);
|
auto info = TypeConversions::ConvertToNodeInfo(nextNode);
|
||||||
bool isUs = info.num == nodeDB->getNodeNum();
|
bool isUs = info.num == nodeDB->getNodeNum();
|
||||||
info.hops_away = isUs ? 0 : info.hops_away;
|
info.hops_away = isUs ? 0 : info.hops_away;
|
||||||
info.last_heard = isUs ? getValidTime(RTCQualityFromNet) : info.last_heard;
|
info.last_heard = isUs ? getValidTime(RTCQualityFromNet) : info.last_heard;
|
||||||
info.snr = isUs ? 0 : info.snr;
|
info.snr = isUs ? 0 : info.snr;
|
||||||
info.via_mqtt = isUs ? false : info.via_mqtt;
|
info.via_mqtt = isUs ? false : info.via_mqtt;
|
||||||
info.is_favorite = info.is_favorite || isUs;
|
info.is_favorite = info.is_favorite || isUs;
|
||||||
nodeInfoQueue.push_back(info);
|
nodeInfoQueue.push_back(info);
|
||||||
added = true;
|
added = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (added)
|
if (added)
|
||||||
@ -614,10 +640,17 @@ bool PhoneAPI::available()
|
|||||||
case STATE_SEND_COMPLETE_ID:
|
case STATE_SEND_COMPLETE_ID:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case STATE_SEND_OTHER_NODEINFOS:
|
case STATE_SEND_OTHER_NODEINFOS: {
|
||||||
if (nodeInfoQueue.empty())
|
concurrency::LockGuard guard(&nodeInfoMutex);
|
||||||
prefetchNodeInfos();
|
if (nodeInfoQueue.empty()) {
|
||||||
|
// Drop the lock before prefetching; prefetchNodeInfos() will re-acquire it.
|
||||||
|
goto PREFETCH_NODEINFO;
|
||||||
|
}
|
||||||
|
}
|
||||||
return true; // Always say we have something, because we might need to advance our state machine
|
return true; // Always say we have something, because we might need to advance our state machine
|
||||||
|
PREFETCH_NODEINFO:
|
||||||
|
prefetchNodeInfos();
|
||||||
|
return true;
|
||||||
case STATE_SEND_PACKETS: {
|
case STATE_SEND_PACKETS: {
|
||||||
if (!queueStatusPacketForPhone)
|
if (!queueStatusPacketForPhone)
|
||||||
queueStatusPacketForPhone = service->getQueueStatusForPhone();
|
queueStatusPacketForPhone = service->getQueueStatusForPhone();
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Observer.h"
|
#include "Observer.h"
|
||||||
|
#include "concurrency/Lock.h"
|
||||||
#include "mesh-pb-constants.h"
|
#include "mesh-pb-constants.h"
|
||||||
#include "meshtastic/portnums.pb.h"
|
#include "meshtastic/portnums.pb.h"
|
||||||
#include <deque>
|
#include <deque>
|
||||||
@ -84,6 +85,8 @@ class PhoneAPI
|
|||||||
std::deque<meshtastic_NodeInfo> nodeInfoQueue;
|
std::deque<meshtastic_NodeInfo> nodeInfoQueue;
|
||||||
// Tunable size of the node info cache so we can keep BLE reads non-blocking.
|
// Tunable size of the node info cache so we can keep BLE reads non-blocking.
|
||||||
static constexpr size_t kNodePrefetchDepth = 4;
|
static constexpr size_t kNodePrefetchDepth = 4;
|
||||||
|
// Protect nodeInfoForPhone + nodeInfoQueue because NimBLE callbacks run in a separate FreeRTOS task.
|
||||||
|
concurrency::Lock nodeInfoMutex;
|
||||||
|
|
||||||
meshtastic_ToRadio toRadioScratch = {
|
meshtastic_ToRadio toRadioScratch = {
|
||||||
0}; // this is a static scratch object, any data must be copied elsewhere before returning
|
0}; // this is a static scratch object, any data must be copied elsewhere before returning
|
||||||
|
|||||||
@ -139,7 +139,7 @@ class NimbleBluetoothFromRadioCallback : public NimBLECharacteristicCallbacks
|
|||||||
{
|
{
|
||||||
bluetoothPhoneAPI->phoneWants = true;
|
bluetoothPhoneAPI->phoneWants = true;
|
||||||
bluetoothPhoneAPI->setIntervalFromNow(0);
|
bluetoothPhoneAPI->setIntervalFromNow(0);
|
||||||
std::lock_guard<std::mutex> guard(bluetoothPhoneAPI->nimble_mutex);
|
std::lock_guard<std::mutex> guard(bluetoothPhoneAPI->nimble_mutex); // BLE callbacks run in NimBLE task
|
||||||
|
|
||||||
if (!bluetoothPhoneAPI->hasChecked) {
|
if (!bluetoothPhoneAPI->hasChecked) {
|
||||||
// Fetch payload on demand; prefetch keeps this fast for the first read.
|
// Fetch payload on demand; prefetch keeps this fast for the first read.
|
||||||
|
|||||||
@ -17,6 +17,7 @@ extends = native_base
|
|||||||
build_flags = ${native_base.build_flags}
|
build_flags = ${native_base.build_flags}
|
||||||
!pkg-config --libs libulfius --silence-errors || :
|
!pkg-config --libs libulfius --silence-errors || :
|
||||||
!pkg-config --libs openssl --silence-errors || :
|
!pkg-config --libs openssl --silence-errors || :
|
||||||
|
!pkg-config --cflags --libs sdl2 --silence-errors || :
|
||||||
|
|
||||||
[env:native-tft]
|
[env:native-tft]
|
||||||
extends = native_base
|
extends = native_base
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user