From 35718747bdb5eaa4d18a4364057bec9af9919393 Mon Sep 17 00:00:00 2001 From: Pat Thoyts Date: Fri, 30 Jun 2023 17:14:55 +0100 Subject: [PATCH] Support reading property set items and use monitor layout. 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 | 10 ++-- opc_app.h | 26 ++++++++-- opc_util.c | 13 +++-- server.c | 129 ++++++++++++++++++++++++++++++++++++++++++++----- wdf-linux | 2 +- wdf_origins.h | 30 ++++++++++++ wdf_props.cpp | 71 +++++++++++++++++++++++++++ wdf_props.h | 17 +++++++ wdf_utils.c | 46 +++++++++++++++++- wdf_utils.h | 14 +++++- 10 files changed, 328 insertions(+), 30 deletions(-) create mode 100644 wdf_origins.h create mode 100644 wdf_props.cpp create mode 100644 wdf_props.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 775312a..362f49e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/opc_app.h b/opc_app.h index bc40944..107edf8 100644 --- a/opc_app.h +++ b/opc_app.h @@ -7,18 +7,33 @@ #include #include #include +#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 diff --git a/opc_util.c b/opc_util.c index 43083bd..e1e75ca 100644 --- a/opc_util.c +++ b/opc_util.c @@ -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, §ion); 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, §ion); 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); } - diff --git a/server.c b/server.c index e5648b9..d1ac9ee 100644 --- a/server.c +++ b/server.c @@ -14,6 +14,7 @@ #include #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)) { diff --git a/wdf-linux b/wdf-linux index 4581e0f..fb10eff 160000 --- 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 index 0000000..ed3826b --- /dev/null +++ b/wdf_origins.h @@ -0,0 +1,30 @@ +// Copyright (c) 2023 Renishaw plc. All rights reserved. +// + +#ifndef _wdf_origins_h_INCLUDE +#define _wdf_origins_h_INCLUDE + +#include + +#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 index 0000000..008cde5 --- /dev/null +++ b/wdf_props.cpp @@ -0,0 +1,71 @@ +#include "wdf_props.h" +#include +#include +#include +#include +#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(&item->val.lVal), &UA_TYPES[UA_TYPES_INT32]); + break; + case PSET_TYPE_FLOAT: + UA_Variant_setScalar(valuePtr, const_cast(&item->val.fltVal), &UA_TYPES[UA_TYPES_FLOAT]); + break; + case PSET_TYPE_DOUBLE: + UA_Variant_setScalar(valuePtr, const_cast(&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 index 0000000..94f2e58 --- /dev/null +++ b/wdf_props.h @@ -0,0 +1,17 @@ +#ifndef _wdf_props_h_INCLUDE +#define _wdf_props_h_INCLUDE + +#include +#include + +#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 diff --git a/wdf_utils.c b/wdf_utils.c index 1789d9c..5ef6d59 100644 --- a/wdf_utils.c +++ b/wdf_utils.c @@ -22,6 +22,7 @@ #include #include #include +#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, §ion); + 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; +} diff --git a/wdf_utils.h b/wdf_utils.h index 90741cb..90fa73d 100644 --- a/wdf_utils.h +++ b/wdf_utils.h @@ -4,12 +4,24 @@ #ifndef _wdf_utils_h_INCLUDE #define _wdf_utils_h_INCLUDE +#include +#include #include +#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 -- 2.23.0