Prueba de velocidad simultánea en múltiples módems LTE

En cuarentena, me ofrecieron participar en el desarrollo de un dispositivo para medir la velocidad de los módems LTE para varios operadores móviles.







El cliente quería evaluar la velocidad de todo tipo de operadores de telecomunicaciones en diferentes ubicaciones geográficas, con el fin de comprender qué operador móvil es el más óptimo para él cuando instala equipos que utilizan una conexión LTE, por ejemplo, para transmisiones de video. Al mismo tiempo, la tarea debía resolverse de la manera más simple y económica posible, sin equipo costoso.



Debo decir de inmediato que la tarea no es la más simple y más intensiva en conocimiento, le diré qué problemas encontré y cómo los resolví. Entonces vamos.



Nota



Medir la velocidad de una conexión LTE es una cuestión muy difícil: debe elegir el equipo y el método de medición correctos, y también tener una buena idea de la topología y el funcionamiento de la red celular. Además, la velocidad puede verse influenciada por varios factores: la cantidad de suscriptores por celda, las condiciones climáticas, incluso de una celda a otra, la velocidad puede ser notablemente diferente debido a la topología de la red. En general, este es un problema con una gran cantidad de incógnitas, y solo un operador de telecomunicaciones puede resolverlo correctamente.



Inicialmente, el cliente solo quería conducir el servicio de mensajería con los teléfonos de los operadores, tomar medidas directamente en el teléfono y luego anotar los resultados de la medición de velocidad en un cuaderno. Mi solución para medir la velocidad de las redes lte, aunque no es la ideal, resuelve el problema.



Debido a la falta de tiempo, tomé decisiones no a favor de la conveniencia o la practicidad, sino a favor de la velocidad del desarrollo. Por ejemplo, para el acceso remoto, se generó ssh inverso, en lugar del vpn más práctico, para ahorrar tiempo en la configuración del servidor y de cada cliente individual.



Tarea técnica



Como se indica en el artículo Sin TK: por qué el cliente no lo quiere : ¡No trabaje sin TK! ¡Nunca, en ninguna parte!



La tarea técnica fue bastante simple, la ampliaré un poco para comprender al usuario final. La elección de soluciones técnicas y equipos fue dictada por el cliente. Entonces, el TK en sí, después de todas las aprobaciones:



vim2 lte- Huawei e3372h — 153 ( n). GPS-, UART. www.speedtest.net :







csv. - 6 . , GPIO.


Describí el TK de forma gratuita, después de muchas aprobaciones. Pero el significado de la tarea ya es visible. La fecha límite para todo fue dada una semana. Pero en realidad duró tres semanas. Esto se debe al hecho de que lo hice solo después del trabajo principal y los fines de semana.



Aquí me gustaría llamar su atención sobre el hecho de que el cliente había accedido previamente a utilizar el servicio y el hardware de medición de velocidad, lo que limitó en gran medida mis capacidades. El presupuesto también fue limitado, por lo que no se compró nada además. Así que tuve que jugar con estas reglas.



Arquitectura y desarrollo



El esquema es simple y directo. Por lo tanto, lo dejaré sin ningún comentario especial.







Decidí implementar todo el proyecto en Python, a pesar de que no tenía experiencia en el desarrollo de este lenguaje. Lo elegí porque había un montón de ejemplos listos y soluciones que podrían acelerar el desarrollo. Por lo tanto, les pido a todos los programadores profesionales que no regañen mi primera experiencia en el desarrollo de Python, y siempre estoy feliz de escuchar críticas constructivas para mejorar mi habilidad.



También en el proceso descubrí que Python tiene dos versiones en ejecución 2 y 3, como resultado, me detuve en la tercera.



Nodos de hardware



Single board vim2



Como máquina principal, me dieron un vim2 de placa única







Un medio excelente y potente se combina para un hogar inteligente y una SMART-TV, pero extremadamente inadecuado para esta tarea o, digamos, poco adecuado. Por ejemplo, su sistema operativo principal es Android, y Linux es un sistema operativo que pasa, y por lo tanto, nadie garantiza el funcionamiento de alta calidad de todos los nodos y controladores bajo Linux. Y supongo que algunos de los problemas estaban relacionados con los controladores USB de esta plataforma, por lo que los módems no funcionaron como se esperaba en esta placa. También tiene documentación muy pobre y dispersa, por lo que cada operación llevó mucho tiempo cavando en los muelles. Incluso el trabajo ordinario con GPIO bebió mucha sangre. Por ejemplo, me llevó varias horas configurar el trabajo con el LED. Pero, para ser objetivos, fundamentalmente no era importante qué tipo de dispositivo de placa única, lo principal es que debería funcionar y tener puertos USB.



Primero, necesito instalar Linux en esta placa. Para no rastrear la jungla de documentación para todos, así como para aquellos que se ocuparán de este dispositivo de placa única, estoy escribiendo este capítulo.



Hay dos opciones para instalar Linux: en una tarjeta SD externa o en una MMC interna. Luché con la tarjeta por la noche y no sabía cómo hacerlo funcionar, así que decidí instalarla en MMC, aunque sin duda sería mucho más fácil trabajar con una tarjeta externa.



El firmware se describe torcidamente aquí . Estoy traduciendo de extraño al ruso. Para actualizar la placa, necesito conectar el hardware UART. Lo conecté de la siguiente manera.



  • Pin de herramienta GND: <—> Pin17 del GPIO de VIM
  • Pin de herramienta TXD: <—> Pin18 del GPIO de VIM (Linux_Rx)
  • Pin de herramienta RXD: <—> Pin19 del GPIO de VIM (Linux_Tx)
  • Pin de herramienta VCC: <—> Pin20 del GPIO de VIM






Después de eso, descargué el firmware desde aquí . La versión de firmware específica es VIM1_Ubuntu-server-bionic_Linux-4.9_arm64_EMMC_V20191231 .



Para cargar este firmware, necesito ciertas utilidades. Lea más sobre esto aquí . No he intentado actualizarlo con Windows, pero necesito decir algunas palabras sobre el firmware de Linux. Primero, instalaré las utilidades de acuerdo con las instrucciones.



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


Iii ... Nada funciona. Pasé un par de horas editando los scripts de instalación para que todo se instalara correctamente para mí. No recuerdo lo que hice allí, sino también ese circo con caballos. Así que ten cuidado. Pero sin estas utilidades no tiene sentido torturar vim2 más. ¡Mejor no meterse con él en absoluto!



Después de siete círculos del infierno, configuración e instalación de scripts, obtuve un paquete de utilidades de trabajo. Conecté la placa a través de USB a mi computadora con Linux, y el UART también está conectado de acuerdo con el diagrama anterior.

Estoy configurando mi terminal minicom favorita en 115200, sin control de errores de hardware o software. Y empecemos.







Al iniciar VIM2 en el terminal UART, presiono cualquier tecla, por ejemplo, la barra espaciadora, para detener el inicio. Después de que aparezca la línea



kvim2# 


Entro el comando:



kvim2# run update


En el host desde donde descargamos, ejecuto:



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


Todos, uf. Le pregunté, hay Linux en el tablero. Nombre de usuario / contraseña khadas: khadas.



Después de eso, pequeñas configuraciones iniciales. Para más trabajo, deshabilito la contraseña para sudo (sí, no es segura, pero conveniente).



sudo visudo


Edito la línea a la vista y guardo



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


Luego cambio la ubicación actual para que la hora sea en Moscú, de lo contrario será GMT.



sudo timedatectl set-timezone Europe/Moscow


o



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


Si le resulta difícil, entonces no use esta placa, Raspberry Pi es mejor. Justa.



Módem Huawei e3372h - 153



Este módem bebió bien mi sangre y, de hecho, se convirtió en el cuello de botella de todo el proyecto. En general, el nombre "módem" para estos dispositivos no refleja en absoluto la esencia del trabajo: esta es una poderosa combinación, esta pieza de hardware tiene un dispositivo compuesto que pretende ser un CD-ROM para instalar los controladores, y luego pasa al modo de tarjeta de red.



