Вход

Просмотр полной версии : ФБ даты и время на ST



AllXXX
30.10.2025, 11:44
Здравствуйте
Поступило задание написать программу управления тепловым узлом с вкл./выкл. по времени и дню недели. Знаю, что в менеджере компонентов есть CLOCK_WEEK (Недельный таймер с двумя режимами работы на FBD) но я изучаю и практикуюсь в программирование на ST.
Почитав форум нашел куски разрозненного кода и решил написать два ФБ переводящие время в сек. от 2000 г. (используется в Owen Logic) в формат год, месяц, день недели, день месяца, час, минуты, секунды и наоборот год, месяц, день, час, минуты, секунды в сек. от 2000 г.
Проверил все работает на 21 век.
Так как форум не дает загружать файлы с расширением fbst упаковал в TIME.ZIP кому нужно пользуйтесь.

capzap
30.10.2025, 11:55
а почему не использовать готовые ПОУ из оскат библиотеки, зачем свое создавать?
АИ-ассистент говорит что есть ошибки
IF udiMonth = 2 AND xLeapYear THEN
IF xLeapYear THEN
arrDays[0]:=29;
ELSE
arrDays[0]:=28;
END_IF
Это избыточно и некорректно: вы уже проверили xLeapYear, но затем снова проверяете его. Более того, вы перезаписываете arrDays[0] вместо изменения arrDays[2].


udiDayOfWeek := ((udiTime + 6) MOD 7);
где udiTime — количество дней с 1.01.2000, но 1 января 2000 года — суббота, а не понедельник! Согласно стандарту IEC 61131-3 и большинству ПЛК, DT начинается с понедельника = 1, воскресенье = 7.

kondor3000
30.10.2025, 12:14
Всё уже выложено давно и с 1970 и с 2000 года, туда и обратно https://owen.ru/forum/showthread.php?t=40116&p=444236&viewfull=1#post444236
Кроме того ПР200 это далеко не ПР205 (103, 225), где все по другому.

И ещё, тут много всего Астротаймеры, Таймеры и примеры https://owen.ru/forum/showthread.php?t=37203&p=426133&viewfull=1#post426133

kondor3000
30.10.2025, 13:55
Здравствуйте
Поступило задание написать программу управления тепловым узлом с вкл./выкл. по времени и дню недели. Знаю, что в менеджере компонентов есть CLOCK_WEEK (Недельный таймер с двумя режимами работы на FBD) но я изучаю и практикуюсь в программирование на ST.
Почитав форум нашел куски разрозненного кода и решил написать два ФБ переводящие время в сек. от 2000 г. (используется в Owen Logic) в формат год, месяц, день недели, день месяца, час, минуты, секунды и наоборот год, месяц, день, час, минуты, секунды в сек. от 2000 г.
Проверил все работает на 21 век.
Так как форум не дает загружать файлы с расширением fbst упаковал в TIME.ZIP кому нужно пользуйтесь.

У вас ошибки с месяцами и годами, при установке 0:0:1 01/01/26 года показывает 13 месяц 25 года 86499
Тоже самое при 2000 годе (0) показывает 13 месяц 4294967295 года 86500
В авто режиме, при 0 на входе, такая же беда.

Прямо как на Миллениум 0:0:0 01/01/2000 года , компы сойдут с ума)

AllXXX
30.10.2025, 14:09
Я не гуру в ST всего лишь киповец самоучка.

а почему не использовать готовые ПОУ из оскат библиотеки, зачем свое создавать? Писал выше без практики мало чему научишься.


АИ-ассистент говорит что есть ошибкиЭто что еще за зверь?


Это избыточно и некорректно: вы уже проверили xLeapYear, но затем снова проверяете его.Здесь согласен в первом условии AND xLeapYear нужно убрать.


Более того, вы перезаписываете arrDays[0] вместо изменения arrDays[2]А здесь нет. в arrDays[0] записывается кол-во дней в месяце текущего цикла FOR.


где udiTime — количество дней с 1.01.2000, но 1 января 2000 года — суббота, а не понедельник! Согласно стандарту IEC 61131-3 и большинству ПЛК, DT начинается с понедельника = 1, воскресенье = 7.А вот здесь не понимаю, о чем вы?

udiDayOfWeek: UDINT; // — день недели, 1..7 (1 - понедельник, ..., 7 - воскресенье);


Всё уже выложено давно и с 1970 и с 2000 года, туда и обратно https://owen.ru/forum/showthread.php?t=40116&p=444236&viewfull=1#post444236
Кроме того ПР200 это далеко не ПР205 (103, 225), где все по другому.Да именно про эти примеры я писал выше.


И ещё много всего Астротаймеры, Таймеры и примеры https://owen.ru/forum/showthread.php?t=37203&p=426133&viewfull=1#post426133Вот за это спасибо, яндекс ссылок на эту ветвь форума не давал ну или я задавал не правильные вопросы.

