.. _samples:

=================
Addi-Pack Samples
=================

Overview
--------

This section contains detailed explanations of every **Addi-Pack sample program** provided with Addi-Pack.
Samples are provided in **C**, **C++**, and **Python**, and demonstrate how to interact with ADDI-DATA cards
through the API in both **interactive** and **automated** ways.

Each sample:

- Is **interactive by default** (if no command-line arguments are provided),
- Can also be run in **fully scripted or automated mode** using command-line options,
- Is designed to be a **complete, working program** ready to build and execute.


Sample Utilities
----------------

.. admonition:: Goal

   Provide common functions used by all samples.
   Such as user prompts, argument parsing, device selection, and interrupt handling.

These utilities form the foundation of all Addi-Pack samples.

The SampleUtils and ArgParser modules have distinct responsibilities:

- **ArgParser**
  Provides the command-line argument interface used by the samples.
  It handles:
  - parsing options such as ``--mode=interrupt`` or ``--card=apcie1032``,
  - checking whether an option or flag is present,
  - retrieving option values (e.g. ``--timebase ms``),
  - validating argument syntax.

- **SampleUtils**
  Implements all interactive behaviour of the samples.
  It is responsible for:
  - deciding whether to run in scripted or interactive mode (based on whether an option is set),
  - prompting the user when an argument is missing or invalid,
  - selecting a device according to capabilities,
  - selecting modes,
  - validating numeric and hexadecimal parameters,
  - helper utilities (interrupt callback, timing helpers, exit helpers, etc.).

Together, these modules allow each sample to support both automated command-line execution
and interactive question-based execution.
They are not part of the public API, but they can be reused for rapid prototyping.

.. tip::
   Even though `ArgParser` is technically a private helper, you can safely use it in your own C/C++ projects
   to build interactive CLI tools consistent with the official samples.

   Using it has several benefits:

   - **No external dependency**: the parser is fully embedded in Addi-Pack and does not require any third-party library.
   - **Cross-platform behavior**: the same command-line arguments work identically on Windows and Linux.
   - **Consistent user experience**: your tools behave exactly like the official samples (same options, same style, same error handling).
   - **Faster prototyping**: no need to implement argument parsing, validation or help messages yourself.
   - **Reliable and tested**: the parser is used internally by all ADDI-DATA samples, ensuring stable behavior.


Key Concepts
~~~~~~~~~~~~

1. **Interactive vs Non-Interactive Mode**

   - If the user passes options (e.g. `--reload=1000 --mode=polling`), the sample runs automatically.
   - Otherwise, prompts are displayed dynamically to ask for missing parameters.

2. **Automatic Device Selection**

   The function `AddiDataUtilsSelectDeviceByCapability()` filters all detected devices
   and selects one based on user input or CLI arguments.

3. **Input Validation**

   Utilities like `AddiDataUtilsGetValidInt()` or `AddiDataUtilsGetValidString()` ensure
   that all parameters are valid before starting the test or operation.

4. **Interrupt Management**

   Functions such as `AddiDataUtilsInterruptCallback()` and
   `AddiDataUtilsHasInterruptHappened()` are shared across interrupt-based samples.


C Utilities
~~~~~~~~~~~

Main header:
``#include <addi-data/addi-pack/utilities/sample_utils.h>``

Below are the main utility functions available in **C** samples:

+------------------------------------------------+-------------------------------------------------------------------------------------------------+
|   **Function**                                 | **Purpose**                                                                                     |
+================================================+=================================================================================================+
| `AddiDataUtilsPromptString()`                  | Prompt the user for a string, with optional accepted values and default.                        |
+------------------------------------------------+-------------------------------------------------------------------------------------------------+
| `AddiDataUtilsPromptInt()`                     | Prompt the user for an integer within a specified range `[min, max]`.                           |
+------------------------------------------------+-------------------------------------------------------------------------------------------------+
| `AddiDataUtilsPromptHex()`                     | Prompt the user for a hexadecimal value within a specified range `[min, max]`.                  |
+------------------------------------------------+-------------------------------------------------------------------------------------------------+
| `AddiDataUtilsGetValidString()`                | Get a validated string from CLI args or fallback to prompt.                                     |
+------------------------------------------------+-------------------------------------------------------------------------------------------------+
| `AddiDataUtilsGetValidInt()`                   | Get a validated integer from CLI args or fallback to prompt.                                    |
+------------------------------------------------+-------------------------------------------------------------------------------------------------+
| `AddiDataUtilsGetValidHex()`                   | Get a validated hexadecimal value from CLI args or fallback to prompt.                          |
+------------------------------------------------+-------------------------------------------------------------------------------------------------+
| `AddiDataUtilsMatchesCardFilter()`             | Check whether a device matches a card filter string.                                            |
+------------------------------------------------+-------------------------------------------------------------------------------------------------+
| `AddiDataUtilsSelectDeviceByCapability()`      | Select a device matching a predicate from a list of devices.                                    |
+------------------------------------------------+-------------------------------------------------------------------------------------------------+
| `AddiDataUtilsSelectMode()`                    | Select a string mode from a list, using CLI `--<name>` or prompt.                               |
+------------------------------------------------+-------------------------------------------------------------------------------------------------+
| `AddiDataUtilsParseTimebase()`                 | Convert a timebase string (`"us"`, `"ms"`, `"s"`) into a `TIMEBASE_UNITS` enum value.           |
+------------------------------------------------+-------------------------------------------------------------------------------------------------+
| `AddiDataUtilsInterruptCallback()`             | Default interrupt handler that logs the source, mask, and elapsed time since the last interrupt.|
+------------------------------------------------+-------------------------------------------------------------------------------------------------+
| `AddiDataUtilsResetInterruptFlag()`            | Reset the interrupt flag and internal timer.                                                    |
+------------------------------------------------+-------------------------------------------------------------------------------------------------+
| `AddiDataUtilsHasInterruptHappened()`          | Check whether an interrupt has occurred since the last reset.                                   |
+------------------------------------------------+-------------------------------------------------------------------------------------------------+
| `AddiDataUtilsExitOnUserInput()`               | Wait for the user to press ENTER and exit the program.                                          |
+------------------------------------------------+-------------------------------------------------------------------------------------------------+
| `AddiDataUtilsPrintAvailableTcwOptions()`      | Print a list of available TCW (Timer-Counter-Watchdog) options supported by the device.         |
+------------------------------------------------+-------------------------------------------------------------------------------------------------+
| `AddiDataUtilsGetTcwOptionsFromUser()`         | Prompt the user to select TCW options interactively.                                            |
+------------------------------------------------+-------------------------------------------------------------------------------------------------+
| `AddiDataUtilsGetValidTimebaseList()`          | Get a list of valid timebases supported by the device.                                          |
+------------------------------------------------+-------------------------------------------------------------------------------------------------+
| `AddiDataUtilsDisplayAllPortsConfigurations()` | Display the configuration (input/output) of all digital ports.                                  |
+------------------------------------------------+-------------------------------------------------------------------------------------------------+

**Example – Using the utilities in C**

.. code-block:: c

   // Used to filter cards with at least one timer
   int hasTimer(const AddiDataDeviceStruct* device) { return device->functions.timer.number_timers > 0; }

   // Initialise the argument parser
   AddiDataArgParserInit();
   AddiDataArgParserAddOption("card", 'c', "Filter by card name", 1);
   AddiDataArgParserAddOption("reload", 'r', "Reload value", 1);
   AddiDataArgParserParse(argc, argv);

   // Get the number of available devices
   uint8_t number_of_cards = 0;
   AddiDataGetNumberOfDevices(&number_of_cards)

   // Allocate and populate the device list
   AddiDataDeviceStruct* devices = malloc(sizeof(AddiDataDeviceStruct) * number_of_cards);
   AddiDataGetDeviceList(devices);

   // Select a device with at least one timer
   const AddiDataDeviceStruct* device = AddiDataUtilsSelectDeviceByCapability(devices, number_of_cards, "card", hasTimer, "timer");

   // Get a valid reload value from command-line or prompt
   int reload = AddiDataUtilsGetValidInt("reload", "Enter reload value", 1, 10000);

   // Display selected card and reload value
   printf("Using %s | Reload = %d\n", device->general.product_name, reload);


C++ Utilities
~~~~~~~~~~~~~

In C++, the same utilities are provided via wrappers and helper classes.

**Example – Using ArgParser and SampleUtils in C++**

