Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add code examples in doxygen manual #42

Merged
merged 3 commits into from
Jul 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading