Одночасний speedtest на декількох LTE-модемах

На карантині мені запропонували взяти участь у розробці пристрою вимірювання швидкості LTE-модемів для кількох операторів мобільного зв'язку.

Одночасний speedtest на декількох LTE-модемах

Замовник хотів оцінити швидкість всіляких операторів зв'язку в різних географічних точках, щоб можна було зрозуміти, який оператор стільникового зв'язку йому найбільш оптимальний при встановленні обладнання, що використовує LTE-з'єднання, наприклад, для відеотрансляцій. При цьому завдання потрібно було вирішити максимально просто та дешево, без дорогого обладнання.

Відразу скажу, що завдання не найпростіше і наукомістке, розповім, які проблеми мені зустрілися і як я їх вирішував. Тож поїхали.

Примітка

Вимірювання швидкості LTE-з'єднання справа дуже складне: необхідно правильно вибрати обладнання та методику вимірювання, також добре представляти топологію та роботу стільникової мережі. Плюс на швидкість може впливати кілька факторів: кількість абонентів на стільнику, погодні умови, навіть від стільника до стільника швидкість може разюче відрізнятися через топологію мережі. Загалом це завдання з величезною кількістю невідомих, і її правильно вирішити може тільки оператор зв'язку.

Спочатку замовник хотів просто ганяти кур'єра з телефонами операторів, проводити вимірювання прямо на телефоні і далі записувати в зошит результати вимірювання швидкості. Моє рішення вимірювання швидкості мереж lte, хоч не ідеальне, але вирішує поставлене завдання.

Через брак часу я приймав рішення не на користь зручності чи практичності, а на користь швидкості розробки. Наприклад, для віддаленого доступу піднімався зворотний ssh ​​замість більш практичного vpn заради економії часу на налаштування сервера і кожного окремого клієнта.

Технічне завдання

Як сказано у статті Без ТЗ: чому клієнт не хоче його: Не працюйте без ТЗ! Ніколи, ніде!

Технічне завдання було досить простим, я трохи його розширю для розуміння кінцевого користувача. Вибір технічних рішень та обладнання продиктував замовник. Отже, саме ТЗ після всіх погоджень:

На базі одноплатного комп'ютера vim2 зробити тестер швидкості lte-з'єднання через модеми Huawei e3372h - 153 кількох операторів зв'язку (від одного до n). Також необхідно отримувати координати з GPS-приймача, підключеного за UART. Виміри швидкості проводити за допомогою сервісу www.speedtest.net і зводити їх у таблицю виду:

Одночасний speedtest на декількох LTE-модемах

Таблиця у форматі CSV. Після цього відсилати на е-майл кожні 6 годин цю табличку. У разі виникнення помилок блимати світлодіодом, підключеним до GPIO.

ТЗ я описав у вільній формі, після безлічі погоджень. Але зміст завдання вже видно. Термін на все було дано тиждень. Але насправді він розтягнувся на три тижні. Це з урахуванням того, що я робив це тільки після основної роботи та у вихідні.

Тут я хочу ще раз звернути увагу на те, що замовником було заздалегідь обумовлено використання сервісу вимірювання швидкості та апаратне забезпечення, що сильно обмежило мої можливості. Був також обмежений бюджет, тому особливо нічого не докуповувалося. Тож довелося грати за цими правилами.

Архітектура та розробка

Схема проста і очевидна. Тож залишу її без особливих коментарів.

Одночасний speedtest на декількох LTE-модемах

Весь проект вирішив реалізувати на python, незважаючи на те, що досвіду розробки цією мовою в мене не було зовсім. Вибрав його, оскільки була купа готових прикладів та рішень, які могли прискорити розробку. Тому, прошу всіх професійних програмістів не лаяти мого першого досвіду розробки на python, і завжди із задоволенням готовий почути конструктивну критику для підвищення свого скілла.

Також у процесі відкрив, що python має дві ходові версії 2 та 3, в результаті зупинився на третій.

Апаратні вузли

Одноплатник vim2

Як основна машина мені був дано одноплатник vim2

