В прошлой статье https://habr.com/p/1103196/ я рассказал о своем подходе к разработке среды программирования промышленного контроллера — а именно о части, касающейся визуализации данных из цепей лестничных диаграмм и функциональных блоков, которые выполняются на стороне микроконтроллера. В этот раз подробнее остановлюсь на цепочке событий.

Как я уже упоминал, вся модель среды ПЛК (которую я разрабатываю) содержится в XML‑файле. Этот файл включает в себя список функциональных блоков:

<LD_Unit Name="GTM" Group="Timer" Width="100" Height="55">

На основе этих данных среда (GUI‑приложение на ПК) группирует блоки по вкладкам, используя тег <Group>. Внутри этих модулей (юнитов) также содержатся графические примитивы, которые определяют визуальный облик каждого блока.

<lists>
        <Item Type="RECT" color="#808080"  Style="box" >
          <Value> </Value>
          <xy x="1" y="20" />
          <dimension width="117" height="57" />
          <stroke Stroke="1" />
          <fill Fill="1" />
        </Item>

Вся цепочка — от XML‑строки до отрисовки блока на экране — выглядит следующим образом:


В параметры FBD‑блоков я добавил тип Style="box". Теперь при очень длинных названиях в момент отрисовки функциональный блок автоматически расширяется под размер строки. Поначалу я хотел просто обрезать строки (как это сделано в типичных китайских средах разработки), но пришел к выводу, что подход небезопасен. Если элемент ссылается на поле сложной структуры — например, на элемент массива в самом конце строки, — то важную часть данных можно просто выпустить из виду, что неминуемо приведет к ошибкам:

После того как мы получили список примитивов, определяющих визуальный дизайн отрисовки функционального блока, мы применяем указанный в XML стиль — то есть, что этот примитив (линия, круг, текст) — должен делать с числом которое приходит из памяти микроконтроллера(ПЛК). Происходит это непосредственно перед рендерингом примитива (в моем случае код написан на Java):

    private void drawLine() 
    {
         gc.setColor(AnimFabric.parseColor(color));
        
          if(Style!=null && sm!=null)
          {
                    if(Style.contains("onoff"))
                    {
                        gc.setColor(AnimFabric.onOff(sm.getValue(),Style));

                    }
          }
 
        Graphics2D g2d = (Graphics2D) gc;
        g2d.setStroke(new BasicStroke((float) stroke.Stroke)); 
        g2d.drawLine((int) xy.x+X, (int) xy.y+Y, (int) x2y2.x+X, (int) x2y2.y+Y);
    }

Класс AnimFabric содержит типы анимации, а объект sm — тег и данные, привязанные к этому примитиву.

Первоначально я хотел сделать среду более современной, но затем расставил приоритеты в пользу легковесности. Для этого я использовал стандартные классы OpenJDK — как для отрисовки графики, так и для дизайна GUI в целом. В итоге среда спокойно запускается даже на ранних версиях Raspberry Pi и требует всего 120–200 МБ ОЗУ. Таким образом, из слабого мини‑ПК можно сделать полноценный самопрограммируемый промышленный контроллер.

Так же, еще раз процесс работы из прошлого поста:

Нативная часть для железа выполняющая программу в качестве ПЛК, требует на стороне микроконтроллера 5 кб ОЗУ, и 32 кб флеш. Работает на микроконтроллерах начиная с STM32G030, где после установки интерпретатора байт‑кода, есть еще 2–3 Кб на программу чего достаточно чтоб превратить такой микроконтроллер в простое программируемое реле.