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_setQuality(asdu, vol4q, q);
SVPublisher_ASDU_setRefrTm(asdu, Hal_getTimeInMs());
SVPublisher_ASDU_setRefrTmNs(asdu, Hal_getTimeInNs());
SVPublisher_ASDU_setSmpCnt(asdu, (uint16_t) sampleCount);

@ -57,6 +57,8 @@ extern "C" {
/** Opaque reference for a server socket instance */
typedef struct sServerSocket* ServerSocket;
typedef struct sUdpSocket* UdpSocket;
/** Opaque reference for a client or connection socket instance */
typedef struct sSocket* Socket;
@ -140,6 +142,18 @@ Handleset_destroy(HandleSet self);
PAL_API ServerSocket
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
ServerSocket_listen(ServerSocket self);

@ -1,7 +1,7 @@
/*
* time.c
*
* Copyright 2013, 2014 Michael Zillgith
* Copyright 2013-2020 Michael Zillgith
*
* 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.
*
@ -54,9 +57,23 @@ extern "C" {
*
* \return the system time with millisecond resolution.
*/
PAL_API uint64_t
PAL_API msSinceEpoch
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;
};
struct sUdpSocket {
int fd;
};
struct sHandleSet {
LinkedList sockets;
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 (DEBUG_SOCKET)
printf("Failed to enable TCP keepalive\n");
printf("SOCKET: Failed to enable TCP keepalive\n");
}
#if defined TCP_KEEPCNT
optval = idleTime;
if (setsockopt(self->fd, IPPROTO_TCP, TCP_KEEPIDLE, &optval, optlen)) {
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;
if (setsockopt(self->fd, IPPROTO_TCP, TCP_KEEPINTVL, &optval, optlen)) {
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;
if (setsockopt(self->fd, IPPROTO_TCP, TCP_KEEPCNT, &optval, optlen)) {
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 */
@ -343,7 +347,7 @@ closeAndShutdownSocket(int socketFd)
if (socketFd != -1) {
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(socketFd, SHUT_RDWR);
@ -505,7 +509,6 @@ Socket_connect(Socket self, const char* address, int port)
return false;
}
static char*
convertAddressToStr(struct sockaddr_storage* addr)
{
@ -667,3 +670,94 @@ Socket_destroy(Socket 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;
};
struct sUdpSocket {
SOCKET fd;
};
HandleSet
Handleset_new(void)
{
@ -612,3 +616,101 @@ Socket_destroy(Socket 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>
uint64_t
msSinceEpoch
Hal_getTimeInMs()
{
struct timeval now;
@ -49,5 +49,34 @@ Hal_getTimeInMs()
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

@ -1,7 +1,7 @@
/*
* iec61850_common.c
*
* Copyright 2013 Michael Zillgith
* Copyright 2013-2020 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -341,14 +341,12 @@ Timestamp_setSubsecondPrecision(Timestamp* self, int subsecondPrecision)
void
Timestamp_setTimeInSeconds(Timestamp* self, uint32_t secondsSinceEpoch)
{
uint8_t* timeArray = (uint8_t*) &secondsSinceEpoch;
uint8_t* valueArray = self->val;
#if (ORDER_LITTLE_ENDIAN == 1)
memcpyReverseByteOrder(valueArray, timeArray, 4);
#else
memcpy(valueArray, timeArray, 4);
#endif
valueArray[0] = (secondsSinceEpoch / 0x1000000 & 0xff);
valueArray[1] = (secondsSinceEpoch / 0x10000 & 0xff);
valueArray[2] = (secondsSinceEpoch / 0x100 & 0xff);
valueArray[3] = (secondsSinceEpoch & 0xff);
self->val[4] = 0;
self->val[5] = 0;
@ -358,18 +356,16 @@ Timestamp_setTimeInSeconds(Timestamp* self, uint32_t secondsSinceEpoch)
}
void
Timestamp_setTimeInMilliseconds(Timestamp* self, uint64_t millisSinceEpoch)
Timestamp_setTimeInMilliseconds(Timestamp* self, msSinceEpoch millisSinceEpoch)
{
uint32_t timeval32 = (uint32_t) (millisSinceEpoch / 1000LL);
uint8_t* timeArray = (uint8_t*) &timeval32;
uint8_t* valueArray = self->val;
#if (ORDER_LITTLE_ENDIAN == 1)
memcpyReverseByteOrder(valueArray, timeArray, 4);
#else
memcpy(valueArray, timeArray, 4);
#endif
valueArray[0] = (timeval32 / 0x1000000 & 0xff);
valueArray[1] = (timeval32 / 0x10000 & 0xff);
valueArray[2] = (timeval32 / 0x100 & 0xff);
valueArray[3] = (timeval32 & 0xff);
uint32_t remainder = (millisSinceEpoch % 1000LL);
uint32_t fractionOfSecond = (remainder) * 16777 + ((remainder * 216) / 1000);
@ -382,32 +378,57 @@ Timestamp_setTimeInMilliseconds(Timestamp* self, uint64_t millisSinceEpoch)
/* 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
Timestamp_getTimeInSeconds(Timestamp* self)
{
uint32_t timeval32;
uint8_t* valueArray = self->val;
#if (ORDER_LITTLE_ENDIAN == 1)
memcpyReverseByteOrder((uint8_t*) &timeval32, valueArray, 4);
#else
memcpy((uint8_t*) &timeval32, valueArray, 4);
#endif
timeval32 = valueArray[3];
timeval32 += valueArray[2] * 0x100;
timeval32 += valueArray[1] * 0x10000;
timeval32 += valueArray[0] * 0x1000000;
return timeval32;
}
uint64_t
msSinceEpoch
Timestamp_getTimeInMs(Timestamp* self)
{
uint32_t timeval32;
uint8_t* valueArray = self->val;
#if (ORDER_LITTLE_ENDIAN == 1)
memcpyReverseByteOrder((uint8_t*) &timeval32, valueArray, 4);
#else
memcpy((uint8_t*) &timeval32, valueArray, 4);
#endif
timeval32 = valueArray[3];
timeval32 += valueArray[2] * 0x100;
timeval32 += valueArray[1] * 0x10000;
timeval32 += valueArray[0] * 0x1000000;
uint32_t fractionOfSecond = 0;
@ -422,6 +443,33 @@ Timestamp_getTimeInMs(Timestamp* self)
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
Timestamp_setByMmsUtcTime(Timestamp* self, MmsValue* mmsValue)
{

@ -418,9 +418,12 @@ Timestamp_clearFlags(Timestamp* self);
LIB61850_API uint32_t
Timestamp_getTimeInSeconds(Timestamp* self);
LIB61850_API uint64_t
LIB61850_API msSinceEpoch
Timestamp_getTimeInMs(Timestamp* self);
LIB61850_API nsSinceEpoch
Timestamp_getTimeInNs(Timestamp* self);
LIB61850_API bool
Timestamp_isLeapSecondKnown(Timestamp* self);
@ -450,11 +453,39 @@ Timestamp_getSubsecondPrecision(Timestamp* self);
LIB61850_API void
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
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
Timestamp_setTimeInMilliseconds(Timestamp* self, uint64_t millisSinceEpoch);
Timestamp_setTimeInNanoseconds(Timestamp* self, nsSinceEpoch nsTime);
LIB61850_API void
Timestamp_setByMmsUtcTime(Timestamp* self, MmsValue* mmsValue);

@ -58,12 +58,11 @@ struct sSVPublisher_ASDU {
uint16_t smpCntLimit;
uint32_t confRev;
uint64_t refrTm;
Timestamp* refrTm;
uint8_t smpMod;
uint16_t smpRate;
uint8_t* smpCntBuf;
uint8_t* refrTmBuf;
uint8_t* smpSynchBuf;
SVPublisher_ASDU _next;
@ -265,40 +264,6 @@ encodeInt64FixedSize(int64_t value, uint8_t* buffer, int 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_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->confRev = confRev;
newAsdu->smpCntLimit = UINT16_MAX;
newAsdu->_next = NULL;
/* append new ASDU to list */
@ -417,8 +381,8 @@ SVPublisher_ASDU_encodeToBuffer(SVPublisher_ASDU self, uint8_t* buffer, int bufP
/* RefrTm */
if (self->hasRefrTm) {
bufPos = BerEncoder_encodeTL(0x84, 8, buffer, bufPos);
self->refrTmBuf = buffer + bufPos;
bufPos = encodeUtcTime(self->refrTm, buffer, bufPos);
self->refrTm = (Timestamp*) (buffer + bufPos);
bufPos += 8;
}
/* SmpSynch */
@ -724,13 +688,34 @@ SVPublisher_ASDU_enableRefrTm(SVPublisher_ASDU self)
}
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->refrTm = refrTm;
if (self->refrTmBuf != NULL)
encodeUtcTime(self->refrTm, self->refrTmBuf, 0);
if (self->refrTm)
memcpy(self->refrTm, refrTm, 8);
}
void

@ -329,12 +329,34 @@ LIB61850_API void
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] refrTmNs the refresh time value with nanoseconds resolution.
*/
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.

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

@ -376,7 +376,7 @@ LIB61850_API bool
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
*
@ -385,6 +385,16 @@ SVSubscriber_ASDU_hasSmpRate(SVSubscriber_ASDU self);
LIB61850_API uint64_t
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
*

Loading…
Cancel
Save