Одночасний speedtest на декількох LTE-модемах

Відмінний, потужний медіакомбайн для розумного будинку та SMART-TV, але на рідкість непридатний для даного завдання, або скажемо так, що слабо підходить. Наприклад, його головна ОС це Android, а Linux це попутна ОС, і відповідно ніхто не гарантує якісної роботи всіх вузлів і драйверів під Linux. І я припускаю, що частина проблем була пов'язана з драйверами USB цієї платформи, тому модеми працювали на цій платі не так, як очікував. Також у нього дуже погана і розрізнена документація, тому кожна операція займала багато часу копання в доках. Навіть звичайна робота з GPIO попила багато крові. Наприклад, щоб налаштувати роботу зі світлодіодом, мені знадобилося кілька годин. Але якщо бути об'єктивним, то принципово не було важливо, що за одноплатник, головне, щоб працював і були USB-порти.

Для початку мені потрібно встановити Linux на цю плату. Щоб не нишпорити всім по нетрях документації, а також для тих, хто розбиратиметься з цим одноплатником, пишу цей розділ.

Є два варіанти встановити лінукс: на зовнішню карту SD, або на внутрішню MMC. З картою я побився вечір, так і не вкурив як змусити працювати, тому вирішив встановлювати на MMC, хоча без сумніву із зовнішньою картою набагато простіше було б працювати.

Про прошивку криво розказано тут. Перекладаю з дивного російською. Для того щоб прошити плату, мені необхідно підключити апаратний UART. Підключав його наступним чином.

  • Tool Pin GND: <—> Pin17 з VIMs GPIO
  • Tool Pin TXD: <—> Pin18 з VIMs GPIO (Linux_Rx)
  • Tool Pin RXD: <—> Pin19 з VIMs GPIO (Linux_Tx)
  • Tool Pin VCC: <—> Pin20 of VIMs GPIO

Одночасний speedtest на декількох LTE-модемах

Після чого, я скачав прошивку звідси. Конкретна версія прошивки VIM1_Ubuntu-server-bionic_Linux-4.9_arm64_EMMC_V20191231.

Для того, щоб залити цю прошивку, мені потрібні потрібні утиліти. Докладніше про це розказано тут. Під Windows не пробував прошивати, а ось про прошивку під Linux треба кілька слів розповісти. Для початку встановлю утиліти, згідно з інструкцією.

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

ІІІ ... Нічого не працює. Витратив пару годин займаючись правками настановних скриптів, щоб усе коректно встановилося у мене. Що там робив не пам'ятаю, але теж ще той цирк із кіньми. Тож будьте обережні. Але без цих утиліт далі мучити vim2 сенсу немає. Краще з ним взагалі не зв'язуватись!

Після семи кіл пекла, конфігурації скриптів та установки отримав пакет працюючих утиліт. Підключив плату USB до мого комп'ютера лінукс, і так само підключений UART за схемою вище.
Налаштовую мій улюблений термінал minicom на швидкість 115200, без апаратного та програмного контролю помилок. І приступаємо.

Одночасний speedtest на декількох LTE-модемах

При завантаженні 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*, який по dhcp отримує ip адресу 192.168.8.100, і стандартний шлюз 192.168.8.1.

І найголовніший момент! Дана модель модему не вміє працювати в режимі саме модему, який управляється АТ-командами.. Все було б дуже простіше створити ppp-з'єднання на кожен модем і далі вже оперувати з ними. Але в моєму випадку "сам" (точніше дайвера Linux згідно з правилами udev), створює eth-інтерфейс і по dhcp призначають йому ip-адресу.

Щоб далі не плутатися, пропоную забути слово «модем» і говорити мережева карта та шлюз, бо по суті це як підключення нової мережевої карти зі шлюзом.
Коли один модем, це не викликає особливих проблем, але коли їх більше одного, а саме n-штук, виникає наступна картина мережі.

Одночасний speedtest на декількох LTE-модемах

Тобто n мережевих карт, з однією IP-адресою, у кожного той самий шлюз за замовчуванням. Але за фактом, кожен із них підключений до свого оператора.

