- added missing SNTP code (LIB61850-360)

v1.6_develop_rgoose_sntp
Michael Zillgith 3 years ago
parent 99bdd4e61c
commit 0f544e9e25

@ -0,0 +1,65 @@
/*
* Copyright 2013-2022 Michael Zillgith
*
* This file is part of libIEC61850.
*
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/
#ifndef LIBIEC61850_SRC_COMMON_INC_SNTP_CLIENT_H_
#define LIBIEC61850_SRC_COMMON_INC_SNTP_CLIENT_H_
#include "libiec61850_common_api.h"
#include "hal_socket.h"
typedef struct sSNTPClient* SNTPClient;
typedef void (*SNTPClient_UserCallback)(void* parameter, bool isSynced);
LIB61850_API SNTPClient
SNTPClient_create();
LIB61850_API void
SNTPClient_setLocalAddress(SNTPClient self, const char* localAddress);
LIB61850_API void
SNTPClient_setLocalPort(SNTPClient self, int udpPort);
LIB61850_API HandleSet
SNTPClient_getHandleSet(SNTPClient self);
LIB61850_API void
SNTPClient_addServer(SNTPClient self, const char* serverAddr, int serverPort);
LIB61850_API void
SNTPClient_setPollInterval(SNTPClient self, uint32_t intervalInSeconds);
LIB61850_API void
SNTPClient_setUserCallback(SNTPClient self, SNTPClient_UserCallback callback, void* parameter);
LIB61850_API bool
SNTPClient_isSynchronized(SNTPClient self);
LIB61850_API void
SNTPClient_start(SNTPClient self);
LIB61850_API void
SNTPClient_stop(SNTPClient self);
LIB61850_API void
SNTPClient_destroy(SNTPClient self);
#endif /* LIBIEC61850_SRC_COMMON_INC_SNTP_CLIENT_H_ */

