Projekt na zpracování a vizualizaci zaznamenaných skutečných poloh spojů PID.
Zdrojem dat je [Golemio open API](https://api.golemio.cz/pid/docs/openapi/#/%F0%9F%9B%A4%20PID%20Realtime%20Positions/get_vehiclepositions) a data z jízdních řádů ve formátu GTFS.
Projekt se skládá ze dvou částí – serveru a klienta.
Server se stará o stahování dat, které pak poskytuje klientovi, který je graficky zobrazuje uživateli.
Celý projekt je psaný v Pythonu3 pomocí asynchronní knihovny [asincio](https://docs.python.org/3/library/asyncio.html).
Grafické rozhraní je v [Qt5](https://doc.qt.io/qt-5/qt5-intro.html).
Pro mapovou část klienta je využit [QGis](https://www.qgis.org/),
jež lze pomocí [pythonního API](https://docs.qgis.org/3.34/en/docs/pyqgis_developer_cookbook/index.html) integrovat do Qt.
Serverová část běží pod [pypy3](https://pypy.org/), klient z důvodu limitací QGisem je spouštěn klasicky pomocí [CPythonu](https://en.wikipedia.org/wiki/CPython).
Podporované verze Pythonu jsou 3.10 až 3.12.
## Server
Server periodicky stahuje data aktuálních poloh pomocí API a provádí na nich předzpracování.
Klientům pak nabízí zpracovaná data a případně dopočítá jednoduché výpočty z nich, aby
se snižoval objem přenesených dat.
### Struktura dat
Adresářová struktura serveru obsahuje následující data
- Aktuální verzi jízdního řádu stahovaného z <https://data.pid.cz/PID_GTFS.zip>
uloženou v `data/gtfs/%Y-%m-%d`.
PID data každý den aktualizuje o výluky, takže dává smysl vždy pracovat s v
daný moment aktuální verzí.
Na stažených datech se ještě dělá drobný preprocessing – zejména rozdělení
velkých souborů na menší, snadněji zpracovatelné, jako například rozdělení
plánovaných tras z `shapes.txt` na jednotlivé trasy do `shape_by_id/<id>`.
- Data aktuálních poloh stažená z API v `data/realtime/%Y-%m-%d/%h/%m-%s.json.zst`.
Data jsou komprimovaná pomocí `zstd -8`, aby jejich skladování bylo úspornější.
API sice nově[^1] podporuje možnost komprimované odpovědi, ale pouze pomocí gzipu level 6,
který nemá tak dobrý kompresní poměr.
- Předzpracovaná data pro rychlejší dotazy agregovaná přes jednotlivé linky (část `trip_id` po první `_`)
uložená v `data/realtime_by_route/%Y-%m-%d/%h/<route_id>`
K těmto datům se navíc doplňují informace o ujeté vzdálenosti po plánované trase spoje pomocí vlastního algoritmu[^2].
[^1]:V době odevzdání práce asi tři dny.
[^2]:API již sice také tuto informaci obsahuje, ale moc není jasné, jak se počítá, proto byla doplněna vlastní, do budoucna rozšiřitelná, implementace.
### Struktura serveru
Server se skládá z několika procesů, které spolu komunikují jednak pomocí unix socketů,
druhak pomocí dat uložených ve file systému.
Klient se k serveru připojuje pomocí [`MainServer`](`prog/server.py`).
Těch současně může být spuštěno několik.
`MainServer` některé požadavky od klienta vyřídí pohledem do uložených dat (jako například dotaz na polohy vozidel před několika dny),
jiné řeší přeposláním dotazu na některý z procesů serveru.
O stahování dat z API se stará <prog/download_server.py>.
Ten jednou za 10 sekund pošle požadavek na API, výstup pak zkomprimuje a uloží.
Proces dále nabízí možnost připojení pomocí `DownloadServer`u, který
umožňuje ostatním procesům ptát se na poslední obdržená data,
případně si objednat dodání dalších dat, jakmile budou staženy.
Generování předzpracovaných dat má na starosti <prog/preprocess.py>.
Ten si od DownloadServeru nebo z file systému získává data, na nich počítá mimo jiné polohy spoje na plánované trase.
Také nabízí klientům možnost dotazu na data, která zrovna uchovává – konkrétně na aktuálně tvořené soubory agregací.
Pro lepší orientaci snad pomůže přiložený obrázek:

### Komunikační protokol
Pro komunikaci mezi serverem a klientem a mezi procesu serveru se
využívá vlastní protokol, který může běhat po libovolné spojované binární spolehlivé vrstvě
(unix socket, SSH, TCP, TLS, dvojice pipe, ...).
Referenční implementace protokolu je v <prog/communication.py>.
#### Vrstva zpráv
Viz `MsgParser` a `msg`.
Komunikace se sestává ze zpráv – každá zpráva obsahuje hlavičku a samotná data.
serializace zprávy je následující:
- 5 bajtů délka hlavičky zapsaná a ASCII v desítkové soustavě
- 12 bajtů délka dat zapsaná a ASCII v desítkové soustavě
- hlavička určené délky
- data určené délky
Hlavička se zpravidla medializuje jako json, data pomocí cbor2,
ovšem vyšší vrstvy mohou dle potřeby určit jinak.
#### Vrstva odpovědí
Viz `Client` a `Server`.
Tázající v hlavičce nastaví pro dané spojení unikátní `['id']: int`
a se stejným `id` mu pak dorazí odpověď.
V hlavičce dotazu i odpovědi se mohou nacházet i jiné klíče vyšších vrstev komunikace.
#### Vrstva volání funkcí
Viz `FuncCaller`.
Hlavička dotazu obsahuje `['func_name']: str` – jméno volané funkce/endpointu a data pak obsahují
parametry dotazu uložené pomocí cbor2 struktury `{'args': args, 'kwargs': kwargs}`.
Odpověď pak obsahuje v datech `{'return': return_value}` serializované pomocí cbor2.
Projekt do tohoto protokolu obaluje volaní metod objektů.
Každý server je implementovaný pomocí jedné třídy.
Její instance mohou být dvou typů – lokální, pak se volání všech funkcí vyhodnocuje ihned v daném
programu, nebo vzdálená pro daný server – pak se vyhodnocení metod dekorovaných pomocí `@server_exec()` vyhodnocuje na příslušném serveu
pomocí výše uvedeného protokolu.
Dále je možné pořídit si druhý nezávislý server a pomocí <prog/data_mover.py>
pak z něj stahovat chybějící data na primárním serveru a odmazávat stará data.
## Klient
# Instalace serveru
Na serveru je nutné mít dostatek diskového prostoru (zhruba 2 GB na každý den archivované historie),
dostatečné množství RAM (alespoň 8 GB) a dostupného výkonu (zejména na počítání poloh na plánované trase).
Samotný download server bez předvýpočtů je méně náročný.
Server předpokládá UNIXový operační systém (testováno na raspbianu a arch linuxu).
Je nutné nainstalovat `pypy3` včetně vývojového balíčků nutného pro kompilaci numpy (pokud je vyčleněný).
Dále se od systému očekává nainstalované `zstd`.
Celý server pak bude žít v adresáři tohoto gitového projektu.
Dále je nutné vytvořit pythonní virtuální prostředí a nainstalovat do něj potřebné balíčky:
```
pypy3 -m venv venv
pip install cbor2 caio numpy
```
... a vytvořit pracovní adresáře:
```
mkdir sockets data tmp data/{gtfs,realtime,realtime_by_route}
```
Pak už stačí jen spustit příslušné servery. V `systemd/init.sh` je script, který se vytvoří a spustí systemd user services na jejich běh.
# Instalace klienta
Pro běh klienta je nutné nainstalovat python, QGis (pyqgis), Qt5 (PyQt), python-cbor2, python-qasync, python-matplotlib a zstd.
Dále je nutné mít přístup na server, ten se konfiguruje v `prog/client-config.py` (vzor v `prog/client-config.py.example`).