From 3c4b00f12f596f897405cfafce04879e271f0fef Mon Sep 17 00:00:00 2001 From: Charles Mita Date: Mon, 26 Mar 2018 18:03:12 +0100 Subject: [PATCH] Initial code commit. Little to no error reporting, still fairly brittle and not very well tested. --- .gitignore | 7 + LICENSE | 24 ++ Makefile | 18 ++ src/file.c | 668 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/file.h | 53 ++++ src/plugin.c | 138 +++++++++++ src/plugin.h | 55 +++++ 7 files changed, 963 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 src/file.c create mode 100644 src/file.h create mode 100644 src/plugin.c create mode 100644 src/plugin.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..377abd3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +*.swp +*.*~ +*.bak +*.tmp + +*.o +*.so diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d642ae5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +Copyright (c) 2018, Diamond Light Source +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d5c24f4 --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +BUILD_DIR ?= ./build +SRC_DIR = ./src +INC_DIR = $(SRC_DIR) + +CC=h5cc +CFLAGS=-Wall -g -O2 -fpic -I$(INC_DIR) + +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c + mkdir -p $(BUILD_DIR) + $(CC) $(CFLAGS) -c $< -o $@ + +plugin: $(BUILD_DIR)/plugin.o $(BUILD_DIR)/file.o + mkdir -p $(BUILD_DIR) + $(CC) $(CFLAGS) -shared $^ -o $(BUILD_DIR)/plugin.so + +.PHONY: clean +clean: + rm -r $(BUILD_DIR) diff --git a/src/file.c b/src/file.c new file mode 100644 index 0000000..0073b90 --- /dev/null +++ b/src/file.c @@ -0,0 +1,668 @@ +/* + * Copyright (c) 2018 Diamond Light Source Ltd. + * Author: Charles Mita + */ + + +#include +#include +#include +#include +#include +#include "file.h" + +hid_t h5_int_type_from_width(const int width) { + if (width == sizeof(char)) { + return H5T_NATIVE_SCHAR; + } else if (width == sizeof(short)) { + return H5T_NATIVE_SHORT; + } else if (width == sizeof(int)) { + return H5T_NATIVE_INT; + } else if (width == sizeof(long)) { + return H5T_NATIVE_LONG; + } else if (width == sizeof(long long)) { + return H5T_NATIVE_LLONG; + } else { + /* TODO: error */ + return -1; + } +} + +void clear_det_visit_objects(struct det_visit_objects_t *objects) { + if (objects->nxdata) { + H5Oclose(objects->nxdata); + objects->nxdata = 0; + } + if (objects->nxdetector) { + H5Oclose(objects->nxdetector); + objects->nxdetector = 0; + } +} + + +void free_dataset_properties(struct dataset_properties_t *p) { + if (p->dims) { + free(p->dims); + p->dims = NULL; + } + p->ndims = 0; +} + + +void free_nxs_data_description(struct data_description_t *desc) { + if (desc->extra) free(desc->extra); /* should just be NULL */ + desc->extra = NULL; +} + +void free_eiger_data_description(struct data_description_t *desc) { + if (!desc->extra) return; + struct eiger_data_description_t *extra = desc->extra; + if (extra->block_sizes) free(extra->block_sizes); + free(extra); + desc->extra = NULL; +} + + +double scale_from_units(const char* unit_string) { + if (strcasecmp("m", unit_string) == 0 || + strcasecmp("metres", unit_string) == 0 || + strcasecmp("meters", unit_string) == 0) { + return 1.; + } else if (strcasecmp("cm", unit_string) == 0 || + strcasecmp("centimetres", unit_string) == 0 || + strcasecmp("centimeters", unit_string) == 0) { + return 0.01; + } else if (strcasecmp("mm", unit_string) == 0 || + strcasecmp("millimetres", unit_string) == 0 || + strcasecmp("millimeters", unit_string) == 0) { + return 0.001; + } else if (strcasecmp("um", unit_string) == 0 || + strcasecmp("microns", unit_string) == 0 || + strcasecmp("micrometres", unit_string) == 0 || + strcasecmp("micrometers", unit_string) == 0) { + return 0.000001; + } else { + fprintf(stderr, "Unrecognised unit string %s", unit_string); + return 1; + } +} + +int get_nxs_dataset_dims(const struct data_description_t *desc, struct dataset_properties_t *properties) { + hid_t g_id, ds_id, s_id, t_id; + int retval = 0; + int ndims = 0; + int width = 0; + hsize_t *dims = NULL; + g_id = desc->data_group_id;; + + ds_id = H5Dopen2(g_id, "data", H5P_DEFAULT); + if (ds_id <= 0) { + retval = -1; + return retval; + } + + t_id = H5Dget_type(ds_id); + if (t_id <= 0) { + retval = -1; + goto close_dataset; + } + + width = H5Tget_size(t_id); + if (width <= 0) { + retval = -1; + goto close_type; + } + + s_id = H5Dget_space(ds_id); + if (s_id <= 0) { + retval = -1; + goto close_dataset; + } + + ndims = H5Sget_simple_extent_ndims(s_id); + if (ndims <= 0) { + retval = -1; + goto close_space; + } + + dims = malloc(ndims * sizeof(hsize_t)); + if (!dims) { + retval = -1; + goto close_space; + } + if (H5Sget_simple_extent_dims(s_id, dims, NULL) < 0) { + retval = -1; + free(dims); + goto close_space; + } + + properties->ndims = ndims; + properties->dims = dims; + properties->data_width = width; + +close_space: + H5Sclose(s_id); +close_type: + H5Tclose(t_id); +close_dataset: + H5Dclose(ds_id); + return retval; +} + +int get_frame(hid_t g_id, const char* name, hsize_t *frame_idx, hsize_t *frame_size, int data_width, void *buffer) { + int retval = 0; + herr_t err = 0; + hid_t ds_id, s_id, ms_id, t_id; + ds_id = H5Dopen2(g_id, name, H5P_DEFAULT); + if (ds_id <= 0) { + retval = -1; + return retval; + } + s_id = H5Dget_space(ds_id); + if (s_id <= 0) { + retval = -1; + goto close_dataset; + } + err = H5Sselect_hyperslab(s_id, H5S_SELECT_SET, frame_idx, NULL, frame_size, NULL); + if (err < 0) { + retval = -1; + goto close_space; + } + ms_id = H5Screate_simple(3, frame_size, frame_size); + if (ms_id < 0) { + retval = -1; + goto close_space; + } + + t_id = h5_int_type_from_width(data_width); + if (t_id < 0) { + retval = -1; + goto close_mspace; + } + err = H5Dread(ds_id, t_id, ms_id, s_id, H5P_DEFAULT, buffer); + if (err < 0) { + retval = -1; + goto close_mspace; + } + +close_mspace: + H5Sclose(ms_id); +close_space: + H5Sclose(s_id); +close_dataset: + H5Dclose(ds_id); + return retval; +} + +int get_nxs_frame( + const struct data_description_t *desc, + const struct dataset_properties_t *ds_prop, + int n, + int data_width, + void *buffer) { + /* detector data are the two inner most indices */ + /* n is indexed from one - hdf5 slices start at zero */ + /* TODO: handle ndims > 3 and select appropriately */ + int retval = 0; + hsize_t frame_idx[3] = {n - 1, 0, 0}; + hsize_t frame_size[3] = {1, ds_prop->dims[1], ds_prop->dims[2]}; + retval = get_frame(desc->data_group_id, "data", frame_idx, frame_size, data_width, buffer); + return retval; +} + + +int get_dectris_eiger_frame( + const struct data_description_t *desc, + const struct dataset_properties_t *ds_prop, + int n, + int data_width, + void *buffer) { + + int retval = 0; + int block, frame_count, idx; + struct eiger_data_description_t *eiger_desc = desc->extra; + char data_name[16] = {0}; + hsize_t frame_idx[3] = {0, 0, 0}; + hsize_t frame_size[3] = {1, ds_prop->dims[1], ds_prop->dims[2]}; + + /* determine the relevant data block */ + frame_count = 0; + block = 0; + while ((frame_count += eiger_desc->block_sizes[block]) < (n-1)) block++; + idx = n - (frame_count - eiger_desc->block_sizes[block]) - 1; /* index in current block */ + printf("n: %d -> Block: %d, idx: %d\n", n, block, idx); + frame_idx[0] = idx; + sprintf(data_name, "data_%06d", block + 1); + retval = get_frame(desc->data_group_id, data_name, frame_idx, frame_size, data_width, buffer); + return retval; +} + + +int get_dectris_eiger_dataset_dims(const struct data_description_t *desc, struct dataset_properties_t *properties) { + int retval = 0; + int n_datas = 0; + int n = 0; + int data_width = 0; + int ndims = 3; + char ds_name[16] = {0}; /* 12 chars in "data_xxxxxx\0" */ + int *frame_counts = NULL; + hsize_t *dims = malloc(3 * sizeof(hsize_t)); + if (!dims) { + retval = -1; + return retval; + } + memset(dims, 0, sizeof(hsize_t) * ndims); + + /* datasets are "data_%06d % n" - need to determine how many of these there are and what the ranges are */ + + sprintf(ds_name, "data_%06d", n_datas + 1); + while (H5Lexists(desc->data_group_id, ds_name, H5P_DEFAULT) > 0) { + sprintf(ds_name, "data_%06d", ++n_datas + 1); + } + + frame_counts = malloc(n_datas * sizeof(*frame_counts)); + + for (n = 0; n < n_datas; n++) { + hid_t ds_id, t_id, s_id; + hsize_t block_dims[3] = {0}; + sprintf(ds_name, "data_%06d", n + 1); + ds_id = H5Dopen2(desc->data_group_id, ds_name, H5P_DEFAULT); + if (ds_id < 0) { + retval = -1; + break; + } + t_id = H5Dget_type(ds_id); + if (t_id < 0) { + retval = -1; + goto close_dataset; + } + s_id = H5Dget_space(ds_id); + if (s_id < 0) { + retval = -1; + goto close_type; + } + + data_width = H5Tget_size(t_id); + if (data_width <= 0) { + retval = -1; + goto close_space; + } + + if (H5Sget_simple_extent_dims(s_id, block_dims, NULL) < 0) { + retval = -1; + goto close_space; + } + + dims[1] = block_dims[1]; + dims[2] = block_dims[2]; + + dims[0] += block_dims[0]; + frame_counts[n] = block_dims[0]; + +close_space: + H5Sclose(s_id); +close_type: + H5Tclose(t_id); +close_dataset: + H5Dclose(ds_id); + } + + if (retval < 0) { + free(dims); + free(frame_counts); + } else { + properties->ndims = ndims; + properties->dims = dims; + properties->data_width = data_width; + ((struct eiger_data_description_t *) desc->extra)->n_data_blocks = n_datas; + ((struct eiger_data_description_t *) desc->extra)->block_sizes = frame_counts; + } + return retval; +} + + +int read_pixel_info(hid_t g_id, const char *path, double *size) { + /* + * NXdetector allows pixel size to be an array (for varied pixel size), + * however XDS only allows for a single value. + * TODO: handle array case (return first value maybe?) + */ + + /* read the scalar dataset value and scale according to the unit in the attribute */ + /* returned value is in metres */ + int retval = 0; + herr_t err = 0; + hid_t ds_id; + double value = 0; + ds_id = H5Dopen2(g_id,path, H5P_DEFAULT); + if (ds_id < 0) { + retval = -1; + return retval;; + } + + err = H5Dread(ds_id, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, &value); + if (err < 0) { + retval = -1; + goto close_dataset; + } + + if (H5Aexists(ds_id, "units") > 0) { + /* string may be variable length */ + hid_t a_id, t_id, mt_id; + void *str_buffer = NULL; + int str_size = -1; + double scale = 1; + a_id = H5Aopen(ds_id, "units", H5P_DEFAULT); + if (a_id < 0) { + retval = -1; + goto close_dataset; + } + + t_id = H5Aget_type(a_id); + if (t_id < 0) { + retval = -1; + goto close_attribute; + } + /* TODO: handle multiple strings in attribute (just detect and error) */ + if (H5Tis_variable_str(t_id) > 0) { + str_size = -1; + str_buffer = malloc(sizeof(char*)); + } else { + str_size = H5Tget_size(t_id); + /* do not assume room has been left for null-byte in fixed length string */ + str_buffer = malloc(str_size + 1); + } + if (str_buffer == NULL) { + retval = -1; + goto close_datatype; + } + mt_id = H5Tcopy(H5T_C_S1); + if (mt_id < 0) { + retval = -1; + goto free_string; + } + err = H5Tset_size(mt_id, str_size == -1 ? H5T_VARIABLE : str_size); + if (err < 0) { + retval = -1; + goto close_mem_datatype; + } + + err = H5Aread(a_id, mt_id, str_buffer); + if (err < 0) { + retval = -1; + goto close_mem_datatype; + } + /* ensure last byte is null */ + if (str_size > 0) ((char*) str_buffer)[str_size] = '\0'; + + scale = scale_from_units(str_size == -1 ? *(char**)str_buffer : (char*)str_buffer); + value *= scale; + + if (str_size == -1) { + /* we have to create this dataspace just to indicate we want + * to clear the entire vlen dataset (all 1 element - good job HDF5) + */ + hsize_t dims[1] = {1}; + hid_t s_id = H5Screate_simple(1, dims, NULL); + H5Sselect_all(s_id); + H5Dvlen_reclaim(mt_id, s_id, H5P_DEFAULT, str_buffer); + H5Sclose(s_id); + } +close_mem_datatype: + H5Tclose(mt_id); +free_string: + free(str_buffer); +close_datatype: + H5Tclose(t_id); +close_attribute: + H5Aclose(a_id); + } + + *size = value; + +close_dataset: + H5Dclose(ds_id); + + return retval; +} + + +int get_nxs_pixel_info(const struct data_description_t *desc, double *x_size, double *y_size) { + if (read_pixel_info(desc->det_group_id, "x_pixel_size", x_size) < 0) { + return -1; + } + if (read_pixel_info(desc->det_group_id, "y_pixel_size", y_size) < 0) { + return -1; + } + return 0; +} + + +int get_dectris_eiger_pixel_info(const struct data_description_t *desc, double *x_size, double *y_size) { + if (read_pixel_info(desc->det_group_id, "detectorSpecific/x_pixel_size", x_size) < 0) { + return -1; + } + if (read_pixel_info(desc->det_group_id, "detectorSpecific/y_pixel_size", y_size) < 0) { + return -1; + } + return 0; +} + + +int get_nxs_pixel_mask(const struct data_description_t *desc, int *buffer) { + int retval = 0; + hid_t ds_id; + herr_t err = 0; + + ds_id = H5Dopen2(desc->det_group_id, "pixel_mask", H5P_DEFAULT); + if (ds_id < 0) { + retval = -1; + return retval; + } + + err = H5Dread(ds_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, buffer); + if (err < 0) { + retval = -1; + goto close_dataset; + } + +close_dataset: + H5Dclose(ds_id); + return retval; +} + + +int get_dectris_eiger_pixel_mask(const struct data_description_t *desc, int *buffer) { + int retval = 0; + hid_t ds_id; + herr_t err = 0; + + ds_id = H5Dopen2(desc->det_group_id, "detectorSpecific/pixel_mask", H5P_DEFAULT); + if (ds_id < 0) { + retval = -1; + return retval; + } + + err = H5Dread(ds_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, buffer); + if (err < 0) { + retval = -1; + goto close_dataset; + } + +close_dataset: + H5Dclose(ds_id); + return retval; + +} + + +herr_t det_visit_callback(hid_t root_id, const char *name, const H5O_info_t *info, void *op_data) { + struct det_visit_objects_t *output_data = op_data; + hid_t g_id; + herr_t retval = 0; + if (info->type != H5O_TYPE_GROUP) return 0; + g_id = H5Oopen(root_id, name, H5P_DEFAULT); + + /* check for an "NX_class" attribute */ + { + char* buffer = NULL; + hid_t a_id, t_id; + H5A_info_t a_info; + if (H5Aexists(g_id, "NX_class") <= 0) { + retval = 0; + goto close_group; + } + a_id = H5Aopen(g_id, "NX_class", H5P_DEFAULT); + if (a_id <= 0) { + /* TODO: error trace */ + retval = -1; + goto close_group; + } + if (H5Aget_info(a_id, &a_info) < 0) { + /* TODO: handle error */ + retval = -1; + goto close_attr; + } + t_id = H5Tcreate(H5T_STRING, a_info.data_size); + if (t_id <= 0) { + retval = -1; + goto close_attr; + } + buffer = malloc(a_info.data_size + 1); + if (buffer == NULL) { + /* TODO: OOM error */ + retval =- 1; + goto close_type; + } + + if (H5Aread(a_id, t_id, buffer) < 0) { + /*TODO: handle*/ + retval = -1; + goto free_buffer; + } + + /* at least one file has been seen where the NX_class attribute was not null terminated + * and extraneous bytes where being read by strcmp - set the end byte to null + */ + buffer[a_info.data_size] = '\0'; + if (strcmp("NXdata", buffer) == 0) { + hid_t out_id = H5Gopen(root_id, name, H5P_DEFAULT); + output_data->nxdata = out_id; + } else if (strcmp("NXdetector", buffer) == 0) { + hid_t out_id = H5Gopen(root_id, name, H5P_DEFAULT); + output_data->nxdetector = out_id; + } + +free_buffer: + free(buffer); +close_type: + H5Tclose(t_id); +close_attr: + H5Aclose(a_id); + } + +close_group: + if (H5Gclose(g_id) < 0) { + /* TODO: error trace */ + retval = -1; + } + return retval; +} + + +int fill_data_descriptor(struct data_description_t *data_desc, struct det_visit_objects_t *visit_result) { + int retval = 0; + data_desc->det_group_id = visit_result->nxdetector; + + /* determine the pixel information location */ + if (H5Lexists(data_desc->det_group_id, "x_pixel_size", H5P_DEFAULT) > 0 && + H5Lexists(data_desc->det_group_id, "y_pixel_size", H5P_DEFAULT)) { + data_desc->get_pixel_properties = &get_nxs_pixel_info; + } else if (H5Lexists(data_desc->det_group_id, "detectorSpecific", H5P_DEFAULT) > 0 && + H5Lexists(data_desc->det_group_id, "detectorSpecifc/x_pixel_size", H5P_DEFAULT) > 0 && + H5Lexists(data_desc->det_group_id, "detectorSpecifc/y_pixel_size", H5P_DEFAULT) > 0) { + data_desc->get_pixel_properties = &get_dectris_eiger_pixel_info; + } else { + data_desc->get_pixel_properties = NULL; + retval = -1; + } + + /* determine pixel mask location */ + if (H5Lexists(data_desc->det_group_id, "pixel_mask", H5P_DEFAULT) > 0) { + data_desc->get_pixel_mask = &get_nxs_pixel_mask; + } else if (H5Lexists(data_desc->det_group_id, "detectorSpecific", H5P_DEFAULT) > 0 && + H5Lexists(data_desc->det_group_id, "detectorSpecific/pixel_mask", H5P_DEFAULT) > 0) { + data_desc->get_pixel_mask = &get_dectris_eiger_pixel_mask; + } else { + data_desc->get_pixel_mask = NULL; + retval = -1; + } + + /* determine where the data is stored and what strategy to use */ + /* we select the "dectris-eiger" strategy if both are valid due to + * potential confusion with the sizes of a virtual dataset (and possible + * failure opening it if the library version is not up to date) + */ + if (H5Lexists(visit_result->nxdetector, "data_000001", H5P_DEFAULT) > 0) { + data_desc->data_group_id = visit_result->nxdetector; + data_desc->get_data_properties = &get_dectris_eiger_dataset_dims; + data_desc->get_data_frame = &get_dectris_eiger_frame; + } else if (H5Lexists(visit_result->nxdetector, "data", H5P_DEFAULT) > 0) { + data_desc->data_group_id = visit_result->nxdetector; + data_desc->get_data_properties = &get_nxs_dataset_dims; + data_desc->get_data_frame = &get_nxs_frame; + } else if (H5Lexists(visit_result->nxdata, "data_000001", H5P_DEFAULT) > 0) { + data_desc->data_group_id = visit_result->nxdata; + data_desc->get_data_properties = &get_dectris_eiger_dataset_dims; + data_desc->get_data_frame = &get_dectris_eiger_frame; + } else if (H5Lexists(visit_result->nxdata, "data", H5P_DEFAULT) > 0) { + data_desc->data_group_id = visit_result->nxdata; + data_desc->get_data_properties = &get_nxs_dataset_dims; + data_desc->get_data_frame = &get_nxs_frame; + } else { + data_desc->data_group_id = 0; + data_desc->get_data_properties = NULL; + data_desc->get_data_frame = NULL; + retval = -1; + } + + if (data_desc->get_data_properties == &get_dectris_eiger_dataset_dims) { + /* setup the "extra eiger info" struct */ + struct eiger_data_description_t *eiger_desc = malloc(sizeof(*eiger_desc)); + memset(eiger_desc, 0, sizeof(*eiger_desc)); + if (!eiger_desc) { + retval = -1; + return retval; + } + data_desc->extra = eiger_desc; + data_desc->free_extra = free_eiger_data_description; + } else { + data_desc->free_extra = free_nxs_data_description; + } + + return retval; +} + + +int extract_detector_info( + const hid_t fid, + struct data_description_t *data_desc, + struct dataset_properties_t *dataset_prop) { + herr_t err = 0; + struct det_visit_objects_t objects = {0}; + err = H5Ovisit(fid, H5_INDEX_NAME, H5_ITER_INC, &det_visit_callback, &objects); + if (err < 0) { + clear_det_visit_objects(&objects); + return -1; + } + if (objects.nxdata == 0) { + fprintf(stderr, "WARNING: Could not locate an NXdata entry\n"); + } + if (objects.nxdetector == 0) { + fprintf(stderr, "WARNING: Could not locate an NXdetector entry\n"); + } + + fill_data_descriptor(data_desc, &objects); + data_desc->get_data_properties(data_desc, dataset_prop); + return 0; +} diff --git a/src/file.h b/src/file.h new file mode 100644 index 0000000..e53e1cc --- /dev/null +++ b/src/file.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018 Diamond Light Source Ltd. + * Author: Charles Mita + */ + + +#ifndef NXS_XDS_FILE_H +#define NXS_XDS_FILE_H + +#include + +struct dataset_properties_t { + int ndims; + int data_width; + hsize_t *dims; +}; + +void free_dataset_properties(struct dataset_properties_t *p); + +struct data_description_t { + hid_t det_group_id; + hid_t data_group_id; + int (*get_pixel_properties)(const struct data_description_t*, double*, double*); + int (*get_pixel_mask)(const struct data_description_t*, int*); + int (*get_data_properties)(const struct data_description_t*, struct dataset_properties_t*); + int (*get_data_frame)(const struct data_description_t*, const struct dataset_properties_t*, int, int, void*); + void *extra; + void (*free_extra)(struct data_description_t*); +}; + +void free_nxs_data_description(struct data_description_t *desc); + +struct eiger_data_description_t { + int n_data_blocks; + int *block_sizes; +}; + +void free_eiger_data_description(struct data_description_t *desc); + +struct det_visit_objects_t { + hid_t nxdata; + hid_t nxdetector; +}; + +void clear_det_visit_objects(struct det_visit_objects_t *objects); + +int get_nxs_dataset_dims(const struct data_description_t *desc, struct dataset_properties_t *properties); + +int fill_data_descriptor(struct data_description_t *data_desc, struct det_visit_objects_t *visit_result); + +int extract_detector_info(const hid_t fid, struct data_description_t *data_desc, struct dataset_properties_t *ds_prop); + +#endif /* NXS_XDS_FILE_H */ diff --git a/src/plugin.c b/src/plugin.c new file mode 100644 index 0000000..a314fe2 --- /dev/null +++ b/src/plugin.c @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2018 Diamond Light Source Ltd. + * Author: Charles Mita + */ + + +#include +#include +#include "file.h" +#include "plugin.h" + + +static hid_t file_id = 0; +static struct data_description_t data_desc = {0}; +static struct dataset_properties_t ds_prop = {0}; +static int *mask_buffer = NULL; + + +void apply_mask(int *data, int *mask, int size) { + int *dptr, *mptr; + dptr = data; + mptr = mask; + while (dptr < data + size && mptr < mask + size) { + /* mask bits loosely based on what Neggia does and what NeXus says should be done */ + /* basically - anything in the low byte (& 0xFF) means "ignore this" */ + if (*mptr & 0x01) *dptr = -1; + if (*mptr & 0xFE) *dptr = -2; + dptr++; + mptr++; + } +} + + +#ifdef __cplusplus +extern "C" { +#endif + +void plugin_open( + const char *filename, + int info[1024], + int *error_flag) { + int err = 0; + info[0] = DLS_CUSTOMER_ID; + info[1] = VERSION_MAJOR; + info[2] = VERSION_MINOR; + info[3] = VERSION_PATCH; + info[4] = VERSION_TIMESTAMP; + file_id = H5Fopen(filename, H5F_ACC_RDONLY, H5P_DEFAULT); + if (file_id < 0) { + /* TODO: backtrace */ + *error_flag = -4; + return; + } + + err = extract_detector_info(file_id, &data_desc, &ds_prop); + if (err < 0) { + *error_flag = -4; + return; + } + + mask_buffer = malloc(ds_prop.dims[1] * ds_prop.dims[2] * sizeof(int)); + if (mask_buffer) { + err = data_desc.get_pixel_mask(&data_desc, mask_buffer); + if (err < 0) { + fprintf(stderr, "WARNING: Could not read pixel mask - no masking will be applied\n"); + free(mask_buffer); + mask_buffer = NULL; + } + } + + *error_flag = 0; +} + + +void plugin_get_header( + int *nx, int *ny, + int *nbytes, + float *qx, float *qy, + int *number_of_frames, + int info[1024], + int *error_flag) { + int err = 0; + double x_pixel_size, y_pixel_size; + + err = data_desc.get_pixel_properties(&data_desc, &x_pixel_size, &y_pixel_size); + if (err < 0) { + *error_flag = -4; + return; + } + + *nx = ds_prop.dims[2]; + *ny = ds_prop.dims[1]; + *nbytes = ds_prop.dims[1] * ds_prop.dims[2] * ds_prop.data_width; + *number_of_frames = ds_prop.dims[0]; + *qx = (float) x_pixel_size; + *qy = (float) y_pixel_size; + *error_flag = 0; +} + + +void plugin_get_data( + int *frame_number, + int *nx, int *ny, + int *data_array, + int info[1024], + int *error_flag) { + int err = 0; + err = data_desc.get_data_frame(&data_desc, &ds_prop, *frame_number, sizeof(int), data_array); + if (err < 0) { + *error_flag = -2; + return; + } + if (mask_buffer) { + apply_mask(data_array, mask_buffer, ds_prop.dims[1] * ds_prop.dims[2]); + } + *error_flag = 0; + return; +} + + +void plugin_close(int *error_flag) { + if (file_id) { + herr_t err = H5Fclose(file_id); + if (err) { + /* TODO: backtrace */ + *error_flag = -1; + } + } + file_id = 0; + + if (mask_buffer) free(mask_buffer); + if (data_desc.free_extra) data_desc.free_extra(&data_desc); + free_dataset_properties(&ds_prop); +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif diff --git a/src/plugin.h b/src/plugin.h new file mode 100644 index 0000000..bf87a4f --- /dev/null +++ b/src/plugin.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2018 Diamond Light Source Ltd. + * Author: Charles Mita + */ + +/* + * External library interface for XDS. + * Ref: https://strucbio.biologie.uni-konstanz.de/xdswiki/index.php/LIB + */ + +#ifndef NXS_XDS_PLUGIN_H +#define NXS_XDS_PLUGIN_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define DLS_CUSTOMER_ID 0x01 /* pretend we're Dectris, otherwise XDS doesn't work */ +#define VERSION_MAJOR 0 +#define VERSION_MINOR 0 +#define VERSION_PATCH 0 +#define VERSION_TIMESTAMP -1 /* good enough for Dectris apparantely */ + + +void plugin_open( + const char *filename, + int info[1024], + int *error_flag); + + +void plugin_get_header( + int *nx, int *ny, + int *nbytes, + float *qx, float *qy, + int *number_of_frames, + int info[1024], + int *error_flag); + + +void plugin_get_data( + int *frame_number, + int *nx, int *ny, + int *data_array, + int info[1024], + int *error_flag); + + +void plugin_close(int *error_flag); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* NXS_XDS_PLUGIN_H */