27.06.2017 Сервер Статистики aka Автоматизация измерений Эпизод II
Ustinov (обсуждение | вклад) (Новая страница: «<summary [ hidden ]> <center>300px</center> '''''Сервера Статистики УИЦ СРТТ''''' </summary> Данная страни…») |
Dneprov (обсуждение | вклад) (→Что за Сервер Статистики?) |
||
(не показаны 58 промежуточных версий 1 участника) | |||
Строка 1: | Строка 1: | ||
<summary [ hidden ]> | <summary [ hidden ]> | ||
− | <center>[[ | + | <center>[[File:StatServer.PNG|400px]]</center> |
− | + | Встречайте нового работника - Сервер Статистики УИЦ СРТТ! | |
</summary> | </summary> | ||
− | + | [[File:StatServer.PNG|right|600px]] | |
+ | '''Д'''анная страница продолжает дело создания Сервера Статистики и написана по мотивам нескольких статей: [https://www.srns.ru/wiki/Blog:Boldenkov/06.04.2017_%D0%90%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D0%B8%D0%B7%D0%BC%D0%B5%D1%80%D0%B5%D0%BD%D0%B8%D0%B9 этой], [https://www.srns.ru/wiki/Blog:Boldenkov/10.04.2017_%D0%9F%D0%BE%D0%B4%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B5_Javad_%D1%87%D0%B5%D1%80%D0%B5%D0%B7_%D1%81%D0%B5%D1%82%D1%8C этой] и [https://www.srns.ru/wiki/Blog:Boldenkov/12.05.2017_Headless_Ubuntu этой]. | ||
+ | |||
+ | Написана она для того, чтобы подвести итог и чтобы потом не забыть, как это сделано и работает сейчас. | ||
Пути улучшения, как всегда, есть, но это дело будущего. | Пути улучшения, как всегда, есть, но это дело будущего. | ||
− | == Что за Сервер Статистики | + | == Что за Сервер Статистики? == |
− | '''Сервер Статистики''' - отдельно выделенный компьютер, работающий круглосуточно, к которому подключены различные приемники, в свою очередь, решающиеся по одной антенне. На данный момент подключены Javad Lexon GGD, Piksi и наш Oryx в походном исполнении <s> | + | '''Сервер Статистики''' - отдельно выделенный компьютер, работающий круглосуточно, к которому подключены различные приемники, в свою очередь, решающиеся по одной антенне. На данный момент подключены '''Javad Lexon GGD''', '''Swift Navigation Piksi''' и наш '''Oryx''' в <s>серой коробке</s> походном исполнении. Далее в статье Oryx фигурирует под мейнстримовым названием '''MCR'''. IP сервера [https://srns.ru/wiki/%D0%A1%D0%BF%D0%B8%D1%81%D0%BE%D0%BA_IP '''192.168.0.94'''], кодовое имя '''Evaluator''', пользователь '''nsl''', пароль известен. |
+ | |||
+ | == Чего он там делает? == | ||
+ | На протяжении суток Сервер Статистики пишет логи себе в ''/tmp'', а в начале каждых суток (00:01) решается по ним, строит картинки, делает некоторые статистические (вот почему Сервер Статистики, а не логов) вычисления и шлет это на заданные почтовые адреса. | ||
+ | |||
+ | После этого для Javad и Oryx '''логи''' копируются в папку ''/home/CommonFiles/logs'', затем стираются. Для Piksi, в настоящее время, логи слишком тяжелы, потому копируется только один, уже обработанный, файл - '''piksi.obs'''. | ||
+ | |||
+ | Необходимые '''скрипты''' находятся в папке ''/home/CommonFiles/scripts''. | ||
+ | |||
+ | Необходимые '''программы''' в папке ''/home/CommonFiles/progs''. | ||
+ | |||
+ | == Как заставить приемники делиться логами? == | ||
+ | Для этого существуют скрипты. | ||
+ | |||
+ | '''Javad''' не имеет ethernet возможностей, потому для него два скрипта. | ||
+ | |||
+ | Первый, настраивает приемник и запускает трансляцию данных в сеть - ''broadcast_javad:'' | ||
+ | <source lang="bash"> | ||
+ | #!/bin/bash | ||
+ | |||
+ | PORT=/dev/ttyUSB0 | ||
+ | |||
+ | stty -F $PORT raw | ||
+ | stty -F $PORT 115200 cs8 -parenb -cstopb -ixon | ||
+ | |||
+ | echo "em,,def:{1,,}" > $PORT | ||
+ | echo "em,,jps/gd" > $PORT | ||
+ | echo "em,,jps/qd" > $PORT | ||
+ | echo "em,,jps/WD" > $PORT | ||
+ | |||
+ | nc -l -p 3500 < $PORT | ||
+ | </source> | ||
+ | |||
+ | Второй скрипт пишет логи - ''start_javad_log:'' | ||
+ | <source lang="bash"> | ||
+ | #!/bin/bash | ||
+ | |||
+ | IP=127.0.0.1 | ||
+ | TcpPort=3500 | ||
+ | |||
+ | DIR=/tmp/ | ||
+ | FILE=javad.log | ||
+ | |||
+ | curl $IP:$TcpPort > $DIR/$FILE | ||
+ | </source> | ||
+ | |||
+ | |||
+ | '''MCR''' транслирует по сети протокол BINR через порт 3491. Для записи логов - ''start_mcr_log:'' | ||
+ | <source lang="bash"> | ||
+ | #!/bin/bash | ||
+ | |||
+ | IP=192.168.0.163 | ||
+ | TcpPort=3491 | ||
+ | |||
+ | DIR=/tmp/ | ||
+ | FILE=mcr.log | ||
+ | |||
+ | curl $IP:$TcpPort > $DIR/$FILE | ||
+ | </source> | ||
+ | |||
+ | |||
+ | '''Piksi''' пишет лог с помощью GUI программы ''swift_console'' в формате JSON, скрипт ''start_piksi_log:'' | ||
+ | <source lang="bash"> | ||
+ | #!/bin/bash | ||
+ | |||
+ | IP=192.168.0.222 | ||
+ | TcpPort=55555 | ||
+ | DIR=/tmp | ||
+ | FILE=piksi.log | ||
+ | |||
+ | echo /home/CommonFiles/progs/swift_console_v1.0.A_linux/console -t -p $IP:$TcpPort -l -o $DIR --logfilename $FILE | ||
+ | DISPLAY=:1 /home/CommonFiles/progs/swift_console_v1.0.A_linux/console -t -p $IP:$TcpPort -l -o $DIR --logfilename $FILE | ||
+ | </source> | ||
+ | Сервер Статистики не имеет экрана, но должен запускать GUI приложение, по крайней мере на данный момент так. При запуске GUI приложений выдаются ошибки об отсутствии дисплея и завершении программы, это несколько странно, ведь наш сервер живет и вроде бы здравствует без экрана, а здесь в качестве выхода из ситуации поднят виртуальный X-сервер, который называется '''Xvfb''', он загружается автоматически при старте системы. Сделано это [https://www.srns.ru/wiki/Blog:Boldenkov/12.05.2017_Headless_Ubuntu так]. Соответственно, при запуске ''swift_console'' принудительно указывается этот виртуальный экран - '''DISPLAY=:1'''. | ||
+ | |||
+ | Посмотреть на виртуальный экран можно командой: | ||
+ | <source lang="bash"> | ||
+ | ssvncviewer 192.168.0.94:5900 | ||
+ | </source> | ||
+ | |||
+ | [[File:swift_console.png|600px]] | ||
+ | |||
+ | Транслирующий изображение '''x11vnc ''' также запускается автоматически, но иногда умудряется просто прекращать свою работу, тогда необходимо запустить скрипт ''run_vnc:'' | ||
+ | <source lang="bash"> | ||
+ | #!/bin/bash | ||
+ | |||
+ | x11vnc -display :1 -bg -nopw -listen 192.168.0.94 -xkb | ||
+ | </source> | ||
+ | |||
+ | Доступные сетевые сервисы Сервера Статистики, в том числе проверить работу ''ssvnc'', можно командой: | ||
+ | <source lang="bash"> | ||
+ | nmap -O 192.168.0.94 | ||
+ | </source> | ||
+ | [[File:VNC_StatServer.png|400px]] | ||
+ | |||
+ | == И что теперь делать с этими логами? == | ||
+ | Обработать. | ||
+ | |||
+ | Шаг 1. Конвертировать собранные логи в RINEX формат, поможет ''convert_all_to_rinex'': | ||
+ | <source lang="bash"> | ||
+ | #!/bin/bash | ||
+ | |||
+ | DIR=/tmp | ||
+ | |||
+ | JAVAD=$DIR/javad.log | ||
+ | MCR=$DIR/mcr.log | ||
+ | PIKSI=$DIR/piksi.log | ||
+ | |||
+ | echo Javad: $JAVAD | ||
+ | convbin $JAVAD -r javad | ||
+ | |||
+ | echo MCR: $MCR | ||
+ | convbin $MCR -r nvs | ||
+ | |||
+ | echo Piksi: $PIKSI | ||
+ | export PIKSI_OUT=`echo $PIKSI | sed 's/\.log/\.obs/'` | ||
+ | /home/CommonFiles/progs/piksi_tools/piksi_tools/sbp2rinex.py $PIKSI -o $PIKSI_OUT | ||
+ | |||
+ | echo "All convert!" | ||
+ | </source> | ||
+ | Для Javad и MCR используется скомпилированная программа '''convbin''' из RTKLib, для Piksi используется '''sbp2rinex.py''', предоставляемая Swift Navigation. | ||
+ | |||
+ | Шаг 2. Рассчитать решение, ''solve_all_from_rinex'': | ||
+ | <source lang="bash"> | ||
+ | #!/bin/bash | ||
+ | |||
+ | DIR=/tmp | ||
+ | |||
+ | JAVAD=$DIR/javad.obs | ||
+ | MCR=$DIR/mcr.obs | ||
+ | PIKSI=$DIR/piksi.obs | ||
+ | |||
+ | NAV=`echo $MCR | sed 's/\.obs/\.nav/g'` | ||
+ | GNAV=`echo $MCR | sed 's/\.obs/\.gnav/g'` | ||
+ | |||
+ | REF="2846044.0 2200316.0 5249376.0" | ||
+ | |||
+ | OUT=`echo $JAVAD | sed 's/\.obs/\.pos/g'` | ||
+ | echo "Javad: $JAVAD -> $OUT" | ||
+ | rnx2rtkp -r $REF -p 0 $JAVAD $NAV $GNAV -e > $OUT | ||
+ | |||
+ | OUT=`echo $MCR | sed 's/\.obs/\.pos/g'` | ||
+ | echo "MCR: $MCR -> $OUT" | ||
+ | rnx2rtkp -r $REF -p 0 $MCR $NAV $GNAV -e > $OUT | ||
+ | |||
+ | OUT=`echo $PIKSI | sed 's/\.obs/\.pos/g'` | ||
+ | echo "Piksi: $PIKSI -> $OUT" | ||
+ | rnx2rtkp -r $REF -p 0 $PIKSI $NAV $GNAV -e > $OUT | ||
+ | |||
+ | echo "All solve!" | ||
+ | </source> | ||
+ | Используется скомпилированная программа '''rnx2rtkp''' из RTKLib, '''-r REF''' - задается опорная точка, '''-p 0''' - задается режим работы (0 - single), '''-e''' - формат выдачи данных (e - X/Y/Z ECEF). | ||
+ | |||
+ | == Когда уже статистика? == | ||
+ | Вот теперь. | ||
+ | |||
+ | Визуализируем полученные данные, ''plot_all'': | ||
+ | <source lang="bash"> | ||
+ | #!/bin/bash | ||
+ | |||
+ | DIR=/tmp | ||
+ | |||
+ | JAVAD=$DIR/javad.pos | ||
+ | MCR=$DIR/mcr.pos | ||
+ | PIKSI=$DIR/piksi.pos | ||
+ | |||
+ | JAVAD_OUT=`echo $JAVAD | sed 's/\.pos/\.plane/g'` | ||
+ | echo "$JAVAD -> $JAVAD_OUT" | ||
+ | cat $JAVAD | grep -v "^%" | awk '{print $3" "$4}' > $JAVAD_OUT | ||
+ | #cat $JAVAD | grep -v "^%" | awk '{print $3$4$5" "$6$7$8}' > $JAVAD_OUT | ||
+ | |||
+ | MCR_OUT=`echo $MCR | sed 's/\.pos/\.plane/g'` | ||
+ | echo "$MCR -> $MCR_OUT" | ||
+ | cat $MCR | grep -v "^%" | awk '{print $3" "$4}' > $MCR_OUT | ||
+ | #cat $MCR | grep -v "^%" | awk '{print $3$4$5" "$6$7$8}' > $MCR_OUT | ||
+ | |||
+ | PIKSI_OUT=`echo $PIKSI | sed 's/\.pos/\.plane/g'` | ||
+ | echo "$PIKSI -> $PIKSI_OUT" | ||
+ | cat $PIKSI | grep -v "^%" | awk '{print $3" "$4}' > $PIKSI_OUT | ||
+ | #cat $PIKSI | grep -v "^%" | awk '{print $3$4$5" "$6$7$8}' > $PIKSI_OUT | ||
+ | |||
+ | #pl $MCR_OUT_SKIP $PIKSI_OUT_SKIP $JAVAD_OUT_SKIP | ||
+ | |||
+ | SKIP=1000 | ||
+ | |||
+ | MCR_OUT_SKIP=`echo $MCR_OUT | sed 's/\.plane/\.plane_skip/g'` | ||
+ | echo "$MCR_OUT -> $MCR_OUT_SKIP" | ||
+ | cat $MCR_OUT | tail -n +$SKIP > $MCR_OUT_SKIP | ||
+ | |||
+ | JAVAD_OUT_SKIP=`echo $JAVAD_OUT | sed 's/\.plane/\.plane_skip/g'` | ||
+ | echo "$JAVAD_OUT -> $JAVAD_OUT_SKIP" | ||
+ | cat $JAVAD_OUT | tail -n +$SKIP > $JAVAD_OUT_SKIP | ||
+ | |||
+ | PIKSI_OUT_SKIP=`echo $PIKSI_OUT | sed 's/\.plane/\.plane_skip/g'` | ||
+ | echo "$PIKSI_OUT -> $PIKSI_OUT_SKIP" | ||
+ | cat $PIKSI_OUT | tail -n +$SKIP > $PIKSI_OUT_SKIP | ||
+ | |||
+ | octave --eval "graphics_toolkit('gnuplot'); | ||
+ | fid=fopen(\"$MCR_OUT_SKIP\", 'r'); | ||
+ | mcr_plot=fscanf(fid, '%f %f\n', [2 Inf]); | ||
+ | fclose(fid); | ||
+ | fid=fopen(\"$JAVAD_OUT_SKIP\", 'r'); | ||
+ | javad_plot=fscanf(fid, '%f %f\n', [2 Inf]); | ||
+ | fclose(fid); | ||
+ | fid=fopen(\"$PIKSI_OUT_SKIP\", 'r'); | ||
+ | piksi_plot=fscanf(fid, '%f %f\n', [2 Inf]); | ||
+ | fclose(fid); | ||
+ | fig = figure(); | ||
+ | set (fig, 'visible', 'off'); | ||
+ | mcr_plot(1,:) = mcr_plot(1,:)-2846044; piksi_plot(1, :) = piksi_plot(1, :)-2846044; javad_plot(1, :) = javad_plot(1, :)-2846044; | ||
+ | mcr_plot(2,:) = mcr_plot(2,:)-2200316; piksi_plot(2, :) = piksi_plot(2, :)-2200316; javad_plot(2, :) = javad_plot(2, :)-2200316; | ||
+ | plot(mcr_plot(1, :), mcr_plot(2, :),'r-*', piksi_plot(1, :), piksi_plot(2, :),'g-+', javad_plot(1, :), javad_plot(2, :),'b-x'); | ||
+ | grid on; set (gca, 'FontSize', 14); | ||
+ | MCR_smpl = length(mcr_plot); piksi_smpl = length(piksi_plot); javad_smpl = length(javad_plot); | ||
+ | samples_str = sprintf('Solutions statistics [MCR: %d, Piksi: %d, Javad: %d]', MCR_smpl, piksi_smpl, javad_smpl); | ||
+ | title (samples_str); | ||
+ | xlabel('X, m'); | ||
+ | ylabel('Y, m'); | ||
+ | box('off'); | ||
+ | Std_MCR = sqrt((norm(mcr_plot,'fro')^2)/MCR_smpl); | ||
+ | Std_Piksi = sqrt((norm(piksi_plot,'fro')^2)/piksi_smpl); | ||
+ | Std_Javad = sqrt((norm(javad_plot,'fro')^2)/javad_smpl); | ||
+ | MCR_lgn = sprintf('MCR: %0.2f m', Std_MCR); | ||
+ | Piksi_lgn = sprintf('Piksi: %0.2f m', Std_Piksi); | ||
+ | Javad_lgn = sprintf('Javad: %0.2f m', Std_Javad); | ||
+ | lgn = legend(MCR_lgn,Piksi_lgn,Javad_lgn,'Location','southeast'); | ||
+ | time = strftime ('%Y-%m-%d_%H_%M_%S', localtime (time ())); | ||
+ | mess = sprintf('%s.png', time); | ||
+ | tic () | ||
+ | print (fig,mess,'-S1024,768','-dpngcairo'); | ||
+ | toc ()" | ||
+ | |||
+ | echo "All plot!" | ||
+ | </source> | ||
+ | Скрипт может откинуть начальные измерения, например, чтобы исключить начальные искажения, в данном случае откидываются '''SKIP''' 1000 начальных измерений. | ||
+ | |||
+ | В построении графиков поможет '''Octave'''. В него парсятся данные из созданных выше файликов, строятся графики, подсчитывается количество измерений и рассчитывается стандартное отклонение измерений относительно опорной точки по каждому приемнику. | ||
+ | [[File:Std_StatServer.PNG|300px]] | ||
+ | |||
+ | '''n''' - количество накопленных измерений. | ||
+ | |||
+ | Получается картинка примерного такого вида: | ||
+ | |||
+ | [[File:2017-06-27_00_08_30.png|600px]] | ||
+ | |||
+ | Достаточно важным моментом здесь является выбор "рендера" и выдаваемого формата файла: | ||
+ | <source lang="bash"> | ||
+ | graphics_toolkit('gnuplot') | ||
+ | ... | ||
+ | print (fig,mess,'-S1024,768','-dpngcairo'); | ||
+ | </source> | ||
+ | '''Cairo''' - наиболее быстрый (проверено экспериментально) способ вывода. Работает только при ''graphics_toolkit('gnuplot')''. Иначе, процесс сохранения картинки занимает баснословное количество времени. | ||
+ | |||
+ | == Где искать эти картинки? == | ||
+ | Картинки сохраняются в папку к скопированным логам ''/home/CommonFiles/logs''. | ||
+ | |||
+ | == И всё? == | ||
+ | Нет, еще картинки отправляются по почте, следующим образом ''send_mail'': | ||
+ | <source lang="bash"> | ||
+ | #!/bin/bash | ||
+ | |||
+ | ATTACHMENT=`find -name '*.png'` | ||
+ | echo $ATTACHMENT | ||
+ | echo " " | mailx -r "srtt@evaluator (Daily solutions)" -s "Solutions statics" -a $ATTACHMENT ****@gmail.com | ||
+ | echo " " | mailx -r "srtt@evaluator (Daily solutions)" -s "Solutions statics" -a $ATTACHMENT *******@srns.ru | ||
+ | cp $ATTACHMENT /home/CommonFiles/logs | ||
+ | rm -rf $ATTACHMENT | ||
+ | echo "Email successfully sent!" | ||
+ | </source> | ||
+ | После отправки картинка копируется в папку ''logs'' и удаляется. | ||
+ | |||
+ | == Как очищаются логи? == | ||
+ | Инквизицией! Шутка. Пока идет запись логов, их файлы защищены на запись, следовательно, придется прервать запись, убив фоновые процессы, скрипт ''kill_logs_pid'': | ||
+ | <source lang="bash"> | ||
+ | #!/bin/bash | ||
+ | |||
+ | pid_log=`ps aux | grep '\(start_\|swift\|curl\|broadcast_\)'| awk '{print $2}'` | ||
+ | kill $pid_log | ||
+ | echo "Pids killed!" | ||
+ | </source> | ||
+ | |||
+ | Скрипт очищающий логи ''refresh_logs'': | ||
+ | <source lang="bash"> | ||
+ | #!/bin/bash | ||
+ | |||
+ | Data=$(date +%Y-%m-%d) | ||
+ | Time=$(date +%H_%M_%S) | ||
+ | mkdir /home/CommonFiles/logs/$Data"_"$Time | ||
+ | cp /tmp/javad.* /home/CommonFiles/logs/$Data"_"$Time | ||
+ | cp /tmp/mcr.* /home/CommonFiles/logs/$Data"_"$Time | ||
+ | cp /tmp/piksi.obs /home/CommonFiles/logs/$Data"_"$Time | ||
+ | cp /dev/null /tmp/javad.log | ||
+ | cp /dev/null /tmp/mcr.log | ||
+ | cp /dev/null /tmp/piksi.log | ||
+ | echo "Logs refreshed!" | ||
+ | </source> | ||
+ | При вызове скрипта фиксируется дата и время, после чего логи для Javad и MCR копируются в соответствующую папку в ''/home/CommonFiles/logs/''. Для Piksi копируются только уже обработанный файл - '''piksi.obs'''. | ||
+ | |||
+ | == Хочу один скрипт для вызова всего! == | ||
+ | И он есть у нас. Один скрипт, чтобы править всеми. Его название ''process_all'': | ||
+ | <source lang="bash"> | ||
+ | #!/bin/bash | ||
+ | |||
+ | convert_all_to_rinex | ||
+ | solve_all_from_rinex | ||
+ | plot_all | ||
+ | send_mail | ||
+ | kill_logs_pid | ||
+ | refresh_logs | ||
+ | echo "All done!" | ||
+ | </source> | ||
+ | |||
+ | |||
+ | '''UPD'''. Время показало, что пока Piksi пишет "тяжелые" логи, они долго обрабатываются. В данном случае пришлось разделить скрипты. Обработка логов ''conv_n_solv'': | ||
+ | <source lang="bash"> | ||
+ | #!/bin/bash | ||
+ | |||
+ | convert_all_to_rinex | ||
+ | solve_all_from_rinex | ||
+ | </source> | ||
+ | |||
+ | От этого немного похудел ''process_all'': | ||
+ | <source lang="bash"> | ||
+ | #!/bin/bash | ||
+ | |||
+ | plot_all | ||
+ | send_mail | ||
+ | kill_logs_pid | ||
+ | refresh_logs | ||
+ | echo "All done!" | ||
+ | </source> | ||
+ | |||
+ | == А где же автоматизация? == | ||
+ | Автоматизируется Сервер Статистики программированием его расписания с помощью планировщика '''crontab''', для этого необходимо указать ему расписание, | ||
+ | <source lang="bash"> | ||
+ | crontab ./cron | ||
+ | </source> | ||
+ | заполненное специальным образом ''cron'': | ||
+ | <source lang="bash"> | ||
+ | SHELL=/bin/bash | ||
+ | MAILTO=srtt | ||
+ | PATH=/sbin:/bin:/usr/sbin:/usr/bin:/home/CommonFiles/scripts:/home/CommonFiles/progs/RTKlib: | ||
+ | 00 00 * * * "bash -c `conv_n_solv`" | ||
+ | 00 01 * * * "bash -c `process_all`" | ||
+ | 15 01 * * * "bash -c `broadcast_javad`" | ||
+ | 16 01 * * * "bash -c `start_javad_log`" | ||
+ | 17 01 * * * "bash -c `start_mcr_log`" | ||
+ | 18 01 * * * "bash -c `start_piksi_log`" | ||
+ | </source> | ||
+ | Обязательно указывается пользователь '''MAILTO''', от которого будут выполнены запланированные команды, и пути нахождения необходимых программ '''PATH'''. | ||
+ | Предполагается, что ранее сбор логов был запущен и 00:00 уже пойдет над ними работа. | ||
+ | |||
+ | Формат записи строчки расписания такой: | ||
+ | <source lang="bash"> | ||
+ | 01 00 * * * "bash -c `process_all`" | ||
+ | минута час день_месяца месяц день_недели команда | ||
+ | </source> | ||
+ | Первое число обозначает минуту, второе час, то есть запуск всех процессов произойдет в одни минуту первого ночи. Звездочку вместо числа следует трактовать как слово ''каждый'', то есть ''каждого дня месяца'', ''каждого месяца'', ''каждого дня недели''. Тонкости настройки ''crontab'' легко ищутся в интернете, например, [http://help.ubuntu.ru/wiki/cron вот] и [http://devacademy.ru/posts/15-otlichnykh-primierov-dlia-sozdaniia-cron-zadach-v-linux/ вот]. | ||
+ | |||
+ | Соответственно, скрипты на запись логов запускаются в 01:00, потому что процессы обработки могут затягиваться, часа точно должно хватить. | ||
+ | |||
+ | Посмотреть текущее расписание можно командой: | ||
+ | <source lang="bash"> | ||
+ | crontab -l | ||
+ | </source> | ||
+ | Удалить текущее расписание: | ||
+ | <source lang="bash"> | ||
+ | crontab -r | ||
+ | </source> | ||
+ | |||
+ | |||
+ | |||
+ | '''P.S.''' | ||
+ | |||
+ | Вот примерно так всё и работает! | ||
+ | |||
+ | [[Категория:HOWTO]] [[Категория:Сервер Статистики]] [[Категория:Oryx]] [[Категория:испытания и эксперименты]] | ||
+ | {{wl-publish: 2017-06-28 10:17:25 +0300 | Ustinov }} |
Текущая версия на 16:18, 5 декабря 2019
Данная страница продолжает дело создания Сервера Статистики и написана по мотивам нескольких статей: этой, этой и этой.
Написана она для того, чтобы подвести итог и чтобы потом не забыть, как это сделано и работает сейчас.
Пути улучшения, как всегда, есть, но это дело будущего.
Содержание |
[править] Что за Сервер Статистики?
Сервер Статистики - отдельно выделенный компьютер, работающий круглосуточно, к которому подключены различные приемники, в свою очередь, решающиеся по одной антенне. На данный момент подключены Javad Lexon GGD, Swift Navigation Piksi и наш Oryx в серой коробке походном исполнении. Далее в статье Oryx фигурирует под мейнстримовым названием MCR. IP сервера 192.168.0.94, кодовое имя Evaluator, пользователь nsl, пароль известен.
[править] Чего он там делает?
На протяжении суток Сервер Статистики пишет логи себе в /tmp, а в начале каждых суток (00:01) решается по ним, строит картинки, делает некоторые статистические (вот почему Сервер Статистики, а не логов) вычисления и шлет это на заданные почтовые адреса.
После этого для Javad и Oryx логи копируются в папку /home/CommonFiles/logs, затем стираются. Для Piksi, в настоящее время, логи слишком тяжелы, потому копируется только один, уже обработанный, файл - piksi.obs.
Необходимые скрипты находятся в папке /home/CommonFiles/scripts.
Необходимые программы в папке /home/CommonFiles/progs.
[править] Как заставить приемники делиться логами?
Для этого существуют скрипты.
Javad не имеет ethernet возможностей, потому для него два скрипта.
Первый, настраивает приемник и запускает трансляцию данных в сеть - broadcast_javad:
PORT=/dev/ttyUSB0
stty -F $PORT raw
stty -F $PORT 115200 cs8 -parenb -cstopb -ixon
echo "em,,def:{1,,}" > $PORT
echo "em,,jps/gd" > $PORT
echo "em,,jps/qd" > $PORT
echo "em,,jps/WD" > $PORT
nc -l -p 3500 < $PORT
Второй скрипт пишет логи - start_javad_log:
IP=127.0.0.1
TcpPort=3500
DIR=/tmp/
FILE=javad.log
curl $IP:$TcpPort > $DIR/$FILE
MCR транслирует по сети протокол BINR через порт 3491. Для записи логов - start_mcr_log:
IP=192.168.0.163
TcpPort=3491
DIR=/tmp/
FILE=mcr.log
curl $IP:$TcpPort > $DIR/$FILE
Piksi пишет лог с помощью GUI программы swift_console в формате JSON, скрипт start_piksi_log:
IP=192.168.0.222
TcpPort=55555
DIR=/tmp
FILE=piksi.log
echo /home/CommonFiles/progs/swift_console_v1.0.A_linux/console -t -p $IP:$TcpPort -l -o $DIR --logfilename $FILE
DISPLAY=:1 /home/CommonFiles/progs/swift_console_v1.0.A_linux/console -t -p $IP:$TcpPort -l -o $DIR --logfilename $FILE
Сервер Статистики не имеет экрана, но должен запускать GUI приложение, по крайней мере на данный момент так. При запуске GUI приложений выдаются ошибки об отсутствии дисплея и завершении программы, это несколько странно, ведь наш сервер живет и вроде бы здравствует без экрана, а здесь в качестве выхода из ситуации поднят виртуальный X-сервер, который называется Xvfb, он загружается автоматически при старте системы. Сделано это так. Соответственно, при запуске swift_console принудительно указывается этот виртуальный экран - DISPLAY=:1.
Посмотреть на виртуальный экран можно командой:
Транслирующий изображение x11vnc также запускается автоматически, но иногда умудряется просто прекращать свою работу, тогда необходимо запустить скрипт run_vnc:
x11vnc -display :1 -bg -nopw -listen 192.168.0.94 -xkb
Доступные сетевые сервисы Сервера Статистики, в том числе проверить работу ssvnc, можно командой:
[править] И что теперь делать с этими логами?
Обработать.
Шаг 1. Конвертировать собранные логи в RINEX формат, поможет convert_all_to_rinex:
DIR=/tmp
JAVAD=$DIR/javad.log
MCR=$DIR/mcr.log
PIKSI=$DIR/piksi.log
echo Javad: $JAVAD
convbin $JAVAD -r javad
echo MCR: $MCR
convbin $MCR -r nvs
echo Piksi: $PIKSI
export PIKSI_OUT=`echo $PIKSI | sed 's/\.log/\.obs/'`
/home/CommonFiles/progs/piksi_tools/piksi_tools/sbp2rinex.py $PIKSI -o $PIKSI_OUT
echo "All convert!"
Для Javad и MCR используется скомпилированная программа convbin из RTKLib, для Piksi используется sbp2rinex.py, предоставляемая Swift Navigation.
Шаг 2. Рассчитать решение, solve_all_from_rinex:
DIR=/tmp
JAVAD=$DIR/javad.obs
MCR=$DIR/mcr.obs
PIKSI=$DIR/piksi.obs
NAV=`echo $MCR | sed 's/\.obs/\.nav/g'`
GNAV=`echo $MCR | sed 's/\.obs/\.gnav/g'`
REF="2846044.0 2200316.0 5249376.0"
OUT=`echo $JAVAD | sed 's/\.obs/\.pos/g'`
echo "Javad: $JAVAD -> $OUT"
rnx2rtkp -r $REF -p 0 $JAVAD $NAV $GNAV -e > $OUT
OUT=`echo $MCR | sed 's/\.obs/\.pos/g'`
echo "MCR: $MCR -> $OUT"
rnx2rtkp -r $REF -p 0 $MCR $NAV $GNAV -e > $OUT
OUT=`echo $PIKSI | sed 's/\.obs/\.pos/g'`
echo "Piksi: $PIKSI -> $OUT"
rnx2rtkp -r $REF -p 0 $PIKSI $NAV $GNAV -e > $OUT
echo "All solve!"
Используется скомпилированная программа rnx2rtkp из RTKLib, -r REF - задается опорная точка, -p 0 - задается режим работы (0 - single), -e - формат выдачи данных (e - X/Y/Z ECEF).
[править] Когда уже статистика?
Вот теперь.
Визуализируем полученные данные, plot_all:
DIR=/tmp
JAVAD=$DIR/javad.pos
MCR=$DIR/mcr.pos
PIKSI=$DIR/piksi.pos
JAVAD_OUT=`echo $JAVAD | sed 's/\.pos/\.plane/g'`
echo "$JAVAD -> $JAVAD_OUT"
cat $JAVAD | grep -v "^%" | awk '{print $3" "$4}' > $JAVAD_OUT
#cat $JAVAD | grep -v "^%" | awk '{print $3$4$5" "$6$7$8}' > $JAVAD_OUT
MCR_OUT=`echo $MCR | sed 's/\.pos/\.plane/g'`
echo "$MCR -> $MCR_OUT"
cat $MCR | grep -v "^%" | awk '{print $3" "$4}' > $MCR_OUT
#cat $MCR | grep -v "^%" | awk '{print $3$4$5" "$6$7$8}' > $MCR_OUT
PIKSI_OUT=`echo $PIKSI | sed 's/\.pos/\.plane/g'`
echo "$PIKSI -> $PIKSI_OUT"
cat $PIKSI | grep -v "^%" | awk '{print $3" "$4}' > $PIKSI_OUT
#cat $PIKSI | grep -v "^%" | awk '{print $3$4$5" "$6$7$8}' > $PIKSI_OUT
#pl $MCR_OUT_SKIP $PIKSI_OUT_SKIP $JAVAD_OUT_SKIP
SKIP=1000
MCR_OUT_SKIP=`echo $MCR_OUT | sed 's/\.plane/\.plane_skip/g'`
echo "$MCR_OUT -> $MCR_OUT_SKIP"
cat $MCR_OUT | tail -n +$SKIP > $MCR_OUT_SKIP
JAVAD_OUT_SKIP=`echo $JAVAD_OUT | sed 's/\.plane/\.plane_skip/g'`
echo "$JAVAD_OUT -> $JAVAD_OUT_SKIP"
cat $JAVAD_OUT | tail -n +$SKIP > $JAVAD_OUT_SKIP
PIKSI_OUT_SKIP=`echo $PIKSI_OUT | sed 's/\.plane/\.plane_skip/g'`
echo "$PIKSI_OUT -> $PIKSI_OUT_SKIP"
cat $PIKSI_OUT | tail -n +$SKIP > $PIKSI_OUT_SKIP
octave --eval "graphics_toolkit('gnuplot');
fid=fopen(\"$MCR_OUT_SKIP\", 'r');
mcr_plot=fscanf(fid, '%f %f\n', [2 Inf]);
fclose(fid);
fid=fopen(\"$JAVAD_OUT_SKIP\", 'r');
javad_plot=fscanf(fid, '%f %f\n', [2 Inf]);
fclose(fid);
fid=fopen(\"$PIKSI_OUT_SKIP\", 'r');
piksi_plot=fscanf(fid, '%f %f\n', [2 Inf]);
fclose(fid);
fig = figure();
set (fig, 'visible', 'off');
mcr_plot(1,:) = mcr_plot(1,:)-2846044; piksi_plot(1, :) = piksi_plot(1, :)-2846044; javad_plot(1, :) = javad_plot(1, :)-2846044;
mcr_plot(2,:) = mcr_plot(2,:)-2200316; piksi_plot(2, :) = piksi_plot(2, :)-2200316; javad_plot(2, :) = javad_plot(2, :)-2200316;
plot(mcr_plot(1, :), mcr_plot(2, :),'r-*', piksi_plot(1, :), piksi_plot(2, :),'g-+', javad_plot(1, :), javad_plot(2, :),'b-x');
grid on; set (gca, 'FontSize', 14);
MCR_smpl = length(mcr_plot); piksi_smpl = length(piksi_plot); javad_smpl = length(javad_plot);
samples_str = sprintf('Solutions statistics [MCR: %d, Piksi: %d, Javad: %d]', MCR_smpl, piksi_smpl, javad_smpl);
title (samples_str);
xlabel('X, m');
ylabel('Y, m');
box('off');
Std_MCR = sqrt((norm(mcr_plot,'fro')^2)/MCR_smpl);
Std_Piksi = sqrt((norm(piksi_plot,'fro')^2)/piksi_smpl);
Std_Javad = sqrt((norm(javad_plot,'fro')^2)/javad_smpl);
MCR_lgn = sprintf('MCR: %0.2f m', Std_MCR);
Piksi_lgn = sprintf('Piksi: %0.2f m', Std_Piksi);
Javad_lgn = sprintf('Javad: %0.2f m', Std_Javad);
lgn = legend(MCR_lgn,Piksi_lgn,Javad_lgn,'Location','southeast');
time = strftime ('%Y-%m-%d_%H_%M_%S', localtime (time ()));
mess = sprintf('%s.png', time);
tic ()
print (fig,mess,'-S1024,768','-dpngcairo');
toc ()"
echo "All plot!"
Скрипт может откинуть начальные измерения, например, чтобы исключить начальные искажения, в данном случае откидываются SKIP 1000 начальных измерений.
В построении графиков поможет Octave. В него парсятся данные из созданных выше файликов, строятся графики, подсчитывается количество измерений и рассчитывается стандартное отклонение измерений относительно опорной точки по каждому приемнику.
n - количество накопленных измерений.
Получается картинка примерного такого вида:
Достаточно важным моментом здесь является выбор "рендера" и выдаваемого формата файла:
...
print (fig,mess,'-S1024,768','-dpngcairo');
Cairo - наиболее быстрый (проверено экспериментально) способ вывода. Работает только при graphics_toolkit('gnuplot'). Иначе, процесс сохранения картинки занимает баснословное количество времени.
[править] Где искать эти картинки?
Картинки сохраняются в папку к скопированным логам /home/CommonFiles/logs.
[править] И всё?
Нет, еще картинки отправляются по почте, следующим образом send_mail:
ATTACHMENT=`find -name '*.png'`
echo $ATTACHMENT
echo " " | mailx -r "srtt@evaluator (Daily solutions)" -s "Solutions statics" -a $ATTACHMENT ****@gmail.com
echo " " | mailx -r "srtt@evaluator (Daily solutions)" -s "Solutions statics" -a $ATTACHMENT *******@srns.ru
cp $ATTACHMENT /home/CommonFiles/logs
rm -rf $ATTACHMENT
echo "Email successfully sent!"
После отправки картинка копируется в папку logs и удаляется.
[править] Как очищаются логи?
Инквизицией! Шутка. Пока идет запись логов, их файлы защищены на запись, следовательно, придется прервать запись, убив фоновые процессы, скрипт kill_logs_pid:
pid_log=`ps aux | grep '\(start_\|swift\|curl\|broadcast_\)'| awk '{print $2}'`
kill $pid_log
echo "Pids killed!"
Скрипт очищающий логи refresh_logs:
Data=$(date +%Y-%m-%d)
Time=$(date +%H_%M_%S)
mkdir /home/CommonFiles/logs/$Data"_"$Time
cp /tmp/javad.* /home/CommonFiles/logs/$Data"_"$Time
cp /tmp/mcr.* /home/CommonFiles/logs/$Data"_"$Time
cp /tmp/piksi.obs /home/CommonFiles/logs/$Data"_"$Time
cp /dev/null /tmp/javad.log
cp /dev/null /tmp/mcr.log
cp /dev/null /tmp/piksi.log
echo "Logs refreshed!"
При вызове скрипта фиксируется дата и время, после чего логи для Javad и MCR копируются в соответствующую папку в /home/CommonFiles/logs/. Для Piksi копируются только уже обработанный файл - piksi.obs.
[править] Хочу один скрипт для вызова всего!
И он есть у нас. Один скрипт, чтобы править всеми. Его название process_all:
convert_all_to_rinex
solve_all_from_rinex
plot_all
send_mail
kill_logs_pid
refresh_logs
echo "All done!"
UPD. Время показало, что пока Piksi пишет "тяжелые" логи, они долго обрабатываются. В данном случае пришлось разделить скрипты. Обработка логов conv_n_solv:
convert_all_to_rinex
solve_all_from_rinex
От этого немного похудел process_all:
plot_all
send_mail
kill_logs_pid
refresh_logs
echo "All done!"
[править] А где же автоматизация?
Автоматизируется Сервер Статистики программированием его расписания с помощью планировщика crontab, для этого необходимо указать ему расписание,
заполненное специальным образом cron:
MAILTO=srtt
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/home/CommonFiles/scripts:/home/CommonFiles/progs/RTKlib:
00 00 * * * "bash -c `conv_n_solv`"
00 01 * * * "bash -c `process_all`"
15 01 * * * "bash -c `broadcast_javad`"
16 01 * * * "bash -c `start_javad_log`"
17 01 * * * "bash -c `start_mcr_log`"
18 01 * * * "bash -c `start_piksi_log`"
Обязательно указывается пользователь MAILTO, от которого будут выполнены запланированные команды, и пути нахождения необходимых программ PATH. Предполагается, что ранее сбор логов был запущен и 00:00 уже пойдет над ними работа.
Формат записи строчки расписания такой:
минута час день_месяца месяц день_недели команда
Первое число обозначает минуту, второе час, то есть запуск всех процессов произойдет в одни минуту первого ночи. Звездочку вместо числа следует трактовать как слово каждый, то есть каждого дня месяца, каждого месяца, каждого дня недели. Тонкости настройки crontab легко ищутся в интернете, например, вот и вот.
Соответственно, скрипты на запись логов запускаются в 01:00, потому что процессы обработки могут затягиваться, часа точно должно хватить.
Посмотреть текущее расписание можно командой:
Удалить текущее расписание:
P.S.
Вот примерно так всё и работает!
[ Хронологический вид ]Комментарии
Войдите, чтобы комментировать.