TwinCAT 3: Синхронизация "F_GetSystemTime" с местным временем Windows? - PullRequest
0 голосов
/ 21 сентября 2019

Я работаю над приложением, в котором я использую метки времени для высокоточных данных, используя F_GetSystemTime.Эта функция возвращает число интервалов 100 нс с 1 января 1601 года.

У меня также есть локальное время Windows, синхронизированное с глобальными сетевыми часами через NTP-сервер.Это гарантирует, что системное время календаря не смещается в течение длительного времени.

Однако я сталкиваюсь с проблемой, когда "F_GetSystemTime" не синхронизируется со временем Windows.В течение нескольких недель в моем "F_GetSystemTime" наблюдается заметный сдвиг.

Есть ли способ сохранить синхронизацию "F_GetSystemTime" с моим временем Windows?

Ответы [ 2 ]

0 голосов
/ 26 сентября 2019

Используйте функциональный блок FB_LocalSystemTime, как в ответе Филиппо.Это дает вам текущее время в миллисекундах и синхронизирует с часами Windows каждые dwCycle секунды.Таким образом, блок всегда дает вам текущее время при вызове и поддерживает синхронизацию.Я не думаю, что вам нужны какие-либо вычисления в буфере и т. Д. Просто запускайте этот код каждый цикл ПЛК.

Если время вашего цикла составляет 10 мс, выход будет изменяться на 10 мс при каждом вызове.Конечно, это не совсем точно на 99%, но должно сработать.Когда время синхронизируется со временем Windows, разница между предыдущим и текущим значением, конечно, немного изменится.Если это не так, увеличьте интервал синхронизации до меньшего или большего значения.

Выходное значение равно TIMESTRUCT, оно содержит дату и время, а также миллисекунды.Просто используйте SYSTEMTIME_TO_DT для конвертации.Помните, что в этом преобразовании округляются миллисекунды, поэтому это может изменить секунды.Вы можете установить миллисекунды на 0 до SYSTEMTIME_TO_DT, чтобы сохранить секунды такими, какие они есть.

//...

VAR
    LocalSystemTime     : FB_LocalSystemTime;
    FirstTimeUpdateOK   : BOOL; //True after the time was synced OK at least once
    EmptyDateAndTime    : TIMESTRUCT := (wYear := 1970, wMonth := 1, wDay := 1);
END_VAR
VAR_OUTPUT
    SyncedTime          : TIMESTRUCT;
END_VAR




//First, set current time to 1970 if not updated yet.
IF NOT FirstTimeUpdateOK THEN
    SyncedTime := EmptyDateAndTime;
END_IF


//Call the time sync block
LocalSystemTime(
    bEnable := TRUE,    //Keep as TRUE always
    dwCycle := 5        //Sync with windows clock every 5 seconds (change to smaller/higher value to prevent small changes every 5 seconds)
);


IF LocalSystemTime.bValid THEN
    //Everything OK, we have valid time
    FirstTimeUpdateOK:= TRUE;
    SyncedTime := LocalSystemTime.systemTime;


ELSIF FirstTimeUpdateOK THEN
    //We have once had the valid time, but not now. Show error?
    //...
END_IF
0 голосов
/ 21 сентября 2019

Вы можете построить внутренний счетчик с точностью вашего цикла задач.Допустим, у вас есть цикл 10 мс, вы можете записывать свои данные с отметкой времени 10 мс.Если вы хотите быть более точным, вы можете либо сократить время выполнения основной задачи plc, либо создать отдельную задачу с более быстрым циклом.

Другая возможность состоит в использовании быстрых операций ввода-вывода с избыточной дискретизацией, таких как el1262.

Вот пример необработанного внутреннего счетчика:

Часть декларации:

PROGRAM MAIN
VAR
    bInit           : BOOL;
    nTime           : UINT;
    tBufferTime     : TIME;
    dtBufferDT      : DT;
    nCalcBuffer     : UDINT;
    sMs             : STRING;
    sLogTime        : STRING;
    sLogTimeWithMs  : STRING;
    stSystemTime    : TIMESTRUCT;
    fbLocalTime     : FB_LocalSystemTime;
END_VAR

Часть реализации:

fbLocalTime(bEnable := TRUE);
IF NOT bInit
THEN
    dtBufferDT := SYSTEMTIME_TO_DT(fbLocalTime.systemTime);
    IF fbLocalTime.bValid 
    THEN
        bInit := TRUE;
    END_IF
ELSE
    nTime := nTime + 1;
    tBufferTime := UINT_TO_TIME(nTime*10);
    IF tBufferTime = T#1S
    THEN
        //Add a second to your system time
        ntime := 0;
        nCalcBuffer := DT_TO_UDINT(dtBufferDT)+1;
        dtBufferDT := UDINT_TO_DT(nCalcBuffer);
        sLogTime := DT_TO_STRING(dtBufferDT);
        sLogTimeWithMs := sLogTime;
    ELSE
        //Add ms string time-stamp
        sMs := TIME_TO_STRING(tBufferTime);
        sLogTimeWithMs := CONCAT(sLogTime,sMs);
    END_IF
END_IF

sLogTimeWithMs покажет что-то вроде этого:

'DT # 2019-09-21-14: 30: 28T # 530ms'

, что дает вам правильное время с точностью 10 мс без необходимости дальнейшегосинхронизация.

Затем вы можете полировать строку, чтобы очистить ее от нежелательных символов, таких как DT #, T # или ms.

...