Skip to content

Commit

Permalink
Add code examples in doxygen manual (#42)
Browse files Browse the repository at this point in the history
Major Changes
This PR makes the major update of adding code examples for coreSNTP library APIs in doxygen documentation. This is done by providing an example POSIX application of using the library to setup an SNTP client, and adding references to the relevant parts of the example file in the doxygen documentation of APIs.
This PR also adds a mechanism to validate build of the POSIX example application through a CI check.

Minor Changes
Fixes in doxygen documentation
Re-purpose size_table.html for public visibility in README.md
  • Loading branch information
aggarw13 authored Jul 22, 2021
1 parent cd2f6dd commit 7d835d5
Show file tree
Hide file tree
Showing 11 changed files with 386 additions and 53 deletions.
17 changes: 12 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,24 @@ jobs:
cmake -S test -B build/ \
-G "Unix Makefiles" \
-DCMAKE_BUILD_TYPE=Debug \
-DBUILD_CLONE_SUBMODULES=ON \
-DCMAKE_C_FLAGS='-O0 -Wall -Wextra -Werror -Wformat -Wformat-security -Warray-bounds'
make -C build/ coverity_analysis -j8
- name: Build Library in Release mode
run: |
rm -rf ./build
cmake -S test -B build/ -G "Unix Makefiles" \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_CLONE_SUBMODULES=ON \
-DCMAKE_C_FLAGS='-Wall -Wextra -Werror -DNDEBUG -Wformat -Wformat-security -Warray-bounds'
make -C build/ coverity_analysis -j8
build-code-example:
runs-on: ubuntu-latest
steps:
- name: Clone This Repo
uses: actions/checkout@v2
- name: Build Code Example used in Doxygen
run: |
cmake -S test -B Build -DBUILD_CODE_EXAMPLE=ON
make -C Build code_example_posix -j8
unittest-with-sanitizer:
runs-on: ubuntu-latest
steps:
Expand All @@ -48,7 +55,7 @@ jobs:
cmake -S test -B build/ \
-G "Unix Makefiles" \
-DCMAKE_BUILD_TYPE=Debug \
-DBUILD_CLONE_SUBMODULES=ON \
-DBUILD_UNIT_TESTS=ON \
-DCMAKE_C_FLAGS="${CFLAGS}"
make -C build all -j8
- name: Run unit tests with sanitizer
Expand All @@ -68,7 +75,7 @@ jobs:
cmake -S test -B build/ \
-G "Unix Makefiles" \
-DCMAKE_BUILD_TYPE=Debug \
-DBUILD_CLONE_SUBMODULES=ON \
-DBUILD_UNIT_TESTS=ON \
-DCMAKE_C_FLAGS='--coverage -Wall -Wextra -Werror -DNDEBUG -Wno-error=pedantic -Wno-variadic-macros -DLOGGING_LEVEL_DEBUG=1'
make -C build/ all
- name: Test
Expand Down Expand Up @@ -164,4 +171,4 @@ jobs:
uses: FreeRTOS/CI-CD-Github-Actions/memory_statistics@main
with:
config: .github/memory_statistics_config.json
check_against: docs/doxygen/include/size_table.html
check_against: docs/doxygen/include/size_table.md
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ An SNTP client can request time from both NTP and SNTP servers. According to the

This library has gone through code quality checks including verification that no function has a [GNU Complexity](https://www.gnu.org/software/complexity/manual/complexity.html) score over 8, and checks against deviations from mandatory rules in the [MISRA coding standard](https://www.misra.org.uk). Deviations from the MISRA C:2012 guidelines are documented under [MISRA Deviations](MISRA.md). This library has also undergone both static code analysis from [Coverity static analysis](https://scan.coverity.com/), and validation of memory safety through the [CBMC automated reasoning tool](https://www.cprover.org/cbmc/).

See memory requirements for this library [here](./docs/doxygen/include/size_table.md).

## Cloning this repository
This repo uses [Git Submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules) to bring in dependent components.

Expand Down Expand Up @@ -57,7 +59,7 @@ git submodule update --checkout --init --recursive test/unit-test/CMock

1. Go to the root directory of this repository. (Make sure that the **CMock** submodule is cloned as described [above](#checkout-cmock-submodule))

1. Run the *cmake* command: `cmake -S test -B build`
1. Run the *cmake* command: `cmake -S test -B build -DBUILD_UNIT_TESTS=ON`

1. Run this command to build the library and unit tests: `make -C build all`

Expand Down
265 changes: 265 additions & 0 deletions docs/doxygen/code_examples/example_sntp_client_posix.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
#include <stdlib.h>
#include <netdb.h>
#include <time.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <poll.h>
#include <string.h>
#include <assert.h>
#include "core_sntp_client.h"

/* @[code_example_sntpdnsresolve] */
/* Example POSIX implementation of SntpDnsReolve_t interface. */
static bool resolveDns( const SntpServerInfo_t * pServerAddr,
uint32_t * pIpV4Addr )
{
bool status = false;
int32_t dnsStatus = -1;
struct addrinfo hints;
struct addrinfo * pListHead = NULL;

hints.ai_family = AF_UNSPEC;

hints.ai_socktype = ( int32_t ) SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;

dnsStatus = getaddrinfo( pServerAddr->pServerName, NULL, &hints, &pListHead );

if( dnsStatus == 0 )
{
struct sockaddr_in * pAddrInfo = ( struct sockaddr_in * ) pListHead->ai_addr;
inet_ntop( pAddrInfo->sin_family,
&pAddrInfo->sin_addr,
( int8_t * ) pIpV4Addr,
INET_ADDRSTRLEN );

status = true;
}

freeaddrinfo( pListHead );

return status;
}
/* @[code_example_sntpdnsresolve] */

/* @[code_example_networkcontext] */
/* Example definition of NetworkContext_t for UDP socket operations. */
struct NetworkContext
{
int udpSocket;
};
/* @[code_example_networkcontext] */

/* @[code_example_udptransport_sendto] */
/* Example POSIX implementation of the UdpTransportSendTo_t function of UDP transport interface. */
static int32_t UdpTransport_Send( NetworkContext_t * pNetworkContext,
uint32_t serverAddr,
uint16_t serverPort,
const void * pBuffer,
uint16_t bytesToSend )
{
int32_t bytesSent = -1, pollStatus = 1;
struct pollfd pollFds;

pollFds.events = POLLOUT | POLLPRI;
pollFds.revents = 0;
pollFds.fd = pNetworkContext->udpSocket;

/* Check if there is data to read from the socket. */
pollStatus = poll( &pollFds, 1, 0 );

if( pollStatus > 0 )
{
struct sockaddr_in addrInfo;
addrInfo.sin_family = AF_INET;
addrInfo.sin_port = htons( serverPort );
addrInfo.sin_addr.s_addr = htonl( serverAddr );

bytesSent = sendto( pNetworkContext->udpSocket,
pBuffer,
bytesToSend, 0,
( const struct sockaddr * ) &addrInfo,
sizeof( addrInfo ) );
}
else if( pollStatus == 0 )
{
bytesSent = 0;
}

return bytesSent;
}
/* @[code_example_udptransport_sendto] */

/* @[code_example_udptransport_recvfrom] */
/* Example POSIX implementation of the UdpTransportRecvFrom_t function of UDP transport interface. */
static int32_t UdpTransport_Recv( NetworkContext_t * pNetworkContext,
uint32_t serverAddr,
uint16_t serverPort,
void * pBuffer,
uint16_t bytesToRecv )
{
int32_t bytesReceived = -1, pollStatus = 1;
struct pollfd pollFds;

pollFds.events = POLLIN | POLLPRI;
pollFds.revents = 0;
pollFds.fd = pNetworkContext->udpSocket;

/* Check if there is data to read from the socket. */
pollStatus = poll( &pollFds, 1, 0 );

if( pollStatus > 0 )
{
struct sockaddr_in addrInfo;
addrInfo.sin_family = AF_INET;
addrInfo.sin_port = htons( serverPort );
addrInfo.sin_addr.s_addr = htonl( serverAddr );
socklen_t addrLen = sizeof( addrInfo );

bytesReceived = recvfrom( pNetworkContext->udpSocket, pBuffer,
bytesToRecv, 0,
( struct sockaddr * ) &addrInfo,
&addrLen );
}
else if( pollStatus == 0 )
{
bytesReceived = 0;
}

return bytesReceived;
}
/* @[code_example_udptransport_recvfrom] */

/* @[code_example_sntpsettime] */
/* Example implementation of the SntpSetTime_t interface for POSIX platforms. */
static void sntpClient_SetTime( const SntpServerInfo_t * pTimeServer,
const SntpTimestamp_t * pServerTime,
int64_t clockOffsetMs,
SntpLeapSecondInfo_t leapSecondInfo )
{
/* @[code_example_sntp_converttounixtime] */
uint32_t unixSecs;
uint32_t unixMs;
SntpStatus_t status = Sntp_ConvertToUnixTime( pServerTime, &unixSecs, &unixMs );

/* @[code_example_sntp_converttounixtime] */
assert( status == SntpSuccess );

struct timespec serverTime =
{
.tv_sec = unixSecs,
.tv_nsec = unixMs * 1000
};

clock_settime( CLOCK_REALTIME, &serverTime );
}
/* @[code_example_sntpsettime] */

/* @[code_example_sntpgettime] */
/* Example implementation of the SntpGetTime_t interface for POSIX platforms. */
static void sntpClient_GetTime( SntpTimestamp_t * pCurrentTime )
{
struct timespec currTime;

( void ) clock_gettime( CLOCK_REALTIME, &currTime );

pCurrentTime->seconds = currTime.tv_sec;
pCurrentTime->fractions = ( currTime.tv_sec / 1000 ) * SNTP_FRACTION_VALUE_PER_MICROSECOND;
}
/* @[code_example_sntpgettime] */

/* Configuration constants for the example SNTP client. */
#define TEST_TIME_SERVER_1 "0.pool.ntp.org"
#define TEST_TIME_SERVER_2 "1.pool.ntp.org"

#define SERVER_RESPONSE_TIMEOUT_MS 3000
#define TIME_REQUEST_SEND_WAIT_TIME_MS 2000
#define TIME_REQUEST_RECEIVE_WAIT_TIME_MS 1000

#define SYSTEM_CLOCK_FREQUENCY_TOLERANCE_PPM 500
#define SYSTEM_CLOCK_DESIRED_ACCURACY_MS 300

int main( void )
{
/* @[code_example_sntp_init] */
/* Memory for network buffer. */
uint8_t networkBuffer[ SNTP_PACKET_BASE_SIZE ];

/* Create UDP socket. */
NetworkContext_t udpContext;

udpContext.udpSocket = socket( AF_INET, SOCK_DGRAM, 0 );

/* Setup list of time servers. */
SntpServerInfo_t pTimeServers[] =
{
{
.port = SNTP_DEFAULT_SERVER_PORT,
.pServerName = TEST_TIME_SERVER_1,
.serverNameLen = strlen( TEST_TIME_SERVER_1 )
},
{
.port = SNTP_DEFAULT_SERVER_PORT,
.pServerName = TEST_TIME_SERVER_2,
.serverNameLen = strlen( TEST_TIME_SERVER_2 )
}
};

/* Set the UDP transport interface object. */
UdpTransportInterface_t udpTransportIntf;
udpTransportIntf.pUserContext = &udpContext;
udpTransportIntf.sendTo = UdpTransport_Send;
udpTransportIntf.recvFrom = UdpTransport_Recv;

/* Context variable. */
SntpContext_t context;

/* Initialize context. */
SntpStatus_t status = Sntp_Init( &context,
pTimeServers,
sizeof( pTimeServers ) / sizeof( SntpServerInfo_t ),
SERVER_RESPONSE_TIMEOUT_MS,
networkBuffer,
SNTP_PACKET_BASE_SIZE,
resolveDns,
sntpClient_GetTime,
sntpClient_SetTime,
&udpTransportIntf,
NULL );
assert( status == SntpSuccess );
/* @[code_example_sntp_init] */

/* Calculate the polling interval period for the SNTP client. */
/* @[code_example_sntp_calculatepollinterval] */
uint32_t pollingIntervalPeriod;
status = Sntp_CalculatePollInterval( SYSTEM_CLOCK_FREQUENCY_TOLERANCE_PPM,
SYSTEM_CLOCK_DESIRED_ACCURACY_MS,
&pollingIntervalPeriod );
/* @[code_example_sntp_calculatepollinterval] */
assert( status == SntpSuccess );

/* Loop of SNTP client for period time synchronization. */
/* @[code_example_sntp_send_receive] */
while( 1 )
{
status = Sntp_SendTimeRequest( &context,
rand() % UINT32_MAX,
TIME_REQUEST_SEND_WAIT_TIME_MS );
assert( status == SntpSuccess );

do
{
status = Sntp_ReceiveTimeResponse( &context, TIME_REQUEST_RECEIVE_WAIT_TIME_MS );
} while( status == SntpNoResponseReceived );

assert( status == SntpSuccess );

/* Delay of poll interval period before next time synchronization. */
sleep( pollingIntervalPeriod );
}

/* @[code_example_sntp_send_receive] */

return EXIT_SUCCESS;
}
7 changes: 5 additions & 2 deletions docs/doxygen/config.doxyfile
Original file line number Diff line number Diff line change
Expand Up @@ -798,14 +798,17 @@ EXCLUDE_SYMLINKS = NO
# that contain example code fragments that are included (see the \include
# command).

EXAMPLE_PATH = source/include docs/doxygen/include
EXAMPLE_PATH = source/include \
docs/doxygen/include \
docs/doxygen/code_examples \
test/unit-test

# If the value of the EXAMPLE_PATH tag contains directories, you can use the
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
# *.h) to filter out the source-files in the directories. If left blank all
# files are included.

EXAMPLE_PATTERNS = *
EXAMPLE_PATTERNS = *c

# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
# searched for input files to be used with the \include or \dontinclude commands
Expand Down
File renamed without changes.
Loading

0 comments on commit 7d835d5

Please sign in to comment.