Jednoczesny speedtest na kilku modemach LTE

Podczas kwarantanny zaproponowano mi udział w opracowaniu urządzenia do pomiaru prędkości modemów LTE dla kilku operatorów komórkowych.

Jednoczesny speedtest na kilku modemach LTE

Klient chciał ocenić prędkość różnych operatorów telekomunikacyjnych w różnych lokalizacjach geograficznych, aby móc zrozumieć, który operator komórkowy będzie dla niego najbardziej optymalny przy instalacji sprzętu wykorzystującego połączenie LTE, na przykład do transmisji wideo. Jednocześnie problem należało rozwiązać możliwie najprościej i najtaniej, bez drogiego sprzętu.

Od razu powiem, że zadanie nie należy do najprostszych i wymaga dużej wiedzy, opowiem z jakimi problemami się spotkałem i jak je rozwiązałem. Więc chodźmy.

Operacja

Pomiar prędkości łącza LTE to bardzo złożona sprawa: trzeba dobrać odpowiedni sprzęt i technikę pomiaru, a także dobrze poznać topologię i działanie sieci komórkowej. Ponadto na prędkość może wpływać kilka czynników: liczba abonentów w komórce, warunki pogodowe, nawet w przypadku poszczególnych komórek prędkość może się znacznie różnić ze względu na topologię sieci. Ogólnie rzecz biorąc, jest to problem z ogromną liczbą niewiadomych i tylko operator telekomunikacyjny może go poprawnie rozwiązać.

Początkowo klient chciał po prostu poprowadzić kuriera telefonami operatorów, dokonać pomiarów bezpośrednio na telefonie, a następnie zapisać wyniki pomiaru prędkości w notatniku. Moje rozwiązanie pomiaru prędkości sieci LTE, choć nie idealne, rozwiązuje problem.

Z braku czasu podjąłem decyzje nie na korzyść wygody czy praktyczności, ale na korzyść szybkości rozwoju. Na przykład do zdalnego dostępu zamiast bardziej praktycznego VPN zastosowano odwrotny ssh, aby zaoszczędzić czas na konfigurowaniu serwera i każdego indywidualnego klienta.

Zadanie techniczne

Jak stwierdzono w artykule Bez specyfikacji technicznych: dlaczego klient tego nie chce: Nie pracuj bez specyfikacji technicznych! Nigdy i nigdzie!

Zadanie techniczne było dość proste, rozwinę je trochę, aby było zrozumiałe dla użytkownika końcowego. Wybór rozwiązań technicznych i wyposażenia został podyktowany przez Klienta. A więc sama specyfikacja techniczna, po wszelkich atestach:

Oparty na komputerze jednopłytkowym vim2 wykonaj tester prędkości dla połączeń LTE za pośrednictwem modemów Huawei e3372h-153 kilku operatorów telekomunikacyjnych (od jednego do n). Konieczne jest także odebranie współrzędnych z odbiornika GPS podłączonego poprzez UART. Dokonuj pomiarów prędkości korzystając z usługi www.speedtest.net i umieść je w tabeli takiej jak:

Jednoczesny speedtest na kilku modemach LTE

Tabela w formacie csv. Następnie wysyłaj ten znak e-mailem co 6 godzin. W przypadku błędów mrugnij diodą podłączoną do GPIO.

Specyfikację techniczną opisałem w dowolnej formie, po wielu atestach. Ale sens zadania jest już widoczny. Na wszystko dano tydzień. Ale w rzeczywistości trwało to trzy tygodnie. Biorąc pod uwagę fakt, że robiłem to tylko po mojej głównej pracy i w weekendy.

W tym miejscu chcę jeszcze raz zwrócić uwagę na fakt, że Klient z góry wyraził zgodę na korzystanie z usługi pomiaru prędkości oraz sprzętu, co znacznie ograniczyło moje możliwości. Budżet również był ograniczony, więc nie zakupiono nic specjalnego. Musieliśmy więc grać według tych zasad.

Architektura i rozwój

Schemat jest prosty i oczywisty. Dlatego pozostawię to bez specjalnych komentarzy.

Jednoczesny speedtest na kilku modemach LTE

Zdecydowałem się na wdrożenie całego projektu w Pythonie, mimo że nie miałem żadnego doświadczenia w programowaniu w tym języku. Wybrałem go, ponieważ było mnóstwo gotowych przykładów i rozwiązań, które mogłyby przyspieszyć rozwój. Dlatego proszę wszystkich profesjonalnych programistów, aby nie karcili mojego pierwszego doświadczenia z programowaniem w Pythonie i zawsze jestem szczęśliwy, gdy słyszę konstruktywną krytykę w celu udoskonalenia moich umiejętności.

W trakcie tego procesu odkryłem, że Python ma dwie działające wersje 2 i 3, w wyniku czego zdecydowałem się na trzecią.

Węzły sprzętowe

Jednopłytowy vim2

Jako maszynę główną dostałem komputer jednopłytkowy vim2

Jednoczesny speedtest na kilku modemach LTE

Doskonały, wydajny procesor multimedialny dla inteligentnego domu i SMART-TV, ale do tego zadania skrajnie się nie nadaje, albo, powiedzmy, słabo się nadaje. Na przykład jego głównym systemem operacyjnym jest Android, a Linux jest systemem dodatkowym, w związku z czym nikt nie gwarantuje wysokiej jakości działania wszystkich węzłów i sterowników pod Linuksem. I zakładam, że część problemów była związana ze sterownikami USB tej platformy, więc modemy na tej płycie nie działały zgodnie z oczekiwaniami. Ma też bardzo ubogą i rozproszoną dokumentację, więc każda operacja zajmowała dużo czasu, przekopując się przez doki. Nawet zwykła praca z GPIO wymagała sporo krwi. Na przykład ustawienie diody LED zajęło mi kilka godzin. Ale żeby być obiektywnym, zasadniczo nie było ważne, jaki to był rodzaj single-board, najważniejsze, że działał i były porty USB.

Najpierw muszę zainstalować Linuksa na tej płycie. Aby nie przedzierać się przez gąszcz dokumentacji dla każdego, a także dla tych, którzy będą mieli do czynienia z tym jednopłytkowym układem, piszę ten rozdział.

Istnieją dwie możliwości instalacji Linuksa: na zewnętrznej karcie SD lub na wewnętrznej pamięci MMC. Wieczór spędziłem próbując wymyślić, jak to zrobić, aby działało z kartą, więc zdecydowałem się zainstalować ją na MMC, chociaż bez wątpienia znacznie łatwiej byłoby pracować z kartą zewnętrzną.

Informacje o oprogramowaniu krzywo powiedziane tutaj. Tłumaczę z obcego na rosyjski. Aby sflashować płytkę muszę podłączyć sprzętowy UART. Połączyłem to w następujący sposób.

  • Pin narzędzia GND: <—> Pin 17 GPIO modułu VIM
  • Pin narzędzia TXD: <—> Pin 18 GPIO modułu VIM (Linux_Rx)
  • Pin narzędzia RXD: <—> Pin 19 GPIO modułu VIM (Linux_Tx)
  • Pin narzędzia VCC: <—> Pin 20 GPIO modułu VIM

Jednoczesny speedtest na kilku modemach LTE

Następnie pobrałem oprogramowanie sprzętowe stąd. Określona wersja oprogramowania sprzętowego VIM1_Ubuntu-server-bionic_Linux-4.9_arm64_EMMC_V20191231.

Aby wgrać to oprogramowanie, potrzebuję narzędzi. Więcej szczegółów na ten temat tutaj. Nie próbowałem flashować go pod Windowsem, ale muszę powiedzieć kilka słów o oprogramowaniu pod Linuksem. Najpierw zainstaluję narzędzia zgodnie z instrukcjami.

git clone https://github.com/khadas/utils
cd /path/to/utils
sudo ./INSTALL

Aaa... Nic nie działa. Spędziłem kilka godzin edytując skrypty instalacyjne, aby wszystko zainstalowało się poprawnie. Nie pamiętam co tam robiłem, ale był też ten cyrk z końmi. Więc uważaj. Ale bez tych narzędzi nie ma sensu dalej torturować vima2. Lepiej w ogóle z nim nie zadzierać!

Po siedmiu kręgach piekła, konfiguracji skryptu i instalacji otrzymałem paczkę działających narzędzi. Podłączyłem płytkę przez USB do mojego komputera z systemem Linux, a także podłączyłem UART zgodnie ze schematem powyżej.
Konfiguruję mój ulubiony terminal minicom na prędkość 115200, bez kontroli błędów sprzętu i oprogramowania. I zaczynajmy.

