Integrator Resources

The official home for NAI Support

Not sure where to start? Try Quick Start Guide or ask a question below!

Toggle Components with Visual Button
JavaScript Form Processing

RTD EtherInterrupts OpenDetect

RTD EtherInterrupts OpenDetect Sample Application (SSK 1.x)

Overview

The RTD EtherInterrupts OpenDetect sample application demonstrates how to monitor RTD (Resistance Temperature Detector) open-circuit conditions — sensor wire breaks — using Ethernet Interrupt Driven Response (IDR) delivery with the NAI Software Support Kit (SSK 1.x). When an RTD sensor becomes disconnected or its wiring breaks, the module detects the open circuit and generates an interrupt. Instead of servicing the interrupt locally through an ISR, the board’s IDR engine packages the open latched status register into a UDP packet and sends it to a remote host. Your application listens on a UDP socket and decodes the incoming status to determine which channel experienced the open fault.

This is a specialized Ethernet IDR variant focused exclusively on RTD open-detect (sensor wire break) monitoring. It operates as a self-contained single-file application with its own socket server and IDR command construction, unlike the multi-file shared-utility approach used by the general RTD Ethernet IDR sample. For the general-purpose RTD Ethernet IDR sample that monitors all RTD status types through a shared utility framework, see the RTD Interrupt Ethernet guide. For basic RTD configuration including open-detect threshold setup without interrupt delivery, see the RTD BasicOps guide.

This sample supports the following RTD module types: G4, RT1, TR1, and TC1.

The sample configures the RTD channel for 3-wire mode with 100-ohm zero-temperature resistance, sets the BIT open interval, then monitors the NAI_RTD_STATUS_OPEN_LATCHED status type. The IDR contains two embedded commands: a read command to retrieve the open latched status register, and a write command to clear it for automatic re-arming.

Why Ethernet IDR for Open Detection?

Open-circuit detection is a critical safety monitor in RTD-based temperature measurement systems. A broken sensor wire means the temperature reading is invalid, and your system needs to know immediately. Ethernet IDR provides several advantages for this use case:

  • Remote health monitoring — your application can monitor sensor integrity from any host reachable over the network, without requiring a physical bus connection to the board.

  • Self-contained notification — each IDR packet contains the open status register value and automatically clears it, so your application receives the fault information and re-arms the interrupt in a single network transaction.

  • No ISR infrastructure — there is no ISR to install, no IRQ to manage, and no thread synchronization to worry about. Your application simply listens on a UDP socket.

  • Simplified deployment — the entire application is a single source file with no dependencies on the shared interrupt utility libraries used by other RTD interrupt samples.

The tradeoff is that Ethernet IDR requires a Gen4 or later board, and network latency is inherently higher than a local ISR. For latency-critical applications where the host is directly attached to the board, standard ISR delivery may be more appropriate.

Prerequisites

Before running this sample, make sure you have:

  • An NAI board with an RTD module installed (G4, RT1, TR1, or TC1).

  • A Gen4 or later board with Ethernet connectivity. Earlier board generations do not support the Ethernet IDR protocol.

  • SSK 1.x installed on your development host.

  • The sample applications built. Refer to the SSK 1.x build instructions for your platform if you have not already compiled them.

  • Network connectivity between the host running this sample and the board. The host must be reachable at the IP address configured in the IDR settings (default: 192.168.1.25).

How to Run

Launch the RTD_EtherInterrupts_OpenDetect executable from your build output directory. On startup the application looks for a configuration file (default_RtdEthInterruptsOpenDetect.txt). On the first run, this file will not exist — the application will present an interactive board menu where you configure a board connection, card index, and module slot. You can save this configuration so that subsequent runs skip the menu and connect automatically. Once connected, the application prompts you for channel selection and IDR network settings, then begins listening for IDR packets from the board.

Board Connection and Module Selection

Note
This startup sequence is common to all NAI sample applications. The board connection and module selection code shown here is not specific to RTD. For details on board connection configuration, see the First Time Setup Guide.

The main() function follows a standard SSK 1.x startup flow:

  1. Call naiapp_RunBoardMenu() to load a saved configuration file (if one exists) or present the interactive board menu. The configuration file (default_RtdEthInterruptsOpenDetect.txt) is not included with the SSK — it is created when the user saves their connection settings from the board menu. On the first run, the menu will always appear.

  2. Query the user for card index and module number.

  3. Retrieve the module ID with naibrd_GetModuleID() to verify an RTD module is installed at the selected slot.

  4. Call Run_RTD_EthInterrupt_OpenDetect() to execute the IDR open-detect workflow.

#if defined (__VXWORKS__)
int32_t RTD_EtherInterrupts_OpenDetect(void)
#else
int32_t main(void)
#endif
{
   bool_t bQuit = FALSE;
   int32_t cardIndex = -1;
   int32_t module = 0;
   uint32_t modId = 0u;
   int32_t moduleCount;
   int8_t inputBuffer[80];
   int32_t inputResponseCnt;

   if (naiapp_RunBoardMenu(CONFIG_FILE) == TRUE)
   {
      while (!bQuit)
      {
         naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
         naibrd_GetModuleCount(cardIndex, &moduleCount);
         naiapp_query_ModuleNumber(moduleCount, 1, &module);
         modId = naibrd_GetModuleID(cardIndex, module);

         bQuit = Run_RTD_EthInterrupt_OpenDetect(cardIndex, module, modId);
         printf("Type %c to exit program : ", NAI_QUIT_CHAR);
         bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR,
                                              inputBuffer, &inputResponseCnt);
      }
   }

   naiapp_access_CloseAllOpenCards();
   return 0;
}

Unlike the multi-file RTD Ethernet interrupt samples, this application does not call shared initialization functions like initializeRTDConfigurations() or initializeIDRConfigurations(). All configuration is handled directly within Run_RTD_EthInterrupt_OpenDetect() using local variables and compile-time defaults.

Important

Common connection errors you may encounter at this stage:

  • No board found — verify that the board is powered on and physically connected. Check that the configuration file lists the correct interface and address.

  • Connection timeout — confirm network settings (for Ethernet connections) or bus configuration (for PCI/PCIe). Firewalls and IP mismatches are frequent causes.

  • Invalid card or module index — indices are zero-based for cards and one-based for modules. Ensure the values you pass match your hardware setup.

  • Module not present at selected slot — the slot you selected does not contain an RTD module. Use the board menu to verify which slots are populated.

Program Structure

The RTD EtherInterrupts OpenDetect sample is implemented entirely in a single source file: RTD_EtherInterrupts_OpenDetect.c. This self-contained design includes the board connection logic, RTD channel configuration, IDR command construction, socket server, and UDP message parsing — all without relying on the shared interrupt utility libraries used by other RTD samples.

Compile-Time Defaults

The sample defines its IDR configuration as compile-time constants:

#define DEF_RTD_IDR_ID                          2
#define DEF_RTD_ETHER_CMD_COUNT                 2
#define DEF_RTD_RESPONSE_PROTOCOL               ETHER_GEN4_UDP_PROTOCOL
#define DEF_RTD_RESPONSE_IP_LEN                 ETHER_GEN4_IPv4_ADDR_LEN
#define DEF_RTD_OPEN_INT_TYPE                   NAI_RTD_STATUS_OPEN_LATCHED
#define DEF_RTD_OPEN_INT_VECTOR                 0x0A

static uint16_t DEF_RTD_RESPONSE_PORT = 52802;
static uint8_t  DEF_RTD_RESPONSE_IP_ADDR[] = { 192,168,1,25 };
  • DEF_RTD_IDR_ID — the IDR ID used to register this interrupt handler with the board’s IDR engine. Must be unique across all active IDR configurations.

  • DEF_RTD_ETHER_CMD_COUNT — the number of embedded Ethernet commands in the IDR (one read + one write = 2).

  • DEF_RTD_OPEN_INT_TYPE — the specific RTD status type being monitored: NAI_RTD_STATUS_OPEN_LATCHED (open-circuit detection).

  • DEF_RTD_OPEN_INT_VECTOR — the interrupt vector that links the module interrupt to the IDR engine. Must match between naibrd_RTD_SetInterruptVector() and naibrd_Ether_SetIDRConfig().

  • DEF_RTD_RESPONSE_PORT and DEF_RTD_RESPONSE_IP_ADDR — the UDP destination where the board sends IDR packets. The user can override these interactively at runtime.

Gen4 Ethernet Requirement Check

The first thing Run_RTD_EthInterrupt_OpenDetect() does is verify that the connected board supports Gen4 Ethernet commands. This is a hard requirement — earlier board generations do not have the IDR engine.

bGen4IDRCommands = SupportsGen4Ether(cardIndex);

SupportsGen4Ether() is a shared utility function that queries the board’s Ethernet generation capability. If the board does not support Gen4, the application prints an error and exits. There is no fallback to an older protocol — you must use a Gen4 or later board for Ethernet IDR.

In your own application, always call SupportsGen4Ether() before attempting any IDR configuration. Calling naibrd_Ether_SetIDRConfig() on a pre-Gen4 board will fail.

Important

Common Errors

  • "Ethernet Interrupt Support Prior to Generation 4 Ethernet commands currently not supported" — the connected board does not support IDR. Verify you are using a Gen4 or later board. Check the board’s firmware version if you believe the hardware should support Gen4.

RTD Channel Configuration

After the Gen4 check passes and the user selects a channel and confirms network settings, the sample configures the RTD channel for open-detect monitoring:

/* Configure the RTD channel */
check_status(naibrd_RTD_SetWireMode(cardIndex, module, channel, NAI_RTD_GEN5_WIRE_MODE_3));
check_status(naibrd_RTD_SetZeroTempResistance(cardIndex, module, channel,
             NAI_RTD_ZERO_TEMP_RESISTANCE_100));
check_status(naibrd_RTD_SetBitOpenInterval(cardIndex, module, 100));

The configuration steps are:

  1. Wire mode — naibrd_RTD_SetWireMode() sets the channel to 3-wire mode (NAI_RTD_GEN5_WIRE_MODE_3). The wire mode must match your physical sensor wiring for accurate open-detect behavior. Consult your module’s manual for supported wire modes.

  2. Zero-temperature resistance — naibrd_RTD_SetZeroTempResistance() sets the reference resistance to 100 ohms (NAI_RTD_ZERO_TEMP_RESISTANCE_100), which is the standard for PT100 sensors.

  3. BIT open interval — naibrd_RTD_SetBitOpenInterval() sets the interval (in milliseconds) between open-circuit detection checks. A value of 100 means the module checks for open circuits every 100 ms.

These calls configure how the module detects the open condition. Once the interrupt system is enabled, an open circuit detected during the BIT open interval check will trigger the IDR.

Important

Common Errors

  • Open detection not triggering — verify the wire mode matches your physical wiring. A wire mode mismatch can prevent the module from correctly identifying an open circuit.

  • False open detections — the BIT open interval is too short for noisy environments. Increase the interval value or check your sensor wiring for intermittent connections.

IDR Configuration

The IDR (Interrupt Driven Response) is the core mechanism that makes Ethernet-based interrupt delivery work. When an open-circuit interrupt fires, the board’s IDR engine executes a preconfigured sequence of register read/write commands, packages the results into a UDP packet, and sends it to the specified IP address and port.

Building IDR Commands

InitRtdOpenDetectIDRCommands() constructs the two embedded Ethernet commands that the board executes when the open-detect interrupt fires:

static nai_status_t InitRtdOpenDetectIDRCommands(int32_t cardIndex, int32_t module,
   int32_t channel, uint8_t commands[], uint16_t *cmdcount, uint16_t *cmdlen)
{
   nai_status_t status = NAI_SUCCESS;
   uint16_t msgIndex = 0;
   uint16_t idrcmdcnt = 0;
   uint16_t ethcmdlen = 0;
   uint32_t moduleOffset;

   status = check_status(naibrd_GetModuleOffset(cardIndex, module, &moduleOffset));
   if (status == NAI_SUCCESS)
   {
      if (bGen4IDRCommands)
      {
         MakeRtdOpenDetectReadRegsCommand(bGen4IDRCommands, msgIndex, moduleOffset,
                                          commands, &ethcmdlen);
         msgIndex += ethcmdlen;
         idrcmdcnt++;

         MakeRtdOpenDetectWriteRegsCommand(bGen4IDRCommands, msgIndex, moduleOffset,
                                           channel, commands, &ethcmdlen);
         msgIndex += ethcmdlen;
         idrcmdcnt++;

         *cmdcount = idrcmdcnt;
         *cmdlen = msgIndex;
      }
   }
   return status;
}

The function retrieves the module’s register offset with naibrd_GetModuleOffset(), then constructs two commands in sequence:

  1. Read command — reads the NAI_RTD_GEN5_REG_OPEN_STATUS_LATCHED_ADD register to capture which channels have an open-circuit condition.

  2. Write command — writes a channel-specific bitmask to the same register to clear the latched status and re-arm the interrupt.

Read Command Construction

MakeRtdOpenDetectReadRegsCommand() builds a Gen4 Ethernet read command targeting the open latched status register:

seqno = 0;
regaddr = moduleOffset + NAI_RTD_GEN5_REG_OPEN_STATUS_LATCHED_ADD;
count = 1;
stride = 4;
msgIndex = (uint16_t)nai_ether_MakeReadMessage(&commands[startIndex], seqno,
   NAI_ETHER_GEN4, NAI_INTF_ONBOARD, regaddr, stride, count, NAI_REG32);

nai_ether_MakeReadMessage() constructs a complete Gen4 Ethernet read command in the command buffer. The register address is the module offset plus the open latched status register offset. A single 32-bit register is read (count = 1, stride = 4).

Write Command Construction

MakeRtdOpenDetectWriteRegsCommand() builds a Gen4 Ethernet write command that clears the latched status for the monitored channel:

uint32_t data = (0x000000001 << (channel - 1)); /* Clear interrupt bits */

seqno = 0;
regaddr = moduleOffset + NAI_RTD_GEN5_REG_OPEN_STATUS_LATCHED_ADD;
count = 1;
stride = 4;

msgIndex = (uint16_t)nai_ether_BeginWriteMessage(&commands[startIndex], seqno,
   NAI_ETHER_GEN4, NAI_INTF_ONBOARD, regaddr, stride, count, NAI_REG32);
msgIndex = (uint16_t)nai_ether_WriteMessageData(&commands[startIndex], msgIndex,
   NAI_REG32, &data, NAI_REG32, count);
msgIndex = (uint16_t)nai_ether_FinishMessage(&commands[startIndex], msgIndex, NAI_ETHER_GEN4);

The write command uses the three-step message construction pattern: nai_ether_BeginWriteMessage() writes the header, nai_ether_WriteMessageData() appends the data payload (a bitmask with the selected channel’s bit set), and nai_ether_FinishMessage() finalizes the message. Including this write in the IDR means the board clears the channel’s open latched status immediately after sending the notification, so the interrupt re-arms without a separate API call from the host.

Installing the IDR Configuration

After the commands are built, the sample clears any previous IDR configuration and installs the new one:

status = check_status(naibrd_Ether_ClearIDRConfig(cardIndex, (uint16_t)DEF_RTD_IDR_ID));

InitRtdOpenDetectIDRCommands(cardIndex, module, channel, commands, &cmdcount, &cmdlength);
for (i = 0; i < DEF_RTD_RESPONSE_IP_LEN; i++)
   ip[i] = DEF_RTD_RESPONSE_IP_ADDR[i];
port = DEF_RTD_RESPONSE_PORT;
status = check_status(naibrd_Ether_SetIDRConfig(cardIndex, (uint16_t)DEF_RTD_IDR_ID,
   protocol, iplen, ip, port, DEF_RTD_OPEN_INT_VECTOR, cmdcount, cmdlength, commands));

