diff --git a/server/trackdirect/objects/Marker.py b/server/trackdirect/objects/Marker.py new file mode 100644 index 0000000..2db8363 --- /dev/null +++ b/server/trackdirect/objects/Marker.py @@ -0,0 +1,50 @@ +from trackdirect.common.Model import Model + + +class Marker(Model): + """Marker represents the marker that each visible packet has, two packet with the same marker id will be connected on map + """ + + def __init__(self, db): + """The __init__ method. + + Args: + db (psycopg2.Connection): Database connection + """ + Model.__init__(self, db) + self.id = None + + def validate(self): + """Returns true on success (when object content is valid), otherwise false + + Returns: + True on success otherwise False + """ + return True + + def insert(self): + """Method to call when we want to save a new object to database + + Since packet will be inserted in batch we never use this method. + + Returns: + True on success otherwise False + """ + if (not self.isExistingObject()): + cursor = self.db.cursor() + cursor.execute("""select nextval('marker_seq')""") + self.id = cursor.fetchone()[0] + cursor.close() + return True + else: + return False + + def update(self): + """Method to call when we want to save changes to database + + Since packet will be updated in batch we never use this method. + + Returns: + True on success otherwise False + """ + return False diff --git a/server/trackdirect/objects/OgnDevice.py b/server/trackdirect/objects/OgnDevice.py new file mode 100644 index 0000000..9b813e2 --- /dev/null +++ b/server/trackdirect/objects/OgnDevice.py @@ -0,0 +1,66 @@ +from trackdirect.common.Model import Model + + +class OgnDevice(Model): + """OgnDevice represents a pre registered device in the ogn ddb + """ + + def __init__(self, db): + """The __init__ method. + + Args: + db (psycopg2.Connection): Database connection + """ + Model.__init__(self, db) + + self.deviceType = None + self.deviceId = None + self.aircraftModel = None + self.registration = None + self.cn = None + self.tracked = None + self.identified = None + self.ddbAircraftType = None # Do not confuse with the aircraft type in aprs message + + def validate(self): + """Returns true on success (when object content is valid), otherwise false + + Returns: + True on success otherwise False + """ + return True + + def insert(self): + """Method to call when we want to save a new object to database + + Returns: + True on success otherwise False + """ + return False + + def update(self): + """Method to call when we want to save changes to database + + Returns: + True on success otherwise False + """ + return False + + def getDict(self): + """Returns a dict representation of the object + + Returns: + Dict representation of the object + """ + data = {} + + data['device_type'] = self.deviceType + data['device_id'] = self.deviceId + data['aircraft_model'] = self.aircraftModel + data['registration'] = self.registration + data['cn'] = self.cn + data['tracked'] = self.tracked + data['identified'] = self.identified + data['ddb_aircraft_type'] = self.ddbAircraftType + + return data diff --git a/server/trackdirect/objects/OgnHiddenStation.py b/server/trackdirect/objects/OgnHiddenStation.py new file mode 100644 index 0000000..aedaf18 --- /dev/null +++ b/server/trackdirect/objects/OgnHiddenStation.py @@ -0,0 +1,65 @@ + +from trackdirect.common.Model import Model + + +class OgnHiddenStation(Model): + """OgnDevice represents a pre registered device in the ogn ddb + """ + + def __init__(self, db): + """The __init__ method. + + Args: + db (psycopg2.Connection): Database connection + """ + Model.__init__(self, db) + + self.id = None + self.hashedName = None + + def getStationName(self): + """Returns the unidentifiable station name used for the current hashed name + + Returns: + string + """ + if (self.isExistingObject()): + return 'UNKNOWN' + str(self.id) + else: + return None + + def validate(self): + """Returns true on success (when object content is valid), otherwise false + + Returns: + True on success otherwise False + """ + return True + + def insert(self): + """Method to call when we want to save a new object to database + + Since packet will be inserted in batch we never use this method. + + Returns: + True on success otherwise False + """ + if (not self.isExistingObject()): + insertCursor = self.db.cursor() + insertCursor.execute( + """insert into ogn_hidden_station(hashed_name) values(%s) RETURNING id""", (str( + self.hashedName).strip(),) + ) + self.id = insertCursor.fetchone()[0] + insertCursor.close() + return True + else: + return False + + def update(self): + """Method to call when we want to save changes to database + + Returns: + True on success otherwise False + """ + return False diff --git a/server/trackdirect/objects/Packet.py b/server/trackdirect/objects/Packet.py new file mode 100644 index 0000000..ccacf5b --- /dev/null +++ b/server/trackdirect/objects/Packet.py @@ -0,0 +1,359 @@ +import logging +import re +from twisted.python import log +import json +import datetime +import time +from math import sin, cos, sqrt, atan2, radians, floor, ceil + +from trackdirect.common.Model import Model +from trackdirect.repositories.StationRepository import StationRepository +from trackdirect.repositories.SenderRepository import SenderRepository +from trackdirect.objects.Station import Station + +from trackdirect.exceptions.TrackDirectMissingSenderError import TrackDirectMissingSenderError +from trackdirect.exceptions.TrackDirectMissingStationError import TrackDirectMissingStationError + + +class Packet(Model): + """Packet represents a APRS packet, AIS packet or any other supported packet + + Note: + Packet corresponds to a row in the packetYYYYMMDD table + """ + + def __init__(self, db): + """The __init__ method. + + Args: + db (psycopg2.Connection): Database connection + """ + Model.__init__(self, db) + self.logger = logging.getLogger('trackdirect') + + self.id = None + self.stationId = None + self.senderId = None + self.packetTypeId = None + self.timestamp = None + self.reportedTimestamp = None + self.positionTimestamp = None # Inherited from prev packet if position was equal + self.latitude = None + self.longitude = None + self.symbol = None + self.symbolTable = None + self.markerId = None + self.markerCounter = None + self.markerPrevPacketTimestamp = None + self.mapId = None + self.sourceId = None + self.mapSector = None + self.relatedMapSectors = [] + self.speed = None + self.course = None + self.altitude = None + self.rng = None + self.phg = None + self.latestRngTimestamp = None + self.latestPhgTimestamp = None + self.comment = None + self.rawPath = None + self.raw = None + + # packet tail timestamp indicates how long time ago we had a tail + self.packetTailTimestamp = None + + # If packet reports a new position for a moving symbol is_moving will be 1 otherwise 0 + # Some times is_moving will be 0 for a moving symbol, but as fast we realize it is moving related packets will have is_moving set to 1 + self.isMoving = 1 + + self.posambiguity = None + + # Following attributes will not allways be loaded from database (comes from related tables) + self.stationIdPath = [] + self.stationNamePath = [] + self.stationLocationPath = [] + + # Will only be used when packet is not inserted to database yet + self.replacePacketId = None + self.replacePacketTimestamp = None + self.abnormalPacketId = None + self.abnormalPacketTimestamp = None + self.confirmPacketId = None + self.confirmPacketTimestamp = None + + # Will only be used when packet is not inserted to database yet + self.ogn = None + self.weather = None + self.telemetry = None + self.stationTelemetryBits = None + self.stationTelemetryEqns = None + self.stationTelemetryParam = None + self.stationTelemetryUnit = None + self.senderName = None + self.stationName = None + + def validate(self): + """Returns true on success (when object content is valid), otherwise false + + Returns: + True on success otherwise False + """ + return True + + def insert(self): + """Method to call when we want to save a new object to database + + Since packet will be inserted in batch we never use this method. + + Returns: + True on success otherwise False + """ + return False + + def update(self): + """Method to call when we want to save changes to database + + Since packet will be updated in batch we never use this method. + + Returns: + True on success otherwise False + """ + return False + + def getDistance(self, p2Lat, p2Lng): + """Get distance in meters between current position and specified position + + Args: + p2Lat (float): Position 2 latitude + p2Lng (float): Position 2 longitude + + Returns: + Distance in meters between the two specified positions (as float) + """ + if (self.latitude is not None + and self.longitude is not None): + p1Lat = self.latitude + p1Lng = self.longitude + R = 6378137 # Earths mean radius in meter + dLat = radians(p2Lat - p1Lat) + dLong = radians(p2Lng - p1Lng) + a = sin(dLat / 2) * sin(dLat / 2) + cos(radians(p1Lat)) * \ + cos(radians(p2Lat)) * sin(dLong / 2) * sin(dLong / 2) + c = 2 * atan2(sqrt(a), sqrt(1 - a)) + d = R * c + return d # returns the distance in meter + else: + return None + + def getCalculatedSpeed(self, prevPacket): + """Get speed compared to previous packet position and timestamp + + Args: + prevPacket (Packet): Previous related packet for the same station + + Returns: + Speed in kmh compared to previous packet position and timestamp (as float) + """ + if (self.latitude is not None + and self.longitude is not None): + distance = self.getDistance( + prevPacket.latitude, prevPacket.longitude) + time = abs(prevPacket.timestamp - self.timestamp) + if (self.reportedTimestamp is not None + and prevPacket.reportedTimestamp is not None + and self.reportedTimestamp != 0 + and prevPacket.reportedTimestamp != 0 + and (self.reportedTimestamp % 60 != 0 or prevPacket.reportedTimestamp % 60 != 0) + and prevPacket.reportedTimestamp != self.reportedTimestamp): + time = abs(prevPacket.reportedTimestamp - + self.reportedTimestamp) + + if (time == 0): + return 0 + return distance / time # meters per second + else: + return None + + def isSymbolEqual(self, comparePacket): + """Returns true if current symbol is equal to symbol in specified packet + + Args: + comparePacket (Packet): Packet to compare current symbol with + + Returns: + True if current symbol is equal to symbol in specified packet + """ + if (self.symbol is not None + and self.symbolTable is not None + and comparePacket.symbol is not None + and comparePacket.symbolTable is not None + and self.symbol == comparePacket.symbol + and self.symbolTable == comparePacket.symbolTable): + return True + else: + return False + + def isPostitionEqual(self, comparePacket): + """Returns true if current position is equal to position in specified packet + + Args: + comparePacket (Packet): Packet to compare current position with + + Returns: + True if current position is equal to position in specified packet + """ + if (comparePacket.latitude is not None + and comparePacket.longitude is not None + and self.longitude is not None + and self.latitude is not None + and round(self.latitude, 5) == round(comparePacket.latitude, 5) + and round(self.longitude, 5) == round(comparePacket.longitude, 5)): + return True + else: + return False + + def getTransmitDistance(self): + """Calculate the transmit distance + + Notes: + require that stationLocationPath is set + + Args: + None + + Returns: + Distance in meters for this transmission + """ + if (self.stationLocationPath is None + or len(self.stationLocationPath) < 1): + return None + + location = self.stationLocationPath[0] + if (location[0] is None + or location[1] is None): + return None + + if (self.latitude is not None + and self.longitude is not None): + + # Current packet contains position, use that + return self.getDistance(location[0], location[1]) + else: + + # Current packet is missing position, use latest station position + stationRepository = StationRepository(self.db) + station = stationRepository.getObjectById(self.stationId) + if (not station.isExistingObject()): + return None + + if (station.latestConfirmedLatitude is not None and station.latestConfirmedLongitude is not None): + curStationLatestLocationPacket = Packet(self.db) + curStationLatestLocationPacket.latitude = station.latestConfirmedLatitude + curStationLatestLocationPacket.longitude = station.latestConfirmedLongitude + return curStationLatestLocationPacket.getDistance(location[0], location[1]) + else: + return None + + def getDict(self, includeStationName=False): + """Returns a dict representation of the object + + Args: + includeStationName (Boolean): Include station name and sender name in dict + + Returns: + Dict representation of the object + """ + data = {} + data['id'] = self.id + + if (self.stationId is not None): + data['station_id'] = int(self.stationId) + else: + data['station_id'] = None + + if (self.senderId is not None): + data['sender_id'] = int(self.senderId) + else: + data['sender_id'] = None + + data['packet_type_id'] = self.packetTypeId + data['timestamp'] = self.timestamp + data['reported_timestamp'] = self.reportedTimestamp + data['position_timestamp'] = self.positionTimestamp + + if (self.latitude is not None and self.longitude is not None): + data['latitude'] = float(self.latitude) + data['longitude'] = float(self.longitude) + else: + data['latitude'] = None + data['longitude'] = None + + data['symbol'] = self.symbol + data['symbol_table'] = self.symbolTable + data['marker_id'] = self.markerId + data['marker_counter'] = self.markerCounter + data['map_id'] = self.mapId + data['source_id'] = self.sourceId + data['map_sector'] = self.mapSector + data['related_map_sectors'] = self.relatedMapSectors + data['speed'] = self.speed + data['course'] = self.course + data['altitude'] = self.altitude + data['rng'] = self.rng + data['phg'] = self.phg + data['latest_phg_timestamp'] = self.latestPhgTimestamp + data['latest_rng_timestamp'] = self.latestRngTimestamp + data['comment'] = self.comment + data['raw_path'] = self.rawPath + data['raw'] = self.raw + data['packet_tail_timestamp'] = self.packetTailTimestamp + data['is_moving'] = self.isMoving + data['posambiguity'] = self.posambiguity + data['db'] = 1 + + if (includeStationName): + try: + stationRepository = StationRepository(self.db) + station = stationRepository.getCachedObjectById( + data['station_id']) + data['station_name'] = station.name + except TrackDirectMissingStationError as e: + data['station_name'] = '' + + try: + senderRepository = SenderRepository(self.db) + sender = senderRepository.getCachedObjectById( + data['sender_id']) + data['sender_name'] = sender.name + except TrackDirectMissingSenderError as e: + data['sender_name'] = '' + + data['station_id_path'] = self.stationIdPath + data['station_name_path'] = self.stationNamePath + data['station_location_path'] = self.stationLocationPath + data['telemetry'] = None + if (self.telemetry is not None): + data['telemetry'] = self.telemetry.getDict() + data['weather'] = None + if (self.weather is not None): + data['weather'] = self.weather.getDict() + data['ogn'] = None + if (self.ogn is not None): + data['ogn'] = self.ogn.getDict() + return data + + def getJson(self): + """Returns a json representation of the object + + Returns: + Json representation of the object (returnes None on failure) + """ + data = self.getDict() + + try: + return json.dumps(data, ensure_ascii=False).encode('utf8') + except (ValueError) as exp: + self.logger.error(e, exc_info=1) + + return None diff --git a/server/trackdirect/objects/PacketOgn.py b/server/trackdirect/objects/PacketOgn.py new file mode 100644 index 0000000..74adb4a --- /dev/null +++ b/server/trackdirect/objects/PacketOgn.py @@ -0,0 +1,84 @@ +from trackdirect.common.Model import Model + + +class PacketOgn(Model): + """PacketOgn represents the OGN data in a APRS packet + """ + + def __init__(self, db): + """The __init__ method. + + Args: + db (psycopg2.Connection): Database connection + """ + Model.__init__(self, db) + self.id = None + self.packetId = None + self.stationId = None + self.timestamp = None + self.ognSenderAddress = None + self.ognAddressTypeId = None + self.ognAircraftTypeId = None + self.ognClimbRate = None + self.ognTurnRate = None + self.ognSignalToNoiseRatio = None + self.ognBitErrorsCorrected = None + self.ognFrequencyOffset = None + + def validate(self): + """Returns true on success (when object content is valid), otherwise false + + Returns: + True on success otherwise False + """ + if (self.stationId <= 0): + return False + + if (self.packetId <= 0): + return False + + return True + + def insert(self): + """Method to call when we want to save a new object to database + + Since packet will be inserted in batch we never use this method. + + Returns: + True on success otherwise False + """ + return False + + def update(self): + """Method to call when we want to save changes to database + + Since packet will be updated in batch we never use this method. + + Returns: + True on success otherwise False + """ + return False + + def getDict(self): + """Returns the packet OGN as a dict + + Args: + None + + Returns: + A packet OGN dict + """ + data = {} + data['id'] = self.id + data['packet_id'] = self.packetId + data['station_id'] = self.stationId + data['timestamp'] = self.timestamp + data['ogn_sender_address'] = self.ognSenderAddress + data['ogn_address_type_id'] = self.ognAddressTypeId + data['ogn_aircraft_type_id'] = self.ognAircraftTypeId + data['ogn_climb_rate'] = self.ognClimbRate + data['ogn_turn_rate'] = self.ognTurnRate + data['ogn_signal_to_noise_ratio'] = self.ognSignalToNoiseRatio + data['ogn_bit_errors_corrected'] = self.ognBitErrorsCorrected + data['ogn_frequency_offset'] = self.ognFrequencyOffset + return data diff --git a/server/trackdirect/objects/PacketTelemetry.py b/server/trackdirect/objects/PacketTelemetry.py new file mode 100644 index 0000000..ad2d204 --- /dev/null +++ b/server/trackdirect/objects/PacketTelemetry.py @@ -0,0 +1,105 @@ +from trackdirect.common.Model import Model +from trackdirect.database.PacketTelemetryTableCreator import PacketTelemetryTableCreator + + +class PacketTelemetry(Model): + """PacketTelemetry represents the telemetry data in a APRS packet + """ + + def __init__(self, db): + """The __init__ method. + + Args: + db (psycopg2.Connection): Database connection + """ + Model.__init__(self, db) + self.id = None + self.packetId = None + self.stationId = None + self.timestamp = None + self.val1 = None + self.val2 = None + self.val3 = None + self.val4 = None + self.val5 = None + self.bits = None + self.seq = None + + def validate(self): + """Returns true on success (when object content is valid), otherwise false + + Returns: + True on success otherwise False + """ + if (self.stationId <= 0): + return False + + if (self.packetId <= 0): + return False + + return True + + def insert(self): + """Method to call when we want to save a new object to database + + Since packet will be inserted in batch we never use this method. + + Returns: + True on success otherwise False + """ + return False + + def update(self): + """Method to call when we want to save changes to database + + Since packet will be updated in batch we never use this method. + + Returns: + True on success otherwise False + """ + return False + + def isDuplicate(self): + """Method returnes true if a duplicate exists in database + + Returns: + True if a duplicate exists in database otherwise False + """ + packetTelemetryTableCreator = PacketTelemetryTableCreator(self.db) + packetTelemetryTableCreator.disableCreateIfMissing() + packetTelemetryTable = packetTelemetryTableCreator.getPacketTelemetryTable( + self.timestamp) + if (packetTelemetryTable is not None): + + selectCursor = self.db.cursor() + selectCursor.execute("""select * from """ + packetTelemetryTable + + """ where station_id = %s order by timestamp desc limit 1""", (self.stationId,)) + + record = selectCursor.fetchone() + selectCursor.close() + if record is not None and record['seq'] == self.seq: + return True + return False + + def getDict(self): + """Returns a packet telemetry dict + + Args: + None + + Returns: + A packet telemetry dict + """ + data = {} + data['id'] = self.id + data['packet_id'] = self.packetId + data['station_id'] = self.stationId + data['timestamp'] = self.timestamp + data['val1'] = self.val1 + data['val2'] = self.val2 + data['val3'] = self.val3 + data['val4'] = self.val4 + data['val5'] = self.val5 + data['bits'] = self.bits + data['seq'] = self.seq + return data diff --git a/server/trackdirect/objects/PacketWeather.py b/server/trackdirect/objects/PacketWeather.py new file mode 100644 index 0000000..aaef5eb --- /dev/null +++ b/server/trackdirect/objects/PacketWeather.py @@ -0,0 +1,92 @@ +from trackdirect.common.Model import Model + + +class PacketWeather(Model): + """PacketWeather represents the weather data in a APRS packet + """ + + def __init__(self, db): + """The __init__ method. + + Args: + db (psycopg2.Connection): Database connection + """ + Model.__init__(self, db) + self.id = None + self.packetId = None + self.stationId = None + self.timestamp = None + self.humidity = None + self.pressure = None + self.rain1h = None + self.rain24h = None + self.rainSinceMidnight = None + self.temperature = None + self.windDirection = None + self.windGust = None + self.windSpeed = None + self.luminosity = None + self.snow = None + self.wxRawTimestamp = None + + def validate(self): + """Returns true on success (when object content is valid), otherwise false + + Returns: + True on success otherwise False + """ + if (self.stationId <= 0): + return False + + if (self.packetId <= 0): + return False + + return True + + def insert(self): + """Method to call when we want to save a new object to database + + Since packet will be inserted in batch we never use this method. + + Returns: + True on success otherwise False + """ + return False + + def update(self): + """Method to call when we want to save changes to database + + Since packet will be updated in batch we never use this method. + + Returns: + True on success otherwise False + """ + return False + + def getDict(self): + """Returns the packet weather as a dict + + Args: + None + + Returns: + A packet weather dict + """ + data = {} + data['id'] = self.id + data['packet_id'] = self.packetId + data['station_id'] = self.stationId + data['timestamp'] = self.timestamp + data['humidity'] = self.humidity + data['pressure'] = self.pressure + data['rain_1h'] = self.rain1h + data['rain_24h'] = self.rain24h + data['rain_since_midnight'] = self.rainSinceMidnight + data['temperature'] = self.temperature + data['wind_direction'] = self.windDirection + data['wind_gust'] = self.windGust + data['wind_speed'] = self.windSpeed + data['luminosity'] = self.luminosity + data['snow'] = self.snow + data['wx_raw_timestamp'] = self.wxRawTimestamp + return data diff --git a/server/trackdirect/objects/Sender.py b/server/trackdirect/objects/Sender.py new file mode 100644 index 0000000..6c926be --- /dev/null +++ b/server/trackdirect/objects/Sender.py @@ -0,0 +1,58 @@ +from trackdirect.common.Model import Model + + +class Sender(Model): + """Sender represents the sender of a packet (often same name as station name) + """ + + def __init__(self, db): + """The __init__ method. + + Args: + db (psycopg2.Connection): Database connection + """ + Model.__init__(self, db) + + self.id = None + self.name = None + + def validate(self): + """Returns true on success (when object content is valid), otherwise false + + Returns: + True on success otherwise False + """ + if (self.name == ''): + return False + + return True + + def insert(self): + """Method to call when we want to save a new object to database + + Since packet will be inserted in batch we never use this method. + + Returns: + True on success otherwise False + """ + if (not self.isExistingObject()): + insertCursor = self.db.cursor() + insertCursor.execute( + """insert into sender(name) values(%s) RETURNING id""", (self.name.strip( + ),) + ) + self.id = insertCursor.fetchone()[0] + insertCursor.close() + return True + else: + return False + + def update(self): + """Method to call when we want to save changes to database + + Since packet will be updated in batch we never use this method. + + Returns: + True on success otherwise False + """ + return False diff --git a/server/trackdirect/objects/Station.py b/server/trackdirect/objects/Station.py new file mode 100644 index 0000000..da4e9d8 --- /dev/null +++ b/server/trackdirect/objects/Station.py @@ -0,0 +1,149 @@ +import logging +from trackdirect.common.Model import Model + + +class Station(Model): + """Station represents the object/station that the packet is about + """ + + def __init__(self, db): + """The __init__ method. + + Args: + db (psycopg2.Connection): Database connection + """ + Model.__init__(self, db) + self.logger = logging.getLogger('trackdirect') + + self.id = None + self.name = None + self.latestSenderId = None + self.stationTypeId = 1 # default to 1 + self.sourceId = None + + self.latestPacketId = None + self.latestPacketTimestamp = None + + self.latestLocationPacketId = None + self.latestLocationPacketTimestamp = None + + self.latestWeatherPacketId = None + self.latestWeatherPacketTimestamp = None + + self.latestTelemetryPacketId = None + self.latestTelemetryPacketTimestamp = None + + # Latest packet with a location that is confirmed to be correct + self.latestConfirmedPacketId = None + self.latestConfirmedPacketTimestamp = None + self.latestConfirmedSymbol = None + self.latestConfirmedSymbolTable = None + self.latestConfirmedLatitude = None + self.latestConfirmedLongitude = None + self.latestConfirmedMarkerId = None + + self.latestOgnPacketId = None + self.latestOgnPacketTimestamp = None + self.latestOgnSenderAddress = None + self.latestOgnAircraftTypeId = None + self.latestOgnAddressTypeId = None + + def validate(self): + """Returns true on success (when object content is valid), otherwise false + + Returns: + True on success otherwise False + """ + if (self.name == ''): + return False + + return True + + def insert(self): + """Method to call when we want to save a new object to database + + Since packet will be inserted in batch we never use this method. + + Returns: + True on success otherwise False + """ + if (not self.isExistingObject()): + insertCursor = self.db.cursor() + insertCursor.execute( + """insert into station(name, station_type_id, source_id) values(%s, %s, %s) RETURNING id""", ( + self.name.strip(), self.stationTypeId, self.sourceId) + ) + self.id = insertCursor.fetchone()[0] + insertCursor.close() + return True + else: + return False + + def update(self): + """Method to call when we want to save changes to database + + Returns: + True on success otherwise False + """ + if (self.isExistingObject()): + cursor = self.db.cursor() + cursor.execute( + """update station set source_id = %s, name = %s, station_type_id = %s where id = %s and source_id is null""", ( + self.sourceId, self.name, self.stationTypeId, self.id) + ) + cursor.close() + return True + else: + return False + + def getShortDict(self): + """Returns a dict representation of the object + + Returns: + Dict representation of the object + """ + data = {} + data['id'] = self.id + + data['name'] = self.name + data['latest_sender_id'] = self.latestSenderId + data['station_type_id'] = self.stationTypeId + data['source_id'] = self.sourceId + + if (self.latestConfirmedPacketId is not None): + data['latest_confirmed_packet_id'] = int( + self.latestConfirmedPacketId) + else: + data['latest_confirmed_packet_id'] = None + + data['latest_confirmed_packet_timestamp'] = self.latestConfirmedPacketTimestamp + data['latest_confirmed_symbol'] = self.latestConfirmedSymbol + data['latest_confirmed_symbol_table'] = self.latestConfirmedSymbolTable + + if (self.latestConfirmedLatitude is not None): + data['latest_confirmed_latitude'] = float( + self.latestConfirmedLatitude) + else: + data['latest_confirmed_latitude'] = None + + if (self.latestConfirmedLongitude is not None): + data['latest_confirmed_longitude'] = float( + self.latestConfirmedLongitude) + else: + data['latest_confirmed_longitude'] = None + + if (self.latestLocationPacketId is not None): + data['latest_location_packet_id'] = self.latestLocationPacketId + else: + data['latest_location_packet_id'] = None + + data['latest_location_packet_timestamp'] = self.latestLocationPacketTimestamp + + if (self.latestPacketId is not None): + data['latest_packet_id'] = int(self.latestPacketId) + else: + data['latest_packet_id'] = None + + data['latest_packet_timestamp'] = self.latestPacketTimestamp + + return data diff --git a/server/trackdirect/objects/StationTelemetryBits.py b/server/trackdirect/objects/StationTelemetryBits.py new file mode 100644 index 0000000..e8e90b0 --- /dev/null +++ b/server/trackdirect/objects/StationTelemetryBits.py @@ -0,0 +1,100 @@ +from trackdirect.common.Model import Model + + +class StationTelemetryBits(Model): + """StationTelemetryBits represents the telemetry bits sent by the related station + """ + + def __init__(self, db): + """The __init__ method. + + Args: + db (psycopg2.Connection): Database connection + """ + Model.__init__(self, db) + self.id = None + self.stationId = None + self.createdTs = None + self.latestTs = None + self.validToTs = None + self.bits = None + self.title = None + + def validate(self): + """Returns true on success (when object content is valid), otherwise false + + Returns: + True on success otherwise False + """ + if (type(self.stationId) != int or self.stationId <= 0): + return False + + return True + + def save(self): + """Save object data to database if attribute data is valid + + Returns: + Returns true on success otherwise false + """ + if (self.validate()): + if (self.isExistingObject()): + return self.update() + else: + cursor = self.db.cursor() + cursor.execute("""update station_telemetry_bits + set latest_ts = %s + where station_id = %s + and valid_to_ts is null + and bits = %s + and title = %s""", + (self.createdTs, + self.stationId, + self.bits, + self.title)) + + if (cursor.rowcount == 0): + return self.insert() + else: + # We do not insert it since it was equal to the existsing row + return True + return False + + def insert(self): + """Method to call when we want to save a new object to database + + Returns: + True on success otherwise False + """ + if (not self.isExistingObject()): + insertCursor = self.db.cursor() + insertCursor.execute("""update station_telemetry_bits + set valid_to_ts = %s + where station_id = %s + and valid_to_ts is null""", + (self.createdTs, + self.stationId)) + + insertCursor.execute("""insert into station_telemetry_bits( + station_id, + created_ts, + latest_ts, + bits, + title) + values (%s, %s, %s, %s, %s)""", + (self.stationId, + self.createdTs, + self.createdTs, + self.bits, + self.title)) + return True + else: + return False + + def update(self): + """Method to call when we want to save changes to database + + Returns: + True on success otherwise False + """ + return False diff --git a/server/trackdirect/objects/StationTelemetryEqns.py b/server/trackdirect/objects/StationTelemetryEqns.py new file mode 100644 index 0000000..85b07b8 --- /dev/null +++ b/server/trackdirect/objects/StationTelemetryEqns.py @@ -0,0 +1,161 @@ +from trackdirect.common.Model import Model + + +class StationTelemetryEqns(Model): + """StationTelemetryEqns represents the telemetry equations sent by the related station + """ + + def __init__(self, db): + """The __init__ method. + + Args: + db (psycopg2.Connection): Database connection + """ + Model.__init__(self, db) + self.id = None + self.stationId = None + self.createdTs = None + self.latestTs = None + self.validToTs = None + self.a1 = None + self.b1 = None + self.c1 = None + self.a2 = None + self.b2 = None + self.c2 = None + self.a3 = None + self.b3 = None + self.c3 = None + self.a4 = None + self.b4 = None + self.c4 = None + self.a5 = None + self.b5 = None + self.c5 = None + + def validate(self): + """Returns true on success (when object content is valid), otherwise false + + Returns: + True on success otherwise False + """ + if (type(self.stationId) != int or self.stationId <= 0): + return False + + return True + + def save(self): + """Save object data to database if attribute data is valid + + Returns: + Returns true on success otherwise false + """ + if (self.validate()): + if (self.isExistingObject()): + return self.update() + else: + cursor = self.db.cursor() + cursor.execute("""update station_telemetry_eqns + set latest_ts = %s + where station_id = %s + and valid_to_ts is null + and a1::numeric = %s + and b1::numeric = %s + and c1::numeric = %s + + and a2::numeric = %s + and b2::numeric = %s + and c2::numeric = %s + + and a3::numeric = %s + and b3::numeric = %s + and c3::numeric = %s + + and a4::numeric = %s + and b4::numeric = %s + and c4::numeric = %s + + and a5::numeric = %s + and b5::numeric = %s + and c5::numeric = %s""", + + (self.createdTs, + self.stationId, + self.a1, + self.b1, + self.c1, + + self.a2, + self.b2, + self.c2, + + self.a3, + self.b3, + self.c3, + + self.a4, + self.b4, + self.c4, + + self.a5, + self.b5, + self.c5)) + + if (cursor.rowcount == 0): + return self.insert() + else: + # We do not insert it since it was equal to the existsing row + return True + return False + + def insert(self): + """Method to call when we want to save a new object to database + + Returns: + True on success otherwise False + """ + if (not self.isExistingObject()): + insertCursor = self.db.cursor() + insertCursor.execute("""update station_telemetry_eqns + set valid_to_ts = %s + where station_id = %s + and valid_to_ts is null""", ( + self.createdTs, + self.stationId)) + + insertCursor.execute("""insert into station_telemetry_eqns(station_id, created_ts, latest_ts, a1, b1, c1, a2, b2, c2, a3, b3, c3, a4, b4, c4, a5, b5, c5) + values (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""", + (self.stationId, + self.createdTs, + self.timestamp, + + self.a1, + self.b1, + self.c1, + + self.a2, + self.b2, + self.c2, + + self.a3, + self.b3, + self.c3, + + self.a4, + self.b4, + self.c4, + + self.a5, + self.b5, + self.c5)) + return True + else: + return False + + def update(self): + """Method to call when we want to save changes to database + + Returns: + True on success otherwise False + """ + return False diff --git a/server/trackdirect/objects/StationTelemetryParam.py b/server/trackdirect/objects/StationTelemetryParam.py new file mode 100644 index 0000000..11657f0 --- /dev/null +++ b/server/trackdirect/objects/StationTelemetryParam.py @@ -0,0 +1,139 @@ +from trackdirect.common.Model import Model + + +class StationTelemetryParam(Model): + """StationTelemetryParam represents the telemetry parameters sent by the related station + """ + + def __init__(self, db): + """The __init__ method. + + Args: + db (psycopg2.Connection): Database connection + """ + Model.__init__(self, db) + self.id = None + self.stationId = None + self.createdTs = None + self.latestTs = None + self.validToTs = None + self.p1 = None + self.p2 = None + self.p3 = None + self.p4 = None + self.p5 = None + self.b1 = None + self.b2 = None + self.b3 = None + self.b4 = None + self.b5 = None + self.b6 = None + self.b7 = None + self.b8 = None + + def validate(self): + """Returns true on success (when object content is valid), otherwise false + + Returns: + True on success otherwise False + """ + if (type(self.stationId) != int or self.stationId <= 0): + return False + + return True + + def save(self): + """Save object data to database if attribute data is valid + + Returns: + Returns true on success otherwise false + """ + if (self.validate()): + if (self.isExistingObject()): + return self.update() + else: + cursor = self.db.cursor() + cursor.execute("""update station_telemetry_param + set latest_ts = %s + where station_id = %s + and valid_to_ts is null + and p1 = %s + and p2 = %s + and p3 = %s + and p4 = %s + and p5 = %s + and b1 = %s + and b2 = %s + and b3 = %s + and b4 = %s + and b5 = %s + and b6 = %s + and b7 = %s + and b8 = %s""", + (self.createdTs, + self.stationId, + self.p1, + self.p2, + self.p3, + self.p4, + self.p5, + self.b1, + self.b2, + self.b3, + self.b4, + self.b5, + self.b6, + self.b7, + self.b8)) + + if (cursor.rowcount == 0): + return self.insert() + else: + # We do not insert it since it was equal to the existsing row + return True + return False + + def insert(self): + """Method to call when we want to save a new object to database + + Returns: + True on success otherwise False + """ + if (not self.isExistingObject()): + insertCursor = self.db.cursor() + insertCursor.execute("""update station_telemetry_param + set valid_to_ts = %s + where station_id = %s + and valid_to_ts is null""", + (self.createdTs, + self.stationId)) + + insertCursor.execute("""insert into station_telemetry_param(station_id, created_ts, latest_ts, p1, p2, p3, p4, p5, b1, b2, b3, b4, b5, b6, b7, b8) + values (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""", + (self.stationId, + self.createdTs, + self.createdTs, + self.p1, + self.p2, + self.p3, + self.p4, + self.p5, + self.b1, + self.b2, + self.b3, + self.b4, + self.b5, + self.b6, + self.b7, + self.b8)) + return True + else: + return False + + def update(self): + """Method to call when we want to save changes to database + + Returns: + True on success otherwise False + """ + return False diff --git a/server/trackdirect/objects/StationTelemetryUnit.py b/server/trackdirect/objects/StationTelemetryUnit.py new file mode 100644 index 0000000..1e9b244 --- /dev/null +++ b/server/trackdirect/objects/StationTelemetryUnit.py @@ -0,0 +1,140 @@ +from trackdirect.common.Model import Model + + +class StationTelemetryUnit(Model): + """StationTelemetryUnit represents the telemetry UNIT sent by the related station + """ + + def __init__(self, db): + """The __init__ method. + + Args: + db (psycopg2.Connection): Database connection + """ + Model.__init__(self, db) + self.id = None + self.stationId = None + self.createdTs = None + self.latestTs = None + self.validToTs = None + self.u1 = None + self.u2 = None + self.u3 = None + self.u4 = None + self.u5 = None + self.l1 = None + self.l2 = None + self.l3 = None + self.l4 = None + self.l5 = None + self.l6 = None + self.l7 = None + self.l8 = None + + def validate(self): + """Returns true on success (when object content is valid), otherwise false + + Returns: + True on success otherwise False + """ + if (type(self.stationId) != int or self.stationId <= 0): + return False + + return True + + def save(self): + """Save object data to database if attribute data is valid + + Returns: + Returns true on success otherwise false + """ + if (self.validate()): + if (self.isExistingObject()): + return self.update() + else: + cursor = self.db.cursor() + cursor.execute("""update station_telemetry_unit + set latest_ts = %s + where station_id = %s + and valid_to_ts is null + and u1 = %s + and u2 = %s + and u3 = %s + and u4 = %s + and u5 = %s + and l1 = %s + and l2 = %s + and l3 = %s + and l4 = %s + and l5 = %s + and l6 = %s + and l7 = %s + and l8 = %s""", + (self.createdTs, + self.stationId, + self.u1, + self.u2, + self.u3, + self.u4, + self.u5, + self.l1, + self.l2, + self.l3, + self.l4, + self.l5, + self.l6, + self.l7, + self.l8)) + + if (cursor.rowcount == 0): + return self.insert() + else: + # We do not insert it since it was equal to the existsing row + return True + return False + + def insert(self): + """Method to call when we want to save a new object to database + + Returns: + True on success otherwise False + """ + if (not self.isExistingObject()): + insertCursor = self.db.cursor() + insertCursor.execute("""update station_telemetry_unit + set valid_to_ts = %s + where station_id = %s + and valid_to_ts is null""", + (self.createdTs, + self.stationId)) + + insertCursor.execute("""insert into station_telemetry_unit(station_id, created_ts, latest_ts, u1, u2, u3, u4, u5, l1, l2, l3, l4, l5, l6, l7, l8) + values (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""", + (self.stationId, + self.createdTs, + self.createdTs, + self.u1, + self.u2, + self.u3, + self.u4, + self.u5, + self.l1, + self.l2, + self.l3, + self.l4, + self.l5, + self.l6, + self.l7, + self.l8)) + + return True + else: + return False + + def update(self): + """Method to call when we want to save changes to database + + Returns: + True on success otherwise False + """ + return False diff --git a/server/trackdirect/objects/__init__.py b/server/trackdirect/objects/__init__.py new file mode 100644 index 0000000..984c177 --- /dev/null +++ b/server/trackdirect/objects/__init__.py @@ -0,0 +1,2 @@ +__version__ = "1.0" +__author__ = "Per Qvarforth"