14 июля 2011 г.

Пример REST-PCA

В предыдущем посте я привел перевод статьи о протоколе REST-PCA, а теперь покажу простую реализацию этих принципов на Ruby, на примере маленького шлюза, который предоставляет доступ к данным ModBus устройств. Сам код полностью можно посмотреть и скачать на GitHub вот тут. Ниже я расскажу о работе и структуре программы, в разрезе этапов разработки. Думаю такой подход к изложению, будет более структурным. Я старался написать статью не только для Руби программистов, но и для всех инженеров в области автоматизации (надеюсь получилось). Тем читателям, кого нюансы реализации не интересуют, раздел Реализация можно пропустить. И так...



Прежде чем приступить, еще раз очень прошу ознакомится с принципами REST-PCA и проникнутся этой идеей, это поможет лучше понять для чего написана эта статья и принцип работы программы.

Постановка задачи

И так, начнем с постановки задачи. Представим, что у нас есть ModBus TCP шлюз к которому подключены ModBus RTU ведомые устройства. Необходимо получить доступ через HTTP протокол к любому из этих устройств, прочитать массив регистров(контактов) и получить данные в удобном для нас формате данных (например JSON). JSON хорошо подходит для обработки данных на JavaScript на HTML страницах, что позволит реализовать простой клиент в окне браузера.

Проектирование

Если посмотреть на структуру Шлюз -> Ведомое -> Область_данных -> Регистр глазами просветленного-программиста, то сразу понимаем, что перед нами ничто иное, как набор узлов REST.

При проектирование REST сервисов нам необходимо решить 2-е основные задачи (авторизация, автоопределение и т.д. не рассматриваем):
  • Структура данных, т.е. описание самих ресурсов (задача "Что?");
  • Задача маршрутизации или структура путей к ресурсам (задача "Где?");
Что?

Раз мы поставили задачу доступа к данным устройств, то вполне достаточно одного типа ресурсов - это регистр(контакт). Конечно, REST-PCA сервера могут поддерживать множество ресурсов (например: соединение, устройство, группа и т.д.), которые могли бы предоставит данные о топологии промышленной сети и состоянии ее узлов. Но у нас задача достаточно узкая, поэтому только регистры, и результат запроса к ним будет ответ в формате JSON:
 
{"coils": {
  "6":{
    "value":0,
    "timestamp":"2011-07-12 18:11:03 +0000",
    "quality":"good"
  },
  "7":{
     "value":0,
     "timestamp":"2011-07-12 18:11:03 +0000",
     "quality":"good"
  }
  ...
} 


Как видите, это набор пар адрес-значение. Значение регистра(контакта) в стиле OPC, т.е с временной меткой и качеством.  На самом деле в случае с шлюзом наличие качества и метки времени лишнее, т.к. данные получает клиент по требованию, и он, или их получит, или нет, а метка времени практически такая же, что и время запроса. Я их добавил в структуру ответа, просто ради позерства)))

Добавим также обработку исключений ModBus и в случае если что не так, программа не грохнется, а скромно сообщит:

{
  "error":{ 
    "type":"ModBus::Errors::IllegalDataValue",
    "message":"A value contained in the query data field is not an allowable value for server"
  }
}

Думаю пока хватит. Для наших целей, этого вполне достаточно.

Где?

К задаче "Где?" можно подойти 2-мя путями:
  • Именованные пути - идея в том, что у узлов, представляющие ресурсы, есть имя (например, Шлюз_1/Устройство_2/Состояние_Насоса). Таким образом мы имеем логическую маршрутизацию.
  • Отображение (mapping) - совершено другой подход, узлы содержат аппаратные координаты устройств (например Адрес_Шлюза/Порт_Шлюза/Адрес_ведомого/...). Этот подход подразумевает аппаратную маршрутизацию.
Оба подхода имеют право на жизнь. Именованные пути позволяют предоставить доступ к ресурсам с помощью маршрутов не зависящих от аппаратной структуры сети и протоколов передачи данных, но требуют от сервера хранение всех координаты устройств, что подразумевает его предварительную конфигурацию. Принцип отображения, напротив не требует конфигурации и позволяет создавать простые шлюзы ориентированные на конкретный протокол, но требую от клиентов знание структуры промышленной сети и адреса устройств.

Так как моей задачей является демонстрация принципов REST, а не создание полноценного REST-PCA сервера с поддержкой различных протоколов. То я выбрал простой принцип отображения, к тому же сам ModBus протокол в него легко укладывается. Ниже диаграмма для пояснения:




Таким, образом HTTP запрос GET http://127.0.0.1:4567/mb/127.0.0.1/8502/1/coils/0/10 можно перевести на русский язык:
Получить данные с REST-PCA сервера с IP адресом 127.0.0.1 по порту 4567, которые он по протоколу ModBus (mb) запросит  через шлюз с IP адресом 127.0.0.1 по порту 8502 у ведомого с адресом 1 в области контактов на чтение (coils) начиная с 0-го по 10-й. Во как!

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