AllXXX
30.10.2025, 14:16
У вас ошибки с месяцами и годами, при установке 0:0:1 01/01/26 года показывает 13 месяц 25 года 86499
Тоже самое при 2000 годе (0) показывает 13 месяц 4294967295 года 86500
В авто режиме, при 0 на входе, такая же беда.

Прямо как на Миллениум 0:0:0 01/01/2000 года , компы сойдут с ума)
Как говориться первый блин комом.
Хорошо на следующей смене поищу баг

Dimensy
30.10.2025, 15:12
Как говориться первый блин комом.
Хорошо на следующей смене поищу баг

Ну и почитайте, чтобы велосипеды не изобретать https://disk.yandex.ru/i/1hbOEDI9wboWGQ

kondor3000
05.11.2025, 11:16
Идея хорошая, написать блок, без использования дополнительных функций и ФБ одним блоком. 86589
Для переноса на любую платформу, достаточно текста одного ФБ.
Кроме того, ФБ работает сам по себе при 0 на входе (время внутри), а при подаче UnixTime как конвертер.
Уже переписал свои ФБ и нашёл 3 ошибки у ТС, теперь всё работает.

Dimensy
07.11.2025, 10:13
Тоже решил побаловаться


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

function_block UDT_to_DT // от 01.01.2000

var_input
UDT: udint := 0; //дата и время в формате Unix от 01.01.2000 в секундах
end_var

var_output
day: udint;
month: udint;
year: udint;
hour: udint;
min: udint;
sec: udint;
week: udint;
end_var

var
day_in_month: array [1..12] of udint := [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
uiDay: udint;
days_to_year, days_to_month: udint;
is_leap_year : bool;
end_var

if UDT = 0 then UDT := dt_to_udint(get_date_time()); end_if
// Определяем год
year := UDT / 31556926 + 2000;
//Количество дней с 01.01.2000 до начала текущего года
days_to_year := (year - 2000) * 365 + (year - 1997)/4 - (year - 1901)/100 + (year - 1601)/400;
//Количество дней с начала года
uiDay := UDT / 86400 - days_to_year + 1;
//Месяц
month := uiDay * 86400 / 2629743 + 1;
//Проверяем год на високосность
is_leap_year := (year mod 400 = 0) or ((year mod 4 = 0) and (year mod 100 <> 0));
//Количество дней до начала месяца
days_to_month := day_in_month[month] + bool_to_udint(is_leap_year and month > 2);
//День
day := uiDay - days_to_month;

//Время
hour := (UDT / 3600) mod 24;
min := (UDT / 60) mod 60;
sec := UDT mod 60;

//День недели
week := ((UDT / 86400 + 6) mod 7);
if week = 0 then week := 7; end_if

end_function_block

kondor3000
07.11.2025, 19:36
Тоже решил побаловаться


Функция SYS.is_leap_year(); уже встроена в ОЛ. Можно ещё сократить )))
И время после 07:08:00 утра, 31 декабря 2025 года показывает 0 день января 2026 года.

Dimensy
07.11.2025, 19:53
Функция SYS.is_leap_year(); уже встроена в ОЛ. Можно ещё сократить )))
И время с 8 утра, 31 декабря 2025 года показывает 0 день января 2026 года.

Все-таки, Алиса была права, на пограничных датах могут быть проблемы. Я гонял, гонял, у меня все нормально было, вы же нашли. Так что, если не хотим внешними функциями пользоваться, то тогда перебором

kondor3000
07.11.2025, 19:59
Все-таки, Алиса была права, на пограничных датах могут быть проблемы. Я гонял, гонял, у меня все нормально было, вы же нашли. Так что, если не хотим внешними функциями пользоваться, то тогда перебором

Просто сильно сократили, у меня получилось вычисление дня до начала года и дня до начала месяца, по 2 одинаковых строки, с разными условиями.
Наступил на те же грабли. Пока у себя проверял, набрался опыта )

Dimensy
07.11.2025, 21:09
Просто сильно сократили, у меня получилось вычисление дня до начала года и дня до начала месяца, по 2 одинаковых строки, с разными условиями.
Наступил на те же грабли. Пока у себя проверял, набрался опыта )
Ну да, вот универсальный код (за исключением первой строчки)


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

function_block UDT_to_DateTime // от 01.01.2000

var_input
UDT: udint := 0; //дата и время в формате Unix от 01.01.2000 в секундах (если = 0, то берем системное время)
end_var

var_output
day: udint;
month: udint;
year: udint;
hour: udint;
min: udint;
sec: udint;
week: udint;
end_var

