Все «тайны» настройки софта для плагинов и периферии OpenCPN

24 Feb 2022
by ignat

https://itnan.ru/post.php?c=1&p=577404

Рабочее место

Это устройство размещается непосредственно в рабочей зоне на складе запасных частей. Чем больше диапазон дистанций для размещения этого устройства, тем лучше. Оптимальное соотношение цена-качество-дистанция у такого кронштейна NB North Bayou Gaming Monitor Stand NB45-B.

Цикл статей

После публикации статьи Использование OpenCPN для автоматизации производства / Хабр (habr.com) в личной почте были вопросы по настройке программного обеспечения на собранном устройстве.

В той статье были даны ключевые ссылки на рабочий в плане TS, LCD и emmc (тачпанели, экрана и встроенной памяти) имидж Debian Linux. И ссылки на строчки открытого кода, которые достаточны для опытного специалиста с избытком времени на изучение чужого кода.

Там же подробно было показан один из возможных способов механической сборки компонентов OLIMEX LTD – OLinuXino Arduino Maple Pinguino ARM Open Source Hardware Development Boards. Все компоненты этой компании идут с подробным открытым описанием как необходимого программного обеспечения, так и с полной открытой публикацией всего дизайна железа. То есть любой желающий может разработать, изготовить и заказать на любом заводе свою собственную интегрированную плату взяв за основу разведенные платы Olimex.

В этой статье будут более детально и последовательно приведены все необходимые конфигурации как самого имиджа Linux, так и необходимых библиотек для OpenCPN и для новых плагинов, о которых я рассказывал в предыдущей статье.

Настройки имиджа Linux

Мы используем Olimexino-MICRO A20. На нашей плате есть память emmc и у нас уже подключён экран LCD через кабель IDC40. На сайте производителя есть кабели длинной 6 см, 10 см и 15 см. Они стоят не дорого и можно купить сразу 3 кабеля и по месту подобрать тот который вам подходит. Либо попробовать использовать кабель от старых настольных PC, которым подключались жесткие диски до эпохи SATA.

Для активизации LCD мы идем с правами root в каталог /root. Пароли по умолчанию в имидже Olimex Debian Linux – olimex. Изначально настроены два пользователя – root и olimex. Оба с одинаковым паролем – olimex. В каталоге мы запускаем скрипт настройки LCD, который открывает меню, где и можно выбрать тип вашего LCD.

./change_display.sh

Я предпочёл вначале сделать все предварительные настройки на SD карте, с целью сохранения загрузочного имиджа на всякий случай. Но вы можете на этом этапе перейти на работу со встроенной emmc памяти.

./emmc.sh

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

poweroff

Вытащить SD карту и путем нажатия кнопки ресет либо путём включения и выключения штекера питания включить устройство. Примерно через 30 секунд загрузка должна завершится и вы увидите X десктоп на своей LCD панели. Если вам необходимы манипуляции с загрузчиком и вы хотите видеть процесс загрузки ядра, подключите до включения устройства дополнительный HDMI монитор.

Библиотеки необходимые для OpenCPN 4.0

Для надежности и для компиляции собственных модулей лучше всего клонировать репозиторий OpenCPN.

git clone https://github.com/OpenCPN/OpenCPN

OpenCPN/OpenCPN: A concise ChartPlotter/Navigator. A cross-platform ship-borne GUI application supporting * GPS/GPDS Postition Input * BSB Raster Chart Display * S57 Vector ENChart Display * AIS Input Decoding * Waypoint Autopilot Navigation (github.com)

Для нашей версии ядра крайняя версия OpenCPN, которая собирается без проблем это 4.1, но мы будем использовать версию 4.0.

cd OpenCPN
git checkout v4.0.0

Для компиляции OpenCPN нам нужны следующие библиотеки и пакеты

По сути в списке достаточно оставить только одноименные пакеты с суффиксом -dev, так как сами библиотеки поставятся автоматически, но я привет те команды, которые я использовал.

sudo apt-get install cmake build-essential libwxbase3.0-0 libwxbase3.0-dev 
sudo apt-get install libwxgtk3.0-dev libwxgtk3.0
sudo apt-get install libcairo2-dev libcairo2
sudo apt-get install portaudio19-dev
sudo apt-get install curl-dev curl libcurl libcurl4-openssl-dev
sudo apt-get install libpangocairo-1.0-0 libpango libpango1.0-dev
sudo apt-get install libsdl-pango-dev pkg-config
sudo apt-get install libpangomm-1.4-dev libpangox-1.0-dev
sudo apt-get install libgtk-pixbuf2.0-0 libgtk-pixbuf libgtkpixbuf
sudo apt-get install libgtkextra-dev libgtk-3.0 libgtk3.0-cil-dev
sudo apt-get install liblzma-dev libarchive-dev libzip2 lbzip2 libbz2-dev
sudo apt-get install libexif-dev libexif-gtk-dev libelf-dev
sudo apt-get install gettext libtinyxml2-dev

Не все эти библиотеки нужны, кой-где возможны ошибки в написании. Но с помощью следующей команды можно найти правильное название.

sudo apt-cache search libtinyx

Дополнительные библиотеки

Пару библиотек для этого проекта я собрал самостоятельно. Стоковая версия OpenCPN не подключается к базе данных MySQL, поэтому потребовались эти доработки. Склонировал automake-1.9 и mysqlcppapi-2.0.0. При сборке последней пришлось поправить пару строчек в 1-ом или 2-ух файлах исходного кода, так как мы по умолчанию используем старый компилятор.

При желании можно собрать и свою версию libwxgtk3.0. Я это сделал, чтоб убедится что все зависимости установлены.

В старой версии OpenCPN работа с прогнозами погоды была не такая продвинутая как в zygrib_7. Поэтому можно собрать и использовать дополнительную программу, а можно доработать встроенный плагин OpenCPN для разбора файлов прогноза. В итоге у вас из исходников должно получиться 2 deb пакета:

zygrib-maps_7.0.0-1_all.deb
zygrib_7.0.0-1_armhf.deb

Библиотеки необходимые для подключения принтера этикеток Zebra GK420d

После установки следующих библиотек и настройки cups Zebra GK420d cтал печатать без доработки исходного кода. При не правильной настройке драйвера или размера этикетки принтер сообщает об ошибке превышения отступов.

sudo apt-get install cups foomatic-db-compressed-ppds cups-pdf smbclient xpp
sudo apt-get install ghostscript-x printer-driver-gutenprint
sudo apt-get install cups-browsed font-droid libpaper-utils

В wxWidget в библиотеке печати прошито намертво приложение evince с характерными для него ключами. Эта команда хранит свои параметры в бинарном формате в каталоге:

/home/olimex/.local/share/gvfs-metadata

Это стандартное хранилище для gnome, но не для нашего облегчённого оконного менеджера – lxde.

Автостарт в lxde

Кстати, если вам нужно настроить автостарт для этой среды, нужно редактировать этот файл:

/home/olimex/.config/lxsession/LXDE/autostart

Либо скопировать desktop файл из каталога /usr/share/application/ в каталог, как в примере ниже:

/home/olimex/.config/autostart/LXinput-setup.desktop

Настройка lp

Я использовал для распечатки команду lp, которую можно настроить на заданный тип принтера из командной строки. Более подробную информацию смотрите на вашем локальном web CUPS сервере через Mozilla Firefox: http://localhost:631/help/options.html

Там же через web интерфейс можно выбрать доступный принтер, настроить драйвера для притера (ppd файлы), поменять права доступа к нему. Так же можно посмотреть очередь печати и возможные ошибки возвращаемые принтером.

lpstat -p -d
lpoptions -d Zebra_Technologies_ZTC_GK420d
lp testprint.pdf
lp test.txt
lp test.jpg

Для запуска нужного приложения по нажатию кнопки “Print preview” в wxWidget нужно обновить альтернативные программы. По умолчанию для показа перед печатью используется браузер, но только если не установлен gv или xpdf, а так же evince.

После того как вы установите необходимый просмотрщик файлов можно попробовать использовать команды:

xdg-mime default lp.desktop application/pdf
update-alternatives --config x-www-browser

И изменить содержание файла /home/olimex/.config/mimeapps.list

application/pdf=pdf.desktop

на

application/pdf=lp.desktop

Файл /usr/share/application/lp.desktop получается копированием файла xpdf.desktop путём замены строчки на Exec=lp. Параметр имя в этом файле тоже надо поменять: Name=lp

Изменить файл /etc/papersize следующим содержанием:

w288h432

Настройка WiFi

Лучше всего подходит модуль R5370-ANT. Он имеет антенну и поддерживает все необходимые протоколы и самое главное поддерживается этим имиджем Olimex из коробки. Имеется аналог, но без антенны R5370.

sudo apt-get install wpasupplicant
rfkill list
rfkill unblock wifi
iwconfig
sudo ifconfig wlan0 up
sudo iwlist wlan0 scan | grep ESSID

В файл /etc/network/interfaces добавить:

auto wlan0
iface wlan0 inet dhcp
wpa-ssid your_wifi_ssid
wpa-psk your_wifi_pass

ssid и psk значения в этих файлах и командах надо заменить на принятые в вашей сети. Для генерации файла /etc/network/wpa_supplicant.conf

cd /etc/network
wpa_passphrase your_wifi_ssid your_wifi_pass | sudo tee wpa_supplicant.conf

Файл получиться примерно такой

network={
  ssid=your_wifi_ssid
  scan_ssid=1
  psk=your_wifi_pass
  key_mgmt=WPA_PSK
}

Следующая команда выполнит подключение устройство к местной wifi сети:

cp wpa_supplicant.conf /etc/wpa_supplicant.conf
killall wpa_supplicant
sudo wpa_supplicant -c /etc/wpa_supplicant.conf -i wlan0
iwconfig
dhclient wlan0
ifconfig

После рестарта устройства WiFi подхватится автоматически.

Настройка времени

apt-get install ntp
timedatectl status
timedatectl list-timezones
timedatectl set-timezone Europe/Madrid

Изменение драйвера Zebra GK420d

Новые принтеры Zebra GK420d отличаются процедурой инициализации для USB от выпущеных ранее 2007 года. Возможно придется внести изменения в настройки CUPS (ELP II) или в ppd файли или в исходный код.

Настройки могут быть связаны с отступами, шириной распечатываемой области (Print Width – на фото ниже помечен овалом справа). Zebra использует старый язык управления принтерами подобный ELP и при переходе к новым стандартам растровой печати с поворотом страницы требуется компенсатция левого отступа.

Вот примерно в этом месте можно вставить изменения прямо в исходники CUPS. Но лучше исправить ppd файл и подгрузить параметры стандартным образом.

Для распечатки конфигурации принтера нажмите кнопку на панели принтера и не отпускайте до момента пока светодиод мигнёт один раз, затем отпустите кнопку.

Этикетки с конфигурацией старого и нового принтера (параметры автоматически настраевыме принтером помечены прямоугольником слева, овалом справа помечен параметр ширины распечатываемой области, который должен быть установлен с учётом отступов в 609)

Этикетки с конфигурацией старого и нового принтера (параметры автоматически настраевыме принтером помечены прямоугольником слева, овалом справа помечен параметр ширины распечатываемой области, который должен быть установлен с учётом отступов в 609)

Серия из одного и двух миганий (пока вы держите кнопку нажатой и отпускаете после двойного мигания) обеспечит автоматическую калибровку носителей (параметры на фото помечены прямоугольником слева). Будут выплюнуты от одно до четырёх наклеек, которые можно будет руками венуть назад.

Если удерживать кнопку до последовательной серии в пять миганий (одно, два, три, четыре, пять), то будет начата распечатка прямоугольников с шагом в 4 мм. Повторное нажатие кнопки подтвердит выбранную вами ширину.

Процедура инициализации новых принтеров выпущенных в 2021 году или позже:

