diff --git a/examples/iec61850_9_2_LE_example/iec61850_9_2_LE_example.c b/examples/iec61850_9_2_LE_example/iec61850_9_2_LE_example.c index 52ee54a8..a7a016e6 100644 --- a/examples/iec61850_9_2_LE_example/iec61850_9_2_LE_example.c +++ b/examples/iec61850_9_2_LE_example/iec61850_9_2_LE_example.c @@ -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); diff --git a/hal/inc/hal_socket.h b/hal/inc/hal_socket.h index 316058a7..5e6d15a9 100644 --- a/hal/inc/hal_socket.h +++ b/hal/inc/hal_socket.h @@ -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); diff --git a/hal/inc/hal_time.h b/hal/inc/hal_time.h index 0e57d338..8944aa19 100644 --- a/hal/inc/hal_time.h +++ b/hal/inc/hal_time.h @@ -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); + /*! @} */ /*! @} */ diff --git a/hal/socket/linux/socket_linux.c b/hal/socket/linux/socket_linux.c index 7d04e4ff..020c850d 100644 --- a/hal/socket/linux/socket_linux.c +++ b/hal/socket/linux/socket_linux.c @@ -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; +} diff --git a/hal/socket/win32/socket_win32.c b/hal/socket/win32/socket_win32.c index 2368f32a..d42a3674 100644 --- a/hal/socket/win32/socket_win32.c +++ b/hal/socket/win32/socket_win32.c @@ -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; +} diff --git a/hal/time/unix/time.c b/hal/time/unix/time.c index f7951fde..6565da33 100644 --- a/hal/time/unix/time.c +++ b/hal/time/unix/time.c @@ -39,7 +39,7 @@ Hal_getTimeInMs() #include -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 diff --git a/src/iec61850/common/iec61850_common.c b/src/iec61850/common/iec61850_common.c index 704be1da..58cb146f 100644 --- a/src/iec61850/common/iec61850_common.c +++ b/src/iec61850/common/iec61850_common.c @@ -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) { diff --git a/src/iec61850/inc/iec61850_common.h b/src/iec61850/inc/iec61850_common.h index dfe1c553..d934aff2 100644 --- a/src/iec61850/inc/iec61850_common.h +++ b/src/iec61850/inc/iec61850_common.h @@ -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); diff --git a/src/sampled_values/sv_publisher.c b/src/sampled_values/sv_publisher.c index 1f7b0110..61346917 100644 --- a/src/sampled_values/sv_publisher.c +++ b/src/sampled_values/sv_publisher.c @@ -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 diff --git a/src/sampled_values/sv_publisher.h b/src/sampled_values/sv_publisher.h index 3c59c1b4..c737ae5f 100644 --- a/src/sampled_values/sv_publisher.h +++ b/src/sampled_values/sv_publisher.h @@ -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. diff --git a/src/sampled_values/sv_subscriber.c b/src/sampled_values/sv_subscriber.c index 1d49b39c..fe014659 100644 --- a/src/sampled_values/sv_subscriber.c +++ b/src/sampled_values/sv_subscriber.c @@ -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 diff --git a/src/sampled_values/sv_subscriber.h b/src/sampled_values/sv_subscriber.h index 44ee9066..dd072679 100644 --- a/src/sampled_values/sv_subscriber.h +++ b/src/sampled_values/sv_subscriber.h @@ -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 *