Jednoczesny speedtest na kilku modemach LTE

Podczas ładowania VIM2 do terminala UART naciskam klawisz, taki jak spacja, aby zatrzymać ładowanie. Po pojawieniu się linii

kvim2# 

Wpisuję polecenie:

kvim2# run update

Na hoście, z którego ładujemy, wykonuję:

burn-tool -v aml -b VIM2 -i  VIM2_Ubuntu-server-bionic_Linux-4.9_arm64_EMMC_V20191231.img

To tyle, uff. Sprawdziłem, na płycie jest Linux. Login/hasło khadas:khadas.

Następnie kilka drobnych ustawień początkowych. Do dalszej pracy wyłączam hasło do Sudo (tak, nie bezpieczne, ale wygodne).

sudo visudo

Edytuję linię w formularzu i zapisuję

# Allow members of group sudo to execute any command
%sudo ALL=(ALL:ALL) NOPASSWD: ALL

Następnie zmieniam obecną lokalizację, aby czas był w Moskwie, w przeciwnym razie będzie w Greenwich.

sudo timedatectl set-timezone Europe/Moscow

lub

ln -s /usr/share/zoneinfo/Europe/Moscow /etc/localtime

Jeśli sprawia ci to trudność, nie używaj tej płyty; Raspberry Pi jest lepsze. Szczerze mówiąc.

Modem Huawei e3372h – 153

Modem ten był dla mnie znaczącym źródłem krwi i tak naprawdę stał się wąskim gardłem całego projektu. Generalnie nazwa „modem” dla tych urządzeń w ogóle nie oddaje istoty pracy: to potężny kombajn, ten sprzęt ma złożone urządzenie, które udaje CD-ROM w celu zainstalowania sterowników, a następnie przełącza się w tryb karty sieciowej.

Architektonicznie z punktu widzenia użytkownika Linuksa po wszystkich ustawieniach wygląda to tak: po podłączeniu modemu mam interfejs sieciowy eth*, który przez dhcp otrzymuje adres IP 192.168.8.100, a bramę domyślną to 192.168.8.1.

I najważniejszy moment! Ten model modemu nie może pracować w trybie modemu, który jest sterowany poleceniami AT. Wszystko byłoby znacznie prostsze, utwórz połączenia PPP dla każdego modemu, a następnie operuj z nimi. Ale w moim przypadku „sam” (dokładniej linuksowy nurek zgodnie z zasadami udev) tworzy interfejs eth i przypisuje mu adres IP poprzez dhcp.

Aby uniknąć dalszych nieporozumień, sugeruję zapomnieć o słowie „modem” i powiedzieć karta sieciowa i brama, ponieważ w istocie przypomina to połączenie nowej karty sieciowej z bramą.
Gdy jest jeden modem, nie powoduje to żadnych specjalnych problemów, ale gdy jest ich więcej, czyli n-sztuk, powstaje następujący obraz sieci.

Jednoczesny speedtest na kilku modemach LTE

Oznacza to, że n kart sieciowych ma ten sam adres IP, a każda ma tę samą bramę domyślną. Ale tak naprawdę każdy z nich jest podłączony do własnego operatora.

Początkowo miałem proste rozwiązanie: za pomocą polecenia ifconfig lub ip wyłącz wszystkie interfejsy i po prostu włącz jeden z nich po kolei i przetestuj. Rozwiązanie było dobre dla wszystkich, z tym wyjątkiem, że w momentach przełączania nie mogłem połączyć się z urządzeniem. A ponieważ przełączanie jest częste i szybkie, właściwie w ogóle nie miałem okazji się połączyć.

Dlatego wybrałem ścieżkę ręcznej zmiany adresów IP modemów, a następnie kierowania ruchem za pomocą ustawień routingu.

Jednoczesny speedtest na kilku modemach LTE

To nie był koniec moich problemów z modemami: w razie problemów z zasilaniem odpadały, a potrzebne było dobre, stabilne zasilanie do koncentratora USB. Rozwiązałem ten problem, lutując na twardo zasilanie bezpośrednio do koncentratora. Kolejny problem, który napotkałem i który zrujnował cały projekt: po ponownym uruchomieniu lub zimnym uruchomieniu urządzenia nie wszystkie modemy zostały wykryte i nie zawsze i nie mogłem ustalić, dlaczego tak się stało i według jakiego algorytmu. Ale najpierw sprawy.

Aby modem działał poprawnie zainstalowałem pakiet usb-modeswitch.

sudo apt update
sudo apt install -y usb-modeswitch

Następnie po podłączeniu modem zostanie poprawnie wykryty i skonfigurowany przez podsystem udev. Ja sprawdzam po prostu podłączając modem i upewniając się, że sieć się pojawia.
Kolejny problem, którego nie mogłem rozwiązać: jak mogę uzyskać nazwę operatora, z którym współpracujemy, z tego modemu? Nazwa operatora jest zawarta w interfejsie internetowym modemu pod adresem 192.168.8.1. Jest to dynamiczna strona internetowa, która otrzymuje dane za pośrednictwem żądań Ajax, więc samo pobranie strony i przeanalizowanie nazwy nie będzie działać. Zacząłem więc zastanawiać się, jak stworzyć stronę internetową itp. i zdałem sobie sprawę, że robię jakiś nonsens. W rezultacie splunął, a operator zaczął odbierać za pomocą samego interfejsu API Speedtest.

Byłoby znacznie łatwiej, gdyby modem miał dostęp za pomocą poleceń AT. Można byłoby go przekonfigurować, utworzyć połączenie ppp, przypisać adres IP, uzyskać operatora telekomunikacyjnego itp. Ale niestety, pracuję z tym, co dostałem.

GPS

Odbiornik GPS, który dostałem, miał interfejs UART i zasilanie. Nie było to najlepsze rozwiązanie, ale nadal było wykonalne i proste. Odbiornik wyglądał mniej więcej tak.

Jednoczesny speedtest na kilku modemach LTE

Szczerze mówiąc, to była moja pierwsza praca z odbiornikiem GPS, ale tak jak się spodziewałem, wszystko było już dawno za nas obmyślone. Korzystamy więc po prostu z gotowych rozwiązań.

Najpierw włączam uart_AO_B (UART_RX_AO_B, UART_TX_AO_B), aby połączyć GPS.

khadas@Khadas:~$ sudo fdtput -t s /dtb.img /serial@c81004e0 status okay

Następnie sprawdzam powodzenie operacji.

khadas@Khadas:~$ fdtget /dtb.img /serial@c81004e0 status
okay

To polecenie najwyraźniej edytuje drzewo deweloperów w locie, co jest bardzo wygodne.

Po pomyślnym zakończeniu tej operacji uruchom ponownie komputer i zainstaluj demona GPS.

khadas@Khadas:~$ sudo reboot

Instalacja demona GPS. Instaluję wszystko i od razu odcinam do dalszej konfiguracji.

sudo apt install gpsd gpsd-clients -y
sudo killall gpsd
 
/* GPS daemon stop/disable */
sudo systemctl stop gpsd.socket
sudo systemctl disable gpsd.socket

Edycja pliku ustawień.

sudo vim /etc/default/gpsd

Instaluję UART na którym będzie wisiał GPS.

DEVICES="/dev/ttyS4"

A potem włączamy wszystko i zaczynamy.

/* GPS daemon enable/start */
sudo systemctl enable gpsd.socket
sudo systemctl start gpsd.socket

Następnie podłączam GPS.

Jednoczesny speedtest na kilku modemach LTE

Przewód GPS mam w rękach, przewody debugera UART widać pod palcami.

Uruchamiam ponownie i sprawdzam działanie GPS za pomocą programu gpsmon.

Jednoczesny speedtest na kilku modemach LTE

Na tym zrzucie ekranu nie widać satelitów, ale widać komunikację z odbiornikiem GPS, a to oznacza, że ​​wszystko jest w porządku.

W Pythonie wypróbowałem wiele opcji pracy z tym demonem, ale zdecydowałem się na tę, która działała poprawnie z Pythonem 3.

Instaluję niezbędną bibliotekę.

sudo -H pip3 install gps3 

I rzeźbię kod pracy.

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 ("Wait gps counter = %d" % counter)
		if counter == 10:
			ErrorMessage("Ошибка GPS приемника!!!")
			return "NA", "NA"
		time.sleep(1.0)