~SD15
~TA000
~JSN
^XA
^ST09,15,2021,11,06,15,M
^XZ
^XA
^SZ2
^PW812
^LL1218
^PON
^PR5,5
^PMN
^MNY
^LS0
^MTD
^MMT,N
^MPE
^XZ
^XA^JUS^XZ

~SD10 – яркость
~TA000 – без отрывания этикетки (для этого надо подключить резак)
~JSN – обратная подача по умолчанию (нет)
^SZ2 – режим EPL II
^PW609 – ширина принтера
^LL1246 – длинна этикетки
^PR5,5 – скорость вывода на печать
^MNY – отслеживание подложки между этикетками, ^MNA – автоматическая калибровка, ^MNM – отлеживание чёрных полосок, ^MNN – рулон без перерывов
^LS0 – левое положение (отступ)
^XA – запуск команды форматирования
^JUS – команда сохранения конфигурации
^XZ – завершение команды форматирования

Для отключения сенсора новой этикетки, если вы просто хотите распечатывать чеки, используют следующую команду:

^XA^MNN^JUS^XZ

Пошлите эту команду в принтер или вставте ^MNN - рулон без перерывов в поток данных, который посылаете.

Пример распечатки документа в wxWidget. Пример настройки причуды для принтера в файле /usr/share/cups/usb.

Wouldn’t it be needed to add this quirck to org.cups.usb-quirks ?

# Zebra GD420 (https://github.com/apple/cups/issues/5395)
0x0a5f 0x0080 unidir no-reattach

Калибровка Тачпанели

Как упоминалось ранее было использовано следующее ультрaзвуковое устройство LCD-TS15.6. Лучшая инструкция по конфигурации и калибровке тут. Но 7 шаг из этой инструкции делать не обязательно, и скорее это приведет к постоянной перегрузке устройства.

Есть лог настройки, который реально помогает понять что нужно сделать.

После установки тачпанели в устройство нам надо повторить выполнение двух команд.

ts_calibrate

После запуска этой команды нужно будет последовательно нажать на 5 крестиков на экране. 4 по краям и один в центре. Затем скопировать записанные данные в надлежащее место.

cp /etc/pointercal /usr/etc/pointercal

Необходимо перегрузить систему. Затем убедится что USB интерфейс тачпанели имеет адрес, который прописан в /usr/share/X11/xorg.conf.d/20-ts.conf и в /etc/environment. Для уверенности я просто отключаю на USB хабе все остальные устройства и включаю их после загрузки системы.

Компиляция OpenCPN

Перед началом компиляции убедитесь что системные часы установлены на текущую дату и время. Без этого шага из-за неправильной автоматической настройки даты модификации файлов, утилита make будет каждый раз перекомпилировать все файлы и сообщать об возможной ошибке. Это очень затратно по времени. Полная компиляция OpenCPN на данном железе A20 может длиться дольше 40 минут.

date -s "16 AUG 2021 14:57"
cd OpenCPN
mkdir build
cd build
cmake ../
make
su
make install

Запуск OpenCPN

opencpn -no_opengl

OpenGL это конечно боль этого старого имиджа Debian для SoC A20. Полагаю что в имиджах для новых продуктах Olimex решат проблему и по умолчанию настроят драйвера и поставят необходимые библиотеки для аппаратного ускорения графики.

Так как у нас на производстве используется планарный (не 3D) дизайн, то пока такой задачи как построение объемных 3D моделей не стоит, как и наложение слоёв в чертежах на данном рабочем месте. Возможно в будущем это будет актуально для других рабочих мест.

Отступление про весы

Весы MyWeigh CTS 30000

В прошлой статье не хватило места рассказать про подключение данных весов к USB разъёму. На выходе этих весов обычный COM DB-9, каких уже нет на современных компьютерах.

Для быстрого превращения COM в USB без паяльника я использовал MOD-RS232 и USB-SERIAL-F. С этими кабелями я много работал прошивая ESP8266 поэтому был совершенно уверен, что они совместимы с любой версией Linux без компиляции дополнительных модулей.

Я открутил разъем от корпуса, подключил модули внутри корпуса, выпустив USB конец кабеля наружу. И предварительно закрепив узел кабеля внутри весов.

Включил режим непрерывной передачи параметров и стал регулярно через MiniCOM принимать данные в текстовом виде от весов в момент изменения веса. Данные состояли из 3 параметров, которые дублировали содержимое 3 экранов на лицевой панели весов (Weight, Unit Weight, Total Count).

Причем меня интересует только Weight, потому что Unit Weight удобнее брать из Базы данных ERP. А Total Count вычислять самостоятельно и сравнивать с необходимым по заказу. И соответственно выводить Диалог с Progress Bar. Тогда работнику не нужно будет следить за числами, достаточно просто смотреть на индикатор.

Создание виртуальных весов для отладки

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

void PlugInManager::SendNMEASentenceToAllPlugIns(const wxString &sentence)
{
    wxString sentence1 = "G.W. :+ 10.1155kg\n";
//    wxString sentence1 = "U.W. :+ 0.08155 g/pcs\n";
//    wxString sentence1 =   "Total:+ 144 pcs\n";
    wxString decouple_sentence(sentence1); // decouples 'const wxString &' and 'wxString &' to keep bin compat for plugins
    for(unsigned int i = 0 ; i < plugin_array.GetCount() ; i++)
    {
        PlugInContainer *pic = plugin_array.Item(i);
        pic->m_pplugin->SetSentence(decouple_sentence);

        if(pic->m_bEnabled && pic->m_bInitState)
        {
            if(pic->m_cap_flag & WANTS_NMEA_SENTENCES)
                pic->m_pplugin->SetNMEASentence(decouple_sentence);
        }
    }
}

Как видно в коде выше OpenCPN формирует строку для передачи в плагин NMEA. По началу я создал отдельную библиотеку для протокола весов. Но в силу нехватки времени переключился на реализацию протокола весов прямо внутри библиотеки разбора NMEA. При этом я планирую вынести весь этот код в отдельную библиотеку дашборда в будущем во время рефакторинга.

Для того чтоб срабатывало прерывание и вызывалась эта функция, я подключал мой USB GPS приёмник. Благодаря этой дополнительной строчке я получал данные характерные для весов.

Для получения реальных данных с весов надо подключить весы, сменить в графических настройках OpenCPN источник данных с GPS на MyWeigh, закомментировать эту тестовую строчку и перекомпилировать этот файл с последующей линковкой и установкой исполняемых библиотек и самой программы opencpn.

Плагин

Функция SetSentence добалвлена в плагин dashboard_pi по аналогии с SetNMEASentence. Причем все протоколы вынесены в отдельные подкаталоги этого плагина и оформлены в виде классов. Я поступил точно так же и завел подкаталог myweigh, в котором находятся все необходимые файлы для парсинга протокола весов.

При компиляции и линковке возникли ошибки. Как я сейчас понимаю, скорее всего из-за не прописанного myweigh.h или myweigh.hpp. Все эти исправления я оставил на рефакторинг, а сами файлы перенес в основную часть плагина dashboard_pi. То есть на один уровень выше, не выделяя в подкаталог.

Посмотрим на функцию АPI плагина, которую я добавил.