Arquitectónicamente, desde el punto de vista de un usuario de Linux, después de todas las configuraciones, se ve así: después de conectar el módem, tengo la interfaz de red eth *, que recibe la dirección IP 192.168.8.100 a través de dhcp, y la puerta de enlace predeterminada 192.168.8.1.



¡Y el punto más importante! Este modelo de módem no sabe cómo funcionar en el modo de módem, que está controlado por comandos AT... Todo sería mucho más fácil, crear conexiones ppp para cada módem y luego operar con ellos. Pero en mi caso, "yo mismo" (más precisamente un buzo de Linux de acuerdo con las reglas de udev), crea una interfaz ética y le asigna una dirección IP a través de dhcp.



Para no confundirse más, propongo olvidar la palabra "módem" y decir una tarjeta de red y una puerta de enlace, porque de hecho es como conectar una nueva tarjeta de red con una puerta de enlace.

Cuando hay un módem, esto no causa ningún problema especial, pero cuando hay más de un módem, es decir, n-piezas, aparece la siguiente imagen de la red.







Es decir, n tarjetas de red, con la misma dirección IP, cada una con la misma puerta de enlace predeterminada. Pero, de hecho, cada uno de ellos está conectado a su propio operador.



Inicialmente, tenía una solución simple: usar el comando ifconfig o ip para extinguir todas las interfaces y solo activar una por vez y probarla. La solución fue buena para todos, excepto que durante los momentos de cambio, no pude conectarme al dispositivo. Y dado que el cambio es frecuente y rápido, de hecho, no tuve oportunidad de conectarme en absoluto.



Por lo tanto, elegí la forma de cambiar "manualmente" las direcciones IP de los módems y continuar generando tráfico utilizando la configuración de enrutamiento.







Este no fue el final de mis problemas con los módems: en caso de problemas de alimentación, se cayeron, se requirió una buena fuente de alimentación estable para el concentrador USB. Este problema se resolvió soldando la fuente de alimentación directamente al concentrador. Otro problema que enfrenté y que arruinó todo el proyecto: después de un reinicio o un arranque en frío del dispositivo, no se detectaron todos los módems y no siempre, y por qué sucedió esto y qué algoritmo no pude instalar. Pero lo primero es lo primero.



Para que el módem funcione correctamente, instalé el paquete usb-modeswitch.



sudo apt update
sudo apt install -y usb-modeswitch


Después de eso, el subsistema udev detectará y configurará correctamente el módem después de la conexión. Lo verifico simplemente conectando el módem y asegurándome de que la red esté activa.

Otro problema que no pude resolver: ¿cómo obtener el nombre del operador con el que trabajamos desde este módem? El nombre del operador está contenido en la interfaz web del módem en 192.168.8.1. Esta es una página web dinámica que recibe datos a través de solicitudes ajax, por lo que no puede simplemente mover la página y analizar el nombre. Entonces comencé a buscar cómo elaborar una página web, etc., y me di cuenta de que estaba haciendo algún tipo de tontería. Como resultado, escupió y comenzó a recibir al operador utilizando la API de Speedtest.



Muchas cosas serían más fáciles si se pudiera acceder al módem mediante comandos AT. Sería posible reconfigurarlo, crear una conexión ppp, asignar una IP, obtener un operador, etc. Pero, por desgracia, trabajo con lo que di.



GPS



El receptor GPS que me dieron tenía una interfaz UART y poder. No era la mejor solución, pero, sin embargo, funcionaba y era simple. El receptor era algo así.







Para ser honesto, esta fue la primera vez que trabajé con un receptor GPS, pero como era de esperar, todo fue inventado por nosotros hace mucho tiempo. Así que solo usamos soluciones ya hechas.



Primero, enciendo uart_AO_B (UART_RX_AO_B, UART_TX_AO_B) para conectar el GPS.



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


Luego verifico el éxito de la operación.



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


Este comando, aparentemente, edita devtree sobre la marcha, lo cual es muy conveniente.