Реализация (только для программистов, остальные читайте выводы)


Прежде чем приступать к реализации мы должны определиться с инструментами.  Раз мы пишем программу для демонстрации и она не имеет требований к сверхпроизводительност, к тому же наше приложение относиться к классу веб, то выбираем, Ruby и не отнимаем у Вас много времени. Из дополнительных библиотек (gem) нам потребуется:
  • rmodbus - реализация стека протоколов ModBus (автор ее - Ваш покорный слуга);
  • sinatra - одно из многих доказательств, что Ruby - это не только Rails. Классная платформа для написания веб приложений в  DSL стиле. Для нас то, что нужно!
  • json - поддержка JSON для Ruby. Быстрая и простая. 
Думаю как ставить gem-ы объяснять не нужно).

Теперь когда, инструменты готовы, приступим к коду. Т.к. нам нужен только метод get то весь код программы:
get '/mb/:ip/:port/:slave/:dataplace/:firstaddr/:lastaddr' do
  resp = {}
  begin
    data = []
    ModBus::TCPClient.new(params[:ip].to_s, params[:port].to_i) do |cl|
      cl.with_slave(params[:slave].to_i) do |slave|
        slave.debug = true
        dataplace = slave.send params[:dataplace]
        data = dataplace[params[:firstaddr].to_i .. params[:lastaddr].to_i]
      end
    end

    resp = { params[:dataplace] => {}}
    data.each_with_index do |v,i|
      resp[params[:dataplace]][params[:firstaddr].to_i + i] = {
        :value => v,
        :timestamp => Time.now.utc.strftime("%Y-%m-%d %H:%M:%S %z"),
        :quality => "good"
      }
    end
  rescue Exception => e
    resp = { :error => {
      :type => e.class,
      :message => e.message }
    }
  end
 
  content_type "application/json"
  resp.to_json
end 
Принцип очень прост, Sinatra позволяет обрабатывать различные отображения URL запросов. В нашем случае мы маршрут, разбили на параметры (:ip, :port и т.д.) Если приложение получило GET запрос, удовлетворяющее правилу, то выполняется блок кода, а сами параметры становятся доступные с помощью обращения params[:имя_параметра]. Раз запрос разобран, то можно приступать к перенаправлению запроса по ModBus TCP:
ModBus::TCPClient.new(params[:ip].to_s, params[:port].to_i) do |cl|
  cl.with_slave(params[:slave].to_i) do |slave|
    slave.debug = true
    dataplace = slave.send params[:dataplace]
    data = dataplace[params[:firstaddr].to_i .. params[:lastaddr].to_i]
  end
end
Для каждого запроса мы вынуждены открывать новое соединение, т.к. адрес шлюза и порт мы получаем через запрос. Обратите внимание на вызов slave.send params[:dataplace]. :dataplace - это область памяти ModBus устройства, и в соответствии с rmodbus и спецификацией ModBus, может принимать значения coils, discrete_inputs, input_registers, holding_registers.
После получения данных, мы формируем хэш ответа:
resp = { params[:dataplace] => {}} data.each_with_index do |v,i|
  resp[params[:dataplace]][params[:firstaddr].to_i + i] = {
    :value => v,
    :timestamp => Time.now.utc.strftime("%Y-%m-%d %H:%M:%S %z"),
    :quality => "good"
  }
end 
А в случае исключения, формируем хэш с информацией об ошибке:
resp = { :error => {
 :type => e.class,
 :message => e.message }
}

Библиотека json подмешивает в стандартный класс Hash метод #to_json который преобразует его в строку JSON, таким образом формируется ответ на запрос resp.to_json.

Если Вы скачали код примера с гитхаба, то там есть еще тестовый ModBus TCP сервер, если вы запустите сервер ruby simple_pca.rb, то можете убедится в его работе с помощью браузера http://127.0.0.1:4567/mb/127.0.0.1/8502/1/coils/6/12. Если вы установили все гемы, то должно работать. Если вы пользователь браузера FireFox то для красивого просмотра JSON есть расширение

 Выводы 

В данной статье я показал, как просто можно реализовать доступ к полевым устройствам с помощью REST-PCA. У инженеров АСУ ТП наверно возникнет вопрос: "А что нам с этим делать?". Наверно ничего, идея REST-PCA пока является просто идеей, но это не значит что ее не следует развивать! Использование REST и простых форматов XML, JSON, HTML позволит создавать простые клиенты с нуля. Например, веб страницы с Javascript или документ Execel, который получает данные в XML с помощь VBA и выводит их на таблицы. Если все таки сообщество людей, которые заинтересованы в этой технологии разродится каким то суженным стандартом, который определит структуры входных и выходных данных, то можно будет рассуждать о серьезной разработке SDK, REST-PCA серверов с поддержкой протоколов ведущих вендеров в автоматизации и расширений существующих SCADA систем.

Зачем нам это, если есть OPC?

Проблему взаимодействия в системах автоматизации на данный момент решают на основе OPC технологии.О проблеме с OPC сказано в "Концепция REST-PCA".  Я же в свою очередь, как инженер АСУ ТП и пользователь этой технологии могу заявить о важных недостатках этого протокола:
  • Господство реализации OPC на DCOM технологии. Сама DCOM технология устарела. К тому же у нее серьезные проблемы в плане работы в  сетях. Обеспечение соединения в  пределах Intranet предприятия является настолько нетривиальной задачей, что многие компании покупают или разрабатывают специальные шлюзы, которые на узлах выглядят как OPC сервера\клиенты, а транслируют данные по сети по своему протоколу. Ну не бред ли??;
  • OPC навязывает использование Windows. Спорно, но все же, я считаю что Windows является не подходящей системой для задач АСУ ТП и П, т.к.  имеет проблемы с надежностью, особенно это касается вирусов. Лучше будет, если серверные части систем автоматизации вернутся к Unix подобным системам, по примеру серверов интернета;
  • Масштабируемость. Этот недостаток явно проявляется когда, предприятие дорастает до внедрения систем типа MES или по нашему АСУ П, которые по OPC протоколу начинают собирать данные с разрозненных АСУ ТП. Поскольку теги OPC в SCADA системах требуют конфигурации, то после того как что-то где-то поменялось, в MES системах требуется переописанние, которое, как правило, из-за плохого взаимодействия служб предприятия, не происходит, что приводит к неадекватности системы. В такой ситуации данные необходимо получать скопом, как это делается в SQL запросах. В REST сервисах, доступ к новым тегам можно осуществить через запрос данных к вышестоящему узлу. Например http://rest.pca.local/scada-1/nasos/ - мы получим список всех сигналов насоса. Конфигурацию можно заменить на правила!!! Такой подход для систем уровня выше чем АСУ ТП более приемлемый.

Будущее

Что касается внедрении инноваций, из всех известных мне отраслей компьютерной техники, АСУ ТП является, пожалую, самой инертной. Я думаю, это вызвано, с одной стороны, необходимостью обеспечения совместимости сверху вниз, т.к. существует огромное количество уже внедренных систем, которые нужно сопровождать (например, текущая версия Intouch 10, но она совместима еще со 2-й). Отсюда чем старше система, тем она страшнее))) А с другой стороны, рынок захвачен небольшой группой вендоров, которые не пропускают "свежую струю". И не потому что они злые, просто если у заказчика везде стоит Intouch, то он мало заинтересован в чем то другом, а затраты на миграцию всего предприятия на что-то другое, навряд ли в итоге окупиться.Получается чем больше существующих внедрений, тем больше внедрений и апгрейтов в будущем. А откуда взяться внедрениям у нового никому неизвестного, но очень хорошего продукта!?

Диаметрально противоположно обстоят дела в области веб приложений, в ней просто бурлят самые разнообразные технологии, развитии настолько стремительное, что за каких-то 20 лет существования интернета (если начать считать от появления HTTP протокола), мы пришли от простеньких HTML страничек, до операционных систем в браузере!! Заслуга тому открытость стандартов HTML, CSS, JavScript, XML и конечно,REST. Интернет и есть REST, все проблемы взаимодействия уже давно решены, этим никто не занимается, развиваются только форматы данных.

Я думаю, что перемены к лучшему в АСУ ТП наступят когда появятся большее количество открытых и доступных стандартов и технологий.Быть открытой технологией - это преимущество, пример тому ModBus.

Я верю, что REST-PCA является новым стандартом де-факто для автоматизации. И тому есть объективные причины:
  • Открытость. REST-PCA пока не является строгой спецификацией, но закладывается, как открытый протокол. Что подразумевает доступность его спецификаций и отсутствие лицензионных отчислений;
  • Переносимость. Мало, кто знает, но HTTP был не единственным претендентом на протокол всемирной паутины и не самым лучшим. Причина его успеха - переносимость. Он оказалась прост в реализации и был быстро перенесен на все платформы. Создание простого веб сервера с нуля не сложная задача, к тому же такой сервер можно уместить даже в 8-битном микроконтроллере!  REST-PCA наследует все эти свойства.
  • Расширяемость. REST-PCA позволяет расширять структуры данных под задачи, это позволит применять его для взаимодействия систем отличных от стандартной АСУ ТП модели  PLC<-->SCADA 

Вместо заключения


На данной момент за REST-PCA никого нет. Домен http://rest-opc.org недоступен (я планирую его зарегистрировать на себя), копия его контента тут. Что такое xpca,org я не понимаю, автор проекта мне не отвечает. Конечно, так это оставлять нельзя. По этому просьба ПРИЗЫВ к думающим и мечтающим людям, работающим в сфере АСУ ТП, АСУ П, да и просто программистам и инженерам, которые могут решать интересные задачи, если есть желание помочь в развитие этой идеи, пишите мне. Будем собирать сообщество и двигаться дальше.

Спасибо за внимание.


 

Комментариев нет:

Отправить комментарий