From 43279813ad717652783fc0a5fa447625e919791d Mon Sep 17 00:00:00 2001
From: Jonathan Hanks <jonathan.hanks@ligo.org>
Date: Tue, 18 Aug 2020 12:32:15 -0500
Subject: [PATCH] Updates to the mbuf_probe check_size test to fix bugs in
 testing.

 * Properly record the expected crc config value
 * Do some output on the first cycle so that we know the test is progressing.
---
 src/drv/mbuf/mbuf_probe/CMakeLists.txt |   5 +-
 src/drv/mbuf/mbuf_probe/check_size.cc  | 320 +++++++++++++++++++++++++
 src/drv/mbuf/mbuf_probe/check_size.hh  |  22 ++
 src/drv/mbuf/mbuf_probe/gap_check.cc   |  48 +---
 src/drv/mbuf/mbuf_probe/mbuf_probe.cc  |  48 +++-
 src/drv/mbuf/mbuf_probe/mbuf_probe.hh  |  67 +++++-
 6 files changed, 450 insertions(+), 60 deletions(-)
 create mode 100644 src/drv/mbuf/mbuf_probe/check_size.cc
 create mode 100644 src/drv/mbuf/mbuf_probe/check_size.hh

diff --git a/src/drv/mbuf/mbuf_probe/CMakeLists.txt b/src/drv/mbuf/mbuf_probe/CMakeLists.txt
index b27b0321b..715c84526 100644
--- a/src/drv/mbuf/mbuf_probe/CMakeLists.txt
+++ b/src/drv/mbuf/mbuf_probe/CMakeLists.txt
@@ -4,12 +4,13 @@ add_executable(mbuf_probe
         mbuf_probe.cc
         mbuf_decoders.cc
         analyze_daq_multi_dc.cc
-        analyze_rmipc.cc)
+        analyze_rmipc.cc
+        check_size.cc)
 target_include_directories(mbuf_probe PRIVATE
         ${CMAKE_CURRENT_SOURCE_DIR}/..
         ${CMAKE_CURRENT_SOURCE_DIR}/../../include
         )
-target_link_libraries(mbuf_probe PUBLIC driver::shmem)
+target_link_libraries(mbuf_probe PUBLIC driver::shmem driver::ini_parsing)
 target_requires_cpp11(mbuf_probe PRIVATE)
 
 install(TARGETS mbuf_probe DESTINATION bin)
\ No newline at end of file
diff --git a/src/drv/mbuf/mbuf_probe/check_size.cc b/src/drv/mbuf/mbuf_probe/check_size.cc
new file mode 100644
index 000000000..01301fed0
--- /dev/null
+++ b/src/drv/mbuf/mbuf_probe/check_size.cc
@@ -0,0 +1,320 @@
+//
+// Created by jonathan.hanks on 8/13/20.
+//
+
+#include "check_size.hh"
+
+#include <algorithm>
+#include <array>
+#include <fstream>
+#include <iostream>
+
+#include "daq_core.h"
+#include "daq_core_defs.h"
+#include "daq_data_types.h"
+
+#include "daqmap.h"
+extern "C" {
+#include "param.h"
+}
+
+namespace check_mbuf_sizes
+{
+    typedef struct ini_file_info
+    {
+        ini_file_info( )
+            : dcu_sizes( ), dcu_config_crc( ), last_dcuid( 0 ), valid( true )
+        {
+            std::fill( dcu_sizes.begin( ), dcu_sizes.end( ), 0 );
+            std::fill( dcu_config_crc.begin( ), dcu_config_crc.end( ), 0 );
+        }
+        ini_file_info( const ini_file_info& ) noexcept = default;
+        ini_file_info& operator=( const ini_file_info& ) noexcept = default;
+
+        std::array< unsigned int, DAQ_TRANSIT_MAX_DCU >  dcu_sizes;
+        std::array< unsigned long, DAQ_TRANSIT_MAX_DCU > dcu_config_crc;
+        int                                              last_dcuid;
+        bool                                             valid;
+    } size_info;
+
+    int
+    channel_callback( char*              channel_name,
+                      struct CHAN_PARAM* params,
+                      void*              user )
+    {
+        auto* info = reinterpret_cast< ini_file_info* >( user );
+        int   chan_size = data_type_size( params->datatype ) *
+            ( params->datarate / DAQ_NUM_DATA_BLOCKS_PER_SECOND );
+        if ( chan_size == 0 )
+        {
+            info->valid = false;
+        }
+        else
+        {
+            info->dcu_sizes[ params->dcuid ] += chan_size;
+        }
+        info->last_dcuid = params->dcuid;
+        return 1;
+    }
+
+    ini_file_info
+    load_master( const std::string& fname )
+    {
+        ini_file_info sysinfo;
+        // File names are specified in `master_config' file
+        std::ifstream mcf( fname );
+
+        for ( std::string line; std::getline( mcf, line ); )
+        {
+            std::string trimmed_line;
+            trimmed_line.reserve( line.size( ) );
+            std::copy_if( line.begin( ),
+                          line.end( ),
+                          std::back_inserter( trimmed_line ),
+                          []( const char ch ) -> bool { return ch != ' '; } );
+            if ( line.front( ) == '#' )
+            {
+                continue;
+            }
+            if ( trimmed_line.empty( ) || trimmed_line.front( ) == '#' )
+            {
+                continue;
+            }
+
+            std::string ext =
+                ( trimmed_line.size( ) > 4
+                      ? trimmed_line.substr( trimmed_line.size( ) - 4 )
+                      : "" );
+            auto testpoint = ( ext == ".par" );
+
+            unsigned long file_crc = 0;
+            sysinfo.valid = true;
+            sysinfo.last_dcuid = -1;
+            int parse_status =
+                ::parseConfigFile( const_cast< char* >( trimmed_line.c_str( ) ),
+                                   &file_crc,
+                                   channel_callback,
+                                   testpoint,
+                                   0,
+                                   reinterpret_cast< void* >( &sysinfo ) );
+            if ( parse_status == 0 || !sysinfo.valid || sysinfo.last_dcuid < 0 )
+            {
+                std::cerr << "Unable to process " << trimmed_line
+                          << " either the file is invalid, bad data types, or "
+                             "other error ("
+                          << sysinfo.valid << ", " << sysinfo.last_dcuid
+                          << ")\n";
+                if ( sysinfo.last_dcuid >= 0 )
+                {
+                    sysinfo.dcu_sizes[ sysinfo.last_dcuid ] = 0;
+                }
+            }
+            else
+            {
+                sysinfo.dcu_config_crc[ sysinfo.last_dcuid ] = file_crc;
+            }
+        }
+        return sysinfo;
+    }
+
+    int
+    check_size_rmipc( volatile void*      buffer,
+                      std::size_t         buffer_size,
+                      const ::ConfigOpts& options )
+    {
+        if ( options.analysis_type != MBUF_RMIPC ||
+             buffer_size != 64 * 1024 * 1024 )
+        {
+            return 0;
+        }
+
+        load_master( options.ini_file_fname );
+
+        unsigned int prev_config_crc{ 0 };
+        unsigned int prev_data_size{ 0 };
+
+        auto header = reinterpret_cast< const volatile rmIpcStr* >( buffer );
+
+        ini_file_info dcu_reference;
+        bool          noted_crc_change{ false };
+
+        bool have_inis = false;
+        if ( !options.ini_file_fname.empty( ) )
+        {
+            dcu_reference = load_master( options.ini_file_fname );
+            have_inis = true;
+        }
+
+        bool first_iteration = true;
+        while ( true )
+        {
+            wait_until_changed< const unsigned int, 5000 >( &header->cycle,
+                                                            header->cycle );
+
+            auto cur_cycle = header->cycle;
+            auto dcuid = header->dcuId;
+
+            if ( !first_iteration )
+            {
+                if ( have_inis )
+                {
+                    if ( header->crc != dcu_reference.dcu_config_crc[ dcuid ] &&
+                         !noted_crc_change )
+                    {
+                        std::cout
+                            << "config change on dcu " << dcuid << " from "
+                            << dcu_reference.dcu_config_crc[ dcuid ] << " to "
+                            << header->crc << "\n";
+                        noted_crc_change = true;
+                    }
+                    if ( dcu_reference.dcu_sizes[ dcuid ] !=
+                         header->dataBlockSize )
+                    {
+                        std::cout << "wrong data size on " << dcuid
+                                  << " expected "
+                                  << dcu_reference.dcu_sizes[ dcuid ] << " got "
+                                  << header->dataBlockSize << "\n";
+                    }
+                }
+                else
+                {
+                    if ( prev_config_crc != header->crc && !noted_crc_change )
+                    {
+                        std::cout << "config change on dcu " << dcuid
+                                  << " from " << prev_config_crc << " to "
+                                  << header->crc << "\n";
+                        noted_crc_change;
+                    }
+                    if ( prev_data_size != header->dataBlockSize )
+                    {
+                        std::cout << "data size change on dcu " << dcuid
+                                  << " from " << prev_data_size << " to "
+                                  << header->dataBlockSize << "\n";
+                    }
+                }
+            }
+            prev_config_crc = header->crc;
+            prev_data_size = header->dataBlockSize;
+
+            if ( first_iteration )
+            {
+                std::cout
+                    << "Completed the first iteration of the check_size test"
+                    << std::endl;
+                first_iteration = false;
+            }
+        }
+        return 0;
+    }
+
+    int
+    check_size_multi( volatile void*      buffer,
+                      std::size_t         buffer_size,
+                      const ::ConfigOpts& options )
+    {
+        if ( !buffer || buffer_size < DAQD_MIN_SHMEM_BUFFER_SIZE ||
+             buffer_size > DAQD_MAX_SHMEM_BUFFER_SIZE ||
+             options.analysis_type != MBUF_DAQ_MULTI_DC )
+        {
+            return 0;
+        }
+
+        load_master( options.ini_file_fname );
+
+        std::array< unsigned int, DAQ_TRANSIT_MAX_DCU > prev_config_crc{};
+        std::array< unsigned int, DAQ_TRANSIT_MAX_DCU > prev_data_size{};
+
+        std::fill( prev_config_crc.begin( ), prev_config_crc.end( ), 0 );
+        std::fill( prev_data_size.begin( ), prev_data_size.end( ), 0 );
+
+        auto multi_header =
+            reinterpret_cast< const volatile daq_multi_cycle_data_t* >(
+                buffer );
+
+        ini_file_info                           dcu_reference;
+        std::array< bool, DAQ_TRANSIT_MAX_DCU > noted_crc_change{};
+        std::fill( noted_crc_change.begin( ), noted_crc_change.end( ), false );
+        bool have_inis = false;
+        if ( !options.ini_file_fname.empty( ) )
+        {
+            dcu_reference = load_master( options.ini_file_fname );
+            have_inis = true;
+        }
+
+        bool first_iteration = true;
+        while ( true )
+        {
+            wait_for_time_change< 5000 >( multi_header->header );
+
+            auto cur_cycle = multi_header->header.curCycle;
+            auto cycle_data_size = multi_header->header.cycleDataSize;
+
+            auto cur_cylce_block =
+                reinterpret_cast< const volatile daq_multi_dcu_data_t* >(
+                    &multi_header->dataBlock[ 0 ] +
+                    (cycle_data_size)*cur_cycle );
+            auto total_models = cur_cylce_block->header.dcuTotalModels;
+
+            for ( auto i = 0; i < total_models; ++i )
+            {
+                auto& cur_header = cur_cylce_block->header.dcuheader[ i ];
+                auto  dcuid = cur_header.dcuId;
+
+                if ( !first_iteration )
+                {
+                    if ( have_inis )
+                    {
+                        if ( cur_header.fileCrc !=
+                                 dcu_reference.dcu_config_crc[ dcuid ] &&
+                             !noted_crc_change[ dcuid ] )
+                        {
+                            std::cout << "config change on dcu " << dcuid
+                                      << " from "
+                                      << dcu_reference.dcu_config_crc[ dcuid ]
+                                      << " to " << cur_header.fileCrc << "\n";
+                            noted_crc_change[ dcuid ] = true;
+                        }
+                        if ( dcu_reference.dcu_sizes[ dcuid ] !=
+                             cur_header.dataBlockSize )
+                        {
+                            std::cout << "wrong data size on " << dcuid
+                                      << " expected "
+                                      << dcu_reference.dcu_sizes[ dcuid ]
+                                      << " got " << cur_header.dataBlockSize
+                                      << "\n";
+                        }
+                    }
+                    else
+                    {
+                        if ( prev_config_crc[ dcuid ] != cur_header.fileCrc &&
+                             !noted_crc_change[ dcuid ] )
+                        {
+                            std::cout << "config change on dcu " << dcuid
+                                      << " from " << prev_config_crc[ dcuid ]
+                                      << " to " << cur_header.fileCrc << "\n";
+                            noted_crc_change[ dcuid ];
+                        }
+                        if ( prev_data_size[ dcuid ] !=
+                             cur_header.dataBlockSize )
+                        {
+                            std::cout << "data size change on dcu " << dcuid
+                                      << " from " << prev_data_size[ dcuid ]
+                                      << " to " << cur_header.dataBlockSize
+                                      << "\n";
+                        }
+                    }
+                }
+                prev_config_crc[ dcuid ] = cur_header.fileCrc;
+                prev_data_size[ dcuid ] = cur_header.dataBlockSize;
+            }
+            if ( first_iteration )
+            {
+                std::cout
+                    << "Completed the first iteration of the check_size test"
+                    << std::endl;
+                first_iteration = false;
+            }
+        }
+        return 0;
+    }
+} // namespace check_mbuf_sizes
\ No newline at end of file
diff --git a/src/drv/mbuf/mbuf_probe/check_size.hh b/src/drv/mbuf/mbuf_probe/check_size.hh
new file mode 100644
index 000000000..221135bca
--- /dev/null
+++ b/src/drv/mbuf/mbuf_probe/check_size.hh
@@ -0,0 +1,22 @@
+//
+// Created by jonathan.hanks on 8/13/20.
+//
+
+#ifndef DAQD_TRUNK_CHECK_SIZE_HH
+#define DAQD_TRUNK_CHECK_SIZE_HH
+
+#include <cstddef>
+
+#include "mbuf_probe.hh"
+
+namespace check_mbuf_sizes
+{
+    int check_size_rmipc( volatile void*    buffer,
+                          std::size_t       buffer_Size,
+                          const ConfigOpts& options );
+    int check_size_multi( volatile void*    buffer,
+                          std::size_t       buffer_Size,
+                          const ConfigOpts& options );
+} // namespace check_mbuf_sizes
+
+#endif // DAQD_TRUNK_CHECK_SIZE_HH
diff --git a/src/drv/mbuf/mbuf_probe/gap_check.cc b/src/drv/mbuf/mbuf_probe/gap_check.cc
index 2e1fce44f..3d3479590 100644
--- a/src/drv/mbuf/mbuf_probe/gap_check.cc
+++ b/src/drv/mbuf/mbuf_probe/gap_check.cc
@@ -2,58 +2,12 @@
 // Created by jonathan.hanks on 3/13/20.
 //
 #include "gap_check.hh"