Después del éxito de esta operación, reinicie e instale el demonio gps.



khadas@Khadas:~$ sudo reboot


Instalando el demonio gps. Instalo todo y lo corto inmediatamente para una mayor configuración.



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


Edición del archivo de configuración.



sudo vim /etc/default/gpsd


Instalo el UART en el que se colgará el GPS.



DEVICES="/dev/ttyS4"


Y luego encendemos todo y comenzamos.



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


Después de eso, conecto el GPS.







El cable del GPS está en las manos, los cables UART del depurador son visibles debajo de los dedos.



Reinicio y verifico el GPS usando el programa gpsmon.







En esta captura de pantalla no puede ver los satélites, pero puede ver la comunicación con el receptor GPS, y esto dice que todo está bien.



En python probé muchas opciones para trabajar con este demonio, pero me decidí por la que funcionaba correctamente con python 3.



Instale la biblioteca requerida.



sudo -H pip3 install gps3 


Y esculpo el código de trabajo.



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


Si necesito obtener las coordenadas, esto se hace mediante la siguiente llamada:



longitude, latitude = getPositionData(agps_thread)


Y dentro de 1-10 segundos obtendré la coordenada o no. Sí, tuve diez intentos de obtener las coordenadas. No es óptimo, torcido y torcido, pero funciona. Decidí hacer esto, porque el GPS puede capturar mal y no siempre recibir datos. Si espera a que se reciban los datos, si trabaja en una habitación remota, el programa se congelará en este lugar. Por lo tanto, implementé una opción no tan elegante.



En principio, habría más tiempo, sería posible recibir datos del GPS directamente a través de UART, analizarlos en una secuencia separada y trabajar con ellos. Pero no había tiempo en absoluto, de ahí el código feroz y feo. Y sí, no me da vergüenza.



Diodo emisor de luz



Conectar el LED fue todo simple y complicado al mismo tiempo. La principal dificultad es que el número de pin en el sistema no corresponde al número de pin en la pizarra y porque la documentación está escrita con el talón izquierdo. Para hacer coincidir el número de pin de hardware y el número de pin en el sistema operativo, debe ejecutar el comando:



gpio readall


Se mostrará una tabla de correspondencia de pines en el sistema y en el tablero. Después de eso, ya puedo operar el pin en el sistema operativo. En mi caso, el LED está conectado a GPIOH_5 .







Transfiero el pin GPIO al modo de salida.



gpio -g mode 421 out


Escribo cero.



gpio -g write 421 0


Escribo uno.



gpio -g write 421 1




Todo está encendido, después de grabar "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)


Ahora, en caso de errores, llamo error_blink () y el LED parpadea muy bien para nosotros.



Nodos de software



API de Speedtest



Es una gran alegría que el servicio speedtest.net tenga su propia API de Python, puede verlo en Github .



La buena noticia es que hay códigos fuente que también se pueden ver. Cómo trabajar con esta API (los ejemplos más simples) se pueden encontrar en la sección correspondiente .



Instala la biblioteca python con el siguiente comando.



sudo -H pip3 install speedtest-cli


Por ejemplo, incluso puede instalar el probador de velocidad en Ubuntu directamente desde el representante. Esta es la misma aplicación de Python que puede ejecutar directamente desde la consola.



sudo apt install speedtest-cli -y


Y mida la velocidad de su Internet. Como resultado, como lo hice. Tuve que profundizar en los códigos fuente de esta prueba de velocidad para implementarlos más completamente en mi proyecto. Una de las tareas más importantes es obtener el nombre del operador de telecomunicaciones para sustituirlo en la placa.



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


Aquí también, todo resultó no ser tan simple, aunque, al parecer, mucho más fácil. Inicialmente, el parámetro de los servidores era igual a [] , dicen, elija el mejor servidor. Como resultado, tenía servidores aleatorios y, como se podría adivinar, velocidad flotante. Este es un tema bastante complejo, usar un servidor fijo, si es así, entonces estático o dinámico, requiere investigación. Pero aquí hay un ejemplo de gráficos para medir la velocidad del operador Beeline con una selección dinámica de un servidor de prueba y uno estáticamente fijo.





