Чтобы несколько разбавить эту скучную тему, выложу код NTP-клиента. Товарищ capzap выкладывал давным-давно свою версию. Моя, пожалуй, чуть получше. Скачать проект можно здесь.
PROGRAM PLC_PRG
VAR
GetTime: CurTimeEx;
TimeAndDate: SystemTimeDate;
Sys_Time: SysTime64;
ntp_socket : DINT:= SOCKET_INVALID;
ntp_sockaddr : SOCKADDRESS;
state: DWORD;
R_BUF: ARRAY[0..47] OF BYTE;
i: WORD;
received: DINT;
NTP_TIME: SDT;
tx, last: DWORD;
OK, ERRORS: DWORD; (* Счётчики удачных и неудачных запросов - для справки *)
END_VAR
VAR CONSTANT
S_BUF: ARRAY[0..47] OF BYTE := 27,47(0);
TIMEOUT: TIME := t#500ms; (* Тайм-аут ожидания ответа ntp-сервера *)
HOME: TIME := t#5h; (* UTC+5 мой часовой пояс *)
PERIOD: TIME := t#30s; (* Время между ntp-запросами. Не менее 30 секунд! *)
ntp_port: UINT := 123;
ntp_addr: UDINT := 16#34B2A129; (* 52.178.161.41 time.windows.com *)
END_VAR
================================================== ===============
(* Для компиляции требуются библиотеки SYSLIBTIME, SysLibSockets и oscat_basic_333 *)
(*Читаем текущее время - здесь только для контроля правильности работы программы *)
TimeAndDate.Year :=0; TimeAndDate.Month :=0; TimeAndDate.Day :=0; TimeAndDate.DayOfWeek :=0;
TimeAndDate.HOUR :=0; TimeAndDate.MINUTE :=0; TimeAndDate.SECOND :=0; TimeAndDate.Milliseconds :=0;
TimeAndDate.dwHighMsec :=0; TimeAndDate.dwLowMSecs :=0;
Sys_time.ulHigh :=0; Sys_time.ulLow :=0;
GetTime (SystemTime:=Sys_Time , TimeDate:= TimeAndDate);
(* tx := T_PLC_MS(); *) (* Этот вариант годится, если часть программы выше отсутствует, т.е. когда нам неизвестно значение переменных структуры TimeAndDate *)
tx := TimeAndDate.dwLowMSecs;
CASE state OF
0: (* Cоздание сокета *)
ntp_socket := SysSockCreate(SOCKET_AF_INET, SOCKET_DGRAM, SOCKET_IPPROTO_UDP);
ntp_sockaddr.sin_family := SOCKET_AF_INET;
ntp_sockaddr.sin_port := ntp_port;
ntp_sockaddr.sin_addr := ntp_addr;
FOR i:=0 TO 47 DO R_BUF[i] := 0; END_FOR (* Очистка приёмного буфера *)
state := 1;
1: (* Посылаем запрос на ntp-сервер *)
SysSockSendTo(ntp_socket, ADR(S_BUF), SIZEOF(S_BUF), SOCKET_MSG_OOB, ADR(ntp_sockaddr), SIZEOF(ntp_sockaddr));
last := tx;
state := 2;
2: (* Ждём ответ ntp-сервера в течение времени TIMEOUT *)
received := SysSockRecvFrom(ntp_socket, ADR(R_BUF), SIZEOF(R_BUF), SOCKET_MSG_OOB, ADR(ntp_sockaddr), SIZEOF(ntp_sockaddr));
IF received=48 AND R_BUF[40]<>0 THEN
state := 3;
ELSE
IF tx-last>TIME_TO_DWORD(TIMEOUT) THEN
ERRORS := ERRORS+1; (* Ошибки появляются только при обрыве связи *)
state := 4;
END_IF
END_IF
3: (* Запись полученного времени в системный таймер*)
NTP_TIME:= DT_TO_SDT( DWORD_TO_DT(DWORD_OF_BYTE(R_BUF[40],R_BUF[41],R_BUF[42],R_BUF[43]) - DWORD#2208988800 + TIME_TO_DWORD(HOME)/1000));
TimeAndDate.Year := NTP_TIME.YEAR; TimeAndDate.Month := NTP_TIME.MONTH; TimeAndDate.Day :=NTP_TIME.DAY;
TimeAndDate.HOUR := NTP_TIME.HOUR; TimeAndDate.MINUTE := NTP_TIME.MINUTE; TimeAndDate.SECOND := NTP_TIME.SECOND;
GetTime (SystemTime:=Sys_Time , TimeDate:= TimeAndDate);
OK := OK + 1;
state := 4;
4: (* Закрываем сокет *)
SysSockClose(ntp_socket);
state := 5;
5: (* Ждём окончания времени между запросами *)
IF tx-last>TIME_TO_DWORD(PERIOD) THEN
state := 0;
END_IF
END_CASE