trackdirect2/server/trackdirect/websocket/aprsis/AprsISPayloadCreator.py

176 lines
6.3 KiB
Python

import logging
import time
import aprslib
from trackdirect.parser.AprsPacketParser import AprsPacketParser
import trackdirect
from trackdirect.exceptions.TrackDirectParseError import TrackDirectParseError
from trackdirect.websocket.responses.ResponseDataConverter import ResponseDataConverter
from trackdirect.websocket.responses.HistoryResponseCreator import HistoryResponseCreator
class AprsISPayloadCreator():
"""The AprsISPayloadCreator creates a payload to send to client based on data received form the APRS-IS server
"""
def __init__(self, state, db):
"""The __init__ method.
Args:
state (ConnectionState): Instance of ConnectionState which contains the current state of the connection
db (psycopg2.Connection): Database connection (with autocommit)
"""
self.logger = logging.getLogger('trackdirect')
self.state = state
self.db = db
self.responseDataConverter = ResponseDataConverter(state, db)
self.historyResponseCreator = HistoryResponseCreator(state, db)
self.config = trackdirect.TrackDirectConfig()
self.stationHashTimestamps = {}
self.saveOgnStationsWithMissingIdentity = False
if (self.config.saveOgnStationsWithMissingIdentity) :
self.saveOgnStationsWithMissingIdentity = True
def getPayloads(self, line, sourceId):
"""Takes a raw packet and returnes a dict with the parsed result
Args:
line (string): The raw packet as a string
sourceId (int): The id of the source
Returns:
generator
"""
try :
packet = self._parse(line, sourceId)
if (not self._isPacketValid(packet)) :
return
if (packet.stationId not in self.state.stationsOnMapDict) :
self.state.stationsOnMapDict[packet.stationId] = True
if (len(self.state.filterStationIdDict) > 0 and packet.stationId in self.state.filterStationIdDict) :
for response in self._getPreviousPacketsPayload(packet):
yield response
yield self._getRealTimePacketPayload(packet)
except (aprslib.ParseError, aprslib.UnknownFormat, TrackDirectParseError) as exp:
# We could send the raw part even if we failed to parse it...
pass
except (UnicodeDecodeError) as exp:
# just forget about this packet
pass
def _parse(self, line, sourceId) :
"""Parse packet raw
Args:
line (string): The raw packet as a string
sourceId (int): The id of the source
Returns:
Packet
"""
basicPacketDict = aprslib.parse(line)
parser = AprsPacketParser(self.db, self.saveOgnStationsWithMissingIdentity)
parser.setDatabaseWriteAccess(False)
parser.setSourceId(sourceId)
try :
packet = parser.getPacket(basicPacketDict)
if (packet.mapId == 15) :
return None
if (packet.mapId == 4) :
# Looks like we don't have enough info in db to get a markerId, wait some and try again
time.sleep(1)
return parser.getPacket(basicPacketDict)
return packet
except (aprslib.ParseError, aprslib.UnknownFormat, TrackDirectParseError) as exp:
return None
def _isPacketValid(self, packet) :
"""Returns True if specified packet is valid to send to client
Args:
packet (Packet): Found packet that we may want to send to client
Returns:
True if specified packet is valid to send to client
"""
if (packet is None) :
return False
if (packet.mapId == 4) :
return False
if (packet.stationId is None) :
return False
if (packet.markerId is None or packet.markerId == 1) :
return False
if (packet.latitude is None or packet.longitude is None) :
return False
if (len(self.state.filterStationIdDict) > 0) :
if (packet.stationId not in self.state.filterStationIdDict) :
# This packet does not belong to the station Id that the user is filtering on
return False
return True
def _getRealTimePacketPayload(self, packet) :
"""Takes a packet received directly from APRS-IS and a creates a payload response
Args:
packet (Packet): The packet direclty received from APRS-IS
Returns:
Dict
"""
options = ["realtime"]
data = self.responseDataConverter.getResponseData([packet], None, options)
payload = {'payload_response_type': 2, 'data': data}
return payload
def _getPreviousPacketsPayload(self, packet) :
"""Creates payload that contains previous packets for the station that has sent the specified packet
Args:
packet (Packet): The packet direclty received from APRS-IS
Returns:
generator
"""
latestTimestampOnMap = self.state.getStationLatestTimestampOnMap(packet.stationId)
if (latestTimestampOnMap is None) :
latestTimestampOnMap = 0
latestTimestampOnMap = latestTimestampOnMap + 5
if (self.state.isStationHistoryOnMap(packet.stationId)
and packet.markerPrevPacketTimestamp is not None
and packet.timestamp > latestTimestampOnMap
and packet.markerPrevPacketTimestamp > latestTimestampOnMap) :
# Ups! We got a problem, the previous packet for this station has not been sent to client
# If no packets at all had been sent we would have marked the realtime-packet to be overwritten,
# but now we have a missing packet from current station that may never be sent!
# Send it now! This may result in that we send the same packet twice (but client should handle that)
request = {}
request["station_id"] = packet.stationId
request["payload_request_type"] = 7
# This request will send all packets that is missing (maybe also this real-time packet, but we can live with that...)
for response in self.historyResponseCreator.getResponses(request, None) :
yield response