Односторонние S7-коммуникации в Simatic. Как организовать обмен ПЛК-ПЛК, программируя и загружая только один ПЛК?
S7-соединения, применяемые для обмена информацией между устройствами серии S7-300, 400, 1200, 1500, являются конфигурируемыми. Это означает, что необходимо явно указать устройствам, кто с кем общается. В общем случае эти соединения являются двунаправленными (двухсторонними или же bilaterally), т.е. добавление конфигурируемого соединения происходит для обоих ПЛК, что приводит к необходимости прогрузки обоих ПЛК. Это не вызывает особенных проблем, если оба ПЛК находятся в ведомстве службы АСУТП предприятия, и для обоих ПЛК есть на руках актуальное прикладное ПО. Но встречаются и частные случаи — необходимо наладить обмен между «старым» S7-300 и «новым» S7-1500, причем, исходники старой программы утрачены. Ну, или эксплуатация просто боится трогать ПЛК. Или эксплуатации просто нет, задача организации обмена «висит» на интеграторе «новой» системы, что приводит к предыдущему условию — трогать ППО неизвестной системы никому особо не хочется.
В таких случаях поможет имеющаяся возможность настроить одностороннее (unilaterally) соединение, сделать его активным (то есть, «наш» ПЛК будет инициировать соединение), добавить коммуникационные программные блоки PUG/GET (с другими блоками этот метод не работает, все другие блоки являются двухсторонними) и загрузить только один ПЛК. Будем считать, что информация по содержимому блоков данных у нас хоть как-то сохранилась — либо из документации на систему, либо из описания тэгов SCADA-системы.
Кратко об одно- и двухсторонним соединениям написано в базе знаний по ссылке.
Много интересного на тему пишет Герр Бергер:
Hans Berger «Automating with SIMATIC S7-1500», стр. 761
Самое важное: мы задаем S7-соединение только на одном CPU, соединение должно быть «активным» (active side), блоки PUT/GET так же вызываются только на «нашей стороне», connection resource партнерской стороны должен быть задан равным 03. Собственно говоря, ресурс за номером 03 уже мониториться средствами операционной системы контроллера, что и дает возможность применять односторонние S7-соединения.
В качестве «черного» ящика я применяю ET200S CPU, который фактически является контроллером серии S7-300 в корпусе на din-рейку. Про черный ящик мне известны только ip-адрес интерфейса и абсолютные адреса трех вещественных переменных, которые с него необходимо считать.
ip-адрес: 192.168.43.4
тэги: DB1.DBD0, DB1.DBD4, DB1.DBD8
В качестве «нашего» контроллера выступает S7-1516, к его прикладной программе и конфигурации мы имеем полный доступ.
В первую очередь добавляем в прикладную программу блок данных, куда мы разаместим результат чтения. Пусть это будет DB13. Не забываем, что при PUT/GET коммуникациях блок данных должен быть «стандартного» доступа.
Далее переходим в Devices & networks.
Нажимаем на кнопку Connections и из выпадающего списка справа от кнопки выбираем S7 connection
Кликаем правой кнопкой по CPU и в выпадающем меню нажимаем Add new connection
У центрального процессора S7-1516 несколько встроенных интерфейсов, поэтому необходимо выбрать тот, к которому подключен наш коммуникационный партнер. В нашем случае это интерфейс X1. Обращаю внимание на Local ID (0x100) — это идентификатор соединения, который потребуется при вызове блока GET.
Ждем Add, закрываем окно и переходим на подвкладку Connections
Задаем ip-адрес партнера
На вкладке Address details задаем rack/slot партнера, его Connection resource оставляем 03. Rack — это номер «стойки», slot — это номер «гнезда», вместе эти параметры «адресуют» CPU в системах Simatic. В нашем случае (как и в подавляющем числе других случаев «классических» ПЛК) эти значения равны 0/2.
Теперь открываем OB1 и добавляем в него функциональный блок GET для чтения данных с коммуникационного партнера.
Заполнять параметры вызова GET можно, как через Properties (организовано все весьма удобно), да и традицонно. Заполним традиционно.
Req — булевая переменная, по переднему фронту которой выполняется запрос данных.
ID — идентификатор соединения, в нашем случае равен 0x100
ADDR_1 — абсолютный адрес читаемой области данных удаленного контроллера, это указатель типа ANY
RD_1 — тут мы указываем, где локально будут складываться считанные даные, и лучше его задать тоже в виде указателя типа ANY. Чуть ниже я объясню, почему.
Для начала прочитаем одну вещественную переменную по удаленном адресу DB1.DBD0.
Немного про страшные символы в поле ADDR_1. Это — указатель типа ANY, позволяет адресовать любую область памяти Simatic. В нашем случае его значение P#DB1.DBX0.0 BYTE 4
DB1 — читаем блок данных с номером 1
DBX0.0 — смещение в блоке данных 0 байт (ну и 0 бит)
BYTE 4 — читаем байты в количестве 4 штук
Таким образом указатель P#DB1.DBX0.0 BYTE 4 соответствует абсолютному адресу DB1.DBD0. Напоминаю, что речь идет про адреса памяти удаленного коммуникационного партнера, о котором в нашем локальном проекте TIA Portal нет никаких данных.
Результат чтения положим в переменную R0 локального блока данных “xEchangeDB”.R0 (что в виде указателя ANY выглядит, как P#DB13.DBX0.0 BYTE 4).
Запрос на чтение (тэг Tag_1) я взвожу самостоятельно, сбрасывается он автоматически при завершении коммуникаций. Напоминаю, что запрос выполняется только по положительному фронту на входе REQ функционального блока, поэтому в реальных проектах не забываем взводить/сбрасывать переменную для запроса.
Взведем переменную Tag_1 и посмотрим на результат чтения:
Итого, с удаленного ПЛК прочиталось значение вещественной переменной, равное 666. Именно такую величину я и дал переменной CPU S7-300 до того, как объявить его черным ящиком. Напомню, что пока я читаю только первые 4 байта из блока данных удаленного контроллера. По этой причине мы видим, что переменные R1 и R2 нулевые — они не читались.
Для чтения всех 3 переменных с коммуникационного партнера изменим указатели типа ANY и вызов приобретет следующий вид:
Посмотрим на результат чтения:
Все три переменные считались успешно. Вообще, полезно выставлять флаг успешности или неуспешности коммуникаций на основании выходных переменных вызова GET: NDR — получены новые данных, ERROR — возникла ошибка и STATUS — текущий статус, код ошибки. Не забываем, что эти переменные «живут» только один цикл сканирования программы, и их надо вылавливать и складывать в отдельные переменные, чтобы увидеть. В данном примере этот момент я опускаю.
Для обозначения локального адреса переменных я использую указатель типа ANY, а не символьное представление структуры. По непонятным для меня причинам символьное представление работает для вызова GET (чтение данных с удаленного контроллера), но делает полностью неработоспособным вызов PUT (запись данных на удаленный контроллер). Причем, PUT даже не сообщает об ошибке и никак не реагирует выходом STATUS. Пока не могу объяснить причину такого поведения.
GET предназначен для чтения данных. Для передачи информации (записи) удаленному контроллеру применяем вызов PUT. Предположим, что нам необходимо записать в другой контроллер одну переменную. Пусть это будет та же переменная R0 блока данных DB13. Добавим вызов PUT в программу контроллера.
Изменим значение локальной переменной
После чего выполним запись данных, задав значение переменной Tag_7 равным «1». Ну, а теперь, чтобы все было по-честному взглянем на значение этой переменной в «черном ящике».
Запись данных выполнена успешно. Таким образом, даже не имея на руках исходную программу контроллера, мы можем как считывать с него информацию, так и записывать. Объем данных ограничен и зависит от типа CPU. ЕМНИП, для «трёхсотой серии» посылка ограничена 160 байтами, подробнее можно узнать в документации. Отсутствие необходимости вносить изменения в ППО справедливо лишь для 300ой и 400ой серии, для «тысячников» требуется разрешить доступ к CPU по методу PUT/GET, поставив соответствующую галочку в системных настройках.