Практическая задача
Классическая КИС-задача, Ротация: переключение работы исполнительных устройств по недельному графику.
В проекте я использовал библиотечный недельный таймер.
Однако у него есть особенность: если в момент когда таймер должен сработать на объекте не было электроэнергии, то библиотечный таймер "пропускает" день / момент когда нужно сработать (или я плохо умею выполнять copy/paste).
На промышленных объектах, там где есть круглосуточная служба эксплуатации, это не есть проблема, а это есть пункт инструкции по эксплуатации.
В микро-объектах - это не то чтобы тоже проблема Века, но: "чегой-то ваш контроллер такой глупый"
Второй вариант явился триггером в конкретном случае который меня попросили до-SMART-ить
Решение задачи принял такое:
- запоминаем дату (и время) когда должно переключится (в энергонезависимой/RETAIN переменной)
- мониторим часы и если после ре-старта (по питанию) время "прошло", то выставляем флаг: "уже все было".
Для решения задачи нужно
- вычислять день недели, текущий
- вычислять день недели, следующий
- посадить "сторожевого пса" (WhatchDog), который будет сидеть и лаять.
Технологический процесс основан на недельных повторяющихся циклах поэтому решил что целесообразно использовать в качестве "значения для контроля" Номер и время следующего дня недели.
Первое с чем столкнулся - написал выше: "как получить текущий день недели"?
Задачу решил, выше, в сообщениях, и ниже, в этом сообщении, увидите .
Вычислить значение следующего дня недели (какое значение возвратит get_date_time()), зная текущий день недели) - это простая формула
Второе с чем столкнулся, это с тем что в функциональных блоках на ST нет типа энергонезависимая переменная.
Это не проблема, решение понятное - использовать переменную в МАКРОСЕ (возможно что причина в продуктовом маркетинге, но для себя объясняю длинной LOG-а - чего еще запланировано сделать).
Ниже привожу что у меня получилось (картинки не из проекта, но из отладки)
Верхний уровень (листа)
Верхний лист.png
МАКРОС "Сторожевой пес" (макрос - потому что нужна энергонезависимая переменная)
Сторожевой пёс.png
И функциональный блок на ST для вычисления "следующего Дня недели", проверки входных параметров (на ST удобнее проверять значения входных параметров)
В коде FB я намеренно внёс ошибку. Четыре ошибки. Сделал небольшой тест для тех, кто будет использовать этот код при сдаче проекта преподавателям![]()
Одна найдется быстро - ее подсветит компилятор
Две - найдутся если разобраться в алгоритме (подсказка: нет ли лишних переменных в формулах)
Ну а 4-ая - возможно Вы получите эстетическое удовлетворение когда вы ее найдетену или скажите "тоже мне ошибка, это же элементарно"
///<Description>Вычисляет значение (в секундах), которое будет возращенно функцией get_date_time() в СЛЕДУЮЩИЙ день недели (час, минуту, секунду). Может быть использовано для энергонезависимой логики управления в запланированное время (час, минута, секунда) в запланированный день недели.</Description>
///<GroupName>Дата и время</GroupName>
(*
ПРИМЕР ИСПОЛЬЗОВАНИЯ
РАБОЧИЙ ЦИКЛ
- установить на входы номер дня недели и время. Например: "SET_WeekDay = 1, SET_HOUR = 10, SET_HOUR =15, SET_SECOND = 45" / Понедельник в 10 часов 15 минут 45 секунд.
- сохранить значение выхода GET_NextWeekTime в энергонезависимой переменной T (значение Т - это значение (секунды) системной функции get_date_time() в следующий Понедельник в 10 часов 15 минут 45 секунд)
- в каждом цикле управляющей программы сравнивать значение T и текущее значение, возвращаемое GET_DateTime / системной функцией get_date_time()
- когда значение GET_DateTime станет равно значению энергонезависимой переменной T, выполнить управление по событию: "Понедельник - 10:15:45 - НАСТУПИЛ"
ПЕРЕЗАПУСК управляющей программы после восстановления электропитания (возобновлении работы управляющей программы)
- в "первом цикле" управляющей программы исключить перезапись значения Т новым значением GET_NextWeekTime
- в "первом цикле" управляющей программы сравнить значение энергонезависимой переменной T и текущее значение, возвращенное GET_DateTime / системной функцией get_date_time()
- если значение GET_DateTime больше или равно значению Т, выполнить управление по событию: "Понедельник - 10:15:45 - ПРОШЕЛ/ПРОПУЩЕН"
ОБРАТИТЬ ВНИМАНИЕ
Если на входы установить день недели совпадающий с СЕГОДНЯ (со значением системных часов реального времени), но задаваемый Час и/или Минута и/или Секунда больше чем текущее время, то значение GET_NextWeekTime будет установлена на СЕГОДНЯ, на указанные час, минута, секунда.
*)
function_block fbGetNextWeekDayInSecond
var_input
///<Description>Допустимые значения: TRUE - ВКЛючена FALSE - ОТКЛючена </Description>
ENABLE : bool ;
///<Description>День недели. Допустимые значения от 1 (ПН) до 7 (ВС)</Description>
SET_WeekDay : udint;
///<Description>Час. Допустимые значения от 0 до 23</Description>
SET_HOUR : udint;
///<Description>Минута. Допустимые значения от 0 до 59</Description>
SET_MINUTE : udint ;
///<Description>Минута. Допустимые значения от 0 до 59</Description>
SET_SECOND : udint ;
end_var
var_output //объявление выходных переменных
///<Description>Значение следующего дня недели и времени. Значение в секундах, возвращаемых системной функцией get_date_time(). При ошибке во входных параметрах и в ОТКЛюченном состоянии - 0.</Description>
GET_NextWeekTime: udint;
///<Description>Значение, возвращенное системной функции get_date_time(). При ошибке во входных параметрах и в ОТКЛюченном состоянии - 4294967295 (максимальное значение для типа данных).</Description>
GET_DateTime: udint;
end_var
var //объявление локальных переменных
ucSecondPerDay : udint; // константа, число секунд в сутках
ucSecondPerHour : udint; // константа, число секунд в часе
ucSecondPerMinute : udint; // константа, число секунд в часе
ucDaysPerWeek : udint; // константа, число дней в неделе
ucSYSYear : udint; // константа, год начала работы счетчика секунд системной функции get_date_time() / 01.01.2000
ucSYSWeekDay : udint; // константа, день недели начала работы счетчика секунд системной функции get_date_time() / 01.01.2000
uWeekDay_TODAY : udint; // вычисляемый текущий день недели
uHour : udint; // вычисляемый текущий час
uMinute :udint; // вычисляемая текущая минута
uSecond :udint; // вычисляемая текущая секунда
end_var
// Выход если вычисления не требуются или ошибка во входных параметрах.
if not ENABLE
or ( SET_WeekDay < 1 or SET_WeekDay > 7 )
or ( SET_HOUR < 0 or SET_HOUR > 24 )
or ( SET_MINUTE < 0 or SET_MINUTE > 59 )
or ( SET_SECOND < 0 or SET_SECOND > 59 )
then
GET_NextWeekTime:= 0;
GET_DateTime := 4294967295; // максимальное значение для типа данных
RETURN;
end_if
// Устанавливаем константы
ucSecondPerDay := 24*60*60; // число секунд в одних сутках
ucSecondPerHour := 60*60; // число секунд в одном часе
ucSecondPerMinute := 60; // число секунд в одной минтуе
ucDaysPerWeek := 7; // число дней в неделе
ucSYSYear := 2000; // год начала работы счетчика секунд системной функции get_date_time() / 01.01.2000
ucSYSWeekDay := 6; // день недели начала работы счетчика секунд системной функции get_date_time() / 01.01.2000 / Суббота
// Устанавливаем значение выхода: значение системной функции get_date_time() в секундах
GET_DateTime := dt_to_udint(get_date_time());
// конвертируем в номер текущего дня недели значение (в секундах) системной функции get_date_time() и значение константы (день недели начала работы начала работы счетчика секунд системной функции get_date_time())
// результат на шкале: 0 - ВС ... 6 - СБ
uWeekDay_TODAY := ( ( GET_DateTime / ucSecondPerDay) + ucSYSWeekDay) mod ucDaysPerWeek; // последовательность номеров дней недели не нарушается в високосный год
// конвертируем в шкалу: 1 - ПН ... 7- ВС
if uWeekDay_TODAY = 0 then uWeekDay_TODAY := 7; end_if;
// конвертируем в текущий час, минуту, секунду значение (в секундах) системной функции get_date_time()
uHour := ( GET_DateTime mod ucSecondPerDay ) / ucSecondPerHour; //Час системых часов реального времени
uMinute := ( ( GET_DateTime mod ucSecondPerDay ) mod ucSecondPerHour ) / ucSecondPerMinute; //Минута системых часов реального времени
uSecond := ( ( GET_DateTime mod ucSecondPerDay ) mod ucSecondPerHour ) mod ucSecondPerMinute; //Секунда системых часов реального времени
// расчитываем значение (в секундах) системной функции get_date_time() для запрошенного "следующего" дня недели, часа, минуты, секунды
if (uWeekDay_TODAY = SET_WeekDay)
and
(
(
SET_HOUR ??? uHour // маленький тестНа какое подходящий оператор сравнения нужно заменить "???". Это может быть ">", "<", "=". Какой правильный ответ?
)
or
(
SET_HOUR = uHour and SET_MINUTE > uMinute
)
or
(
SET_HOUR = uHour and SET_MINUTE = uMinute and SET_SECOND > uSecond
)
)
then
// значение для текущего дня недели
GET_NextWeekTime := ( ( GET_DateTime / ucSecondPerDay ) * ucSecondPerDay) + (SET_HOUR * ucSecondPerHour) + (SET_MINUTE * ucSecondPerMinute) + SET_SECOND / ucSYSYear;
else
// значение для следующего дня недели
GET_NextWeekTime := ( ( ( GET_DateTime / ucSecondPerDay) + ucDaysPerWeek - uWeekDay_TODAY + SET_WeekDay) * ucSecondPerDay) + (SET_HOUR * ucSecondPerHour) + (SET_MINUTE * ucSecondPerMinute) + SET_SECOND * ucSecondPerDay;
end_if
end_function_block
Последний раз редактировалось Shiryaevo; 22.08.2024 в 21:10.
У меня нет никаких причин с Вами спорить.Согласитесь, что, вот так
<cat>
выглядит намного приятнее глазу
Последний раз редактировалось Shiryaevo; 22.08.2024 в 21:20.
День недели для любой даты, а не только для сегодня т.е не использую функцию get_date_time() https://owen.ru/forum/showthread.php...l=1#post441126
Код:FUNCTION dayOfWeek : UDINT; // (c) PeterA VAR_INPUT year: udint; mounth: udint; day:udint; Sun_7:bool; //если 1, то воскресенье 7 день, иначе 0 END_VAR VAR MM: ARRAY [1..12] OF udint := [5,1,1,4,6,2,4,0,3,5,1,3]; dayOfWeekTMP: udint; END_VAR IF udint_to_bool(year MOD 4 ) THEN MM[1]:= 5; MM[2]:= 1; ELSE MM[1]:= 4; MM[2]:= 0; END_IF dayOfWeekTMP:= (year+year/4+ MM[mounth] + day) MOD 7; dayOfWeek:= dayOfWeekTMP + bool_to_udint(Sun_7 AND dayOfWeekTMP=0) * 7; end_function
Мой канал на ютубе
https://www.youtube.com/c/ПетрАртюков
Мой канал на РУТУБЕ
https://rutube.ru/channel/23641433/
Библиотека ГМ для СП300
https://disk.yandex.com/d/gHLMhLi8x1_HBg
А ПР-ки уже могут в ARRAY?