Durante la quarantena mi è stato proposto di partecipare allo sviluppo di un dispositivo per misurare la velocità dei modem LTE per diversi operatori di telefonia mobile.
Il cliente voleva valutare la velocità di vari operatori di telefonia in diversi punti geografici, per capire quale operatore fosse più adatto all'installazione di attrezzature che utilizzano una connessione LTE, ad esempio per le dirette streaming. La sfida doveva essere risolta nel modo più semplice ed economico possibile, senza attrezzature costose.
Dico subito che la sfida non è stata delle più semplici e richiede molto lavoro; parlerò delle problematiche che ho incontrato e di come le ho affrontate. Quindi, iniziamo.
Nota
Misurare la velocità di una connessione LTE è un compito piuttosto complesso: è necessario scegliere correttamente l'attrezzatura e la metodologia di misura, oltre a comprendere bene la topologia e il funzionamento della rete cellulare. Inoltre, la velocità può essere influenzata da diversi fattori: il numero di utenti sulla cella, le condizioni meteorologiche, e anche da cella a cella la velocità può variare notevolmente a causa della topologia della rete. In generale, questa è una sfida con un gran numero di variabili sconosciute, e solo un operatore di telefonia è in grado di risolverla correttamente.
Inizialmente, il cliente voleva semplicemente far correre un corriere con i telefoni degli operatori, eseguendo misurazioni direttamente sui telefoni e registrando poi i risultati su un quaderno. La mia soluzione per misurare la velocità delle reti LTE, sebbene non sia perfetta, raggiunge l'obiettivo prefissato.
A causa della mancanza di tempo, ho preso decisioni che non favorivano la comodità o la praticità, ma la rapidità di sviluppo. Ad esempio, per l'accesso remoto ho impostato un SSH inverso, invece di una VPN più pratica, per risparmiare tempo nella configurazione del server e di ogni singolo client.
Capitolato tecnico
Come riportato nell'articolo : Non lavorare senza specifiche! Mai, da nessuna parte!
Le specifiche tecniche erano piuttosto semplici, le espanderò un po' per la comprensione dell'utente finale. La scelta delle soluzioni tecniche e dell'attrezzatura è stata dettata dal cliente. Dunque, le specifiche finali, dopo tutte le approvazioni:
Su base di un computer a scheda singola vim2 creare un tester di velocità per la connessione LTE tramite modem Huawei e3372h — 153 diversi operatori di telecomunicazioni (da uno a n). È anche necessario ottenere le coordinate da un ricevitore GPS collegato tramite UART. Non utilizzare il servizio per misurare la velocità e raccoglierli in una tabella del tipo:
Tabella in formato csv. Dopodiché inviare questa tabella tramite email ogni 6 ore. In caso di errori, lampeggiare un LED collegato al GPIO.
Ho descritto il progetto in modo libero, dopo molte approvazioni. Ma il senso del compito è già chiaro. Il termine per tutto era di una settimana. Ma in realtà si è prolungato per tre settimane. Questo considerando che l'ho fatto solo dopo il lavoro principale e nei fine settimana.
Voglio sottolineare ancora una volta che è stato concordato in anticipo con il committente l'uso del servizio di misurazione della velocità e dell'hardware, il che ha limitato notevolmente le mie possibilità. Anche il budget era limitato, quindi non sono stati acquistati particolari elementi. Quindi, ho dovuto seguire queste regole.
Architettura e sviluppo
Lo schema è semplice e ovvio. Pertanto, lo lascerò senza particolari commenti.

Ho deciso di realizzare l'intero progetto in python, nonostante non avessi alcuna esperienza nello sviluppo in questo linguaggio. L'ho scelto perché c'erano molti esempi e soluzioni pronte che potevano accelerare lo sviluppo. Quindi, chiedo a tutti i programmatori professionisti di non criticare la mia prima esperienza di sviluppo in python e sono sempre felice di ricevere critiche costruttive per migliorare le mie abilità.
Ho anche scoperto che python ha due versioni principali, 2 e 3, e alla fine ho scelto la terza.
Unità hardware
Scheda a una scheda vim2
Come macchina principale mi è stata fornita una scheda a una scheda

