Вход

Просмотр полной версии : Как получить "День недели" на ST из get_date_time()?



Shiryaevo
22.08.2024, 18:50
Здравствуйте!

Относительно давно не заходил на форум, задач по OWEN Logic не было.

Давеча появилась задача, нужно было чуть расширить ранее написанную управляющую программу функцией ... после напишу.
Для решения задачи потребовалось получать номер "дня недели" из ST-кода ПР200.
Первым делом посмотрел что уже есть на форуме, почитал обновления в релизах и обнаружил что теперь на языке ST можно написать функциональный блок ("создать экземпляр объекта") и появилась системная функция get_date_time(), которая возвращает количество секунд, прошедших с 01 января 2000 года (начало "эпохи" по ОВЕН-ски :)

Прогресс!
Молодцы производители OL, двигаются.

Однако для моей задачи (о чем ниже) нужен Номер Дня недели.
Почитал новости/посты производителя, узнал что на новой платформе (ПР205 и ПР103 и даже на ПР225) есть сервисная переменная "Номер дня недели", "погонял" эту переменную в тестовом проекте, но мою задачу на ПР200 это не решало.

Немного забегая вперед скажу что задачу я решил, но по ходу пьесы написал функциональный блок на ST для определения Года, месяца, дня, номера дня недели, час, минута, секунда. из системной функции get_date_time().

Конечно я исследовал не только новости :)
Проштудировал обновления в библиотеке макросов, новые функции OL (и их исправления :), изучил пример МАКРОСА (не функционального блока) для получения номера дня недели, хотел уже было использовать его, но возобладала многолетняя Школа программирования и потому выбрал путь: "сделаю свой джентльменский набор для извлечения даты и времени ST".

Код ФБ публикую дальше.
В основном для себя, чтобы когда вновь будет задача на железе OWEN, то смогу найти на форуме и не буду повторно изобретать свой же велосипед :)
Если же кто-то решит его изучить и даже возможно найдет полезным .... если кратко - Пож-та, я за вас заранее Рад! :)

Shiryaevo
22.08.2024, 19:11
И так код функционального блока на ST для извлечения "Год, Месяц, День месяца, Номер дня недели, Час, Минута, Секунда"
Для чего я "изобрел велосипед" если в библиотеке МАКРОСОВ уже есть готовый пример?

Для программистов объяснение простое - инкапсуляция.
Для электриков (и тех кто развился в КИС-овца из Школы Профессиональных Электриков) скажу так: специалист по эксплуатации ЩИТ-а не лезет внутрь. Для него разработчиком ЩИТ-а предусмотрены Лампы сигнализации, Переключатели управления на лицевой панели. Так же и для программиста - "спрятать" от пользователя устройство "внутренней кухни" - это не из разряда "красоты", а необходимый практический прием для системы, эксплуатируемой и поддерживаемый годами :)
Для эстетов визуального программирования ( а я общался на этом форуме с несколькими Профессионалами - без сарказма) обья]снение такое - по мне если приходится "протаскивать" сервисные переменные часов к каждому Блоку (макросу, ФБ), то это "захламляет полотно", прячет красоту логической мысли ... а тех для тех кто будет это в дальнейшем эксплуатировать ... вообщем может не получить эстетического удовольствия :).

Код функционального блока привожу как есть. В нем скорее всего есть ошибки, которые я не нашел :)
Но в будущем, если сам найду, то поправлю здесь .... собственно как написал выше - выкладываю в первую очередь для себя же :)

///<Description>Возвращает год, месяц, день, день недели, час, минуту, секунду на основании значения системной функции get_date_time(). В ОТКЛюченном состоянии все выходы равны 0. Может быть использовано в программном коде на ST.</Description>
///<GroupName>Дата и время</GroupName>

(*

ПРИМЕР ИСПОЛЬЗОВАНИЯ в коде на ST.

function_block NameOfMyFB
var //объявление локальных переменных
....
varDateTime : USR.getDateTime; //тип переменной - имя функционального блока
...
y,m,d,dw,h,m,s :udint; // переменные год, месяц, день, номер дня недели, час, минусат, секунда
...
end_var

....
// создаем экземпляр функционального блока, передаем значение на Вход, получаем значение Выходов (значение Выходов будет записано в указанные локальные переменные)
varDateTime (ENABLE := true, Year => y, Month => m, Day => d, DayOfWeek => dw, Hour => h, Minute => m, Second => s);

// Логика использования значений: год, месяц, день, номер дня недели, час, минута, секунда
....

end_function_block


ОСОБЕННОСТИ РЕАЛИЗАЦИИ
1. Функциональный блок может быть использован в управляющей программе прибора, который оснащен источником часов реального времени (доступны системные функции часов реального времени).
2. "Год, месяц, день" вычисляется в первом цикле управляющей программы (после создания экземпляра функционального блока) и далее, пере-вычисляется, в одном цикле управляющей программы, в котором произошла смена суток.
Из этой реализации есть два следствия:
а) "Длинные" циклы вычисления "Год, Месяц, День" выполняются только тогда, когда они необходимы. В остальных случаях "Год, Месяц, День" восстанавливается из локальной памяти экземпляра функционального блока.
б) при каждом ОТКЛ/ВКЛ на входе ENABLE происходит пересчет "Год, Месяц, День". Рекомендуется оценивать целесообразность изменения значения входа ENABLE в коде управляющей программы.

3. Функциональный блок используется значения, возвращаемые системными функциями get_date_time() и SYS.is_leap_year() для версии OL: 2.7.354.0.
При изменениях Производителем протокола работы этих системных функций требуется проверка (тестирование) кода функционального блока.

ОТКАЗ ОТ ГАРАНТИЙ
Права копирования, воспроизведения, передачи функционального блока, полностью и/или частично, реулируется лицензионным соглашением Производителя OL.
Код функцинального блока не использует другие лицензируемые библиотеки.
Код функционального блока приведен как есть, рекомендован к ознакомлению исключительно в образовательных целях.
Использование лицом функционального блока, полностью и/или частично, в проектах означает принятие этим лицом всех рисков убытков и/или любых других последствий.
С момента первого, полностью и/или частично, копирования и/или воспроизведения функционального блока вы соглашаетесь со всеми вышеперечисленными условиями отказа от гарантий (безусловный акцепт оферты).
*)

function_block USR.getDateTime //имя функционального блока.
var_input //объявление входных переменных
///<Description>ВКЛючить / ОТКЛючить работу функционального блока. В ОТКЛюченном состоянии все выходы равны 0. Допустимые значения: TRUE - ВКЛючен FALSE - ОТКЛючен. </Description>
ENABLE : bool;
end_var

var_output //объявление выходных переменных
///<Description>Год системных часов реального времени. Диапазон выходных значений: 0 - ФБ ОТКЛючен, 2000 .. 2136</Description>
Year : udint;
///<Description>Минута системных часов реального времени. Диапазон выходных значений: 0 - ФБ ОТКЛючен, 1 .. 12</Description>
Month : udint;
///<Description>Секунда системных часов реального времени. Диапазон выходных значений: 0 - ФБ ОТКЛючен, 1 .. 31</Description>
Day : udint;
///<Description>Номер Дня недели системных часов реального времени. Диапазон выходных значений: 0 - ФБ ОТКЛючен, 1 - ПН .. 7 - ВС</Description>
DayOfWeek: udint;
///<Description>Час системных часов реального времени. Диапазон выходных значений: 0 .. 23</Description>
Hour : udint;
///<Description>Минута системных часов реального времени. Диапазон выходных значений: 0 .. 59</Description>
Minute : udint;
///<Description>Секунда системных часов реального времени. Диапазон выходных значений: 0 .. 59</Description>
Second : udint;
end_var

