По време на карантина ми предложиха да участвам в разработването на устройство за измерване на скоростта на LTE модеми за няколко мобилни оператора.
Клиентът искаше да оцени скоростта на различни оператори на мрежата в различни географски точки, за да може да реши кой мобилен оператор е най-подходящият при инсталирането на оборудване, използващо LTE свързаност, например за видеопредавания. Задачата трябваше да се реши по максимално прост и икономичен начин, без скъпо оборудване.
Веднага ще кажа, че задачата не е най-простата и е наукоемка, ще разкажа за проблемите, с които се сблъсках, и как ги решавах. И така, да започваме.
Забележка
Измерването на скоростта на LTE свързаност е доста сложно: необходимо е правилно да се избере оборудването и методиката за измерване, както и да се разбере топологията и работата на мобилната мрежа. Освен това на скоростта могат да влияят няколко фактора: брой на абонатите на клетката, метеорологични условия, дори скоростта може да варира значително от клетка до клетка заради топологията на мрежата. Общо взето, това е задача с множество неизвестни, и само операторът на мрежата може да я реши правилно.
Първоначално клиентът искаше просто да изпрати куриер с телефоните на операторите, да извършва измервания директно на телефона и след това да записва резултатите в тетрадка. Моето решение за измерване на скоростта на мрежите LTE, макар и не перфектно, решава зададената задача.
Поради липса на време, взех решения, които не бяха в полза на удобството или практичността, а в полза на скоростта на разработката. Например, за отдалечен достъп настроих обратен ssh, вместо по-практичния vpn, за да спестя време за настройка на сървъра и на всеки отделен клиент.
Техническо задание
Както е посочено в статията : Не работете без ТЗ! Никога, никъде!
Техническото задание беше достатъчно просто, ще го разширя малко за разбиране от крайния потребител. Изборът на технически решения и оборудване беше диктуван от клиента. И така, самото ТЗ, след всички съгласувания:
На базата на едноплатен компютър vim2 да се направи тестер за скорост на LTE свързаност чрез модеми Huawei e3372h — 153 няколко телекомуникационни оператора (от един до n). Също така е необходимо да се получават координати от GPS приемник, свързан по UART. Измерванията на скоростта се извършват с помощта на услугата и да се събират в таблица от вида:
Таблица в формат csv. След това да се изпраща по е-майл на всеки 6 часа. В случай на възникване на грешки, да мига светодиод, свързан с GPIO.
Технизирането описах свободно, след множество одобрения. Но същността на задачата вече е ясна. Срокът за всичко беше определен на една седмица. Но в действителност той се разтегна на три седмици. Това с оглед на факта, че го правех само след основната работа и през уикендите.
Тук искам отново да подчертая, че поръчителят предварително е уговорил използването на услуга за измерване на скоростта и хардуер, което значително ограничи моите възможности. Бюджетът също беше ограничен, така че не беше закупувано допълнително оборудване. Затова беше необходимо да се играе по данни правила.
Архитектура и разработка
Схемата е проста и очевидна. Затова оставям без особени коментари.

Целият проект реших да реализирам на python, въпреки че нямах никакъв опит в разработката на този език. Избрах го, защото имаше множество готови примери и решения, които можеха да ускорят разработката. Затова моля всички професионални програмисти да не критикуват първия ми опит в разработката на python и винаги съм готов да чуя конструктивна критика, за подобряване на уменията си.
Също така в процеса открих, че python има две работещи версии 2 и 3, като накрая се спрях на третата.
Аппаратни модули
Одноплатен компютър vim2
Като основна машина ми беше предоставен едноплатен компютър

Отличен, мощен медиакомбайн за умен дом и SMART-TV, но рядко подходящ за тази задача, или да кажем така, слабо подходящ. Например, главната му ОС е Android, а Linux е спомагателна ОС, и съответно никой не гарантира качествената работа на всички модули и драйвери под Linux. И предполагам, част от проблемите бяха свързани с USB драйверите на тази платформа, затова модемите не работеха на тази дъска както очаквах. Също така, много лоша и разпръсната документация, което направи всяка операция продължителна и трудна. Дори обикновената работа с GPIO ми създаде много проблеми. Например, за да настроя работа с светодиод, ми отне няколко часа. Но, ако бъда обективен, принципно не беше важно какъв е едноплатникът, важното е да работи и да има USB порти.
В началото трябва да инсталирам Linux на тази дъска. За да не се блъскам в дебрите на документацията и за тези, които ще се занимават с този едноплатник, пиша тази глава.
Има два варианта за инсталиране на Linux: на външна SD карта или на вътрешна MMC. С картата се борих доста вечер, но не разбрах как да я накарам да заработи, затова реших да инсталирам на MMC, макар че без съмнение с външната карта щеше да бъде много по-лесно.
За фърмуера . Превеждам от странно на български. За да флашна дъската, трябва да свържа хардуерния UART. Свързах го
- Tool Pin GND: Pin17 на GPIO на VIM
- Tool Pin TXD: Pin18 на GPIO на VIM (Linux_Rx)
- Tool Pin RXD: Pin19 на GPIO на VIM (Linux_Tx)
- Tool Pin VCC: Pin20 на GPIO на VIM