The key API calls are:

  • naibrd_Ether_ClearIDRConfig() — clears any existing IDR configuration for the specified IDR ID. Always call this before reconfiguring an IDR to avoid stale command data.

  • naibrd_Ether_SetIDRConfig() — installs the complete IDR configuration on the board: protocol, IP length, destination IP address, port, interrupt vector to match, command count, command byte length, and the command buffer. The vector DEF_RTD_OPEN_INT_VECTOR (0x0A) must match the vector assigned to the RTD module’s open interrupt.

Important

Common Errors

  • naibrd_Ether_SetIDRConfig() fails — verify the IDR ID is valid and that no other IDR is already configured with the same ID. Always call naibrd_Ether_ClearIDRConfig() first.

  • Incorrect register address — the module offset must be queried at runtime with naibrd_GetModuleOffset(). Hardcoded offsets will fail on different board configurations.

  • Wrong board interface — this sample uses NAI_INTF_ONBOARD for all IDR commands. Using NAI_INTF_PCI would target the wrong bus.

Interrupt Configuration

After the IDR is configured, the sample sets up the RTD module to generate interrupts on open-circuit detection and starts the IDR engine:

/* Configure to generate interrupts on OPEN DETECT */
check_status(naibrd_RTD_SetInterruptEdgeLevel(cardIndex, module, channel,
   DEF_RTD_OPEN_INT_TYPE, NAI_RTD_EDGE_INTERRUPT));
check_status(naibrd_RTD_SetInterruptVector(cardIndex, module,
   DEF_RTD_OPEN_INT_TYPE, DEF_RTD_OPEN_INT_VECTOR));
check_status(naibrd_RTD_SetInterruptSteering(cardIndex, module,
   DEF_RTD_OPEN_INT_TYPE, NAIBRD_INT_STEERING_ON_BOARD_1));
check_status(naibrd_RTD_SetInterruptEnable(cardIndex, module, channel,
   DEF_RTD_OPEN_INT_TYPE, 1));

/* Clear NAI_RTD_STATUS_OPEN_LATCHED channel status */
check_status(naibrd_RTD_ClearStatus(cardIndex, module, channel, DEF_RTD_OPEN_INT_TYPE));

/* Start the IDR */
status = check_status(naibrd_Ether_StartIDR(cardIndex, (uint16_t)DEF_RTD_IDR_ID));

The steps are:

  1. Set trigger mode — naibrd_RTD_SetInterruptEdgeLevel() configures edge triggering (NAI_RTD_EDGE_INTERRUPT) for the selected channel on the NAI_RTD_STATUS_OPEN_LATCHED status type. Edge triggering fires once when the open condition transitions from not-detected to detected.

  2. Set vector — naibrd_RTD_SetInterruptVector() assigns vector 0x0A to the open latched status type. This vector must match the one specified in naibrd_Ether_SetIDRConfig() so the IDR engine recognizes the interrupt.

  3. Set steering — naibrd_RTD_SetInterruptSteering() routes the interrupt to NAIBRD_INT_STEERING_ON_BOARD_1, directing it to the board’s onboard processor where the IDR engine runs.

  4. Enable interrupt — naibrd_RTD_SetInterruptEnable() enables the open-detect interrupt for the selected channel.

  5. Clear status — naibrd_RTD_ClearStatus() clears any pre-existing open latched status to prevent a stale condition from triggering an immediate interrupt.

  6. Start the IDR — naibrd_Ether_StartIDR() activates the IDR engine for the configured IDR ID. From this point forward, any open-circuit detection will trigger a UDP packet to the configured host.

Important

Common Errors

  • No interrupts received — verify that naibrd_RTD_SetInterruptSteering() used NAIBRD_INT_STEERING_ON_BOARD_1. Using the wrong steering routes the interrupt away from the IDR engine.

  • Interrupt vector mismatch — the vector passed to naibrd_RTD_SetInterruptVector() must match the vector in naibrd_Ether_SetIDRConfig(). A mismatch means the IDR engine will not recognize the interrupt.

  • Stale interrupts fire immediately — always clear status with naibrd_RTD_ClearStatus() before or immediately after enabling interrupts.

UDP Server and IDR Message Handling

Once the IDR is started and interrupts are enabled, the sample spawns a listener thread that runs a UDP server to receive incoming IDR packets from the board:

terminateRtdOpenDetectThread = FALSE;

#if defined (__VXWORKS__)
taskSpawn("uprRtdOpenDetectHandler", 100, 0, 10000,
          (FUNCPTR)UPR_RtdOpenDetectEthInt_Handler, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0);
#elif defined (LINUX)
pthread_create(&interruptRtdOpenDetectThread, NULL,
               (void*)UPR_RtdOpenDetectEthInt_Handler, NULL);
#else /* Default Windows */
CreateThread(NULL, 0, UPR_RtdOpenDetectEthInt_Handler, NULL, 0, NULL);
#endif

The thread handler (UPR_RtdOpenDetectEthInt_Handler) creates either a TCP or UDP server depending on DEF_RTD_RESPONSE_PROTOCOL (default is UDP). The UDP server binds to DEF_RTD_RESPONSE_PORT (52802) and enters a receive loop, calling DecodeUPRRtdOpenDetectIDRMessages() for each incoming packet.

Decoding IDR Packets

DecodeUPRRtdOpenDetectIDRMessages() iterates through the received byte stream, extracting individual Ethernet messages delimited by Gen4 preamble and postamble markers:

static void DecodeUPRRtdOpenDetectIDRMessages(const uint8_t msg[], int32_t msgsize)
{
   uint32_t arraysize = MAX_ETHER_IDR_CMD_CNT * MAX_ETHER_BLOCK_REG_CNT;
   uint16_t responselen;
   uint8_t response[MAX_ETHER_BLOCK_REG_CNT];
   int32_t startIndex;

   startIndex = 0;
   while (startIndex < msgsize)
   {
      if (ParseRtdOpenDetectIDRResponse(startIndex, arraysize, (uint8_t *)msg,
                                        &responselen, MAX_ETHER_BLOCK_REG_CNT, response))
      {
         startIndex += responselen;
         DisplayDecodedRtdOpenDetectIDRResponse(responselen, response);
      }
   }
}

Each IDR packet may contain multiple embedded command responses (one per command configured in the IDR). ParseRtdOpenDetectIDRResponse() scans for ETHER_GEN4_PREAMBLE to locate the start of each message, then copies bytes until it finds ETHER_GEN4_POSTAMBLE.

Displaying Decoded Results

DisplayDecodedRtdOpenDetectIDRResponse() decodes the message header and displays the results:

offset = nai_ether_DecodeMessageHeader(response, responselen, &seq, &tc, gen, &size);

/* Parse IDR Response Sequence Value */
seqrsptype = seq & ETHER_GEN4_SEQ_UPR_MASK;
seqrspidindex = (seq & (ETHER_GEN4_SEQ_ID_MASK)) >> ETHER_GEN4_SEQ_ID_SHIFT;
seqcmdindex = (seq & (ETHER_GEN4_SEQ_CMD_MASK)) >> ETHER_GEN4_SEQ_CMD_SHIFT;

The sequence number encodes three pieces of information:

  • Response type (bits 15-14) — identifies whether this is an unprompted TDR or IDR response.

  • IDR ID (bits 13-10) — the IDR ID index (value is ID minus 1).

  • Command index (bits 9-6) — which command in the IDR configuration produced this response.

For read command responses (NAI_ETHER_TYPECODE_RSP_COMMAND_COMPLETE_READ_4), the handler extracts 32-bit data values from the payload. The open latched status register is a bitmask where each bit corresponds to a channel (bit 0 = channel 1, bit 1 = channel 2, etc.). A set bit indicates that channel detected an open circuit.

For write command responses (NAI_ETHER_TYPECODE_RSP_COMMAND_COMPLETE_WRITE_4), the handler simply confirms the write completed.

Important

Common Errors

  • No IDR packets received — verify the destination IP address matches the host running the application. Check that no firewall is blocking UDP port 52802. Confirm the board has network connectivity.

  • IDR packets arrive but show status 0x0 — no open-circuit condition exists on the monitored channel. Open detection requires the RTD sensor to be physically disconnected or its wiring to be broken.

  • Packets received but parsing fails — verify the protocol matches between the IDR configuration and the server. The sample defaults to UDP; using TCP requires both sides to agree.

Cleanup

When the user presses the quit key, the sample terminates the listener thread and stops the IDR engine:

terminateRtdOpenDetectThread = TRUE;

/* Stop the IDR */
status = check_status(naibrd_Ether_StopIDR(cardIndex, (uint16_t)DEF_RTD_IDR_ID));

The cleanup sets the terminateRtdOpenDetectThread flag, which causes the UDP receive loop to exit on the next iteration. Then naibrd_Ether_StopIDR() halts the IDR engine on the board so it no longer sends UDP notifications for this IDR ID.

Note that this sample does not explicitly call naibrd_Ether_ClearIDRConfig() during cleanup — the IDR configuration remains on the board until cleared. In a production application, you should also call naibrd_Ether_ClearIDRConfig() after stopping the IDR to fully release the board resources.

Troubleshooting Reference

Note
This section summarizes errors covered in the preceding sections and provides additional diagnostics. Consult your module’s manual for hardware-specific diagnostics.
Error / Symptom Possible Causes Suggested Resolution

"Ethernet Interrupt Support Prior to Generation 4 Ethernet commands currently not supported"

Board does not support Gen4 Ethernet protocol.

Use a Gen4 or later board. Check firmware version with your NAI support contact.

No IDR packets received

Destination IP address does not match the host. Firewall blocking UDP port. Board has no network connectivity. IDR not started.

Verify IP address matches the host running the application. Open UDP port 52802 in the firewall. Ping the board to confirm connectivity. Confirm naibrd_Ether_StartIDR() was called after naibrd_Ether_SetIDRConfig().

IDR packets arrive but show status 0x0

Interrupt already cleared. No open-circuit condition present on the channel.

Verify an RTD sensor is physically disconnected or broken. Open detection requires an actual hardware fault.

Open detection not triggering despite disconnected sensor

Wire mode mismatch. BIT open interval too long. Interrupt not enabled.

Verify naibrd_RTD_SetWireMode() matches physical wiring. Check naibrd_RTD_SetBitOpenInterval() value. Confirm naibrd_RTD_SetInterruptEnable() was called.

Interrupts fire continuously without stopping

Open-circuit condition persists on the hardware. Edge-triggered interrupt re-arms via the IDR write command, but the fault immediately re-triggers.

Disable interrupts for the faulted channel with naibrd_RTD_SetInterruptEnable(). Reconnect the RTD sensor before re-enabling.

naibrd_Ether_SetIDRConfig() returns error

Stale IDR configuration not cleared. Invalid IDR ID. Board does not support IDR.

Call naibrd_Ether_ClearIDRConfig() before naibrd_Ether_SetIDRConfig(). Verify the IDR ID is within the valid range for your board.

Socket bind fails on the host

Another application is already using UDP port 52802. Insufficient permissions.

Close any other application using the same port. Run with appropriate permissions. Change DEF_RTD_RESPONSE_PORT to an unused port.

No board found / Connection timeout

Board not powered or connected. Incorrect interface or address in configuration file.

Verify physical connection. Delete the configuration file and re-run to enter settings interactively.

Invalid card or module index

Zero-based card index or one-based module index mismatch.

Cards are zero-indexed, modules are one-indexed. Verify values match your hardware setup.

Module not present at selected slot

RTD module not installed in the selected slot.

Use the board menu to verify which slots are populated with G4, RT1, TR1, or TC1 modules.

Full Source

Full Source — RTD_EtherInterrupts_OpenDetect.c (SSK 1.x)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

/* Common Sample Program include files */
#include "include/naiapp_boardaccess_menu.h"
#include "include/naiapp_boardaccess_query.h"
#include "include/naiapp_boardaccess_access.h"
#include "include/naiapp_boardaccess_display.h"
#include "include/naiapp_boardaccess_utils.h"

/* naibrd include files */
#include "nai.h"
#include "naibrd.h"
#include "functions/naibrd_rtd.h"
#include "naibrd_ether.h"
#include "advanced/nai_ether_adv.h"
#include "maps/nai_map_rtd.h"

#if defined (WIN32)
#include <winsock2.h>
#include <ws2tcpip.h>
/* Need to link with Ws2_32.lib */
#pragma comment (lib, "Ws2_32.lib")
/* The following eliminates the FD_SET warning C4127 associated with the do...while(0) construct in the Microsoft winsock2.h file */
#pragma warning (disable:4127)
#elif (LINUX)
#include <sys/errno.h>
typedef int32_t SOCKET;
#define ZeroMemory(S, N)         memset((S), 0, (N))
#define closesocket(SD)          close(SD)
#define INVALID_SOCKET -1
#define SOCKET_ERROR   -1
#define SD_RECEIVE        0
#define SD_SEND           1
#define SD_BOTH           2
#elif (__VXWORKS__)
#include <netinet/ip.h>
#define ZeroMemory(S, N)         memset((S), 0, (N))
#define closesocket(SD)          close(SD)
#define INVALID_SOCKET -1
#define SOCKET_ERROR   -1
#define SD_RECEIVE        0
#define SD_SEND           1
#define SD_BOTH           2
#endif

static const int8_t *CONFIG_FILE = "default_RtdEthInterruptsOpenDetect.txt";

/* Function prototypes */
static bool_t Run_RTD_EthInterrupt_OpenDetect(int32_t cardIndex, int32_t module, uint32_t modid);
static bool_t QueryEthInterruptRxIPAddr(void);
static nai_status_t InitRtdOpenDetectIDRCommands(int32_t cardIndex, int32_t module, int32_t channel, uint8_t commands[], uint16_t *cmdcount, uint16_t *cmdlen);
static void MakeRtdOpenDetectReadRegsCommand(bool_t bGen4Ether, uint16_t startIndex, uint32_t moduleOffset, uint8_t commands[], uint16_t *cmdlen);
static void MakeRtdOpenDetectWriteRegsCommand(bool_t bGen4Ether, uint16_t startIndex, uint32_t moduleOffset, int32_t channel, uint8_t commands[], uint16_t *cmdlen);
static void CreateRtdOpenDetectIDRTCPServer(void);
static void CreateRtdOpenDetectIDRUDPServer(void);
static void DecodeUPRRtdOpenDetectIDRMessages(const uint8_t msg[], int32_t msgsize);
static bool_t ParseRtdOpenDetectIDRResponse(int32_t startIndex, int32_t rsparraysize, uint8_t response[], uint16_t *idrrsplen, int32_t idrrsparraysize, uint8_t idr_response[]);
static void DisplayDecodedRtdOpenDetectIDRResponse(uint16_t responselen, uint8_t response[]);

static bool_t bGen4IDRCommands = FALSE;
/* Default Ethernet Command Count specified for the IDR Commands.
   Change this to match the Command Count specified in InitRtdOpenDetectIDRCommands() routine
*/
#define DEF_RTD_IDR_ID                          2
#define DEF_RTD_ETHER_CMD_COUNT                 2   /* Number of Ethernet Commands in IDR Configuration */
#define DEF_RTD_RESPONSE_PROTOCOL               ETHER_GEN4_UDP_PROTOCOL
#define DEF_RTD_RESPONSE_IP_LEN                 ETHER_GEN4_IPv4_ADDR_LEN
#define DEF_RTD_OPEN_INT_TYPE                   NAI_RTD_STATUS_OPEN_LATCHED
#define DEF_RTD_OPEN_INT_VECTOR                 0x0A

static uint16_t DEF_RTD_RESPONSE_PORT = 52802;
static uint8_t  DEF_RTD_RESPONSE_IP_ADDR[] = { 192,168,1,25 };