void dashboard_pi::SetSentence( wxString &sentence )
{
//    m_MyWeigh << sentence;
    m_NMEA0183 << sentence;
//    wxCharBuffer buf = sentence.ToUTF8();
//    wxString mnemonic = buf+1;
//    mnemonic = mnemonic+2;

    bool bGoodData = false;

    if( m_NMEA0183.PreParse1() )
    {
        if( m_NMEA0183.LastSentenceIDReceived == _T("G.W."))
        {
            if( m_NMEA0183.Parse1() )
            {
                bascula_weigh = 0.0;
                if(m_NMEA0183.Gw.IsDataValid == NTrue)
                {
                    bascula_weigh = m_NMEA0183.Gw.UnitWeighKg;
                    bGoodData = true;
                    SendSentenceToAllInstruments( OCPN_DBP_WEIGH, bascula_weigh, "kg" );
//                    wxString msg1;
//                    msg1 += _T("\n\n");
                    progress_dialog = instrument_progress_dialog->GetPprog(bascula_weigh, db_weigh, db_quantity);
//                    progress_dialog->Hide();
//                    instrument_progress_dialog->ppprog->Update(20);
//                    instrument_progress_dialog->ppprog->Show();
//                    instrument_progress_dialog->ppprog->Raise();
                }
            }
        }


        if( m_NMEA0183.LastSentenceIDReceived == _T("U.W."))
        {
            if( m_NMEA0183.Parse1() )
            {
                double unit_weigh = 0.0;
                if(m_NMEA0183.Uw.IsDataValid == NTrue)
                {
                    unit_weigh = m_NMEA0183.Uw.UnitWeighKg;
                    SendSentenceToAllInstruments( OCPN_DBP_UNIT_WEIGH, unit_weigh, "kg/pcs" );
                }
            }
        }

        if( m_NMEA0183.LastSentenceIDReceived == _T("Tota"))
        {
            if( m_NMEA0183.Parse1() )
            {
                double total_unit = 0;
                if(m_NMEA0183.Tot.IsDataValid == NTrue)
                {
                    total_unit = m_NMEA0183.Tot.TotalUnit;
//                  getUsrDistanceUnit_Plugin( g_iDashDepthUnit );
                    SendSentenceToAllInstruments( OCPN_DBP_TOTAL_QUANTITY, total_unit, "pcs" );
//                    SendSentenceToAllInstruments( OCPN_DBP_TOTAL_QUANTITY, total_unit, m_NMEA0183.LastSentenceIDReceived );
                }
            }
        }


    }

//    if(bGoodData)
//    {
//        Refresh(false);
//    }
}

В 13, 35, 48 строчке видно что парсинг происходит по первым 4 символам принимаемым от весов. В отличие от NMEA протокола нет пропуска первого служебного символа. Так как протокол весов не содержит такового.

После 2 дополнительных проверок m_NMEA0183.Parse1() и m_NMEA0183.Gw.IsDataValid происходит присваивание значения через вызов метода соответствующего класса (WeightUnit WeightTotal Count).

В строчках 20, 42 и 55 происходит вызов 3 различных классов, которые написаны для разбора 3 различных сообщений весов. Эти классы мы посмотрим позже (Смотрите параграф ниже – “Класс Gw”).

А пока обратим внимание на вызов функции в 22 строчке:

SendSentenceToAllInstruments( OCPN_DBP_WEIGH, bascula_weigh, "kg" );  

Тут происходит выбор виртуального дисплея или инструмента (яхтенный сленг) для вывода полученных данных – bascula_weigh. Причем указываются единицы измерения килограммы – “kg”. Отображение информации происходит в момент её получения.

Все задержки связаны только с работой нашего устройства и физических протоколов передачи через COM->USB. И не зависят от Бакенд сервера. Поэтому можно считать что мы на своём дашборде контролируем и получаем информацию в реальном режиме времени. Так как на нашем заводе не будет более 10 таких устройств (на текущий момент всего 2 устройства), то бакенд для доступа к базе данных не нужен. Можно забирать необходимые данные напрямую.

Реализацию класса DashboardInstrument_Weight я сделал по аналогии с уже готовым стандартным одиночным (одно значение) инструментом. Посмотреть его можно в файле instrument.cpp.

Разбор протокола весов

Заглянем в файл sentence.cpp. Разница в методах Field1 и Field заключена всего в одной строчке которая определяет первый разбираемый символ и выборе другого разделяющего символа. Это 8 строчка, которая говорит что мы считаем символы с нулевой позиции (в Си и С++ индексы нумеруются с 0).

onst wxString& SENTENCE::Field1( int desired_field_number ) const
{
//   ASSERT_VALID( this );

   static wxString return_string;
   return_string.Empty();

   int index = 0; // Keep over the G/U at the begining of the sentence
   int current_field_number = 0;
   int string_length        = 0;


   string_length = Sentence.Len();

   while( current_field_number < desired_field_number && index < string_length )
   {
      if ( Sentence[ index ] == ' ' )
      {
         current_field_number++;
      }

      if( Sentence[ index ] == '+')
          return_string += Sentence[ index ];

      index++;
   }

   if ( current_field_number == desired_field_number )
   {
      while( index < string_length    &&
//             Sentence[ index ] != '+' &&
             Sentence[ index ] != ' ' &&
//             Sentence[ index ] != ',' &&
             Sentence[ index ] != '+' &&
             Sentence[ index ] != 0x00 )
      {
         return_string += Sentence[ index ];
         index++;
      }
   }



   return( return_string );
}

А еще мы запятую ‘,’ заменили на пробел ‘ ‘, а звездочку ‘*’ на плюс ‘+’ в полном соответствии с протоколом весов.

Класс Gw

Вызов этого парсинга происходит через функцию Double1 из нашего класса Gw.

bool GW::Parse( const SENTENCE& sentence )
{
   /*
   UW - Unit Waigh
        1   2 3   4 5
        |   | |   | |
 $--VLW,x.x,N,x.x,N*hh<CR><LF>
 Field Number:
  1) Total cumulative distance
  2) N = Nautical Miles
  3) Distance since Reset
  4) N = Nautical Miles
  5) Checksum
   */

   /*
   ** First we check the checksum...
   */

//   if ( sentence.IsChecksumBad( 5 ) == TRUE )
//   {
//      SetErrorMessage( _T("Invalid Checksum") );
//      return( FALSE );
//   }

//   TotalMileage = sentence.Double( 1 );
   UnitWeighKg = sentence.Double1( 2 );
//   UnitWeighKg = 20.0;
//   TripMileage  = sentence.Double( 3 );

   return( TRUE );
}

Как видно все комментарии требуют рефакторинга, который я планировал сделать после подключения другого вида весов, чтоб уловить общие закономерности в китайских протоколах и сделать этот метод разбора более гибким. То есть добавить дополнительные проверки и условия.

Прогрес диалог

Среди инструментов создан еще один класс DashboardInstrument_ProgressDialog который предназначен для вывода модального окна с индикатором веса (аналогичного индикатору копирования файлов). Оно всегда висит сверху и не закрывается даже при закрытии родительского окна.

Идея в том что когда работник из пакета насыпает мелкие детальки на весы, ему не нужно их считать и не нужно нажимать дополнительные кнопки на весах для каждой новой позиции в заказе. Кроме того система учитывает размеры полных пакетов и даёт информацию по остатку неполного пакета, который проходит через весы. Тем самым сокращается время на рутинные операции.

Формула расчета положение индикатора находится в файле instrument.cpp.

wxProgressDialog* DashboardInstrument::GetPprog( double bascula_weigh, double db_weigh, double db_quantity )
{
    int cur_count;
    cur_count = wxRound( pd_count * bascula_weigh / (db_weigh * db_quantity) );
    if (cur_count >  90) { cur_count = 95; }
    wxString msg5;
    msg5.Printf(_T("%d * %d / ( db_weigh * db_quantity ) = %d "),
        pd_count, wxRound(bascula_weigh), wxRound(db_weigh * db_quantity));
    ppprog->Update(cur_count, msg5);
    return ppprog;
}

Индикатор доходит до 95% только, потому что после 100% он исчезает, где исправить это поведение я не знаю. Нужно чтоб этот диалог всегда находился на своём месте. Перерисовывать его каждый раз при выборе новой позиции считаю не рациональным по затратам времени.

Так же распечатывает необходимые этикетки. И делает это все в базе данных в рабочей ERP, состояние которой видит весь офис и может отслеживать выполнение заказа. Под офисом я понимаю отделы кладовщика, продажников, логистику, администратора и начальника.

Интерфейс пользователя

Весь новый интерфейс пользователя сосредоточен в отдельном втором основном модальном окне. Весь код которого находится в одном файле myframe1.cpp.

Доступ к базе данных оформлен внутри этого файла, но позже надо будет разделить и подумать чтоб в далёкой перспективе эту часть можно заменить на ORM или API.