След което изтеглих фърмуера . Конкретната версия на фърмуера .
За да флашна този фърмер, ми трябват някои инструменти. По-подробно за това е описано . Под Windows не опитвах да флашвам, но за флашването под Linux трябва да спомена няколко думи. Първо ще инсталирам инструментите, според инструкциите.
git clone https://github.com/khadas/utils
cd /path/to/utils
sudo ./INSTALLИии… Нищо не работи. Изразходих няколко часа в корекции на инсталационните скриптове, за да се инсталира всичко правилно. Какво точно направих, не помня, но беше истински цирк. Така че бъдете внимателни. Но без тези инструменти, да мъча vim2, няма смисъл. По-добре изобщо да не се захващате с него!
След седем кръга ада, конфигуриране на скриптове и инсталиране, получих пакет работещи утилити. Свързах платката по USB с компютър под Linux, и също така свързах UART по схемата по-горе.
Настройвам любимия си терминал minicom на скорост 115200, без хардуерно и софтуерно контролиране на грешките. И започваме.

Когато зареждам VIM2 в терминала UART, натискам произволен клавиш, например интервал, за да спра зареждането. След като се появи редът
kvim2# Въвеждам командата:
kvim2# run updateНа хоста, от който зареждаме, изпълнявам:
burn-tool -v aml -b VIM2 -i VIM2_Ubuntu-server-bionic_Linux-4.9_arm64_EMMC_V20191231.imgГотово, уф. Програмирах, на платката има Linux. Логин/парола khadas:khadas.
След това малки първоначални настройки. За по-нататъшна работа отключвам паролата за sudo (да, не е безопасно, но удобно).
sudo visudoРедактирам реда до вида и запазвам
# Allow members of group sudo to execute any command
%sudo ALL=(ALL:ALL) NOPASSWD: ALLСлед това променям настоящата локализация, за да е времето по Москва, иначе ще е по Гринуич.
sudo timedatectl set-timezone Europe/Moscowили
ln -s /usr/share/zoneinfo/Europe/Moscow /etc/localtimeАко това ти се струва сложно, по-добре не използвай тази платка, Raspberry Pi е по-добре. Честно.
Модем Huawei e3372h — 153
Този модем ми създаде доста проблеми и в действителност стана най-слабата връзка в целия проект. Всъщност, названието "модем" за тези устройства изобщо не отразява същността на работата: това е мощна комбинация, този хардуер има съставно устройство, което се представя за CD-ROM, за да инсталира драйвери, а след това преминава в режим на мрежова карта.
Архитектурно, от гледна точка на потребителя на Linux след всички настройки, изглежда така: след свързването на модема, имам мрежов интерфейс eth*, който получава IP адрес 192.168.8.100 чрез DHCP, и шлюз по подразбиране 192.168.8.1.
И най-важният момент! Тази модел модем не може да работи в режим на модем, който се управлява с AT-команди.Всичко щеше да е много по-просто, да се създаде ppp-съединение за всеки модем и след това вече да се оперира с тях. Но в моя случай „сам“ (по-точно драйверите на Linux според правилата на udev) създават eth-интерфейс и назначават IP адрес чрез DHCP.
За да не се объркваме по-нататък, предлагам да забравим думата „модем“ и да говорим за мрежова карта и шлюз, защото по същество, това е като свързване на нова мрежова карта със шлюз.
Когато има един модем, това не предизвиква особени проблеми, но когато има повече от един, а именно n-брой, то тогава възниква следната картина на мрежата.

Т.е. n мрежови карти с един IP адрес, при всеки един и същ шлюз по подразбиране. Но всъщност, всеки от тях е свързан с отделен оператор.
Първоначално имах просто решение: с командите ifconfig или ip да изключвам всичките интерфейси и последователно да активирам един и да го тествам. Решението беше добро, освен че в моментите на превключване нямах възможност да се свържа с устройството. А тъй като превключванията са чести и бързи, фактически нямах възможност да се свържа изобщо.
Затова избрах да променям 'ръчно' IP адресите на модемите и да предавам трафик с настройки за маршрутизиране.

С това проблемите с модемите не приключиха: при проблеми с захранването, те се изключваха, необходимо беше добро стабилно захранване на USB хъба. Реших този проблем, като запоих захранването директно към хъба. Друг проблем, с който се сблъсках и който провали целия проект: след рестартиране или студен старт на устройството не се разпознаваха всички модеми и не винаги, и не успях да разбера защо се случваше това и по какъв алгоритъм. Но всичко е по реда си.
За коректната работа на модема инсталирах пакета usb-modeswitch.
sudo apt update
sudo apt install -y usb-modeswitch След това модемът след свързване ще бъде коректно разпознат и конфигуриран от подсистемата udev. Проверявам, просто свързвайки модема и уверявайки се, че мрежата е налична.
Още един проблем, който не успях да реша: как да получа името на оператора, с който работим от този модем? Името на оператора се съдържа в уеб интерфейса на модема на адрес 192.168.8.1. Това е динамична уеб страница, която получава данни чрез ajax заявки, така че просто да wget-на страницата и да извлека името не е възможно. Затова започнах да гледам как да работя с уеб страницата и т.н., и разбрах, че правя нещо безсмислено. В резултат плюх и започнах да получавам оператора чрез API на самия Speedtest.
Много по-лесно щеше да е, ако модемът имаше достъп чрез AT команди. Можеше да се преконфигурира, да се създаде ppp свързване, да се назначи IP, да се получи оператор на свързване и т.н. Но за съжаление, работя с това, което ми дадоха.
GPS
GPS приемник, който ми издадоха, имаше интерфейс UART и захранване. Не беше най-доброто решение, но все пак работеше и беше просто. Приемникът изглеждаше приблизително така.

Честно казано, работя с GPS приемник за първи път, но както предполагах, всичко отдавна е измислено за нас. Просто използваме готови решения.
Първо включвам uart_AO_B (UART_RX_AO_B, UART_TX_AO_B) за свързване на GPS.
khadas@Khadas:~$ sudo fdtput -t s /dtb.img /serial@c81004e0 status okayСлед това проверявам успешността на операцията.
khadas@Khadas:~$ fdtget /dtb.img /serial@c81004e0 status
okayТази команда, изглежда, редактира devtree на момента, което е доста удобно.
След успеха на тази операция, рестартираме и инсталираме gps демон.
khadas@Khadas:~$ sudo rebootИнсталация на gps демон. Инсталирам всичко и го изключвам веднага за последваща конфигурация.
sudo apt install gpsd gpsd-clients -y
sudo killall gpsd
/* Спрете/изключете GPS демона */
sudo systemctl stop gpsd.socket
sudo systemctl disable gpsd.socketРедактирам конфигурационния файл.
sudo vim /etc/default/gpsdНастройвам UART, на който ще е свързан GPS.
DEVICES="/dev/ttyS4"След това включваме всичко и стартираме.
/* GPS daemon enable/start */
sudo systemctl enable gpsd.socket
sudo systemctl start gpsd.socketСлед което свързвам GPS.

В ръцете ми е кабел GPS, под пръстите ми се виждат проводниците на UART отладчика.
Рестартирам и проверявам работата на GPS с помощта на програмата gpsmon.