Ottimo, potente media combiner per smart home e SMART-TV, ma raramente adatto per questo compito, o diciamo pure, scarsamente appropriato. Ad esempio, il suo sistema operativo principale è Android, mentre Linux è un sistema operativo secondario, e di conseguenza nessuno garantisce un buon funzionamento di tutti i nodi e driver sotto Linux. E presumo che parte dei problemi fosse legata ai driver USB di questa piattaforma, quindi i modem non funzionavano su questa scheda come previsto. Ha anche una documentazione molto scadente e disgiunta, quindi ogni operazione richiedeva molto tempo per cercare nei documenti. Anche un lavoro banale con GPIO ha richiesto molto impegno. Ad esempio, per impostare il funzionamento con un LED, ho impiegato diverse ore. Tuttavia, per essere obiettivo, non era fondamentale quale single-board computer fosse, l'importante era che funzionasse e avesse porte USB.
Per iniziare, ho bisogno di installare Linux su questa scheda. Per non dover cercare a caso nella documentazione, e anche per chi si cimenterà con questo single-board, scrivo questo capitolo.
Ci sono due opzioni per installare Linux: su una scheda SD esterna o su MMC interna. Ho passato un'intera serata con la scheda e non sono riuscito a farla funzionare, quindi ho deciso di installare su MMC, anche se senza dubbio con la scheda esterna sarebbe stato molto più semplice lavorare.
Su come eseguire il flashing . Traduco da un linguaggio strano al russo. Per eseguire il flashing della scheda, ho bisogno di connettere un UART hardware. L'ho connesso
- Tool Pin GND: Pin17 del GPIO di VIM
- Tool Pin TXD: Pin18 del GPIO di VIM (Linux_Rx)
- Tool Pin RXD: Pin19 del GPIO di VIM (Linux_Tx)
- Tool Pin VCC: Pin20 del GPIO di VIM

Dopo di che, ho scaricato il firmware . La versione specifica del firmware .
Per caricare questo firmware, ho bisogno di alcune utility. Maggiori dettagli sono forniti . Non ho provato a eseguire il flashing sotto Windows, ma devo dire qualche parola sul flashing sotto Linux. Prima di tutto, installerò le utility seguendo le istruzioni.
git clone https://github.com/khadas/utils
cd /path/to/utils
sudo ./INSTALLE... niente funziona. Ho speso un paio d'ore a modificare gli script di installazione affinché si installassero correttamente. Non ricordo esattamente cosa ho fatto, ma è stato un vero circo. Quindi siate cauti. Ma senza queste utility non ha senso continuare a lavorare con vim2. Meglio non averci a che fare!
Dopo sette cerchi dell'inferno, configurazioni degli script e installazioni ho ottenuto un pacchetto di utility funzionanti. Ho collegato la scheda tramite USB al mio computer Linux, e ho anche collegato l'UART secondo lo schema sopra.
Sto configurando il mio terminale preferito minicom a una velocità di 115200, senza controllo degli errori hardware o software. E iniziamo.