Спочатку я мав просте рішення: за допомогою команди ifconfig або ip гасити всі інтерфейси і просто включати по черзі один і тестувати його. Рішення було всім добре, крім того, що в моменти комутації я не міг підключитися до пристрою. А оскільки комутації часті та швидкі, то фактично я не мав можливості підключитися взагалі.

Тому я вибрав шлях міняти "вручну" ip-адреси модемів і далі ганяти трафік за допомогою налаштувань маршрутизації.

Одночасний speedtest на декількох LTE-модемах

На цьому у мене проблеми з модемами не закінчилися: у разі проблем із харчуванням, вони відвалювалися, потрібно хороше стабільне харчування USB-хаба. Цю проблему вирішив жорстко припаяти харчування прямо до хаба. Інша проблема, з якою я зіткнувся і яка занапастила весь проект: після перезавантаження або холодного старту пристрою визначалися не всі модеми і не завжди, і чому це відбувалося і за яким алгоритмом мені встановити не вдалося. Але про все по порядку.

Для коректної роботи модему я встановив пакет usb-modeswitch.

sudo apt update
sudo apt install -y usb-modeswitch

Після цього модем після підключення буде коректно визначатися і конфігуруватися підсистемою udev. Перевіряю, просто підключивши модем і переконавшись, що з'явилася мережа.
Ще одна проблема, яку я не зміг вирішити: це як із цього модему отримати ім'я оператора, з яким ми працюємо? Ім'я оператора міститься на веб-інтерфейсі модему за адресою 192.168.8.1. Це динамічна веб-сторінка, яка отримує дані за допомогою ajax-запитів, тому просто wget-тнути сторінку і спарсити ім'я не вийде. Тому почав дивитися, як відпрацювати web-сторінку тощо, і зрозумів, що займаюся якоюсь дурницею. В результаті плюнув, і оператор почав отримувати за допомогою API самого Speedtest.

Багато було б простіше, якби модем був доступ через AT-команди. Можна було б його конфігурувати, створювати ppp-з'єднання, призначати IP, отримувати оператора зв'язку і т.д. Але, на жаль, працюю з тим, що дали.

GPS

GPS-приймач, який мені видали, мав інтерфейс UART та живлення. Це було не найкраще рішення, проте робоче і просте. Приймач був приблизно такого виду.

Одночасний speedtest на декількох LTE-модемах

Чесно кажучи, вперше працював із 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 daemon stop/disable */
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.

Одночасний speedtest на декількох LTE-модемах

У руках провід GPS, під пальцями видно проводи UART відладчика.

Перезавантажуюсь, і перевіряю роботу GPS за допомогою gpsmon.

Одночасний speedtest на декількох LTE-модемах