Принтер управляется отдельными классами. Для построения списка распечатываемых этикеток и управления самим принтером есть специальные файлы: Label.cppLabelPoint.cppSelect.cpplabelprintout.cpp. В них все более или менее стандартно. Необходим опыт использования системы, чтоб понять в какую сторону двигаться.

В системе есть функции построения QR кодов (как растровых, так и текстовых), отрисовки изображения (как на экране, так и на этикетке).

Если будут интересны детали отрисовки окон с таблицами на wxWidget напишите в комментариях, тогда я дополню статью или напишу дополнительную по графическому интерфейсу пользователя, поведенческим вызовам или обработчикам мышки, клавиатуры, тачпанели.

Отступление про USB хаб

Чтоб подключить несколько устройств USB (тачпаннель, принтер, весы, wifi, bt для безпроводной клавиатуры и мышки) к OLinuxino A20 с всего лишь двумя USB разъёмами, нужно подключать USB hub. Но подобные хабы через один USB кабель при передаче данных могут обеспечить ограниченное питание на каждый порт\устройство.

USB hub и штекеры питания которые к нему не подходят

Поэтому рядом с кабелем USB на них имеется специальный разъём для питания в 5 вольт всего мультиплексора. Сейчас такие блоки питания редкие.

Вероятно они использовались раньше для многих старых мобильных телефонов. Если вам не удастся подобрать штекер, то придется перепаивать на гнездо большего размера и дорабатывать корпус хаба под это гнездо.

Отступление про то в чём я мало разбираюсь

Есть вариант изготовления своей собственной платы периферии и стандартного модуля Olimex, подключаемого по шине расширения. В механическом плане это примерно плюс 5 мм -10 мм к толщине алюминиевого профиля необходимого для тачпанели и LCD (надеюсь что меня поправят опытные механики-электроники). Все конечно зависит от соединений плат внутри корпуса. Если взять с запасом на неудачные толстые кабели, то в целом 15 мм достаточно с запасом для интеграции всех готовых Olimex плат внутри металлического корпуса. Итого толщина профиля должна быть между 24 мм и 40 мм.

Примерно 30 лет назад, мы все в МИЭТ (год основания 1965) сдавали курсовик по начерталке на тему изготовления корпуса стандартного электронного устройства. Вероятно, технологии которые я использую для корпуса устройства, сопоставимы с возрастом моей новой яхты. East Anglian MkII изготовленной из дерева в Англии в 1961 году. Фотографии данного класса яхт есть в предыдущей стататье. Естественно у меня также имеются все оригинальные полные чертежи этой яхты, сделанной на заказ. Таким образом конструкция этой яхты, как и дизайн моего навигатора является Open-source hardware (OSH).

East Anglian MkII

Уверен что в ближайшие годы Olimex найдет способ использовать микропроцессор с открытым дизайном, если такой появится на рынке. Существуют с 2000 года бесплатные программные продукты для дизайна микропроцессоров. Например, Static Free Software Home Page. В этом проекте мы с коллегами участвовали как русскоязычные тестеры и переводчики документации. И тогда веский аргумент о полной недоступности открытого во всех смыслах микропроцессора (включая устройство ядра) уйдет в прошлое. Предположительно говорю о ядре RISC-V. Наиболее вероятный чип для будущей платы Olimex на весну 2021 это Allwinner AP (application processor) SOC c открытм ISA (open standard instruction set architecture) для RISC-V. Вот тут подробнее. Оригинал статьи тут.

IMHO останутся вопросы по графическим сопроцессорам OpenGL, но со временем и их дизайн будет открыт.

Другие статьи на тему автоматизации яхтинга своими силами:

Использование OpenCPN для автоматизации производства / Хабр (habr.com)
IT техническая сторона яхтинга / Хабр (habr.com)
Шпаргалка, которая нужна на яхте / Хабр (habr.com)
IT Релокация на яхте. Из Швеции в Испанию / Хабр (habr.com)

Теги:aisolinuxino-microolimexяхтаяхтингqr-кодысенсоры
Хабы:Настройка LinuxПрограммированиеC++Промышленное программированиеПрототипирование
Minecraft Edu © 2022