All'avvio di VIM2 nel terminale UART premo un qualsiasi tasto, ad esempio la barra spaziatrice, per fermare il caricamento. Dopo che appare la riga
kvim2# Inserisco il comando:
kvim2# run updateSull'host da cui stiamo caricando, eseguo:
burn-tool -v aml -b VIM2 -i VIM2_Ubuntu-server-bionic_Linux-4.9_arm64_EMMC_V20191231.imgTutto, uff. Ho flashato, sulla scheda c'è Linux. Login/password khadas:khadas.
Dopo ciò, alcune piccole impostazioni iniziali. Per un utilizzo successivo disabilito la password per sudo (sì, non è sicuro, ma è comodo).
sudo visudoModifico la riga fino a questo formato e salviamo
# Allow members of group sudo to execute any command
%sudo ALL=(ALL:ALL) NOPASSWD: ALLDopodiché cambio la località attuale, affinché l'orario sia quello di Mosca, altrimenti sarà quello di Greenwich.
sudo timedatectl set-timezone Europe/Moscowoppure
ln -s /usr/share/zoneinfo/Europe/Moscow /etc/localtimeSe ti sembra complicato, allora non utilizzare questa scheda, meglio un Raspberry Pi. Onestamente.
Modem Huawei e3372h — 153
Questo modem mi ha dato un bel po' di problemi, e in effetti è diventato il collo di bottiglia dell'intero progetto. In realtà, il termine 'modem' per questi dispositivi non riflette affatto la loro funzionalità: si tratta di un potente combinatore, questo dispositivo ha un'unità composta, che si finge CD-ROM per installare i driver, e poi passa in modalità scheda di rete.
Architettonicamente, dal punto di vista dell'utente Linux dopo tutte le impostazioni, appare così: dopo aver collegato il modem, mi appare l'interfaccia di rete eth*, che tramite DHCP riceve l'indirizzo IP 192.168.8.100, e il gateway predefinito 192.168.8.1.
E il punto principale! Questo modello di modem non sa funzionare in modalità modem, controllato dai comandi AT.. Sarebbe stato molto più semplice creare connessioni PPP per ogni modem e poi operare con esse. Ma nel mio caso il 'può' (piuttosto i driver Linux secondo le regole udev) crea un'interfaccia eth e per DHCP le assegna un indirizzo IP.
Per non confonderci ulteriormente, propongo di dimenticare la parola 'modem' e parlare di scheda di rete e gateway, perché in effetti, è come collegare una nuova scheda di rete con un gateway.
Quando c'è un solo modem, non ci sono problemi particolari, ma quando ce ne sono più di uno, vale a dire n pezzi, si presenta il seguente quadro di rete.

Cioè n schede di rete, con un solo indirizzo IP, con lo stesso gateway predefinito per ognuna. Ma di fatto, ognuna di esse è collegata al proprio operatore.
Inizialmente avevo una soluzione semplice: utilizzare il comando ifconfig o ip per disattivare tutte le interfacce e accenderle una alla volta per testarle. La soluzione andava bene, ma durante i passaggi non avevo la possibilità di connettermi al dispositivo. E poiché i passaggi erano frequenti e rapidi, in effetti non riuscivo a connettermi affatto.
Pertanto ho scelto di cambiare gli indirizzi IP dei modem "manualmente" e poi instradare il traffico tramite le impostazioni di routing.

Così i miei problemi con i modem non sono finiti: in caso di problemi di alimentazione, si scollegavano, era necessaria una buona alimentazione stabile per l'hub USB. Ho risolto questo problema saldando l'alimentazione direttamente all'hub. Un altro problema che ho affrontato e che ha rovinato l'intero progetto è stato che dopo un riavvio o un avvio a freddo non venivano riconosciuti tutti i modem, e non sempre, e non sono riuscito a capire perché e secondo quale algoritmo ciò accadesse. Ma andiamo con ordine.
Per un corretto funzionamento del modem, ho installato il pacchetto usb-modeswitch.
sudo apt update
sudo apt install -y usb-modeswitch Dopo di che, il modem verrà correttamente riconosciuto e configurato dal sottosistema udev. Controllo semplicemente collegando il modem e assicurandomi che la rete sia apparsa.
Un altro problema che non sono riuscito a risolvere: come ottenere il nome dell'operatore dal modem con cui lavoriamo? Il nome dell'operatore è presente nell'interfaccia web del modem all'indirizzo 192.168.8.1. Si tratta di una pagina web dinamica che ottiene i dati tramite richieste ajax, quindi non è possibile semplicemente usare wget per scaricare la pagina e analizzare il nome. Così ho iniziato a esplorare come gestire la pagina web ecc., e ho capito che stavo facendo qualcosa di assurdo. Alla fine ho desistito e ho iniziato a ottenere il nome dell'operatore tramite l'API di Speedtest.
Molte cose sarebbero state più semplici se il modem avesse avuto accesso tramite comandi AT. Si sarebbe potuto riconfigurare, creare una connessione ppp, assegnare un IP, ottenere il nome dell'operatore, ecc. Ma purtroppo, lavoro con quello che mi è stato dato.
GPS
Il ricevitore GPS che mi è stato fornito aveva un'interfaccia UART e alimentazione. Non era la soluzione migliore, ma comunque funzionante e semplice. Il ricevitore aveva un aspetto simile a questo.