El resultado de medir la velocidad al elegir un servidor dinámico.





Resultado de la prueba de velocidad, con un servidor estrictamente seleccionado.



La "lana" durante las pruebas está allí y allí, y debe eliminarse por métodos matemáticos. Pero con un servidor fijo es un poco menos y la amplitud es más estable.

En general, este es un lugar de gran investigación. Y mediría la velocidad de mi servidor usando la utilidad iperf. Pero nos atenemos a los conocimientos tradicionales.



Envío de correos y errores



Probé varias docenas de opciones diferentes para enviar correo, pero como resultado me decidí por lo siguiente. Registré un buzón en Yandex y luego tomé este ejemplo de envío de correo . Lo revisé y lo implementé en el programa. Este ejemplo explora varias opciones, incluido el envío desde gmail, etc. No quería molestarme en levantar mi servidor de correo y no tuve tiempo para esto, pero como resultó más tarde, también fue en vano.



Los registros se enviaban de acuerdo con el planificador, si había una conexión , cada 6 horas: a las 00 horas, 06 am, 12 del mediodía y 18 pm. Lo envié de la siguiente manera.



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("Network problem for send mail")
		return False
	return True


Los errores también se presentaron inicialmente. Para empezar, se acumularon en la lista y luego los enviaron también usando el programador, si había una conexión. Sin embargo, hubo problemas con el hecho de que Yandex tiene un límite en la cantidad de mensajes enviados por día (esto es dolor, tristeza y humillación). Como podría haber una gran cantidad de errores incluso en un minuto, tuvieron que negarse a enviar errores por correo. Por lo tanto, tenga en cuenta al enviar automáticamente a través de los servicios de Yandex sobre tal problema.



Servidor de comentarios



Para tener acceso a una pieza remota de hardware y poder ajustarla y reconfigurarla, necesitaba un servidor externo. En general, para ser justos, sería correcto enviar todos los datos al servidor y construir todos los hermosos gráficos en la interfaz web. Pero no todos a la vez.



Elegí ruvds.com como VPS . Se puede tomar el servidor más simple. Y en general, para mis propósitos, esto sería suficiente para los ojos. Pero como no estaba pagando el servidor de mi bolsillo, decidí tomarlo con un pequeño margen, de modo que sería suficiente si implementamos una interfaz web, nuestro propio servidor SMTP, vpn, etc. Además, para poder configurar un bot de Telegram y no tener problemas para bloquearlo. Por lo tanto, elegí Amsterdam y los siguientes parámetros.







Como una forma de comunicarse con una pieza de hardware, vim2 eligió una conexión ssh inversa, y como la práctica ha demostrado, no es la mejor. Si la conexión se interrumpe, el servidor retiene el puerto y es imposible conectarse a través de él durante algún tiempo. Por lo tanto, aún es mejor usar otros métodos de comunicación, por ejemplo vpn. En el futuro, quería cambiar a vpn, pero no tuve tiempo.



No entraré en los detalles de configurar un firewall, limitar los derechos, deshabilitar las conexiones raíz ssh y otras verdades comunes de la configuración de VPS. Me gustaría creer que ya lo sabes todo. Para una conexión remota, creo un nuevo usuario en el servidor.



adduser vimssh


En nuestro hardware, genero claves de conexión ssh.



ssh-keygen


Y los copio a nuestro servidor.



ssh-copy-id vimssh@host.com


En nuestra pieza de hardware, creo una conexión automática del ssh inverso en cada arranque. Preste atención al puerto 8083: determina en qué puerto me conectaré a través de ssh inverso. Agregar al inicio y comenzar.