...
f __name__ == '__main__':
...
	#gps
	agps_thread = AGPS3mechanism()  # Instantiate AGPS3 Mechanisms
	agps_thread.stream_data()  # From localhost (), or other hosts, by example, (host='gps.ddns.net')
	agps_thread.run_thread()  # Throttle time to sleep after an empty lookup, default '()' 0.2 two tenths of a second

Jeśli potrzebuję uzyskać współrzędne, można to zrobić za pomocą następującego wywołania:

longitude, latitude = getPositionData(agps_thread)

W ciągu 1–10 sekund albo otrzymam współrzędne, albo nie. Tak, miałem dziesięć prób uzyskania współrzędnych. Nieoptymalne, krzywe i krzywe, ale działa. Zdecydowałem się to zrobić, ponieważ GPS może mieć słaby odbiór i nie zawsze odbiera dane. Jeśli będziesz czekać na odbiór danych, to jeśli będziesz pracować w odległym pokoju, program w tym miejscu się zawiesi. Dlatego zaimplementowałem tę nieelegancką opcję.

W zasadzie gdyby było więcej czasu to dałoby się odbierać dane z GPS bezpośrednio przez UART, analizować je w osobnym wątku i pracować z nimi. Ale czasu w ogóle nie było, stąd brutalnie brzydki kod. I tak, nie wstydzę się.

LED

Podłączenie diody LED było proste i trudne jednocześnie. Główna trudność polega na tym, że numer pinu w systemie nie odpowiada numerowi pinu na płycie oraz dlatego, że dokumentacja jest pisana lewą ręką. Aby porównać numer PIN sprzętu i numer PIN w systemie operacyjnym, należy uruchomić polecenie:

gpio readall

Wyświetlona zostanie tabela korespondencji pinów w systemie i na płytce. Po czym mogę już obsługiwać pin w samym systemie operacyjnym. W moim przypadku dioda LED jest podłączona do GPIOH_5.

Jednoczesny speedtest na kilku modemach LTE

Przełączam pin GPIO w tryb wyjściowy.

gpio -g mode 421 out

Zapisuję zero.

gpio -g write 421 0

Zapisuję jedno.

gpio -g write 421 1

Jednoczesny speedtest na kilku modemach LTE
Wszystko się świeci, po wpisaniu „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)

Teraz w razie błędów wywołuję error_blink() i dioda LED będzie pięknie migać.

Węzły oprogramowania

Speedtest API

To wielka radość, że usługa speedtest.net ma własne python-API, możesz zajrzeć Github.

Dobrą rzeczą jest to, że istnieją również kody źródłowe, które można przeglądać. Jak pracować z tym API (proste przykłady) można znaleźć w odpowiednia sekcja.

Instaluję bibliotekę Pythona za pomocą następującego polecenia.

sudo -H pip3 install speedtest-cli

Na przykład możesz nawet zainstalować tester prędkości w Ubuntu bezpośrednio z oprogramowania. Jest to ta sama aplikacja Pythona, którą można następnie uruchomić bezpośrednio z konsoli.

sudo apt install speedtest-cli -y

I zmierz prędkość swojego Internetu.

speedtest-cli
Retrieving speedtest.net configuration...
Testing from B***** (*.*.*.*)...
Retrieving speedtest.net server list...
Selecting best server based on ping...
Hosted by MTS (Moscow) [0.12 km]: 11.8 ms
Testing download speed................................................................................
Download: 7.10 Mbit/s
Testing upload speed......................................................................................................
Upload: 3.86 Mbit/s

W efekcie tak jak ja. Musiałem dostać się do kodów źródłowych tego testu prędkości, aby pełniej wdrożyć je w moim projekcie. Jednym z najważniejszych zadań jest uzyskanie nazwy operatora telekomunikacyjnego w celu podstawienia jej w tabliczce.

import speedtest
from datetime import datetime
...
#Указываем конкретный сервер для теста
#6053) MaximaTelecom (Moscow, Russian Federation)
servers = ["6053"]
# If you want to use a single threaded test
threads = None
s = speedtest.Speedtest()
#получаем имя оператора сотовой связи
opos = '%(isp)s' % s.config['client']
s.get_servers(servers)
#получаем текстовую строку с параметрами сервера
testserver = '%(sponsor)s (%(name)s) [%(d)0.2f km]: %(latency)s ms' % s.results.server
#тест загрузки
s.download(threads=threads)
#тест выгрузки
s.upload(threads=threads)
#получаем результаты
s.results.share()