A dire il vero, è la prima volta che lavoro con un ricevitore GPS, ma come immaginavo, hanno già pensato a tutto per noi. Quindi, utilizziamo semplicemente soluzioni pronte.
Per cominciare, attivo uart_AO_B (UART_RX_AO_B, UART_TX_AO_B) per collegare il GPS.
khadas@Khadas:~$ sudo fdtput -t s /dtb.img /serial@c81004e0 status okayDopo verifico il successo dell'operazione.
khadas@Khadas:~$ fdtget /dtb.img /serial@c81004e0 status
okayQuesto comando, a quanto pare, modifica il devtree al volo, il che è molto comodo.
Dopo il successo di questa operazione, riavviamo e installiamo il demone GPS.
khadas@Khadas:~$ sudo rebootInstallazione del demone GPS. Installo tutto e lo spengo immediatamente per una configurazione successiva.
sudo apt install gpsd gpsd-clients -y
sudo killall gpsd
/* Fermare/disabilitare il demone GPS */
sudo systemctl stop gpsd.socket
sudo systemctl disable gpsd.socketModifico il file di configurazione.
sudo vim /etc/default/gpsdImposto la UART su cui sarà collegato il GPS.
DEVICES="/dev/ttyS4"E poi accendiamo tutto e avviamo.
/* GPS daemon enable/start */
sudo systemctl enable gpsd.socket
sudo systemctl start gpsd.socketSuccessivamente, collego il GPS.

Nelle mani ho il cavo GPS, sotto le dita si vedono i fili UART del debugger.
Riavvio e controllo il funzionamento del GPS utilizzando il programma gpsmon.

In questo screenshot non ci sono satelliti visibili, ma è evidente la comunicazione con il ricevitore GPS, e questo indica che tutto va bene.
Ho testato molte opzioni per lavorare con questo demone in Python, ma mi sono fermato su quella che funzionava correttamente con Python 3.
Installo la libreria necessaria.
sudo -H pip3 install gps3 E scrivo il codice di funzionamento.
from gps3.agps3threaded import AGPS3mechanism
...
def getPositionData(agps_thread):
counter = 0;
while True:
longitude = agps_thread.data_stream.lon
latitude = agps_thread.data_stream.lat
if latitude != 'n/a' and longitude != 'n/a':
return '{}' .format(longitude), '{}' .format(latitude)
counter = counter + 1
print ("Aspetta gps contatore = %d" % counter)
if counter == 10:
ErrorMessage("Errore del ricevitore GPS!!!")
return "NA", "NA"
time.sleep(1.0)
...
f __name__ == '__main__':
... #gps
agps_thread = AGPS3mechanism() # Instanzia i meccanismi AGPS3
agps_thread.stream_data() # Da localhost (), o altri host, ad esempio, (host='gps.ddns.net')
agps_thread.run_thread() # Tempo di throttling per dormire dopo un lookup vuoto, di default '()' 0.2 due decimi di secondo
Se ho bisogno di ottenere le coordinate, lo faccio con la seguente chiamata:
longitude, latitude = getPositionData(agps_thread)
E in 1-10 secondi riceverò o le coordinate o non le riceverò. Sì, ho fatto dieci tentativi per ottenere le coordinate. Non è ottimale, è un po' impreciso, ma funziona. Ho deciso di procedere in questo modo perché il GPS può ricevere male e non ottenere sempre i dati. Se aspettassi di ricevere i dati, in caso di lavoro in un ambiente chiuso, il programma si bloccherebbe in quel punto. Per questo ho implementato una soluzione non elegante.
In linea di principio, se avessi avuto più tempo, avrei potuto ricevere i dati dal GPS direttamente tramite UART, analizzarli in un thread separato e lavorarci. Ma non avevo tempo del tutto, ecco perché il codice è brutto e complicato. E sì, non mi vergogno.
LED
Collegare il LED è stato semplice ma complicato allo stesso tempo. La difficoltà principale è che il numero del pin nel sistema non corrisponde al numero del pin sulla scheda e perché la documentazione è scritta in modo incomprensibile. Per abbinare il numero del pin hardware e il numero del pin nel sistema operativo, è necessario eseguire il comando:
gpio readallVerrà visualizzata una tabella di corrispondenza tra il pin nel sistema e sulla scheda. Dopo di che posso già lavorare con il pin nel sistema operativo. Nel mio caso, il LED è collegato a GPIOH_5.

