Офисный переключатель интернета
Провели нам в офис вторую линию интернета. Так как основная (дальше я буду называть её первой) хоть и хороша по-скорости, но ограничена по трафику. Вторая немного медленнее, но безлимитна. Днём вторая линия почти свободна и выдаёт хорошую скорость, поэтому и была выбрана основной для рабочего дня. К вечеру скорость сильно падает из-за нагрузки на канал и приходится переключаться на первую. Так бывает не всегда, но достаточно часто.Потому возник вопрос в переключении линий. Роутером у нас трудится обычный компьютер с FreeBSD на борту. Городить хитрую логику проверки скорости канала смысла не было, к тому же нужна была индикация активного подключения. Настроив переключение каналов на консольные команды с помощью sh скриптов в папке /bin, возникли 2 проблемы:1. Доступ к консоли только у админа, а он не всегда на месте, да и дёргать ради переключения каналов тоже не очень удобно.2. Нет индикации актвного, на данный момент, подключения.Поэтому было принято решение сделать переключатель физическим и снабдить идикаторами.ПроектИз имеющегося на данный момент в наличии железа, а именно кучки AVR Atmega 8A и FT232RL, было решено сделать переключатель, который подключается к USB порту и простым переключением тумблера меняет активный канал на другой. А так же светодиодом показывает тот, который сейчас выбран.Логика работы устройства очень проста: Несколько раз в секунду atmega проверяет состояние входов на порту C и передаёт это состояние через UART в виде символа A для первого канала и B для второго. К UART МК подключен преобразователь на FT232RL, который пересылае этот символ через USB в виртуальный COM порт сервера. На сервере работает простой демон написанный на питоне, который в случае изменения канала, выполняет команду переключения (смена шлюза по-умолчанию и статичных маршрутов, но это уже выходит за рамки данной статьи).Демон запускается вместе с системой, поэтому для него написан rc скрипт.Но обо всём по-порядку.Подготовка Исходя из задачи в пакете DipTrace была сделана схема: U1 это FT232RL, U3 — Atmega 8.S1 — тумблер переключения, который так же переключает состояние светодиода-индикатора активного канала.Примечение уже после сброки узнал про свинью, которую FTDI подложила всем пользователям их чипов, так что в следующий раз дважды задумаюсь над применением их продукции. Так как отличить оригинал от подделки практически невозможно, то самым лучшим будет не играть в эту лотерею. Но так как в наличии осталось несколько их чипов, то решил делать всё-таки на них. Забегая вперёд скажу, что пляски с драйверами избежать не удалось, но в итоге удалось заставить эти чипы работать. C FreeBSD же проблем не возникло, так как там старый драйвер.По этой схеме была разведена плата под имеющийся корпус: Далее встал вопрос как делать плату. Можно, конечно, старым дедовским ЛУТом. Но есть способ лучше: заказать у китайцев. На хабре достаточно подробно описывался процесс заказа, и в почта России тоже частенько стала радовать своей работой.DipTrace умеет делать экспорт в формат Gerber, поэтому проблем с заказом не возникло.Сборка Через пару недель получив посылку с платами на почте, можно приступать к сборке. Так как схема очень проста, то и проблем со сброкой не возникло: Замечу только, что SMD компоненты паял с помощью паяльной пасты и фена. Так удобнее и получается намного качественней чем паяльником. Особенно при пайке такой мелочи как FT232.
Настройка Во FreeBSD есть драйвер для чипов FTDI, поэтому проблем с подключением не возникло. Единственно, что потребовалось включить загрузку модуля ядра. В файле /boot/loader.conf прописать: uftdi_load=«YES» Atmega была прошита с помощью avrdude, пропатченным для работы с ft232, прямо через USB подключение.Исходник прошивки /* * net_switch.c * * Created: 09.09.2014 10:07:41 * Author: exp131 */ #define F_CPU 1000000UL // Частота 1МГц, внутренний кварц #define BAUD 2400 // Скорость UART, 2400 хватает за глаза #define MYUBRR F_CPU/16/BAUD-1
#include
// Так как не использую файл с заголовками, то описываю функцию тут void ReportStatus ();
// Обработчик прерывания по таймеру ISR (TIMER0_OVF_vect) { ReportStatus (); // Вызываем отправку состояния переключателя }
// Функция проверки входов 0 и 1, порта С и выдача в UART соответствующего символа
void ReportStatus ()
{
cli (); // Отключим прерывания на время передачи
unsigned char a;
if ((PINC & (1< //Инициализация
void init (void)
{
// Настройки UART, включена передача и установлена скорость 2400
UCSRB = (1< int main (void)
{
// Тут инициализируемся
init ();
while (1) // и запускаем основной цикл
{
}
}
В качестве базы для демона нашёл в Сети класс на питоне. К сожалению ссылка на источник не сохранилась, просто приведу тут код. Код класса демона
#!/usr/bin/env python import sys, os, time, atexit
from signal import SIGTERM class Daemon:
def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
self.stdin = stdin
self.stdout = stdout
self.stderr = stderr
self.pidfile = pidfile
def demonize (self):
try:
pid = os.fork ()
if pid > 0:
sys.exit (0)
except OSError, e:
sys.stderr.write («fork #1 failed: %d (%s)\n» % (e.errno, e.strerror))
sys.exit (1)
os.chdir (»/»)
os.setsid ()
os.umask (0)
sys.stdout.flush ()
sys.stderr.flush ()
si = file (self.stdin, 'r')
so = file (self.stdout, 'a+')
se = file (self.stderr, 'a+', 0)
os.dup2(si.fileno (), sys.stdin.fileno ())
os.dup2(so.fileno (), sys.stdout.fileno ())
os.dup2(se.fileno (), sys.stderr.fileno ())
atexit.register (self.delpid)
pid = str (os.getpid ())
file (self.pidfile, 'w+').write (»%s\n» % pid)
def delpid (self):
os.remove (self.pidfile)
def start (self):
try:
pf = file (self.pidfile, 'r')
pid = int (pf.read ().strip ())
pf.close ()
except IOError:
pid = None
if pid:
message = «Pidfile %s already exists. Deamon already running?\n»
sys.stderr.write (message % self.pidfile)
sys.exit (1)
self.demonize ()
self.run ()
def stop (self):
try:
pf = file (self.pidfile, 'r')
pid = int (pf.read ().strip ())
pf.close ()
except IOError:
pid = None
if not pid:
message = «Pidfile %s does not exists. Daemon is not running?\n»
sys.stderr.write (message % self.pidfile)
return
try:
while 1:
os.kill (pid, SIGTERM)
time.sleep (0.1)
except OSError, err:
err = str (err)
if err.find («No such process») > 0:
if os.path.exists (self.pidfile):
os.remove (self.pidfile)
else:
print str (err)
sys.exit (1)
def restart (self):
self.stop ()
self.start ()
def run (self):
»«
Need to be overriden
»«
На базе этого класса был написан простой демон который слушает указанный в конфиге порт и выполняет, в зависимости от состояния преключателя, либо команду А, либо команду В.Конфиг
[global]
port=/dev/cuaU1
rate=2400
log=/var/log/net_switch.log
cmdA=/bin/vist
cmdB=/bin/unico
А вот и код самого демона: Код демона
#!/usr/bin/env python import sys, os, time, serial, ConfigParser
from daemon import Daemon class NetSwitch (Daemon): def run (self):
file (self.logfile, 'a+').write («Net switch started\n»)
while True:
ser = serial.Serial (self.port, self.rate, timeout=1)
x = ser.read ()
if not x == self.state:
self.state = x
if x == 'A':
os.system (self.cmdA)
else:
os.system (self.cmdB)
file (self.logfile, 'a+').write («State changed %s\n» % x)
time.sleep (0.2)
def loadConfig (self, configPath):
try:
config = ConfigParser.RawConfigParser ()
config.read (configPath)
self.port = config.get ('global', 'port')
self.rate = config.getint ('global', 'rate')
self.logfile = config.get ('global','log')
self.cmdA = config.get ('global', 'cmdA')
self.cmdB = config.get ('global', 'cmdB')
self.state = 'A'
return True
except:
return False
if __name__ == »__main__»:
daemon = NetSwitch ('/var/run/net_switch.pid')
if len (sys.argv) == 2:
if 'start' == sys.argv[1]:
print «Usage: %s start path_to_config» % sys.argv[0]
elif 'stop' == sys.argv[1]:
daemon.stop ()
elif 'restart' == sys.argv[1]:
daemon.restart ()
else:
print «Unknown command»
sys.exit (2)
sys.exit (0)
elif len (sys.argv) == 3:
if 'start' == sys.argv[1]:
configPath = sys.argv[2]
if daemon.loadConfig (configPath):
daemon.start ()
else:
print «Unable to load config file\n»
else:
print «Usage %s start path_to_config» % sys.argv[0]
else:
print «Usage: %s start|stop|restart» % sys.argv[0]
sys.exit (2)
И в заключении, для того, чтобы демон стартовал вместе с системой, был написан rc скрипт: RC скрипт для запуска
#!/bin/sh . /etc/rc.subr name=net_switch
rcvar=`set_rcvar` #reading the config
load_rc_config $name
: ${net_switch_enable:=«NO»}
: ${net_switch_config:=»/usr/local/etc/net_switch/config.conf»} pidfile=»/var/run/net_switch.pid»
command=»/usr/local/sbin/${name}.py» start_cmd=«start_cmd»
stop_cmd=«stop_cmd»
restart_cmd=«restart_cmd» start_cmd ()
{
${command} start ${net_switch_config}
} stop_cmd ()
{
${command} stop
} restart_cmd ()
{
${command} restart
} run_rc_command »$1»
Для того, чтобы демон мог стартовать вместе с загрузкой системы, нужно в /etc/rc.conf добавить строку:
net_switch_enable=«YES»
Заключение
В результате получилось забавное, но функциональное устройство. Теперь любой сотрудник находящийся ближе к серверу может переключить активный канал в случае каких-либо проблем. Так же светодиоды показывают какой из каналов на данный момент активен. Он разного цвета (зеленый и красный), так что из далека видно каким подключением пользуемся.И в заключении хочу сказать, что у меня ещё остались платы под это устройство, так как при заказе меньше 10 цена всё равно не меняется, так что заказал сразу 10 штук. Удобно на случай если какая-то плата будет загублена в результате кривых рук при монтаже. Если кого заинтересовал этот девайс и есть желание собрать что-то подобное — пишите в личку, готов поделиться платами.