...и так закончилось - "на смотр художественной самодеятельности"...
тов.Бывалов, кинофильм "Волга, Волга", 1938 год
Когда я приступил к задаче подключения фасадного освещения, то наткнулся на проблему, которую совсем не ожидал получить - контроллер Beckhoff BC9000 не имеет RTC (часы реального времени)! То есть никак не спросить "А вы не скажете, который час?". Данная проблема, честно говоря меня подкосила, так как такой подлости я не ожидал. Конечно, у меня есть компьютер-сервер, который работает круглосуточно и через ADS я могу в нужное мне время дать команду контроллеру на включение нужного выхода... Но ведь неаккуратненько как-то!! Я уже начал подумывать о покупке более совершенного контроллера и перечитывая документацию на новый контроллер на сайте Beckhoff наткнулся случайно на такую фразу ".. так как обращение к внутренним системным часам RTC достаточно медленное, то мы не рекомендуем обращаться к ним каждый раз в программном цикле. Используйте программную реализацию RTC в TwinCat с помощью соотвествующих библиотек.."
Ага! то есть в TwinCat реализованы программные часы! Работают они достаточно просто - надо в начале работы их один раз запустить и потом можно пользоваться. Для того, чтобы использовать программные часы в контроллере BC9000, нужно подключить к проекту библиотеку TcPlcUtilitiesBC.Lb6 (копируется на диск при инсталяции TwinCat). После этого, в переменных нашей программы создаем функциональный блок типа RTC
Код: Выделить всё
PROGRAM MAIN
VAR
BUTT_H2_R1 : SM_BUTT; (*ГОСТЕВОЙ ДОМ - КНОПКА -СПАЛЬНЯ 1*)
.......
INT_RTC : RTC; (* ПРОГРАММНЫЕ ЧАСЫ*)
END_VAR
Код: Выделить всё
INT_RTC (EN:=INT_CLOCK_INITFLAG, PDT:=INT_CLOCK_INITTIME); (* согласно документации вызываем блок в каждом цикле *)
IF INT_CLOCK_INITFLAG THEN (* после разовой инициализации, сбрасываем флаг *)
INT_CLOCK_INITFLAG := FALSE;
END_IF
INT_CLOCK_STATUS := INT_RTC.Q; (* если часы запущены, то статус будет TRUE.... *)
INT_CLOCK_CURR := INT_RTC.CDT; (* ... и получаем текущеее время для дальнейшей работы *)
- флажок статуса (INT_CLOCK_STATUS) который сигнализирует, что часы были инициализированы, в настоящий момент работают и ими можно пользоваться
- непосредственно само текущее время (INT_CLOCK_CURR), которое мы дальше можем использовать в своих целях.
Ну что же, неплохо... Теперь для запуска надо как-то дать контроллеру текущее время. Это можно сделать двумя способами:
1. Передача текущего времени от компьютера-сервера по ADS
Объявляем в фиксированной области памяти (чтобы они были доступны извне) следующие переменные
Код: Выделить всё
INT_CLOCK_CURR AT %MB10: DATE_AND_TIME;
INT_CLOCK_INITTIME AT %MB14: DATE_AND_TIME;
INT_CLOCK_INITFLAG AT %MX18.0: BOOL := FALSE;
INT_CLOCK_STATUS AT %MX18.1: BOOL := FALSE;
Теперь нам нужно написать на компьютере-сервере программку, которая будет по ADS передавать текущее стартовое время. Так как сервер у меня на Windows XP то для простоты воспользуемся встроенным Windows Scripting Host. Создаем текстовый файл с расширением *.VBS и пишем в нем следующее
Код: Выделить всё
Dim TcPLC ' переменная для объекта подключения
Dim INT_CLOCK_INITFLAG ' флаг инициализации на контроллере
Dim INT_CLOCK_INITTIME ' время инициализации на контроллере
Dim INT_CLOCK_CURR ' текущее время на контроллере
Dim COMP_CLOCK_CURR ' текущее время на компьютере
'создаем объект и подключаемся к контроллеру
Set TcPLC = CreateObject("TCSCRIPT.TcScriptSync")
Call TcPLC.ConnectTo("192.168.208.20.1.1", 800)
If (TcPLC.ReadAdsState() <> 5) Then
WScript.echo("Контроллер не найден или не работает!")
Else
'расчитываем текущее время на компьютере
COMP_CLOCK_CURR = DateDiff ("S", "01/01/1970", Date()+Time())
'считываем время на контроллере
INT_CLOCK_CURR = TcPLC.ReadInt32(&H4020, 10)
'если разница во времени больше 5 секунд, то обновляем время на контроллере
If Abs(COMP_CLOCK_CURR-INT_CLOCK_CURR)>5 Then
'для этого записываем в контроллер правильное время
Call TcPLC.WriteInt32(&H4020, 14, COMP_CLOCK_CURR)
'и взводим флаг чтобы контроллер понял, что надо обновить время
Call TcPLC.WriteBool(&H4021, 18*8, 1)
End If
WScript.echo("Время на контроллере актуально.")
End If
ЗАМЕЧАНИЕ. Согласно документации от Beckhoff программные часы могут врать. По их заявлениям ошибка может набегать до 1 минуты за 24 часа работы. По опыту подтверждаю, что ошибка действительно накапливается в указанных пределах и контроллер начинает отставать по времени. Надо перодически запускать скрипт, чтобы подправить время. В моем скрипте выше проверяется наличие расхождения в 5 секунд.
2. Текущее время по SNTP
На нашем форуме появляются все новые Beckhoff-последователи. Участник форума под ником Sunseich нашел еще один способ решения вопроса актуализации времени на контроллере. На сайте Beckhoff бесплатно представлена библиотека, которая содержит функицональный блок FB_SNTP. Данный функциональных блок позволяет получить точное время у сервера по стандарту SNTP. Тоже достаточно красивый метод. Однако к нему пришлось идти с определенными трудностями. Библиотека SNTP работает только на прошивке контроллера BB и выше. У меня и у Sunseich прошивка контроллера была ниже. Как я писал год назад, я остался доволен оперативностью службой поддержки Beckhoff в Москве. Однако в этот раз нас ждало разочарование. Служба поддержки долго мурыжила Sunseich, пыталась сказать, что новые прошивки теперь могут установить только сотрудники компании, и в итоге отказали дать прошивку, так как контроллер был куплен не у них стандартно сославшись на "политику компании". Логично, когда политика любой компании направлена на популяризацию собственных продуктов, так как основная цель - продавать много. Многие бренды давно поняли это, предоставляя общемировой сервис... Но да ладно, это не моего ума дело. В конце концов я не имею акций компании Beckhoff и ее продажи меня мало волнуют. Мне нужна была прошивка В итоге было написано письмо немцам, с которыми я познакомился покупая модули для контроллера. Они занимаются альтернативной энергетикой и являются хорошим партнером Beckhoff-a. Через день я уже получил самую свежую прошивку контроллера с кодом BE и благополучно поставил ее в течение 1 минуты (спасибо немцы!! Рот фронт!!).
Ну собственно переходим к коду. Объявляем переменные:
Код: Выделить всё
PROGRAM MAIN
VAR
INT_RTC : RTC; (* ПРОГРАММНЫЕ ЧАСЫ*)
INT_CLOCK_CURR AT %MB10: DATE_AND_TIME; (* ЗДЕСЬ БУДЕМ ХРАНИТЬ ТЕКУЩЕЕ ВРЕМЯ *)
INT_CLOCK_INITTIME AT %MB14: DATE_AND_TIME;
INT_CLOCK_INITFLAG AT %MX18.0: BOOL := FALSE;
INT_CLOCK_STATUS AT %MX18.1: BOOL := FALSE;
SNTP : FB_SNTP; (*ФУНКЦИОНАЛЬНЫЙ БЛОК ДЛЯ ОБЩЕНИЯ С СЕРВЕРОМ *)
SNTP_FLAG: BOOL := TRUE; (* ФЛАГ ПОДКЛЮЧЕНИЯ К СЕРВЕРУ *)
SNTP_TIMER: TON; (* ТАЙМЕР ДЛЯ ПЕРИОДИЧЕСКОГО ОПРОСА SNTP *)
sServer : STRING(15):='77.41.132.50'; (* ОДИН ИЗ ПУБЛИЧНЫХ SNTP СЕРВЕРОВ В ЗОНЕ RU *)
END_VAR
Код: Выделить всё
SNTP_TIMER (IN := TRUE, PT := t#60s); (*будем сверять время каждую минута*)
IF SNTP_TIMER .Q THEN
SNTP_TIMER (IN := FALSE);
SNTP_FLAG := TRUE; (* пора сверить часы *)
END_IF
IF SNTP_FLAG THEN
(* стучимся к SNTP серверу не дольше чем 5 секунд *)
SNTP (bStart := TRUE, sSntpServer := sServer, tTimeOut := t#5s);
IF SNTP.bBusy = FALSE THEN
INT_CLOCK_INITFLAG := TRUE;
INT_CLOCK_INITTIME := SNTP.tTime; (* если достучались то имеем точное время *)
SNTP (bStart := FALSE);
SNTP_FLAG := FALSE;
END_IF
END_IF
INT_RTC (EN:=INT_CLOCK_INITFLAG, PDT:=INT_CLOCK_INITTIME); (* согласно документации вызываем блок в каждом цикле*)
INT_CLOCK_INITFLAG := FALSE; (* после инициализации сбрасываем флаг *)
INT_CLOCK_STATUS := INT_RTC.Q; (* если часы запущены то статус будет TRUE.... *)
INT_CLOCK_CURR := INT_RTC.CDT; (* ... и получаем текущее время для дальнейшей работы *)
3. А какой способ выбрать?
Я испробовал оба способа у себя на контроллере и остановился на первом способе.
Синхронизация времени с SNTP сервером имеет как плюсы:
- контроллер синхронизируется сам, наличие компьютера-сервера не обязательно
- время на контроллере сбрасывается при обновлении программы или обесточивании. Первое совсем не критично, а во-втором случае ситуация следующая - первым при включении питания оживает контроллер (ему надо на загрузку не более 10-15 секунд), вторым оживает роутер, который дает доступ в Интернет (40-50 секунд), ну и самым последним просыпается компьютер (на загрузку Windows с основными сервисами требуется 2-3 минуты). Таким образом, как только появляется доступ в Интернет контроллер имеет точное время.
... так и минусы...
- Интернет у нас в деревушке только на уровне полудохлого 3G, много неудач при подключении
- мне не столько важно, чтобы на контроллере было точное время. Мне важно, чтобы оно было одинаковым с компьютером-сервером на котором происходит запись в базу данных всех событий.
В итоге, минусы перевесили и я пока остановился на первом способе. Хотя можно будет потом придумать и что-то комбинированное.
Решив вопрос с часами я без проблем подключил фасадное освещение, о чем будет следующий пост.