#После чего формируется строка для записи в csv-файл.
#получаем позицию GPS
longitude, latitude = getPositionData(agps_thread)
#время и дата
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"
#тут идет запись в файл логов

Tutaj również wszystko okazało się nie takie proste, choć wydawałoby się znacznie prostsze. Początkowo parametr serwerów był równy [], mówią, wybierz najlepszy serwer. W rezultacie miałem losowe serwery i, jak można się domyślić, zmienną prędkość. Jest to dość złożony temat, korzystanie ze stałego serwera, jeśli tak, statycznego lub dynamicznego, wymaga badań. Ale oto przykład wykresów pomiaru prędkości dla operatora Beeline przy dynamicznym wyborze serwera testowego i serwera statycznie ustalonego.

Jednoczesny speedtest na kilku modemach LTE
Wynik pomiaru prędkości przy wyborze serwera dynamicznego.

Jednoczesny speedtest na kilku modemach LTE
Wynik testów szybkości, z jednym ściśle wybranym serwerem.

Podczas testów w obu miejscach pojawiło się „futro”, które należy usunąć metodami matematycznymi. Ale w przypadku serwera stałego jest on nieco mniejszy, a amplituda jest bardziej stabilna.
Ogólnie rzecz biorąc, jest to miejsce świetnych badań. Zmierzyłbym prędkość mojego serwera za pomocą narzędzia iperf. Trzymamy się jednak specyfikacji technicznych.

Wysyłanie poczty i błędy

Wypróbowałem kilkadziesiąt różnych opcji wysyłania poczty, ale ostatecznie zdecydowałem się na następujące. Zarejestrowałem skrzynkę pocztową na Yandex, a następnie wziąłem To jest przykład wysyłania poczty. Sprawdziłem i zaimplementowałem w programie. W tym przykładzie zbadano różne opcje, w tym wysyłanie z Gmaila itp. Nie chciało mi się zawracać głowy konfiguracją serwera pocztowego i nie miałem na to czasu, ale jak się później okazało, to też było na marne.

Logi zostały wysłane zgodnie z harmonogramem, jeśli istnieje połączenie, co 6 godzin: o godzinie 00:06, 12:18, XNUMX:XNUMX i XNUMX:XNUMX. Wysłałem w następujący sposób.

from send_email import *
...
message_log = "Логи тестирования платы №1"
EmailForSend = ["[email protected]", "[email protected]"]
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, "Логи за " + curdata + " " + сurtime, files)
	except:
		print("Network problem for send mail")
		return False
	return True

Początkowo wysyłane były również błędy. Na początku były one gromadzone na liście, a następnie wysyłane również za pomocą harmonogramu, jeśli było połączenie. Jednak potem pojawiły się problemy z tym, że Yandex ma ograniczenie liczby wysyłanych dziennie wiadomości (jest to ból, smutek i upokorzenie). Ponieważ nawet w ciągu minuty mogła pojawić się ogromna liczba błędów, musieliśmy zrezygnować z wysyłania błędów pocztą. Należy więc pamiętać o automatycznym wysyłaniu informacji o takim problemie za pośrednictwem usług Yandex.

Serwer opinii

Aby mieć dostęp do zdalnego sprzętu i móc go dostosowywać i rekonfigurować, potrzebowałem zewnętrznego serwera. Ogólnie rzecz biorąc, słuszne byłoby przesłanie wszystkich danych na serwer i zbudowanie wszystkich pięknych wykresów w interfejsie internetowym. Ale nie wszystko na raz.

Wybrałem VPS ruvds.com. Możesz wziąć najprostszy serwer. I ogólnie dla moich celów to by wystarczyło. Ponieważ jednak nie płaciłem za serwer z własnej kieszeni, zdecydowałem się wziąć go z niewielką rezerwą, aby wystarczyło nam wdrożenie interfejsu WWW, własnego serwera SMTP, VPN itp. Ponadto możesz skonfigurować bota Telegramu i nie mieć problemów z jego blokowaniem. Dlatego wybrałem Amsterdam i następujące parametry.

Jednoczesny speedtest na kilku modemach LTE

