kopia lustrzana https://github.com/ge0rg/aprsdroid
213 wiersze
7.9 KiB
Scala
213 wiersze
7.9 KiB
Scala
package org.aprsdroid.app
|
|
import _root_.android.util.Log
|
|
import scala.collection.mutable
|
|
import _root_.net.ab0oo.aprs.parser._
|
|
import java.util.Date
|
|
|
|
class DigipeaterService(prefs: PrefsWrapper, TAG: String, sendDigipeatedPacket: String => Unit) {
|
|
private val recentDigipeats: mutable.Map[String, Instant] = mutable.Map()
|
|
|
|
def dedupeTime: Int = prefs.getStringInt("p.dedupe", 30) // Fetch the latest dedupe time from preferences
|
|
def digipeaterpath: String = prefs.getString("digipeater_path", "WIDE1,WIDE2") // Fetch digipeater path from preferences
|
|
|
|
// Function to add or update the digipeat
|
|
def storeDigipeat(sourceCall: String, destinationCall: String, payload: String): Unit = {
|
|
// Unique identifier using source call, destination call, and payload
|
|
val key = s"$sourceCall>$destinationCall:$payload"
|
|
recentDigipeats(key) = new.Date() // Store the current timestamp
|
|
}
|
|
|
|
// Function to filter digipeats that are older than dedupeTime seconds
|
|
def isDigipeatRecent(sourceCall: String, destinationCall: String, payload: String): Boolean = {
|
|
// Unique identifier using source call, destination call, and payload
|
|
val key = s"$sourceCall>$destinationCall:$payload"
|
|
recentDigipeats.get(key) match {
|
|
case Some(timestamp) =>
|
|
// Check if the packet was heard within the last dedupeTime seconds
|
|
val now = new Date()
|
|
now.getTime - timestamp.getTime < (dedupeTime * 1000)
|
|
case None =>
|
|
false // Not found in recent digipeats
|
|
}
|
|
}
|
|
|
|
// Function to clean up old entries
|
|
def cleanupOldDigipeats(): Unit = {
|
|
val now = new Date()
|
|
// Retain only those digipeats that are within the last dedupeTime seconds
|
|
recentDigipeats.retain { case (_, timestamp) =>
|
|
now.getTime - timestamp.getTime < (dedupeTime * 1000)
|
|
}
|
|
}
|
|
|
|
def processIncomingPost(post: String) {
|
|
Log.d(TAG, "POST STRING TEST: " + post) // Log the incoming post for debugging
|
|
|
|
// Check if backendName contains "KISS" or "AFSK"
|
|
if (prefs.getBackendName().contains("KISS") || prefs.getBackendName().contains("AFSK")) {
|
|
android.util.Log.d("PrefsAct", "Backend contains KISS or AFSK")
|
|
} else {
|
|
android.util.Log.d("PrefsAct", "Backend does not contain KISS or AFSK")
|
|
return
|
|
}
|
|
//TODO, Add workaround for unsupported formats.
|
|
// Attempt to parse the incoming post to an APRSPacket.
|
|
val packet = try {
|
|
Parser.parse(post) // Attempt to parse
|
|
} catch {
|
|
case e: Exception =>
|
|
Log.e("Parsing FAILED!", s"Failed to parse packet: $post", e)
|
|
return // Exit the function if parsing fails
|
|
}
|
|
|
|
// New regen
|
|
if (!prefs.isDigipeaterEnabled() && prefs.isRegenerateEnabled()) {
|
|
Log.d("APRSdroid.Service", "Regen enabled")
|
|
sendDigipeatedPacket(packet.toString)
|
|
return // Exit if both digipeating and regeneration are enabled
|
|
}
|
|
|
|
// Check if the digipeating setting is enabled
|
|
if (!prefs.isDigipeaterEnabled()) {
|
|
Log.d("APRSdroid.Service", "Digipeating is disabled; skipping processing.")
|
|
return // Exit if digipeating is not enabled
|
|
}
|
|
|
|
cleanupOldDigipeats() // Clean up old digipeats before processing
|
|
|
|
// Try to parse the incoming post to an APRSPacket
|
|
try {
|
|
// Now you can access the source call from the packet
|
|
val callssid = prefs.getCallSsid()
|
|
val sourceCall = packet.getSourceCall()
|
|
val destinationCall = packet.getDestinationCall();
|
|
val lastUsedDigi = packet.getDigiString()
|
|
val payload = packet.getAprsInformation()
|
|
|
|
val payloadString = packet.getAprsInformation().toString() // Ensure payload is a String
|
|
|
|
|
|
// Check if callssid matches sourceCall; if they match, do not digipeat
|
|
if (callssid == sourceCall) {
|
|
Log.d("APRSdroid.Service", s"No digipeat: callssid ($callssid) matches source call ($sourceCall).")
|
|
return // Exit if no digipeating is needed
|
|
}
|
|
|
|
// Check if this packet has been digipeated recently
|
|
if (isDigipeatRecent(sourceCall, destinationCall, payloadString)) {
|
|
Log.d("APRSdroid.Service", s"Packet from $sourceCall to $destinationCall and $payload has been heard recently, skipping digipeating.")
|
|
return // Skip processing this packet
|
|
}
|
|
|
|
|
|
val (modifiedDigiPath, digipeatOccurred) = processDigiPath(lastUsedDigi, callssid)
|
|
|
|
|
|
Log.d("APRSdroid.Service", s"Source: $sourceCall")
|
|
Log.d("APRSdroid.Service", s"Destination: $destinationCall")
|
|
Log.d("APRSdroid.Service", s"Digi: $lastUsedDigi")
|
|
Log.d("APRSdroid.Service", s"Modified Digi Path: $modifiedDigiPath")
|
|
|
|
Log.d("APRSdroid.Service", s"Payload: $payload")
|
|
|
|
// Format the string for sending
|
|
val digipeatedPacket = s"$sourceCall>$destinationCall,$modifiedDigiPath:$payload"
|
|
|
|
// Optionally, send a test packet with the formatted string only if a digipeat occurred
|
|
if (digipeatOccurred) {
|
|
sendDigipeatedPacket(digipeatedPacket)
|
|
|
|
// Store the digipeat to the recent list
|
|
storeDigipeat(sourceCall, destinationCall, payloadString)
|
|
|
|
} else {
|
|
Log.d("APRSdroid.Service", "No digipeat occurred, not sending a test packet.")
|
|
}
|
|
|
|
} catch {
|
|
case e: Exception =>
|
|
Log.e("APRSdroid.Service", s"Failed to parse packet: $post", e)
|
|
}
|
|
}
|
|
|
|
def processDigiPath(lastUsedDigi: String, callssid: String): (String, Boolean) = {
|
|
// Log the input Digi path
|
|
Log.d("APRSdroid.Service", s"Original Digi Path: '$lastUsedDigi'")
|
|
|
|
// If lastUsedDigi is empty, return it unchanged
|
|
if (lastUsedDigi.trim.isEmpty) {
|
|
Log.d("APRSdroid.Service", "LastUsedDigi is empty, returning unchanged.")
|
|
return (lastUsedDigi, false)
|
|
}
|
|
|
|
// Remove leading comma for easier processing
|
|
val trimmedPath = lastUsedDigi.stripPrefix(",")
|
|
|
|
// Split the path into components, avoiding empty strings
|
|
val pathComponents = trimmedPath.split(",").toList.filter(_.nonEmpty)
|
|
val digipeaterPaths = digipeaterpath.split(",").toList.filter(_.nonEmpty)
|
|
|
|
// Create a new list of components with modifications
|
|
val (modifiedPath, modified) = pathComponents.foldLeft((List.empty[String], false)) {
|
|
case ((acc, hasModified), component) =>
|
|
|
|
// Check if callssid* is in the path and skip if found
|
|
if (component == s"$callssid*") {
|
|
// Skip digipeating if callssid* is found
|
|
return (lastUsedDigi, false) // Return the original path, do not modify
|
|
|
|
} else if (!hasModified && (digipeaterPaths.exists(path => component.split("-")(0) == path) || digipeaterPaths.contains(component) || component == callssid)) {
|
|
// We need to check if the first unused component matches digipeaterpath
|
|
if (acc.isEmpty || acc.last.endsWith("*")) {
|
|
// This is the first unused component
|
|
component match {
|
|
|
|
case w if w.matches(".*-(\\d+)$") =>
|
|
// Extract the number from the suffix
|
|
val number = w.split("-").last.toInt
|
|
// Decrement the number
|
|
val newNumber = number - 1
|
|
|
|
if (newNumber == 0 || w == callssid) {
|
|
// If the number is decremented to 0, remove the component and insert callssid*
|
|
(acc :+ s"$callssid*", true)
|
|
|
|
} else {
|
|
// Otherwise, decrement the number and keep the component
|
|
val newComponent = w.stripSuffix(s"-$number") + s"-$newNumber"
|
|
(acc :+ s"$callssid*" :+ newComponent, true)
|
|
}
|
|
|
|
case _ =>
|
|
// Leave unchanged if there's no -N suffix
|
|
(acc :+ component, hasModified)
|
|
}
|
|
|
|
} else {
|
|
// If the first unused component doesn't match digipeaterpath, keep unchanged
|
|
(acc :+ component, hasModified)
|
|
}
|
|
|
|
} else {
|
|
// Keep the component as it is
|
|
(acc :+ component, hasModified)
|
|
}
|
|
}
|
|
|
|
// Rebuild the modified path
|
|
val resultPath = modifiedPath.mkString(",")
|
|
|
|
// Log the modified path before returning
|
|
Log.d("APRSdroid.Service", s"Modified Digi Path: '$resultPath'")
|
|
|
|
// If no modification occurred, return the original lastUsedDigi
|
|
if (resultPath == trimmedPath) {
|
|
Log.d("APRSdroid.Service", "No modifications were made; returning the original path.")
|
|
return (lastUsedDigi, false)
|
|
}
|
|
|
|
// Return the modified path with a leading comma
|
|
(s"$resultPath", true)
|
|
}
|
|
|
|
} |