var //объявление локальных переменных
ucSecondPerDay : udint := 86400; // константа, число секунд в сутках
ucSecondPerHour : udint := 3600; // константа, число секунд в часе
ucSecondPerMinute : udint := 60; // константа, число секунд в минуте
ucDaysPerWeek : udint := 7; // константа, число дней в неделе
ucSYSYear : udint := 2000; // константа, год для нулевого значения регистра секунд, возвращаемого системной функцией get_date_time() / 01.01.2000
ucSYSWeekDay : udint := 6; // константа, номер дня недели для нулевого значения регистра секунд, возвращаемого системной функцией get_date_time() / 01.01.2000 / Суббота
uLastYear : udint := 0; // переменная используется для хранения года
uLastMonth : udint := 0; // переменная используется для хранения месяца
uLastDay : udint := 0; // переменная используется для хранения дня
uLastDayOfWeek : udint := 0; // переменная используется для хранения номера для недели
uLastDateTime : udint := 0; // переменная используется для хранения предыдущего значения возвращаемого системной функцией get_date_time()
uaDayInMonths: ARRAY [0..11] OF UDINT; // масив констант, количество дней в месяце
end_var

// обнуление выходов и завершение исполнения, если вычисления не требуются
if not ENABLE then
Year := 0;
Month := 0;
Day := 0;
DayOfWeek := 0;
Hour := 0;
Minute := 0;
Second := 0;
RETURN;
end_if

// Присваиваем значения массиву констант: число дней в каждом месяце года
if 0 = uLastDayOfWeek then // инициализируем масив "один раз", в первом рабочем цикле после создания экземпляра функционального блока
uaDayInMonths[0] := 31; // число дней в месяце, ЯНВАРЬ
uaDayInMonths[1] := 28; // число дней в месяце, ФЕВРАЛЬ, в високосном году - 29.
uaDayInMonths[2] := 31; // число дней в месяце, МАРТ
uaDayInMonths[3] := 30; // число дней в месяце, АПРЕЛЬ
uaDayInMonths[4] := 31; // число дней в месяце, МАЙ
uaDayInMonths[5] := 30; // число дней в месяце, ИЮНЬ
uaDayInMonths[6] := 31; // число дней в месяце, ИЮЛЬ
uaDayInMonths[7] := 31; // число дней в месяце, АВГУСТ
uaDayInMonths[8] := 30; // число дней в месяце, СЕНТЯБРЬ
uaDayInMonths[9] := 31; // число дней в месяце, ОКТЯБРЬ
uaDayInMonths[10] := 30; // число дней в месяце, НОЯБРЬ
uaDayInMonths[11] := 31; // число дней в месяце, ДЕКАБРЬ
end_if

// Восстанавливаем значения год, месяц, день из локальной памяти экземпляра функцинального блока.
Year := uLastYear;
Month := uLastMonth;
Day := uLastDay;

// конвертируем значения get_date_time() в Номер Дня недели.
DayOfWeek := ( (dt_to_udint(get_date_time()) / ucSecondPerDay) + ucSYSWeekDay) mod ucDaysPerWeek; // високосный год не влияет на последовательность номеров дней в неделе
// переводим со шкалы: 0=ВС ... 6=СБ на шкалу: 1=ПН ... 7=ВС
if DayOfWeek = 0 then DayOfWeek := 7; end_if

// конвертируем значения get_date_time() в Час, Минута, Секунда
Hour := ( dt_to_udint(get_date_time()) mod ucSecondPerDay ) / ucSecondPerHour; //Час системых часов реального времени
Minute := ( (dt_to_udint(get_date_time()) mod ucSecondPerDay ) mod ucSecondPerHour ) / ucSecondPerMinute; //Минута системых часов реального времени
Second := ( (dt_to_udint(get_date_time()) mod ucSecondPerDay ) mod ucSecondPerHour ) mod ucSecondPerMinute; //Секунда системых часов реального времени