Imposto il pin GPIO in modalità uscita.
gpio -g mode 421 outScrivo zero.
gpio -g write 421 0Scrivo uno.
gpio -g write 421 1 
Tutto si accende dopo aver scritto '1'.
#gpio subsistem
def gpio_init():
os.system("gpio -g mode 421 out")
os.system("gpio -g write 421 1")
def gpio_set(val):
os.system("gpio -g write 421 %d" % val)
def error_blink():
gpio_set(0)
time.sleep(0.1)
gpio_set(1)
time.sleep(0.1)
gpio_set(0)
time.sleep(0.1)
gpio_set(1)
time.sleep(0.1)
gpio_set(0)
time.sleep(1.0)
gpio_set(1)
def good_blink():
gpio_set(1)
Ora, in caso di errori, chiamo error_blink() e il LED lampeggia in modo carino.
Moduli software
API Speedtest
È una grande gioia che il servizio speedtest.net abbia una sua propria API python, che può essere consultata su .
Il bello è che ci sono i codici sorgente che possono essere consultati. Come lavorare con questa API (esempi semplici) può essere visto nella .
Installo la libreria python con il seguente comando.
sudo -H pip3 install speedtest-cliPer esempio, puoi anche installare Speedtest in Ubuntu direttamente dai repository. È la stessa applicazione python, che poi può essere eseguita direttamente dalla console.
sudo apt install speedtest-cli -yE misurare la velocità della tua connessione.
speedtest-cli
Recupero della configurazione di speedtest.net...
Test in corso da B***** (*.*.*.*)...
Recupero dell'elenco dei server speedtest.net...
Selezione del miglior server in base al ping...
Ospitato da MTS (Mosca) [0.12 km]: 11.8 ms
Test della velocità di download................................................................................
Download: 7.10 Mbit/s
Test della velocità di upload......................................................................................................
Upload: 3.86 Mbit/s
Di conseguenza, come l'ho fatto io. Ho dovuto entrare nei codici sorgente di questo Speedtest per integrarli meglio nel mio progetto. Una delle principali sfide è raccogliere anche il nome dell'operatore telefonico, per inserirlo nella tabella.
import speedtest
from datetime import datetime
...
# Indica un server specifico per il test
#6053) MaximaTelecom (Mosca, Federazione Russa)
servers = ["6053"]
# Se vuoi utilizzare un test a thread singolo
threads = None
s = speedtest.Speedtest()
# otteniamo il nome dell'operatore di telefonia mobile
opos = '%(isp)s' % s.config['client']
s.get_servers(servers)
# otteniamo una stringa testuale con i parametri del server
testserver = '%(sponsor)s (%(name)s) [%(d)0.2f km]: %(latency)s ms' % s.results.server
# test di download
s.download(threads=threads)
# test di upload
s.upload(threads=threads)
# otteniamo i risultati
s.results.share()
# Dopodiché si forma una stringa per la registrazione in un file csv.
# otteniamo la posizione GPS
longitude, latitude = getPositionData(agps_thread)
# ora e data
curdata = datetime.now().strftime('%d.%m.%Y')
curtime = datetime.now().strftime('%H:%M:%S')
delimiter = ';'
result_string = opos + delimiter + str(curpos) + delimiter +
curdata + delimiter + curtime + delimiter + longitude + ', ' + latitude + delimiter +
str(s.results.download/1000.0/1000.0) + delimiter + str(s.results.upload / 1000.0 / 1000.0) +
delimiter + str(s.results.ping) + delimiter + testserver + "n"
# qui si procede con la scrittura nel file di log
Qui si è rivelato tutto meno semplice del previsto, anche se, sembrerebbe, potrebbe essere più semplice. Inizialmente il parametro servers era impostato su [], ovvero scegli il server migliore. Di conseguenza, avevo server casuali e, come è facile immaginare, una velocità fluttuante. È un tema piuttosto complesso, usare un server fisso, se sì, allora statico o dinamico, richiede una certa ricerca. Ma ecco un esempio di grafici delle misurazioni della velocità dell'operatore Beeline con scelta dinamica del server di test e uno staticamente fissato.

Risultato della misurazione della velocità con selezione di un server dinamico.

Risultato del test di velocità, con un server scelto rigorosamente.
La "povere" durante i test è presente sia lì che là, e deve essere rimossa con metodi matematici. Ma con un server fisso ce n'è un po' meno e l'ampiezza è più stabile.
In realtà, questo è un campo di grandi ricerche. E io avrei condotto misurazioni della velocità verso il mio server utilizzando l'utility iperf. Ma noi seguiamo le specifiche.
Invio di posta e errori
Per l'invio di posta ho provato diverse decine di opzioni, ma alla fine mi sono fermato su quella seguente. Ho registrato un'email su yandex e poi ho preso . L'ho testato e implementato nel programma. In questo esempio si analizzano diverse opzioni, inclusa l'invio da gmail, ecc. Non avevo voglia di mettermi a gestire il mio server di posta e non avevo tempo per questo, ma si è rivelato essere uno spreco di tempo.
Ho inviato i log tramite il pianificatore, in presenza di connessione, ogni 6 ore: alle 00, 06, 12 e 18. Inviavo nel seguente modo.
from send_email import *
...
message_log = "Log di test della scheda n. 1"
EmailForSend = ["dlinyj@trololo.ru", "pupkin@trololo.ru"]
files = ["/home/khadas/modems_speedtest/csv"]
...
def sendLogs():
global EmailForSend
curdata = datetime.now().strftime('%d.%m.%Y')
сurtime = datetime.now().strftime('%H:%M:%S')
try:
for addr_to in EmailForSend:
send_email(addr_to, message_log, "Log per " + curdata + " " + сurtime, files)
except:
print("Problema di rete durante l'invio dell'email")
return False
return True
Gli errori venivano inizialmente inviati. Inizialmente si accumulavano in una lista, e poi li inviavo anche tramite il pianificatore, in presenza di connessione. Tuttavia, in seguito si sono presentati problemi con il fatto che yandex ha una limitazione sul numero di messaggi inviati al giorno (è un dolore, un dispiacere e una vergogna). Poiché gli errori potevano essere numerosi anche in un solo minuto, ho dovuto rinunciare all'invio degli errori via email. Quindi tieni presente che, durante l'invio automatico tramite i servizi di yandex, esiste questo problema.
Server di feedback
Per avere accesso all'hardware remoto e poterlo riconfigurare e ristrutturare, avevo bisogno di un server esterno. In effetti, per essere giusti, sarebbe stato corretto inviare tutti i dati a un server e costruire tutte le belle grafiche nell'interfaccia web. Ma non si può avere tutto subito.
Come VPS ho scelto . Si sarebbe potuto prendere un server molto semplice. In generale, per i miei scopi sarebbe stato più che sufficiente. Ma poiché pagavo il server non di tasca mia, ho deciso di prenderne uno con un piccolo margine, per essere coperti nel caso avessimo sviluppato un'interfaccia web, un proprio server SMTP, VPN, ecc. Inoltre, avere la possibilità di configurare un bot Telegram e non avere problemi con le sue restrizioni. Perciò ho scelto Amsterdam e i seguenti parametri.

Come metodo di connessione con l'hardware vim2, ho scelto una connessione SSH inversa e, come ha dimostrato la pratica, non è stata la migliore. In caso di interruzione della connessione, il server mantiene la porta e non è possibile connettersi per un certo periodo. Quindi, è comunque meglio utilizzare altri metodi di connessione, come la VPN. In futuro, volevo passare alla VPN, ma non ci sono riuscito.
Non entrerò nei dettagli della configurazione del firewall, delle limitazioni dei diritti, della disattivazione della connessione ssh per root e altre banalità nella configurazione di un VPS. Spero che sappiate già tutto. Per la connessione remota, creo un nuovo utente sul server.
adduser vimsshGenerando le chiavi di connessione ssh sulla nostra macchina.
ssh-keygenE le copio sul nostro server.
ssh-copy-id vimssh@host.comSulla nostra macchina creo una connessione automatica back ssh all'avvio.
[Unit]
Descrizione=Auto Reverse SSH
Richiede=systemd-networkd-wait-online.service
Dopo=systemd-networkd-wait-online.service
[Service]
Utente=khadas
ExecStart=/usr/bin/ssh -NT -o ExitOnForwardFailure=yes -o ServerAliveInterval=60 -CD 8080 -R 8083:localhost:22 vimssh@host.com
RestartSec=5
Restart=always
[Install]
WantedBy=multi-user.target
Fate attenzione alla porta 8083: è quella che determina la porta per la connessione tramite back ssh. Aggiungiamo all'avvio automatico e partiamo.
sudo systemctl enable autossh.service
sudo systemctl start autossh.servicePossiamo anche controllare lo stato:
sudo systemctl status autossh.serviceOra, sul nostro server VPS, se eseguiamo:
ssh -p 8083 khadas@localhostVengo indirizzato alla mia macchina di test. E da lì posso anche inviare registri e qualsiasi dato via ssh al mio server, il che è molto comodo.
Mettiamo tutto insieme