#if defined (__VXWORKS__)
int UPR_RtdOpenDetectEthInt_Handler(int Param);
#elif LINUX
pthread_t interruptRtdOpenDetectThread;
static void* UPR_RtdOpenDetectEthInt_Handler(void* lpParam);
#else /* Default Windows */
DWORD WINAPI UPR_RtdOpenDetectEthInt_Handler(LPVOID lpParam);
#endif
static int terminateRtdOpenDetectThread;

/*****************************************************************************/
/**
<summary>
The purpose of the RTD_EtherInterrupts_OpenDetect is to demonstrate the methods to call in the
naibrd library to perform Ethernet interrupt operations with the RTD modules.

The following system configuration routines from the nai_sys_cfg.c file are
called to assist with the configuration setup for this program prior to
calling the naibrd AD routines.
 - ConfigDevice
 - DisplayDeviceCfg
 - GetBoardSNModCfg
 - CheckModule
</summary>
*/
/*****************************************************************************/
#if defined (__VXWORKS__)
int32_t RTD_EtherInterrupts_OpenDetect(void)
#else
int32_t main(void)
#endif
{
   bool_t bQuit = FALSE;
   int32_t cardIndex = -1;
   int32_t module = 0;
   uint32_t modId = 0u;
   int32_t moduleCount;
   int8_t inputBuffer[80];
   int32_t inputResponseCnt;

   if (naiapp_RunBoardMenu(CONFIG_FILE) == TRUE)
   {

      while (!bQuit)
      {
         naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
         naibrd_GetModuleCount(cardIndex, &moduleCount);
         naiapp_query_ModuleNumber(moduleCount, 1, &module);
         modId = naibrd_GetModuleID(cardIndex, module);

         bQuit = Run_RTD_EthInterrupt_OpenDetect(cardIndex, module, modId);
         printf("Type %c to exit program : ", NAI_QUIT_CHAR);
         bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
      }
   }

   naiapp_access_CloseAllOpenCards();

   return 0;
}

/**************************************************************************************************************/
/**
<summary>
This function runs handles configuring a RTD channel to receive NAI_RTD_STATUS_OPEN_LATCHED Ethernet
Interrupt commands. This routine will create a thread to wait to receive the unprompted interrupt ethernet
messages.
</summary>
*/
/**************************************************************************************************************/
static bool_t Run_RTD_EthInterrupt_OpenDetect(int32_t cardIndex, int32_t module, uint32_t modid)
{
   bool_t bQuit = FALSE;
   nai_status_t status;
   int32_t MAX_CHANNELS = naibrd_RTD_GetChannelCount(modid);
   int32_t channel;
   uint8_t commands[MAX_ETHER_IDR_CMD_CNT*MAX_ETHER_BLOCK_REG_CNT];
   uint16_t cmdcount = 0;
   uint16_t cmdlength = 0;
   uint16_t protocol = DEF_RTD_RESPONSE_PROTOCOL;
   uint16_t iplen = DEF_RTD_RESPONSE_IP_LEN;
   uint16_t port;
   uint8_t ip[DEF_RTD_RESPONSE_IP_LEN];
   int32_t i;
   int8_t inputBuffer[80];
   int32_t inputResponseCnt;

   /* Determine if the board selected supports the Generation 4 Ethernet Commands */
   bGen4IDRCommands = SupportsGen4Ether(cardIndex);

   /* Get the RTD channel to configure for Ethernet interrupts */
   printf("RTD Channel Selection:");
   bQuit = naiapp_query_ChannelNumber(MAX_CHANNELS, 1, &channel);

   if (!bQuit)
      bQuit = QueryEthInterruptRxIPAddr();

   if (!bQuit)
   {
      if (bGen4IDRCommands)
      {
         /* Configure the RTD channel */
         check_status(naibrd_RTD_SetWireMode(cardIndex, module, channel, NAI_RTD_GEN5_WIRE_MODE_3));
         check_status(naibrd_RTD_SetZeroTempResistance(cardIndex, module, channel, NAI_RTD_ZERO_TEMP_RESISTANCE_100));
         check_status(naibrd_RTD_SetBitOpenInterval(cardIndex, module, 100));

         /* Configure the Interrupt Driven Response Ethernet Command */
         status = check_status(naibrd_Ether_ClearIDRConfig(cardIndex, (uint16_t)DEF_RTD_IDR_ID));

         InitRtdOpenDetectIDRCommands(cardIndex, module, channel, commands, &cmdcount, &cmdlength);
         for (i = 0; i < DEF_RTD_RESPONSE_IP_LEN; i++)
            ip[i] = DEF_RTD_RESPONSE_IP_ADDR[i];
         port = DEF_RTD_RESPONSE_PORT;
         status = check_status(naibrd_Ether_SetIDRConfig(cardIndex, (uint16_t)DEF_RTD_IDR_ID, protocol, iplen, ip, port,
            DEF_RTD_OPEN_INT_VECTOR, cmdcount, cmdlength, commands));
         if (status == NAI_SUCCESS)
         {
            printf("IDR ID = %d configured.\n", DEF_RTD_IDR_ID);
         }

         /* Configure to generate interrupts on OPEN DETECT */
         check_status(naibrd_RTD_SetInterruptEdgeLevel(cardIndex, module, channel, DEF_RTD_OPEN_INT_TYPE, NAI_RTD_EDGE_INTERRUPT));  /* Edge triggered */
         check_status(naibrd_RTD_SetInterruptVector(cardIndex, module, DEF_RTD_OPEN_INT_TYPE, DEF_RTD_OPEN_INT_VECTOR));
         check_status(naibrd_RTD_SetInterruptSteering(cardIndex, module, DEF_RTD_OPEN_INT_TYPE, NAIBRD_INT_STEERING_ON_BOARD_1));
         check_status(naibrd_RTD_SetInterruptEnable(cardIndex, module, channel, DEF_RTD_OPEN_INT_TYPE, 1));

         /* Clear NAI_RTD_STATUS_OPEN_LATCHED channel status */
         check_status(naibrd_RTD_ClearStatus(cardIndex, module, channel, DEF_RTD_OPEN_INT_TYPE));

         /* Start the IDR */
         status = check_status(naibrd_Ether_StartIDR(cardIndex, (uint16_t)DEF_RTD_IDR_ID));
         if (status == NAI_SUCCESS)
         {
            printf("IDR ID = %d Started\n", DEF_RTD_IDR_ID);
         }

         /* Create a thread that will receive Unprompted Reply interrupt messages */
         terminateRtdOpenDetectThread = FALSE;

#if defined (__VXWORKS__)
         taskSpawn("uprRtdOpenDetectHandler", 100, 0, 10000, (FUNCPTR)UPR_RtdOpenDetectEthInt_Handler, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0);
#elif defined (LINUX)
         pthread_create(&interruptRtdOpenDetectThread, NULL, (void*)UPR_RtdOpenDetectEthInt_Handler, NULL);
#else /* Default Windows */
         CreateThread(NULL, 0, UPR_RtdOpenDetectEthInt_Handler, NULL, 0, NULL);
#endif

         while (!bQuit)
         {
            printf("Type %c to exit program : ", NAI_QUIT_CHAR);
            bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
            if (bQuit)
            {
               terminateRtdOpenDetectThread = TRUE;

               /* Stop the IDR */
               status = check_status(naibrd_Ether_StopIDR(cardIndex, (uint16_t)DEF_RTD_IDR_ID));
               if (status == NAI_SUCCESS)
               {
                  printf("IDR ID = %d Stopped\n", DEF_RTD_IDR_ID);
               }
            }
         }
      }
      else
      {
         printf("Ethernet Interrupt Support Prior to Generation 4 Ethernet commands currently not supported\n");
         bQuit = TRUE;
      }
   }
   return bQuit;
}

