Improved Network Insights Feature

This commit is contained in:
Russel Yasol
2025-08-24 20:43:13 +08:00
parent 629d9a35bc
commit e2ea01f64a
73 changed files with 471 additions and 99 deletions

View File

@@ -0,0 +1,372 @@
#!/bin/sh
# Network Insights Interpreter Service
# Monitors qcainfo.json and generates network event interpretations
# OpenWrt/BusyBox compatible version
# Configuration
QCAINFO_FILE="/www/signal_graphs/qcainfo.json"
INTERPRETED_FILE="/tmp/interpreted_result.json"
LAST_ENTRY_FILE="/tmp/last_qcainfo_entry.json"
LOCKFILE="/tmp/network_interpreter.lock"
MAX_INTERPRETATIONS=50
# Logging function (OpenWrt compatible)
log_message() {
if command -v logger >/dev/null 2>&1; then
logger -t network_interpreter -p daemon.info "$1"
else
# Use simpler date format for BusyBox
echo "$(date) [network_interpreter] $1" >&2
fi
}
# Convert datetime to timestamp (OpenWrt/BusyBox compatible)
datetime_to_timestamp() {
local datetime="$1"
# Try GNU date first, fallback to string comparison for BusyBox
if date -d "$datetime" +%s >/dev/null 2>&1; then
date -d "$datetime" +%s
else
# For BusyBox, just return the datetime string for string comparison
# This is less precise but works for sequential comparison
echo "$datetime"
fi
}
# Compare timestamps/datetime strings (OpenWrt compatible)
is_datetime_newer() {
local datetime1="$1"
local datetime2="$2"
local ts1=$(datetime_to_timestamp "$datetime1")
local ts2=$(datetime_to_timestamp "$datetime2")
# If we got numeric timestamps, compare numerically
if [ "$ts1" -eq "$ts1" ] 2>/dev/null && [ "$ts2" -eq "$ts2" ] 2>/dev/null; then
[ "$ts1" -gt "$ts2" ]
else
# Fall back to string comparison (works for ISO format)
[ "$datetime1" \> "$datetime2" ]
fi
}
# Parse QCAINFO output to extract band information
parse_qcainfo_bands() {
local output="$1"
# Clean up the output - remove escape sequences and extra characters
local clean_output=$(echo "$output" | tr -d '\r' | sed 's/\\r//g; s/\\n/\n/g')
# Extract all band information from QCAINFO lines
echo "$clean_output" | grep "+QCAINFO:" | while IFS= read -r line; do
if echo "$line" | grep -q "LTE BAND"; then
band=$(echo "$line" | sed -n 's/.*"LTE BAND \([0-9][0-9]*\)".*/B\1/p')
if [ -n "$band" ]; then
echo "LTE:$band"
fi
elif echo "$line" | grep -q "NR5G BAND"; then
band=$(echo "$line" | sed -n 's/.*"NR5G BAND \([0-9][0-9]*\)".*/N\1/p')
if [ -n "$band" ]; then
echo "NR5G:$band"
fi
fi
done
}
# Get network mode from bands
get_network_mode() {
local bands="$1"
local has_lte=false
local has_nr5g=false
if echo "$bands" | grep -q "LTE:"; then
has_lte=true
fi
if echo "$bands" | grep -q "NR5G:"; then
has_nr5g=true
fi
if [ "$has_lte" = true ] && [ "$has_nr5g" = true ]; then
echo "NSA"
elif [ "$has_lte" = true ]; then
echo "LTE"
elif [ "$has_nr5g" = true ]; then
echo "SA"
else
echo "NO_SIGNAL"
fi
}
# Get band list from parsed bands
get_band_list() {
local bands="$1"
if [ -z "$bands" ]; then
echo ""
return
fi
echo "$bands" | sed 's/LTE://g; s/NR5G://g' | sort -u | tr '\n' ',' | sed 's/,$//'
}
# Get carrier count
get_carrier_count() {
local bands="$1"
if [ -z "$bands" ]; then
echo "0"
return
fi
echo "$bands" | wc -l
}
# Compare two band configurations and generate interpretation
compare_configurations() {
local base_output="$1"
local new_output="$2"
local base_datetime="$3"
local new_datetime="$4"
# Parse both configurations
local base_bands=$(parse_qcainfo_bands "$base_output")
local new_bands=$(parse_qcainfo_bands "$new_output")
local base_mode=$(get_network_mode "$base_bands")
local new_mode=$(get_network_mode "$new_bands")
local base_band_list=$(get_band_list "$base_bands")
local new_band_list=$(get_band_list "$new_bands")
local base_carrier_count=$(get_carrier_count "$base_bands")
local new_carrier_count=$(get_carrier_count "$new_bands")
local interpretations=""
# Check for no signal condition
if [ "$new_mode" = "NO_SIGNAL" ]; then
if [ "$base_mode" != "NO_SIGNAL" ]; then
interpretations="Signal lost - No cellular connection detected"
fi
# Check if signal was restored
elif [ "$base_mode" = "NO_SIGNAL" ] && [ "$new_mode" != "NO_SIGNAL" ]; then
interpretations="Signal restored - Connected to $new_mode network"
if [ -n "$new_band_list" ]; then
interpretations="$interpretations ($new_band_list)"
fi
# Check if CA was activated immediately upon signal restoration
if [ "$new_carrier_count" -gt 1 ]; then
interpretations="$interpretations; Carrier Aggregation activated - Now using $new_carrier_count carriers"
fi
else
# Network mode changes
if [ "$base_mode" != "$new_mode" ]; then
case "$new_mode" in
"LTE")
if [ "$base_mode" = "NSA" ]; then
interpretations="Network mode changed from NSA to LTE-only"
elif [ "$base_mode" = "SA" ]; then
interpretations="Network mode changed from 5G SA to LTE"
fi
;;
"SA")
if [ "$base_mode" = "LTE" ]; then
interpretations="Network mode changed from LTE to 5G SA"
elif [ "$base_mode" = "NSA" ]; then
interpretations="Network mode changed from NSA to 5G SA"
fi
;;
"NSA")
if [ "$base_mode" = "LTE" ]; then
interpretations="Network mode changed from LTE to NSA"
elif [ "$base_mode" = "SA" ]; then
interpretations="Network mode changed from 5G SA to NSA"
fi
;;
esac
fi
# Band changes
if [ "$base_band_list" != "$new_band_list" ]; then
if [ -n "$interpretations" ]; then
interpretations="$interpretations; "
fi
# Find added and removed bands
local added_bands=""
local removed_bands=""
# Check for new bands
for band in $(echo "$new_band_list" | tr ',' ' '); do
if [ -n "$band" ] && ! echo "$base_band_list" | grep -q "$band"; then
if [ -n "$added_bands" ]; then
added_bands="$added_bands, $band"
else
added_bands="$band"
fi
fi
done
# Check for removed bands
for band in $(echo "$base_band_list" | tr ',' ' '); do
if [ -n "$band" ] && ! echo "$new_band_list" | grep -q "$band"; then
if [ -n "$removed_bands" ]; then
removed_bands="$removed_bands, $band"
else
removed_bands="$band"
fi
fi
done
if [ -n "$added_bands" ] && [ -n "$removed_bands" ]; then
interpretations="${interpretations}Band configuration changed - Added: $added_bands, Removed: $removed_bands"
elif [ -n "$added_bands" ]; then
interpretations="${interpretations}New bands added: $added_bands"
elif [ -n "$removed_bands" ]; then
interpretations="${interpretations}Bands removed: $removed_bands"
else
interpretations="${interpretations}Band sequence changed from ($base_band_list) to ($new_band_list)"
fi
fi
# Carrier Aggregation changes
if [ "$base_carrier_count" != "$new_carrier_count" ]; then
if [ -n "$interpretations" ]; then
interpretations="$interpretations; "
fi
if [ "$new_carrier_count" -gt 1 ] && [ "$base_carrier_count" -le 1 ]; then
interpretations="${interpretations}Carrier Aggregation activated - Now using $new_carrier_count carriers"
elif [ "$new_carrier_count" -le 1 ] && [ "$base_carrier_count" -gt 1 ]; then
interpretations="${interpretations}Carrier Aggregation deactivated - Single carrier mode"
elif [ "$new_carrier_count" -gt "$base_carrier_count" ]; then
interpretations="${interpretations}Additional carriers aggregated - Carriers increased from $base_carrier_count to $new_carrier_count"
elif [ "$new_carrier_count" -lt "$base_carrier_count" ]; then
interpretations="${interpretations}Carriers reduced from $base_carrier_count to $new_carrier_count"
fi
fi
fi
# Return interpretation if any changes detected
if [ -n "$interpretations" ]; then
echo "$interpretations"
fi
}
# Add interpretation to JSON file
add_interpretation() {
local datetime="$1"
local interpretation="$2"
# Initialize file if it doesn't exist
if [ ! -f "$INTERPRETED_FILE" ]; then
echo "[]" > "$INTERPRETED_FILE"
fi
# Add new interpretation using jq
local temp_file="${INTERPRETED_FILE}.tmp.$$"
jq --arg dt "$datetime" \
--arg interp "$interpretation" \
'. + [{"datetime": $dt, "interpretation": $interp}] | .[-'"$MAX_INTERPRETATIONS"':]' \
"$INTERPRETED_FILE" > "$temp_file" 2>/dev/null && mv "$temp_file" "$INTERPRETED_FILE"
chmod 644 "$INTERPRETED_FILE"
log_message "Added interpretation: $interpretation"
}
# Process QCAINFO entries and generate interpretations
process_qcainfo_data() {
if [ ! -f "$QCAINFO_FILE" ]; then
log_message "QCAINFO file not found: $QCAINFO_FILE"
return 1
fi
# Get total number of entries
local total_entries=$(jq 'length' "$QCAINFO_FILE" 2>/dev/null || echo "0")
if [ "$total_entries" -lt 2 ]; then
log_message "Not enough entries to compare ($total_entries)"
return 0
fi
# Get the last processed entry timestamp
local last_processed=""
if [ -f "$LAST_ENTRY_FILE" ]; then
last_processed=$(cat "$LAST_ENTRY_FILE" 2>/dev/null)
fi
# Process entries sequentially
local i=0
while [ "$i" -lt $((total_entries - 1)) ]; do
local base_entry=$(jq -r ".[$i]" "$QCAINFO_FILE" 2>/dev/null)
local next_entry=$(jq -r ".[$(($i + 1))]" "$QCAINFO_FILE" 2>/dev/null)
local base_datetime=$(echo "$base_entry" | jq -r '.datetime' 2>/dev/null)
local next_datetime=$(echo "$next_entry" | jq -r '.datetime' 2>/dev/null)
local base_output=$(echo "$base_entry" | jq -r '.output' 2>/dev/null)
local next_output=$(echo "$next_entry" | jq -r '.output' 2>/dev/null)
# Skip if this entry was already processed
if [ -n "$last_processed" ] && [ "$next_datetime" = "$last_processed" ]; then
i=$((i + 1))
continue
fi
# Only process entries after the last processed one
if [ -n "$last_processed" ]; then
if ! is_datetime_newer "$next_datetime" "$last_processed"; then
i=$((i + 1))
continue
fi
fi
# Compare configurations and generate interpretation
local interpretation=$(compare_configurations "$base_output" "$next_output" "$base_datetime" "$next_datetime")
if [ -n "$interpretation" ]; then
add_interpretation "$next_datetime" "$interpretation"
fi
i=$((i + 1))
done
# Update last processed entry
if [ "$total_entries" -gt 0 ]; then
local last_datetime=$(jq -r '.[-1].datetime' "$QCAINFO_FILE" 2>/dev/null)
echo "$last_datetime" > "$LAST_ENTRY_FILE"
fi
}
# Check for new entries every 61 seconds
monitor_qcainfo() {
log_message "Starting network insights interpreter monitoring"
while true; do
# Acquire lock (OpenWrt compatible)
if (set -C; echo $$ > "$LOCKFILE") 2>/dev/null; then
trap 'rm -f "$LOCKFILE"; exit' INT TERM EXIT
process_qcainfo_data
# Release lock
rm -f "$LOCKFILE"
trap - INT TERM EXIT
else
log_message "Another instance is running, skipping this cycle"
fi
sleep 61
done
}
# Main execution
case "${1:-monitor}" in
"monitor")
monitor_qcainfo
;;
"process")
process_qcainfo_data
;;
*)
echo "Usage: $0 {monitor|process}"
echo " monitor - Run continuous monitoring (default)"
echo " process - Process current data once"
exit 1
;;
esac