Inizio, procediamo con lo sviluppo e il debug
Uff, bene, sembra che abbia descritto tutti i nodi. Ora è il momento di raccogliere tutto in un'unica soluzione. Puoi visualizzare il codice .
Un aspetto importante del codice: questo progetto potrebbe non avviarsi così «com'è», poiché è stato progettato per uno specifico compito, in una specifica architettura. Anche se fornisco i sorgenti, comunque il più prezioso lo discuterò qui, nel testo, altrimenti risulterebbe completamente incomprensibile.
All'inizio vanno l'inizializzazione gps, gpio e l'avvio di un thread separato per il pianificatore.
#запуск потока планировщика
pShedulerThread = threading.Thread(target=ShedulerThread, args=(1,))
pShedulerThread.start()Il pianificatore è piuttosto semplice: controlla se è il momento di inviare i messaggi e qual è lo stato attuale degli errori. Se c'è un flag di errore, lampeggiamo il LED.
#sheduler
def ShedulerThread(name):
global ready_to_send
while True:
d = datetime.today()
time_x = d.strftime('%H:%M')
if time_x in time_send_csv:
ready_to_send = True
if error_status:
error_blink()
else:
good_blink()
time.sleep(1)Il momento più complesso di questo progetto è mantenere la connessione ssh inversa attiva ad ogni test. In ogni test, si configura nuovamente il gateway predefinito e il server dns. Poiché tanto nessuno legge, sappiate che il treno non corre su rotaie di legno. Chi troverà l'uovo di Pasqua, avrà una caramella.
Per questo creo una tabella di routing separata —set-mark 0x2 e una regola per il reindirizzamento del traffico.
def InitRouteForSSH():
cmd_run("sudo iptables -t mangle -A OUTPUT -p tcp -m tcp --dport 22 -j MARK --set-mark 0x2")
cmd_run("sudo ip rule add fwmark 0x2/0x2 lookup 102")Per ulteriori dettagli su come funziona, puoi .
Dopo di che, passo a un ciclo infinito, dove ogni volta otteniamo un elenco dei modem connessi (per sapere se la configurazione della rete è cambiata).
network_list = getNetworklist()Ottenere l'elenco delle interfacce di rete è abbastanza semplice.
def getNetworklist():
full_networklist = os.listdir('/sys/class/net/')
network_list = [x for x in full_networklist if "eth" in x and x != "eth0"]
return network_listDopo aver ottenuto l'elenco, assegno gli indirizzi IP a tutte le interfacce, come mostrato nell'immagine del capitolo sul modem.
SetIpAllNetwork(network_list)
def SetIpAllNetwork(network_list):
for iface in network_list:
lastip = "%d" % (3 + network_list.index(iface))
cmd_run ("sudo ifconfig " + iface + " 192.168.8." + lastip + " up")Poi semplicemente, in un ciclo, attraversiamo ogni interfaccia. E configuriamo ogni interfaccia.
for iface in network_list:
ConfigNetwork(iface)def ConfigNetwork(iface):
#ripristiniamo tutte le impostazioni
cmd_run("sudo ip route flush all")
#Assegniamo il gateway predefinito
cmd_run("sudo route add default gw 192.168.8.1 " + iface)
#impostiamo il server DNS (questo è necessario per il funzionamento di speedtest)
cmd_run ("sudo bash -c 'echo nameserver 8.8.8.8 > /etc/resolv.conf'")Controllo l'interfaccia per verificarne il funzionamento; se non c'è rete, genero errori. Se la rete è presente, è tempo di agire!
Qui configuro il routing ssh per questa interfaccia (se non è stato fatto), invio errori al server, se il momento è arrivato, invio i log e alla fine eseguo speedtest e salvo i log in un file csv.
if not NetworkAvalible():
....
#Qui generiamo errori
....
else: #C'è rete, evviva, lavoriamo!
#Se abbiamo un'interfaccia problematica su cui è presente ssh, la cambiamo
if (sshint == lastbanint or sshint == "free"):
print("********** Setup SSH ********************")
if sshint != "free":
cmd_run("sudo ip route del default via 192.168.8.1 dev " + sshint + " table 102")
SetupReverseSSH(iface)
sshint = iface
#Poiché la rete funziona, spediamo tutto il prima possibile!!!
if ready_to_send:
print ("**** Pronto per inviare!!!")
if sendLogs():
ready_to_send = False
if error_status:
SendErrors()
#e poi testiamo la velocità e salviamo i log. Si può dire qualcosa sulla funzione di configurazione del reverse ssh.
def SetupReverseSSH(iface):
cmd_run("sudo systemctl stop autossh.service")
cmd_run("sudo ip route add default via 192.168.8.1 dev " + iface + " table 102")
cmd_run("sudo systemctl start autossh.service")E ovviamente, è necessario aggiungere tutta questa bellezza all'avvio automatico. Per questo creo un file:
sudo vim /etc/systemd/system/modems_speedtest.serviceE ci scrivo dentro:
[Unit]
Descrizione=Test di Velocità del Modem
Richiede=systemd-networkd-wait-online.service
Dopo=systemd-networkd-wait-online.service
[Service]
Utente=khadas
ExecStart=\/usr\/bin\/python3.6 \/home\/khadas\/modems_speedtest\/networks.py
RestartSec=5
Restart=always
[Install]
WantedBy=multi-user.target
Attivo l'avvio automatico e lo avvio!
sudo systemctl enable modems_speedtest.service
sudo systemctl start modems_speedtest.serviceOra posso controllare i log di quello che succede con il comando:
journalctl -u modems_speedtest.service --no-pager -fRisultati
Beh, ora la cosa più importante: cosa è risultato alla fine? Presento alcuni grafici che sono riuscito a catturare durante il processo di sviluppo e debug. I grafici sono stati realizzati con gnuplot usando il seguente script.
#! /usr/bin/gnuplot -persist
set terminal postscript eps enhanced color solid
set output "Rostelecom.ps"
#set terminal png size 1024, 768
#set output "Rostelecom.png"
set datafile separator ';'
set grid xtics ytics
set xdata time
set ylabel "Speed Mb/s"
set xlabel 'Time'
set timefmt '%d.%m.%Y;%H:%M:%S'
set title "Rostelecom Speed"
plot "Rostelecom.csv" using 3:6 with lines title "Download", '' using 3:7 with lines title "Upload"
set title "Rostelecom 2 Ping"
set ylabel "Ping ms"
plot "Rostelecom.csv" using 3:8 with lines title "Ping"
La prima esperienza è stata con l'operatore Tele2, che ho condotto per diversi giorni.

Qui ho utilizzato un server di misurazione dinamico. Le misurazioni della velocità funzionano, ma variano molto, tuttavia è visibile una certa media, che può essere ottenuta filtrando i dati, ad esempio usando la media mobile.
In seguito ho creato altri grafici per altri operatori di telecomunicazioni. In questo caso, il server di test era unico e anche i risultati sono molto interessanti.




Come si può vedere, l'argomento è molto ampio per ricerche e per l'elaborazione di questi dati, e chiaramente non possiamo pensare che bastino un paio di settimane di lavoro. Ma...
Risultato del lavoro
Il lavoro è stato bruscamente interrotto per motivi indipendenti dalla mia volontà. Una delle debolezze di questo progetto, dal mio punto di vista, era il modem, che non voleva funzionare bene insieme ad altri modem, e ad ogni avvio mostrava questi problemi. Per queste esigenze ci sono molte altre modelli di modem, che di solito hanno un formato Mini PCI-e e vengono installati all'interno del dispositivo, ed è molto più semplice configurarli. Ma questa è un'altra storia. Il progetto era interessante e sono stato molto felice di aver avuto l'opportunità di partecipare.
Fonte: habr.com