/**************************************************************************************************************/
/**
<summary>
This function queries the user to enter the IP address associated with receiving RTD Interrupt commands.
</summary>
*/
/**************************************************************************************************************/
static bool_t QueryEthInterruptRxIPAddr(void)
{
   bool_t bQuit = FALSE;
   bool_t bContinue = TRUE;
   int8_t inputBuffer[80];
   int32_t inputResponseCnt;

   /* Get the IP Address */
   if (!bQuit)
   {
      bContinue = TRUE;
      while (bContinue)
      {
         /* Get the Response IP Address */
         printf("Please Enter IDR Response IP Address: [default=%d.%d.%d.%d]): ",
            DEF_RTD_RESPONSE_IP_ADDR[0], DEF_RTD_RESPONSE_IP_ADDR[1], DEF_RTD_RESPONSE_IP_ADDR[2], DEF_RTD_RESPONSE_IP_ADDR[3]);
         bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
         if (bQuit)
            bContinue = FALSE;
         else
         {
            if (inputResponseCnt > MAX_ETHER_IP_LEN)
               printf("ERROR: Invalid IP Address.\n");
            else
            {
               if (inputResponseCnt > 0)
                  ParseIPv4Address((char *)inputBuffer, DEF_RTD_RESPONSE_IP_ADDR);
            }
         }
         if (!bQuit)
         {
            /* Get the Response Port */
            printf("Please Enter IDR Response Port: [default=%d]): ", DEF_RTD_RESPONSE_PORT);
            bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
            if (bQuit)
               bContinue = FALSE;
            else
            {
               if (inputResponseCnt > 0)
                  DEF_RTD_RESPONSE_PORT = (uint16_t)atol((const char *)inputBuffer);
               bContinue = FALSE;
            }

         }
      }
   }
   return bQuit;
}

/**************************************************************************************************************/
/**
<summary>
This function configures the IDR (Interrupt Driven Response) commands when a RTD OPEN interrupt occurs.
There are two Ethernet commands that will be processed by the board when a RTD OPEN interrupt occurs. This
routine calls the MakeRtdOpenDetectReadRegsCommand() and MakeRtdOpenDetectWriteRegsCommand() to configure the two
Ethernet commands to include when setting the IDR.
</summary>
*/
/**************************************************************************************************************/
static nai_status_t InitRtdOpenDetectIDRCommands(int32_t cardIndex, int32_t module, int32_t channel, uint8_t commands[], uint16_t *cmdcount, uint16_t *cmdlen)
{
   nai_status_t status = NAI_SUCCESS;
   uint16_t msgIndex = 0;
   uint16_t idrcmdcnt = 0;
   uint16_t ethcmdlen = 0;
   uint16_t idrcmdlen = 0;
   uint32_t moduleOffset;

   status = check_status(naibrd_GetModuleOffset(cardIndex, module, &moduleOffset));
   if (status == NAI_SUCCESS)
   {
      if (bGen4IDRCommands)
      {
         /* First command */
         MakeRtdOpenDetectReadRegsCommand(bGen4IDRCommands, msgIndex, moduleOffset, commands, &ethcmdlen);
         msgIndex += ethcmdlen;
         idrcmdlen += ethcmdlen;
         idrcmdcnt++;

         /* Next command */
         MakeRtdOpenDetectWriteRegsCommand(bGen4IDRCommands, msgIndex, moduleOffset, channel, commands, &ethcmdlen);
         msgIndex += ethcmdlen;
         idrcmdlen += ethcmdlen;
         idrcmdcnt++;

         *cmdcount = idrcmdcnt;
         *cmdlen = msgIndex;
      }
      else
      {
         printf("Ethernet Interrupt Support Prior to Generation 4 Ethernet commands currently not supported\n");
         status = NAI_ERROR_NOT_SUPPORTED;
      }
   }
   return status;
}

/**************************************************************************************************************/
/**
<summary>
This function formats the buffer for the Ethernet Read Registers command.
</summary>
*/
/**************************************************************************************************************/
static void MakeRtdOpenDetectReadRegsCommand(bool_t bGen4Ether, uint16_t startIndex, uint32_t moduleOffset, uint8_t commands[], uint16_t *cmdlen)
{
   uint16_t msgIndex = startIndex;
   uint16_t seqno;
   uint32_t count, stride;
   uint32_t regaddr;

   if (bGen4Ether)
   {
      /* Create a ReadRegs command with the following attributes:
            Onboard Access, 32-bit Register Size,
            Reg Address: NAI_RTD_GEN5_REG_OPEN_STATUS_LATCHED_ADD (to read the open status)
            Count: 1, Stride 4
      */
      seqno = 0;
      regaddr = moduleOffset + NAI_RTD_GEN5_REG_OPEN_STATUS_LATCHED_ADD;
      count = 1;
      stride = 4;
      msgIndex = (uint16_t)nai_ether_MakeReadMessage(&commands[startIndex], seqno, NAI_ETHER_GEN4, NAI_INTF_ONBOARD, regaddr, stride, count, NAI_REG32);

      *cmdlen = msgIndex;
   }
}

/**************************************************************************************************************/
/**
<summary>
This function formats the buffer for the Ethernet Write Registers command.
</summary>
*/
/**************************************************************************************************************/
static void MakeRtdOpenDetectWriteRegsCommand(bool_t bGen4Ether, uint16_t startIndex, uint32_t moduleOffset, int32_t channel,
   uint8_t commands[], uint16_t *cmdlen)
{
   uint16_t msgIndex = startIndex;
   uint16_t seqno;
   uint32_t count, stride;
   uint32_t regaddr;
   uint32_t data = (0x000000001 << (channel - 1)); /* Clear interrupt bits */

   if (bGen4Ether)
   {
      /* Create a WriteRegs command with the following attributes:
            Onboard Access, 32-bit Register Size,
            Reg Address: NAI_RTD_GEN5_REG_OPEN_STATUS_LATCHED_ADD, Count: 1, Stride 4,
            Data: 0xFFFFFFFF (to clear the interrupts)
      */
      seqno = 0;
      regaddr = moduleOffset + NAI_RTD_GEN5_REG_OPEN_STATUS_LATCHED_ADD;
      count = 1;
      stride = 4;

      msgIndex = (uint16_t)nai_ether_BeginWriteMessage(&commands[startIndex], seqno, NAI_ETHER_GEN4, NAI_INTF_ONBOARD, regaddr, stride, count, NAI_REG32);
      if (msgIndex >= 0)
      {
         msgIndex = (uint16_t)nai_ether_WriteMessageData(&commands[startIndex], msgIndex, NAI_REG32, &data, NAI_REG32, count);

         if (msgIndex >= 0)
         {
            msgIndex = (uint16_t)nai_ether_FinishMessage(&commands[startIndex], msgIndex, NAI_ETHER_GEN4);
         }
      }
      *cmdlen = msgIndex;
   }
}

/**************************************************************************************************************/
/**
<summary>
This thread calls CreateRtdOpenDetectIDRTCPServer or CreateRtdOpenDetectIDRUDPServer depending on the
DEF_RTD_RESPONSE_PROTOCOL to receive the Interrupt Driven Response (IDR) Unprompted Ethernet Replies.
</summary>
*/
/**************************************************************************************************************/
#if defined (__VXWORKS__)
int UPR_RtdOpenDetectEthInt_Handler(int32_t nParam)
{
#elif LINUX
static void* UPR_RtdOpenDetectEthInt_Handler(void *lpParam)
{
#else /* Default Windows */
DWORD WINAPI UPR_RtdOpenDetectEthInt_Handler(LPVOID lpParam)
{
#endif

   uint16_t protocol = DEF_RTD_RESPONSE_PROTOCOL;

#if defined (WIN32)
   UNREFERENCED_PARAMETER(lpParam);
#endif

   printf("\nUnprompted Reply Thread Started....\n");
   if (protocol == ETHER_GEN4_TCP_PROTOCOL)
      CreateRtdOpenDetectIDRTCPServer();
   else
      CreateRtdOpenDetectIDRUDPServer();
   printf("\nUnprompted Reply Thread Terminated\n");
   return 0;
}

/**************************************************************************************************************/
/**
<summary>
This function creates a TCP Listener socket to receive the Interrupt Driven Response (IDR) Unprompted Ethernet Replies
and waits for the Ethernet messages.
</summary>
*/
/**************************************************************************************************************/
static void CreateRtdOpenDetectIDRTCPServer(void)
{
   nai_socket_t listensock = INVALID_SOCKET, clientsock = INVALID_SOCKET;
   struct addrinfo hints;
   struct addrinfo *info;
   uint8_t portbuf[10];
   uint8_t recvbuf[NAI_ETHER_MAX_PACKET_SIZE_BYTES];
   int32_t recvbuflen = NAI_ETHER_MAX_PACKET_SIZE_BYTES;
   int32_t result;

#if defined (WIN32)
   WSADATA wsaData;
   result = WSAStartup(MAKEWORD(2, 2), &wsaData);
   if (result != 0)
   {
      printf("WSAStartup failed with error: %d\n", result);
      return;
   }
#endif

   ZeroMemory(&hints, sizeof(hints));
   hints.ai_family = AF_INET;
   hints.ai_socktype = SOCK_STREAM;
   hints.ai_protocol = IPPROTO_TCP;
   hints.ai_flags = AI_PASSIVE;

   /* Resolve the server address and port */
   sprintf((char *)portbuf, "%d", DEF_RTD_RESPONSE_PORT);
#if defined (WIN32)
   result = getaddrinfo(NULL, (PCSTR)portbuf, &hints, &info);
#elif defined(LINUX) || defined(__VXWORKS__)
   result = getaddrinfo(NULL, portbuf, &hints, &info);
#endif
   if (result != 0)
   {
      printf("getaddrinfo failed with error: %d\n", result);
#if defined (WIN32)
      WSACleanup();
#endif
      return;
   }

   /* Create a SOCKET for connecting to server */
   listensock = socket(info->ai_family, info->ai_socktype, info->ai_protocol);
   if (listensock == INVALID_SOCKET)
   {
#if defined (WIN32)
      printf("socket failed with error: %ld\n", WSAGetLastError());
#elif defined(LINUX) || defined(__VXWORKS__)
      printf("socket failed with error: %s\n", strerror(errno));
#endif
      freeaddrinfo(info);
#if defined (WIN32)
      WSACleanup();
#endif
      return;
   }

   /* Setup the TCP listening socket */
   result = bind(listensock, info->ai_addr, (int)info->ai_addrlen);
   if (result == SOCKET_ERROR)
   {
#if defined (WIN32)
      printf("bind failed with error: %d\n", WSAGetLastError());
#elif defined(LINUX) || defined(__VXWORKS__)
      printf("bind failed with error: %s\n", strerror(errno));
#endif
      freeaddrinfo(info);
      closesocket(listensock);
#if defined (WIN32)
      WSACleanup();
#endif
      return;
   }

   freeaddrinfo(info);

   result = listen(listensock, SOMAXCONN);
   if (result == SOCKET_ERROR)
   {
#if defined (WIN32)
      printf("listen failed with error: %d\n", WSAGetLastError());
#elif defined(LINUX) || defined(__VXWORKS__)
      printf("listen failed with error: %s\n", strerror(errno));
#endif
      closesocket(listensock);
#if defined (WIN32)
      WSACleanup();
#endif
      return;
   }

   /* Accept a client socket */
   clientsock = accept(listensock, NULL, NULL);
   if (clientsock == INVALID_SOCKET)
   {
#if defined (WIN32)
      printf("accept failed with error: %d\n", WSAGetLastError());
#elif defined(LINUX) || defined(__VXWORKS__)
      printf("accept failed with error: %s\n", strerror(errno));
#endif
      closesocket(listensock);
#if defined (WIN32)
      WSACleanup();
#endif
      return;
   }

   /* No longer need server socket */
   closesocket(listensock);

   printf("Ready to receive a TCP messages...\n");

   /* Receive until the peer shuts down the connection */
   do
   {
      result = recv(clientsock, (char *)recvbuf, recvbuflen, 0);
      if (result > 0)
      {
         printf("\n\nTCP Server: Bytes received: %d\n", result);
         DecodeUPRRtdOpenDetectIDRMessages(recvbuf, result);
      }
      else if (result == 0)
         printf("Connection closing...\n");
      else
      {
#if defined (WIN32)
         printf("recv failed with error: %d\n", WSAGetLastError());
#elif defined(LINUX) || defined(__VXWORKS__)
         printf("recv failed with error: %s\n", strerror(errno));
#endif
         closesocket(clientsock);
#if defined (WIN32)
         WSACleanup();
#endif
         return;
      }
   } while ((result > 0) && (terminateRtdOpenDetectThread == FALSE));

   /* shutdown the connection when done */
   result = shutdown(clientsock, SD_SEND);
   if (result == SOCKET_ERROR)
   {
#if defined (WIN32)
      printf("shutdown failed with error: %d\n", WSAGetLastError());
#elif defined(LINUX) || defined(__VXWORKS__)
      printf("shutdown failed with error: %s\n", strerror(errno));
#endif
      closesocket(clientsock);
#if defined (WIN32)
      WSACleanup();
#endif
      return;
   }

   /*cleanup */
   closesocket(clientsock);
#if defined (WIN32)
   WSACleanup();
#endif
}

/**************************************************************************************************************/
/**
<summary>
This function creates a UDP Listener socket to receive the Interrupt Driven Response (IDR) Unprompted Ethernet Replies
and waits for the Ethernet messages.
</summary>
*/
/**************************************************************************************************************/
static void CreateRtdOpenDetectIDRUDPServer()
{
   nai_socket_t recvsock;
   uint8_t recvbuf[NAI_ETHER_MAX_PACKET_SIZE_BYTES];
   int32_t recvbuflen = NAI_ETHER_MAX_PACKET_SIZE_BYTES;
#if defined (WIN32)
   SOCKADDR_IN recvaddr;
   SOCKADDR_IN sendaddr;
#elif defined(LINUX) || defined(__VXWORKS__)
   struct sockaddr_in recvaddr;
   struct sockaddr_in sendaddr;
#endif
   int32_t sendaddrsize = sizeof(sendaddr);
   int32_t result;

#if defined (WIN32)
   WSADATA wsaData;
   result = WSAStartup(MAKEWORD(2, 2), &wsaData);
   if (result != 0)
   {
      printf("WSAStartup failed with error: %d\n", result);
      return;
   }
#endif

   /* Create a new socket to receive datagrams on. */
   recvsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
   if (recvsock == INVALID_SOCKET)
   {
#if defined (WIN32)
      printf("socket failed with error: %ld\n", WSAGetLastError());
#elif defined(LINUX) || defined(__VXWORKS__)
      printf("socket failed with error: %s\n", strerror(errno));
#endif
#if defined (WIN32)
      WSACleanup();
#endif
      return;
   }

   /* Set up a SOCKADDR_IN structure that will tell bind that we
      want to receive datagrams from all interfaces using port DEF_RESPONSE_PORT.
   */
   /* The IPv4 family */
   recvaddr.sin_family = AF_INET;
   recvaddr.sin_port = htons(DEF_RTD_RESPONSE_PORT);
   /* From all interface (0.0.0.0) */
   recvaddr.sin_addr.s_addr = htonl(INADDR_ANY);

   /* Associate the address information with the socket using bind.
      At this point you can receive datagrams on your bound socket.
   */
#if defined (WIN32)
   result = bind(recvsock, (SOCKADDR *)&recvaddr, sizeof(recvaddr));
#elif defined(LINUX) || defined(__VXWORKS__)
   result = bind(recvsock, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
#endif
   if (result == SOCKET_ERROR)
   {
#if defined (WIN32)
      printf("bind failed with error: %d\n", WSAGetLastError());
#elif defined(LINUX) || defined(__VXWORKS__)
      printf("bind failed with error: %s\n", strerror(errno));
#endif
      closesocket(recvsock);
#if defined (WIN32)
      WSACleanup();
#endif
      return;
   }

   printf("Ready to receive UDP messages...\n");

   while (terminateRtdOpenDetectThread == FALSE)
   {
      /* Call recvfrom() to get it then display the received data. */
#if defined (WIN32)
      result = recvfrom(recvsock, (char *)recvbuf, recvbuflen,
         0, (SOCKADDR *)&sendaddrsize, &sendaddrsize);
#elif defined(LINUX) || defined(__VXWORKS__)
      result = recvfrom(recvsock, (char *)recvbuf, recvbuflen,
         0, (struct sockaddr *)&sendaddrsize, &sendaddrsize);
#endif
      if (result > 0)
      {
         printf("\n\nUDP Server: Bytes received: %d\n", result);
         DecodeUPRRtdOpenDetectIDRMessages(recvbuf, result);
      }
      else if (result <= 0)
#if defined (WIN32)
         printf("Connection closed with error code: %ld\n", WSAGetLastError());
#elif defined(LINUX) || defined(__VXWORKS__)
         printf("Connection closed with error code: %s\n", strerror(errno));
#endif
      else
#if defined (WIN32)
         printf("recvfrom() failed with error code: %d\n", WSAGetLastError());
#elif defined(LINUX) || defined(__VXWORKS__)
         printf("recvfrom() failed with error code: %s\n", strerror(errno));
#endif
   }

   /* When application is finished receiving datagrams close the socket. */
   printf("Server: Finished receiving. Closing the listening socket...\n");
   closesocket(recvsock);
#if defined (WIN32)
   WSACleanup();
#endif
   return;
}

/**************************************************************************************************************/
/**
<summary>
This function calls ParseRtdOpenDetectIDRResponse() to break down the IDR message received to individual Ethernet messages
and calls DisplayDecodedRtdOpenDetectIDRResponse() to display the data in the Ethernet message.
</summary>
*/
/**************************************************************************************************************/
static void DecodeUPRRtdOpenDetectIDRMessages(const uint8_t msg[], int32_t msgsize)
{
   uint32_t arraysize = MAX_ETHER_IDR_CMD_CNT * MAX_ETHER_BLOCK_REG_CNT;
   uint16_t responselen;
   uint8_t response[MAX_ETHER_BLOCK_REG_CNT];
   int32_t startIndex;

   startIndex = 0;
   while (startIndex < msgsize)
   {
      if (ParseRtdOpenDetectIDRResponse(startIndex, arraysize, (uint8_t *)msg, &responselen, MAX_ETHER_BLOCK_REG_CNT, response))
      {
         startIndex += responselen;
         DisplayDecodedRtdOpenDetectIDRResponse(responselen, response);
      }
   }
}

/**************************************************************************************************************/
/**
<summary>
This function break downs the IDR message received to individual Ethernet messages.
</summary>
*/
/**************************************************************************************************************/
static bool_t ParseRtdOpenDetectIDRResponse(int32_t startIndex, int32_t rsparraysize, uint8_t response[], uint16_t *idrrsplen, int32_t idrrsparraysize, uint8_t idr_response[])
{
   bool_t bParsed = FALSE;
   bool_t bContinueCopy = TRUE;
   uint16_t preamble, postamble;
   int32_t rspIndex = startIndex;
   int32_t idrIndex = 0;

   preamble = (response[startIndex] << 8) | response[startIndex + 1];
   if (bGen4IDRCommands)
   {
      if (preamble == ETHER_GEN4_PREAMBLE)
      {
         while ((bContinueCopy) && (idrIndex < idrrsparraysize) && (rspIndex < rsparraysize - 1))
         {
            postamble = (response[rspIndex] << 8) | response[rspIndex + 1];
            if (postamble == ETHER_GEN4_POSTAMBLE)
            {
               idr_response[idrIndex++] = response[rspIndex++];
               idr_response[idrIndex++] = response[rspIndex++];
               bContinueCopy = FALSE;
            }
            else
            {
               idr_response[idrIndex++] = response[rspIndex++];
            }
         }
         bParsed = TRUE;
         *idrrsplen = (uint16_t)idrIndex;
      }
   }
   else
   {
      printf("Ethernet Interrupt Support Prior to Generation 4 Ethernet commands currently not supported\n");
   }
   return bParsed;
}

/**************************************************************************************************************/
/**
<summary>
This function displays the data in the Ethernet message.
</summary>
*/
/**************************************************************************************************************/
static void DisplayDecodedRtdOpenDetectIDRResponse(uint16_t responselen, uint8_t response[])
{
   uint16_t seq;
   nai_ether_typecode_t tc;
   nai_ether_gen_t gen;
   int32_t size;
   int32_t offset;
   uint16_t seqrsptype;
   uint16_t seqrspidindex;
   uint16_t seqcmdindex;
   uint16_t datacnt;
   uint32_t data;
   int32_t i;

   if (bGen4IDRCommands)
      gen = NAI_ETHER_GEN4;
   else
      gen = NAI_ETHER_GEN3;

   offset = nai_ether_DecodeMessageHeader(response, responselen, &seq, &tc, gen, &size);

   printf("Size=%d   ", size);

   /* Parse IDR Response Sequence Value */
   seqrsptype = seq & ETHER_GEN4_SEQ_UPR_MASK;
   seqrspidindex = (seq & (ETHER_GEN4_SEQ_ID_MASK)) >> ETHER_GEN4_SEQ_ID_SHIFT;
   seqcmdindex = (seq & (ETHER_GEN4_SEQ_CMD_MASK)) >> ETHER_GEN4_SEQ_CMD_SHIFT;
   printf("seq:0x%04X (", seq);
   if (seqrsptype == ETHER_GEN4_SEQ_UPR_TDR)
      printf("Type=Unprompted TDR ");
   else if (seqrsptype == ETHER_GEN4_SEQ_UPR_IDR)
      printf("Type=Unprompted IDR ");
   else
      printf("Type=UNKNOWN(0x%X) ", seqrsptype);
   printf("ID=%d Cmd=%d)   ", seqrspidindex + 1, seqcmdindex + 1);

   switch (tc)
   {
      case NAI_ETHER_TYPECODE_RSP_COMMAND_COMPLETE_READ_4:
         printf("Typecode: Read Regs (0x%4X)\n", tc);
         datacnt = (responselen - 10) / NAI_REG32;
         printf("   Data (Count=%d): ", datacnt);
         for (i = 0; i < datacnt; i++)
         {
            if (i != 0)
               printf(",");
            data = 0;
            data = response[offset++] << 24;
            data |= response[offset++] << 16;
            data |= response[offset++] << 8;
            data |= response[offset++];
            printf("0x%08X", data);
         }
         printf("\n");
         break;
      case NAI_ETHER_TYPECODE_RSP_COMMAND_COMPLETE_WRITE_4:
         printf("Typecode: Write Regs (0x%4X)\n", tc);
         break;
      case NAI_ETHER_TYPECODE_RSP_COMMAND_COMPLETE_READ_BLOCK_4:
         printf("Typecode: Read Block (0x%4X)\n", tc);
         datacnt = (responselen - 10) / NAI_REG32;
         printf("   Data (Count=%d): ", datacnt);
         for (i = 0; i < datacnt; i++)
         {
            if (i != 0)
               printf(",");
            data = 0;
            data = response[offset++] << 24;
            data |= response[offset++] << 16;
            data |= response[offset++] << 8;
            data |= response[offset++];
            printf("0x%08X", data);
         }
         printf("\n");
         break;
      case NAI_ETHER_TYPECODE_RSP_COMMAND_COMPLETE_WRITE_BLOCK_4:
         printf("Typecode: Write Block (0x%4X)\n", tc);
         break;
      case NAI_ETHER_TYPECODE_RSP_ERROR_BLOCK_NOT_CONFIG_4:
         printf("Typecode: Block Error (Not Configured) (0x%4X)\n", tc);
         break;
      default:
         printf("Typecode: Unknown (0x%4X)\n", tc);
         break;
   }
}

Help Bot

X