kopia lustrzana https://github.com/jprochazka/adsb-receiver
Rewrote flights.py and maintenance.py.
rodzic
f6533814b5
commit
d5c698c9f5
|
|
@ -242,7 +242,7 @@ if [[ "${ADVANCED}" = "true" ]] ; then
|
|||
case "${DATABASEENGINE}" in
|
||||
"MySQL")
|
||||
CheckPackage mariadb-client
|
||||
CheckPackage python3-mysql.connector
|
||||
CheckPackage python3-mysqldb
|
||||
CheckPackage php${DISTRO_PHP_VERSION}-mysql
|
||||
;;
|
||||
"SQLite")
|
||||
|
|
|
|||
|
|
@ -1,50 +1,18 @@
|
|||
#!/bin/bash
|
||||
|
||||
#####################################################################################
|
||||
# ADS-B RECEIVER #
|
||||
#####################################################################################
|
||||
# #
|
||||
# This script is not meant to be executed directly. #
|
||||
# Instead execute install.sh to begin the installation process. #
|
||||
# #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# #
|
||||
# Copyright (c) 2015-2024 Joseph A. Prochazka #
|
||||
# #
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy #
|
||||
# of this software and associated documentation files (the "Software"), to deal #
|
||||
# in the Software without restriction, including without limitation the rights #
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell #
|
||||
# copies of the Software, and to permit persons to whom the Software is #
|
||||
# furnished to do so, subject to the following conditions: #
|
||||
# #
|
||||
# The above copyright notice and this permission notice shall be included in all #
|
||||
# copies or substantial portions of the Software. #
|
||||
# #
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR #
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE #
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER #
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, #
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE #
|
||||
# SOFTWARE. #
|
||||
# #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
|
||||
## VARIABLES
|
||||
|
||||
PORTAL_PYTHON_DIRECTORY="${RECEIVER_BUILD_DIRECTORY}/portal/python"
|
||||
PYTHON_PATH=`which python3`
|
||||
python_path=`which python3`
|
||||
|
||||
## SETUP FLIGHT LOGGING
|
||||
|
||||
echo -e ""
|
||||
echo -e "\e[95m Setting up flight logging...\e[97m"
|
||||
echo -e "\e[95m Setting up portal flight logging and maintenance...\e[97m"
|
||||
echo -e ""
|
||||
|
||||
# Create the cron jobs responsible for logging and maintenance.
|
||||
echo -e "\e[94m Creating the maintenance maintenance script...\e[97m"
|
||||
echo -e "\e[94m Creating the portal script cron file...\e[97m"
|
||||
sudo tee /etc/cron.d/adsb-receiver-flight-logging > /dev/null <<EOF
|
||||
* * * * * root ${PYTHON_PATH} ${PORTAL_PYTHON_DIRECTORY}/flights.py
|
||||
30 * * * * root ${PYTHON_PATH} ${PORTAL_PYTHON_DIRECTORY}/maintenance.py
|
||||
* * * * * root ${python_path} ${RECEIVER_BUILD_DIRECTORY}/portal/python/flights.py
|
||||
0 0 * * * root ${python_path} ${RECEIVER_BUILD_DIRECTORY}/portal/python/maintenance.py
|
||||
EOF
|
||||
|
|
|
|||
|
|
@ -1,40 +1,9 @@
|
|||
#!/bin/bash
|
||||
|
||||
#####################################################################################
|
||||
# ADS-B RECEIVER #
|
||||
#####################################################################################
|
||||
# #
|
||||
# This script is not meant to be executed directly. #
|
||||
# Instead execute install.sh to begin the installation process. #
|
||||
# #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# #
|
||||
# Copyright (c) 2015-2024, Joseph A. Prochazka #
|
||||
# #
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy #
|
||||
# of this software and associated documentation files (the "Software"), to deal #
|
||||
# in the Software without restriction, including without limitation the rights #
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell #
|
||||
# copies of the Software, and to permit persons to whom the Software is #
|
||||
# furnished to do so, subject to the following conditions: #
|
||||
# #
|
||||
# The above copyright notice and this permission notice shall be included in all #
|
||||
# copies or substantial portions of the Software. #
|
||||
# #
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR #
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE #
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER #
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, #
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE #
|
||||
# SOFTWARE. #
|
||||
# #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
|
||||
## SOFTWARE VERSIONS
|
||||
|
||||
# The ADS-B Receiver Project
|
||||
PROJECT_VERSION="2.8.3"
|
||||
PROJECT_VERSION="2.8.4"
|
||||
|
||||
# FlightAware
|
||||
DUMP1090_FA_VERSION="9.0"
|
||||
|
|
|
|||
|
|
@ -1,33 +1,5 @@
|
|||
<?php
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// ADS-B RECEIVER PORTAL //
|
||||
// =============================================================================== //
|
||||
// Copyright and Licensing Information: //
|
||||
// //
|
||||
// The MIT License (MIT) //
|
||||
// //
|
||||
// Copyright (c) 2015-2024 Joseph A. Prochazka //
|
||||
// //
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy //
|
||||
// of this software and associated documentation files (the "Software"), to deal //
|
||||
// in the Software without restriction, including without limitation the rights //
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell //
|
||||
// copies of the Software, and to permit persons to whom the Software is //
|
||||
// furnished to do so, subject to the following conditions: //
|
||||
// //
|
||||
// The above copyright notice and this permission notice shall be included in all //
|
||||
// copies or substantial portions of the Software. //
|
||||
// //
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE //
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, //
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE //
|
||||
// SOFTWARE. //
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class template {
|
||||
|
||||
// PUT THE TEMPLATE TOGETHER
|
||||
|
|
@ -37,7 +9,7 @@
|
|||
|
||||
// Check if the portal is installed or needs upgraded.
|
||||
|
||||
$thisVersion = "2.8.3";
|
||||
$thisVersion = "2.8.4";
|
||||
|
||||
if (!file_exists($_SERVER['DOCUMENT_ROOT']."/classes/settings.class.php")) {
|
||||
header ("Location: /install/install.php");
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// The most current stable release.
|
||||
$thisVersion = "2.8.3";
|
||||
$thisVersion = "2.8.4";
|
||||
|
||||
// Begin the upgrade process if this release is newer than what is installed.
|
||||
if (file_exists($_SERVER['DOCUMENT_ROOT'].DIRECTORY_SEPARATOR."classes".DIRECTORY_SEPARATOR."settings.class.php")) {
|
||||
|
|
@ -233,7 +233,7 @@ EOF;
|
|||
flight VARCHAR(10) NOT NULL);';
|
||||
$positionsSql = 'CREATE TABLE '.$dbPrefix.'positions (
|
||||
id INT(11) AUTO_INCREMENT PRIMARY KEY,
|
||||
flight BIGINT NOT NULL,
|
||||
flight BIGINT NULL,
|
||||
aircraft BIGINT NOT NULL,
|
||||
time datetime NOT NULL,
|
||||
message INT NOT NULL,
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@
|
|||
|
||||
try {
|
||||
|
||||
// Update the version and patch settings..
|
||||
// Update the version and patch settings.
|
||||
$common->updateSetting("version", "2.8.3");
|
||||
$common->updateSetting("patch", "");
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// ADS-B RECEIVER PORTAL //
|
||||
// =============================================================================== //
|
||||
// Copyright and Licensing Information: //
|
||||
// //
|
||||
// The MIT License (MIT) //
|
||||
// //
|
||||
// Copyright (c) 2015 Joseph A. Prochazka //
|
||||
// //
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy //
|
||||
// of this software and associated documentation files (the "Software"), to deal //
|
||||
// in the Software without restriction, including without limitation the rights //
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell //
|
||||
// copies of the Software, and to permit persons to whom the Software is //
|
||||
// furnished to do so, subject to the following conditions: //
|
||||
// //
|
||||
// The above copyright notice and this permission notice shall be included in all //
|
||||
// copies or substantial portions of the Software. //
|
||||
// //
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE //
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, //
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE //
|
||||
// SOFTWARE. //
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////
|
||||
// UPGRADE TO V2.8.4
|
||||
///////////////////////
|
||||
|
||||
// --------------------------------------------------------
|
||||
// Updates the version setting to 2.8.4.
|
||||
// --------------------------------------------------------
|
||||
|
||||
$results = upgrade();
|
||||
exit(json_encode($results));
|
||||
|
||||
function upgrade() {
|
||||
require_once($_SERVER['DOCUMENT_ROOT'].DIRECTORY_SEPARATOR."classes".DIRECTORY_SEPARATOR."common.class.php");
|
||||
require_once($_SERVER['DOCUMENT_ROOT'].DIRECTORY_SEPARATOR."classes".DIRECTORY_SEPARATOR."settings.class.php");
|
||||
|
||||
$common = new common();
|
||||
$settings = new settings();
|
||||
|
||||
try {
|
||||
|
||||
// Update the version and patch settings..
|
||||
$common->updateSetting("version", "2.8.4");
|
||||
$common->updateSetting("patch", "");
|
||||
|
||||
// Allow NULL flights in the positions table.
|
||||
$dbh = $common->pdoOpen();
|
||||
$sql = "ALTER TABLE ".$settings::db_prefix."positions MODIFY flight BIGINT NULL";
|
||||
$sth = $dbh->prepare($sql);
|
||||
$sth->execute();
|
||||
$sth = NULL;
|
||||
|
||||
// The upgrade process completed successfully.
|
||||
$results['success'] = TRUE;
|
||||
$results['message'] = "Upgrade to v2.8.4 successful.";
|
||||
return $results;
|
||||
|
||||
} catch(Exception $e) {
|
||||
// Something went wrong during this upgrade process.
|
||||
$results['success'] = FALSE;
|
||||
$results['message'] = $e->getMessage();
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
|
|
@ -33,7 +33,7 @@
|
|||
$common = new common();
|
||||
|
||||
// The most current stable release.
|
||||
$thisVersion = "2.8.3";
|
||||
$thisVersion = "2.8.4";
|
||||
|
||||
// Begin the upgrade process if this release is newer than what is installed.
|
||||
if ($common->getSetting("version") == $thisVersion) {
|
||||
|
|
@ -213,6 +213,15 @@
|
|||
$version = "2.8.3";
|
||||
}
|
||||
|
||||
// UPGRADE TO V2.8.4
|
||||
if ($common->getSetting("version") == "2.8.3" && $success) {
|
||||
$json = file_get_contents("http://localhost/install/upgrade-v2.8.4.php");
|
||||
$results = json_decode($json, TRUE);
|
||||
$success = $results['success'];
|
||||
$message = $results['message'];
|
||||
$version = "2.8.4";
|
||||
}
|
||||
|
||||
require_once($_SERVER['DOCUMENT_ROOT'].DIRECTORY_SEPARATOR."admin".DIRECTORY_SEPARATOR."includes".DIRECTORY_SEPARATOR."header.inc.php");
|
||||
|
||||
// Display the instalation wizard.
|
||||
|
|
|
|||
|
|
@ -302,8 +302,8 @@
|
|||
var i,x,y,ARRcookies=document.cookie.split(";");
|
||||
for (i=0;i<ARRcookies.length;i++)
|
||||
{
|
||||
x=ARRcookies[i].substr(0,ARRcookies[i].indexOf("="));
|
||||
y=ARRcookies[i].substr(ARRcookies[i].indexOf("=")+1);
|
||||
x=ARRcookies[i].substr(0,ARRcookies[i].toString().indexOf("="));
|
||||
y=ARRcookies[i].substr(ARRcookies[i].toString().indexOf("=")+1);
|
||||
x=x.replace(/^\s+|\s+$/g,"");
|
||||
if (x==c_name)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,38 +1,41 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
import datetime
|
||||
import fcntl
|
||||
import json
|
||||
import logging
|
||||
import MySQLdb
|
||||
import os
|
||||
import time
|
||||
import sqlite3
|
||||
|
||||
from urllib import urlopen
|
||||
|
||||
now = None
|
||||
from datetime import datetime
|
||||
from time import sleep
|
||||
from urllib.request import urlopen
|
||||
|
||||
class AircraftProcessor(object):
|
||||
|
||||
# Log infromation to console
|
||||
def log(self, string):
|
||||
#print(f'[{datetime.now().strftime("%Y/%m/%d %H:%M:%S")}] {string}') # uncomment to enable debug logging
|
||||
return
|
||||
|
||||
# Create database connection
|
||||
def create_connection():
|
||||
def create_connection(self):
|
||||
self.log("Setting up database connection")
|
||||
with open(os.path.dirname(os.path.realpath(__file__)) + '/config.json') as config_file:
|
||||
config = json.load(config_file)
|
||||
|
||||
match config["database"]["type"].lower():
|
||||
case 'mysql':
|
||||
import mysql.connector
|
||||
return mysql.connector.connect(
|
||||
return MySQLdb.connect(
|
||||
host=config["database"]["host"],
|
||||
user=config["database"]["user"],
|
||||
password=config["database"]["passwd"],
|
||||
database=config["database"]["db"]
|
||||
passwd=config["database"]["passwd"],
|
||||
db=config["database"]["db"]
|
||||
)
|
||||
case 'sqlite':
|
||||
import sqlite3
|
||||
return sqlite3.connect(config["database"]["db"])
|
||||
|
||||
# Read JSON supplied by dump1090
|
||||
def read_json():
|
||||
def read_json(self):
|
||||
self.log("Reading aircraft.json")
|
||||
try:
|
||||
raw_json = urlopen('http://127.0.0.1/dump1090/data/aircraft.json')
|
||||
json_object = json.load(raw_json)
|
||||
|
|
@ -42,15 +45,18 @@ class AircraftProcessor(object):
|
|||
return
|
||||
|
||||
# Begin processing data retrived from dump1090
|
||||
def process_all_aircraft(self, all_aircraft):
|
||||
def process_all_aircraft(self):
|
||||
data = self.read_json()
|
||||
aircraft_data = data["aircraft"]
|
||||
|
||||
connection = self.create_connection()
|
||||
self.cursor = connection.cursor()
|
||||
if len(aircraft_data) == 0:
|
||||
self.log(f'There is no aircraft data to process at this time')
|
||||
return
|
||||
|
||||
for aircraft in all_aircraft:
|
||||
self.log(f'Begining to proocess {len(aircraft_data)} aircraft')
|
||||
for aircraft in aircraft_data:
|
||||
self.process_aircraft(aircraft)
|
||||
|
||||
connection.commit()
|
||||
connection.close()
|
||||
|
||||
return
|
||||
|
|
@ -59,117 +65,170 @@ class AircraftProcessor(object):
|
|||
def process_aircraft(self, aircraft):
|
||||
tracked=False
|
||||
aircraft_id=None
|
||||
|
||||
|
||||
try:
|
||||
self.cursor.execute("SELECT COUNT(*) FROM aircraft WHERE icao = %s", (aircraft["hex"],))
|
||||
if self.cursor.fetchone()[0] > 0:
|
||||
cursor.execute("SELECT COUNT(*) FROM adsb_aircraft WHERE icao = %s", (aircraft["hex"],))
|
||||
if cursor.fetchone()[0] > 0:
|
||||
tracked=True
|
||||
except Exception as ex:
|
||||
logging.error(f"Error encountered while checking if aircraft '{aircraft["hex"]}' has already been added", exc_info=ex)
|
||||
logging.error(f'Error encountered while checking if aircraft {aircraft["hex"]} has already been added', exc_info=ex)
|
||||
return
|
||||
|
||||
if tracked:
|
||||
query = "UPDATE aircraft SET lastSeen = %s WHERE icao = %s",
|
||||
parameters = (now, aircraft["hex"])
|
||||
error_message = f"Error encountered while trying to update aircraft '{aircraft["hex"]}'"
|
||||
self.log(f'Updating aircraft ICAO {aircraft["hex"]}')
|
||||
try:
|
||||
cursor.execute(
|
||||
"UPDATE adsb_aircraft SET lastSeen = %s WHERE icao = %s",
|
||||
(now, aircraft["hex"])
|
||||
)
|
||||
connection.commit()
|
||||
cursor.execute(
|
||||
"SELECT id FROM adsb_aircraft WHERE icao = %s",
|
||||
(aircraft["hex"],)
|
||||
)
|
||||
aircraft_id = cursor.fetchone()[0]
|
||||
except Exception as ex:
|
||||
logging.error(f'Error encountered while trying to update aircraft {aircraft["hex"]}', exc_info=ex)
|
||||
return
|
||||
else:
|
||||
query = "INSERT INTO aircraft (icao, firstSeen, lastSeen) VALUES (%s, %s, %s)",
|
||||
parameters = (aircraft["hex"], now, now)
|
||||
error_message = f"Error encountered while trying to insert aircraft '{aircraft["hex"]}'"
|
||||
|
||||
try:
|
||||
self.cursor.execute(query, parameters)
|
||||
aircraft_id = self.cursor.lastrowid
|
||||
except Exception as ex:
|
||||
logging.error(error_message, exc_info=ex)
|
||||
return
|
||||
self.log(f'Inserting aircraft ICAO {aircraft["hex"]}')
|
||||
try:
|
||||
cursor.execute(
|
||||
"INSERT INTO adsb_aircraft (icao, firstSeen, lastSeen) VALUES (%s, %s, %s)",
|
||||
(aircraft["hex"], now, now)
|
||||
)
|
||||
connection.commit()
|
||||
aircraft_id = cursor.lastrowid
|
||||
except Exception as ex:
|
||||
logging.error(f'Error encountered while trying to insert aircraft {aircraft["hex"]}', exc_info=ex)
|
||||
return
|
||||
|
||||
if 'flight' in aircraft:
|
||||
self.process_flight(aircraft_id, aircraft)
|
||||
else:
|
||||
self.process_positions(aircraft_id , None, aircraft)
|
||||
|
||||
return
|
||||
|
||||
# Process the flight
|
||||
def process_flight(self, aircraft_id, aircraft):
|
||||
tracked=False
|
||||
try:
|
||||
self.cursor.execute("SELECT COUNT(*) FROM flights WHERE flight = %s", (aircraft["flight"],))
|
||||
if self.cursor.fetchone()[0] > 0:
|
||||
tracked=True
|
||||
except Exception as ex:
|
||||
logging.error(f"Error encountered while checking if flight '{aircraft["flight"]}' has already been added", exc_info=ex)
|
||||
return
|
||||
if 'flight' in aircraft:
|
||||
flight = aircraft["flight"].strip()
|
||||
|
||||
tracked=False
|
||||
try:
|
||||
cursor.execute("SELECT COUNT(*) FROM adsb_flights WHERE flight = %s", (flight,))
|
||||
if cursor.fetchone()[0] > 0:
|
||||
tracked=True
|
||||
except Exception as ex:
|
||||
logging.error(f'Error encountered while checking if flight {flight} has already been added', exc_info=ex)
|
||||
return
|
||||
|
||||
if tracked:
|
||||
self.log(f' Updating flight {flight} assigned to aircraft ICAO {aircraft["hex"]}')
|
||||
try:
|
||||
cursor.execute(
|
||||
"UPDATE adsb_flights SET lastSeen = %s WHERE flight = %s",
|
||||
(now, flight)
|
||||
)
|
||||
connection.commit()
|
||||
cursor.execute(
|
||||
"SELECT id FROM adsb_flights WHERE flight = %s",
|
||||
(flight,)
|
||||
)
|
||||
flight_id = cursor.fetchone()[0]
|
||||
except Exception as ex:
|
||||
logging.error(f'Error encountered while trying to update flight {flight}', exc_info=ex)
|
||||
return
|
||||
else:
|
||||
self.log(f'Inserting flight {flight} assigned to aircraft ICAO {aircraft["hex"]}')
|
||||
try:
|
||||
cursor.execute(
|
||||
"INSERT INTO adsb_flights (aircraft, flight, firstSeen, lastSeen) VALUES (%s, %s, %s, %s)",
|
||||
(aircraft_id, flight, now, now)
|
||||
)
|
||||
connection.commit()
|
||||
flight_id = cursor.lastrowid
|
||||
except Exception as ex:
|
||||
logging.error(f'Error encountered while trying to insert flight {flight}', exc_info=ex)
|
||||
return
|
||||
|
||||
if tracked:
|
||||
query = "UPDATE flights SET lastSeen = %s WHERE icao = %s",
|
||||
parameters = (now, aircraft["flight"])
|
||||
error_message = f"Error encountered while trying to update flight '{aircraft["flight"]}'"
|
||||
else:
|
||||
query = "INSERT INTO flights (aircraft, flight, firstSeen, lastSeen) VALUES (%s, %s, %s, %s)",
|
||||
parameters = (aircraft_id, aircraft["flight"], now, now)
|
||||
error_message = f"Error encountered while trying to insert flight '{aircraft["flight"]}'"
|
||||
self.log(f' Aircraft ICAO {aircraft["hex"]} was not assigned a flight')
|
||||
|
||||
try:
|
||||
self.cursor.execute(query, parameters)
|
||||
flight_id = self.cursor.lastrowid
|
||||
except Exception as ex:
|
||||
logging.error(error_message, exc_info=ex)
|
||||
return
|
||||
|
||||
position_keys = ('lat', 'lon', 'nav_altitude', 'gs', 'track', 'geom_rate', 'hex')
|
||||
if (all(key in aircraft for key in position_keys) and aircraft["altitude"] != "ground"):
|
||||
self.process_positions(aircraft_id, flight_id, aircraft)
|
||||
self.process_positions(aircraft_id, flight_id, aircraft)
|
||||
|
||||
return
|
||||
|
||||
# Process positions
|
||||
def process_positions(self, aircraft_id , flight_id, aircraft):
|
||||
tracked=False
|
||||
try:
|
||||
self.cursor.execute("SELECT COUNT(*) FROM positions WHERE flight = %s AND message = %s", (flight_id, aircraft["messages"]))
|
||||
if self.cursor.fetchone()[0] > 0:
|
||||
tracked=True
|
||||
except Exception as ex:
|
||||
logging.error(f"Error encountered while checking if position has already been added for message ID '{aircraft["messages"]}' related to flight '{flight_id}'", exc_info=ex)
|
||||
return
|
||||
|
||||
if tracked:
|
||||
return
|
||||
position_keys = ('lat', 'lon', 'alt_baro', 'gs', 'track', 'geom_rate', 'hex')
|
||||
if (all(key in aircraft for key in position_keys)):
|
||||
|
||||
squawk = None
|
||||
if 'squawk' in aircraft:
|
||||
squawk = aircraft["squawk"]
|
||||
tracked=False
|
||||
try:
|
||||
cursor.execute("SELECT COUNT(*) FROM adsb_positions WHERE flight = %s AND message = %s", (flight_id, aircraft["messages"]))
|
||||
if cursor.fetchone()[0] > 0:
|
||||
tracked=True
|
||||
except Exception as ex:
|
||||
logging.error(f'Error encountered while checking if position has already been added for message ID {aircraft["messages"]} related to flight {flight_id}', exc_info=ex)
|
||||
return
|
||||
|
||||
if tracked:
|
||||
return
|
||||
|
||||
squawk = None
|
||||
if 'squawk' in aircraft:
|
||||
squawk = aircraft["squawk"]
|
||||
|
||||
altitude = aircraft["alt_baro"]
|
||||
if 'alt_geom' in aircraft:
|
||||
altitude = aircraft["alt_geom"]
|
||||
|
||||
try:
|
||||
if flight_id is None:
|
||||
self.log(f' Inserting position for aircraft ICAO {aircraft["hex"]}')
|
||||
else:
|
||||
self.log(f' Inserting position for aircraft ICAO {aircraft["hex"]} assigned flight {flight_id}')
|
||||
cursor.execute(
|
||||
"INSERT INTO adsb_positions (flight, time, message, squawk, latitude, longitude, track, altitude, verticleRate, speed, aircraft) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)",
|
||||
(flight_id, now, aircraft["messages"], squawk, aircraft["lat"], aircraft["lon"], aircraft["track"], altitude, aircraft["geom_rate"], aircraft["gs"], aircraft_id)
|
||||
)
|
||||
connection.commit()
|
||||
except Exception as ex:
|
||||
logging.error(f'Error encountered while inserting position data for message ID {aircraft["messages"]} related to flight {flight_id}', exc_info=ex)
|
||||
return
|
||||
|
||||
else:
|
||||
self.log(f' Data required to insert position data for Aircraft ICAO {aircraft["hex"]} is not present')
|
||||
|
||||
try:
|
||||
self.cursor.execute(
|
||||
"INSERT INTO positions (flight, time, message, squawk, latitude, longitude, track, altitude, verticleRate, speed, aircraft) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)",
|
||||
(flight_id, now, aircraft["messages"], squawk, aircraft["lat"], aircraft["lon"], aircraft["track"], aircraft["nav_altitude"], aircraft["geom_rate"], aircraft["gs"], aircraft_id)
|
||||
)
|
||||
flight_id = self.cursor.lastrowid
|
||||
except Exception as ex:
|
||||
logging.error(f"Error encountered while inserting position data for message ID '{aircraft["messages"]}' related to flight '{flight_id}'", exc_info=ex)
|
||||
return
|
||||
|
||||
return
|
||||
|
||||
if __name__ == "__main__":
|
||||
processor = AircraftProcessor()
|
||||
|
||||
logging.info(f"Beginning flight recording job on {datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S")}")
|
||||
processor.log("-- CHECKING IF FLIGHT RECORDER JOB IS ALREADY RUNNING")
|
||||
|
||||
# Do not allow another instance of the job to run
|
||||
lock_file = open('/tmp/flights.py.lock','w')
|
||||
try:
|
||||
fcntl.flock(lock_file, fcntl.LOCK_EX|fcntl.LOCK_NB)
|
||||
except (IOError, OSError):
|
||||
logging.info('Another instance already running')
|
||||
processor.log("-- ANOTHER INSTANCE OF THIS JOB IS RUNNING")
|
||||
quit()
|
||||
|
||||
# Begin flight recording job
|
||||
lock_file.write('%d\n'%os.getpid())
|
||||
|
||||
while True:
|
||||
now = datetime.datetime.now()
|
||||
data = processor.read_json()
|
||||
processor.process_all_aircraft(data["aircraft"])
|
||||
logging.info(f"Flight recording job ended on {datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S")}")
|
||||
time.sleep(15)
|
||||
processor.log("-- BEGINING FLIGHT RECORDER JOB")
|
||||
|
||||
# Set up database connection
|
||||
connection = processor.create_connection()
|
||||
cursor = connection.cursor()
|
||||
|
||||
# Begin flight recording job
|
||||
now = datetime.now()
|
||||
processor.process_all_aircraft()
|
||||
processor.log("-- FLIGHT RECORD JOB COMPLETE")
|
||||
processor.log("SLEEPING 15 SECONDS BEFORE NEXT RUN")
|
||||
sleep(15)
|
||||
|
|
|
|||
|
|
@ -1,66 +1,64 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
import fcntl
|
||||
import json
|
||||
import logging
|
||||
import MySQLdb
|
||||
import os
|
||||
import sqlite3
|
||||
import time
|
||||
import yaml
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
config = yaml.safe_load(open("config.yml"))
|
||||
|
||||
class MaintenanceProcessor(object):
|
||||
|
||||
# Log infromation to console
|
||||
def log(self, string):
|
||||
print(f'[{datetime.now().strftime("%Y/%m/%d %H:%M:%S")}] {string}') # uncomment to enable debug logging
|
||||
return
|
||||
|
||||
# Create database connection
|
||||
def create_connection():
|
||||
def create_connection(self):
|
||||
self.log("Setting up database connection")
|
||||
with open(os.path.dirname(os.path.realpath(__file__)) + '/config.json') as config_file:
|
||||
config = json.load(config_file)
|
||||
|
||||
match config["database"]["type"].lower():
|
||||
case 'mysql':
|
||||
import mysql.connector
|
||||
return mysql.connector.connect(
|
||||
return MySQLdb.connect(
|
||||
host=config["database"]["host"],
|
||||
user=config["database"]["user"],
|
||||
password=config["database"]["passwd"],
|
||||
database=config["database"]["db"]
|
||||
passwd=config["database"]["passwd"],
|
||||
db=config["database"]["db"]
|
||||
)
|
||||
case 'sqlite':
|
||||
import sqlite3
|
||||
return sqlite3.connect(config["database"]["db"])
|
||||
|
||||
# Begin maintenance
|
||||
# Begin maintenance
|
||||
def begin_maintenance(self):
|
||||
connection = self.create_connection()
|
||||
self.cursor = connection.cursor()
|
||||
|
||||
self.log("Getting maintenance settings from the database")
|
||||
purge_old_aircraft = False
|
||||
try:
|
||||
self.cursor.execute("SELECT value FROM settings WHERE name 'purgeAircraft'")
|
||||
result = self.cursor.fetchone()[0]
|
||||
cursor.execute("SELECT value FROM adsb_settings WHERE name = 'purgeAircraft'")
|
||||
result = cursor.fetchone()[0]
|
||||
purge_old_aircraft = result.lower() in ['true', '1']
|
||||
except Exception as ex:
|
||||
logging.error(f"Error encountered while getting value for setting 'purgeAircraft'", exc_info=ex)
|
||||
logging.error(f"Error encountered while getting value for setting purgeAircraft", exc_info=ex)
|
||||
return
|
||||
|
||||
if purge_old_aircraft:
|
||||
cutoff_date = datetime.now() - timedelta(years = 20)
|
||||
try:
|
||||
self.cursor.execute("SELECT value FROM settings WHERE name 'purgeDaysOld'")
|
||||
days_to_save = self.cursor.fetchone()[0]
|
||||
cursor.execute("SELECT value FROM adsb_settings WHERE name = 'purgeDaysOld'")
|
||||
days_to_save = cursor.fetchone()[0]
|
||||
except Exception as ex:
|
||||
logging.error(f"Error encountered while getting value for setting 'purgeDaysOld'", exc_info=ex)
|
||||
logging.error(f"Error encountered while getting value for setting purgeDaysOld", exc_info=ex)
|
||||
return
|
||||
cutoff_date = datetime.now() - timedelta(days = days_to_save)
|
||||
|
||||
self.purge_aircraft(cutoff_date)
|
||||
self.purge_flights(cutoff_date)
|
||||
self.purge_positions(cutoff_date)
|
||||
else:
|
||||
self.log("Maintenance is disabled")
|
||||
|
||||
connection.commit()
|
||||
|
||||
|
||||
connection.close()
|
||||
|
||||
return
|
||||
|
|
@ -68,10 +66,10 @@ class MaintenanceProcessor(object):
|
|||
# Remove aircraft not seen since the specified date
|
||||
def purge_aircraft(self, cutoff_date):
|
||||
try:
|
||||
self.cursor.execute("SELECT id FROM aircraft WHERE lastSeen < %s", (cutoff_date,))
|
||||
aircraft_ids = self.cursor.fetchall()
|
||||
cursor.execute("SELECT id FROM adsb_aircraft WHERE lastSeen < %s", (cutoff_date,))
|
||||
aircraft_ids = cursor.fetchall()
|
||||
except Exception as ex:
|
||||
logging.error(f"Error encountered while getting aircraft IDs not seen since '{cutoff_date}'", exc_info=ex)
|
||||
logging.error(f"Error encountered while getting aircraft IDs not seen since {cutoff_date}", exc_info=ex)
|
||||
return
|
||||
|
||||
if len(aircraft_ids) > 0:
|
||||
|
|
@ -79,11 +77,11 @@ class MaintenanceProcessor(object):
|
|||
aircraft_id_params = {'id': id}
|
||||
|
||||
try:
|
||||
self.cursor.execute("DELETE FROM aircraft WHERE id IN %(t)s", aircraft_id_params)
|
||||
cursor.execute("DELETE FROM adsb_aircraft WHERE id IN %(t)s", aircraft_id_params)
|
||||
except Exception as ex:
|
||||
logging.error(f"Error deleting aircraft not seen since '{cutoff_date}'", exc_info=ex)
|
||||
logging.error(f"Error deleting aircraft not seen since {cutoff_date}", exc_info=ex)
|
||||
return
|
||||
|
||||
|
||||
self.purge_flights_related_to_aircraft(aircraft_id_params, cutoff_date)
|
||||
self.purge_positions_related_to_aircraft(aircraft_id_params, cutoff_date)
|
||||
|
||||
|
|
@ -92,30 +90,30 @@ class MaintenanceProcessor(object):
|
|||
# Remove flights related to aircraft not seen since the specified date
|
||||
def purge_flights_related_to_aircraft(self, aircraft_id_params, cutoff_date):
|
||||
try:
|
||||
self.cursor.execute("DELETE FROM flights WHERE aircraft = %(t)s", aircraft_id_params)
|
||||
cursor.execute("DELETE FROM adsb_flights WHERE aircraft = %(t)s", aircraft_id_params)
|
||||
except Exception as ex:
|
||||
logging.error(f"Error deleting flights related to aircraft not seen since '{cutoff_date}'", exc_info=ex)
|
||||
logging.error(f"Error deleting flights related to aircraft not seen since {cutoff_date}", exc_info=ex)
|
||||
return
|
||||
|
||||
|
||||
return
|
||||
|
||||
# Remove positions related to aircraft not seen since the specified date
|
||||
def purge_positions_related_to_aircraft(self, aircraft_id_params, cutoff_date):
|
||||
try:
|
||||
self.cursor.execute("DELETE FROM positions WHERE aircraft = %(t)s", aircraft_id_params)
|
||||
cursor.execute("DELETE FROM adsb_positions WHERE aircraft = %(t)s", aircraft_id_params)
|
||||
except Exception as ex:
|
||||
logging.error(f"Error deleting positions related to aircraft not seen since '{cutoff_date}'", exc_info=ex)
|
||||
logging.error(f"Error deleting positions related to aircraft not seen since {cutoff_date}", exc_info=ex)
|
||||
return
|
||||
|
||||
|
||||
return
|
||||
|
||||
|
||||
# Remove positions older than the specified date
|
||||
def purge_flights(self, cutoff_date):
|
||||
try:
|
||||
self.cursor.execute("SELECT id FROM flights WHERE lastSeen < %s", (cutoff_date,))
|
||||
flight_ids = self.cursor.fetchall()
|
||||
cursor.execute("SELECT id FROM adsb_flights WHERE lastSeen < %s", (cutoff_date,))
|
||||
flight_ids = cursor.fetchall()
|
||||
except Exception as ex:
|
||||
logging.error(f"Error encountered while getting aircraft IDs not seen since '{cutoff_date}'", exc_info=ex)
|
||||
logging.error(f"Error encountered while getting aircraft IDs not seen since {cutoff_date}", exc_info=ex)
|
||||
return
|
||||
|
||||
if len(flight_ids) > 0:
|
||||
|
|
@ -123,51 +121,53 @@ class MaintenanceProcessor(object):
|
|||
flight_id_params = {'id': id}
|
||||
|
||||
try:
|
||||
self.cursor.execute("DELETE FROM flights WHERE id IN %(t)s", flight_id_params)
|
||||
cursor.execute("DELETE FROM adsb_flights WHERE id IN %(t)s", flight_id_params)
|
||||
except Exception as ex:
|
||||
logging.error(f"Error deleting flights older than the cut off date of '{cutoff_date}'", exc_info=ex)
|
||||
logging.error(f"Error deleting flights older than the cut off date of {cutoff_date}", exc_info=ex)
|
||||
return
|
||||
|
||||
|
||||
self.purge_positions_related_to_flights(flight_id_params, cutoff_date)
|
||||
|
||||
return
|
||||
|
||||
|
||||
# Remove positions related to aircraft not seen since the specified date
|
||||
def purge_positions_related_to_flights(self, flight_id_params, cutoff_date):
|
||||
try:
|
||||
self.cursor.execute("DELETE FROM positions WHERE flight = %(t)s", flight_id_params)
|
||||
cursor.execute("DELETE FROM adsb_positions WHERE flight = %(t)s", flight_id_params)
|
||||
except Exception as ex:
|
||||
logging.error(f"Error deleting positions related to flights not seen since '{cutoff_date}'", exc_info=ex)
|
||||
logging.error(f"Error deleting positions related to flights not seen since {cutoff_date}", exc_info=ex)
|
||||
return
|
||||
|
||||
|
||||
return
|
||||
|
||||
# Remove positions older than the specified date
|
||||
def purge_positions(self, cutoff_date):
|
||||
try:
|
||||
self.cursor.execute("DELETE FROM positions WHERE time < %s", (cutoff_date,))
|
||||
cursor.execute("DELETE FROM adsb_positions WHERE time < %s", (cutoff_date,))
|
||||
except Exception as ex:
|
||||
logging.error(f"Error deleting positions older than the cut off date of '{cutoff_date}'", exc_info=ex)
|
||||
logging.error(f"Error deleting positions older than the cut off date of {cutoff_date}", exc_info=ex)
|
||||
return
|
||||
|
||||
|
||||
return
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
processor = MaintenanceProcessor()
|
||||
|
||||
logging.info(f"Beginning maintenance job on {datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S")}")
|
||||
processor.log("-- BEGINING PORTAL MAINTENANCE JOB")
|
||||
|
||||
# Do not allow another instance of the job to run
|
||||
lock_file = open('/tmp/maintenance.py.lock','w')
|
||||
try:
|
||||
fcntl.flock(lock_file, fcntl.LOCK_EX|fcntl.LOCK_NB)
|
||||
except (IOError, OSError):
|
||||
logging.info('Another instance already running')
|
||||
processor.log("-- ANOTHER INSTANCE OF THIS JOB IS RUNNING")
|
||||
quit()
|
||||
|
||||
|
||||
# Set up database connection
|
||||
connection = processor.create_connection()
|
||||
cursor = connection.cursor()
|
||||
|
||||
# Begin maintenance job
|
||||
lock_file.write('%d\n'%os.getpid())
|
||||
while True:
|
||||
processor.begin_maintenance()
|
||||
logging.info(f"Maintenance job ended on {datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S")}")
|
||||
time.sleep(15)
|
||||
processor.begin_maintenance()
|
||||
processor.log("-- PORTAL MAINTENANCE JOB COMPLETE")
|
||||
|
|
|
|||
Ładowanie…
Reference in New Issue