Страница 1 из 2 12 ПоследняяПоследняя
Показано с 1 по 10 из 18

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

  1. #1

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

    Здравствуйте!

    Относительно давно не заходил на форум, задач по 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:37.

  2. #2

    По умолчанию

    И так код функционального блока на 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


  3. #3

    По умолчанию

    Есть такая модель календаря - Юлианские даты.
    https://ru.wikipedia.org/wiki/%D0%AE...B0%D1%82%D0%B0

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

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

  4. #4

    По умолчанию

    Дата, Время и День недели через UnixTime с 1970 года на ST 1 Дата Время UnixUime.jpg


    Тоже самое с 2000 года 1 Работа с 2000 года.jpg
    Дата, Время и День недели через DT на ST. Подходит для ПР200, ПР103 и ПР205, ПР225
    Вложения Вложения
    Последний раз редактировалось kondor3000; 15.05.2025 в 19:11.

  5. #5

    По умолчанию

    По мотивам предыдущего "полного кода" привожу пример легкой функцию на 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:27.

  6. #6

    По умолчанию

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

  7. #7

    По умолчанию

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

  8. #8
    Пользователь
    Регистрация
    09.12.2013
    Адрес
    Ставрополь
    Сообщений
    1,713

    По умолчанию

    Цитата Сообщение от kondor3000 Посмотреть сообщение
    Здесь Дата, Время и День недели через UnixTime и через DT 1 Дата Время UnixUime.jpg
    Подходит для ПР200 и ПР205
    У ПР205 время отсчитывается от 2000 года

  9. #9

    По умолчанию

    И добавлю ещё ссылку на формулы вычисления дня недели
    https://en.wikipedia.org/wiki/Determ...ay_of_the_week

  10. #10
    Пользователь
    Регистрация
    09.12.2013
    Адрес
    Ставрополь
    Сообщений
    1,713

    По умолчанию

    Пользуйтесь тегом "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
    выглядит намного приятнее глазу

Страница 1 из 2 12 ПоследняяПоследняя

Похожие темы

  1. Ответов: 2
    Последнее сообщение: 29.12.2022, 19:02
  2. Март 2020. Свежая подборка статей на "Дзене" от "Датчиков ОВЕН"!
    от Алексей Сидорцев в разделе Трёп (Курилка)
    Ответов: 1
    Последнее сообщение: 18.04.2020, 17:32
  3. Ответов: 0
    Последнее сообщение: 02.02.2020, 21:44
  4. как гарантированно получить "тики" меньше секунды
    от Keldish в разделе Среда программирования OWEN Logic
    Ответов: 14
    Последнее сообщение: 12.04.2018, 09:35
  5. Ответов: 16
    Последнее сообщение: 15.02.2017, 11:39

Метки этой темы

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •