Support reading property set items and use monitor layout.
authorPat Thoyts <pat.thoyts@gmail.com>
Fri, 30 Jun 2023 16:14:55 +0000 (17:14 +0100)
committerPat Thoyts <pat.thoyts@gmail.com>
Fri, 30 Jun 2023 16:14:55 +0000 (17:14 +0100)
Added switch for the monitor layout (default) or an improved layout for
the node tree.
Added Instrument Type, Exposure Time and data origins (time and flags).

CMakeLists.txt
opc_app.h
opc_util.c
server.c
wdf-linux
wdf_origins.h [new file with mode: 0644]
wdf_props.cpp [new file with mode: 0644]
wdf_props.h [new file with mode: 0644]
wdf_utils.c
wdf_utils.h

index 775312afb6e6aa8f069a5572e692a78600458f3d..362f49e8fe41194c90785de94fbee9cfe39bc749 100644 (file)
@@ -29,14 +29,18 @@ project(opc_server
 )
 
 set (TARGET ${PROJECT_NAME})
-set (SOURCES server.c opc_util.c opc_app.h wdf_utils.c wdf_utils.h)
+set (SOURCES server.c opc_util.c opc_app.h wdf_utils.c wdf_utils.h wdf_origins.h wdf_props.cpp wdf_props.h)
 
 add_subdirectory(wdf-linux/wdflib wdflib)
 
 option (APP_USE_OPEN62541_SUBMODULE "Use the open62541 submodule" ON)
 if (APP_USE_OPEN62541_SUBMODULE)
+    if (MSVC)
+        set (UA_MSVC_FORCE_STATIC_CRT OFF CACHE BOOL "Allow linking to msvcrt")
+    endif()
     set (UA_ENABLE_DISCOVERY ON CACHE BOOL "Enable UA Discovery")
-    set (UA_ENABLE_PUBSUB ON CACHE BOOL "Enable PubSub protocol support")
+    #set (UA_ENABLE_DISCOVERY_MULTICAST ON CACHE BOOL "Enable UA Discovery multicast")
+    #set (UA_ENABLE_PUBSUB ON CACHE BOOL "Enable PubSub protocol support")
     add_subdirectory(open62541)  # target open62541::open62541
 else()
     set (CMAKE_FIND_PACKAGE_PREFER_CONFIG ON)  # prefer open62541's own config file
@@ -44,7 +48,7 @@ else()
 endif()
 
 add_executable(${TARGET} ${SOURCES})
-target_compile_features(${TARGET} PUBLIC c_std_11)
+target_compile_features(${TARGET} PUBLIC c_std_11 cxx_std_17)
 target_include_directories(${TARGET} PUBLIC open62541::open62541 ${wdflib_SOURCE_DIR})
 target_link_libraries(${TARGET} PUBLIC open62541::open62541 wdflib)
 
index bc40944558746862f0af8430e6c79858601cbbb8..107edf8d2db403b56db0a9bd4342897ae2b4d3cb 100644 (file)
--- a/opc_app.h
+++ b/opc_app.h
@@ -7,18 +7,33 @@
 #include <open62541/server.h>
 #include <open62541/client.h>
 #include <wdf.h>
+#include "wdf_origins.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum nodeset_t {
+    NodeSet_Monitor,
+    NodeSet_Custom
+};
 
 typedef struct {
     UA_Server *server;
     UA_Client *regClient;
-    UA_Int64 regId;
     UA_Int16 ns;
+    enum nodeset_t nodeset;
     const char *regUri;
     int filehandle;
     WdfHeader hdr;
+    uint32_t exposureTime; // in milliseconds
+    UA_Variant instrumentType;
     uint64_t spectrumIndex;
+    int64_t spectrumTime;
     float *xlist;
     float *ilist;
+    wdf_origin_t *origins;
+    int originCount;
 } App;
 
 UA_StatusCode add_scalar(App *app, UA_NodeId parentNode,
@@ -29,11 +44,14 @@ UA_StatusCode add_object_node(App *app, UA_NodeId parentNode,
     UA_NodeId *newNodeId);
 UA_StatusCode add_vector(App *app, UA_NodeId parentNode,
     const char *fullname, const char *displayName,
-    size_t len, UA_Float *value, const UA_DataType *valueType);
+    size_t len, void *value, const UA_DataType *valueType);
 
 UA_StatusCode update_scalar(App *app, UA_NodeId nodeId, void *valuePtr, const UA_DataType *typePtr);
 UA_StatusCode update_float_vector(App *app, UA_NodeId nodeId, size_t len, const float *vector);
-UA_StatusCode update_xlist(App *app);
-UA_StatusCode update_ilist(App *app);
+UA_StatusCode update_xlist(App *app, const char *nodename);
+UA_StatusCode update_ilist(App *app, const char *nodename);
 
+#ifdef __cplusplus
+}
+#endif
 #endif // _opc_app_h_INCLUDE
index 43083bd252e9f3c6dbdf06cee97275e922f096fb..e1e75ca5e0b61f9a23864807b6542d4d30faea7e 100644 (file)
@@ -30,7 +30,7 @@ UA_StatusCode add_scalar(App *app, UA_NodeId parentNode, const char *fullname, c
     return addVariableNode(app->server, app->ns, parentNode, fullname, displayName, attr);
 }
 
-UA_StatusCode add_vector(App *app, UA_NodeId parentNode, const char *fullname, const char *displayName, size_t len, UA_Float *value, const UA_DataType *valueType)
+UA_StatusCode add_vector(App *app, UA_NodeId parentNode, const char *fullname, const char *displayName, size_t len, void *value, const UA_DataType *valueType)
 {
     UA_Int32 dims = (UA_Int32)len;
     UA_VariableAttributes attr = UA_VariableAttributes_default;
@@ -77,29 +77,28 @@ UA_StatusCode update_scalar(App *app, UA_NodeId nodeId, void *valuePtr, const UA
     return UA_Server_writeValue(app->server, nodeId, value);
 }
 
-UA_StatusCode update_xlist(App *app)
+UA_StatusCode update_xlist(App *app, const char *nodename)
 {
     UA_StatusCode status = UA_STATUSCODE_BADNOTFOUND;
     WdfBlock section = {0};
     uint64_t pos = wdf_find_section(app->filehandle, WDF_BLOCKID_XLIST, WDF_BLOCKID_ANY, &section);
     if (pos != (uint64_t)-1)
     {
-        ssize_t count = wdf_read_xlist(app->filehandle, app->hdr.npoints, app->xlist);
+        int64_t count = wdf_read_xlist(app->filehandle, app->hdr.npoints, app->xlist);
         if (count > 0)
         {
-            UA_NodeId node = UA_NODEID_STRING_ALLOC(app->ns, "renishaw.spd.spectrum.xlist");
+            UA_NodeId node = UA_NODEID_STRING_ALLOC(app->ns, nodename);
             status = update_float_vector(app, node, app->hdr.npoints, app->xlist);
         }
     }
     return status;
 }
 
-UA_StatusCode update_ilist(App *app)
+UA_StatusCode update_ilist(App *app, const char *nodename)
 {
     WdfBlock section = {0};
     uint64_t pos = wdf_find_section(app->filehandle, WDF_BLOCKID_DATA, WDF_BLOCKID_ANY, &section);
     wdf_read_spectrum(app->filehandle, pos, app->hdr.npoints, app->spectrumIndex, app->ilist);
-    UA_NodeId node = UA_NODEID_STRING_ALLOC(app->ns, "renishaw.spd.spectrum.ilist");
+    UA_NodeId node = UA_NODEID_STRING_ALLOC(app->ns, nodename);
     return update_float_vector(app, node, app->hdr.npoints, app->ilist);
 }
-
index e5648b9e613205c4ba0ef7a524cd7618650d3c42..d1ac9eef4cb8c1e36d47d2ec342d44f97228542b 100644 (file)
--- a/server.c
+++ b/server.c
@@ -14,6 +14,7 @@
 #include <wdf.h>
 #include "opc_app.h"
 #include "wdf_utils.h"
+#include "wdf_props.h"
 
 #define DISCOVERY_SERVER_ENDPOINT "opc.tcp://localhost:4840"
 
@@ -34,12 +35,6 @@ static void on_interrupt(int signo)
 }
 #endif
 
-static int usage(int exitCode)
-{
-    fprintf(stderr, "usage: opc_server ?-port NUM? ?-register? ?-interval MS? FILENAME\n");
-    exit(exitCode);
-}
-
 // Register the server with LDS
 // periodic server register after 10 Minutes, delay first register for 500ms
 UA_StatusCode registerServer(App *app)
@@ -49,28 +44,56 @@ UA_StatusCode registerServer(App *app)
     if (UA_StatusCode_isGood(status))
     {
         status = UA_Server_addPeriodicServerRegisterCallback(app->server,
-            app->regClient, app->regUri, 10 * 60 * 1000, 500, &app->regId);
+            app->regClient, app->regUri, 10 * 60 * 1000, 500, NULL);
     }
     return status;
 }
 
 static void getSpectrumCallback(UA_Server *server, void *clientData)
 {
+    enum {INDEX, TIME, FLAGS, XLIST, ILIST };
+    struct map_t { const char *names[2]; };
+    struct map_t ID[6] = {
+        { "Index", "renishaw.spd.spectrum.index" },
+        { "Time",  "renishaw.spd.spectrum.time" },
+        { "Flags", "renishaw.spd.spectrum.flags" },
+        { "XList", "renishaw.spd.spectrum.xlist" },
+        { "YList", "renishaw.spd.spectrum.ilist" }
+    };
+
     App *app = (App *)clientData;
 
     if (app->spectrumIndex == 0)
-        update_xlist(app);
-    update_ilist(app);
+        update_xlist(app, ID[XLIST].names[app->nodeset]);
+    update_ilist(app, ID[ILIST].names[app->nodeset]);
 
-    UA_NodeId node = UA_NODEID_STRING_ALLOC(app->ns, "renishaw.spd.spectrum.index");
+    UA_NodeId node = UA_NODEID_STRING_ALLOC(app->ns, ID[INDEX].names[app->nodeset]);
     update_scalar(app, node, &app->spectrumIndex, &UA_TYPES[UA_TYPES_INT64]);
 
+    for (int n = 0; n < app->originCount; ++n)
+    {
+        if (app->origins[n].type == WdfDataType_Time)
+        {
+            UA_NodeId nodeTime = UA_NODEID_STRING_ALLOC(app->ns, ID[TIME].names[app->nodeset]);
+            origin_value_t value = {0};
+            wdf_get_origin_values(app->filehandle, &app->hdr, &app->origins[n], app->spectrumIndex, app->spectrumIndex, &value);
+            update_scalar(app, nodeTime, &value.time, &UA_TYPES[UA_TYPES_DATETIME]);
+        }
+        else if (app->origins[n].type == WdfDataType_Flags)
+        {
+            UA_NodeId node = UA_NODEID_STRING_ALLOC(app->ns, ID[FLAGS].names[app->nodeset]);
+            origin_value_t value = { 0 };
+            wdf_get_origin_values(app->filehandle, &app->hdr, &app->origins[n], app->spectrumIndex, app->spectrumIndex, &value);
+            update_scalar(app, node, &value.flags, &UA_TYPES[UA_TYPES_UINT64]);
+        }
+    }
+
     ++app->spectrumIndex;
     if (app->spectrumIndex > app->hdr.ncollected)
         app->spectrumIndex = 0;
 }
 
-static UA_StatusCode add_nodes(App *app)
+static UA_StatusCode add_nodes_custom(App *app)
 {
     // Add new object node for our data as Renishaw
     UA_NodeId mainNode, spdNode;
@@ -85,12 +108,18 @@ static UA_StatusCode add_nodes(App *app)
         status = add_scalar(app, spdNode, "renishaw.spd.npoints", "Points", &app->hdr.npoints, &UA_TYPES[UA_TYPES_INT32]);
     if (UA_StatusCode_isGood(status))
         status = add_scalar(app, spdNode, "renishaw.spd.laserwavenum", "Laser Wavenumber", &app->hdr.laserwavenum, &UA_TYPES[UA_TYPES_FLOAT]);
+    if (UA_StatusCode_isGood(status))
+        status = add_scalar(app, spdNode, "renishaw.spd.exposuretime", "Exposure Time", &app->exposureTime, &UA_TYPES[UA_TYPES_UINT32]);
     if (UA_StatusCode_isGood(status))
     {
         UA_NodeId spectrumNode;
         status = add_object_node(app, spdNode, "renishaw.spd.spectrum", "Spectrum", &spectrumNode);
         if (UA_StatusCode_isGood(status))
             status = add_scalar(app, spectrumNode, "renishaw.spd.spectrum.index", "Index", &app->spectrumIndex, &UA_TYPES[UA_TYPES_INT64]);
+        if (UA_StatusCode_isGood(status))
+            status = add_scalar(app, spectrumNode, "renishaw.spd.spectrum.time", "Time", &app->spectrumTime, &UA_TYPES[UA_TYPES_DATETIME]);
+        if (UA_StatusCode_isGood(status))
+            status = add_scalar(app, spectrumNode, "renishaw.spd.spectrum.flags", "Flags", &app->spectrumIndex, &UA_TYPES[UA_TYPES_UINT64]);
         if (UA_StatusCode_isGood(status))
             status = add_vector(app, spectrumNode, "renishaw.spd.spectrum.xlist", "XList", app->hdr.npoints, app->xlist, &UA_TYPES[UA_TYPES_FLOAT]);
         if (UA_StatusCode_isGood(status))
@@ -99,6 +128,60 @@ static UA_StatusCode add_nodes(App *app)
     return status;
 }
 
+// Create a node tree that matches the Renishaw Monitor application
+static UA_StatusCode add_nodes_monitor(App *app)
+{
+    // Add new object node for our data as Renishaw
+    UA_NodeId mainNode;
+    UA_StatusCode status = add_object_node(app, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), "Renishaw", "Renishaw", &mainNode);
+    if (UA_StatusCode_isGood(status))
+    {
+        UA_NodeId subNode;
+        float datavalues[2] = { 0, 0 };
+        UA_String name1 = UA_STRING("Intensity At Point 700");
+        UA_String name2 = UA_STRING("Signal To Baseline From 500 To 600");
+        UA_String names[2] = { name1, name2 };
+        status = add_object_node(app, mainNode, "Analysis", "Analysis", &subNode);
+        if (UA_StatusCode_isGood(status))
+            status = add_vector(app, subNode, "Data Value", "Data Value", 2, datavalues, &UA_TYPES[UA_TYPES_FLOAT]);
+        if (UA_StatusCode_isGood(status))
+            status = add_vector(app, subNode, "ResultName", "ResultName", 2, names, &UA_TYPES[UA_TYPES_STRING]);
+    }
+    if (UA_StatusCode_isGood(status))
+    {
+        UA_NodeId subNode;
+        status = add_object_node(app, mainNode, "Info", "Info", &subNode);
+        if (UA_StatusCode_isGood(status))
+            status = add_scalar(app, subNode, "Laser Wavenumber", "Laser Wavenumber", &app->hdr.laserwavenum, &UA_TYPES[UA_TYPES_FLOAT]);
+        if (UA_StatusCode_isGood(status))
+            status = add_scalar(app, subNode, "Exposure Time", "Exposure Time", &app->exposureTime, &UA_TYPES[UA_TYPES_UINT32]);
+        if (UA_StatusCode_isGood(status))
+            status = add_scalar(app, subNode, "Instrument Type", "Instrument Type", app->instrumentType.data, &UA_TYPES[UA_TYPES_STRING]);
+    }
+    if (UA_StatusCode_isGood(status))
+    {
+        UA_NodeId subNode;
+        status = add_object_node(app, mainNode, "Spectrum", "Spectrum", &subNode);
+        if (UA_StatusCode_isGood(status))
+            status = add_scalar(app, subNode, "Index", "Index", &app->spectrumIndex, &UA_TYPES[UA_TYPES_INT64]);
+        if (UA_StatusCode_isGood(status))
+            status = add_scalar(app, subNode, "Time", "Time", &app->spectrumTime, &UA_TYPES[UA_TYPES_DATETIME]);
+        if (UA_StatusCode_isGood(status))
+            status = add_scalar(app, subNode, "Flags", "Flags", &app->spectrumIndex, &UA_TYPES[UA_TYPES_UINT64]);
+        if (UA_StatusCode_isGood(status))
+            status = add_vector(app, subNode, "XList", "XList", app->hdr.npoints, app->xlist, &UA_TYPES[UA_TYPES_FLOAT]);
+        if (UA_StatusCode_isGood(status))
+            status = add_vector(app, subNode, "IList", "IList", app->hdr.npoints, app->ilist, &UA_TYPES[UA_TYPES_FLOAT]);
+    }
+    return status;
+}
+
+static int usage(int exitCode)
+{
+    fprintf(stderr, "usage: opc_server ?-port NUM? ?-register? ?-interval MS? ?-custom? FILENAME\n");
+    exit(exitCode);
+}
+
 int main(int argc, char** argv)
 {
     App application = {0};
@@ -120,6 +203,10 @@ int main(int argc, char** argv)
             {
                 app->regUri = DISCOVERY_SERVER_ENDPOINT;
             }
+            else if (strncmp("-custom", argv[n], 7) == 0)
+            {
+                app->nodeset = NodeSet_Custom;
+            }
             else if (strncmp("-interval", argv[n], 9) == 0)
             {
                 interval = strtoul(argv[n+1], NULL, 0);
@@ -148,6 +235,12 @@ int main(int argc, char** argv)
         fprintf(stderr, "failed to read wdf file\n");
         exit(1);
     }
+    app->originCount = wdf_get_origins(app->filehandle, &app->hdr, &app->origins);
+    if (app->origins == NULL || app->originCount == 0)
+    {
+        fprintf(stderr, "failed to get data origins\n");
+        exit(1);
+    }
 
     size_t len = sizeof(float) * app->hdr.npoints;
     app->xlist = (float *)UA_malloc(len);
@@ -156,6 +249,13 @@ int main(int argc, char** argv)
     memset(app->ilist, 0, len);
     app->spectrumIndex = 0;
 
+    WdfBlock propsBlock;
+    uint64_t pos = wdf_find_section(app->filehandle, WDF_BLOCKID_MEASUREMENT, WDF_BLOCKID_ANY, &propsBlock);
+    app->exposureTime = wdf_get_scan_exposuretime(filename, pos);
+    pos = wdf_find_section(app->filehandle, WDF_BLOCKID_INSTRUMENT, WDF_BLOCKID_ANY, &propsBlock);
+    UA_Variant_init(&app->instrumentType);
+    wdf_get_property(filename, pos, "Instrument type", &app->instrumentType);
+
     app->server = UA_Server_new();
     UA_ServerConfig *config = UA_Server_getConfig(app->server);
     UA_StatusCode status = UA_ServerConfig_setMinimal(config, port, NULL);
@@ -169,7 +269,12 @@ int main(int argc, char** argv)
 #endif
 
     if (UA_StatusCode_isGood(status))
-        status = add_nodes(app);
+    {
+        if (app->nodeset == NodeSet_Custom)
+            status = add_nodes_custom(app);
+        else
+            status = add_nodes_monitor(app);
+    }
 
     if (UA_StatusCode_isGood(status))
     {
index 4581e0fb267ccb22d5983c69d00ff1e5be008897..fb10eff367ad0a3d4b64f6d363080dc27d44b0b6 160000 (submodule)
--- a/wdf-linux
+++ b/wdf-linux
@@ -1 +1 @@
-Subproject commit 4581e0fb267ccb22d5983c69d00ff1e5be008897
+Subproject commit fb10eff367ad0a3d4b64f6d363080dc27d44b0b6
diff --git a/wdf_origins.h b/wdf_origins.h
new file mode 100644 (file)
index 0000000..ed3826b
--- /dev/null
@@ -0,0 +1,30 @@
+// Copyright (c) 2023 Renishaw plc. All rights reserved.
+//
+
+#ifndef _wdf_origins_h_INCLUDE
+#define _wdf_origins_h_INCLUDE
+
+#include <wdf.h>
+
+#pragma pack(push,1)
+typedef struct {
+    union {
+        struct {
+            uint32_t type : 31;
+            uint32_t alternate : 1;
+        };
+        uint32_t datatype;
+    };
+    uint32_t units;
+    char label[16];
+    uint64_t pos;  // offset of the start of the origin data
+} wdf_origin_t;
+#pragma pack(pop)
+
+typedef union origin_value_type {
+    double value;
+    uint64_t flags;
+    uint64_t time;
+} origin_value_t;
+
+#endif // _wdf_origins_h_INCLUDE;
diff --git a/wdf_props.cpp b/wdf_props.cpp
new file mode 100644 (file)
index 0000000..008cde5
--- /dev/null
@@ -0,0 +1,71 @@
+#include "wdf_props.h"
+#include <fstream>
+#include <wdf.h>
+#include <pset.h>
+#include <open62541/types_generated.h>
+#include "wdf_utils.h"
+
+constexpr int WDF_BLOCK_HDR_SIZE = 16;
+
+// works for WXDA properties - get by name.
+UA_StatusCode wdf_get_property(const char *filename, uint64_t pos, const char *keyname, UA_Variant *valuePtr)
+{
+    UA_StatusCode status = UA_STATUSCODE_BAD;
+    std::ifstream file(filename, std::ios::in | std::ios::binary);
+    file.seekg(pos + WDF_BLOCK_HDR_SIZE, std::ios::beg);
+    uint32_t len = Pset::IsPset(file);
+    if (len)
+    {
+        const auto itemlist = Pset::ParseStream(file, len);
+        Pset pset(itemlist, nullptr);
+        if (pset.exists(keyname))
+        {
+            status = UA_STATUSCODE_GOOD;
+            const auto item = pset.get_item(keyname);
+            switch (item->hdr.type)
+            {
+            case PSET_TYPE_INT:
+                UA_Variant_setScalar(valuePtr, const_cast<int32_t*>(&item->val.lVal), &UA_TYPES[UA_TYPES_INT32]);
+                break;
+            case PSET_TYPE_FLOAT:
+                UA_Variant_setScalar(valuePtr, const_cast<float*>(&item->val.fltVal), &UA_TYPES[UA_TYPES_FLOAT]);
+                break;
+            case PSET_TYPE_DOUBLE:
+                UA_Variant_setScalar(valuePtr, const_cast<double*>(&item->val.dblVal), &UA_TYPES[UA_TYPES_DOUBLE]);
+                break;
+            case PSET_TYPE_STRING:
+            {
+                UA_String str = { 0 };
+                str.length = item->length;
+                str.data = (UA_Byte*)UA_malloc(item->length);
+                memcpy(str.data, item->val.strVal, item->length);
+                UA_Variant_setScalarCopy(valuePtr, &str, &UA_TYPES[UA_TYPES_STRING]);
+                break;
+            }
+            default:
+                status = UA_STATUSCODE_BAD;
+            }
+        }
+    }
+    return status;
+}
+
+uint32_t wdf_get_scan_exposuretime(const char* filename, uint64_t pos)
+{
+    uint32_t value = 0;
+    std::ifstream file(filename, std::ios::in | std::ios::binary);
+    file.seekg(pos + WDF_BLOCK_HDR_SIZE, std::ios::beg);
+    uint32_t len = Pset::IsPset(file);
+    if (len)
+    {
+        const auto itemlist = Pset::ParseStream(file, len);
+        Pset pset(itemlist, nullptr);
+        const auto& niitem= pset.get_item("NamedItems");
+        Pset nameditems(niitem->val.childVal, &pset);
+        const auto& scanitem = nameditems.get_item("Scan");
+        Pset scan(scanitem->val.childVal, &nameditems);
+        const auto extime = scan.get_item("Exposure Time");
+        value = extime->val.ulVal;
+    }
+    return value;
+}
diff --git a/wdf_props.h b/wdf_props.h
new file mode 100644 (file)
index 0000000..94f2e58
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef _wdf_props_h_INCLUDE
+#define _wdf_props_h_INCLUDE
+
+#include <stdint.h>
+#include <open62541/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+uint32_t wdf_get_property(const char *filename, uint64_t pos, const char *keyname, UA_Variant *value);
+uint32_t wdf_get_scan_exposuretime(const char* filename, uint64_t pos);
+
+#ifdef __cplusplus
+}
+#endif
+#endif // _wdf_props_h_INCLUDE
\ No newline at end of file
index 1789d9c66ed4aeea5a6a96d2f9ff0b0c63db39fd..5ef6d5928018d2ef869ae8fc37157a521fac895e 100644 (file)
@@ -22,6 +22,7 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <stdio.h>
+#include "wdf_utils.h"
 #include "opc_app.h"
 
 int wdf_open(const char *filename, WdfHeader *wdfPtr)
@@ -74,7 +75,7 @@ uint64_t wdf_find_section(int fd, uint32_t id, uint32_t uid, WdfBlock *blockPtr)
 /// @param[in] length - number of elements to be read
 /// @param[out] xlist - pointer to array to be filled
 /// @return -1 on error or the number of bytes read into the spectrum
-ssize_t wdf_read_xlist(int fd, uint32_t length, float *xlist)
+int64_t wdf_read_xlist(int fd, uint32_t length, float *xlist)
 {
     ssize_t r = -1;
     WdfBlock section = {0};
@@ -92,7 +93,7 @@ ssize_t wdf_read_xlist(int fd, uint32_t length, float *xlist)
         if (r > 0)
             r = _read(fd, xlist, sizeof(float) * length);
     }
-    return r;
+    return (int64_t)r;
 }
 
 int wdf_read_spectrum(int fd, uint64_t base, uint32_t length, uint64_t spectrum, float *ilist)
@@ -102,3 +103,44 @@ int wdf_read_spectrum(int fd, uint64_t base, uint32_t length, uint64_t spectrum,
         r = _read(fd, ilist, sizeof(float)*length);
     return r;
 }
+
+// Get the data origin description headers and returns the number of origins or 0
+// caller needs to free the origins array.
+int wdf_get_origins(int fd, WdfHeader *hdr, wdf_origin_t **originsPtr)
+{
+    WdfBlock section = {0};
+    uint64_t pos = wdf_find_section(fd, WDF_BLOCKID_ORIGIN, WDF_BLOCKID_ANY, &section);
+    uint32_t originCount = 0;
+    uint64_t originSize = hdr->nspectra * sizeof(uint64_t);
+    int r = _read(fd, &originCount, sizeof(originCount));
+    wdf_origin_t *origins = (wdf_origin_t *)malloc(sizeof(wdf_origin_t) * originCount);
+    if (origins == NULL)
+    {
+        perror("malloc");
+        originCount = 0;
+    }
+    else
+    {
+        for (uint32_t n = 0; n < originCount; ++n)
+        {
+            _read(fd, &origins[n].datatype, sizeof(uint32_t));
+            _read(fd, &origins[n].units, sizeof(uint32_t));
+            _read(fd, &origins[n].label, 16);
+            origins[n].pos = _lseeki64(fd, 0, SEEK_CUR);
+            _lseeki64(fd, originSize, SEEK_CUR);
+        }
+        *originsPtr = origins;
+    }
+    return originCount;
+}
+
+int wdf_get_origin_values(int fd, WdfHeader *hdr, wdf_origin_t *origin, uint64_t start, uint64_t end, origin_value_t *dataPtr)
+{
+    uint64_t offset = sizeof(double) * start;
+    uint32_t byteCount = (uint32_t)(((end - start) + 1) * sizeof(double));
+    int64_t pos = _lseeki64(fd, origin->pos + offset, SEEK_SET);
+    int r = _read(fd, dataPtr, byteCount);
+    if (r != byteCount)
+        fprintf(stderr, "error: misread in wdf_get_orign_values\n");
+    return r;
+}
index 90741cbe64227bcbc1c9d29d869d3c1b5ebac430..90fa73d7266e777d34c5c6d9ea7f0e4bcffcab50 100644 (file)
@@ -4,12 +4,24 @@
 #ifndef _wdf_utils_h_INCLUDE
 #define _wdf_utils_h_INCLUDE
 
+#include <sys/types.h>
+#include <stddef.h>
 #include <wdf.h>
+#include "wdf_origins.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
 
 int wdf_open(const char *filename, WdfHeader *wdfPtr);
 int wdf_close(int fd);
 uint64_t wdf_find_section(int fd, uint32_t id, uint32_t uid, WdfBlock *blockPtr);
-ssize_t wdf_read_xlist(int fd, uint32_t length, float *xlist);
+int64_t wdf_read_xlist(int fd, uint32_t length, float *xlist);
 int wdf_read_spectrum(int fd, uint64_t base, uint32_t length, uint64_t spectrum, float *ilist);
+int wdf_get_origins(int fd, WdfHeader *hdr, wdf_origin_t **originsPtr);
+int wdf_get_origin_values(int fd, WdfHeader *hdr, wdf_origin_t *origin, uint64_t start, uint64_t end, origin_value_t *dataPtr);
 
+#ifdef __cplusplus
+}
+#endif
 #endif // _wdf_utils_h_INCLUDE