-#include <chrono>
-#include <daq_core.h>
-
-#include <sys/time.h>
-#include <unistd.h>
 
 #include <iostream>
+#include "mbuf_probe.hh"
 
 namespace check_gap
 {
-    static std::int64_t
-    time_now_ms( )
-    {
-        timeval tv;
-        gettimeofday( &tv, 0 );
-        return static_cast< std::uint64_t >( tv.tv_sec * 1000 +
-                                             tv.tv_usec / 1000 );
-    }
-
-    struct cycle_sample_t
-    {
-        unsigned int  cycle;
-        unsigned int  gps;
-        unsigned int  gps_nano;
-        unsigned int  gps_cycle;
-        std::uint64_t time_ms;
-    };
-
-    cycle_sample_t
-    wait_for_time_change( volatile daq_multi_cycle_header_t& header )
-    {
-        cycle_sample_t results;
-        unsigned int   cur_cycle = header.curCycle;
-        while ( header.curCycle == cur_cycle )
-        {
-            usleep( 2000 );
-        }
-        results.time_ms = time_now_ms( );
-        results.cycle = header.curCycle;
-
-        unsigned int   stride = header.cycleDataSize;
-        volatile char* buffer_data =
-            reinterpret_cast< volatile char* >( &header ) +
-            sizeof( daq_multi_cycle_header_t );
-        volatile daq_dc_data_t* daq =
-            reinterpret_cast< volatile daq_dc_data_t* >(
-                buffer_data + stride * header.curCycle );
-        results.gps = daq->header.dcuheader[ 0 ].timeSec;
-        results.gps_nano = daq->header.dcuheader[ 0 ].timeNSec;
-        results.gps_cycle = daq->header.dcuheader[ 0 ].cycle;
-        return results;
-    }
 
     int
     check_gaps( volatile void* buffer, std::size_t buffer_size )
diff --git a/src/drv/mbuf/mbuf_probe/mbuf_probe.cc b/src/drv/mbuf/mbuf_probe/mbuf_probe.cc
index 4e81684ac..e1879d559 100644
--- a/src/drv/mbuf/mbuf_probe/mbuf_probe.cc
+++ b/src/drv/mbuf/mbuf_probe/mbuf_probe.cc
@@ -28,6 +28,10 @@
 #include "analyze_daq_multi_dc.hh"
 #include "analyze_rmipc.hh"
 #include "gap_check.hh"
+#include "check_size.hh"
+
+const int OK = 0;
+const int ERROR = 1;
 
 void
 usage( const char* progname )
@@ -42,6 +46,8 @@ usage( const char* progname )
     std::cout << "\tanalyze - continually read the mbuf and do some analysis\n";
     std::cout << "\tgap_check - continually scan the buffers looking for "
                  "gaps/jumps in the data stream\n";
+    std::cout << "\tcheck_sizes - check the given buffer's data segment sizes "
+                 "against the expected size as calculated in the ini files.";
     std::cout << "\t-b <buffer name> - The name of the buffer to act on\n";
     std::cout
         << "\t-m <buffer size in MB> - The size of the buffer in megabytes\n";
@@ -49,6 +55,8 @@ usage( const char* progname )
                  "multiple of 4k)\n";
     std::cout << "\t-o <filename> - Output file for the copy operation "
                  "(defaults to probe_out.bin)\n";
+    std::cout << "\t-i <filename> - Path to the master ini file (only used "
+                 "for the check_size action)\n";
     std::cout
         << "\t--struct <type> - Type of structure to analyze [rmIpcStr]\n";
     std::cout << "\t--dcu <dcuid> - Optional DCU id used to select a dcu for "
@@ -79,6 +87,7 @@ parse_options( int argc, char* argv[] )
     command_lookup.insert( std::make_pair( "delete", DELETE ) );
     command_lookup.insert( std::make_pair( "analyze", ANALYZE ) );
     command_lookup.insert( std::make_pair( "gap_check", GAP_CHECK ) );
+    command_lookup.insert( std::make_pair( "check_size", CHECK_SIZE ) );
 
     std::map< std::string, MBufStructures > struct_lookup;
     struct_lookup.insert( std::make_pair( "rmIpcStr", MBUF_RMIPC ) );
@@ -157,6 +166,17 @@ parse_options( int argc, char* argv[] )
             is >> offset;
             opts.decoder = DataDecoder( offset, format_spec );
         }
+        else if ( arg == "-i" )
+        {
+            if ( args.empty( ) )
+            {
+                opts.set_error(
+                    "You must specify a ini file path when using -i" );
+                return opts;
+            }
+            opts.ini_file_fname = args.front( );
+            args.pop_front( );
+        }
         else if ( arg == "--struct" )
         {
             if ( args.empty( ) )
@@ -325,9 +345,6 @@ list_shmem_segments( )
 int
 handle_analyze( const ConfigOpts& opts )
 {
-    const int OK = 0;
-    const int ERROR = 1;
-
     volatile void* buffer =
         shmem_open_segment( opts.buffer_name.c_str( ), opts.buffer_size );
     switch ( opts.analysis_type )
@@ -348,9 +365,6 @@ handle_analyze( const ConfigOpts& opts )
 int
 handle_gap_check( const ConfigOpts& opts )
 {
-    const int OK = 0;
-    const int ERROR = 1;
-
     if ( opts.analysis_type != MBUF_DAQ_MULTI_DC )
     {
         std::cout << "daq_multi_cycle is the only mode that the crc_check is "
@@ -363,6 +377,26 @@ handle_gap_check( const ConfigOpts& opts )
     return OK;
 }
 
+int
+handle_check_size( const ConfigOpts& opts )
+{
+    volatile void* buffer =
+        shmem_open_segment( opts.buffer_name.c_str( ), opts.buffer_size );
+    switch ( opts.analysis_type )
+    {
+    case MBUF_RMIPC:
+        check_mbuf_sizes::check_size_rmipc( buffer, opts.buffer_size, opts );
+        break;
+    case MBUF_DAQ_MULTI_DC:
+        check_mbuf_sizes::check_size_multi( buffer, opts.buffer_size, opts );
+        break;
+    default:
+        std::cout << "Unknown analysis type for the check size commandn";
+        return ERROR;
+    }
+    return OK;
+}
+
 int
 main( int argc, char* argv[] )
 {
@@ -408,6 +442,8 @@ main( int argc, char* argv[] )
         return handle_analyze( opts );
     case GAP_CHECK:
         return handle_gap_check( opts );
+    case CHECK_SIZE:
+        return handle_check_size( opts );
     }
     return 0;
 }
diff --git a/src/drv/mbuf/mbuf_probe/mbuf_probe.hh b/src/drv/mbuf/mbuf_probe/mbuf_probe.hh
index 2b1e64c71..9609af1d6 100644
--- a/src/drv/mbuf/mbuf_probe/mbuf_probe.hh
+++ b/src/drv/mbuf/mbuf_probe/mbuf_probe.hh
@@ -5,12 +5,17 @@
 #ifndef DAQD_MBUF_PROBE_HH
 #define DAQD_MBUF_PROBE_HH
 
+#include <chrono>
 #include <cstddef>
 #include <string>
 #include <vector>
+#include <type_traits>
 
+#include <sys/time.h>
 #include <unistd.h>
 
+#include <daq_core.h>
+
 #include "mbuf_decoders.hh"
 
 enum MBufCommands
@@ -22,6 +27,7 @@ enum MBufCommands
     DELETE,
     ANALYZE,
     GAP_CHECK,
+    CHECK_SIZE,
 };
 
 enum MBufStructures
@@ -35,8 +41,9 @@ struct ConfigOpts
 {
     ConfigOpts( )
         : action( INVALID ), buffer_size( 0 ), buffer_name( "" ),
-          output_fname( "probe_out.bin" ), error_msg( "" ), dcu_id( -1 ),
-          decoder( ), analysis_type( MBUF_INVALID )
+          output_fname( "probe_out.bin" ), error_msg( "" ),
+          ini_file_fname( "" ), dcu_id( -1 ), decoder( ),
+          analysis_type( MBUF_INVALID )
     {
     }
     MBufCommands   action;
@@ -44,6 +51,7 @@ struct ConfigOpts
     std::string    buffer_name;
     std::string    output_fname;
     std::string    error_msg;
+    std::string    ini_file_fname;
     int            dcu_id;
     DataDecoder    decoder;
     MBufStructures analysis_type;
@@ -115,6 +123,13 @@ struct ConfigOpts
                            "as well as daq_multi_cycle" );
             }
             break;
+        case CHECK_SIZE:
+            if ( buffer_name.empty( ) || buffer_size == 0 )
+            {
+                set_error( "To check sizes you must specify a buffer, size, "
+                           "and optionally an ini file" );
+            }
+            break;
         case INVALID:
         default:
             set_error( "Please select a valid action" );
@@ -134,17 +149,59 @@ struct ConfigOpts
     }
 };
 
-template < typename T >
+template < typename T, int CheckPeriodUS = 250 >
 unsigned int
 wait_until_changed( volatile T* counter, T old_counter )
 {
-    T cur_cycle = *counter;
+    typename std::remove_const< T >::type cur_cycle = *counter;
     do
     {
-        usleep( 250 );
+        usleep( CheckPeriodUS );
         cur_cycle = *counter;
     } while ( cur_cycle == old_counter );
     return cur_cycle;
 }
 
+inline std::int64_t
+time_now_ms( )
+{
+    timeval tv;
+    gettimeofday( &tv, 0 );
+    return static_cast< std::uint64_t >( tv.tv_sec * 1000 + tv.tv_usec / 1000 );
+}
+
+struct cycle_sample_t
+{
+    unsigned int  cycle;
+    unsigned int  gps;
+    unsigned int  gps_nano;
+    unsigned int  gps_cycle;
+    std::uint64_t time_ms;
+};
+
+template < int CheckPeriodUS = 2000 >
+cycle_sample_t
+wait_for_time_change( const volatile daq_multi_cycle_header_t& header )
+{
+    cycle_sample_t results;
+    unsigned int   cur_cycle = header.curCycle;
+    while ( header.curCycle == cur_cycle )
+    {
+        usleep( CheckPeriodUS );
+    }
+    results.time_ms = time_now_ms( );
+    results.cycle = header.curCycle;
+
+    unsigned int         stride = header.cycleDataSize;
+    const volatile char* buffer_data =
+        reinterpret_cast< const volatile char* >( &header ) +
+        sizeof( daq_multi_cycle_header_t );
+    const volatile daq_dc_data_t* daq =
+        reinterpret_cast< const volatile daq_dc_data_t* >(
+            buffer_data + stride * header.curCycle );
+    results.gps = daq->header.dcuheader[ 0 ].timeSec;
+    results.gps_nano = daq->header.dcuheader[ 0 ].timeNSec;
+    results.gps_cycle = daq->header.dcuheader[ 0 ].cycle;
+    return results;
+}
 #endif // DAQD_MBUF_PROBE_HH
-- 
GitLab