var
day_month: array [1..12] of udint := [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
uiDate, uiDay: udint;
days_to_year, days_to_month: udint;
is_leap_year: bool;
end_var

if UDT = 0 then UDT := dt_to_udint(get_date_time()); end_if;

//Общее количество дней
uiDate := UDT / 86400;
//Приблизительно определяем год
Year := uiDate / 365 + 2000;
//Количество дней от 01.01.2000 до начала года
days_to_year := (Year - 2000)*365 + (Year - 1997)/4 - (Year - 1901)/100 + (Year-1601)/400;
//Уточняем год
if uiDate < days_to_year then
Year := Year - 1;
days_to_year := (Year - 2000)*365 + (Year - 1997)/4 - (Year - 1901)/100 + (Year-1601)/400;
end_if
//Количество дней от начала года
uiDay := uiDate - days_to_year + 1;
//Приблизительно определяем месяц
month := uiDay / 29 + 1;
if month > 12 then month := 12; end_if
//Проверяем год на високосность
is_leap_year := (Year mod 400 = 0) or ((Year mod 4 = 0) and (Year mod 100 <> 0));
//Количество дней от нового года до начала месяца
days_to_month := day_month[month] + bool_to_udint(is_leap_year and Month > 2);
//Уточняем месяц
if uiDay <= days_to_month then
Month := Month - 1;
days_to_month := day_month[month] + bool_to_udint(is_leap_year and Month > 2);
end_if
//Находим день
Day := uiDay - days_to_month;
//Время
hour := (UDT / 3600) mod 24;
min := (UDT / 60) mod 60;
sec := UDT mod 60;
//День недели
week := ((UDT / 86400 + 6) mod 7);
if week = 0 then week := 7; end_if

end_function_block

kondor3000
07.11.2025, 21:49
Ну да, вот универсальный код (за исключением первой строчки)

Почти один в один, как у меня)



/// <Description>Извлечение времени и даты из UnixTime c 2000 г. </Description>
/// <GroupName>ФБ Даты и времени</GroupName>
function_block DT_UTC // Извлечение времени и даты из UnixTime
var_input //объявление входных переменных
DT_: udint; // время DT
end_var
var_output //объявление выходных переменных
Has: udint; // Часы
Myn: udint; // Минуты
Sek: udint; // Секунды
Day:udint; // День
Month:udint; // MONTH Месяц
Year:udint; // YEAR Год
D_Week:udint; // День недели
DT1: udint; // время DT
end_var
var
DayInMonth: array [1..12] of udint := [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
uiDate, uiDay: udint;
days_to_year, days_to_month: udint;
Timer: udint; // Время
end_var

if DT_=0 then
DT_:=dt_to_udint(get_date_time()); // Дата и время DT
end_if DT1:=DT_;
Timer:=DT_ MOD 86400; // Время
D_Week := ((DT_ / 86400 + 6) mod 7);
if D_Week=0 then D_Week:=7; end_if // День недели
Has:=Timer/3600; Myn:=Timer mod 3600/60; Sek:=Timer mod 60; // Время часы, минуты, сек.
uiDate := DT_/ 86400;
Year := uiDate / 365 + 2000; // Год +2000
days_to_year := (Year - 2000)*365 + (Year - 1997)/4 - (Year - 1901)/100 + (Year-1601)/400;
if uiDate < days_to_year then
Year := Year - 1;
days_to_year :=(Year - 2000)*365 + (Year - 1997)/4 - (Year - 1901)/100 + (Year-1601)/400;
end_if
uiDay := uiDate - days_to_year + 1;
Month := uiDay / 29 + 1;
if Month > 12 then Month := 12; end_if
days_to_month := DayInMonth[Month] + bool_to_udint( SYS.is_leap_year(Year) and Month > 2);
if uiDay <= days_to_month then
Month := Month - 1;
days_to_month :=DayInMonth[Month] + bool_to_udint( SYS.is_leap_year(Year) and Month > 2);
end_if
Day := uiDay - days_to_month;

end_function_block

Dimensy
08.11.2025, 10:34
Кстати, не понял, а почему мы так делаем


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

проще же можно

week := ((UDT / 86400 + 5) mod 7) + 1;

И, еще,


year := uiDate / 365 + 2000;
days_to_year := (year - 2000)*365 + (year - 1997)/4 - (year - 1901)/100 + (year-1601)/400;

нагляднее будет


year := uiDate / 365; //Приблизительно определяем год
days_to_year := year * 365 + (year + 3)/4 - (year + 99)/100 + (year + 399)/400;
и только в конце уже добавить 2000

kondor3000
08.11.2025, 10:56
Кстати, не понял, а почему мы так делаем

С Днём недели можно, а с годами можно так


year := uiDate / 365; //Приблизительно определяем год
days_to_year := year * 365 + (year + 3)/4 - (year + 99)/100 + (year + 399)/400;
if uiDate < days_to_year then
Year := Year - 1 ;
days_to_year := year * 365 + (year + 3)/4 - (year + 99)/100 + (year + 399)/400;
end_if
year := year+2000;
Последний вариант универсального ФБ времени, версия 5, выложил тут https://owen.ru/forum/showthread.php?t=40116&p=444236&viewfull=1#post444236