Improved backend & multi stop support
This commit is contained in:
parent
883bb30fea
commit
6214b6cdf1
|
@ -1,5 +1,4 @@
|
||||||
import threading
|
import threading
|
||||||
from operator import itemgetter
|
|
||||||
from wsgiref.simple_server import make_server, WSGIRequestHandler
|
from wsgiref.simple_server import make_server, WSGIRequestHandler
|
||||||
from departures import Departure
|
from departures import Departure
|
||||||
from bottle import route, error, run, get, ServerAdapter, static_file
|
from bottle import route, error, run, get, ServerAdapter, static_file
|
||||||
|
@ -17,13 +16,9 @@ class Server(ServerAdapter):
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.server.shutdown()
|
self.server.shutdown()
|
||||||
|
|
||||||
@route('/departures')
|
@route('/departures/<stop_id>')
|
||||||
def index():
|
def index(stop_id: int):
|
||||||
resp = []
|
return {'departures': Departure.get(stop_id)}
|
||||||
for d in Departure.storage:
|
|
||||||
resp.append(Departure.storage[d].json())
|
|
||||||
resp.sort(key=itemgetter("departure"))
|
|
||||||
return {'departures': resp}
|
|
||||||
|
|
||||||
@get("/")
|
@get("/")
|
||||||
def static():
|
def static():
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from math import floor
|
from math import floor
|
||||||
|
from operator import itemgetter
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from dateutil import parser
|
from dateutil import parser
|
||||||
|
|
||||||
|
@ -7,6 +8,16 @@ class Departure:
|
||||||
id_pool = []
|
id_pool = []
|
||||||
storage = dict()
|
storage = dict()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get(stop_id: str):
|
||||||
|
resp = []
|
||||||
|
for id in Departure.storage:
|
||||||
|
d = Departure.storage[id]
|
||||||
|
if d.stop_id == int(stop_id):
|
||||||
|
resp.append(d.json())
|
||||||
|
resp.sort(key=itemgetter("departure"))
|
||||||
|
return resp
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_id():
|
def get_id():
|
||||||
i = 0
|
i = 0
|
||||||
|
@ -21,20 +32,20 @@ class Departure:
|
||||||
remove = []
|
remove = []
|
||||||
for did in Departure.storage:
|
for did in Departure.storage:
|
||||||
d = Departure.storage[did]
|
d = Departure.storage[did]
|
||||||
if d.get_departure() < -35:
|
if d.get_departure() < -3.5:
|
||||||
Departure.id_pool.remove(d.id)
|
Departure.id_pool.remove(d.id)
|
||||||
remove.append(did)
|
remove.append(did)
|
||||||
for did in remove:
|
for did in remove:
|
||||||
Departure.storage.pop(did)
|
Departure.storage.pop(did)
|
||||||
|
|
||||||
def __init__(self, did, type, line, last_stop, departure, delay, lora_controller):
|
def __init__(self, did, stop_id, type, line, last_stop, departure, delay, lora_controller):
|
||||||
departure = (parser.parse(departure)).timestamp()
|
departure = (parser.parse(departure)).timestamp()
|
||||||
if -(datetime.now().timestamp() - (departure + delay*60))/60 <= -1:
|
if -(datetime.now().timestamp() - (departure + delay*60))/60 <= -1:
|
||||||
return
|
return
|
||||||
Departure.storage[did] = self
|
|
||||||
if len(last_stop) >= 21:
|
if len(last_stop) >= 21:
|
||||||
last_stop = last_stop[:20].strip() + "..."
|
last_stop = last_stop[:20].strip() + "..."
|
||||||
self.did = did
|
self.did = did
|
||||||
|
self.stop_id = stop_id
|
||||||
self.id = Departure.get_id()
|
self.id = Departure.get_id()
|
||||||
self.line = line
|
self.line = line
|
||||||
self.type = type
|
self.type = type
|
||||||
|
@ -43,6 +54,7 @@ class Departure:
|
||||||
self.delay = delay
|
self.delay = delay
|
||||||
self.controller = lora_controller
|
self.controller = lora_controller
|
||||||
self.controller.data(self)
|
self.controller.data(self)
|
||||||
|
Departure.storage[did] = self
|
||||||
|
|
||||||
def update(self, delay):
|
def update(self, delay):
|
||||||
if delay != self.delay:
|
if delay != self.delay:
|
||||||
|
@ -50,14 +62,10 @@ class Departure:
|
||||||
self.delay = delay
|
self.delay = delay
|
||||||
|
|
||||||
def get_departure(self):
|
def get_departure(self):
|
||||||
departure = floor(self.get_accurate_departure()*10)
|
return -(floor(((datetime.now().timestamp() - (self.departure + self.delay*60))/60)*10)/10)
|
||||||
return departure
|
|
||||||
|
|
||||||
def get_accurate_departure(self):
|
|
||||||
return -((datetime.now().timestamp() - (self.departure + self.delay*60))/60)
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"{self.id}|{self.type}|{self.line}|{self.last_stop}|{self.get_departure()}"
|
return f"{self.id}|{self.type}|{self.line}|{self.last_stop}|{(self.get_departure()*10):.0f}"
|
||||||
|
|
||||||
def json(self):
|
def json(self):
|
||||||
return {
|
return {
|
||||||
|
@ -65,6 +73,6 @@ class Departure:
|
||||||
'line': self.line,
|
'line': self.line,
|
||||||
'type': self.type,
|
'type': self.type,
|
||||||
'last_stop': self.last_stop,
|
'last_stop': self.last_stop,
|
||||||
'departure': floor(self.get_accurate_departure()),
|
'departure': self.get_departure(),
|
||||||
'delay': self.delay
|
'delay': self.delay
|
||||||
}
|
}
|
|
@ -35,8 +35,8 @@ class LoraController:
|
||||||
data = requests.post(url, verify=False, headers=headers, data=json.dumps(data)).json()
|
data = requests.post(url, verify=False, headers=headers, data=json.dumps(data)).json()
|
||||||
self.token = data["token"]
|
self.token = data["token"]
|
||||||
|
|
||||||
def new(self, id):
|
def new(self, id: int, stop_id: int):
|
||||||
self.devices.append(LoraDevice(self, id))
|
self.devices.append(LoraDevice(self, id, stop_id))
|
||||||
|
|
||||||
def time(self):
|
def time(self):
|
||||||
def to_string():
|
def to_string():
|
||||||
|
@ -46,10 +46,11 @@ class LoraController:
|
||||||
|
|
||||||
def data(self, data):
|
def data(self, data):
|
||||||
for d in self.devices:
|
for d in self.devices:
|
||||||
d.send(data.__repr__)
|
if d.stop_id == data.stop_id:
|
||||||
|
d.send(data.__repr__)
|
||||||
|
|
||||||
def send(self, id, port, msg):
|
def send(self, id, msg):
|
||||||
self.message_pool.append((id, port, msg))
|
self.message_pool.append((id, msg))
|
||||||
if not self.thread or not self.thread.is_alive():
|
if not self.thread or not self.thread.is_alive():
|
||||||
self.thread = threading.Thread(target=self.thread_sending)
|
self.thread = threading.Thread(target=self.thread_sending)
|
||||||
self.thread.start()
|
self.thread.start()
|
||||||
|
@ -60,14 +61,14 @@ class LoraController:
|
||||||
break
|
break
|
||||||
data = self.message_pool.pop(0)
|
data = self.message_pool.pop(0)
|
||||||
url = f"https://lora.plzen.eu/api/v2/nodes/{data[0]:0>16x}/queue"
|
url = f"https://lora.plzen.eu/api/v2/nodes/{data[0]:0>16x}/queue"
|
||||||
string = data[2]()
|
string = data[1]()
|
||||||
headers = CaseInsensitiveDict()
|
headers = CaseInsensitiveDict()
|
||||||
headers["Content-Type"] = "application/json"
|
headers["Content-Type"] = "application/json"
|
||||||
headers["Authorization"] = f"Bearer {self.token}"
|
headers["Authorization"] = f"Bearer {self.token}"
|
||||||
payload = {
|
payload = {
|
||||||
"confirmed": True,
|
"confirmed": True,
|
||||||
"data": b64encode(string.encode("utf-8")).decode("ascii"),
|
"data": b64encode(string.encode("utf-8")).decode("ascii"),
|
||||||
"fPort": data[1],
|
"fPort": 10,
|
||||||
"reference": "string"
|
"reference": "string"
|
||||||
}
|
}
|
||||||
requests.post(url, verify=False, headers=headers, data=json.dumps(payload))
|
requests.post(url, verify=False, headers=headers, data=json.dumps(payload))
|
||||||
|
@ -75,11 +76,10 @@ class LoraController:
|
||||||
|
|
||||||
class LoraDevice:
|
class LoraDevice:
|
||||||
|
|
||||||
def __init__(self, controller: LoraController, deveui: int):
|
def __init__(self, controller: LoraController, deveui: int, stop_id: int):
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
self.id = deveui
|
self.id = deveui
|
||||||
self.port = 0
|
self.stop_id = stop_id
|
||||||
|
|
||||||
def send(self, msg):
|
def send(self, msg):
|
||||||
self.controller.send(self.id, self.port+1, msg)
|
self.controller.send(self.id, msg)
|
||||||
self.port = (self.port + 1) % 15
|
|
|
@ -12,7 +12,7 @@ import sys
|
||||||
|
|
||||||
class Main:
|
class Main:
|
||||||
|
|
||||||
def __init__(self, stop_id):
|
def __init__(self):
|
||||||
config = Path("config.yml")
|
config = Path("config.yml")
|
||||||
try:
|
try:
|
||||||
self.config = yaml.safe_load(config.read_text(encoding="utf-8-sig"))
|
self.config = yaml.safe_load(config.read_text(encoding="utf-8-sig"))
|
||||||
|
@ -20,9 +20,9 @@ class Main:
|
||||||
print("Invalid config file!")
|
print("Invalid config file!")
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
self.ended = False
|
self.ended = False
|
||||||
self.stop_id = stop_id
|
|
||||||
lora_controller = LoraController(self)
|
lora_controller = LoraController(self)
|
||||||
lora_controller.new(0xbdea85badeedf1)
|
for d in self.config["devices"]:
|
||||||
|
lora_controller.new(d["id"], d["stop_id"])
|
||||||
self.controller = lora_controller
|
self.controller = lora_controller
|
||||||
self.thread = threading.Thread(target=self.update_loop)
|
self.thread = threading.Thread(target=self.update_loop)
|
||||||
self.thread.start()
|
self.thread.start()
|
||||||
|
@ -44,13 +44,14 @@ class Main:
|
||||||
"FullResults": False
|
"FullResults": False
|
||||||
}
|
}
|
||||||
|
|
||||||
resp = requests.post(url, headers=headers, data=json.dumps(data), timeout=100).json()
|
resp = requests.post(url, headers=headers, data=json.dumps(data), timeout=1000).json()
|
||||||
|
|
||||||
for c in resp:
|
for c in resp:
|
||||||
did = f"{stop_id}|{abs(c['ConnectionId']['ConnectionId'])}"
|
did = f"{stop_id}|{abs(c['ConnectionId']['ConnectionId'])}"
|
||||||
if did not in Departure.storage:
|
if did not in Departure.storage:
|
||||||
Departure(
|
Departure(
|
||||||
did,
|
did,
|
||||||
|
stop_id,
|
||||||
c["Line"]["TractionType"],
|
c["Line"]["TractionType"],
|
||||||
c["Line"]["Name"],
|
c["Line"]["Name"],
|
||||||
c["LastStopName"],
|
c["LastStopName"],
|
||||||
|
@ -76,7 +77,8 @@ class Main:
|
||||||
|
|
||||||
if refetch == 0:
|
if refetch == 0:
|
||||||
self.controller.time()
|
self.controller.time()
|
||||||
self.fetch(self.stop_id, 20)
|
for s in self.config["stops"]:
|
||||||
|
self.fetch(s["id"], 15)
|
||||||
|
|
||||||
refetch = (refetch + 1) % 3
|
refetch = (refetch + 1) % 3
|
||||||
regenerate = (regenerate + 1) % (6*30)
|
regenerate = (regenerate + 1) % (6*30)
|
||||||
|
@ -119,5 +121,5 @@ class Main:
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
self.stop()
|
self.stop()
|
||||||
|
|
||||||
main = Main("40")
|
main = Main()
|
||||||
main.input_loop()
|
main.input_loop()
|
|
@ -47,11 +47,11 @@
|
||||||
<div class="header">zpoždění</div>
|
<div class="header">zpoždění</div>
|
||||||
<div class="header">odjezd</div>
|
<div class="header">odjezd</div>
|
||||||
<template v-for="d in departures">
|
<template v-for="d in departures">
|
||||||
<template v-if="d.departure >= 0">
|
<template v-if="d.departure > -0.7">
|
||||||
<div class="line" :class="'type'+d.type">{{ d.line }}</div>
|
<div class="line" :class="'type'+d.type">{{ d.line }}</div>
|
||||||
<div class="last-stop">{{ d.last_stop }}</div>
|
<div class="last-stop">{{ d.last_stop }}</div>
|
||||||
<div class="delay">{{ d.delay == 0 ? '' : '+'+d.delay }}</div>
|
<div class="delay">{{ d.delay == 0 ? '' : '+'+d.delay }}</div>
|
||||||
<div class="departure">{{ d.departure == 0 ? '<1' : d.departure }}</div>
|
<div class="departure">{{ d.departure < 1 ? '<1' : Math.floor(d.departure) }}</div>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
@ -78,7 +78,7 @@
|
||||||
resolve(null);
|
resolve(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
xhr.open("GET", "/departures", true);
|
xhr.open("GET", "/departures/40", true);
|
||||||
xhr.send();
|
xhr.send();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue