eventMonitor_example.c
Description
This source file is an example of how to use NTAPI to monitor events. The example program runs in an infinite loop polling the event stream for events. When an event is received, it is printed on the screen. It also shows how to translate read sensor data into a text representation.
The example program uses the following NTAPI functions:
- NT_Init()
- NT_InfoOpen()
- NT_InfoRead()
- NT_InfoClose()
- NT_EventOpen()
- NT_EventRead()
- NT_EventClose()
- NT_Done()
- NT_ExplainError()
Prerequisites
- None.
Program flow
The following is required to monitor events:
- #include/nt.h - Applications/Tools only need to include nt.h to obtain prototypes, macros etc. from NTAPI.
- NT_Init(NTAPI_VERSION) - Initialize the NTAPI library. NTAPI_VERSION is a define that describes the version of the API described in the header files included by nt.h. NT_Init() instructs the NTAPI library to convert returned data to the NTAPI_VERSION, if possible. This ensures that applications can run on NTAPI libraries of newer versions.
- NT_EventOpen() - Open an event stream making it possible to receive events. When an event stream is opened, all events are stored on an event queue until they are read.
- NT_EventRead() - Wait for either a timeout or for an event to arrive, then return a single event from the event queue. If no event arrives within the timeout period, a NT_STATUS_TIMEOUT is returned.
- NT_EventClose() - Close an event stream.
- NT_InfoOpen() - Open an information stream.
- NT_InfoRead() - Read sensor information data in order to get the name and sensor limits.
- NT_InfoClose() - Close an information stream.
- NT_Done() - Close down the NTAPI library.
- NT_ExplainError() - Explain an error code returned by NTAPI functions.
Code
/*
*
* Copyright 2021 Napatech A/S. All Rights Reserved.
*
* 1. Copying, modification, and distribution of this file, or executable
* versions of this file, is governed by the terms of the Napatech Software
* license agreement under which this file was made available. If you do not
* agree to the terms of the license do not install, copy, access or
* otherwise use this file.
*
* 2. Under the Napatech Software license agreement you are granted a
* limited, non-exclusive, non-assignable, copyright license to copy, modify
* and distribute this file in conjunction with Napatech SmartNIC's and
* similar hardware manufactured or supplied by Napatech A/S.
*
* 3. The full Napatech Software license agreement is included in this
* distribution, please see "NP-0405 Napatech Software license
* agreement.pdf"
*
* 4. Redistributions of source code must retain this copyright notice,
* list of conditions and the following disclaimer.
*
* THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTIES, EXPRESS OR
* IMPLIED, AND NAPATECH DISCLAIMS ALL IMPLIED WARRANTIES INCLUDING ANY
* IMPLIED WARRANTY OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, OR OF
* FITNESS FOR A PARTICULAR PURPOSE. TO THE EXTENT NOT PROHIBITED BY
* APPLICABLE LAW, IN NO EVENT SHALL NAPATECH BE LIABLE FOR PERSONAL INJURY,
* OR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES WHATSOEVER,
* INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF PROFITS, CORRUPTION OR
* LOSS OF DATA, FAILURE TO TRANSMIT OR RECEIVE ANY DATA OR INFORMATION,
* BUSINESS INTERRUPTION OR ANY OTHER COMMERCIAL DAMAGES OR LOSSES, ARISING
* OUT OF OR RELATED TO YOUR USE OR INABILITY TO USE NAPATECH SOFTWARE OR
* SERVICES OR ANY THIRD PARTY SOFTWARE OR APPLICATIONS IN CONJUNCTION WITH
* THE NAPATECH SOFTWARE OR SERVICES, HOWEVER CAUSED, REGARDLESS OF THE THEORY
* OF LIABILITY (CONTRACT, TORT OR OTHERWISE) AND EVEN IF NAPATECH HAS BEEN
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME JURISDICTIONS DO NOT ALLOW
* THE EXCLUSION OR LIMITATION OF LIABILITY FOR PERSONAL INJURY, OR OF
* INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY TO YOU.
*
*
*/
/**
* @example eventMonitor_example.c
* @section eventMonitor_example_description Description
*
* This source file is an example of how to use NTAPI to monitor events.
* The example program runs in an infinite loop polling the event stream
* for events. When an event is received, it is printed on the screen.
* It also shows how to translate read sensor data into a text representation.
*
* The example program uses the following NTAPI functions:
* - @ref NT_Init()
* - @ref NT_InfoOpen()
* - @ref NT_InfoRead()
* - @ref NT_InfoClose()
* - @ref NT_EventOpen()
* - @ref NT_EventRead()
* - @ref NT_EventClose()
* - @ref NT_Done()
* - @ref NT_ExplainError()
*
* @section eventMonitor_example_prerequisites Prerequisites
* - None.
*
* @section eventMonitor_example_flow Program flow
* @{
* The following is required to monitor events:
* - \#include/nt.h - Applications/Tools only need to include @ref
* nt.h to obtain prototypes, macros etc. from NTAPI.
* - @ref NT_Init(@ref NTAPI_VERSION) - Initialize the NTAPI
* library. @ref NTAPI_VERSION is a define that describes the version
* of the API described in the header files included by @ref
* nt.h. NT_Init() instructs the NTAPI library to convert returned data
* to the @ref NTAPI_VERSION, if possible. This ensures that
* applications can run on NTAPI libraries of newer versions.
* - @ref NT_EventOpen() - Open an event stream making it possible to receive events.
* When an event stream is opened, all events are stored on an event queue
* until they are read.
* - @ref NT_EventRead() - Wait for either a timeout or for an event to arrive,
* then return a single event from the event queue. If no event arrives
* within the timeout period, a NT_STATUS_TIMEOUT is returned.
* - @ref NT_EventClose() - Close an event stream.
* - @ref NT_InfoOpen() - Open an information stream.
* - @ref NT_InfoRead() - Read sensor information data in order to get the name
* and sensor limits.
* - @ref NT_InfoClose() - Close an information stream.
* - @ref NT_Done() - Close down the NTAPI library.
* - @ref NT_ExplainError() - Explain an error code returned by NTAPI functions.
*
*<hr>
* @section eventMonitor_example_code Code
* @}
*/
#include <stdio.h>
#include <signal.h>
#include "nt.h"
#if defined(WIN32) || defined(WIN64)
#include <winsock2.h> // SetConsoleCtrlHandler(), for Windows 2000 Pro/Server or later
#include <string.h> // strcpy_s()
#endif
#if defined(WIN32) || defined(WIN64)
#define snprintf(dst, ...) _snprintf_s((dst), _countof(dst), __VA_ARGS__)
#define strcpy(dst, src) strcpy_s((dst), _countof(dst), (src))
#define strncat(dst, src, sz) strcat_s(dst, sz, src)
#endif
//Error codes returned by the application
#define NT_APPL_SUCCESS 0
#define NT_APPL_ERROR_NTAPI 1
#define NT_APPL_ERROR_OS 2
//Flag to signal that application should stop.
//The function called when the user presses CTRL-C
#if defined(WIN32) || defined (WIN64)
#else
#endif
{
#ifdef WIN32
stopApplication = true;
return TRUE;
#else
if (sig == SIGINT)
stopApplication = true;
#endif
}
//Translate sensor type into a string representation.
{
switch (Type)
{
default:
assert(false);
return "Unhandled";
}
}
//Return the sensor multiplier
{
switch (Type)
{
default:
assert(false);
return 0.0;
}
}
//Get the sensor unit.
{
switch (Type)
{
default:
assert(false);
return "Unhandled";
}
}
/*
* Translate a sensor value into a string representation
*
* This function converts the sensor value integer into a text string taking
* care of multiplication with the sensor type multiplier and using a user supplied
* formatting string and unit prefix.
*
* Input: Str Where to put the result
* Input: Size The maximum number of characters that can
* be written to Str including the terminating
* zero.
* Input: Value The sensor value
* Input: Type The sensor type (what it measures)
* Input: FmtStr String containing a printf format string for
* a float value (%f) and a unit string (%s).
* If the string does not contain an "f" value
* is not printet, and if "s" is not specified,
* the unit is not printed.
* Input: Prefix Indicates if the unit is preceeded with a
* prefix which can be "", (no prefix), "m" (milli) or
* "u" (micro). By specifying a prefix, Value
* is changed accordingly.
* @retval Str
*/
size_t Size,
int Value,
NtSensorType_t Type,
const char* FmtStr,
char* Prefix)
{
char UnitStr[32];
float FloatVal;
float PrefixMultiplier;
{
return Str;
}
//Multiply with sensor multiplier in order to get a value that is in
//correspondence with the sensor unit
FloatVal = (float)Value * NT_GetSensorMultiplier(Type);
if (strcmp(Prefix, "m") == 0)
{
PrefixMultiplier = 1e3;
strcpy(UnitStr, "m");
}
else if (strcmp(Prefix, "u") == 0)
{
PrefixMultiplier = 1e6;
strcpy(UnitStr, "u");
}
else
{
PrefixMultiplier = 1;
strcpy(UnitStr, "");
}
strncat(UnitStr, NT_GetSensorUnit(Type), sizeof(UnitStr) - strlen(UnitStr) - 1);
FloatVal *= PrefixMultiplier;
//Test if value and/or unit should be printet
{
snprintf(Str, Size, FmtStr, FloatVal, UnitStr);
else
snprintf(Str, Size, FmtStr, UnitStr);
}
else
snprintf(Str, Size, FmtStr, FloatVal);
return Str;
}
//Translate a port event into a string representation.
{
switch (Event) {
default:
assert(false);
return "Unhandled";
}
}
//Read additional sensor data in order to get sensor name, limits etc
{
NtInfoStream_t hStream; // Info stream handle
// Open the info stream
if (status != NT_SUCCESS) {
fprintf(stderr, "\n>>> Error: Opening Info stream failed. Code 0x%x = %s\n", status, errBuf);
return status;
}
// Setup sensor data to read
pInfo->u.sensor.sourceIndex = pSensor->sourceIndex; // Source index - This is either port number or adapter number
pInfo->u.sensor.sensorIndex = pSensor->sensorIndex; // Sensor index - This is the number of the sensor to read
// Read sensor data
status = NT_InfoRead(hStream, pInfo);
if (status != NT_SUCCESS) {
NT_ExplainError(status, errBuf, sizeof(errBuf));
fprintf(stderr, "\n>>> Error: Reading Info failed. Code 0x%x = %s\n", status, errBuf);
NT_InfoClose(hStream);
return status;
}
// Close sensor stream
status = NT_InfoClose(hStream);
if (status != NT_SUCCESS) {
NT_ExplainError(status, errBuf, sizeof(errBuf));
fprintf(stderr, "\n>>> Error: Closing Info stream failed. Code 0x%x = %s\n", status, errBuf);
return status;
}
return NT_SUCCESS;
}
//Dump the sensor information
{
NtError_t status;
NtInfo_t info;
NtInfoSensor_t* pSensor;
NtSensorType_t SensorType;
char UnitStr[16];
char ValStr[16] = "";
char LimLowStr[16] = "";
char LimHghStr[16] = "";
char AlarmStr[16] = "";
char Prefix[4];
const char* pValFmt;
char LevelStr[16];
char OrigStr[32];
//Define the format for printing various sensor type values.
//Curr is not used - only as a place holder
// Unk Temp Volt Curr OptPwr FAN ElecPwr
const char* ValFmt[7] = {"%.1f", "%.1f", "%.2f", "%.2f", "%.0f", "%.0f", "%.1f"};
switch (pSensorEvent->source) {
strcpy(LevelStr, "0");
break;
strcpy(LevelStr, "1");
break;
default:
strcpy(LevelStr, "?");
}
switch (pSensorEvent->source) {
case NT_SENSOR_SOURCE_PORT:
strcpy(OrigStr, "Port");
break;
case NT_SENSOR_SOURCE_ADAPTER:
strcpy(OrigStr, "Adapter");
break;
default:
strcpy(OrigStr, "?");
}
if ((status = GetSensorData(pSensorEvent, &info)) != NT_SUCCESS)
return status;
SensorType = pSensor->type;
//Specify the prefix. Since we dont print any adapter currents it is ok to
//set the general current prefix to "m"
strcpy(Prefix, "m");
strcpy(Prefix, "u");
else
strcpy(Prefix, "");
//Get the unit string alone by not specifying any float translation in the
//output format using the arbitrary value 0
{
pValFmt = ValFmt[SensorType];
//Dump sensor data
//Do note:
//The sensor value is taken from the event. If it had been taken from the
//data acquired by GetSensorData an inconsistency might arise since the
//alarm from the event might not correspond to the value read a little later.
NT_TranslateSensorValue(LimLowStr, sizeof(ValStr), pSensor->limitLow , SensorType, pValFmt, Prefix);
NT_TranslateSensorValue(LimHghStr, sizeof(ValStr), pSensor->limitHigh , SensorType, pValFmt, Prefix);
}
// If the sensor is in an alert state, then print alert message.
strcpy(AlarmStr, "Alarm");
else
strcpy(AlarmStr, "No alarm");
printf("%s %d: Sensor(%s-%s, Id = %d, Level = %s), Value = %s%s, Range = [%s..%s]: %s\n",
OrigStr,
pSensor->sourceIndex,
pSensor->sensorIndex,
LevelStr,
ValStr,
UnitStr,
LimLowStr,
LimHghStr,
AlarmStr);
return NT_SUCCESS;
}
//Dump config change events. Sent if any changes are made to the configuration.
{
// Find the type of change event
printf("--------------------------------------\n");
printf("Auto Negotiation ............ %2d\n", pConfigEvent->u.portSettings_v2.data.autoNegotiation);
printf("Advertising Full Duplex Mask %2d\n", pConfigEvent->u.portSettings_v2.data.advertise.fullDuplexMask);
printf("Advertising Half Duplex Mask %2d\n", pConfigEvent->u.portSettings_v2.data.advertise.halfDuplexMask);
printf("--------------------------------------\n");
break;
printf("current OS time\n");
else
break;
printf("Timesync has changed for adapter %d - Action 0x%X - Reference time %16llu\n", pConfigEvent->u.timesyncWrite.adapter,
break;
printf("Limits for ");
{
case NT_SENSOR_SOURCE_PORT:
break;
case NT_SENSOR_SOURCE_ADAPTER:
break;
default:
}
break;
default:
printf("Unknown change event %u\n", pConfigEvent->parm);
break;
}
}
//Convert time reference to a string.
{
switch (ref) {
return "free running clock";
return "PTP";
//TODO
return "Int1";
return "Int2";
return "Ext1";
return "OS clock";
default:
return "<unknown>";
}
}
//Convert PTP port state to a string
{
switch (state) {
return "initializing";
return "faulty";
return "disabled";
return "listening";
return "pre master";
return "master";
return "passive";
return "uncalibrated";
return "slave";
return "inactive";
default:
return "<unknown>";
}
}
//Dump the events.
{
NtError_t status = NT_SUCCESS;
uint32_t i;
printf("********************************************************************************\n");
// Port event received. Check the action of port event.
break;
break;
break;
break;
printf("SDRAM usage: Filled %3d%% - Stream ID %3d - No. of streams %2d\n", (int)((pEventInfo->u.sdramFillLevelEvent.used*100)/pEventInfo->u.sdramFillLevelEvent.size),
printf("------------------------------------------------------------\n");
printf(" Hostbuffer: App %3d%% - Driver %3d%% - Adapter %3d%%\n\n", (int)((pEventInfo->u.sdramFillLevelEvent.hb.deQueued*100)/pEventInfo->u.sdramFillLevelEvent.hb.size),
(int)((pEventInfo->u.sdramFillLevelEvent.hb.enQueued*100)/pEventInfo->u.sdramFillLevelEvent.hb.size),
(int)((pEventInfo->u.sdramFillLevelEvent.hb.enQueuedAdapter*100)/pEventInfo->u.sdramFillLevelEvent.hb.size));
printf(" Stream %3d - Used %3d%% - Process ID %08llu\n", pEventInfo->u.sdramFillLevelEvent.aStreams[i].streamIndex,
(int)((pEventInfo->u.sdramFillLevelEvent.aStreams[i].enQueued*100)/pEventInfo->u.sdramFillLevelEvent.hb.size),
}
break;
{
printf("Adapter %d: PTP link is %s\n", adapter,
break;
}
{
printf("Adapter %d: Loses %s as time reference\n", adapter,
break;
printf("Adapter %d: Uses %s as time reference\n", adapter,
break;
printf("Adapter %d: Fails to choose %s as time reference\n", adapter,
break;
printf("Adapter %d: Is in sync with %s time reference\n", adapter,
break;
printf("Adapter %d: Is out of sync with %s time reference\n", adapter,
break;
/* "Old" state, ptpState[0] == NT_PTP_PORT_STATE_NA first time */
printf("Adapter %d: Changes PTP state to %s\n", adapter,
else
printf("Adapter %d: Changes PTP state from %s to %s\n", adapter,
break;
printf("Adapter %d: Time stamp clock is set explicitly to %llu\n",
adapter,
break;
printf("Adapter %d: External device lost synchronization signal", adapter);
break;
printf("Adapter %d: External device is out of synchronization", adapter);
break;
printf("Adapter %d: External device lost time of day information", adapter);
break;
}
break;
}
default:
printf("Unknown event 0x%X\n", pEventInfo->type);
break;
}
return status;
}
//Main function.
{
NtEventStream_t hEvent; // Event stream handle
NtEvent_t eventInfo; // Event data container
NtError_t status = NT_SUCCESS;
// Set up ctrl+c handler
#if defined(WIN32) || defined (WIN64)
SetConsoleCtrlHandler((PHANDLER_ROUTINE)StopApplication, TRUE);
#else
struct sigaction newaction; // Ctrl+c handle
memset(&newaction, 0, sizeof(newaction));
newaction.sa_handler = StopApplication;
if (sigaction(SIGINT, &newaction, NULL) < 0) {
fprintf(stderr, "Failed to register sigaction.\n");
}
#endif
printf("\nNapatech Event Monitor Example\n");
printf("------------------------------\n");
// Initialize NTAPI library
NT_ExplainError(status, errBuf, sizeof(errBuf));
fprintf(stderr, ">>> Error: NT_Init failed. Code 0x%X = %s\n", status, errBuf);
}
// Open the event stream
NT_ExplainError(status, errBuf, sizeof(errBuf));
fprintf(stderr, ">>> Error: Open event stream failed. Code %08X = %s\n", status, errBuf);
NT_Done();
return NT_APPL_ERROR_NTAPI;
}
// Run in an infinite loop in order to get all events
while (!stopApplication) {
// Read an event from the event queue. Wait one second if there are no events.
status = NT_EventRead(hEvent, &eventInfo, 1000);
if (status == NT_STATUS_TIMEOUT)
continue; // Timeout due to no events. Try again.
if (status != NT_SUCCESS) {
NT_ExplainError(status, errBuf, sizeof(errBuf));
fprintf(stderr, ">>> Error: Reading event failed. Code 0x%X = %s\n", status, errBuf);
break;
}
// Print the received event on the screen
if ((status = DumpEvent(&eventInfo)) != NT_SUCCESS)
break;
}
// Close the event stream keeping the first error if any
if (status == NT_SUCCESS) {
NT_ExplainError(status, errBuf, sizeof(errBuf));
fprintf(stderr, ">>> Error: Closing event stream failed. Code 0x%X = %s\n", status, errBuf);
}
}
else
NT_EventClose(hEvent);
NT_Done(); // Close down the NTAPI library
if (status != NT_SUCCESS)
return NT_APPL_ERROR_NTAPI;
}