1
0
mirror of https://github.com/craigerl/aprsd.git synced 2024-11-21 23:55:17 -05:00

Merge pull request #139 from craigerl/walt-test

Walt test
This commit is contained in:
Walter A. Boring IV 2023-11-17 13:47:05 -05:00 committed by GitHub
commit b73373db3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 709 additions and 423 deletions

View File

@ -145,6 +145,8 @@ def sample_config(ctx):
if not sys.argv[1:]:
raise SystemExit
raise
LOG.warning(conf.namespace)
return
generator.generate(conf)

View File

@ -1,7 +1,6 @@
import abc
from dataclasses import asdict, dataclass, field
import datetime
import json
from datetime import datetime
import logging
import re
import time
@ -9,9 +8,9 @@ import time
from typing import List
import dacite
from dataclasses_json import dataclass_json
from aprsd.utils import counter
from aprsd.utils import json as aprsd_json
LOG = logging.getLogger("APRSD")
@ -28,12 +27,20 @@ PACKET_TYPE_BEACON = "beacon"
PACKET_TYPE_THIRDPARTY = "thirdparty"
PACKET_TYPE_UNCOMPRESSED = "uncompressed"
NO_DATE = datetime(1900, 10, 24)
def _init_timestamp():
"""Build a unix style timestamp integer"""
return int(round(time.time()))
def _init_send_time():
# We have to use a datetime here, or the json encoder
# Fails on a NoneType.
return NO_DATE
def _init_msgNo(): # noqa: N802
"""For some reason __post__init doesn't get called.
@ -45,6 +52,20 @@ def _init_msgNo(): # noqa: N802
return c.value
def factory_from_dict(packet_dict):
pkt_type = get_packet_type(packet_dict)
if pkt_type:
cls = TYPE_LOOKUP[pkt_type]
return cls.from_dict(packet_dict)
def factory_from_json(packet_dict):
pkt_type = get_packet_type(packet_dict)
if pkt_type:
return TYPE_LOOKUP[pkt_type].from_json(packet_dict)
@dataclass_json
@dataclass(unsafe_hash=True)
class Packet(metaclass=abc.ABCMeta):
from_call: str = field(default=None)
@ -64,7 +85,19 @@ class Packet(metaclass=abc.ABCMeta):
# Fields related to sending packets out
send_count: int = field(repr=False, default=0, compare=False, hash=False)
retry_count: int = field(repr=False, default=3, compare=False, hash=False)
last_send_time: datetime.timedelta = field(repr=False, default=None, compare=False, hash=False)
# last_send_time: datetime = field(
# metadata=dc_json_config(
# encoder=datetime.isoformat,
# decoder=datetime.fromisoformat,
# ),
# repr=True,
# default_factory=_init_send_time,
# compare=False,
# hash=False
# )
last_send_time: float = field(repr=False, default=0, compare=False, hash=False)
last_send_attempt: int = field(repr=False, default=0, compare=False, hash=False)
# Do we allow this packet to be saved to send later?
allow_delay: bool = field(repr=False, default=True, compare=False, hash=False)
path: List[str] = field(default_factory=list, compare=False, hash=False)
@ -73,16 +106,12 @@ class Packet(metaclass=abc.ABCMeta):
def __post__init__(self):
LOG.warning(f"POST INIT {self}")
@property
def __dict__(self):
return asdict(self)
@property
def json(self):
"""
get the json formated string
"""
return json.dumps(self.__dict__, cls=aprsd_json.EnhancedJSONEncoder)
return self.to_json()
def get(self, key, default=None):
"""Emulate a getter on a dict."""
@ -289,6 +318,7 @@ class RejectPacket(Packet):
self.payload = f":{self.to_call.ljust(9)} :rej{self.msgNo}"
@dataclass_json
@dataclass(unsafe_hash=True)
class MessagePacket(Packet):
message_text: str = field(default=None)
@ -406,12 +436,12 @@ class GPSPacket(Packet):
def _build_time_zulu(self):
"""Build the timestamp in UTC/zulu."""
if self.timestamp:
local_dt = datetime.datetime.fromtimestamp(self.timestamp)
local_dt = datetime.fromtimestamp(self.timestamp)
else:
local_dt = datetime.datetime.now()
self.timestamp = datetime.datetime.timestamp(local_dt)
local_dt = datetime.now()
self.timestamp = datetime.timestamp(local_dt)
utc_offset_timedelta = datetime.datetime.utcnow() - local_dt
utc_offset_timedelta = datetime.utcnow() - local_dt
result_utc_datetime = local_dt + utc_offset_timedelta
time_zulu = result_utc_datetime.strftime("%d%H%M")
return time_zulu
@ -567,7 +597,7 @@ class WeatherPacket(GPSPacket):
class ThirdParty(Packet):
# Holds the encapsulated packet
subpacket: Packet = None
subpacket: Packet = field(default=None, compare=True, hash=False)
def __repr__(self):
"""Build the repr version of the packet."""
@ -600,7 +630,7 @@ def get_packet_type(packet: dict):
pkt_format = packet.get("format", None)
msg_response = packet.get("response", None)
packet_type = "unknown"
packet_type = PACKET_TYPE_UNKNOWN
if pkt_format == "message" and msg_response == "ack":
packet_type = PACKET_TYPE_ACK
elif pkt_format == "message" and msg_response == "rej":
@ -620,6 +650,10 @@ def get_packet_type(packet: dict):
packet_type = PACKET_TYPE_WX
elif pkt_format == PACKET_TYPE_THIRDPARTY:
packet_type = PACKET_TYPE_THIRDPARTY
if packet_type == PACKET_TYPE_UNKNOWN:
if "latitude" in packet:
packet_type = PACKET_TYPE_BEACON
return packet_type

View File

@ -19,11 +19,12 @@ class PacketList(MutableMapping):
lock = threading.Lock()
_total_rx: int = 0
_total_tx: int = 0
types = {}
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._maxlen = 1000
cls._maxlen = 100
cls.d = OrderedDict()
return cls._instance
@ -32,6 +33,10 @@ class PacketList(MutableMapping):
"""Add a packet that was received."""
self._total_rx += 1
self._add(packet)
ptype = packet.__class__.__name__
if not ptype in self.types:
self.types[ptype] = {"tx": 0, "rx": 0}
self.types[ptype]["rx"] += 1
seen_list.SeenList().update_seen(packet)
stats.APRSDStats().rx(packet)
@ -40,6 +45,10 @@ class PacketList(MutableMapping):
"""Add a packet that was received."""
self._total_tx += 1
self._add(packet)
ptype = packet.__class__.__name__
if not ptype in self.types:
self.types[ptype] = {"tx": 0, "rx": 0}
self.types[ptype]["tx"] += 1
seen_list.SeenList().update_seen(packet)
stats.APRSDStats().tx(packet)
@ -50,6 +59,9 @@ class PacketList(MutableMapping):
def _add(self, packet):
self[packet.key] = packet
def copy(self):
return self.d.copy()
@property
def maxlen(self):
return self._maxlen

View File

@ -65,6 +65,7 @@ class PacketTrack(objectstore.ObjectStoreMixin):
@wrapt.synchronized(lock)
def add(self, packet):
key = packet.msgNo
packet._last_send_attempt = 0
self.data[key] = packet
self.total_tracked += 1
@ -83,7 +84,7 @@ class PacketTrack(objectstore.ObjectStoreMixin):
"""Walk the list of messages and restart them if any."""
for key in self.data.keys():
pkt = self.data[key]
if pkt.last_send_attempt < pkt.retry_count:
if pkt._last_send_attempt < pkt.retry_count:
tx.send(pkt)
def _resend(self, packet):

View File

@ -1,4 +1,3 @@
import datetime
import logging
import time
@ -128,10 +127,10 @@ class SendPacketThread(aprsd_threads.APRSDThread):
# Message is still outstanding and needs to be acked.
if packet.last_send_time:
# Message has a last send time tracking
now = datetime.datetime.now()
now = int(round(time.time()))
sleeptime = (packet.send_count + 1) * 31
delta = now - packet.last_send_time
if delta > datetime.timedelta(seconds=sleeptime):
if delta > sleeptime:
# It's time to try to send it again
send_now = True
else:
@ -140,7 +139,7 @@ class SendPacketThread(aprsd_threads.APRSDThread):
if send_now:
# no attempt time, so lets send it, and start
# tracking the time.
packet.last_send_time = datetime.datetime.now()
packet.last_send_time = int(round(time.time()))
send(packet, direct=True)
packet.send_count += 1
@ -173,13 +172,13 @@ class SendAckThread(aprsd_threads.APRSDThread):
if self.packet.last_send_time:
# Message has a last send time tracking
now = datetime.datetime.now()
now = int(round(time.time()))
# aprs duplicate detection is 30 secs?
# (21 only sends first, 28 skips middle)
sleep_time = 31
delta = now - self.packet.last_send_time
if delta > datetime.timedelta(seconds=sleep_time):
if delta > sleep_time:
# It's time to try to send it again
send_now = True
elif self.loop_count % 10 == 0:
@ -190,7 +189,7 @@ class SendAckThread(aprsd_threads.APRSDThread):
if send_now:
send(self.packet, direct=True)
self.packet.send_count += 1
self.packet.last_send_time = datetime.datetime.now()
self.packet.last_send_time = int(round(time.time()))
time.sleep(1)
self.loop_count += 1

View File

@ -0,0 +1,403 @@
var packet_list = {};
var tx_data = [];
var rx_data = [];
var packet_types_data = {};
var mem_current = []
var mem_peak = []
function start_charts() {
console.log("start_charts() called");
// Initialize the echarts instance based on the prepared dom
create_packets_chart();
create_packets_types_chart();
create_messages_chart();
create_ack_chart();
create_memory_chart();
}
function create_packets_chart() {
// The packets totals TX/RX chart.
pkt_c_canvas = document.getElementById('packetsChart');
packets_chart = echarts.init(pkt_c_canvas);
// Specify the configuration items and data for the chart
var option = {
title: {
text: 'APRS Packet totals'
},
legend: {},
tooltip : {
trigger: 'axis'
},
toolbox: {
show : true,
feature : {
mark : {show: true},
dataView : {show: true, readOnly: true},
magicType : {show: true, type: ['line', 'bar']},
restore : {show: true},
saveAsImage : {show: true}
}
},
calculable : true,
xAxis: { type: 'time' },
yAxis: { },
series: [
{
name: 'tx',
type: 'line',
smooth: true,
color: 'red',
encode: {
x: 'timestamp',
y: 'tx' // refer sensor 1 value
}
},{
name: 'rx',
type: 'line',
smooth: true,
encode: {
x: 'timestamp',
y: 'rx'
}
}]
};
// Display the chart using the configuration items and data just specified.
packets_chart.setOption(option);
}
function create_packets_types_chart() {
// The packets types chart
pkt_types_canvas = document.getElementById('packetTypesChart');
packet_types_chart = echarts.init(pkt_types_canvas);
// The series and data are built and updated on the fly
// as packets come in.
var option = {
title: {
text: 'Packet Types'
},
legend: {},
tooltip : {
trigger: 'axis'
},
toolbox: {
show : true,
feature : {
mark : {show: true},
dataView : {show: true, readOnly: true},
magicType : {show: true, type: ['line', 'bar']},
restore : {show: true},
saveAsImage : {show: true}
}
},
calculable : true,
xAxis: { type: 'time' },
yAxis: { },
}
packet_types_chart.setOption(option);
}
function create_messages_chart() {
msg_c_canvas = document.getElementById('messagesChart');
message_chart = echarts.init(msg_c_canvas);
// Specify the configuration items and data for the chart
var option = {
title: {
text: 'Message Packets'
},
legend: {},
tooltip: {
trigger: 'axis'
},
toolbox: {
show: true,
feature: {
mark : {show: true},
dataView : {show: true, readOnly: true},
magicType : {show: true, type: ['line', 'bar']},
restore : {show: true},
saveAsImage : {show: true}
}
},
calculable: true,
xAxis: { type: 'time' },
yAxis: { },
series: [
{
name: 'tx',
type: 'line',
smooth: true,
color: 'red',
encode: {
x: 'timestamp',
y: 'tx' // refer sensor 1 value
}
},{
name: 'rx',
type: 'line',
smooth: true,
encode: {
x: 'timestamp',
y: 'rx'
}
}]
};
// Display the chart using the configuration items and data just specified.
message_chart.setOption(option);
}
function create_ack_chart() {
ack_canvas = document.getElementById('acksChart');
ack_chart = echarts.init(ack_canvas);
// Specify the configuration items and data for the chart
var option = {
title: {
text: 'Ack Packets'
},
legend: {},
tooltip: {
trigger: 'axis'
},
toolbox: {
show: true,
feature: {
mark : {show: true},
dataView : {show: true, readOnly: false},
magicType : {show: true, type: ['line', 'bar']},
restore : {show: true},
saveAsImage : {show: true}
}
},
calculable: true,
xAxis: { type: 'time' },
yAxis: { },
series: [
{
name: 'tx',
type: 'line',
smooth: true,
color: 'red',
encode: {
x: 'timestamp',
y: 'tx' // refer sensor 1 value
}
},{
name: 'rx',
type: 'line',
smooth: true,
encode: {
x: 'timestamp',
y: 'rx'
}
}]
};
ack_chart.setOption(option);
}
function create_memory_chart() {
ack_canvas = document.getElementById('memChart');
memory_chart = echarts.init(ack_canvas);
// Specify the configuration items and data for the chart
var option = {
title: {
text: 'Memory Usage'
},
legend: {},
tooltip: {
trigger: 'axis'
},
toolbox: {
show: true,
feature: {
mark : {show: true},
dataView : {show: true, readOnly: false},
magicType : {show: true, type: ['line', 'bar']},
restore : {show: true},
saveAsImage : {show: true}
}
},
calculable: true,
xAxis: { type: 'time' },
yAxis: { },
series: [
{
name: 'current',
type: 'line',
smooth: true,
color: 'red',
encode: {
x: 'timestamp',
y: 'current' // refer sensor 1 value
}
},{
name: 'peak',
type: 'line',
smooth: true,
encode: {
x: 'timestamp',
y: 'peak'
}
}]
};
memory_chart.setOption(option);
}
function updatePacketData(chart, time, first, second) {
tx_data.push([time, first]);
rx_data.push([time, second]);
option = {
series: [
{
name: 'tx',
data: tx_data,
},
{
name: 'rx',
data: rx_data,
}
]
}
chart.setOption(option);
}
function updatePacketTypesData(time, typesdata) {
//The options series is created on the fly each time based on
//the packet types we have in the data
var series = []
for (const k in typesdata) {
tx = [time, typesdata[k]["tx"]]
rx = [time, typesdata[k]["rx"]]
if (packet_types_data.hasOwnProperty(k)) {
packet_types_data[k]["tx"].push(tx)
packet_types_data[k]["rx"].push(rx)
} else {
packet_types_data[k] = {'tx': [tx], 'rx': [rx]}
}
}
}
function updatePacketTypesChart() {
series = []
for (const k in packet_types_data) {
entry = {
name: k+"tx",
data: packet_types_data[k]["tx"],
type: 'line',
smooth: true,
encode: {
x: 'timestamp',
y: k+'tx' // refer sensor 1 value
}
}
series.push(entry)
entry = {
name: k+"rx",
data: packet_types_data[k]["rx"],
type: 'line',
smooth: true,
encode: {
x: 'timestamp',
y: k+'rx' // refer sensor 1 value
}
}
series.push(entry)
}
option = {
series: series
}
console.log(option)
packet_types_chart.setOption(option);
}
function updateTypeChart(chart, key) {
//Generic function to update a packet type chart
if (! packet_types_data.hasOwnProperty(key)) {
return;
}
if (! packet_types_data[key].hasOwnProperty('tx')) {
return;
}
var option = {
series: [{
name: "tx",
data: packet_types_data[key]["tx"],
},
{
name: "rx",
data: packet_types_data[key]["rx"]
}]
}
chart.setOption(option);
}
function updateMemChart(time, current, peak) {
mem_current.push([time, current]);
mem_peak.push([time, peak]);
option = {
series: [
{
name: 'current',
data: mem_current,
},
{
name: 'peak',
data: mem_peak,
}
]
}
memory_chart.setOption(option);
}
function updateMessagesChart() {
updateTypeChart(message_chart, "MessagePacket")
}
function updateAcksChart() {
updateTypeChart(ack_chart, "AckPacket")
}
function update_stats( data ) {
console.log(data);
our_callsign = data["stats"]["aprsd"]["callsign"];
$("#version").text( data["stats"]["aprsd"]["version"] );
$("#aprs_connection").html( data["aprs_connection"] );
$("#uptime").text( "uptime: " + data["stats"]["aprsd"]["uptime"] );
const html_pretty = Prism.highlight(JSON.stringify(data, null, '\t'), Prism.languages.json, 'json');
$("#jsonstats").html(html_pretty);
t = Date.parse(data["time"]);
ts = new Date(t);
updatePacketData(packets_chart, ts, data["stats"]["packets"]["sent"], data["stats"]["packets"]["received"]);
updatePacketTypesData(ts, data["stats"]["packets"]["types"]);
updatePacketTypesChart();
updateMessagesChart();
updateAcksChart();
updateMemChart(ts, data["stats"]["aprsd"]["memory_current"], data["stats"]["aprsd"]["memory_peak"]);
//updateQuadData(message_chart, short_time, data["stats"]["messages"]["sent"], data["stats"]["messages"]["received"], data["stats"]["messages"]["ack_sent"], data["stats"]["messages"]["ack_recieved"]);
//updateDualData(email_chart, short_time, data["stats"]["email"]["sent"], data["stats"]["email"]["recieved"]);
//updateDualData(memory_chart, short_time, data["stats"]["aprsd"]["memory_peak"], data["stats"]["aprsd"]["memory_current"]);
}

View File

@ -6,6 +6,7 @@
<script src="https://cdn.socket.io/4.7.1/socket.io.min.js" integrity="sha512-+NaO7d6gQ1YPxvc/qHIqZEchjGm207SszoNeMgppoqD/67fEqmc1edS8zrbxPD+4RQI3gDgT/83ihpFW61TG/Q==" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.4/dist/Chart.bundle.js"></script>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/semantic-ui@2.4.2/dist/semantic.min.css">
<script src="https://cdn.jsdelivr.net/npm/semantic-ui@2.4.2/dist/semantic.min.js"></script>
@ -15,7 +16,7 @@
<link rel="stylesheet" href="/static/css/prism.css">
<script src="/static/js/prism.js"></script>
<script src="/static/js/main.js"></script>
<script src="/static/js/charts.js"></script>
<script src="/static/js/echarts.js"></script>
<script src="/static/js/tabs.js"></script>
<script src="/static/js/send-message.js"></script>
<script src="/static/js/logs.js"></script>
@ -83,6 +84,7 @@
<div class="item" data-tab="plugin-tab">Plugins</div>
<div class="item" data-tab="config-tab">Config</div>
<div class="item" data-tab="log-tab">LogFile</div>
<!-- <div class="item" data-tab="oslo-tab">OSLO CONFIG</div> //-->
<div class="item" data-tab="raw-tab">Raw JSON</div>
</div>
@ -92,25 +94,25 @@
<div class="ui equal width relaxed grid">
<div class="row">
<div class="column">
<div class="ui segment" style="height: 300px">
<canvas id="packetsChart"></canvas>
</div>
</div>
<div class="column">
<div class="ui segment" style="height: 300px">
<canvas id="messageChart"></canvas>
</div>
<div class="ui segment" style="height: 300px" id="packetsChart"></div>
</div>
</div>
<div class="row">
<div class="column">
<div class="ui segment" style="height: 300px">
<canvas id="emailChart"></canvas>
</div>
<div class="ui segment" style="height: 300px" id="packetTypesChart"></div>
</div>
</div>
<div class="row">
<div class="column">
<div class="ui segment" style="height: 300px" id="messagesChart"></div>
</div>
<div class="column">
<div class="ui segment" style="height: 300px">
<canvas id="memChart"></canvas>
<div class="ui segment" style="height: 300px" id="acksChart"></div>
</div>
</div>
<div class="row">
<div class="column">
<div class="ui segment" style="height: 300px" id="memChart">
</div>
</div>
</div>
@ -118,7 +120,7 @@
<div id="stats" class="two column">
<button class="ui button" id="toggleStats">Toggle raw json</button>
<pre id="jsonstats" class="language-json">{{ stats }}</pre>
</div> --!>
</div> //-->
</div>
</div>
@ -164,9 +166,15 @@
<pre id="logContainer" style="height: 600px;overflow-y:auto;overflow-x:auto;"><code id="logtext" class="language-log" ></code></pre>
</div>
<!--
<div class="ui bottom attached tab segment" data-tab="oslo-tab">
<h3 class="ui dividing header">OSLO</h3>
<pre id="osloContainer" style="height:600px;overflow-y:auto;" class="language-json">{{ oslo_out|safe }}</pre>
</div> //-->
<div class="ui bottom attached tab segment" data-tab="raw-tab">
<h3 class="ui dividing header">Raw JSON</h3>
<pre id="jsonstats" class="language-json">{{ stats|safe }}</pre>
<pre id="jsonstats" class="language-yaml" style="height:600px;overflow-y:auto;">{{ stats|safe }}</pre>
</div>
<div class="ui text container">

View File

@ -1,4 +1,6 @@
import datetime
import importlib.metadata as imp
import io
import json
import logging
from logging.handlers import RotatingFileHandler
@ -8,7 +10,7 @@ import flask
from flask import Flask
from flask.logging import default_handler
from flask_httpauth import HTTPBasicAuth
from oslo_config import cfg
from oslo_config import cfg, generator
import socketio
from werkzeug.security import check_password_hash
@ -96,9 +98,17 @@ def _stats():
if packet_list:
rx = packet_list.total_rx()
tx = packet_list.total_tx()
types = {}
types_copy = packet_list.types.copy()
for key in types_copy:
types[str(key)] = dict(types_copy[key])
stats_dict["packets"] = {
"sent": tx,
"received": rx,
"types": types,
}
if track:
size_tracker = len(track)
@ -123,7 +133,6 @@ def stats():
@app.route("/")
def index():
stats = _stats()
LOG.debug(stats)
wl = aprsd_rpc_client.RPCClient().get_watch_list()
if wl and wl.is_enabled():
watch_count = len(wl)
@ -185,6 +194,7 @@ def index():
watch_age=watch_age,
seen_count=seen_count,
plugin_count=plugin_count,
# oslo_out=generate_oslo()
)
@ -205,10 +215,12 @@ def get_packets():
LOG.debug("/packets called")
packet_list = aprsd_rpc_client.RPCClient().get_packet_list()
if packet_list:
packets = packet_list.get()
tmp_list = []
for pkt in packets:
tmp_list.append(pkt.json)
pkts = packet_list.copy()
for key in pkts:
pkt = packet_list.get(key)
if pkt:
tmp_list.append(pkt.json)
return json.dumps(tmp_list)
else:
@ -225,6 +237,36 @@ def plugins():
return "reloaded"
def _get_namespaces():
args = []
all = imp.entry_points()
selected = []
if "oslo.config.opts" in all:
for x in all["oslo.config.opts"]:
if x.group == "oslo.config.opts":
selected.append(x)
for entry in selected:
if "aprsd" in entry.name:
args.append("--namespace")
args.append(entry.name)
return args
def generate_oslo():
CONF.namespace = _get_namespaces()
string_out = io.StringIO()
generator.generate(CONF, string_out)
return string_out.getvalue()
@auth.login_required
@app.route("/oslo")
def oslo():
return generate_oslo()
@auth.login_required
@app.route("/save")
def save():
@ -348,6 +390,8 @@ if __name__ == "uwsgi_file_aprsd_wsgi":
log_level = init_app(
log_level="DEBUG",
config_file="/config/aprsd.conf",
# Commented out for local development.
# config_file=cli_helper.DEFAULT_CONFIG_FILE
)
setup_logging(app, log_level)
sio.register_namespace(LoggingNamespace("/logs"))
@ -362,7 +406,11 @@ if __name__ == "aprsd.wsgi":
sio = socketio.Server(logger=True, async_mode=async_mode)
app.wsgi_app = socketio.WSGIApp(sio, app.wsgi_app)
log_level = init_app(config_file="/config/aprsd.conf", log_level="DEBUG")
log_level = init_app(
log_level="DEBUG",
# config_file="/config/aprsd.conf",
config_file=cli_helper.DEFAULT_CONFIG_FILE,
)
setup_logging(app, log_level)
sio.register_namespace(LoggingNamespace("/logs"))
CONF.log_opt_values(LOG, logging.DEBUG)

View File

@ -1,212 +1,87 @@
#
# This file is autogenerated by pip-compile with Python 3.11
# This file is autogenerated by pip-compile with Python 3.10
# by the following command:
#
# pip-compile --annotation-style=line dev-requirements.in
#
add-trailing-comma==3.1.0
# via gray
alabaster==0.7.13
# via sphinx
attrs==23.1.0
# via
# jsonschema
# referencing
autoflake==1.5.3
# via gray
babel==2.12.1
# via sphinx
black==23.7.0
# via gray
build==1.0.3
# via pip-tools
cachetools==5.3.1
# via tox
certifi==2023.7.22
# via requests
cfgv==3.4.0
# via pre-commit
chardet==5.2.0
# via tox
charset-normalizer==3.2.0
# via requests
click==8.1.7
# via
# black
# pip-tools
colorama==0.4.6
# via tox
commonmark==0.9.1
# via rich
configargparse==1.7
# via gray
coverage[toml]==7.3.1
# via pytest-cov
distlib==0.3.7
# via virtualenv
docutils==0.20.1
# via sphinx
filelock==3.12.3
# via
# tox
# virtualenv
fixit==0.1.4
# via gray
flake8==6.1.0
# via
# -r dev-requirements.in
# fixit
# pep8-naming
gray==0.13.0
# via -r dev-requirements.in
identify==2.5.27
# via pre-commit
idna==3.4
# via requests
imagesize==1.4.1
# via sphinx
importlib-resources==6.0.1
# via fixit
iniconfig==2.0.0
# via pytest
isort==5.12.0
# via
# -r dev-requirements.in
# gray
jinja2==3.1.2
# via sphinx
jsonschema==4.19.0
# via fixit
jsonschema-specifications==2023.7.1
# via jsonschema
libcst==1.0.1
# via fixit
markupsafe==2.1.3
# via jinja2
mccabe==0.7.0
# via flake8
mypy==1.5.1
# via -r dev-requirements.in
mypy-extensions==1.0.0
# via
# black
# mypy
# typing-inspect
nodeenv==1.8.0
# via pre-commit
packaging==23.1
# via
# black
# build
# pyproject-api
# pytest
# sphinx
# tox
pathspec==0.11.2
# via black
pep8-naming==0.13.3
# via -r dev-requirements.in
pip-tools==7.3.0
# via -r dev-requirements.in
platformdirs==3.10.0
# via
# black
# tox
# virtualenv
pluggy==1.3.0
# via
# pytest
# tox
pre-commit==3.4.0
# via -r dev-requirements.in
pycodestyle==2.11.0
# via flake8
pyflakes==3.1.0
# via
# autoflake
# flake8
pygments==2.16.1
# via
# rich
# sphinx
pyproject-api==1.6.1
# via tox
pyproject-hooks==1.0.0
# via build
pytest==7.4.2
# via
# -r dev-requirements.in
# pytest-cov
pytest-cov==4.1.0
# via -r dev-requirements.in
pyupgrade==3.10.1
# via gray
pyyaml==6.0.1
# via
# fixit
# libcst
# pre-commit
referencing==0.30.2
# via
# jsonschema
# jsonschema-specifications
requests==2.31.0
# via sphinx
rich==12.6.0
# via gray
rpds-py==0.10.2
# via
# jsonschema
# referencing
snowballstemmer==2.2.0
# via sphinx
sphinx==7.2.5
# via
# -r dev-requirements.in
# sphinxcontrib-applehelp
# sphinxcontrib-devhelp
# sphinxcontrib-htmlhelp
# sphinxcontrib-qthelp
# sphinxcontrib-serializinghtml
sphinxcontrib-applehelp==1.0.7
# via sphinx
sphinxcontrib-devhelp==1.0.5
# via sphinx
sphinxcontrib-htmlhelp==2.0.4
# via sphinx
sphinxcontrib-jsmath==1.0.1
# via sphinx
sphinxcontrib-qthelp==1.0.6
# via sphinx
sphinxcontrib-serializinghtml==1.1.9
# via sphinx
tokenize-rt==5.2.0
# via
# add-trailing-comma
# pyupgrade
toml==0.10.2
# via autoflake
tox==4.11.2
# via -r dev-requirements.in
typing-extensions==4.7.1
# via
# libcst
# mypy
# typing-inspect
typing-inspect==0.9.0
# via libcst
unify==0.5
# via gray
untokenize==0.1.1
# via unify
urllib3==2.0.7
# via requests
virtualenv==20.24.5
# via
# pre-commit
# tox
wheel==0.41.2
# via pip-tools
add-trailing-comma==3.1.0 # via gray
alabaster==0.7.13 # via sphinx
attrs==23.1.0 # via jsonschema, referencing
autoflake==1.5.3 # via gray
babel==2.13.1 # via sphinx
black==23.11.0 # via gray
build==1.0.3 # via pip-tools
cachetools==5.3.2 # via tox
certifi==2023.7.22 # via requests
cfgv==3.4.0 # via pre-commit
chardet==5.2.0 # via tox
charset-normalizer==3.3.2 # via requests
click==8.1.7 # via black, pip-tools
colorama==0.4.6 # via tox
commonmark==0.9.1 # via rich
configargparse==1.7 # via gray
coverage[toml]==7.3.2 # via coverage, pytest-cov
distlib==0.3.7 # via virtualenv
docutils==0.20.1 # via sphinx
exceptiongroup==1.1.3 # via pytest
filelock==3.13.1 # via tox, virtualenv
fixit==0.1.4 # via gray
flake8==6.1.0 # via -r dev-requirements.in, fixit, pep8-naming
gray==0.13.0 # via -r dev-requirements.in
identify==2.5.31 # via pre-commit
idna==3.4 # via requests
imagesize==1.4.1 # via sphinx
importlib-resources==6.1.1 # via fixit
iniconfig==2.0.0 # via pytest
isort==5.12.0 # via -r dev-requirements.in, gray
jinja2==3.1.2 # via sphinx
jsonschema==4.20.0 # via fixit
jsonschema-specifications==2023.11.1 # via jsonschema
libcst==1.1.0 # via fixit
markupsafe==2.1.3 # via jinja2
mccabe==0.7.0 # via flake8
mypy==1.7.0 # via -r dev-requirements.in
mypy-extensions==1.0.0 # via black, mypy, typing-inspect
nodeenv==1.8.0 # via pre-commit
packaging==23.2 # via black, build, pyproject-api, pytest, sphinx, tox
pathspec==0.11.2 # via black
pep8-naming==0.13.3 # via -r dev-requirements.in
pip-tools==7.3.0 # via -r dev-requirements.in
platformdirs==3.11.0 # via black, tox, virtualenv
pluggy==1.3.0 # via pytest, tox
pre-commit==3.5.0 # via -r dev-requirements.in
pycodestyle==2.11.1 # via flake8
pyflakes==3.1.0 # via autoflake, flake8
pygments==2.16.1 # via rich, sphinx
pyproject-api==1.6.1 # via tox
pyproject-hooks==1.0.0 # via build
pytest==7.4.3 # via -r dev-requirements.in, pytest-cov
pytest-cov==4.1.0 # via -r dev-requirements.in
pyupgrade==3.15.0 # via gray
pyyaml==6.0.1 # via fixit, libcst, pre-commit
referencing==0.31.0 # via jsonschema, jsonschema-specifications
requests==2.31.0 # via sphinx
rich==12.6.0 # via gray
rpds-py==0.13.0 # via jsonschema, referencing
snowballstemmer==2.2.0 # via sphinx
sphinx==7.2.6 # via -r dev-requirements.in, sphinxcontrib-applehelp, sphinxcontrib-devhelp, sphinxcontrib-htmlhelp, sphinxcontrib-qthelp, sphinxcontrib-serializinghtml
sphinxcontrib-applehelp==1.0.7 # via sphinx
sphinxcontrib-devhelp==1.0.5 # via sphinx
sphinxcontrib-htmlhelp==2.0.4 # via sphinx
sphinxcontrib-jsmath==1.0.1 # via sphinx
sphinxcontrib-qthelp==1.0.6 # via sphinx
sphinxcontrib-serializinghtml==1.1.9 # via sphinx
tokenize-rt==5.2.0 # via add-trailing-comma, pyupgrade
toml==0.10.2 # via autoflake
tomli==2.0.1 # via black, build, coverage, mypy, pip-tools, pyproject-api, pyproject-hooks, pytest, tox
tox==4.11.3 # via -r dev-requirements.in
typing-extensions==4.8.0 # via black, libcst, mypy, typing-inspect
typing-inspect==0.9.0 # via libcst
unify==0.5 # via gray
untokenize==0.1.1 # via unify
urllib3==2.1.0 # via requests
virtualenv==20.24.6 # via pre-commit, tox
wheel==0.41.3 # via pip-tools
# The following packages are considered to be unsafe in a requirements file:
# pip

View File

@ -36,3 +36,4 @@ rpyc
shellingham
geopy
rush
dataclasses-json

View File

@ -1,180 +1,83 @@
#
# This file is autogenerated by pip-compile with Python 3.11
# This file is autogenerated by pip-compile with Python 3.10
# by the following command:
#
# pip-compile --annotation-style=line requirements.in
#
aprslib==0.7.2
# via -r requirements.in
attrs==23.1.0
# via
# -r requirements.in
# ax253
# kiss3
# rush
ax253==0.1.5.post1
# via kiss3
beautifulsoup4==4.12.2
# via -r requirements.in
bidict==0.22.1
# via python-socketio
bitarray==2.8.1
# via
# ax253
# kiss3
blinker==1.6.2
# via flask
certifi==2023.7.22
# via requests
charset-normalizer==3.2.0
# via requests
click==8.1.7
# via
# -r requirements.in
# click-completion
# click-params
# flask
click-completion==0.5.2
# via -r requirements.in
click-params==0.4.1
# via -r requirements.in
commonmark==0.9.1
# via rich
dacite2==2.0.0
# via -r requirements.in
dataclasses==0.6
# via -r requirements.in
debtcollector==2.5.0
# via oslo-config
decorator==5.1.1
# via validators
dnspython==2.4.2
# via eventlet
eventlet==0.33.3
# via -r requirements.in
flask==2.3.3
# via
# -r requirements.in
# flask-httpauth
# flask-socketio
flask-httpauth==4.8.0
# via -r requirements.in
flask-socketio==5.3.6
# via -r requirements.in
geographiclib==2.0
# via geopy
geopy==2.4.0
# via -r requirements.in
gevent==23.9.1
# via -r requirements.in
greenlet==3.0.0rc3
# via
# eventlet
# gevent
idna==3.4
# via requests
imapclient==2.3.1
# via -r requirements.in
importlib-metadata==6.8.0
# via
# ax253
# kiss3
itsdangerous==2.1.2
# via flask
jinja2==3.1.2
# via
# click-completion
# flask
kiss3==8.0.0
# via -r requirements.in
markupsafe==2.1.3
# via
# jinja2
# werkzeug
netaddr==0.8.0
# via oslo-config
oslo-config==9.2.0
# via -r requirements.in
oslo-i18n==6.1.0
# via oslo-config
pbr==5.11.1
# via
# -r requirements.in
# oslo-i18n
# stevedore
pluggy==1.3.0
# via -r requirements.in
plumbum==1.8.2
# via rpyc
pygments==2.16.1
# via rich
pyserial==3.5
# via pyserial-asyncio
pyserial-asyncio==0.6
# via kiss3
python-engineio==4.7.0
# via python-socketio
python-socketio==5.9.0
# via
# -r requirements.in
# flask-socketio
pytz==2023.3.post1
# via -r requirements.in
pyyaml==6.0.1
# via
# -r requirements.in
# oslo-config
requests==2.31.0
# via
# -r requirements.in
# oslo-config
# update-checker
rfc3986==2.0.0
# via oslo-config
rich==12.6.0
# via -r requirements.in
rpyc==5.3.1
# via -r requirements.in
rush==2021.4.0
# via -r requirements.in
shellingham==1.5.3
# via
# -r requirements.in
# click-completion
six==1.16.0
# via
# -r requirements.in
# click-completion
# eventlet
# imapclient
soupsieve==2.5
# via beautifulsoup4
stevedore==5.1.0
# via oslo-config
tabulate==0.9.0
# via -r requirements.in
thesmuggler==1.0.1
# via -r requirements.in
update-checker==0.18.0
# via -r requirements.in
urllib3==2.0.7
# via requests
validators==0.20.0
# via click-params
werkzeug==3.0.1
# via
# -r requirements.in
# flask
wrapt==1.15.0
# via
# -r requirements.in
# debtcollector
zipp==3.16.2
# via importlib-metadata
zope-event==5.0
# via gevent
zope-interface==6.0
# via gevent
aprslib==0.7.2 # via -r requirements.in
attrs==23.1.0 # via -r requirements.in, ax253, kiss3, rush
ax253==0.1.5.post1 # via kiss3
beautifulsoup4==4.12.2 # via -r requirements.in
bidict==0.22.1 # via python-socketio
bitarray==2.8.3 # via ax253, kiss3
blinker==1.7.0 # via flask
certifi==2023.7.22 # via requests
charset-normalizer==3.3.2 # via requests
click==8.1.7 # via -r requirements.in, click-completion, click-params, flask
click-completion==0.5.2 # via -r requirements.in
click-params==0.4.1 # via -r requirements.in
commonmark==0.9.1 # via rich
dacite2==2.0.0 # via -r requirements.in
dataclasses==0.6 # via -r requirements.in
dataclasses-json==0.6.2 # via -r requirements.in
debtcollector==2.5.0 # via oslo-config
decorator==5.1.1 # via validators
dnspython==2.4.2 # via eventlet
eventlet==0.33.3 # via -r requirements.in
flask==3.0.0 # via -r requirements.in, flask-httpauth, flask-socketio
flask-httpauth==4.8.0 # via -r requirements.in
flask-socketio==5.3.6 # via -r requirements.in
geographiclib==2.0 # via geopy
geopy==2.4.0 # via -r requirements.in
gevent==23.9.1 # via -r requirements.in
greenlet==3.0.1 # via eventlet, gevent
h11==0.14.0 # via wsproto
idna==3.4 # via requests
imapclient==3.0.0 # via -r requirements.in
importlib-metadata==6.8.0 # via ax253, kiss3
itsdangerous==2.1.2 # via flask
jinja2==3.1.2 # via click-completion, flask
kiss3==8.0.0 # via -r requirements.in
markupsafe==2.1.3 # via jinja2, werkzeug
marshmallow==3.20.1 # via dataclasses-json
mypy-extensions==1.0.0 # via typing-inspect
netaddr==0.9.0 # via oslo-config
oslo-config==9.2.0 # via -r requirements.in
oslo-i18n==6.2.0 # via oslo-config
packaging==23.2 # via marshmallow
pbr==6.0.0 # via -r requirements.in, oslo-i18n, stevedore
pluggy==1.3.0 # via -r requirements.in
plumbum==1.8.2 # via rpyc
pygments==2.16.1 # via rich
pyserial==3.5 # via pyserial-asyncio
pyserial-asyncio==0.6 # via kiss3
python-engineio==4.8.0 # via python-socketio
python-socketio==5.10.0 # via -r requirements.in, flask-socketio
pytz==2023.3.post1 # via -r requirements.in
pyyaml==6.0.1 # via -r requirements.in, oslo-config
requests==2.31.0 # via -r requirements.in, oslo-config, update-checker
rfc3986==2.0.0 # via oslo-config
rich==12.6.0 # via -r requirements.in
rpyc==5.3.1 # via -r requirements.in
rush==2021.4.0 # via -r requirements.in
shellingham==1.5.4 # via -r requirements.in, click-completion
simple-websocket==1.0.0 # via python-engineio
six==1.16.0 # via -r requirements.in, click-completion, eventlet
soupsieve==2.5 # via beautifulsoup4
stevedore==5.1.0 # via oslo-config
tabulate==0.9.0 # via -r requirements.in
thesmuggler==1.0.1 # via -r requirements.in
typing-extensions==4.8.0 # via typing-inspect
typing-inspect==0.9.0 # via dataclasses-json
update-checker==0.18.0 # via -r requirements.in
urllib3==2.1.0 # via requests
validators==0.20.0 # via click-params
werkzeug==3.0.1 # via -r requirements.in, flask
wrapt==1.16.0 # via -r requirements.in, debtcollector
wsproto==1.2.0 # via simple-websocket
zipp==3.17.0 # via importlib-metadata
zope-event==5.0 # via gevent
zope-interface==6.1 # via gevent
# The following packages are considered to be unsafe in a requirements file:
# setuptools

View File

@ -2,7 +2,7 @@
minversion = 2.9.0
skipdist = True
skip_missing_interpreters = true
envlist = pep8,py{39,310}
envlist = pep8,py{39,310,311}
#requires = tox-pipenv
# pip==22.0.4
# pip-tools==5.4.0