chasemapper/chasemapper/logger.py

183 wiersze
5.6 KiB
Python

#!/usr/bin/env python
#
# Project Horus - Chase Logging
#
# Copyright (C) 2019 Mark Jessop <vk5qi@rfhead.net>
# Released under GNU GPL v3 or later
#
import datetime
import json
import logging
import os
import pytz
import time
from threading import Thread, Lock
try:
# Python 2
from Queue import Queue
except ImportError:
# Python 3
from queue import Queue
class ChaseLogger(object):
""" Chase Data Logger Class.
Log all chase data into a file as lines of JSON.
"""
def __init__(self, filename=None, log_dir="./log_files"):
if filename is not None:
# Use user-supplied filename if provided
self.filename = filename
else:
# Otherwise, create a filename based on the current time.
self.filename = os.path.join(
log_dir, datetime.datetime.utcnow().strftime("%Y%m%d-%H%MZ.log")
)
self.file_lock = Lock()
# Input Queue.
self.input_queue = Queue()
# Open the file.
try:
self.f = open(self.filename, "a")
logging.info("Logging - Opened log file %s." % self.filename)
except Exception as e:
self.log_error("Logging - Could not open log file - %s" % str(e))
return
# Start queue processing thread.
self.input_processing_running = True
self.log_process_thread = Thread(target=self.process_queue)
self.log_process_thread.start()
def add_car_position(self, data):
""" Log a chase car position update.
Input dict expected to be in the format:
{
'time' : _time_dt,
'lat' : _lat,
'lon' : _lon,
'alt' : _alt,
'comment': _comment
}
"""
data["log_type"] = "CAR POSITION"
data["log_time"] = pytz.utc.localize(datetime.datetime.utcnow()).isoformat()
# Convert the input datetime object into a string.
data["time"] = data["time"].isoformat()
# Add it to the queue if we are running.
if self.input_processing_running:
self.input_queue.put(data)
else:
self.log_error("Processing not running, discarding.")
def add_balloon_telemetry(self, data):
""" Log balloon telemetry.
"""
data["log_type"] = "BALLOON TELEMETRY"
data["log_time"] = pytz.utc.localize(datetime.datetime.utcnow()).isoformat()
# Convert the input datetime object into a string.
data["time"] = data["time_dt"].isoformat()
# Remove the time_dt element (this cannot be serialised to JSON).
data.pop("time_dt")
# Add it to the queue if we are running.
if self.input_processing_running:
self.input_queue.put(data)
else:
self.log_error("Processing not running, discarding.")
def add_balloon_prediction(self, data):
""" Log a prediction run """
data["log_type"] = "PREDICTION"
data["log_time"] = pytz.utc.localize(datetime.datetime.utcnow()).isoformat()
# Add it to the queue if we are running.
if self.input_processing_running:
self.input_queue.put(data)
else:
self.log_error("Processing not running, discarding.")
def add_bearing(self, data):
""" Log a packet of bearing data """
data["log_type"] = "BEARING"
data["log_time"] = pytz.utc.localize(datetime.datetime.utcnow()).isoformat()
# Add it to the queue if we are running.
if self.input_processing_running:
self.input_queue.put(data)
else:
self.log_error("Processing not running, discarding.")
def process_queue(self):
""" Process data from the input queue, and write telemetry to log files.
"""
self.log_info("Started Chase Logger Thread.")
while self.input_processing_running:
# Process everything in the queue.
self.file_lock.acquire()
while self.input_queue.qsize() > 0:
try:
_data = self.input_queue.get_nowait()
_data_str = json.dumps(_data)
self.f.write(_data_str + "\n")
except Exception as e:
self.log_error("Error processing data - %s" % str(e))
self.file_lock.release()
# Sleep while waiting for some new data.
time.sleep(5)
def running(self):
""" Check if the logging thread is running.
Returns:
bool: True if the logging thread is running.
"""
return self.input_processing_running
def close(self):
try:
self.input_processing_running = False
self.f.close()
except Exception as e:
self.log_error("Error when closing - %s" % str(e))
self.log_info("Stopped Telemetry Logger Thread.")
def log_debug(self, line):
""" Helper function to log a debug message with a descriptive heading.
Args:
line (str): Message to be logged.
"""
logging.debug("Chase Logger - %s" % line)
def log_info(self, line):
""" Helper function to log an informational message with a descriptive heading.
Args:
line (str): Message to be logged.
"""
logging.info("Chase Logger - %s" % line)
def log_error(self, line):
""" Helper function to log an error message with a descriptive heading.
Args:
line (str): Message to be logged.
"""
logging.error("Chase Logger - %s" % line)