Jako metodę komunikacji ze sprzętem vim2 wybrał odwrotne połączenie ssh i jak pokazała praktyka, nie jest to najlepsze. W przypadku utraty połączenia serwer wstrzymuje port i przez pewien czas nie można się przez niego połączyć. Dlatego nadal lepiej jest skorzystać z innych metod komunikacji, na przykład VPN. W przyszłości chciałem przejść na VPN, ale nie miałem czasu.

Nie będę wdawał się w szczegóły konfiguracji firewalla, ograniczania praw, wyłączania połączeń root ssh i inne truizmy związane z konfiguracją VPS. Chciałbym wierzyć, że już wszystko wiesz. W przypadku połączenia zdalnego tworzę nowego użytkownika na serwerze.

adduser vimssh

Generuję klucze połączenia ssh na naszym sprzęcie.

ssh-keygen

I kopiuję je na nasz serwer.

ssh-copy-id [email protected]

Na naszym sprzęcie tworzę automatyczne odwrotne połączenie SSH przy każdym uruchomieniu.

[Unit] Description=Auto Reverse SSH
Requires=systemd-networkd-wait-online.service
After=systemd-networkd-wait-online.service
[Service] User=khadas
ExecStart=/usr/bin/ssh -NT -o ExitOnForwardFailure=yes -o ServerAliveInterval=60 -CD 8080 -R 8083:localhost:22 [email protected]
RestartSec=5
Restart=always
[Install] WantedBy=multi-user.target

Zwróć uwagę na port 8083: określa, którego portu użyję do połączenia przez odwrotny ssh. Dodaj go do startu i uruchom.

sudo systemctl enable autossh.service
sudo systemctl start autossh.service

Możesz nawet zobaczyć status:

sudo systemctl status autossh.service

Teraz na naszym serwerze VPS, jeśli uruchomimy:

ssh -p 8083 khadas@localhost

Następnie przechodzę do mojego testowego sprzętu. Ze sprzętu mogę także wysyłać logi i dowolne dane przez ssh na mój serwer, co jest bardzo wygodne.

Wszystko razem

Jednoczesny speedtest na kilku modemach LTE
Włączając, zacznijmy programowanie i debugowanie

Uff, cóż, to wszystko, opisałem wszystkie węzły. Teraz nadszedł czas, aby to wszystko połączyć w jedną całość. Możesz zobaczyć kod tutaj.

Ważny punkt dotyczący kodu: Ten projekt nie może zacząć się w ten sposób, ponieważ był dostosowany do konkretnego zadania, o określonej architekturze. Mimo że podam kod źródłowy, to i tak to, co najcenniejsze, wyjaśnię tutaj, bezpośrednio w tekście, bo inaczej byłoby to całkowicie niezrozumiałe.

Na początek inicjuję gps, gpio i uruchamiam osobny wątek harmonogramu.

#запуск потока планировщика
pShedulerThread = threading.Thread(target=ShedulerThread, args=(1,))
pShedulerThread.start()

Harmonogram jest dość prosty: sprawdza, czy nadszedł czas na wysłanie wiadomości i jaki jest bieżący stan błędu. Jeśli pojawi się flaga błędu, migamy diodą 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)

Najtrudniejszą częścią tego projektu jest utrzymanie odwrotnego połączenia SSH dla każdego testu. Każdy test polega na rekonfiguracji bramy domyślnej i serwera DNS. Ponieważ i tak nikt nie czyta, wiedzcie, że pociąg nie jedzie po drewnianych szynach. Kto znajdzie jajko wielkanocne, otrzyma cukierka.

Aby to zrobić, tworzę osobną tablicę routingu -set-mark 0x2 i regułę przekierowującą ruch.

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")

Możesz dowiedzieć się więcej o tym, jak to działa przeczytaj w tym artykule.

Po czym wchodzę w nieskończoną pętlę, gdzie za każdym razem otrzymujemy listę podłączonych modemów (aby dowiedzieć się, czy konfiguracja sieci nagle się nie zmieniła).

network_list = getNetworklist()

Uzyskanie listy interfejsów sieciowych jest dość proste.

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_list

Po otrzymaniu listy ustawiam adresy IP dla wszystkich interfejsów, tak jak pokazałem na obrazku w rozdziale o modemie.

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")

Następnie po prostu przechodzę przez każdy interfejs w pętli. I konfiguruję każdy interfejs.

	for iface in network_list:
		ConfigNetwork(iface)

def ConfigNetwork(iface):
#сбрасываем все настройки
		cmd_run("sudo ip route flush all")