На този скриншот не се виждат сателити, но се вижда комуникация с GPS приемника, и това показва, че всичко е наред.
Пробвах много варианти за работа с този демон на python, но се спрях на този, който работеше коректно с python 3.
Инсталирам необходимата библиотека.
sudo -H pip3 install gps3 И създавам кода за работа.
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 ("Изчакайте gps брояч = %d" % counter)
if counter == 10:
ErrorMessage("Грешка в GPS приемника!!!")
return "NA", "NA"
time.sleep(1.0)
...
f __name__ == '__main__':
... #gps
agps_thread = AGPS3mechanism() # Създайте AGPS3 механизми
agps_thread.stream_data() # От localhost (), или от други хостове, например, (host='gps.ddns.net')
agps_thread.run_thread() # Времето за забавяне след празно търсене, по подразбиране '()' 0.2 две десети от секунда
Ако ми трябва да получа координати, това става с следния извик:
longitude, latitude = getPositionData(agps_thread)
И в течение 1-10 секунди аз или ще получа координата, или няма. Да, имах десет опити да получа координати. Не е оптимално, накриво и криво, но работи. Реших да правя така, защото GPS може да улови слабо и не винаги да получава данни. Ако чакам данни, то при работа в глухо помещение, програмата ще се спре на това място. Затова реализирах такъв неелегантен вариант.
В принципе, ако имаше повече време, можеше да получавам данни от GPS директно по UART, да ги парсирам в отделен поток и да работя с тях. Но времето беше съвсем недостатъчно, затова имам ужасен код. И да, не се срамувам.
LED
С подключването на светодиода всичко беше просто и сложно в същото време. Основната сложност е, че номера на пина в системата не съответства на номера на пина на платката и защото документацията е написана с лявата пета. За да съпоставя номера на хардуерния пин и номера на пина в ОС, трябва да изпълня командата:
gpio readallЩе се изведе таблица с разширението на пина в системата и на платката. След това вече мога да работя с пина в самата ОС. В моя случай светодиодът е свързан към GPIOH_5.

Преобразувам пина GPIO в режим на изход.
gpio -g mode 421 outЗаписвам нула.
gpio -g write 421 0Записвам единица.
gpio -g write 421 1 
Всичко свети, след като запиша «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)
Сега, в случай на грешки, извиквам error_blink() и светодиодът ми ще ми мига красиво.
Програмни модули
Speedtest API
Голяма радост е, че услугата speedtest.net има свой собствен python-API, може да се види на .
Добре е, че има изходен код, който също може да се види. Как да работим с този API (основни примери) може да се види в .
Инсталирам python-библиотеката с следната команда.
sudo -H pip3 install speedtest-cliЗа примера можете дори да инсталирате спийдтест в Ubuntu директно от репозиториите. Това е същото python-приложение, което след това може да се стартира директно от конзолата.
sudo apt install speedtest-cli -yИ да извършите измервания на скоростта на интернет.
speedtest-cli
Изтегляне на конфигурация от speedtest.net...
Тестване от B***** (*.*.*.*)...
Изтегляне на списъка със сървъри на speedtest.net...
Избор на най-добрия сървър на базата на пинг...
Хостван от MTS (Москва) [0.12 км]: 11.8 ms
Тестване на скоростта на изтегляне................................................................................
Изтегляне: 7.10 Mbit/s
Тестване на скоростта на качване......................................................................................................
Качване: 3.86 Mbit/s
В резултат на това, как го направих. Трябваше да се вмъкна в изходния код на този спидтест, за да ги внедря напълно в проекта ми. Една от най-важните задачи е да получавам името на оператора с цел да го вмъкна в таблицата.
импортиране на speedtest
от datetime импортиране на datetime
...
#Указваме конкретен сървър за теста
#6053) MaximaTelecom (Москва, Руска федерация)
сървъри = ["6053"]
# Ако искате да използвате тест с един поток
поток = None
s = speedtest.Speedtest()
#получаваме името на оператора на мобилни мрежи
opos = '%(isp)s' % s.config['client']
s.get_servers(сървъри)
#получаваме текстова стринг с параметрите на сървъра
testserver = '%(sponsor)s (%(name)s) [%(d)0.2f km]: %(latency)s ms' % s.results.server
#тест за сваляне
s.download(поток=поток)
#тест за качване
s.upload(поток=поток)
#получаваме резултатите
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"
#тук записваме в лог файл
Тук също не се оказа толкова просто, макар че изглежда, че е много по-лесно. Първоначално параметърът servers беше равен [], моля, избери най-добрия сървър. В резултат на това имах произволни сървъри и, както не е трудно да се досетим, плаваща скорост. Това е достатъчно сложна тема, използването на фиксиран сървър, да, то статичен или динамичен, изисква проучване. Но ето пример на графики на измерванията на скоростта на оператора Билайн при динамичен избор на тестов сървър и статично фиксиран.

Резултат от измерването на скоростта при избора на динамичен сървър.