(*
Системная Дата и/или Время может быть изменена в системном меню прибора, по сетевому протоколу и ... при отладке в OL.
В случае измения системной Даты и/или Времени при котором не произошло изменение номера Дня недели, необходимо пересчитать Год, Месяц, День.
В локальной переменной uLastDateTime храним значение get_date_time() на предыдущем рабочем цикле управляющей программы.
Если разница между значением get_date_time() на предыдущем цикле больше 1 секунды значения get_date_time() на текущем цикле, то устанавливаем признак "Пересчитать Год, Месяц, День"
В качестве признака "Пересчитать Год, Месяц, День" используем значение переменной uLastDayOfWeek
*)
if abs(udint_to_real((dt_to_udint(get_date_time()) - uLastDateTime))) > 1.0 then uLastDayOfWeek := 0; end_if;
// uLastDateTime сохраняем значение get_date_time() для следующего рабочего цикла управляющей программы.
uLastDateTime := dt_to_udint(get_date_time());

(*
Вычисление "Год, Месяц, День" требует выполнения "длинной" цепочки арифмитических вычислений.
Значение "Год, Месяц, День" изменяется одн раз в сутки.
Целесообразно выполнять код вычисления "Год, Месяц, День" в случае если изменились Сутки.
Используем переменную uLastDayOfWeek в качестве признака "Пересчитать Год, Месяц, День".
Если не требуется выполнить пересчет "Год, Месяц, День", то выходим из ФБ.
*)
if uLastDayOfWeek = DayOfWeek then RETURN; end_if;

// Начинаем вычисление "Год, Месяц, День"
// uLastDayOfWeek сохраняем значение Номера Дня недели для следующего рабочего цикла управляющей программы.
uLastDayOfWeek := DayOfWeek;

// вычисляем текущий год. До 25 вычислений на 1 цикл управляющей программы для 2025 года. 30 для 2030 ... 136 для 2136 года (2136 - это предел регистра секунд системной функции get_date_time()).
Year := ucSYSYear; // начинаем с нулевой точки отсчета и далее вычитаем дни, пока не дойдем до текущего года
Day := dt_to_udint(get_date_time()) / ucSecondPerDay;
WHILE true DO
IF SYS.is_leap_year(Year) THEN
if Day > 366 then
Day := Day - 366;
Year := Year + 1;
else EXIT; end_if;
ELSE
if Day > 365 then
Day := Day - 365;
Year := Year + 1;
else EXIT; end_if;
END_IF;
END_WHILE;

// вычисляем месяц. До 12 вычислений на 1 цикл управляющей программы
Month := 0; // начинаем с января и далее вычитаем дни, пока не дойдем до текущего месяца
WHILE true DO
IF not SYS.is_leap_year(Year) THEN
if Day > (uaDayInMonths[Month]) then
Day := Day - uaDayInMonths[Month];
Month := Month + 1;
else EXIT; end_if;
ELSE
if Month = 1 then // февраль високосного года
if Day > (uaDayInMonths[Month] + 1) then
Day := Day - uaDayInMonths[Month] - 1; // в феврале високосного года 29 дней
Month := Month + 1;
else EXIT; end_if;
else
if Day > (uaDayInMonths[Month]) then
Day := Day - uaDayInMonths[Month];
Month := Month + 1;
else EXIT; end_if;
end_if;
END_IF;
END_WHILE;

// Корректируем результаты вычисления месяца и дня
Month := 1 + Month;
Day := 1 + Day;

// Сохраняем вычисленные значения в локальной памяти экземпляра функцинального блока.
// Вычисленные в этом цикле значения будут пере-использованы в следующих циклах управляющей программы.
uLastYear := Year;
uLastMonth := Month;
uLastDay := Day;

end_function_block

FPavel
22.08.2024, 19:19
Есть такая модель календаря - Юлианские даты.
https://ru.wikipedia.org/wiki/%D0%AE%D0%BB%D0%B8%D0%B0%D0%BD%D1%81%D0%BA%D0%B0%D 1%8F_%D0%B4%D0%B0%D1%82%D0%B0