#Назначаем шлюз по умолчанию
		cmd_run("sudo route add default gw 192.168.8.1 " + iface)
#задаем dns-сервер (это нужно для работы speedtest)
		cmd_run ("sudo bash -c 'echo nameserver 8.8.8.8 > /etc/resolv.conf'")

Sprawdzam interfejs pod kątem funkcjonalności, jeżeli nie ma sieci to generuje mi błędy. Jeśli jest sieć, czas działać!

Tutaj konfiguruję routing ssh do tego interfejsu (jeśli tego nie zrobiono), wysyłam błędy do serwera jeśli nadejdzie czas, wysyłam logi i na koniec uruchamiam speedtest i zapisuję logi do pliku csv.

if not NetworkAvalible():
....
#Здесь мы формируем ошибки
....
else: #Есть сеть, ура, работаем!
#Если у нас проблемный интерфейс, на котором ssh, то меняем его
  if (sshint == lastbanint or sshint =="free"):
    print("********** Setup SSH ********************")
    if sshint !="free":
      сmd_run("sudo ip route del default via 192.168.8.1 dev " + sshint +" table 102")
    SetupReverseSSH(iface)
    sshint = iface
#раз сетка работает, то давай срочно все отправим!!!
    if ready_to_send:
      print ("**** Ready to send!!!")
        if sendLogs():
          ready_to_send = False
        if error_status:
          SendErrors()
#и далее тестируем скорость и сохраняем логи. 

Warto wspomnieć o funkcji ustawienia odwrotnego 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")

I oczywiście musisz dodać całe to piękno do startu. W tym celu tworzę plik:

sudo vim /etc/systemd/system/modems_speedtest.service

I piszę w nim:

[Unit] Description=Modem Speed Test
Requires=systemd-networkd-wait-online.service
After=systemd-networkd-wait-online.service
[Service] User=khadas
ExecStart=/usr/bin/python3.6 /home/khadas/modems_speedtest/networks.py
RestartSec=5
Restart=always
[Install] WantedBy=multi-user.target

Włączam automatyczne ładowanie i zaczynam!

sudo systemctl enable modems_speedtest.service
sudo systemctl start modems_speedtest.service

Teraz mogę zobaczyć logi tego, co się dzieje, za pomocą polecenia:

journalctl -u modems_speedtest.service --no-pager -f

wyniki

Cóż, teraz najważniejsze jest to, co się w rezultacie wydarzyło? Oto kilka wykresów, które udało mi się uchwycić podczas procesu programowania i debugowania. Wykresy zostały zbudowane przy użyciu gnuplot z następującym skryptem.

#! /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"

Pierwsze doświadczenie było z operatorem Tele2, które przeprowadziłem przez kilka dni.

Jednoczesny speedtest na kilku modemach LTE

Tutaj użyłem dynamicznego serwera pomiarowego. Pomiary prędkości działają, ale bardzo się wahają, ale nadal widoczna jest pewna wartość średnia, a można to uzyskać filtrując dane, na przykład średnią ruchomą.

Później zbudowałem szereg wykresów dla innych operatorów telekomunikacyjnych. W tym przypadku istniał już jeden serwer testowy i wyniki również były bardzo interesujące.

Jednoczesny speedtest na kilku modemach LTE

Jednoczesny speedtest na kilku modemach LTE

Jednoczesny speedtest na kilku modemach LTE

Jednoczesny speedtest na kilku modemach LTE

Jak widać temat jest bardzo obszerny pod względem badania i przetwarzania tych danych i wyraźnie nie starcza na kilka tygodni pracy. Ale…

Wynik pracy

Prace zostały zakończone nagle z przyczyn niezależnych ode mnie. Jedną ze słabości tego projektu, moim subiektywnym zdaniem, był modem, który nie bardzo chciał współpracować z innymi modemami jednocześnie i robił takie sztuczki przy każdym załadowaniu. Do tych celów istnieje ogromna liczba innych modeli modemów, zwykle są one już w formacie Mini PCI-e i są instalowane wewnątrz urządzenia i są znacznie łatwiejsze w konfiguracji. Ale to zupełnie inna historia. Projekt był ciekawy i bardzo się cieszę, że mogłam w nim wziąć udział.

Jednoczesny speedtest na kilku modemach LTE

Źródło: www.habr.com

Dodaj komentarz