Резултат от теста на скоростта, при един строго избран сървър.
«Вълна» при тестването присъства и тук, и там, и трябва да се премахне математически. Но при фиксираният сървър, има малко по-малко и амплитудата е по-стабилна.
Всъщност това е място за големи изследвания. И бих измервал скоростта до своя сървър, чрез утилитата iperf. Но ние се придържаме към спецификацията.
Изпращане на имейл и грешки
За изпращане на имейл опитах няколко десетки различни варианта, но в крайна сметка спря на следното. Регистрирах пощенска кутия на yandex и след това взех . Проверих го и го интегрирах в програмата. В този пример се разглеждат различни варианти, включително изпращане с gmail и т.н. Не ми се струваше, че имам време да настройвам собствен пощенски сървър, но впоследствие се оказа, че е било напразно.
Изпращането на логове става чрез график, при наличие на свързаност, на всеки 6 часа: в 00:00, 06:00, 12:00 и 18:00. Изпращах по следния начин.
from send_email import *
...
message_log = "Логове за тестове на платка №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, "Логове за " + curdata + " " + сurtime, files)
except:
print("Проблем с мрежата при изпращане на имейл")
return False
return True
Грешките също се изпращаха в началото. Първоначално те се натрупваха в списък и после също се изпращаха с помощта на графика, при наличие на свързаност. Въпреки това по-късно се появиха проблеми, тъй като Yandex има ограничение на броя изпратени съобщения на ден (това е мъка, тъга и унижение). Понеже грешките можеха да бъдат в огромно количество дори за минута, затова се наложи да се откажа от изпращането на грешки по имейл. Затова имайте предвид, че при автоматично изпращане през услугите на Yandex има такъв проблем.
Сървър за обратна връзка
За да имам достъп до отдалечения хардуер и да мога да го настройвам и преконфигурирам, ми беше необходим външен сървър. Всъщност, за истинността, би било правилно да се изпращат всички данни на сървъра и в уеб интерфейса да се изграждат всички красиви графики. Но не всичко наведнъж.
Като VPS избрах . Можеше да избера най-простия сървър. И в общи линии за моите цели това щеше да е достатъчно. Но тъй като не плащах за сървъра от собствения си джоб, реших да взема с малко запас, за да стигне, ако започнем да изграждаме уеб интерфейс, собствен SMTP сървър, VPN и т.н. Плюс да имам възможност да настроя Telegram бот и да нямам проблеми с блокирането му. Затова избрах Амстердам и следните параметри.

Като начин за свързване с хардуера vim2 избрах обратно ssh свързване и, както показа практиката, не е най-доброто. При прекъсване на връзката сървърът задържа порта и за известно време е невъзможно да се свържете. Затова е все пак по-добре да се използват други методи за свързване, например VPN. В бъдеще исках да премина на VPN, но не успях.
Няма да се задълбочавам в детайлите на конфигуриране на защитната стена, ограничения на правата, деактивиране на SSH свързването на root и други основни настройки на VPS. Надявам се, че всичко вече знаете. За дистанционно свързване създавам нов потребител на сървера.
adduser vimsshНа нашето оборудване генерирам ключове за SSH свързване.
ssh-keygenИ ги копирам на нашия сървър.
ssh-copy-id vimssh@host.comНа нашето оборудване създавам автоматично свързване чрез обратен SSH при всяко зареждане.
[Unit]
Описание=Автоматичен обратен SSH
Изисква=systemd-networkd-wait-online.service
След=systemd-networkd-wait-online.service
[Service]
Потребител=khadas
ExecStart=\/usr\/bin\/ssh -NT -o ExitOnForwardFailure=yes -o ServerAliveInterval=60 -CD 8080 -R 8083:localhost:22 vimssh@host.com
RestartSec=5
Restart=винаги
[Install]
WantedBy=multi-user.target
Обърнете внимание на порта 8083: той определя по кой порт ще се осъществи свързването чрез обратен SSH. Добавяме в автозареждане и стартираме.
sudo systemctl enable autossh.service
sudo systemctl start autossh.serviceМоже дори да проверите статуса:
sudo systemctl status autossh.serviceСега, на нашия VPS-сървър, ако изпълните:
ssh -p 8083 khadas@localhostТогава попадам на моето тестово оборудване. И от устройството мога също така да изпращам логове и всякакви данни по SSH към моя сървър, което е много удобно.
Събираме всичко в едно