.. code-block:: cpp

   #include <addi-data/addi-pack/cpp/sample_utils.hpp>
   #include <addi-data/addi-pack/cpp/timer_interface.hpp>

   using namespace addidata;
   using namespace addipack;
   using namespace utils;

   bool hasTimer(const std::shared_ptr<Device>& device) {
      return device->getDeviceInformation().functions.timer.number_timers > 0;
   }

   int main(int argc, char* argv[]) {
       ArgParser::init();
       ArgParser::addOption("card", 'c', "Filter by card name");
       ArgParser::addOption("reload", 'r', "Reload value");
       ArgParser::parse(argc, argv);

       auto devices = DeviceDiscovery::getDeviceList();
       auto device = selectDeviceByCapability(devices, "card", hasTimer, "timer");

       int reload = getValidInt("reload", "Enter reload value", 1, 10000);
       std::cout << "Selected device: " << device->getDeviceInformation().id << " | Reload = " << reload << std::endl;
   }


Python Utilities
~~~~~~~~~~~~~~~~

**Example – Using SampleUtils in Python**

.. code-block:: python

   from sample_utils import *

   def has_timer(device):
      return device.get_card_information().functions.timer.number_timers > 0

   def parse_args():
    parser = argparse.ArgumentParser(description="example")
    parser.add_argument("-c", "--card", help="Filter device by name (apcie1032, apci1500 ...)")
    parser.add_argument("-r", "--reload", type=int, help="Reload value")
    return parser.parse_args()

   def main():
      args = parse_args()
      device = select_device_by_capability(args.card, has_timer, "timer")
      info = device.get_card_information()
      reload_value = get_valid_int(args.reload, "reload", 1, 10000)

   print(f"Selected {info.id} with reload={reload_value}")


get_devices_info
----------------

.. admonition:: Goal

   List all detected ADDI-DATA cards connected to the system and display their detailed properties.

This is the **first sample to run after installation**, as it verifies that:

- The Addi-Pack drivers are correctly installed,
- The API can communicate with the hardware,
- Cards are properly enumerated and accessible.

This sample calls the core device discovery functions:

- `AddiDataGetNumberOfDevices()` → returns the number of cards detected
- `AddiDataGetDeviceList()` → fills a list of `AddiDataDeviceStruct` entries

It then prints for each card:

- Product name and type (PCI or PCIe)
- Bus information (bus, slot, vendor and device IDs)
- Function capabilities (digital I/O, timers, counters, watchdogs)
- Interrupt number and BAR addresses

The user can **filter cards interactively** (by name, type, or model)
or directly using a **command-line argument** such as `--card=apcie1032`.


Key Features
~~~~~~~~~~~~

1. **Dynamic card detection**

   Enumerates all ADDI-DATA devices automatically, regardless of model.

2. **Flexible filtering**

   - `--card=apcie` → lists only PCIe cards
   - `--card=apci` → lists only PCI cards
   - `--card=1032` → lists only 1032 cards

3. **Automatic behavior**

   - If only one card is found, it’s automatically selected.
   - If multiple cards are found, the user can choose or filter interactively.

4. **Cross-platform output**

   Works identically on **Windows** and **Linux**, and displays unified information.


Example Usage
~~~~~~~~~~~~~

**Interactive mode (default):**

.. code-block:: bash

   ./get_device_info
   One device found. Automatically selecting it.
   ========= Device 0 (apcie-1032) =========
   Product Name: APCIe-1032
   Bus: 3 | Device: 0 | Vendor ID: 0x15B8 | Device ID: 0x1032
   Interrupt: 17 | Bus Type: PCIe
   Digital Inputs: 32 | Outputs: 0 | Ports: 1 | Mask: 0xFFFF
   Timers: 0 | Counters: 0 | Watchdogs: 0

**Filtered mode:**

.. code-block:: bash

   ./get_device_info --card=apci1500

It displays only matching cards (here apci1500).


digital_input
-------------

.. admonition:: Goal

   Read digital input channels in **polling** or **interrupt** mode.

This sample demonstrates how to acquire input states from ADDI-DATA cards,
either by continuously reading the lines or by waiting for hardware events.


Concept and Purpose
~~~~~~~~~~~~~~~~~~~

The `digital_input` sample is designed to validate **input acquisition** and **interrupt configuration**
on any card supporting digital inputs.

It serves both as:

- A **functional test**, confirming that inputs toggle and can trigger events.
- A **reference implementation**, showing how to configure edge detection, logic, and masks.

The sample operates in **two distinct modes**:

- **read** → polling mode: the application continuously reads and displays input states.
- **interrupt** → event-driven mode: the program waits for hardware-triggered input changes.