@ -0,0 +1,489 @@
/*
* Copyright 2013-2020 Michael Zillgith
*
* This file is part of libIEC61850.
*
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/
#include "hal_socket.h"
#include "hal_time.h"
#include "hal_thread.h"
#include "libiec61850_platform_includes.h"
#include "sntp_client.h"
#include <time.h>
#include <errno.h>
#define SNTP_DEFAULT_PORT 123
#ifndef SNTP_DEBUG
#define SNTP_DEBUG 1
#endif
typedef struct sSNTPClient* SNTPClient;
/* new time types */
typedef struct {
uint32_t coarse;
uint32_t fine;
} NtpTime;
struct sSNTPClient
{
UdpSocket socket;
HandleSet handleSet;
nsSinceEpoch lastReceivedMessage; /* time of last received synchronization message */
uint64_t nextMessageExpected;
bool outStandingRequest;
uint64_t pollInterval; /* poll interval in ns */
nsSinceEpoch lastRequestTimestamp; /* timestamp used in the last request message */
SNTPClient_UserCallback userCallback;
void* userCallbackParameter;
Thread thread;
bool running;
bool clockSynced;
char* serverAddr;
int serverPort;
};
msSinceEpoch
nsTimeToMsTime(nsSinceEpoch nsTime)
{
return nsTime / 1000000UL;
}
#define UNIX_EPOCH_OFFSET 2208988800 /* offset between NTP seconds and UNIX time */
static nsSinceEpoch
ntpTimeToNsTime(uint32_t coarse, uint32_t fine)
{
if ((coarse == 0) && (fine == 0))
return 0;
uint64_t nsTime = (coarse - UNIX_EPOCH_OFFSET) * 1000000000UL;
uint64_t nsPart = fine * 1000000000UL;
nsPart = nsPart >> 32;
nsTime += nsPart;
return nsTime;
}
static void
nsTimeToNtpTime(nsSinceEpoch nsTime, uint32_t* coarse, uint32_t* fine)
{
uint32_t secondsPart = (nsTime / 1000000000UL) + UNIX_EPOCH_OFFSET;
*coarse = secondsPart;
uint64_t nsPart = nsTime % 1000000000UL;
nsPart = nsPart << 32;
nsPart = nsPart / 1000000000UL;
*fine = (uint32_t) nsPart;
}
static uint32_t
nsTimeToSeconds(nsSinceEpoch nsTime)
{
uint32_t secondsSinceEpoch = nsTime / 1000000000UL;
return secondsSinceEpoch;
}
static uint32_t
getUsPartFromNsTime(nsSinceEpoch nsTime)
{
uint64_t nsPart = nsTime % 1000000000UL;
uint32_t msPart = nsPart / 1000UL;
return msPart;
}
static int
encodeUint32(uint8_t* buffer, int bufPos, uint32_t value)
{
buffer[bufPos++] = (uint8_t) (value / 0x1000000);
buffer[bufPos++] = (uint8_t) ((value / 0x10000) % 0x100);
buffer[bufPos++] = (uint8_t) ((value / 0x100) % 0x100);
buffer[bufPos++] = (uint8_t) (value % 0x100);
return bufPos;
}
static int
decodeUint32(uint8_t* buffer, int bufPos, uint32_t* value)
{
uint32_t val;
val = buffer[bufPos++] * 0x1000000;
val += buffer[bufPos++] * 0x10000;
val += buffer[bufPos++] * 0x100;
val += buffer[bufPos++];
*value = val;
return bufPos;
}
static void
parseResponseMessage(SNTPClient self, uint8_t* buffer, int bufSize)
{
self->lastReceivedMessage = Hal_getTimeInNs();
int bufPos = 0;
uint8_t header = buffer[bufPos++];
int8_t stratum = (int8_t) buffer[bufPos++];
int8_t poll = (int8_t) buffer[bufPos++];
int8_t precision = (int8_t) buffer[bufPos++];
int li = (header & 0xc0)>> 6;
/* check for "clock-not-synchronized" */
if (li == 3) {
/* ignore time message */
if (SNTP_DEBUG)
printf("WARNING: received clock-not-synchronized from server\n");
//TODO call user callback?
return;
}
int version = (header & 0x38) >> 3;
int mode = (header & 0x7);
/* TODO: expect mode 4 - server */
int timeoutInSeconds = 1 << poll;
if (SNTP_DEBUG)
printf("SNTP: response - version: %i mode: %i (4 = server, 3 = client) timeout(in s): %i\n", version, mode, timeoutInSeconds);
/* root delay */
bufPos += 4;
/* root dispersion */
bufPos += 4;
/* reference */
bufPos += 4;
/* reference timestamp */
uint32_t coarse;
uint32_t fine;
bufPos = decodeUint32(buffer, bufPos, &coarse);
bufPos = decodeUint32(buffer, bufPos, &fine);
uint64_t refTime = ntpTimeToNsTime(coarse, fine);
/* originate timestamp */
bufPos = decodeUint32(buffer, bufPos, &coarse);
bufPos = decodeUint32(buffer, bufPos, &fine);
uint64_t origTime = ntpTimeToNsTime(coarse, fine);
/* receive timestamp */
bufPos = decodeUint32(buffer, bufPos, &coarse);
bufPos = decodeUint32(buffer, bufPos, &fine);
uint64_t recvTime = ntpTimeToNsTime(coarse, fine);
/* transmit timestamp */
bufPos = decodeUint32(buffer, bufPos, &coarse);
bufPos = decodeUint32(buffer, bufPos, &fine);
uint64_t trnsTime = ntpTimeToNsTime(coarse, fine);
/* set system time */
if (Hal_setTimeInNs(trnsTime) == false) {
if (SNTP_DEBUG)
printf("SNTP: failed to set system clock!\n");
}
self->clockSynced = true;
self->outStandingRequest = false;
if (self->userCallback) {
self->userCallback(self->userCallbackParameter, true);
}
if (SNTP_DEBUG) {
printf("SNTP: reference time: %u.%.6u\n", nsTimeToSeconds(refTime), getUsPartFromNsTime(refTime));
printf("SNTP: original time: %u.%.9u\n", nsTimeToSeconds(origTime), getUsPartFromNsTime(origTime));
printf("SNTP: receive time: %u.%.6u\n", nsTimeToSeconds(recvTime), getUsPartFromNsTime(recvTime));
printf("SNTP: transmit time: %u.%.6u\n", nsTimeToSeconds(trnsTime), getUsPartFromNsTime(trnsTime));
}
}
static void
sendRequestMessage(SNTPClient self, const char* serverAddr, int serverPort)
{
uint8_t buffer[100];
memset(buffer, 0, sizeof(buffer));
uint8_t li = 0;
int8_t version = 3;
int8_t mode = 3; /* mode: client */
int8_t stratum = 0; /* stratum: not specified */
int8_t poll = 4;
int8_t precision = -6;
int bufPos = 0;
buffer[bufPos++] = (li << 6) | (version << 3) | mode;
buffer[bufPos++] = stratum;
buffer[bufPos++] = poll;
buffer[bufPos++] = precision;
/* root delay */
bufPos = encodeUint32(buffer, bufPos, 1 << 16);
/* root dispersion */
bufPos = encodeUint32(buffer, bufPos, 1 << 16);
/* reference */
bufPos += 4;
/* skip reference timestamp */
bufPos += 8;
/* skip originate timestamp */
bufPos += 8;
/* skip receive timestamp */
bufPos += 8;
uint32_t time_coarse;
uint32_t time_fine;
nsSinceEpoch nsTime = Hal_getTimeInNs();
nsTimeToNtpTime(nsTime, &time_coarse, &time_fine);
/* transmit timestamp */
bufPos = encodeUint32(buffer, bufPos, time_coarse);
bufPos = encodeUint32(buffer, bufPos, time_fine);
UdpSocket_sendTo(self->socket, serverAddr ,serverPort, buffer, bufPos);
if (SNTP_DEBUG)
printf("SNTP: sent request to %s:%i\n", serverAddr, serverPort);
self->lastRequestTimestamp = nsTime;
self->outStandingRequest = true;
//if (self->lastReceivedMessage == 0)
// self->lastReceivedMessage = nsTime;
}
SNTPClient
SNTPClient_create()
{
SNTPClient self = (SNTPClient) GLOBAL_CALLOC(1, sizeof(struct sSNTPClient));
if (self) {
self->socket = UdpSocket_create();
if (self->socket == NULL) {
GLOBAL_FREEMEM(self);
self = NULL;
}
else {
self->handleSet = Handleset_new();
Handleset_addSocket(self->handleSet, (Socket) self->socket);
self->pollInterval = 30000000000UL; /* 30 s */
}
}
return self;
}
HandleSet
SNTPClient_getHandleSet(SNTPClient self)
{
return self->handleSet;
}
void
SNTPClient_addServer(SNTPClient self, const char* serverAddr, int serverPort)
{
if (self) {
self->serverAddr = StringUtils_copyString(serverAddr);
self->serverPort = serverPort;
}
}
bool
SNTPClient_isSynchronized(SNTPClient self)
{
return self->clockSynced;
}
void
SNTPClient_setUserCallback(SNTPClient self, SNTPClient_UserCallback callback, void* parameter)
{
if (self) {
self->userCallback = callback;
self->userCallbackParameter = parameter;
}
}
void
SNTPClient_setPollInterval(SNTPClient self, uint32_t intervalInSeconds)
{
if (self)
self->pollInterval = intervalInSeconds * 1000000000UL;
}
void
SNTPClient_tick(SNTPClient self)
{
nsSinceEpoch now = Hal_getTimeInNs();
if (self->lastReceivedMessage > now)
self->lastReceivedMessage = now;
if (self->lastRequestTimestamp > now)
self->lastRequestTimestamp = now;
if (self->lastRequestTimestamp > 0) {
/* check for timeout */
if ((now - self->lastReceivedMessage) > 300000000000UL) {
if (self->clockSynced) {
self->clockSynced = false;
printf("SNTP: request timeout\n");
//TODO when to call user handler?
if (self->userCallback) {
self->userCallback(self->userCallbackParameter, false);
}
}
}
}
if (self->serverAddr) {
if ((now - self->lastRequestTimestamp) > self->pollInterval) {
sendRequestMessage(self, self->serverAddr, self->serverPort);
}
}
}
void
SNTPClient_handleIncomingMessage(SNTPClient self)
{
char ipAddress[200];
uint8_t buffer[200];
int rcvdBytes = UdpSocket_receiveFrom(self->socket, ipAddress, 200, buffer, sizeof(buffer));
if (rcvdBytes > 0) {
if (SNTP_DEBUG)
printf("SNTP: received response from %s\n", ipAddress);
parseResponseMessage(self, buffer, rcvdBytes);
}
else if (rcvdBytes == -1) {
printf("UDP socket error\n");
}
else {
printf("No data!\n");
}
}
static void*
handleThread(void* parameter)
{
SNTPClient self = (SNTPClient) parameter;
self->running = true;
while (self->running) {
SNTPClient_tick(self);
if (Handleset_waitReady(self->handleSet, 1000) > 0) {
SNTPClient_handleIncomingMessage(self);
}
}
return NULL;
}
void
SNTPClient_start(SNTPClient self)
{
if (self) {
int sntpPoirt = SNTP_DEFAULT_PORT;
if (UdpSocket_bind(self->socket, "0.0.0.0", SNTP_DEFAULT_PORT)) {
printf("Start NTP thread\n");
self->thread = Thread_create(handleThread, self, false);
if (self->thread)
Thread_start(self->thread);
}
else {
if (SNTP_DEBUG)
printf("SNTP: Failed to bind to port %i\n", sntpPoirt);
}
}
}
void
SNTPClient_stop(SNTPClient self)
{
if (self->thread) {
self->running = false;
Thread_destroy(self->thread);
self->thread = NULL;
}
}
void
SNTPClient_destroy(SNTPClient self)
{
if (self) {
SNTPClient_stop(self);
if (self->serverAddr)
GLOBAL_FREEMEM(self->serverAddr);
if (self->socket) {
Socket_destroy((Socket) self->socket);
}
GLOBAL_FREEMEM(self);
}
}
Loading…
Cancel
Save