Започваме с разработване и отстраняване на проблеми
Ух, изглежда, че описах всичките узли. Сега е време да съберем всичко на едно място. Кодът може да бъде видян .
Важно нещо с кода: Този проект не може да стартира «просто така», тъй като е опростен за определена задача, на определена архитектура. Въпреки че предоставям изходния код, все пак най-ценната част ще бъде обяснена тук, направо в текста, иначе изобщо не е ясно.
В началото имам инициализация на GPS, GPIO и стартиране на отделен поток на планировчика.
#запуск потока планировщика
pShedulerThread = threading.Thread(target=ShedulerThread, args=(1,))
pShedulerThread.start()Планировчикът е доста прост: той следи дали е дошло времето за изпращане на съобщения и какъв е текущият статус на грешките. Ако има флаг на грешка, мигаме с 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)Най-сложният момент в този проект е да се запази обратното SSH свързване при всяко тестване. При всяко тестване се конфигурира отново шлюзът по подразбиране и DNS сървърът. Понеже никой не чете, знайте, че влакът не се движи по дървени релси. Който намери великденското яйце, ще получи бонбони.
За това създавам отделна таблица за маршрутизация —set-mark 0x2 и правило за пренасочване на трафика.
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")Повече за това как работи може да .
След това преминавам в безкраен цикъл, в който всеки път получавам списък с включени модеми (за да разбера дали конфигурацията на мрежата е променена).
network_list = getNetworklist()Получаването на списък с мрежови интерфейси е доста просто.
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След получаването на списъка, задавам IP адреси на всички интерфейси, както показах на картинката в главата за модема.
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")След това просто минавам през всеки интерфейс в цикъл. И конфигурирам всеки интерфейс.
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'")Проверявам интерфейса за работоспособност, ако мрежата не е налична, генерирам грешки. Ако мрежата е налична, време е за действие!
Тук настройвам SSH маршрутизация на дадения интерфейс (ако не е направено), изпращам грешки на сървъра, ако е време, изпращам логовете и накрая провеждам speedtest и записвам логовете в CSV файл.
if not NetworkAvalible():
....
#Тук формулираме грешките
....
else: #Има мрежа, ура, работим!
#Ако имаме проблемен интерфейс, на който е SSH, сменяме го
if (sshint == lastbanint or sshint == "free"):
print("********** Настройка на SSH ********************")
if sshint != "free":
cmd_run("sudo ip route del default via 192.168.8.1 dev " + sshint + " table 102")
SetupReverseSSH(iface)
sshint = iface
#ако мрежата работи, да изпратим всичко!!!
if ready_to_send:
print ("**** Готови за изпращане!!!")
if sendLogs():
ready_to_send = False
if error_status:
SendErrors()
#и изследваме скоростта и записваме логовете. Само трябва да се спомене функцията за настройка на обратен 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")Разбира се, необходимо е всичко това да се добави в автоматичното стартиране. За това създавам файл:
sudo vim /etc/systemd/system/modems_speedtest.serviceИ записвам в него:
[Unit]
Описание=Тест на скоростта на модема
Изисква=systemd-networkd-wait-online.service
След=systemd-networkd-wait-online.service
[Service]
Потребител=khadas
ExecStart=\/usr\/bin\/python3.6 \/home\/khadas\/modems_speedtest\/networks.py
RestartSec=5
Restart=винаги
[Install]
WantedBy=multi-user.target
Включвам автоматичното стартиране и стартирам!
sudo systemctl enable modems_speedtest.service
sudo systemctl start modems_speedtest.serviceСега мога да преглеждам логовете за това, което се случва, с командата:
journalctl -u modems_speedtest.service --no-pager -fРезултати
Така че най-важният въпрос е какво се случи в крайна сметка? Ще покажа няколко графики, които успях да заснема по време на разработката и отстраняването на проблеми. Графиките бяха създадени с помощта на gnuplot и следния скрипт.
#! /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"
Първият опит беше с оператора Tele2, който проведох в продължение на няколко дни.

Тук използвах динамичен измерващ сървър. Измерванията на скоростта работят, но значително варират, обаче все пак се вижда някаква средна стойност, която може да се получи чрез филтриране на данните, например с плъзгаща се средна стойност.
По-късно построих още редица графики за други оператори. Тестовият сървър в този случай беше един, и резултатите също бяха много интересни.




Както се вижда, темата е много обширна за изследване и обработка на тези данни и очевидно не се ограничава до няколко седмици работа. Но...
Заключение на работата
Работата беше внезапно прекратена по причини, независещи от мен. Един от слабите моменти на този проект, по мое субективно мнение, беше модемът, който не искаше да работи едновременно с други модеми и при всяко включване проявяваше различни проблеми. За тези цели съществува огромно количество други модели модеми, които обикновено имат формат Mini PCI-e и се инсталират вътре в устройството, и са значително по-лесни за конфигуриране. Но това вече е съвсем друга история. Проектът беше интересен и бях много радостен, че успях да участвам в него.
Източник: habr.com