Позволяет находить разницу между датами, дату следующих суток (с учётом високосного года и без массивов с размерами месяцев), день недели.

Кроме формулы с очень большими числами существуют разновидности, отсчитывающие от 01 января 2000 г. - они менее требовательны к ресурсам (размеру переменных). Сейчас их быстро не нашел, но интернет всё помнит.

kondor3000
22.08.2024, 19:20
Дата, Время и День недели через UnixTime с 1970 года на ST 78144


Тоже самое с 2000 года 78152
Дата, Время и День недели через DT на ST. Подходит для ПР200, ПР103 и ПР205, ПР225

PS: Добавил универсальный ФБ версия 5, без внешних функций и ФБ, всё встроено внутри. 86658
Для переноса в любой проект, достаточно скопировать 1 текст.
Может работать как функция (при подаче UNIX_TIME) , а при 0 на входе сам определяет время. Работает с Годом 2 знака (25) или 4 знака (2025).

Shiryaevo
22.08.2024, 19:23
По мотивам предыдущего "полного кода" привожу пример легкой функцию на ST, которая вычисляет только Номер дня недели

Визуально эта функция занимает "мало месте на холсте" при подаче на вход Макроса, вычислительно "легкая".
Для тех кто хочет изучить "как там под капотом", добавил комментарии.

///<Description>Вычисляет день недели на основании значения системной функции get_date_time(). Работает для диапазона дат, поддерживаемого системной функции get_date_time()</Description>
///<OutputDescription>Значение дня недели: 1 - ПН .. 7 - ВС. В ОТКЛючено(-м) состоянии: 0.</OutputDescription>
///<GroupName>Дата и время</GroupName>
function getSYSWeekDay: udint; //имя функции и тип данных выхода

var_input
///<Description>ВКЛючает/ОТКЛючает подсчет</Description>
Enable :bool;
end_var
var
ucSYSWeekDay : udint; // Константа, день недели начала счетчика секунд системной функции get_date_time() / 01.01.2000
end_var
if not Enable then
getSYSWeekDay := 0;
RETURN;
end_if
// Устанавливаем константу
ucSYSWeekDay := 6; // День недели начала работы счетчика секунд истемной функции get_date_time() / 01.01.2000 / Суббота. Диапазон значений: 0 - ВС .. 6 - СБ.
// вычисляем текущий день недели. get_date_time() возвращает количество секунд с 00:00:00 01.01.2000. Високосный год не влияет на последовательность дней в неделе.
getSYSWeekDay := ( (dt_to_udint(get_date_time()) / (24*60*60) ) + ucSYSWeekDay) mod 7; // Число прошедших суток с начала работы счетчика системной функции get_date_time() сдвигаем к "нулевой точке отсчета" (номер дня недели начала работы счетчика системной функции get_date_time()) и в остатке от деления получаем номер дня недели СИСТЕМНОГО дня на шкале: 0 - ВС до 6 - СБ.
if getSYSWeekDay = 0 then getSYSWeekDay := 7; end_if // переводим в шкалу: 1 - ПН .. 7 - ВС

end_function

Shiryaevo
22.08.2024, 19:26
UNIX-время скоро умрет :) - Год П не за горами... ближе чем проблема 2000 года и быстрее чем проекты, использующие на UNIX-время, неожиданно перестанут работать в один прекрасный солнечный день :)
Возможно потому OWEN разумно решил начать свою эпоху с 01/01/2000 года - в чем с ними лично я солидарен :)

Shiryaevo
22.08.2024, 19:34
Может и так. Не смотрел, не отлаживал :)
За комментарий и ссылку спасибо!
Когда возникнет связанная задача и я вернусь к этому посту - обязательно включу в список на просмотр.