Execution Workflow
~~~~~~~~~~~~~~~~~~

1. **Device detection and selection**

   The sample first detects all installed ADDI-DATA cards with input capability
   using `AddiDataUtilsSelectDeviceByCapability()`.
   If multiple compatible cards are found, the user can select one interactively
   or provide it via the command line (e.g. `--card=apcie1032`).

2. **Mode selection**

   The user chooses between:

   - `read` mode → constant polling,
   - `interrupt` mode → asynchronous event handling.

   This can be specified via `--mode=` or through an interactive prompt.

3. **Configuration phase (interrupt mode)**

   When running in **interrupt mode**, several parameters are configured:

   - **Port index** (`--port`): selects which input port to monitor.
   - **Interrupt mask** (`--mask`): selects which input bits trigger events.
   - **Event type** (`--event`): rising/falling/any edge or level-based detection.
   - **Logic** (`--logic`): whether multiple active inputs combine with `OR` or `AND`.

   The sample validates all these parameters and applying interactive prompts as needed.

4. **Runtime phase**

   - In **read mode**, the sample prints live input states in binary form.
     The screen is refreshed continuously until the user presses **ENTER**.

   - In **interrupt mode**, the sample:

     - Registers the default interrupt callback `AddiDataUtilsInterruptCallback`.
     - Enables device and digital interrupts.
     - Prints a message every time an interrupt occurs, including:

       - the source,
       - the input mask,
       - and the elapsed time since the previous interrupt.

5. **Termination**

   When the user presses **ENTER**, the sample:

   - Disables all interrupt sources,
   - Restores device state,
   - Frees allocated resources,
   - And exits cleanly.


Runtime Behavior
~~~~~~~~~~~~~~~~

**In read mode:**
- The program polls all input lines periodically (every 100 ms by default).
- Values are printed as a live binary bit field:

   - Example output:
      `Inputs: 0000000000001111`

- Press **ENTER** to stop reading.

**In interrupt mode:**
- The card triggers an interrupt whenever the configured event occurs on one of the selected bits.
- Each interrupt produces a console message such as:

  .. code-block:: text

     [INTERRUPT] Callback: source=0x02 | mask=0x0004 | elapsed=12.45 ms

- The `elapsed` field measures the delay between consecutive events,
  helping assess response time and signal stability.


digital_output
---------------

.. admonition:: Goal

   Control and test digital output lines on ADDI-DATA cards, either **individually** or **as a group**.

This sample demonstrates how to write digital output values using the Addi-Pack API,
in both interactive and scripted (CLI) modes.


Concept and Purpose
~~~~~~~~~~~~~~~~~~~

The `digital_output` sample is designed to validate the correct functioning of the **digital outputs** on any compatible card.
It allows developers or test engineers to quickly verify that all output lines can be toggled as expected.

It provides two complementary operating modes:

- **single mode:** interactively set individual output channels one by one.
- **mask mode:** apply a bitmask to modify multiple outputs simultaneously.
- **interrupt mode:** monitor hardware fault events on digital outputs


Execution Workflow
~~~~~~~~~~~~~~~~~~

1. **Device detection and selection**

   The sample first detects all installed ADDI-DATA devices using
   `AddiDataUtilsSelectDeviceByCapability()`, filtering only those with output capability.
   If several compatible cards are present, the user can choose interactively or provide a
   `--card` filter (e.g. `--card=apci1564`).

2. **Mode selection**

   The user selects between:

   - `single` → manually set each output line.
   - `mask` → apply one value to all lines defined by a bitmask.
   - `interrupt` → enable and monitor on digital output interrupts.

   This can be done interactively or using `--mode=single` / `--mode=mask`.

3. **Configuration phase**

   Depending on the chosen mode, the sample requests or parses additional parameters:

   - **Mode = single**

     - Channel index to modify (`0` → `number_outputs - 1`)
     - Output value to write (`0` or `1`)

   - **Mode = mask**

     - Output mask (`--mask=0xFF`): defines which bits to affect
     - Value (`--value=0` or `--value=1`): sets all selected outputs high or low

   - **Mode = interrupt**

     - Output interrupt mask (`--mask`):

       - Bit `0x01` → enable **VCC (power supply failure)** interrupt
       - Bit `0x02` → enable **CC (short-circuit)** interrupt

4. **Output operation**

   - For **single mode**, the program loops indefinitely, asking the user for a channel and a value until they press **ENTER**.
   - For **mask mode**, the selected mask is written once with the chosen value.
   - For **interrupt mode**, the program registers the default interrupt callback
     `AddiDataUtilsInterruptCallback`, enables device and output interrupts,
     and prints a message each time an interrupt occurs, including the source,
     mask, and elapsed time since the last event.

5. **Termination**

   When finished, all resources are released, and the program exits cleanly after waiting for user confirmation.


Runtime Behavior
~~~~~~~~~~~~~~~~

**Single Mode (interactive control)**

- The user specifies one channel and a value (0 or 1).
- The output state is immediately applied and confirmed in the console.
- The loop continues until the user presses **ENTER** on an empty line.

Typical output:

.. code-block:: text

   Select output channel [0-15] (empty = quit): 3
   Set value for this channel [0-1]: 1
   Channel 3 set to 1

   Select output channel [0-15] (empty = quit): 5
   Set value for this channel [0-1]: 0
   Channel 5 set to 0

**Mask Mode (group operation)**

- The user defines a hexadecimal mask and a single value (0/1).
- All output lines corresponding to bits set in the mask are updated simultaneously.

Example:

.. code-block:: text

   Enter output mask in hexadecimal (max 0xFFFF): 0x0F
   Set value for the entire mask [0-1]: 1
   Writing value 1 to mask 0x0F...
   Digital outputs updated successfully.

In this example, the first four outputs (0–3) are set to logical 1.

digital_port
------------

.. _digital_port:

.. admonition:: Goal

   Configure and interact with digital I/O ports on ADDI-DATA cards.

This sample demonstrates how to:
  - Discover devices and select one with digital I/O port capabilities
  - Configure port directions (input/output)
  - Read digital inputs from a port
  - Write digital outputs to a port using a mask

It is designed to be a **complete, working program** ready to build and execute.

.. admonition:: Key Features

   - **Device Discovery and Selection:**
     The user can filter devices by name (e.g., "apci1696") to find compatible hardware.
     The sample checks if the selected device has digital I/O ports.

   - **Display Initial Configuration:**
     The sample reads and displays the current direction and status of all digital ports.
     It shows which ports are configured as inputs or outputs and their current values.

   - **Port Direction Configuration:**
     The user can enter a hexadecimal mask to set the direction of all ports at once.
     For example, a mask of 0xF would set ports 0-3 as outputs and the rest as inputs.

   - **Mode Selection:**
     The user can choose between "display" mode to continuously show port statuses or "write" mode to set outputs.
     In "display" mode, the sample refreshes the port status every 200ms until the user presses ENTER.
     In "write" mode, the user selects a specific output port and provides a mask and value to set the outputs.

.. admonition:: Workflow

   1. **Device Discovery & Selection:**
      - The user can filter devices by name (e.g., "apci1696") to find compatible hardware.
      - The sample checks if the selected device has digital I/O ports.

   2. **Display Initial Configuration:**
      - The sample reads and displays the current direction and status of all digital ports.
      - It shows which ports are configured as inputs or outputs and their current values.

   3. **Port Direction Configuration:**
      - The user can enter a hexadecimal mask to set the direction of all ports at once.
      - For example, a mask of 0xF would set ports 0-3 as outputs and the rest as inputs.

   4. **Mode Selection:**
      - The user can choose between "display" mode to continuously show port statuses or "write" mode to set outputs.
      - In "display" mode, the sample refreshes the port status every 200ms until the user presses ENTER.
      - In "write" mode, the user selects a specific output port and provides a mask and value to set the outputs.

timer
------

.. admonition:: Goal

   Initialise, start, and read hardware timers in **polling** or **interrupt** mode.

This sample demonstrates how to configure and operate hardware timers available on ADDI-DATA cards, using both
continuous polling and event-driven (interrupt) approaches.


Concept and Purpose
~~~~~~~~~~~~~~~~~~~

The `timer` sample is designed to validate the **timer subsystem** of ADDI-DATA cards.
It shows how to configure timebases, reload values, and operational options dynamically, with support for both
standard and interrupt-driven operation.

It serves as both a **reference implementation** and a **diagnostic tool** for timing features on cards such as:

- **APCI-1500 / APCIe-1500**
- **APCI-1564 / APCIe-1564**
- **APCIe-1032**

The timer is typically used for periodic events, time measurements, or watchdog triggers.


Execution Workflow
~~~~~~~~~~~~~~~~~~

1. **Device detection and selection**

   The program enumerates all installed cards and filters those with timer capability using
   `AddiDataUtilsSelectDeviceByCapability()`.
   If multiple cards are detected, the user can select one interactively or specify it through
   `--card=<name>`.

2. **Mode selection**

   Two operating modes are available:

   - **polling** → the sample periodically reads and prints the timer status.
   - **interrupt** → the program waits for timer expiration events, notified by hardware interrupts.

   This can be set interactively or by passing `--mode=polling` or `--mode=interrupt`.

3. **Timer configuration**

   Once the device and mode are selected, the program interactively requests or parses:

   - **Timer index** (`--timer`): which timer to configure.
     Most cards have multiple independent timers (e.g. 3 on the APCI-1500).

   - **Timebase** (`--timebase`): defines the unit for reload timing —
     microseconds (`us`), milliseconds (`ms`), or seconds (`s`).

   - **Reload value** (`--reload`): defines the countdown duration before expiration.
     The maximum value depends on the card and the timebase.

   - **Timer options**: optional configuration flags such as continuous or single-shot operation,
     retrieved via `AddiDataUtilsGetTcwOptionsFromUser()`.

4. **Initialization and startup**

   The timer is configured and started in three steps:

   - `AddiDataInitTimer()` → apply configuration (index, timebase, reload, options)
   - `AddiDataTriggerTimer()` → trigger initial value
   - `AddiDataStartTimer()` → start periodic operation

   When interrupt mode is selected, the program also registers the default callback
   `AddiDataUtilsInterruptCallback` and enables timer interrupts via:

   - `AddiDataEnableDeviceInterrupts()`
   - `AddiDataEnableTimerInterrupt()`

5. **Runtime phase**

   - In **polling mode**, the program repeatedly calls `AddiDataReadTimerStatus()` to display:
     - current value,
     - status flag,
     - trigger state.

   - In **interrupt mode**, messages are printed each time the timer interrupt fires,
     including timestamps and elapsed times.

6. **Termination**

   When the user presses **ENTER**, the timer is stopped and interrupts are disabled.
   The program then frees allocated memory and exits cleanly.


Runtime Behavior
~~~~~~~~~~~~~~~~

**Polling mode:**

- Displays live timer countdown values every 100 ms.
- Example output:

  .. code-block:: text

     Timer: value=487 | status=1 | triggered=0 | expired=0
     Timer: value=238 | status=1 | triggered=0 | expired=0

  The display refreshes until **ENTER** is pressed.

**Interrupt mode:**

- The card raises a hardware interrupt each time the timer expires.
- The registered callback prints timing information such as:

  .. code-block:: text

     [INTERRUPT] Timer event received | ID=0 | elapsed=100.03 ms

- The user can observe event frequency and verify timing accuracy.


counter
--------

.. admonition:: Goal

   Initialise, start, and read hardware counters in **polling** or **interrupt** mode.

This sample demonstrates how to configure and operate hardware counters available on ADDI-DATA cards,
for tasks such as event counting, frequency measurement, or pulse detection.


Concept and Purpose
~~~~~~~~~~~~~~~~~~~

The `counter` sample is designed to validate and illustrate the behavior of **hardware counters** on ADDI-DATA cards.
It shows how to configure reload values, counting modes, and options, while supporting both
continuous polling and interrupt-driven operations.

It serves as a **reference implementation** for developers and testers working on:

- Counting external pulses or encoder signals,
- Measuring frequencies or durations,
- Detecting input edges and generating interrupts on threshold events.

Supported cards include (but are not limited to):

- **APCI-1500 / APCIe-1500**
- **APCI-1564 / APCIe-1564**


Execution Workflow
~~~~~~~~~~~~~~~~~~

1. **Device detection and selection**

   The sample detects all installed ADDI-DATA cards and filters those supporting counters
   using `AddiDataUtilsSelectDeviceByCapability()`.

   The user can then select the desired device interactively or provide a filter via `--card` (e.g. `--card=apcie1564`).

2. **Mode selection**

   The program offers two operation modes:

   - **polling** → continuously read and print counter values.
   - **interrupt** → wait for counter overflow or event-driven notifications.

   The mode can be selected through `--mode=` or via an interactive menu.

3. **Counter configuration**

   Once the mode is chosen, the program requests the following parameters:

   - **Counter index** (`--counter`): selects which hardware counter to use.
     Some cards (e.g. APCI-1500) have multiple counters (typically 3).

   - **Reload value** (`--reload`): defines the threshold after which the counter resets or triggers an interrupt.

   - **Counter options**: retrieved interactively via
     `AddiDataUtilsGetTcwOptionsFromUser()`, allowing configuration such as counting direction or gate control.

4. **Initialization and startup**

   The counter is configured and started in three steps:

   - `AddiDataInitCounter()` → apply reload value and mode options.
   - `AddiDataStartCounter()` → enable counting.
   - Optionally, `AddiDataEnableCounterInterrupt()` and `AddiDataSetDeviceInterruptCallback()` if interrupt mode is selected.

   When interrupts are used, the callback `AddiDataUtilsInterruptCallback` prints timestamped events each time
   a counter interrupt occurs (e.g. overflow or external trigger).

5. **Runtime phase**

   - In **polling mode**, the program continuously calls `AddiDataReadCounterStatus()` to display:
     - the current count value,
     - the counter status flag,
     - and the trigger state.

   - In **interrupt mode**, the program remains idle until hardware events trigger an interrupt.

6. **Termination**

   On user input (press **ENTER**), the counter is stopped,
   interrupts are disabled, and all resources are released before the program exits.


Runtime Behavior
~~~~~~~~~~~~~~~~

**Polling mode:**

- The current counter value is printed at regular intervals (100 ms by default).
- Example output:

  .. code-block:: text

     Counter: value=52 | status=1 | triggered=0 | expired=0
     Counter: value=103 | status=1 | triggered=0 | expired=0

- This mode is useful for verifying real-time increments when an external signal (pulse train) is applied.

**Interrupt mode:**

- The card raises a hardware interrupt when the counter reaches its reload value or an event condition.
- The callback prints messages such as:

  .. code-block:: text

     [INTERRUPT] Counter event | ID=1 | elapsed=250.12 ms

- This helps evaluate signal stability and interrupt timing precision.


watchdog
---------

.. admonition:: Goal

   Configure and test the hardware watchdog mechanism available on ADDI-DATA cards.

This sample demonstrates how to initialise, start, trigger, and observe watchdog expiration events.
It validates both **interrupt handling** and **system safety behavior** using the Addi-Pack API.


Concept and Purpose
~~~~~~~~~~~~~~~~~~~

The `watchdog` sample is designed to verify that the **hardware watchdog** correctly monitors
system activity and generates interrupts when it is not regularly triggered.

A watchdog is a **safety timer** that resets or signals the system if it is not "fed" (triggered) within a defined period.
This mechanism is crucial for ensuring system reliability in industrial environments.

This sample shows how to:

- Configure watchdog parameters (timebase, reload value, options),
- Trigger it periodically (software "kick"),
- Detect expiration events via interrupts.

Supported cards include (depending on model and firmware):

- **APCI-1500 / APCIe-1500**
- **APCI-1564 / APCIe-1564**


Execution Workflow
~~~~~~~~~~~~~~~~~~

1. **Device detection and selection**

   The sample enumerates all installed devices and filters those with watchdog capability
   using `AddiDataUtilsSelectDeviceByCapability()`.

   The user can specify a card via `--card=<name>` or select interactively.

2. **Watchdog configuration**

   The user specifies:

   - **Watchdog index** (`--watchdog`): ID of the watchdog to configure.
   - **Timebase** (`--timebase`): `us`, `ms`, or `s` for microsecond, millisecond, or second precision.
   - **Reload value** (`--reload`): defines the duration before expiration (e.g., 500 ms or 10 s).

   Additional options are set through `AddiDataUtilsGetTcwOptionsFromUser()`, allowing configuration
   of continuous cycle or hardware gate modes.

3. **Initialization phase**

   The watchdog is initialized via `AddiDataInitWatchdog()`.
   Device and watchdog interrupts are enabled with:

   - `AddiDataEnableDeviceInterrupts()`
   - `AddiDataEnableWatchdogInterrupt()`

   A default callback (`AddiDataUtilsInterruptCallback`) is registered to report expiration events.

   For visual verification, all digital outputs are set high (`AddiDataWriteAllDigitalOutputs(..., 1)`)
   so that they toggle on watchdog events.

4. **Triggering and monitoring phase**

   The watchdog is started (`AddiDataStartWatchdog()`), then **manually triggered three times**
   by calling `AddiDataTriggerWatchdog()` with a 1-second delay between each call.

   After the third trigger, the sample waits for the watchdog to expire naturally.
   When it does, an interrupt message is displayed, confirming proper expiration handling.

5. **Termination**

   Once expiration is detected or the user presses **ENTER**, the program:

   - Stops the watchdog (`AddiDataStopWatchdog()`),
   - Disables interrupts,
   - Resets all outputs,
   - Frees resources and exits cleanly.


Runtime Behavior
~~~~~~~~~~~~~~~~

**Example console output:**

.. code-block:: text

   Using card: apcie-1564
   Watchdog will be triggered 3 times...
   Triggering Watchdog [1/3]
   Triggering Watchdog [2/3]
   Triggering Watchdog [3/3]
   Waiting for watchdog to expire...
   [INTERRUPT] Watchdog event | ID=0 | elapsed=5000.12 ms
   Watchdog expired. Press ENTER to exit...

The elapsed time shown corresponds to the time between the last trigger and the hardware expiration interrupt.


Card-Specific Notes
~~~~~~~~~~~~~~~~~~~~

Some cards exhibit hardware-dependent behaviors that differ from the
generic TCW (Timer/Counter/Watchdog) model. The following notes help avoid
unexpected results during testing or integration.

**APCI-1564 / APCIe-1564**

- A watchdog interrupt will **not** occur if you simply wait for expiration.
  One of the following must happen **before expiration**:

  - Trigger manually via ``AddiDataTriggerWatchdog()``,
  - **or** set all digital outputs to ``1`` using ``AddiDataWriteAllDigitalOutputs()``.

- Without one of these actions, the watchdog remains **inactive**, meaning
  no expiration and no interrupt.

- Behavior varies depending on activation method:

  - Manual trigger → typically **one interrupt** on expiration.
  - Setting all outputs high → sometimes **two interrupts** (expiration + internal reset).

.. note::

   These quirks are specific to the 1564 family and may vary slightly depending on firmware version.
   It is recommended to always perform either a trigger or output write before waiting for expiration on this card.

**APCI-1500 / APCIe-1500**

- All **three timers share the same timebase** (µs, ms, s).
  Changing the timebase for one timer **updates it for all timers**.

- To avoid unexpected timing mismatches, always configure **the same
  timebase for every enabled timer** on this card.

- ``reload_value`` remains independent per timer — only the **timebase**
  is global.

.. note::

   This constraint applies to both timers and watchdog timing behavior on
   APCI-1500 family cards.

**APCI-2032 / APCIe-2032**

- The watchdog has an attribute "has_expired", which is set to true when the
  watchdog reaches 0.

- PCI cards do not generate an interrupt on expiration;
  instead, the application must poll "has_expired" to detect expiration events.

- APCI-2032 / APCIe-2032 are currently the only ADDI-DATA cards that support
  digital output interrupts (VCC / CC fault detection).

**Interrupt Source Mapping**
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The meaning of ``interrupt_source`` bits is *not universal* across cards.
This means:

- ``interrupt_source = 0x1`` **does not necessarily mean the same thing**
  on APCIx-1500, APCIx-1032 or APCIx-1564.
- Always refer to the **card-specific interrupt handler** and mapping table.

Typical examples by card:

**APCI-1500 / APCIe-1500**

    - ``0x01`` → Port A interrupt
    - ``0x02`` → Port B interrupt
    - ``0x04`` → Timer 1 interrupt
    - ``0x08`` → Timer 2 interrupt
    - ``0x10`` → Timer 3 interrupt
    - ``0x20`` → Watchdog interrupt
    - ``0x40`` / ``0x80`` → Voltage / short-circuit faults

**APCIe-1032**

    - ``0x01`` → DigitalInputEventLogic
    - ``0x04`` → Timer interrupt (PCIe only)

**APCI-1564 / APCIe-1564**

    - ``0x01`` → Digital Input Event Logic
    - ``0x02`` → Digital Output interrupt
    - ``0x04`` → Timer interrupt (per timer bitmask in ``interrupt_mask``)
    - ``0x08`` → Counter interrupt (per counter bitmask in ``interrupt_mask``)

**APCI-2032 / APCIe-2032**

    - ``0x01`` → Digital Output interrupt PCI
    - ``0x02`` → Digital Output interrupt PCIe
    - ``0x04`` → Watchdog interrupt