На цьому скріншоті супутників не видно, але видно спілкування з 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 ("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

Якщо мені потрібно отримати координати, то це робиться наступним викликом:

longitude, latitude = getPositionData(agps_thread)

І протягом 1-10 секунд я або отримаю координату, або ні. Так, у мене спроб отримати координати було десять. Не оптимально, криво та косо, але працює. Я вирішив це зробити, тому що GPS може ловити погано і не завжди отримувати дані. Якщо чекати на отримання даних, то у разі роботи в глухому приміщенні, програма зависне в цьому місці. Тому реалізував такий елегантний варіант.

В принципі, було б більше часу, можна було б безпосередньо UART отримувати дані з GPS, парсувати їх в окремому потоці і працювати з ними. Але часу було зовсім, звідси лютий некрасивий код. І так, мені не соромно.

світлодіод

З підключенням світлодіода було все просто та складно одночасно. Головна складність у тому, що номер піна в системі не відповідає номеру піна на платі, тому що документація написана лівою п'ятою. Щоб зіставити номер апаратного піна та номер піна в ОС, треба виконати команду:

gpio readall

Буде виведено таблицю відповідності піна в системі та на платі. Після чого я вже можу оперувати піном у самій ОС. У моєму випадку світлодіод підключений до GPIOH_5.

Одночасний speedtest на декількох LTE-модемах

Перекладаю пін GPIO у режим виведення.

gpio -g mode 421 out

Записую нуль.

gpio -g write 421 0

Записую одиницю.

gpio -g write 421 1

Одночасний speedtest на декількох LTE-модемах
Все горить після запису «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, подивитися можна на Github.

Чим добре, що є вихідні коди, які також можна подивитися. Як працювати з даними API (найпростіші приклади) можна подивитися в відповідному розділі.

Встановлюю python-бібліотеку наступною командою.

sudo -H pip3 install speedtest-cli

Для прикладу ви можете взагалі поставити спідтестер в Ubuntu прямо з реп. Це теж саме python-додаток, який потім можна запустити прямо з консолі.

sudo apt install speedtest-cli -y

І зробити виміри швидкості вашого інтернету.

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

В результаті, як це зробив я. Мені довелося влізти у вихідні коди цього спідтесту, щоб повніше впровадити їх у мій проект. Одне з найважливіших завдань — це отримувати ще ім'я оператора зв'язку для підстановки його в табличку.

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"
#тут идет запись в файл логов

Тут теж виявилося не так просто, хоча, здавалося б, куди простіше. Спочатку параметр servers у мене дорівнював []Вибери найкращий сервер. В результаті у мене були випадкові сервери, і як неважко здогадатися, плаваюча швидкість. Це досить складна тема, використовувати фіксований сервер, якщо так, то статичний чи динамічний вимагає дослідження. Але приклад графіків вимірів швидкості оператора Білайна при динамічному виборі тестового сервера і статично зафіксованого.

Одночасний speedtest на декількох LTE-модемах
Результат виміру швидкості при виборі динамічного сервера.

Одночасний speedtest на декількох LTE-модемах
Результат тестування швидкості при одному строго обраному одному сервері.

"Шерсть" при тестуванні є і там і там, і її потрібно прибирати математичними методами. Але при фіксованому сервері її трохи менше і амплітуда стабільніша.
Взагалі, це місце великих досліджень. І я б проводив виміри швидкості до свого сервера за допомогою утиліти iperf. Але ми дотримуємось від ТЗ.

Надсилання пошти та помилок

Для відправки пошти спробував кілька десятків різних варіантів, але в результаті зупинився на наступному. Зареєстрував поштову скриньку на yandex і далі взяв даний приклад відправки пошти. Перевірив його та впровадив у програму. У цьому прикладі розбираються різні варіанти, у тому числі відправка з gmail тощо. Вовтузиться з підняттям свого поштового сервера мені не хотілося і не було часу на це, але як потім виявилося марно.

Відправлення логів здійснював за планувальником, за наявності зв'язку, кожні 6 годин: о 00 годині, 06 ранку, 12 дні та 18 вечора. Відправляв так.

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

Помилки теж спочатку вирушали. Спочатку вони накопичувалися у списку, і потім відправляв також з допомогою планувальника, за наявності зв'язку. Однак потім виникли проблеми з тим, що yandex має обмеження на кількість повідомлень, що відправляються на добу (це біль, печаль і приниження). Оскільки помилок навіть за хвилину могла бути величезна кількість, відповідно від відправки помилок поштою довелося відмовитися. Тож майте на увазі, при автоматичному відправленні через сервіси яндекса про таку проблему.

Сервер зворотного зв'язку

Для того, щоб мати доступ до віддаленої залізниці та мати можливість її доналаштувати та переконфігурувати мені знадобився зовнішній сервер. Взагалі, справедливості кажучи, правильно було б усі дані надсилати на сервер та у веб-інтерфейсі будувати усі гарні графіки. Але не все одразу.

Як VPS я вибрав ruvds.com. Можна було б взяти найпростіший сервер. І в цілому для моїх цілей цього вистачило б за очі. Але оскільки платив за сервер не зі своєї кишені, вирішив взяти з невеликим запасом, щоб вистачило, якщо розгортатимемо web-інтерфейс, свій SMTP-сервер, vpn і т.д. Плюс мати можливість налаштувати Telegram-бота та не мати проблем із його блокуваннями. Тому вибрав Amsterdam і такі параметри.

Одночасний speedtest на декількох LTE-модемах

Як спосіб зв'язку із залізницею vim2 вибрав зворотне ssh з'єднання і як показала практика – не найкраще. При розриві з'єднання сервер утримує порт і по ньому неможливо підключитися деякий час. Тому, все ж таки краще використовувати інші способи зв'язку, наприклад vpn. У майбутньому хотів перейти на vpn, але не встиг.

Не буду вдаватися до подробиць налаштування файрвола, обмеження прав, відключення ssh з'єднання root та інші великі істини налаштування VPS. Хочеться вірити, що ви й так усе знаєте. Для віддаленого з'єднання створюю нового користувача на сервері.

adduser vimssh

На нашій залозці генерую ключі ssh з'єднання.

ssh-keygen

І копіюю їх на наш сервер.

ssh-copy-id [email protected]

На нашій залізці створюю автоматичне підключення зворотного ssh при кожному завантаженні.

[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

Зверніть увагу на порт 8083: він і визначає який порт у мене буде здійснюється підключення через зворотний ssh. Додаємо в автозавантаження та стартуємо.

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

Можна навіть переглянути статус:

sudo systemctl status autossh.service

Тепер, на нашому VPS-сервері, якщо виконати:

ssh -p 8083 khadas@localhost

То я попадаю на мою тестову залізку. І з залізниці можу так само відправляти логи та будь-які дані про ssh на мій сервер, що дуже зручно.

Збираємо все воєдино

Одночасний speedtest на декількох LTE-модемах
Включення, приступаємо до розробки та налагодження

Фух, ну начебто все, описав усі вузли. Тепер настав час зібрати все це в єдину купу. Код можна переглянути ось тут.

Важливий момент з кодом: Даний проект ось так «влоб» може не запуститися, оскільки заточувався на певне завдання, певної архітектури. Хоч я і даю вихідники, але все ж таки найцінніше розберу ось тут, прямо в тексті, інакше зовсім незрозуміло.

На початку у мене йде ініціалізація gps, gpio та запуск окремого потоку планувальника.

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

Планувальник досить простий: він дивиться, чи не настав час відправки повідомлень і який зараз стоїть статус помилок. Якщо є прапор помилки, то блимаємо світлодіодом.

#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("********** 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()
#и далее тестируем скорость и сохраняем логи. 

Хіба що варто сказати про функцію налаштування зворотного 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] 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

Включаю автозавантаження та стартую!

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, який я проводив протягом декількох днів.

Одночасний speedtest на декількох LTE-модемах

Тут я використав динамічний вимірювальний сервер. Виміри швидкості працюють, але дуже сильно плавають, проте все ж таки видно деяку середню величину, і її можна отримати, зробивши фільтрацію даних, наприклад, ковзним середнім.

Пізніше я побудував ще низку графіків для інших операторів зв'язку. Сервер тестування в цьому випадку вже був один, і результати також дуже цікаві.

Одночасний speedtest на декількох LTE-модемах

Одночасний speedtest на декількох LTE-модемах

Одночасний speedtest на декількох LTE-модемах

Одночасний speedtest на декількох LTE-модемах

Як видно тема дуже широка для досліджень та обробки цих даних, і явно не тягне на кілька тижнів роботи. Але...

підсумок роботи

Робота була різко завершена за незалежними від мене обставинами. Однією із слабких сторін даного проекту, на мій суб'єктивний погляд, був модем, який не дуже хотів працювати одночасно з іншими модемами, і при кожному завантаженні виробляв такі фортелі. Для цих цілей існує величезна кількість інших моделей модемів, зазвичай вони вже мають формат Mini PCI-e і ставляться всередину пристрою і їх дуже легко конфігурувати. Але це вже зовсім інша історія. Проект був цікавий і дуже радий, що вдалося в ньому взяти участь.

Одночасний speedtest на декількох LTE-модемах

Джерело: habr.com

Додати коментар або відгук