Dimensy
22.08.2024, 20:08
Здесь Дата, Время и День недели через UnixTime и через DT 78144
Подходит для ПР200 и ПР205

У ПР205 время отсчитывается от 2000 года

FPavel
22.08.2024, 20:10
И добавлю ещё ссылку на формулы вычисления дня недели
https://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week

Dimensy
22.08.2024, 20:14
Пользуйтесь тегом "CODE" в расширенном режиме.
Согласитесь, что, вот так


/// <Description>Определение дня недели из времени в формате UnixTime от 01.01.2000</Description>
/// <GroupName>Дата и время</GroupName>

function DayOfTheWeek: Udint;
var_input
UDT: udint; //от 01.01.2000
end_var

DayOfTheWeek := ((UDT / 86400 + 6) mod 7);
if DayOfTheWeek = 0 then DayOfTheWeek := 7; end_if

end_function

выглядит намного приятнее глазу

Dimensy
22.08.2024, 20:19
б) при каждом ОТКЛ/ВКЛ на входе ENABLE происходит пересчет "Год, Месяц, День". Рекомендуется оценивать целесообразность изменения значения входа ENABLE в коде управляющей программы.

а это зачем?

Shiryaevo
22.08.2024, 21:04
Практическая задача
Классическая КИС-задача, Ротация: переключение работы исполнительных устройств по недельному графику.
В проекте я использовал библиотечный недельный таймер.
Однако у него есть особенность: если в момент когда таймер должен сработать на объекте не было электроэнергии, то библиотечный таймер "пропускает" день / момент когда нужно сработать (или я плохо умею выполнять copy/paste).

На промышленных объектах, там где есть круглосуточная служба эксплуатации, это не есть проблема, а это есть пункт инструкции по эксплуатации.
В микро-объектах - это не то чтобы тоже проблема Века, но: "чегой-то ваш контроллер такой глупый" :)

Второй вариант явился триггером в конкретном случае который меня попросили до-SMART-ить:)

Решение задачи принял такое:
- запоминаем дату (и время) когда должно переключится (в энергонезависимой/RETAIN переменной)
- мониторим часы и если после ре-старта (по питанию) время "прошло", то выставляем флаг: "уже все было".

Для решения задачи нужно
- вычислять день недели, текущий
- вычислять день недели, следующий
- посадить "сторожевого пса" (WhatchDog), который будет сидеть и лаять.

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

Первое с чем столкнулся - написал выше: "как получить текущий день недели"?
Задачу решил, выше, в сообщениях, и ниже, в этом сообщении, увидите .
Вычислить значение следующего дня недели (какое значение возвратит get_date_time()), зная текущий день недели) - это простая формула :)

Второе с чем столкнулся, это с тем что в функциональных блоках на ST нет типа энергонезависимая переменная.
Это не проблема, решение понятное - использовать переменную в МАКРОСЕ (возможно что причина в продуктовом маркетинге, но для себя объясняю длинной LOG-а - чего еще запланировано сделать).

Ниже привожу что у меня получилось (картинки не из проекта, но из отладки)

Верхний уровень (листа)

78146

МАКРОС "Сторожевой пес" (макрос - потому что нужна энергонезависимая переменная)
78147

И функциональный блок на 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:13
Согласитесь, что, вот так
<cat>
выглядит намного приятнее глазу

У меня нет никаких причин с Вами спорить.

Shiryaevo
22.08.2024, 21:16
а это зачем?

... по причинам, описанным в коде.

petera
23.08.2024, 13:08
День недели для любой даты, а не только для сегодня т.е не использую функцию get_date_time() https://owen.ru/forum/showthread.php?t=37840&p=441126&viewfull=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

iAlien
28.08.2024, 08:06
А ПР-ки уже могут в ARRAY?

1exan
28.08.2024, 08:21
А ПР-ки уже могут в ARRAY?

Пока только внутри блоков на ST

bayk
15.09.2024, 08:35
Пока только внутри блоков на ST

И только одномерный