[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 vimssh@host.com

RestartSec=5

Restart=always

[Install]

WantedBy=multi-user.target








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


Incluso puedes ver el estado:



sudo systemctl status autossh.service


Ahora, en nuestro servidor VPS, si ejecuta:



ssh -p 8083 khadas@localhost


Luego llego a mi pieza de prueba. Y desde la pieza de hierro también puedo enviar registros y cualquier información a través de ssh a mi servidor, lo cual es muy conveniente.



Poniendolo todo junto









Encendiendo, comenzamos a desarrollar y depurar Fuh, bueno, todo parece haber descrito todos los nodos. Ahora es el momento de poner todo junto. El código se puede ver aquí .



Un punto importante con el código: este proyecto como este "vlob" puede no iniciarse, ya que se agudizó en una tarea específica de una arquitectura específica. Aunque doy el código fuente, aún analizaré el más valioso aquí, justo en el texto, de lo contrario es completamente incomprensible.



Al principio, tengo la inicialización de gps, gpio y el lanzamiento de un hilo del planificador separado.



#  
pShedulerThread = threading.Thread(target=ShedulerThread, args=(1,))
pShedulerThread.start()


El programador es bastante simple: busca ver si ha llegado el momento de enviar mensajes y cuál es el estado del error ahora. Si hay un indicador de error, parpadee el 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)


El punto más difícil en este proyecto es mantener la conexión ssh inversa en cada prueba. En cada prueba, la puerta de enlace predeterminada y el servidor DNS se reconfiguran. Como nadie lee de todos modos, tenga en cuenta que el tren no rueda sobre rieles de madera. Quien encuentre un huevo de Pascua será goloso.



Para hacer esto, creo una tabla de enrutamiento separada: set-mark 0x2 y una regla para redirigir el tráfico.



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


Puede leer más sobre cómo funciona esto en este artículo .



Luego entro en un bucle sin fin, donde cada vez que obtenemos una lista de módems conectados (para averiguar si la configuración de la red ha cambiado).



network_list = getNetworklist()


Obtener una lista de interfaces de red es bastante sencillo.



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


Después de recibir la lista, asigno direcciones IP a todas las interfaces, como lo mostré en la imagen del capítulo sobre el módem.



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


Luego solo paso por cada interfaz en un bucle. Y configuro cada interfaz.



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


Verifico la operabilidad de la interfaz, si no hay red, entonces genero errores. Si hay una red, ¡es hora de actuar!



Aquí configuro el enrutamiento ssh para esta interfaz (si no se hizo), envío errores al servidor, si ha llegado el momento, envío registros y finalmente ejecuta una prueba de velocidad y guarda los registros en un archivo 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()
#      . 


Excepto por la función de configuración ssh inversa.



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


Y, por supuesto, debe agregar toda esta belleza al inicio. Para hacer esto, creo un archivo:



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


Y escribo en él: ¡enciendo la carga automática y empiezo!



[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


Ahora puedo ver los registros de lo que está sucediendo con el comando:



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


resultados



Bueno, ahora lo más importante es lo que sucedió como resultado. Aquí hay algunos gráficos que logré capturar durante el proceso de desarrollo y depuración. Los gráficos fueron construidos usando gnuplot con el siguiente 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 primera experiencia fue el operador Tele2, que pasé varios días.







Usé un servidor de medición dinámica aquí. Las mediciones de velocidad funcionan, pero flotan mucho, sin embargo, todavía se puede ver algún valor promedio y se puede obtener filtrando los datos, por ejemplo, con un promedio móvil.



Más tarde construí una serie de gráficos para otros operadores de telecomunicaciones. En este caso, ya había un servidor de prueba, y los resultados también son muy interesantes.



















Como puede ver, el tema es muy extenso para la investigación y el procesamiento de estos datos, y claramente no dura un par de semanas de trabajo. Pero…



Resultado del trabajo



El trabajo se completó abruptamente debido a circunstancias fuera de mi control. Una de las debilidades de este proyecto, en mi opinión subjetiva, fue el módem, que realmente no quería trabajar simultáneamente con otros módems, e hizo un truco con cada arranque. Para estos fines, hay una gran cantidad de otros modelos de módems, generalmente ya tienen el formato Mini PCI-e, están instalados dentro del dispositivo y son mucho más fáciles de configurar. Pero esa es una historia completamente diferente. El proyecto fue interesante y me alegré mucho de haber logrado participar en él.






All Articles