pull/215/head
Michael Zillgith 6 years ago
commit 8726bb19d1

@ -226,7 +226,7 @@ main(int argc, char** argv)
SVPublisher_ASDU_setINT32(asdu, vol4, voltageN); SVPublisher_ASDU_setINT32(asdu, vol4, voltageN);
SVPublisher_ASDU_setQuality(asdu, vol4q, q); SVPublisher_ASDU_setQuality(asdu, vol4q, q);
SVPublisher_ASDU_setRefrTm(asdu, Hal_getTimeInMs()); SVPublisher_ASDU_setRefrTmNs(asdu, Hal_getTimeInNs());
SVPublisher_ASDU_setSmpCnt(asdu, (uint16_t) sampleCount); SVPublisher_ASDU_setSmpCnt(asdu, (uint16_t) sampleCount);

@ -57,6 +57,8 @@ extern "C" {
/** Opaque reference for a server socket instance */ /** Opaque reference for a server socket instance */
typedef struct sServerSocket* ServerSocket; typedef struct sServerSocket* ServerSocket;
typedef struct sUdpSocket* UdpSocket;
/** Opaque reference for a client or connection socket instance */ /** Opaque reference for a client or connection socket instance */
typedef struct sSocket* Socket; typedef struct sSocket* Socket;
@ -140,6 +142,18 @@ Handleset_destroy(HandleSet self);
PAL_API ServerSocket PAL_API ServerSocket
TcpServerSocket_create(const char* address, int port); TcpServerSocket_create(const char* address, int port);
PAL_API UdpSocket
UdpSocket_create();
PAL_API bool
UdpSocket_bind(UdpSocket self, const char* address, int port);
PAL_API bool
UdpSocket_sendTo(UdpSocket self, const char* address, int port, uint8_t* msg, int msgSize);
PAL_API int
UdpSocket_receiveFrom(UdpSocket self, char** address, int maxAddrSize, uint8_t* msg, int msgSize);
PAL_API void PAL_API void
ServerSocket_listen(ServerSocket self); ServerSocket_listen(ServerSocket self);

@ -1,7 +1,7 @@
/* /*
* time.c * time.c
* *
* Copyright 2013, 2014 Michael Zillgith * Copyright 2013-2020 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -46,6 +46,9 @@ extern "C" {
* @{ * @{
*/ */
typedef uint64_t nsSinceEpoch;
typedef uint64_t msSinceEpoch;
/** /**
* Get the system time in milliseconds. * Get the system time in milliseconds.
* *
@ -54,9 +57,23 @@ extern "C" {
* *
* \return the system time with millisecond resolution. * \return the system time with millisecond resolution.
*/ */
PAL_API uint64_t PAL_API msSinceEpoch
Hal_getTimeInMs(void); Hal_getTimeInMs(void);
/**
* Get the system time in nanoseconds.
*
* The time value returned as 64-bit unsigned integer should represent the nanoseconds
* since the UNIX epoch (1970/01/01 00:00 UTC).
*
* \return the system time with nanosecond resolution.
*/
PAL_API nsSinceEpoch
Hal_getTimeInNs();
PAL_API bool
Hal_setTimeInNs(nsSinceEpoch nsTime);
/*! @} */ /*! @} */
/*! @} */ /*! @} */

@ -58,6 +58,10 @@ struct sServerSocket {
int backLog; int backLog;
}; };
struct sUdpSocket {
int fd;
};
struct sHandleSet { struct sHandleSet {
LinkedList sockets; LinkedList sockets;
bool pollfdIsUpdated; bool pollfdIsUpdated;
@ -184,26 +188,26 @@ Socket_activateTcpKeepAlive(Socket self, int idleTime, int interval, int count)
if (setsockopt(self->fd, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen)) { if (setsockopt(self->fd, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen)) {
if (DEBUG_SOCKET) if (DEBUG_SOCKET)
printf("Failed to enable TCP keepalive\n"); printf("SOCKET: Failed to enable TCP keepalive\n");
} }
#if defined TCP_KEEPCNT #if defined TCP_KEEPCNT
optval = idleTime; optval = idleTime;
if (setsockopt(self->fd, IPPROTO_TCP, TCP_KEEPIDLE, &optval, optlen)) { if (setsockopt(self->fd, IPPROTO_TCP, TCP_KEEPIDLE, &optval, optlen)) {
if (DEBUG_SOCKET) if (DEBUG_SOCKET)
printf("Failed to set TCP keepalive TCP_KEEPIDLE parameter\n"); printf("SOCKET: Failed to set TCP keepalive TCP_KEEPIDLE parameter\n");
} }
optval = interval; optval = interval;
if (setsockopt(self->fd, IPPROTO_TCP, TCP_KEEPINTVL, &optval, optlen)) { if (setsockopt(self->fd, IPPROTO_TCP, TCP_KEEPINTVL, &optval, optlen)) {
if (DEBUG_SOCKET) if (DEBUG_SOCKET)
printf("Failed to set TCP keepalive TCP_KEEPINTVL parameter\n"); printf("SOCKET: Failed to set TCP keepalive TCP_KEEPINTVL parameter\n");
} }
optval = count; optval = count;
if (setsockopt(self->fd, IPPROTO_TCP, TCP_KEEPCNT, &optval, optlen)) { if (setsockopt(self->fd, IPPROTO_TCP, TCP_KEEPCNT, &optval, optlen)) {
if (DEBUG_SOCKET) if (DEBUG_SOCKET)
printf("Failed to set TCP keepalive TCP_KEEPCNT parameter\n"); printf("SOCKET: Failed to set TCP keepalive TCP_KEEPCNT parameter\n");
} }
#endif /* TCP_KEEPCNT */ #endif /* TCP_KEEPCNT */
@ -343,7 +347,7 @@ closeAndShutdownSocket(int socketFd)
if (socketFd != -1) { if (socketFd != -1) {
if (DEBUG_SOCKET) if (DEBUG_SOCKET)
printf("socket_linux.c: call shutdown for %i!\n", socketFd); printf("SOCKET: call shutdown for %i!\n", socketFd);
/* shutdown is required to unblock read or accept in another thread! */ /* shutdown is required to unblock read or accept in another thread! */
shutdown(socketFd, SHUT_RDWR); shutdown(socketFd, SHUT_RDWR);
@ -505,7 +509,6 @@ Socket_connect(Socket self, const char* address, int port)
return false; return false;
} }
static char* static char*
convertAddressToStr(struct sockaddr_storage* addr) convertAddressToStr(struct sockaddr_storage* addr)
{ {
@ -667,3 +670,94 @@ Socket_destroy(Socket self)
GLOBAL_FREEMEM(self); GLOBAL_FREEMEM(self);
} }
UdpSocket
UdpSocket_create()
{
UdpSocket self = NULL;
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock != -1) {
self = (UdpSocket) GLOBAL_MALLOC(sizeof(struct sSocket));
self->fd = sock;
}
else {
if (DEBUG_SOCKET)
printf("SOCKET: failed to create UDP socket (errno=%i)\n", errno);
}
return self;
}
bool
UdpSocket_bind(UdpSocket self, const char* address, int port)
{
struct sockaddr_in localAddress;
if (!prepareServerAddress(address, port, &localAddress)) {
close(self->fd);
self->fd = 0;
return false;
}
int result = bind(self->fd, (struct sockaddr*)&localAddress, sizeof(localAddress));
if (result == -1) {
if (DEBUG_SOCKET)
printf("SOCKET: failed to bind UDP socket (errno=%i)\n", errno);
close(self->fd);
self->fd = 0;
return false;
}
return true;
}
bool
UdpSocket_sendTo(UdpSocket self, const char* address, int port, uint8_t* msg, int msgSize)
{
struct sockaddr_in remoteAddress;
if (!prepareServerAddress(address, port, &remoteAddress)) {
if (DEBUG_SOCKET)
printf("SOCKET: failed to lookup remote address %s\n", address);
return false;
}
int result = sendto(self->fd, msg, msgSize, 0, (struct sockaddr*)&remoteAddress, sizeof(remoteAddress));
if (result == msgSize) {
return true;
}
else if (result == -1) {
if (DEBUG_SOCKET)
printf("SOCKET: failed to send UDP message (errno=%i)\n", errno);
}
else {
if (DEBUG_SOCKET)
printf("SOCKET: failed to send UDP message (insufficient data sent)\n");
}
return false;
}
int
UdpSocket_receiveFrom(UdpSocket self, char** address, int maxAddrSize, uint8_t* msg, int msgSize)
{
struct sockaddr_in remoteAddress;
int result = recvfrom(self->fd, msg, msgSize, MSG_DONTWAIT, NULL, NULL);
if (result == -1) {
if (DEBUG_SOCKET)
printf("SOCKET: failed to receive UDP message (errno=%i)\n", errno);
}
return result;
}

@ -58,6 +58,10 @@ struct sHandleSet {
SOCKET maxHandle; SOCKET maxHandle;
}; };
struct sUdpSocket {
SOCKET fd;
};
HandleSet HandleSet
Handleset_new(void) Handleset_new(void)
{ {
@ -612,3 +616,101 @@ Socket_destroy(Socket self)
GLOBAL_FREEMEM(self); GLOBAL_FREEMEM(self);
} }
UdpSocket
UdpSocket_create()
{
UdpSocket self = NULL;
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock != -1) {
self = (UdpSocket) GLOBAL_MALLOC(sizeof(struct sSocket));
self->fd = sock;
setSocketNonBlocking((Socket)self);
}
else {
if (DEBUG_SOCKET)
printf("SOCKET: failed to create UDP socket (errno=%i)\n", errno);
}
return self;
}
bool
UdpSocket_bind(UdpSocket self, const char* address, int port)
{
struct sockaddr_in localAddress;
if (!prepareServerAddress(address, port, &localAddress)) {
closesocket(self->fd);
self->fd = 0;
return false;
}
int result = bind(self->fd, (struct sockaddr*)&localAddress, sizeof(localAddress));
if (result == -1) {
if (DEBUG_SOCKET)
printf("SOCKET: failed to bind UDP socket (errno=%i)\n", errno);
closesocket(self->fd);
self->fd = 0;
return false;
}
return true;
}
bool
UdpSocket_sendTo(UdpSocket self, const char* address, int port, uint8_t* msg, int msgSize)
{
struct sockaddr_in remoteAddress;
if (!prepareServerAddress(address, port, &remoteAddress)) {
if (DEBUG_SOCKET)
printf("SOCKET: failed to lookup remote address %s\n", address);
return false;
}
int result = sendto(self->fd, (const char*) msg, msgSize, 0, (struct sockaddr*)&remoteAddress, sizeof(remoteAddress));
if (result == msgSize) {
return true;
}
else if (result == -1) {
if (DEBUG_SOCKET)
printf("SOCKET: failed to send UDP message (errno=%i)\n", errno);
}
else {
if (DEBUG_SOCKET)
printf("SOCKET: failed to send UDP message (insufficient data sent)\n");
}
return false;
}
int
UdpSocket_receiveFrom(UdpSocket self, char** address, int maxAddrSize, uint8_t* msg, int msgSize)
{
struct sockaddr_in remoteAddress;
int result = recvfrom(self->fd, (char*) msg, msgSize, 0, NULL, NULL);
if (result == 0) /* peer has closed socket */
return -1;
if (result == SOCKET_ERROR) {
if (WSAGetLastError() == WSAEWOULDBLOCK)
return 0;
else
return -1;
}
return result;
}

@ -39,7 +39,7 @@ Hal_getTimeInMs()
#include <sys/time.h> #include <sys/time.h>
uint64_t msSinceEpoch
Hal_getTimeInMs() Hal_getTimeInMs()
{ {
struct timeval now; struct timeval now;
@ -49,5 +49,34 @@ Hal_getTimeInMs()
return ((uint64_t) now.tv_sec * 1000LL) + (now.tv_usec / 1000); return ((uint64_t) now.tv_sec * 1000LL) + (now.tv_usec / 1000);
} }
nsSinceEpoch
Hal_getTimeInNs()
{
struct timespec now;
clock_gettime(CLOCK_REALTIME, &now);
nsSinceEpoch nsTime = now.tv_sec * 1000000000UL;
nsTime += now.tv_nsec;
return nsTime;
}
bool
Hal_setTimeInNs(nsSinceEpoch nsTime)
{
struct timespec tv;
tv.tv_sec = nsTime / 1000000000UL;
tv.tv_nsec = nsTime % 1000000000UL;
if (clock_settime(CLOCK_REALTIME, &tv) < 0) {
return false;
}
return true;
}
#endif #endif

@ -1,7 +1,7 @@
/* /*
* iec61850_common.c * iec61850_common.c
* *
* Copyright 2013 Michael Zillgith * Copyright 2013-2020 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -341,14 +341,12 @@ Timestamp_setSubsecondPrecision(Timestamp* self, int subsecondPrecision)
void void
Timestamp_setTimeInSeconds(Timestamp* self, uint32_t secondsSinceEpoch) Timestamp_setTimeInSeconds(Timestamp* self, uint32_t secondsSinceEpoch)
{ {
uint8_t* timeArray = (uint8_t*) &secondsSinceEpoch;
uint8_t* valueArray = self->val; uint8_t* valueArray = self->val;
#if (ORDER_LITTLE_ENDIAN == 1) valueArray[0] = (secondsSinceEpoch / 0x1000000 & 0xff);
memcpyReverseByteOrder(valueArray, timeArray, 4); valueArray[1] = (secondsSinceEpoch / 0x10000 & 0xff);
#else valueArray[2] = (secondsSinceEpoch / 0x100 & 0xff);
memcpy(valueArray, timeArray, 4); valueArray[3] = (secondsSinceEpoch & 0xff);
#endif
self->val[4] = 0; self->val[4] = 0;
self->val[5] = 0; self->val[5] = 0;
@ -358,18 +356,16 @@ Timestamp_setTimeInSeconds(Timestamp* self, uint32_t secondsSinceEpoch)
} }
void void
Timestamp_setTimeInMilliseconds(Timestamp* self, uint64_t millisSinceEpoch) Timestamp_setTimeInMilliseconds(Timestamp* self, msSinceEpoch millisSinceEpoch)
{ {
uint32_t timeval32 = (uint32_t) (millisSinceEpoch / 1000LL); uint32_t timeval32 = (uint32_t) (millisSinceEpoch / 1000LL);
uint8_t* timeArray = (uint8_t*) &timeval32;
uint8_t* valueArray = self->val; uint8_t* valueArray = self->val;
#if (ORDER_LITTLE_ENDIAN == 1) valueArray[0] = (timeval32 / 0x1000000 & 0xff);
memcpyReverseByteOrder(valueArray, timeArray, 4); valueArray[1] = (timeval32 / 0x10000 & 0xff);
#else valueArray[2] = (timeval32 / 0x100 & 0xff);
memcpy(valueArray, timeArray, 4); valueArray[3] = (timeval32 & 0xff);
#endif
uint32_t remainder = (millisSinceEpoch % 1000LL); uint32_t remainder = (millisSinceEpoch % 1000LL);
uint32_t fractionOfSecond = (remainder) * 16777 + ((remainder * 216) / 1000); uint32_t fractionOfSecond = (remainder) * 16777 + ((remainder * 216) / 1000);
@ -382,32 +378,57 @@ Timestamp_setTimeInMilliseconds(Timestamp* self, uint64_t millisSinceEpoch)
/* don't touch time quality */ /* don't touch time quality */
} }
void
Timestamp_setTimeInNanoseconds(Timestamp* self, nsSinceEpoch nsTime)
{
uint32_t timeval32 = (nsTime / 1000000000LLU);
uint8_t* valueArray = self->val;
valueArray[0] = (timeval32 / 0x1000000 & 0xff);
valueArray[1] = (timeval32 / 0x10000 & 0xff);
valueArray[2] = (timeval32 / 0x100 & 0xff);
valueArray[3] = (timeval32 & 0xff);
uint64_t remainder = (nsTime % 1000000000LLU);
remainder = remainder << 24;
remainder = remainder / 1000000000UL;
uint32_t fractionOfSecond = (uint32_t) remainder;
/* encode fraction of second */
valueArray[4] = ((fractionOfSecond >> 16) & 0xff);
valueArray[5] = ((fractionOfSecond >> 8) & 0xff);
valueArray[6] = (fractionOfSecond & 0xff);
/* don't touch time quality */
}
uint32_t uint32_t
Timestamp_getTimeInSeconds(Timestamp* self) Timestamp_getTimeInSeconds(Timestamp* self)
{ {
uint32_t timeval32; uint32_t timeval32;
uint8_t* valueArray = self->val; uint8_t* valueArray = self->val;
#if (ORDER_LITTLE_ENDIAN == 1) timeval32 = valueArray[3];
memcpyReverseByteOrder((uint8_t*) &timeval32, valueArray, 4); timeval32 += valueArray[2] * 0x100;
#else timeval32 += valueArray[1] * 0x10000;
memcpy((uint8_t*) &timeval32, valueArray, 4); timeval32 += valueArray[0] * 0x1000000;
#endif
return timeval32; return timeval32;
} }
uint64_t msSinceEpoch
Timestamp_getTimeInMs(Timestamp* self) Timestamp_getTimeInMs(Timestamp* self)
{ {
uint32_t timeval32; uint32_t timeval32;
uint8_t* valueArray = self->val; uint8_t* valueArray = self->val;
#if (ORDER_LITTLE_ENDIAN == 1) timeval32 = valueArray[3];
memcpyReverseByteOrder((uint8_t*) &timeval32, valueArray, 4); timeval32 += valueArray[2] * 0x100;
#else timeval32 += valueArray[1] * 0x10000;
memcpy((uint8_t*) &timeval32, valueArray, 4); timeval32 += valueArray[0] * 0x1000000;
#endif
uint32_t fractionOfSecond = 0; uint32_t fractionOfSecond = 0;
@ -422,6 +443,33 @@ Timestamp_getTimeInMs(Timestamp* self)
return (uint64_t) msVal; return (uint64_t) msVal;
} }
nsSinceEpoch
Timestamp_getTimeInNs(Timestamp* self)
{
uint32_t timeval32;
uint8_t* valueArray = self->val;
timeval32 = valueArray[3];
timeval32 += valueArray[2] * 0x100;
timeval32 += valueArray[1] * 0x10000;
timeval32 += valueArray[0] * 0x1000000;
uint32_t fractionOfSecond;
fractionOfSecond = valueArray[6];
fractionOfSecond += valueArray[5] * 0x100;
fractionOfSecond += valueArray[4] * 0x10000;
uint64_t nsVal = fractionOfSecond;
nsVal = nsVal * 1000000000UL;
nsVal = nsVal >> 24;
uint64_t timeval64 = (uint64_t) timeval32 * 1000000000LLU + nsVal;
return timeval64;
}
void void
Timestamp_setByMmsUtcTime(Timestamp* self, MmsValue* mmsValue) Timestamp_setByMmsUtcTime(Timestamp* self, MmsValue* mmsValue)
{ {

@ -418,9 +418,12 @@ Timestamp_clearFlags(Timestamp* self);
LIB61850_API uint32_t LIB61850_API uint32_t
Timestamp_getTimeInSeconds(Timestamp* self); Timestamp_getTimeInSeconds(Timestamp* self);
LIB61850_API uint64_t LIB61850_API msSinceEpoch
Timestamp_getTimeInMs(Timestamp* self); Timestamp_getTimeInMs(Timestamp* self);
LIB61850_API nsSinceEpoch
Timestamp_getTimeInNs(Timestamp* self);
LIB61850_API bool LIB61850_API bool
Timestamp_isLeapSecondKnown(Timestamp* self); Timestamp_isLeapSecondKnown(Timestamp* self);
@ -450,11 +453,39 @@ Timestamp_getSubsecondPrecision(Timestamp* self);
LIB61850_API void LIB61850_API void
Timestamp_setSubsecondPrecision(Timestamp* self, int subsecondPrecision); Timestamp_setSubsecondPrecision(Timestamp* self, int subsecondPrecision);
/**
* \brief Set the time in seconds
*
* NOTE: the fractionOfSecond part is set to zero
* NOTE: the subSecondPrecision is not touched
*
* \param self the Timestamp instance
* \param secondsSinceEpoch the seconds since unix epoch (unix timestamp)
*/
LIB61850_API void LIB61850_API void
Timestamp_setTimeInSeconds(Timestamp* self, uint32_t secondsSinceEpoch); Timestamp_setTimeInSeconds(Timestamp* self, uint32_t secondsSinceEpoch);
/**
* \brief Set the time in milliseconds
*
* NOTE: the subSecondPrecision is not touched
*
* \param self the Timestamp instance
* \param msTime the milliseconds since unix epoch
*/
LIB61850_API void
Timestamp_setTimeInMilliseconds(Timestamp* self, msSinceEpoch msTime);
/**
* \brief Set the time in nanoseconds
*
* NOTE: the subSecondPrecision is not touched
*
* \param self the Timestamp instance
* \param msTime the nanoseconds since unix epoch
*/
LIB61850_API void LIB61850_API void
Timestamp_setTimeInMilliseconds(Timestamp* self, uint64_t millisSinceEpoch); Timestamp_setTimeInNanoseconds(Timestamp* self, nsSinceEpoch nsTime);
LIB61850_API void LIB61850_API void
Timestamp_setByMmsUtcTime(Timestamp* self, MmsValue* mmsValue); Timestamp_setByMmsUtcTime(Timestamp* self, MmsValue* mmsValue);

@ -58,12 +58,11 @@ struct sSVPublisher_ASDU {
uint16_t smpCntLimit; uint16_t smpCntLimit;
uint32_t confRev; uint32_t confRev;
uint64_t refrTm; Timestamp* refrTm;
uint8_t smpMod; uint8_t smpMod;
uint16_t smpRate; uint16_t smpRate;
uint8_t* smpCntBuf; uint8_t* smpCntBuf;
uint8_t* refrTmBuf;
uint8_t* smpSynchBuf; uint8_t* smpSynchBuf;
SVPublisher_ASDU _next; SVPublisher_ASDU _next;
@ -265,40 +264,6 @@ encodeInt64FixedSize(int64_t value, uint8_t* buffer, int bufPos)
return bufPos; return bufPos;
} }
static int
encodeUtcTime(uint64_t timeval, uint8_t* buffer, int bufPos)
{
uint32_t timeval32 = (timeval / 1000LL);
uint8_t* timeArray = (uint8_t*) &timeval32;
uint8_t* valueArray = buffer + bufPos;
#if (ORDER_LITTLE_ENDIAN == 1)
valueArray[0] = timeArray[3];
valueArray[1] = timeArray[2];
valueArray[2] = timeArray[1];
valueArray[3] = timeArray[0];
#else
valueArray[0] = timeArray[0];
valueArray[1] = timeArray[1];
valueArray[2] = timeArray[2];
valueArray[3] = timeArray[3];
#endif
uint32_t remainder = (timeval % 1000LL);
uint32_t fractionOfSecond = (remainder) * 16777 + ((remainder * 216) / 1000);
/* encode fraction of second */
valueArray[4] = ((fractionOfSecond >> 16) & 0xff);
valueArray[5] = ((fractionOfSecond >> 8) & 0xff);
valueArray[6] = (fractionOfSecond & 0xff);
/* encode time quality */
valueArray[7] = 0x0a; /* 10 bit sub-second time accuracy */
return bufPos + 8;
}
SVPublisher SVPublisher
SVPublisher_createEx(CommParameters* parameters, const char* interfaceId, bool useVlanTag) SVPublisher_createEx(CommParameters* parameters, const char* interfaceId, bool useVlanTag)
{ {
@ -332,7 +297,6 @@ SVPublisher_addASDU(SVPublisher self, const char* svID, const char* datset, uint
newAsdu->datset = datset; newAsdu->datset = datset;
newAsdu->confRev = confRev; newAsdu->confRev = confRev;
newAsdu->smpCntLimit = UINT16_MAX; newAsdu->smpCntLimit = UINT16_MAX;
newAsdu->_next = NULL; newAsdu->_next = NULL;
/* append new ASDU to list */ /* append new ASDU to list */
@ -417,8 +381,8 @@ SVPublisher_ASDU_encodeToBuffer(SVPublisher_ASDU self, uint8_t* buffer, int bufP
/* RefrTm */ /* RefrTm */
if (self->hasRefrTm) { if (self->hasRefrTm) {
bufPos = BerEncoder_encodeTL(0x84, 8, buffer, bufPos); bufPos = BerEncoder_encodeTL(0x84, 8, buffer, bufPos);
self->refrTmBuf = buffer + bufPos; self->refrTm = (Timestamp*) (buffer + bufPos);
bufPos = encodeUtcTime(self->refrTm, buffer, bufPos); bufPos += 8;
} }
/* SmpSynch */ /* SmpSynch */
@ -724,13 +688,34 @@ SVPublisher_ASDU_enableRefrTm(SVPublisher_ASDU self)
} }
void void
SVPublisher_ASDU_setRefrTm(SVPublisher_ASDU self, uint64_t refrTm) SVPublisher_ASDU_setRefrTmNs(SVPublisher_ASDU self, nsSinceEpoch refrTmNs)
{
self->hasRefrTm = true;
if (self->refrTm) {
Timestamp_setTimeInNanoseconds(self->refrTm, refrTmNs);
Timestamp_setSubsecondPrecision(self->refrTm, 20);
}
}
void
SVPublisher_ASDU_setRefrTm(SVPublisher_ASDU self, msSinceEpoch refrTm)
{
self->hasRefrTm = true;
if (self->refrTm) {
Timestamp_setTimeInMilliseconds(self->refrTm, refrTm);
Timestamp_setSubsecondPrecision(self->refrTm, 10);
}
}
void
SVPublisher_ASDU_setRefrTmByTimestamp(SVPublisher_ASDU self, Timestamp* refrTm)
{ {
self->hasRefrTm = true; self->hasRefrTm = true;
self->refrTm = refrTm;
if (self->refrTmBuf != NULL) if (self->refrTm)
encodeUtcTime(self->refrTm, self->refrTmBuf, 0); memcpy(self->refrTm, refrTm, 8);
} }
void void

@ -329,12 +329,34 @@ LIB61850_API void
SVPublisher_ASDU_enableRefrTm(SVPublisher_ASDU self); SVPublisher_ASDU_enableRefrTm(SVPublisher_ASDU self);
/** /**
* \brief Set the refresh time attribute of the ASDU. * \brief Set the refresh time attribute of the ASDU with nanosecond resolution
* *
* \param[in] self the Sampled Values ASDU instance. * \param[in] self the Sampled Values ASDU instance.
* \param[in] refrTmNs the refresh time value with nanoseconds resolution.
*/ */
LIB61850_API void LIB61850_API void
SVPublisher_ASDU_setRefrTm(SVPublisher_ASDU self, uint64_t refrTm); SVPublisher_ASDU_setRefrTmNs(SVPublisher_ASDU self, nsSinceEpoch refrTmNs);
/**
* \brief Set the refresh time attribute of the ASDU with millisecond resolution
*
* \param[in] self the Sampled Values ASDU instance.
* \param[in] refrTmNs the refresh time value with with milliseconds resolution.
*/
LIB61850_API void
SVPublisher_ASDU_setRefrTm(SVPublisher_ASDU self, msSinceEpoch refrTm);
/**
* \brief Set the refresh time attribute of the ASDU
*
* NOTE: Using this function you can control the time quality flags and the
* sub second precision (number of valid bits) of the RefrTm value.
*
* \param[in] self the Sampled Values ASDU instance.
* \param[in] refrTm the refresh time value
*/
LIB61850_API void
SVPublisher_ASDU_setRefrTmByTimestamp(SVPublisher_ASDU self, Timestamp* refrTm);
/** /**
* \brief Set the sample mode attribute of the ASDU. * \brief Set the sample mode attribute of the ASDU.

@ -339,7 +339,7 @@ parseASDU(SVReceiver self, SVSubscriber subscriber, uint8_t* buffer, int length)
if (SVSubscriber_ASDU_hasDatSet(&asdu)) if (SVSubscriber_ASDU_hasDatSet(&asdu))
printf("SV_SUBSCRIBER: DatSet: %s\n", asdu.datSet); printf("SV_SUBSCRIBER: DatSet: %s\n", asdu.datSet);
if (SVSubscriber_ASDU_hasRefrTm(&asdu)) if (SVSubscriber_ASDU_hasRefrTm(&asdu))
printf("SV_SUBSCRIBER: RefrTm: %lu\n", SVSubscriber_ASDU_getRefrTmAsMs(&asdu)); printf("SV_SUBSCRIBER: RefrTm[ns]: %lu\n", SVSubscriber_ASDU_getRefrTmAsNs(&asdu));
if (SVSubscriber_ASDU_hasSmpMod(&asdu)) if (SVSubscriber_ASDU_hasSmpMod(&asdu))
printf("SV_SUBSCRIBER: SmpMod: %d\n", SVSubscriber_ASDU_getSmpMod(&asdu)); printf("SV_SUBSCRIBER: SmpMod: %d\n", SVSubscriber_ASDU_getSmpMod(&asdu));
if (SVSubscriber_ASDU_hasSmpRate(&asdu)) if (SVSubscriber_ASDU_hasSmpRate(&asdu))
@ -599,8 +599,8 @@ SVSubscriber_ASDU_getSmpCnt(SVSubscriber_ASDU self)
return retVal; return retVal;
} }
static uint64_t static nsSinceEpoch
decodeUtcTime(uint8_t* buffer, uint8_t* timeQuality) decodeUtcTimeToNsTime(uint8_t* buffer, uint8_t* timeQuality)
{ {
uint32_t timeval32; uint32_t timeval32;
@ -609,33 +609,45 @@ decodeUtcTime(uint8_t* buffer, uint8_t* timeQuality)
timeval32 += buffer[1] * 0x10000; timeval32 += buffer[1] * 0x10000;
timeval32 += buffer[0] * 0x1000000; timeval32 += buffer[0] * 0x1000000;
uint32_t msVal;
uint32_t fractionOfSecond; uint32_t fractionOfSecond;
fractionOfSecond = buffer[6]; fractionOfSecond = buffer[6];
fractionOfSecond += buffer[5] * 0x100; fractionOfSecond += buffer[5] * 0x100;
fractionOfSecond += buffer[4] * 0x10000; fractionOfSecond += buffer[4] * 0x10000;
msVal = (uint32_t) (((uint64_t) fractionOfSecond * 1000) / 16777215); uint64_t nsVal = fractionOfSecond;
nsVal = nsVal * 1000000000UL;
nsVal = nsVal >> 24;
if (timeQuality != NULL) if (timeQuality != NULL)
*timeQuality = buffer[7]; *timeQuality = buffer[7];
uint64_t timeval64 = (uint64_t) timeval32 * 1000 + (uint64_t) msVal; uint64_t timeval64 = (uint64_t) timeval32 * 1000000000LLU + nsVal;
return timeval64; return timeval64;
} }
uint64_t msSinceEpoch
SVSubscriber_ASDU_getRefrTmAsMs(SVSubscriber_ASDU self) SVSubscriber_ASDU_getRefrTmAsMs(SVSubscriber_ASDU self)
{ {
uint64_t msTime = 0; msSinceEpoch msTime = 0;
if (self->refrTm != NULL)
msTime = decodeUtcTimeToNsTime(self->refrTm, NULL);
return (msTime / 1000000LLU);
}
nsSinceEpoch
SVSubscriber_ASDU_getRefrTmAsNs(SVSubscriber_ASDU self)
{
nsSinceEpoch nsTime = 0;
if (self->refrTm != NULL) if (self->refrTm != NULL)
msTime = decodeUtcTime(self->refrTm, NULL); nsTime = decodeUtcTimeToNsTime(self->refrTm, NULL);
return msTime; return nsTime;
} }
bool bool

@ -376,7 +376,7 @@ LIB61850_API bool
SVSubscriber_ASDU_hasSmpRate(SVSubscriber_ASDU self); SVSubscriber_ASDU_hasSmpRate(SVSubscriber_ASDU self);
/** /**
* \brief Get the RefrTim value included in SV ASDU as ms timestamp * \brief Get the RefrTim value included in SV ASDU as milliseconds timestamp
* *
* \param self ASDU object instance * \param self ASDU object instance
* *
@ -385,6 +385,16 @@ SVSubscriber_ASDU_hasSmpRate(SVSubscriber_ASDU self);
LIB61850_API uint64_t LIB61850_API uint64_t
SVSubscriber_ASDU_getRefrTmAsMs(SVSubscriber_ASDU self); SVSubscriber_ASDU_getRefrTmAsMs(SVSubscriber_ASDU self);
/**
* \brief Get the RefrTim value included in SV ASDU as nanoseconds timestamp
*
* \param self ASDU object instance
*
* \return the time value as nanoseconds timestamp or 0 if RefrTm is not present in the SV ASDU
*/
LIB61850_API nsSinceEpoch
SVSubscriber_ASDU_getRefrTmAsNs(SVSubscriber_ASDU self);
/** /**
* \brief Get an INT8 data value in the data part of the ASDU * \brief Get an INT8 data value in the data part of the ASDU
* *

Loading…
Cancel
Save