diff --git a/.gitignore b/.gitignore index f6ca787..d59e10a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ build* BUILD* .dir-locals.el +*.pyc diff --git a/CMakeLists.txt b/CMakeLists.txt index db6da92..5911e8c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ #------------------------------------------------------------------------------- # nest in situ vis # -# Copyright (c) 2017 RWTH Aachen University, Germany, +# Copyright (c) 2017-2018 RWTH Aachen University, Germany, # Virtual Reality & Immersive Visualisation Group. #------------------------------------------------------------------------------- # License @@ -27,6 +27,15 @@ set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) set(CMAKE_CXX_STANDARD 17) find_package(Boost REQUIRED COMPONENTS python) +find_library(Boost_PYTHON_NUMPY_LIBRARY + boost_numpy + PATHS ${Boost_LIBRARY_DIRS} + NO_DEFAULT_PATH + ) +set(Boost_LIBRARIES + ${Boost_LIBRARIES} + ${Boost_PYTHON_NUMPY_LIBRARY} + ) find_package(PythonInterp) find_package(PythonLibs) @@ -46,7 +55,7 @@ include(py.test) include(WarningLevels) -find_package(conduit) +find_package(Conduit) set_target_properties(conduit PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${CONDUIT_INCLUDE_DIRS}/..") diff --git a/LICENSE b/LICENSE index 8d73b9f..acfd3e0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ -------------------------------------------------------------------------------- nest in situ vis - Copyright (c) 2017 RWTH Aachen University, Germany, + Copyright (c) 2017-2018 RWTH Aachen University, Germany, Virtual Reality & Immersive Visualisation Group. -------------------------------------------------------------------------------- Contact Information diff --git a/README.md b/README.md index 0e71830..4891859 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # nest in situ vis -*nest in situ vis* is Copyright (c) 2017 RWTH Aachen University, Germany, +*nest in situ vis* is Copyright (c) 2017-2018 RWTH Aachen University, Germany, Virtual Reality & Immersive Visualization Group. diff --git a/cmake/ConfigureFiles.cmake b/cmake/ConfigureFiles.cmake index a3594b7..6e45a48 100644 --- a/cmake/ConfigureFiles.cmake +++ b/cmake/ConfigureFiles.cmake @@ -1,7 +1,7 @@ #------------------------------------------------------------------------------- # nest in situ vis # -# Copyright (c) 2017 RWTH Aachen University, Germany, +# Copyright (c) 2017-2018 RWTH Aachen University, Germany, # Virtual Reality & Immersive Visualisation Group. #------------------------------------------------------------------------------- # License diff --git a/cmake/WarningLevels.cmake b/cmake/WarningLevels.cmake index 75bad0c..1d0c36a 100644 --- a/cmake/WarningLevels.cmake +++ b/cmake/WarningLevels.cmake @@ -1,7 +1,7 @@ #------------------------------------------------------------------------------- # nest in situ vis # -# Copyright (c) 2017 RWTH Aachen University, Germany, +# Copyright (c) 2017-2018 RWTH Aachen University, Germany, # Virtual Reality & Immersive Visualisation Group. #------------------------------------------------------------------------------- # License diff --git a/cmake/catch.cmake b/cmake/catch.cmake index b6e4607..76e6ba7 100644 --- a/cmake/catch.cmake +++ b/cmake/catch.cmake @@ -1,7 +1,7 @@ #------------------------------------------------------------------------------- # nest in situ vis # -# Copyright (c) 2017 RWTH Aachen University, Germany, +# Copyright (c) 2017-2018 RWTH Aachen University, Germany, # Virtual Reality & Immersive Visualisation Group. #------------------------------------------------------------------------------- # License @@ -39,8 +39,14 @@ endfunction() function(ADD_TEST_CATCH_INTERNAL_ NAME SOURCES HEADERS INCLUDE_DIRECTORIES LINK_LIBRARIES PATH_TO_ADD) + STRING(REGEX REPLACE + "^__" "" + NAME "${NAME}" + ) add_executable(${NAME} ${SOURCES} ${HEADERS}) - target_include_directories(${NAME} PRIVATE ${INCLUDE_DIRECTORIES}) + target_include_directories(${NAME} + PRIVATE ${INCLUDE_DIRECTORIES} + ) target_link_libraries(${NAME} ${LINK_LIBRARIES}) add_test(NAME ${NAME} COMMAND ${NAME}) set_warning_levels_RWTH(${NAME}) @@ -83,13 +89,22 @@ function(ADD_TEST_CATCH) # add test for each test source file foreach(TEST_SOURCE_FILE ${ADD_TEST_CATCH_SOURCES}) + get_filename_component(TEST_SUBDIR ${TEST_SOURCE_FILE} DIRECTORY) + STRING(REGEX REPLACE + "^${CMAKE_CURRENT_SOURCE_DIR}" "" + TEST_SUBDIR "${TEST_SUBDIR}" + ) + STRING(REGEX REPLACE + "[/|\\]" "__" + TEST_SUBDIR "${TEST_SUBDIR}" + ) get_filename_component(TEST_NAME ${TEST_SOURCE_FILE} NAME_WE) - ADD_TEST_CATCH_INTERNAL_("${TEST_NAME}" + ADD_TEST_CATCH_INTERNAL_("${TEST_SUBDIR}__${TEST_NAME}" "${TEST_SOURCE_FILE}" "" "${ADD_TEST_CATCH_INCLUDE_DIRECTORIES}" "${ADD_TEST_CATCH_LINK_LIBRARIES};${ADD_TEST_CATCH_NAME}_catch_main" "${ADD_TEST_CATCH_PATH_TO_ADD}" - ) + ) endforeach() endfunction() diff --git a/cmake/cpplint.cmake b/cmake/cpplint.cmake index 9856c1c..7445b85 100644 --- a/cmake/cpplint.cmake +++ b/cmake/cpplint.cmake @@ -1,7 +1,7 @@ #------------------------------------------------------------------------------- # nest in situ vis # -# Copyright (c) 2017 RWTH Aachen University, Germany, +# Copyright (c) 2017-2018 RWTH Aachen University, Germany, # Virtual Reality & Immersive Visualisation Group. #------------------------------------------------------------------------------- # License @@ -27,7 +27,7 @@ if(NOT PYTHON_EXECUTABLE) CMake will not generate the project without it. ") endif() -find_file(CPPLINT_COMMAND cpplint.py +find_file(CPPLINT_COMMAND cpplint.py cpplint PATHS $ENV{PATH} $ENV{CPPLINT_DIR} ) if(NOT CPPLINT_COMMAND) diff --git a/cmake/niv-config.cmake.in b/cmake/niv-config.cmake.in index f74c3c7..7c25fb6 100644 --- a/cmake/niv-config.cmake.in +++ b/cmake/niv-config.cmake.in @@ -1,7 +1,7 @@ #------------------------------------------------------------------------------- # nest in situ vis # -# Copyright (c) 2017 RWTH Aachen University, Germany, +# Copyright (c) 2017-2018 RWTH Aachen University, Germany, # Virtual Reality & Immersive Visualisation Group. #------------------------------------------------------------------------------- # License diff --git a/cmake/py.test.cmake b/cmake/py.test.cmake index 0694fe1..6d28b29 100644 --- a/cmake/py.test.cmake +++ b/cmake/py.test.cmake @@ -1,7 +1,7 @@ #------------------------------------------------------------------------------- # nest in situ vis # -# Copyright (c) 2017 RWTH Aachen University, Germany, +# Copyright (c) 2017-2018 RWTH Aachen University, Germany, # Virtual Reality & Immersive Visualisation Group. #------------------------------------------------------------------------------- # License @@ -27,7 +27,7 @@ if(NOT PYTHON_EXECUTABLE) CMake will not generate the project without it. ") endif() -find_file(PY_TEST_COMMAND py.test +find_file(PY_TEST_COMMAND py.test pytest PATHS $ENV{PATH} $ENV{PY_TEST_DIR} ) if(NOT PY_TEST_COMMAND) @@ -35,7 +35,7 @@ if(NOT PY_TEST_COMMAND) " ERROR: Could not find py.test. Having py.test is a mandatory requirement. CMake will not generate the project without it. - Add its location to the environments variables PATH or CPPLINT_DIR.") + Add its location to the environments variables PATH or PY_TEST_DIR.") endif() function(ADD_TEST_PY_TEST) diff --git a/cmake/suppress_warnings.hpp.in b/cmake/suppress_warnings.hpp.in index 70c97e0..a21f33e 100644 --- a/cmake/suppress_warnings.hpp.in +++ b/cmake/suppress_warnings.hpp.in @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // nest in situ vis // -// Copyright (c) 2017 RWTH Aachen University, Germany, +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, // Virtual Reality & Immersive Visualisation Group. //------------------------------------------------------------------------------ // License @@ -22,23 +22,58 @@ #ifndef NEST_IN_SITU_VIS_SUPPRESS_WARNINGS_ #define NEST_IN_SITU_VIS_SUPPRESS_WARNINGS_ +// clang-format off #if defined __clang__ -#define SUPPRESS_WARNINGS_BEGIN \ - _Pragma("clang diagnostic push") \ - _Pragma("clang diagnostic ignored \"-Wall\"") \ - _Pragma("clang diagnostic ignored \"-Wextra\"") \ - _Pragma("clang diagnostic ignored \"-Wc++98-compat-pedantic\"") +#define SUPPRESS_WARNINGS_BEGIN \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wall\"") \ + _Pragma("clang diagnostic ignored \"-Wextra\"") \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat-pedantic\"") \ + _Pragma("clang diagnostic ignored \"-Wundefined-func-template\"") \ + _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \ + _Pragma("clang diagnostic ignored \"-Wreserved-id-macro\"") \ + _Pragma("clang diagnostic ignored \"-Wdocumentation-unknown-command\"") \ + _Pragma("clang diagnostic ignored \"-Wdeprecated-dynamic-exception-spec\"") \ + _Pragma("clang diagnostic ignored \"-Wpadded\"") \ + _Pragma("clang diagnostic ignored \"-Wdocumentation\"") \ + _Pragma("clang diagnostic ignored \"-Wswitch-enum\"") \ + _Pragma("clang diagnostic ignored \"-Wextra-semi\"") \ + _Pragma("clang diagnostic ignored \"-Wundef\"") \ + _Pragma("clang diagnostic ignored \"-Wcovered-switch-default\"") \ + _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") \ + _Pragma("clang diagnostic ignored \"-Wdeprecated\"") \ + _Pragma("clang diagnostic ignored \"-Wmissing-noreturn\"") \ + _Pragma("clang diagnostic ignored \"-Wdouble-promotion\"") \ + _Pragma("clang diagnostic ignored \"-Wdouble-promotion\"") \ + _Pragma("clang diagnostic ignored \"-Wdisabled-macro-expansion\"") \ + _Pragma("clang diagnostic ignored \"-Wsign-conversion\"") \ + _Pragma("clang diagnostic ignored \"-Wcomma\"") \ + _Pragma("clang diagnostic ignored \"-Wconversion\"") \ + _Pragma("clang diagnostic ignored \"-Wshadow\"") \ + _Pragma("clang diagnostic ignored \"-Wexit-time-destructors\"") \ + _Pragma("clang diagnostic ignored \"-Wcast-align\"") +// clang-format on + +#define SUPPRESS_WARNINGS_BEGIN_PADDED \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wpadded\"") + #define SUPPRESS_WARNINGS_END _Pragma("clang diagnostic pop") #elif defined _MSC_VER #define NOMINMAX #define SUPPRESS_WARNINGS_BEGIN __pragma(warning(push, 0)); +#define SUPPRESS_WARNINGS_BEGIN_PADDED + #define SUPPRESS_WARNINGS_END __pragma(warning(pop)); #elif defined __GNUC__ #define SUPPRESS_WARNINGS_BEGIN \ _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wall\"") \ - _Pragma("GCC diagnostic ignored \"-Wextra\"") + _Pragma("GCC diagnostic ignored \"-Wextra\"") \ + _Pragma("GCC diagnostic ignored \"-Wunused-local-typedefs\"") \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#define SUPPRESS_WARNINGS_BEGIN_PADDED #define SUPPRESS_WARNINGS_END _Pragma("GCC diagnostic pop") #endif diff --git a/demo/CMakeLists.txt b/demo/CMakeLists.txt index f7ef20d..84d5f99 100644 --- a/demo/CMakeLists.txt +++ b/demo/CMakeLists.txt @@ -1,7 +1,7 @@ #------------------------------------------------------------------------------- # nest in situ vis # -# Copyright (c) 2017 RWTH Aachen University, Germany, +# Copyright (c) 2017-2018 RWTH Aachen University, Germany, # Virtual Reality & Immersive Visualisation Group. #------------------------------------------------------------------------------- # License @@ -20,3 +20,4 @@ #------------------------------------------------------------------------------- add_subdirectory(nest_python_vis) +add_subdirectory(brunel_example) diff --git a/demo/brunel_example/CMakeLists.txt b/demo/brunel_example/CMakeLists.txt new file mode 100644 index 0000000..0a78ca1 --- /dev/null +++ b/demo/brunel_example/CMakeLists.txt @@ -0,0 +1,39 @@ +#------------------------------------------------------------------------------- +# nest in situ vis +# +# Copyright (c) 2017-2018 RWTH Aachen University, Germany, +# Virtual Reality & Immersive Visualisation Group. +#------------------------------------------------------------------------------- +# License +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#------------------------------------------------------------------------------- + +set(NEST_PYTHON_VIS_SOURCE nest_python_vis.py) +set(NEST_SIM_SOURCE nest_sim.py) + +if(NOT CMAKE_MAKE_PROGRAM STREQUAL "/usr/bin/xcodebuild") + +file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/run_vis.sh + CONTENT "PYTHONPATH=$:$ ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/${NEST_PYTHON_VIS_SOURCE}" + ) + +set(NEST_DIR + CACHE PATH "Path to installed nest") +file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/run_sim.sh + CONTENT +"source ${NEST_DIR}/bin/nest_vars.sh +PYTHONPATH=$:$PYTHONPATH ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/${NEST_SIM_SOURCE}" + ) + +endif() diff --git a/demo/brunel_example/brunel_example.py b/demo/brunel_example/brunel_example.py new file mode 100644 index 0000000..e101f07 --- /dev/null +++ b/demo/brunel_example/brunel_example.py @@ -0,0 +1,264 @@ + +""" +Definition of spatially extended Brunel network. +This module provides layer and projections declarations suitable for +use with the NEST Topology Module. +It defines a Brunel-style network with neurons placed on a regular grid. +Connectivity is probabilitstic from the entire network, i.e., connectivity +is not structured spatially. +""" + +from copy import deepcopy +from math import sqrt +import numpy as np +from mpl_toolkits.mplot3d import Axes3D +import matplotlib.pyplot as plt + +import nest +import nest.raster_plot +import nest.topology as tp + + +class Brunel3D: + + def __init__(self): + self.layer_dict = {} + # nest.SetKernelStatus({'print_time': True}) + + class Parameters: + """ + Define model parameters. + """ + order = 9 # must be square + NE = 4 * order # number of excitatory neurons. + NI = 1 * order # number of inhibitory neurons + + lengthE = int(sqrt(NE)) + lengthI = int(sqrt(NI)) + + if not (lengthE**2 == NE and lengthI**2 == NI): + raise ValueError('Order must be a square number (order = n**2)') + + # neuron_model = 'iaf_psc_alpha' + neuron_model = 'mat2_psc_exp' + + g = 5.0 # ratio inhibitory weight/excitatory weight + eta = 2.0 # external rate relative to threshold rate + epsilon = 0.1 # connection probability + + delay = 1.5 # synaptic delay in ms + + tauMem = 20.0 # time constant of membrane potential in ms + theta = 20.0 # membrane threshold potential in mV + + J = 0.1 # postsynaptic amplitude in mV + J_ex = J # amplitude of excitatory postsynaptic potential + J_in = -g * J_ex # amplitude of inhibitory postsynaptic potential + + CE = int(epsilon * NE) # number of excitatory synapses per neuron + + nu_th = theta / (J * CE * tauMem) + nu_ex = eta * nu_th + p_rate = 1.0 * nu_ex * CE + + neuron_params = {"C_m": 1.0, + "tau_m": tauMem, + "t_ref": 2.0, + "E_L": 0.0, + # "V_reset": 0.0, # doesn't work with mat2_psc_exp + # "V_th": theta, # doesn't work with mat2_psc_exp + "V_m": 0.0 + } + mm_params = {'record_from': ['V_m', 'V_th'], + 'record_to': ['stream']} + poisson_params = {"rate": p_rate} + + def modified_copy(self, orig, diff): + """ + Returns a deep copy of dictionary with changes applied. + @param orig original dictionary, will be deep-copied + @param diff copy will be updated with this dict + """ + + tmp = deepcopy(orig) + tmp.update(diff) + return tmp + + def make_layer_specs(self): + """ + Returns lists of dictionaries with model, layer, and synapse model + specifications. + """ + + P = self.Parameters + + self.models = [(P.neuron_model, 'excitatory', P.neuron_params), + (P.neuron_model, 'inhibitory', P.neuron_params), + ('poisson_generator', 'noise', P.poisson_params), + ('multimeter', 'recordingNode', P.mm_params)] + + self.syn_models = [('static_synapse', 'static_excitatory', {})] + + dE = 1.0 / float(P.lengthE) + limE = (1.0 - dE) / 2.0 + posE = np.linspace(-limE, limE, num=P.lengthE) + + dI = 1.0 / float(P.lengthI) + limI = (1.0 - dI) / 2.0 + posI = np.linspace(-limI, limI, num=P.lengthI) + + self.layers = [('Excitatory', {'positions': [[x, y, 0.4] + for x in posE + for y in posE], + 'edge_wrap': True, + 'elements': 'excitatory'}), + ('Inhibitory', {'positions': [[x, y, -0.4] + for x in posI + for y in posI], + 'edge_wrap': True, + 'elements': 'inhibitory'})] + self.layers.append(('PoissonGenerator', + {'positions': [[0.0, 0.0, 0.0]], + 'elements': 'noise'})) + self.layers.append(('Multimeter', + {'positions': [[0.0, 0.0, 0.0]], + 'elements': 'recordingNode'})) + self.layers.append(('SpikeDetector', + {'positions': [[0.0, 0.0, 0.0]], + 'elements': 'spike_detector'})) + + def make_connection_specs(self): + """ + Returns list of dictionaries specifying projections for Brunel network. + """ + + P = self.Parameters + + self.projections = [('Excitatory', 'Excitatory', + {'connection_type': 'convergent', + 'synapse_model': 'static_synapse', + 'kernel': P.epsilon, + 'weights': P.J_ex, + 'delays': P.delay}), + ('Excitatory', 'Inhibitory', + {'connection_type': 'convergent', + 'synapse_model': 'static_synapse', + 'kernel': P.epsilon, + 'weights': P.J_ex, + 'delays': P.delay}), + ('Inhibitory', 'Excitatory', + {'connection_type': 'convergent', + 'synapse_model': 'static_synapse', + 'kernel': P.epsilon, + 'weights': P.J_in, + 'delays': P.delay}), + ('Inhibitory', 'Excitatory', + {'connection_type': 'convergent', + 'synapse_model': 'static_synapse', + 'kernel': P.epsilon, + 'weights': P.J_in, + 'delays': P.delay}), + ('Multimeter', 'Excitatory', + {'connection_type': 'convergent'}), + ('Excitatory', 'SpikeDetector', + {'connection_type': 'convergent'}), + ('Inhibitory', 'SpikeDetector', + {'connection_type': 'convergent'}), + ('PoissonGenerator', 'Excitatory', + {'connection_type': 'convergent'})] + + def make_layers(self): + # First, we copy the models with custom specifications + # Neuron models + for model in self.models: + old_name, new_name, spec = model + nest.CopyModel(old_name, new_name, spec) + # Synapse models + for model in self.syn_models: + old_name, new_name, spec = model + nest.CopyModel(old_name, new_name, spec) + + # Next we make the layers + for l in self.layers: + name = l[0] + specs = l[1] + self.layer_dict.update({name: tp.CreateLayer(specs)}) + + def make_connections(self): + # Connect layers + for proj in self.projections: + pre_layer = self.layer_dict[proj[0]] + post_layer = self.layer_dict[proj[1]] + conn_specs = proj[2] + tp.ConnectLayers(pre_layer, post_layer, conn_specs) + + def simulate(self): + nest.Simulate(1000) + + def plot_positions(self): + ex_pos = self.layers[0][1]['positions'] + in_pos = self.layers[1][1]['positions'] + fig = plt.figure() + ax = Axes3D(fig) + for c, m, positions in [('b', 'o', ex_pos), ('r', '^', in_pos)]: + ax.scatter([x for x, y, z in positions], + [y for x, y, z in positions], + [z for x, y, z in positions], + c=c, marker=m) + + def get_results(self): + mm = (self.layer_dict['Multimeter'][0] + 1,) + sd = (self.layer_dict['SpikeDetector'][0] + 1,) + mm_status = nest.GetStatus(mm)[0] + sd_status = nest.GetStatus(sd)[0] + + nest.raster_plot.from_device(sd, hist=True) + + senders = mm_status['events']['senders'] + times = mm_status['events']['times'] + v_m = mm_status['events']['V_m'] + v_th = mm_status['events']['V_th'] + step = int(max(senders)/100 + 1) # Only plot results from some GIDs + + mm_events = [] + for i in range(1, max(senders) + 1, step): + if i in senders: + indices = np.argwhere(senders == i) + mm_events.append({'GID': i, + 'times': [times[n] for n in indices], + 'V_m': [v_m[n] for n in indices], + 'V_th': [v_th[n] for n in indices]}) + + return {'multimeter': mm_events, + 'spike_detector': nest.GetStatus(sd)[0]} + + +if __name__ == '__main__': + nest.ResetKernel() + + print('Making specifications') + brunel = Brunel3D() + brunel.make_layer_specs() + brunel.make_connection_specs() + + print('Making layers') + brunel.make_layers() + nest.topology.DumpLayerNodes([l[0] for l in brunel.layer_dict.values()][:2], + 'brunel_nodes.txt') + + print('Making connections') + brunel.make_connections() + + brunel.simulate() + + print('Getting results') + brunel.plot_positions() + results = brunel.get_results() + + for value in ['V_m', 'V_th']: + plt.figure() + for n in results['multimeter'][::20]: + plt.plot(n['times'], n[value], label='{}'.format(n['GID'])) + plt.legend() + plt.title(value) + plt.show() diff --git a/demo/brunel_example/nest_python_vis.py b/demo/brunel_example/nest_python_vis.py new file mode 100644 index 0000000..41e19ea --- /dev/null +++ b/demo/brunel_example/nest_python_vis.py @@ -0,0 +1,123 @@ +#------------------------------------------------------------------------------- +# nest in situ vis +# +# Copyright (c) 2017-2018 RWTH Aachen University, Germany, +# Virtual Reality & Immersive Visualisation Group. +#------------------------------------------------------------------------------- +# License +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#------------------------------------------------------------------------------- + +import sys + +import matplotlib.pyplot as plt +import numpy as np +import seaborn as sns + +import pyniv + +from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QGridLayout, QPushButton +from PyQt5.QtCore import QTimer + +class MainWindow: + def __init__(self): + self.SetupStreaming() + self.SetupWindow() + self.SetupPlot() + self.SetupUpdateTimer() + + def SetupStreaming(self): + self.receiver = pyniv.ConsumerReceiver() + + self.multimeter = pyniv.ConsumerMultimeter("recordingNode51") + self.multimeter.SetAttribute("V_m") + + self.backend = pyniv.ConsumerBackend(); + self.backend.Connect(self.receiver); + self.backend.Connect(self.multimeter); + + def SetupWindow(self): + self.visualize_button = QPushButton("Visualize") + self.visualize_button.clicked.connect(self.VisualizeButtonClicked) + + def SetupUpdateTimer(self): + self.update_timer = QTimer() + self.update_timer.timeout.connect(self.Visualize) + + def SetupPlot(self): + sns.set_style("darkgrid") + self.fig = plt.figure() + self.ax1 = self.fig.add_subplot(1,1,1) + + + def VisualizeButtonClicked(self): + self.visualize_button.setEnabled(False) + self.update_timer.start(0.5) + self.Visualize() + + def Show(self): + self.visualize_button.show() + button_geometry = self.visualize_button.geometry() + self.visualize_button.setGeometry( + 0, 0, + button_geometry.width(), + button_geometry.height()) + + def Visualize(self): + self.backend.Receive() + plot_ts, plot_vs = self.GetValues(self.multimeter) + self.Plot(plot_ts, plot_vs); + + def GetValues(self, multimeter): + ts = multimeter.GetTimesteps() + plot_ts = [] + plot_vs = [] + for t in ts: + multimeter.SetTime(t) + multimeter.Update() + vs = multimeter.GetValues() + if len(vs) > 0: + plot_ts.append(t.tolist()) + plot_vs.append(vs.tolist()) + return plot_ts, plot_vs + + def Plot(self, ts, vs): + values = [] + for i in range(0, 35): + tmp = [] + for t in range(0, len(vs)): + tmp.append(vs[t][i]) + values.append(tmp) + + self.ax1.clear() + for i in range(0, len(values)): + self.ax1.plot(ts, values[i]) + self.ax1.set_title("Brunel Example") + self.ax1.set_xlabel("Time") + self.ax1.set_ylabel("V_m") + + plt.show(block=False) + self.fig.canvas.draw() + + +def main(argv): + app = QApplication(argv) + + w = MainWindow() + w.Show() + + return app.exec_() + +if __name__ == "__main__": + main(sys.argv) diff --git a/demo/brunel_example/nest_sim.py b/demo/brunel_example/nest_sim.py new file mode 100644 index 0000000..65788db --- /dev/null +++ b/demo/brunel_example/nest_sim.py @@ -0,0 +1,94 @@ +#------------------------------------------------------------------------------- +# nest in situ vis +# +# Copyright (c) 2017-2018 RWTH Aachen University, Germany, +# Virtual Reality & Immersive Visualisation Group. +#------------------------------------------------------------------------------- +# License +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#------------------------------------------------------------------------------- + +import sys +import nest +import numpy + +from PyQt5.QtWidgets import QApplication, QPushButton +from PyQt5.QtCore import QTimer + +import brunel_example + +class Simulation: + def __init__(self): + self.SetupSim() + + def SetupSim(self): + nest.ResetKernel() + + print('Making specifications') + self.brunel = brunel_example.Brunel3D() + self.brunel.make_layer_specs() + self.brunel.make_connection_specs() + + print('Making layers') + self.brunel.make_layers() + nest.topology.DumpLayerNodes([l[0] for l in self.brunel.layer_dict.values()][:2], + 'brunel_nodes.txt') + + print('Making connections') + self.brunel.make_connections() + + def Simulate(self, steps): + nest.Simulate(steps) + + +class MainWindow: + def __init__(self, screen_resolution): + self.screen_resolution = screen_resolution + self.SetupWindow() + self.simulation = Simulation() + + def SetupWindow(self): + self.simulate_button = QPushButton("nest.Simulate(1000)") + self.simulate_button.clicked.connect(self.SimulateButtonClicked) + + def SimulateButtonClicked(self): + self.simulate_button.setEnabled(False) + QApplication.processEvents() + + self.simulation.Simulate(1000) + + QApplication.processEvents() + self.simulate_button.setEnabled(True) + + def Show(self): + self.simulate_button.show() + button_geometry = self.simulate_button.geometry() + self.simulate_button.setGeometry( + self.screen_resolution.width() - button_geometry.width(), + self.screen_resolution.height() - button_geometry.height(), + button_geometry.width(), + button_geometry.height()) + + +def main(argv): + app = QApplication(argv) + screen_resolution = app.desktop().screenGeometry() + + w = MainWindow(screen_resolution) + w.Show() + + return app.exec_() + +if __name__ == "__main__": + main(sys.argv) diff --git a/demo/nest_python_vis/CMakeLists.txt b/demo/nest_python_vis/CMakeLists.txt index ff3fb9b..0a78ca1 100644 --- a/demo/nest_python_vis/CMakeLists.txt +++ b/demo/nest_python_vis/CMakeLists.txt @@ -1,7 +1,7 @@ #------------------------------------------------------------------------------- # nest in situ vis # -# Copyright (c) 2017 RWTH Aachen University, Germany, +# Copyright (c) 2017-2018 RWTH Aachen University, Germany, # Virtual Reality & Immersive Visualisation Group. #------------------------------------------------------------------------------- # License @@ -22,6 +22,8 @@ set(NEST_PYTHON_VIS_SOURCE nest_python_vis.py) set(NEST_SIM_SOURCE nest_sim.py) +if(NOT CMAKE_MAKE_PROGRAM STREQUAL "/usr/bin/xcodebuild") + file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/run_vis.sh CONTENT "PYTHONPATH=$:$ ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/${NEST_PYTHON_VIS_SOURCE}" ) @@ -33,3 +35,5 @@ file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/run_sim.sh "source ${NEST_DIR}/bin/nest_vars.sh PYTHONPATH=$:$PYTHONPATH ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/${NEST_SIM_SOURCE}" ) + +endif() diff --git a/demo/nest_python_vis/nest_python_vis.py b/demo/nest_python_vis/nest_python_vis.py index 5eda3e7..97141a9 100644 --- a/demo/nest_python_vis/nest_python_vis.py +++ b/demo/nest_python_vis/nest_python_vis.py @@ -1,7 +1,7 @@ #------------------------------------------------------------------------------- # nest in situ vis # -# Copyright (c) 2017 RWTH Aachen University, Germany, +# Copyright (c) 2017-2018 RWTH Aachen University, Germany, # Virtual Reality & Immersive Visualisation Group. #------------------------------------------------------------------------------- # License @@ -21,6 +21,10 @@ import sys +import matplotlib.pyplot as plt +import numpy as np +import seaborn as sns + import pyniv from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QGridLayout, QPushButton @@ -28,33 +32,32 @@ class MainWindow: def __init__(self): - self.receiver = pyniv.ConduitReceiver() + self.SetupStreaming() self.SetupWindow() + self.SetupPlot() self.SetupUpdateTimer() + def SetupStreaming(self): + self.receiver = pyniv.ConsumerReceiver() + + self.multimeter_a = pyniv.ConsumerMultimeter("multimeter A") + self.multimeter_a.SetAttribute("V_m") - def SetupWindow(self): - self.label_V_m = QLabel("V_m:") - self.value_V_m = QLabel("{:0.3f} mV".format(0.0)) - - self.label_g_ex = QLabel("g_ex:") - self.value_g_ex = QLabel("{:0.3f}".format(0.0)) + self.multimeter_b = pyniv.ConsumerMultimeter("multimeter B") + self.multimeter_b.SetAttribute("V_m") - self.label_g_in = QLabel("g_ex:") - self.value_g_in = QLabel("{:0.3f}".format(0.0)) + self.backend = pyniv.ConsumerBackend(); + self.backend.Connect(self.receiver); + self.backend.Connect(self.multimeter_a); + self.backend.Connect(self.multimeter_b); - self.start_button = QPushButton("Start") - self.start_button.clicked.connect(self.StartButtonClicked) + def SetupWindow(self): + self.visualize_button = QPushButton("Visualize") + self.visualize_button.clicked.connect(self.VisualizeButtonClicked) self.layout = QGridLayout() - self.layout.addWidget(self.label_V_m, 0, 0) - self.layout.addWidget(self.value_V_m, 0, 1) - self.layout.addWidget(self.label_g_ex, 1, 0) - self.layout.addWidget(self.value_g_ex, 1, 1) - self.layout.addWidget(self.label_g_in, 2, 0) - self.layout.addWidget(self.value_g_in, 2, 1) - self.layout.addWidget(self.start_button, 3, 0, 1, 2) + self.layout.addWidget(self.visualize_button, 0, 0, 1, 1) self.window = QWidget() self.window.setLayout(self.layout) @@ -62,25 +65,46 @@ def SetupWindow(self): def SetupUpdateTimer(self): self.update_timer = QTimer() - self.update_timer.timeout.connect(self.UpdateValue) + self.update_timer.timeout.connect(self.Visualize) + def SetupPlot(self): + self.fig = plt.figure() + self.ax1 = self.fig.add_subplot(1,1,1) - def StartButtonClicked(self): - self.start_button.setEnabled(False) - self.receiver.Start() - self.update_timer.start(0.01) - self.UpdateValue() - + def VisualizeButtonClicked(self): + self.visualize_button.setEnabled(False) + self.update_timer.start(0.5) + self.Visualize() + def Show(self): self.window.show() - def UpdateValue(self): - self.value_V_m.setText("{:0.3f} mV".format(self.receiver.Get("V_m"))) - self.value_g_ex.setText("{:0.3f}".format(self.receiver.Get("g_ex"))) - self.value_g_in.setText("{:0.3f}".format(self.receiver.Get("g_in"))) - - self.value_V_m.update() - self.window.update() + def Visualize(self): + self.backend.Receive() + plot_ts_a, plot_vs_a = self.GetValues(self.multimeter_a) + plot_ts_b, plot_vs_b = self.GetValues(self.multimeter_b) + self.Plot([[plot_ts_a, plot_vs_a], [plot_ts_b, plot_vs_b]]); + + def GetValues(self, multimeter): + ts = multimeter.GetTimesteps() + plot_ts = [] + plot_vs = [] + for t in ts: + multimeter.SetTime(t) + multimeter.Update() + vs = multimeter.GetValues() + if len(vs) > 0: + plot_ts.append(t) + plot_vs.append(vs[0]) + return plot_ts, plot_vs + + def Plot(self, values): + self.ax1.clear() + for [ts, vs] in values: + self.ax1.plot(ts, vs) + plt.show(block=False) + self.fig.canvas.draw() + def main(argv): app = QApplication(argv) diff --git a/demo/nest_python_vis/nest_sim.py b/demo/nest_python_vis/nest_sim.py index 5af7183..c3b6898 100644 --- a/demo/nest_python_vis/nest_sim.py +++ b/demo/nest_python_vis/nest_sim.py @@ -1,7 +1,7 @@ #------------------------------------------------------------------------------- # nest in situ vis # -# Copyright (c) 2017 RWTH Aachen University, Germany, +# Copyright (c) 2017-2018 RWTH Aachen University, Germany, # Virtual Reality & Immersive Visualisation Group. #------------------------------------------------------------------------------- # License @@ -29,8 +29,8 @@ class Simulation: def __init__(self): self.ConfigureNest() - self.CreateNeuron() - self.CreateAndConnectMultimeters() + self.CreateAndConnectNeurons() + self.CreateAndConnectRecordingDevices() self.CreateAndConnectSpikeGenerators() def ConfigureNest(self): @@ -40,33 +40,45 @@ def ConfigureNest(self): "data_prefix": ""}) nest.SetDefaults('static_synapse', {'delay': 0.1}) - def CreateNeuron(self): + def CreateAndConnectNeurons(self): neuron_params={"tau_syn_ex": 1.0, "V_reset": -70.0} - self.neuron = nest.Create("iaf_cond_alpha", - params=neuron_params) - - def CreateAndConnectMultimeters(self): - self.CreateAndConnectConduitMultimeter() - self.CreateAndConnectScreenMultimeter() - - def CreateAndConnectConduitMultimeter(self): - multimeter_conduit_params = {"interval": 0.1, - "record_from": ["V_m", "g_ex", "g_in"], - "record_to": ["conduit"], - "label": "multimeter_conduit"} + self.populationA = nest.Create("iaf_cond_alpha", + params=neuron_params) + self.populationB = nest.Create("iaf_cond_alpha", 2, + params=neuron_params) + nest.Connect(self.populationA, self.populationB, syn_spec={"weight": 100.0}); + + def CreateAndConnectRecordingDevices(self): + self.CreateAndConnectConduitMultimeter(self.populationA, "multimeter A") + self.CreateAndConnectConduitMultimeter(self.populationB, "multimeter B") + + self.CreateAndConnectConduitSpikeDetector(self.populationA, "spikes A") + self.CreateAndConnectConduitSpikeDetector(self.populationB, "spikes B") + + self.CreateAndConnectScreenMultimeter(self.populationA, "screen MM A") + + def CreateAndConnectConduitMultimeter(self, population, label): multimeter_conduit = nest.Create("multimeter", - params=multimeter_conduit_params) - nest.Connect(multimeter_conduit, self.neuron) - - def CreateAndConnectScreenMultimeter(self): - multimeter_screen_params = {"interval": 0.1, - "record_from": ["V_m", "g_ex", "g_in"], - "record_to": ["screen"], - "label": "my_screen_multimeter"} + params={"interval": 0.1, + "record_from": ["V_m", "g_ex", "g_in"], + "record_to": ["stream"], + "label": label}) + nest.Connect(multimeter_conduit, population) + + def CreateAndConnectConduitSpikeDetector(self, population, label): + spike_detector_conduit = nest.Create("spike_detector", + params={"record_to": ["stream"], + "label": label}) + nest.Connect(population, spike_detector_conduit) + + def CreateAndConnectScreenMultimeter(self, population, label): multimeter_screen = nest.Create("multimeter", - params=multimeter_screen_params) - nest.Connect(multimeter_screen, self.neuron) + params={"interval": 0.1, + "record_from": ["V_m", "g_ex", "g_in"], + "record_to": ["screen"], + "label": label}) + nest.Connect(multimeter_screen, population) def CreateAndConnectSpikeGenerators(self): self.CreateAndConnectExitatorySpikeGenerator() @@ -76,13 +88,13 @@ def CreateAndConnectExitatorySpikeGenerator(self): spikes_ex_params = {"spike_times": numpy.array([1.0, 5.0, 10.0, 15.0, 20.0, 50.0])} spikes_ex = nest.Create("spike_generator", params=spikes_ex_params) - nest.Connect(spikes_ex, self.neuron, syn_spec={"weight": 40.0}) + nest.Connect(spikes_ex, self.populationA, syn_spec={"weight": 40.0}) def CreateAndConnectInhibitorySpikeGenerator(self): spikes_in_params = {"spike_times": numpy.array([4.0, 8.0, 13.0, 18.0, 23.0, 53.0])} spikes_in = nest.Create("spike_generator", params=spikes_in_params) - nest.Connect(spikes_in, self.neuron, syn_spec={"weight": -20.0}) + nest.Connect(spikes_in, self.populationA, syn_spec={"weight": -20.0}) def Simulate(self, time): nest.Simulate(time) diff --git a/niv/CMakeLists.txt b/niv/CMakeLists.txt index 3a0f319..557d2f4 100644 --- a/niv/CMakeLists.txt +++ b/niv/CMakeLists.txt @@ -1,7 +1,7 @@ #------------------------------------------------------------------------------- # nest in situ vis # -# Copyright (c) 2017 RWTH Aachen University, Germany, +# Copyright (c) 2017-2018 RWTH Aachen University, Germany, # Virtual Reality & Immersive Visualisation Group. #------------------------------------------------------------------------------- # License @@ -19,9 +19,9 @@ # limitations under the License. #------------------------------------------------------------------------------- -file(GLOB NIV_SOURCES src/*.cpp) -file(GLOB NIV_HEADERS src/*.hpp) -file(GLOB NIV_API_HEADERS include/niv/*.hpp) +file(GLOB NIV_SOURCES src/*.cpp src/**/*.cpp) +file(GLOB NIV_HEADERS src/*.hpp src/**/*.hpp) +file(GLOB NIV_API_HEADERS include/niv/*.hpp include/niv/**/*.hpp) add_library(niv ${NIV_SOURCES} @@ -30,25 +30,38 @@ add_library(niv ) target_include_directories(niv PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include - PUBLIC ${CMAKE_CURRENT_BINARY_DIR} + PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/include + PUBLIC ${Boost_INCLUDE_DIR} ) + +SET(ADDITIONAL_LINUX_LIBRARIES ) +if(UNIX AND NOT APPLE) + SET(ADDITIONAL_LINUX_LIBRARIES + pthread + rt + ) +endif() target_link_libraries(niv conduit + ${ADDITIONAL_LINUX_LIBRARIES} + ) +target_compile_options(niv + PRIVATE -fPIC ) generate_export_header(niv EXPORT_FILE_NAME ${CMAKE_CURRENT_BINARY_DIR}/include/niv/export.hpp -) + ) set_warning_levels_RWTH(niv SUPPRESS_WARNINGS_HEADER ${CMAKE_CURRENT_BINARY_DIR}/include/niv/suppress_warnings.hpp -) + ) add_test_cpplint(NAME "niv--cpplint" ${NIV_SOURCES} ${NIV_HEADERS} ${NIV_API_HEADERS} -) + ) generate_configure_files(niv) diff --git a/niv/helper_apps/CMakeLists.txt b/niv/helper_apps/CMakeLists.txt index 037766a..ca1eb2d 100644 --- a/niv/helper_apps/CMakeLists.txt +++ b/niv/helper_apps/CMakeLists.txt @@ -1,7 +1,7 @@ #------------------------------------------------------------------------------- # nest in situ vis # -# Copyright (c) 2017 RWTH Aachen University, Germany, +# Copyright (c) 2017-2018 RWTH Aachen University, Germany, # Virtual Reality & Immersive Visualisation Group. #------------------------------------------------------------------------------- # License @@ -25,3 +25,7 @@ add_executable(niv-shared-memory target_link_libraries(niv-shared-memory niv ) +set_warning_levels_RWTH(niv-shared-memory + SUPPRESS_WARNINGS_HEADER + ${CMAKE_CURRENT_BINARY_DIR}/include/niv/suppress_warnings.hpp + ) diff --git a/niv/helper_apps/src/shared_memory.cpp b/niv/helper_apps/src/shared_memory.cpp index aa55b4a..7d6d8f1 100644 --- a/niv/helper_apps/src/shared_memory.cpp +++ b/niv/helper_apps/src/shared_memory.cpp @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // nest in situ vis // -// Copyright (c) 2017 RWTH Aachen University, Germany, +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, // Virtual Reality & Immersive Visualisation Group. //------------------------------------------------------------------------------ // License @@ -26,7 +26,8 @@ #include "conduit/conduit_node.hpp" #include "conduit/conduit_schema.hpp" -#include "niv/shared_memory.hpp" +#include "niv/exchange/shared_memory.hpp" +#include "niv/exchange/shared_memory_synchronization.hpp" conduit::Node AnyNode() { conduit::Node node; @@ -39,37 +40,39 @@ conduit::Node AnyNode() { return node; } -void StoreSchema(const conduit::Node& node, niv::SharedMemory* shared_memory) { - conduit::Schema schema; - node.schema().compact_to(schema); - shared_memory->Store(schema.to_json()); -} - -void StoreData(const conduit::Node& node, niv::SharedMemory* shared_memory) { - std::vector data; - node.serialize(data); - shared_memory->Store(data); -} - -void FillWithData(niv::SharedMemory* shared_memory) { +void FillWithData(niv::exchange::SharedMemory* shared_memory) { conduit::Node node{AnyNode()}; - StoreSchema(node, shared_memory); - StoreData(node, shared_memory); + shared_memory->Store(node); } void Create() { - niv::SharedMemory shared_memory{niv::SharedMemory::Create()}; - FillWithData(&shared_memory); + niv::exchange::SharedMemory segment{niv::exchange::SharedMemory::Create()}; + FillWithData(&segment); } void Fill() { - niv::SharedMemory shared_memory{niv::SharedMemory::Access()}; - FillWithData(&shared_memory); + niv::exchange::SharedMemory access{niv::exchange::SharedMemory::Access()}; + FillWithData(&access); } void Destroy() { - niv::SharedMemory s{niv::SharedMemory::Access()}; - s.Destroy(); + niv::exchange::SharedMemory access{niv::exchange::SharedMemory::Access()}; + access.Destroy(); +} + +void CreateMutex() { + niv::exchange::SharedMemorySynchronization::ManagedMutex mutex{ + boost::interprocess::create_only, + niv::exchange::SharedMemorySynchronization::MutexName()}; +} + +void DestroyMutex() { + niv::exchange::SharedMemorySynchronization::ManagedMutex mutex{ + boost::interprocess::open_only, + niv::exchange::SharedMemorySynchronization::MutexName()}; + mutex.unlock(); + niv::exchange::SharedMemorySynchronization::ManagedMutex::remove( + niv::exchange::SharedMemorySynchronization::MutexName()); } int Command(char* command) { @@ -81,6 +84,12 @@ int Command(char* command) { } else if (std::string(command) == std::string("destroy")) { Destroy(); return EXIT_SUCCESS; + } else if (std::string(command) == std::string("create_mutex")) { + CreateMutex(); + return EXIT_SUCCESS; + } else if (std::string(command) == std::string("destroy_mutex")) { + DestroyMutex(); + return EXIT_SUCCESS; } return EXIT_FAILURE; } diff --git a/niv/src/sending_relay_shared_memory.cpp b/niv/include/niv/consumer/backend.hpp similarity index 55% rename from niv/src/sending_relay_shared_memory.cpp rename to niv/include/niv/consumer/backend.hpp index 4e681c1..1bd2481 100644 --- a/niv/src/sending_relay_shared_memory.cpp +++ b/niv/include/niv/consumer/backend.hpp @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // nest in situ vis // -// Copyright (c) 2017 RWTH Aachen University, Germany, +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, // Virtual Reality & Immersive Visualisation Group. //------------------------------------------------------------------------------ // License @@ -19,36 +19,41 @@ // limitations under the License. //------------------------------------------------------------------------------ -#include "niv/sending_relay_shared_memory.hpp" +#ifndef NIV_INCLUDE_NIV_CONSUMER_BACKEND_HPP_ +#define NIV_INCLUDE_NIV_CONSUMER_BACKEND_HPP_ -#include -#include #include #include "conduit/conduit_node.hpp" -#include "conduit/conduit_schema.hpp" + +#include "niv/consumer/device.hpp" +#include "niv/consumer/receiver.hpp" namespace niv { +namespace consumer { + +class Backend { + public: + Backend() = default; + Backend(const Backend&) = delete; + Backend(Backend&&) = delete; + virtual ~Backend() = default; -SendingRelaySharedMemory::SendingRelaySharedMemory( - std::unique_ptr shared_memory) - : RelaySharedMemory{std::move(shared_memory)} {} + Backend& operator=(const Backend&) = delete; + Backend& operator=(Backend&&) = delete; -void SendingRelaySharedMemory::Send(const conduit::Node& node) { - SendData(node); - SendSchema(node); -} + void Connect(niv::consumer::Receiver* receiver); + void Connect(niv::consumer::Device* device); + void Receive(); -void SendingRelaySharedMemory::SendData(const conduit::Node& node) { - std::vector data; - node.serialize(data); - shared_memory_->Store(data); -} + protected: + niv::consumer::Receiver* receiver_{nullptr}; + std::vector devices_; -void SendingRelaySharedMemory::SendSchema(const conduit::Node& node) { - conduit::Schema schema; - node.schema().compact_to(schema); - shared_memory_->Store(schema.to_json()); -} + conduit::Node node_; +}; +} // namespace consumer } // namespace niv + +#endif // NIV_INCLUDE_NIV_CONSUMER_BACKEND_HPP_ diff --git a/niv/include/niv/consumer/device.hpp b/niv/include/niv/consumer/device.hpp new file mode 100644 index 0000000..603e055 --- /dev/null +++ b/niv/include/niv/consumer/device.hpp @@ -0,0 +1,70 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#ifndef NIV_INCLUDE_NIV_CONSUMER_DEVICE_HPP_ +#define NIV_INCLUDE_NIV_CONSUMER_DEVICE_HPP_ + +#include +#include + +#include "conduit/conduit_node.hpp" + +namespace niv { +namespace consumer { + +class Device { + public: + Device() = delete; + Device(const Device&) = default; + Device(Device&&) = default; + virtual ~Device() = default; + + Device& operator=(const Device&) = default; + Device& operator=(Device&&) = default; + + const std::vector& GetTimesteps(); + + virtual void SetTime(double time); + + virtual void Update() = 0; + + void SetNode(const conduit::Node* node) { node_ = node; } + + protected: + explicit Device(const std::string& name); + + void SetTimestepNode(); + const conduit::Node* GetTimestepNode() const; + + private: + const conduit::Node* node_{nullptr}; + const conduit::Node* timestep_node_{nullptr}; + + std::vector timesteps_; + + double time_{0.0}; + std::string name_{""}; +}; + +} // namespace consumer +} // namespace niv + +#endif // NIV_INCLUDE_NIV_CONSUMER_DEVICE_HPP_ diff --git a/niv/include/niv/receiving_relay_shared_memory.hpp b/niv/include/niv/consumer/multimeter.hpp similarity index 53% rename from niv/include/niv/receiving_relay_shared_memory.hpp rename to niv/include/niv/consumer/multimeter.hpp index 0193274..cf72564 100644 --- a/niv/include/niv/receiving_relay_shared_memory.hpp +++ b/niv/include/niv/consumer/multimeter.hpp @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // nest in situ vis // -// Copyright (c) 2017 RWTH Aachen University, Germany, +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, // Virtual Reality & Immersive Visualisation Group. //------------------------------------------------------------------------------ // License @@ -19,28 +19,44 @@ // limitations under the License. //------------------------------------------------------------------------------ -#ifndef NIV_INCLUDE_NIV_RECEIVING_RELAY_SHARED_MEMORY_HPP_ -#define NIV_INCLUDE_NIV_RECEIVING_RELAY_SHARED_MEMORY_HPP_ +#ifndef NIV_INCLUDE_NIV_CONSUMER_MULTIMETER_HPP_ +#define NIV_INCLUDE_NIV_CONSUMER_MULTIMETER_HPP_ -#include +#include +#include #include "conduit/conduit_node.hpp" -#include "niv/relay_shared_memory.hpp" -#include "niv/shared_memory.hpp" +#include "niv/consumer/device.hpp" namespace niv { +namespace consumer { -class ReceivingRelaySharedMemory : public RelaySharedMemory { +class Multimeter : public consumer::Device { public: - explicit ReceivingRelaySharedMemory( - std::unique_ptr shared_memory); - virtual ~ReceivingRelaySharedMemory() = default; + Multimeter() = delete; + explicit Multimeter(const std::string& name); + Multimeter(const Multimeter&) = default; + Multimeter(Multimeter&&) = default; + ~Multimeter() = default; - void Receive(conduit::Node* node); - void Listen(conduit::Node* node); + Multimeter& operator=(const Multimeter&) = default; + Multimeter& operator=(Multimeter&&) = default; + + void SetAttribute(const std::string& attribute); + + void Update() override; + + const std::vector& GetValues() const; + + private: + void SetValues(); + + std::vector values_; + std::string attribute_{""}; }; +} // namespace consumer } // namespace niv -#endif // NIV_INCLUDE_NIV_RECEIVING_RELAY_SHARED_MEMORY_HPP_ +#endif // NIV_INCLUDE_NIV_CONSUMER_MULTIMETER_HPP_ diff --git a/niv/include/niv/sending_relay_shared_memory.hpp b/niv/include/niv/consumer/receiver.hpp similarity index 60% rename from niv/include/niv/sending_relay_shared_memory.hpp rename to niv/include/niv/consumer/receiver.hpp index d168a12..9fe8206 100644 --- a/niv/include/niv/sending_relay_shared_memory.hpp +++ b/niv/include/niv/consumer/receiver.hpp @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // nest in situ vis // -// Copyright (c) 2017 RWTH Aachen University, Germany, +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, // Virtual Reality & Immersive Visualisation Group. //------------------------------------------------------------------------------ // License @@ -19,31 +19,36 @@ // limitations under the License. //------------------------------------------------------------------------------ -#ifndef NIV_INCLUDE_NIV_SENDING_RELAY_SHARED_MEMORY_HPP_ -#define NIV_INCLUDE_NIV_SENDING_RELAY_SHARED_MEMORY_HPP_ - -#include +#ifndef NIV_INCLUDE_NIV_CONSUMER_RECEIVER_HPP_ +#define NIV_INCLUDE_NIV_CONSUMER_RECEIVER_HPP_ #include "conduit/conduit_node.hpp" -#include "niv/relay_shared_memory.hpp" -#include "niv/shared_memory.hpp" +#include "niv/exchange/relay_shared_memory.hpp" namespace niv { +namespace consumer { -class SendingRelaySharedMemory : public RelaySharedMemory { +class Receiver { public: - explicit SendingRelaySharedMemory( - std::unique_ptr shared_memory); - virtual ~SendingRelaySharedMemory() = default; + Receiver() = default; + Receiver(const Receiver&) = delete; + Receiver(Receiver&&) = default; + virtual ~Receiver() = default; + + Receiver& operator=(const Receiver&) = delete; + Receiver& operator=(Receiver&&) = default; + + void SetNode(conduit::Node* node) { node_ = node; } - void Send(const conduit::Node& node); + virtual void Receive(); private: - void SendData(const conduit::Node& node); - void SendSchema(const conduit::Node& node); + exchange::RelaySharedMemory relay_; + conduit::Node* node_; }; +} // namespace consumer } // namespace niv -#endif // NIV_INCLUDE_NIV_SENDING_RELAY_SHARED_MEMORY_HPP_ +#endif // NIV_INCLUDE_NIV_CONSUMER_RECEIVER_HPP_ diff --git a/niv/include/niv/exchange/node_storage.hpp b/niv/include/niv/exchange/node_storage.hpp new file mode 100644 index 0000000..affe2b8 --- /dev/null +++ b/niv/include/niv/exchange/node_storage.hpp @@ -0,0 +1,124 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#ifndef NIV_INCLUDE_NIV_EXCHANGE_NODE_STORAGE_HPP_ +#define NIV_INCLUDE_NIV_EXCHANGE_NODE_STORAGE_HPP_ + +#include +#include + +#include "conduit/conduit_node.hpp" + +namespace niv { +namespace exchange { + +template +class NodeStorage { + public: + NodeStorage() = delete; + NodeStorage(SchemaStorage* schema_storage, DataStorage* data_storage) + : schema_storage_{schema_storage}, data_storage_{data_storage} {} + NodeStorage(const NodeStorage&) = default; + NodeStorage(NodeStorage&&) = default; + virtual ~NodeStorage() = default; + + void Store(const conduit::Node& node) { + StoreSchema(node); + StoreData(node); + } + + void Update(const conduit::Node& node) { + conduit::Node tmp{Read()}; + tmp.update(node); + Store(tmp); + } + + conduit::Node Read() { + constexpr bool keep_listening = false; + return ConstructNode(keep_listening); + } + + conduit::Node Listen() { + constexpr bool keep_listening = true; + return ConstructNode(keep_listening); + } + + void Clear() { + schema_storage_->clear(); + data_storage_->clear(); + } + + bool IsEmpty() const { + return schema_storage_->empty() && data_storage_->empty(); + } + + NodeStorage& operator=(const NodeStorage&) = default; + NodeStorage& operator=(NodeStorage&&) = default; + + protected: + SchemaStorage* GetSchemaStorage() { return schema_storage_; } + DataStorage* GetDataStorage() { return data_storage_; } + + private: + conduit::Node ConstructNode(bool keep_listening) { + if (schema_storage_->empty()) { + return conduit::Node(); + } else { + return conduit::Node(GetSchema(), data_storage_->data(), keep_listening); + } + } + + std::string GetSchema() { + return std::string(schema_storage_->begin(), schema_storage_->end()); + } + void StoreSchema(const conduit::Node& node) { + const std::string schema{CompactedSchemaJson(node)}; + schema_storage_->clear(); + schema_storage_->assign(schema.begin(), schema.end()); + } + + void StoreData(const conduit::Node& node) { + const std::vector data{Serialize(node)}; + data_storage_->clear(); + data_storage_->assign(data.begin(), data.end()); + } + + static std::string CompactedSchemaJson(const conduit::Node& node) { + const conduit::Schema schema{node.schema()}; + conduit::Schema compact_schema; + schema.compact_to(compact_schema); + return compact_schema.to_json(); + } + + static std::vector Serialize(const conduit::Node& node) { + std::vector data; + node.serialize(data); + return data; + } + + SchemaStorage* schema_storage_{nullptr}; + DataStorage* data_storage_{nullptr}; +}; + +} // namespace exchange +} // namespace niv + +#endif // NIV_INCLUDE_NIV_EXCHANGE_NODE_STORAGE_HPP_ diff --git a/niv/include/niv/exchange/relay_shared_memory.hpp b/niv/include/niv/exchange/relay_shared_memory.hpp new file mode 100644 index 0000000..25d1f3b --- /dev/null +++ b/niv/include/niv/exchange/relay_shared_memory.hpp @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#ifndef NIV_INCLUDE_NIV_EXCHANGE_RELAY_SHARED_MEMORY_HPP_ +#define NIV_INCLUDE_NIV_EXCHANGE_RELAY_SHARED_MEMORY_HPP_ + +#include +#include + +#include "conduit/conduit_node.hpp" + +#include "niv/exchange/shared_memory.hpp" +#include "niv/exchange/shared_memory_synchronization.hpp" + +namespace niv { +namespace exchange { + +SUPPRESS_WARNINGS_BEGIN_PADDED +class RelaySharedMemory { + public: + class CreateSharedMemory {}; + class AccessSharedMemory {}; + + RelaySharedMemory(); + virtual ~RelaySharedMemory(); + RelaySharedMemory(const RelaySharedMemory&) = delete; + RelaySharedMemory(RelaySharedMemory&&) = delete; + + void Send(const conduit::Node& node); + conduit::Node Receive(); + + RelaySharedMemory& operator=(const RelaySharedMemory&) = delete; + RelaySharedMemory& operator=(RelaySharedMemory&&) = delete; + + bool IsEmpty() const; + + protected: + explicit RelaySharedMemory(const CreateSharedMemory&); + explicit RelaySharedMemory(const AccessSharedMemory&); + SharedMemory* GetSharedMemory() { return shared_memory_.get(); } + const SharedMemory* GetSharedMemory() const { return shared_memory_.get(); } + + private: + void SendUpdate(const conduit::Node& node); + + std::unique_ptr shared_memory_; + std::unique_ptr synchronization_; +}; +SUPPRESS_WARNINGS_END + +} // namespace exchange +} // namespace niv + +#endif // NIV_INCLUDE_NIV_EXCHANGE_RELAY_SHARED_MEMORY_HPP_ diff --git a/niv/include/niv/shared_memory.hpp b/niv/include/niv/exchange/shared_memory.hpp similarity index 51% rename from niv/include/niv/shared_memory.hpp rename to niv/include/niv/exchange/shared_memory.hpp index cdcd76e..ec62e37 100644 --- a/niv/include/niv/shared_memory.hpp +++ b/niv/include/niv/exchange/shared_memory.hpp @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // nest in situ vis // -// Copyright (c) 2017 RWTH Aachen University, Germany, +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, // Virtual Reality & Immersive Visualisation Group. //------------------------------------------------------------------------------ // License @@ -19,19 +19,26 @@ // limitations under the License. //------------------------------------------------------------------------------ -#ifndef NIV_INCLUDE_NIV_SHARED_MEMORY_HPP_ -#define NIV_INCLUDE_NIV_SHARED_MEMORY_HPP_ +#ifndef NIV_INCLUDE_NIV_EXCHANGE_SHARED_MEMORY_HPP_ +#define NIV_INCLUDE_NIV_EXCHANGE_SHARED_MEMORY_HPP_ +#include #include #include #include +SUPPRESS_WARNINGS_BEGIN #include "boost/interprocess/allocators/allocator.hpp" #include "boost/interprocess/managed_shared_memory.hpp" +SUPPRESS_WARNINGS_END #include "conduit/conduit_core.hpp" +#include "conduit/conduit_node.hpp" + +#include "niv/exchange/node_storage.hpp" namespace niv { +namespace exchange { class SharedMemory { public: @@ -42,34 +49,55 @@ class SharedMemory { using SegmentManager = ManagedSharedMemory::segment_manager; template using Allocator = boost::interprocess::allocator; - using DataVector = std::vector>; - using SchemaString = std::vector>; + using DataStorage = std::vector>; + using SchemaStorage = std::vector>; + SharedMemory() = delete; explicit SharedMemory(const Create&); explicit SharedMemory(const Access&); - virtual ~SharedMemory() = default; + SharedMemory(const SharedMemory&) = delete; + SharedMemory(SharedMemory&&) = delete; + virtual ~SharedMemory(); void Destroy(); std::size_t GetFreeSize() const; - void Store(const std::vector& data); - void Store(const std::string& schema); - std::vector GetData() const; - conduit::uint8* GetRawData() const; - std::string GetSchema() const; + void Store(const conduit::Node& node); + void Update(const conduit::Node& node); + conduit::Node Read(); + conduit::Node Listen(); + + void Clear(); + bool IsEmpty() const; static constexpr const char* SegmentName() { return "niv-shared-memory"; } - static constexpr const char* DataVectorName() { return "DataVector"; } - static constexpr const char* SchemaStringName() { return "SchemaString"; } - static constexpr std::size_t InitialSize() { return 65536u; } + static constexpr const char* DataStorageName() { return "DataStorage"; } + static constexpr const char* SchemaStorageName() { return "SchemaStorage"; } + static constexpr const char* ReferenceCountName() { return "ReferenceCount"; } + + static constexpr std::size_t InitialSize() { return 1073741824u; } + + SharedMemory& operator=(const SharedMemory&) = delete; + SharedMemory& operator=(SharedMemory&&) = delete; + + int GetReferenceCount() const; + + private: + SchemaStorage* ConstructSchemaStorage(); + DataStorage* ConstructDataStorage(); + int* ConstructReferenceCount(); + + SchemaStorage* FindSchemaStorage(); + DataStorage* FindDataStorage(); + int* FindReferenceCount(); - protected: ManagedSharedMemory segment_; - DataVector* data_vector_{nullptr}; - SchemaString* schema_string_{nullptr}; + NodeStorage node_storage_; + int* reference_count_; }; +} // namespace exchange } // namespace niv -#endif // NIV_INCLUDE_NIV_SHARED_MEMORY_HPP_ +#endif // NIV_INCLUDE_NIV_EXCHANGE_SHARED_MEMORY_HPP_ diff --git a/niv/include/niv/exchange/shared_memory_synchronization.hpp b/niv/include/niv/exchange/shared_memory_synchronization.hpp new file mode 100644 index 0000000..a70ed8a --- /dev/null +++ b/niv/include/niv/exchange/shared_memory_synchronization.hpp @@ -0,0 +1,70 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#ifndef NIV_INCLUDE_NIV_EXCHANGE_SHARED_MEMORY_SYNCHRONIZATION_HPP_ +#define NIV_INCLUDE_NIV_EXCHANGE_SHARED_MEMORY_SYNCHRONIZATION_HPP_ + +SUPPRESS_WARNINGS_BEGIN +#include "boost/interprocess/sync/named_mutex.hpp" +#include "boost/interprocess/sync/scoped_lock.hpp" +SUPPRESS_WARNINGS_END + +namespace niv { +namespace exchange { + +class SharedMemorySynchronization { + public: + class Create {}; + class Access {}; + + using ManagedMutex = boost::interprocess::named_mutex; + using ManagedScopedLock = boost::interprocess::scoped_lock; + + SharedMemorySynchronization() = delete; + explicit SharedMemorySynchronization( + const SharedMemorySynchronization::Create&); + explicit SharedMemorySynchronization( + const SharedMemorySynchronization::Access&); + SharedMemorySynchronization(const SharedMemorySynchronization&) = delete; + SharedMemorySynchronization(SharedMemorySynchronization&&) = delete; + virtual ~SharedMemorySynchronization() = default; + + SharedMemorySynchronization& operator=(const SharedMemorySynchronization&) = + delete; + SharedMemorySynchronization& operator=(SharedMemorySynchronization&&) = + delete; + + void Destroy(); + + ManagedScopedLock ScopedLock(); + bool TryLock(); + void Unlock(); + + static constexpr const char* MutexName() { return "niv-shared-mutex"; } + + private: + ManagedMutex mutex_; +}; + +} // namespace exchange +} // namespace niv + +#endif // NIV_INCLUDE_NIV_EXCHANGE_SHARED_MEMORY_SYNCHRONIZATION_HPP_ diff --git a/niv/include/niv/nest_test_data.hpp b/niv/include/niv/nest_test_data.hpp new file mode 100644 index 0000000..6f72db5 --- /dev/null +++ b/niv/include/niv/nest_test_data.hpp @@ -0,0 +1,76 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#ifndef NIV_INCLUDE_NIV_NEST_TEST_DATA_HPP_ +#define NIV_INCLUDE_NIV_NEST_TEST_DATA_HPP_ + +#include +#include + +#include "niv/producer/multimeter.hpp" + +namespace niv { + +class Testing { + public: + Testing() = delete; + Testing(const Testing&) = delete; + Testing(Testing&&) = delete; + ~Testing() = delete; + + Testing& operator=(const Testing&) = delete; + Testing& operator=(Testing&&) = delete; +}; + +namespace testing { + +std::string AnyAttribute(); +std::string AnotherAttribute(); +std::string ThirdAttribute(); + +double AnyTime(); + +std::vector AnyAttributesValues(); +std::vector AnotherAttributesValues(); +std::vector ThirdAttributesValues(); + +std::vector AnyValueNames(); + +std::string AnyMultimeterName(); + +conduit::Node AnyNestData(); + +void Send(const conduit::Node& node); + +conduit::Node AnyNode(); + +conduit::Node AnotherNode(); + +conduit::Node Update(); + +conduit::Node UpdatedNode(); + +conduit::Node ADifferentNode(); + +} // namespace testing +} // namespace niv + +#endif // NIV_INCLUDE_NIV_NEST_TEST_DATA_HPP_ diff --git a/niv/include/niv/niv.hpp b/niv/include/niv/niv.hpp index 6cce4f0..aeeb342 100644 --- a/niv/include/niv/niv.hpp +++ b/niv/include/niv/niv.hpp @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // nest in situ vis // -// Copyright (c) 2017 RWTH Aachen University, Germany, +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, // Virtual Reality & Immersive Visualisation Group. //------------------------------------------------------------------------------ // License diff --git a/niv/include/niv/producer/device.hpp b/niv/include/niv/producer/device.hpp new file mode 100644 index 0000000..7d3c4fe --- /dev/null +++ b/niv/include/niv/producer/device.hpp @@ -0,0 +1,70 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#ifndef NIV_INCLUDE_NIV_PRODUCER_DEVICE_HPP_ +#define NIV_INCLUDE_NIV_PRODUCER_DEVICE_HPP_ + +#include +#include + +SUPPRESS_WARNINGS_BEGIN +#include "conduit/conduit_node.hpp" +SUPPRESS_WARNINGS_END + +namespace conduit { + +template <> +DataArray::~DataArray(); + +} // namespace conduit + +namespace niv { +namespace producer { + +class Device { + public: + Device(const Device&) = default; + Device(Device&&) = default; + virtual ~Device() = default; + + void SetRecordingTime(double time); + + virtual void Record(std::size_t); + virtual void Record(std::size_t, const std::vector&); + + Device& operator=(const Device&) = default; + Device& operator=(Device&&) = default; + + protected: + Device(const std::string& name, conduit::Node* node); + + conduit::Node& GetTimestepNode(); + + private: + conduit::Node* node_{nullptr}; + conduit::Node* timestep_node_{nullptr}; + std::string name_{"recorder"}; +}; + +} // namespace producer +} // namespace niv + +#endif // NIV_INCLUDE_NIV_PRODUCER_DEVICE_HPP_ diff --git a/niv/include/niv/producer/multimeter.hpp b/niv/include/niv/producer/multimeter.hpp new file mode 100644 index 0000000..ed4d5bb --- /dev/null +++ b/niv/include/niv/producer/multimeter.hpp @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#ifndef NIV_INCLUDE_NIV_PRODUCER_MULTIMETER_HPP_ +#define NIV_INCLUDE_NIV_PRODUCER_MULTIMETER_HPP_ + +#include +#include +#include +#include + +#include "niv/producer/device.hpp" + +namespace niv { +namespace producer { + +class Multimeter final : public Device { + public: + Multimeter(const std::string& name, + const std::vector& value_names, conduit::Node* node); + Multimeter(const Multimeter&) = default; + Multimeter(Multimeter&&) = default; + virtual ~Multimeter() = default; + + void Record(std::size_t id, const std::vector& values) override; + + Multimeter& operator=(const Multimeter&) = default; + Multimeter& operator=(Multimeter&&) = default; + + static std::unique_ptr New( + const std::string& name, const std::vector& value_names, + conduit::Node* node); + + private: + void RecordValue(std::string id_string, const std::vector values, + std::size_t value_index); + std::string IdString(std::size_t id) const; + + std::vector value_names_; +}; + +} // namespace producer +} // namespace niv + +#endif // NIV_INCLUDE_NIV_PRODUCER_MULTIMETER_HPP_ diff --git a/niv/include/niv/producer/spike_detector.hpp b/niv/include/niv/producer/spike_detector.hpp new file mode 100644 index 0000000..8f0a52d --- /dev/null +++ b/niv/include/niv/producer/spike_detector.hpp @@ -0,0 +1,57 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#ifndef NIV_INCLUDE_NIV_PRODUCER_SPIKE_DETECTOR_HPP_ +#define NIV_INCLUDE_NIV_PRODUCER_SPIKE_DETECTOR_HPP_ + +#include +#include +#include + +#include "niv/producer/device.hpp" + +namespace niv { +namespace producer { + +class SpikeDetector final : public Device { + public: + SpikeDetector(const std::string& name, conduit::Node* node); + SpikeDetector(const SpikeDetector&) = default; + SpikeDetector(SpikeDetector&&) = default; + virtual ~SpikeDetector() = default; + + void Record(std::size_t id) override; + + SpikeDetector& operator=(const SpikeDetector&) = default; + SpikeDetector& operator=(SpikeDetector&&) = default; + + static std::unique_ptr New(const std::string& name, + conduit::Node* node); + + private: + std::vector GetData(const conduit::Node& node); + std::vector AsVector(const conduit::uint64_array& array); +}; + +} // namespace producer +} // namespace niv + +#endif // NIV_INCLUDE_NIV_PRODUCER_SPIKE_DETECTOR_HPP_ diff --git a/niv/include/niv/relay_shared_memory.hpp b/niv/include/niv/relay_shared_memory.hpp deleted file mode 100644 index 8144d61..0000000 --- a/niv/include/niv/relay_shared_memory.hpp +++ /dev/null @@ -1,42 +0,0 @@ -//------------------------------------------------------------------------------ -// nest in situ vis -// -// Copyright (c) 2017 RWTH Aachen University, Germany, -// Virtual Reality & Immersive Visualisation Group. -//------------------------------------------------------------------------------ -// License -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -//------------------------------------------------------------------------------ - -#ifndef NIV_INCLUDE_NIV_RELAY_SHARED_MEMORY_HPP_ -#define NIV_INCLUDE_NIV_RELAY_SHARED_MEMORY_HPP_ - -#include - -#include "niv/shared_memory.hpp" - -namespace niv { - -class RelaySharedMemory { - public: - virtual ~RelaySharedMemory() = default; - - protected: - explicit RelaySharedMemory(std::unique_ptr shared_memory); - std::unique_ptr shared_memory_; -}; - -} // namespace niv - -#endif // NIV_INCLUDE_NIV_RELAY_SHARED_MEMORY_HPP_ diff --git a/niv/include/niv/shared_memory_access.hpp b/niv/include/niv/shared_memory_access.hpp deleted file mode 100644 index 68dc8d6..0000000 --- a/niv/include/niv/shared_memory_access.hpp +++ /dev/null @@ -1,37 +0,0 @@ -//------------------------------------------------------------------------------ -// nest in situ vis -// -// Copyright (c) 2017 RWTH Aachen University, Germany, -// Virtual Reality & Immersive Visualisation Group. -//------------------------------------------------------------------------------ -// License -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -//------------------------------------------------------------------------------ - -#ifndef NIV_INCLUDE_NIV_SHARED_MEMORY_ACCESS_HPP_ -#define NIV_INCLUDE_NIV_SHARED_MEMORY_ACCESS_HPP_ - -#include "niv/shared_memory.hpp" - -namespace niv { - -class SharedMemoryAccess : public SharedMemory { - public: - SharedMemoryAccess(); - virtual ~SharedMemoryAccess() = default; -}; - -} // namespace niv - -#endif // NIV_INCLUDE_NIV_SHARED_MEMORY_ACCESS_HPP_ diff --git a/niv/include/niv/shared_memory_segment.hpp b/niv/src/consumer/backend.cpp similarity index 59% rename from niv/include/niv/shared_memory_segment.hpp rename to niv/src/consumer/backend.cpp index 61c9fa0..9f3516a 100644 --- a/niv/include/niv/shared_memory_segment.hpp +++ b/niv/src/consumer/backend.cpp @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // nest in situ vis // -// Copyright (c) 2017 RWTH Aachen University, Germany, +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, // Virtual Reality & Immersive Visualisation Group. //------------------------------------------------------------------------------ // License @@ -19,30 +19,35 @@ // limitations under the License. //------------------------------------------------------------------------------ -#ifndef NIV_INCLUDE_NIV_SHARED_MEMORY_SEGMENT_HPP_ -#define NIV_INCLUDE_NIV_SHARED_MEMORY_SEGMENT_HPP_ +#include "niv/consumer/backend.hpp" -#include +#include -#include - -#include "boost/interprocess/allocators/allocator.hpp" -#include "boost/interprocess/managed_shared_memory.hpp" - -#include "conduit/conduit_core.hpp" - -#include "niv/shared_memory.hpp" +#include "niv/consumer/receiver.hpp" namespace niv { -class SharedMemorySegment : public SharedMemory { - public: - SharedMemorySegment(); - ~SharedMemorySegment(); - - private: -}; +void consumer::Backend::Connect(niv::consumer::Receiver* receiver) { + receiver->SetNode(&node_); + receiver_ = receiver; +} + +void consumer::Backend::Connect(niv::consumer::Device* device) { + auto found = std::find(devices_.begin(), devices_.end(), device); + if (found == devices_.end()) { + device->SetNode(&node_); + devices_.push_back(device); + } +} + +void consumer::Backend::Receive() { + if (receiver_ != nullptr) { + receiver_->Receive(); + } + + for (auto device : devices_) { + device->Update(); + } +} } // namespace niv - -#endif // NIV_INCLUDE_NIV_SHARED_MEMORY_SEGMENT_HPP_ diff --git a/niv/src/consumer/device.cpp b/niv/src/consumer/device.cpp new file mode 100644 index 0000000..b9e4adc --- /dev/null +++ b/niv/src/consumer/device.cpp @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include "niv/consumer/device.hpp" + +#include + +#include +#include +#include + +namespace niv { +namespace consumer { + +Device::Device(const std::string& name) : name_{name} {} + +const std::vector& Device::GetTimesteps() { + timesteps_.clear(); + + const conduit::Node* device_node{nullptr}; + try { + device_node = &node_->fetch_child(name_); + } catch (...) { + return timesteps_; + } + + const std::string device_node_path{device_node->path()}; + for (auto i = 0u; i < device_node->number_of_children(); ++i) { + const conduit::Node& curr_child{device_node->child(i)}; + const std::string child_path{curr_child.path()}; + const std::string child_local_path{ + child_path.substr(device_node_path.size() + 1, + child_path.size() - device_node_path.size() - 1)}; + timesteps_.push_back(std::strtof(child_local_path.c_str(), nullptr)); + } + + std::sort(timesteps_.begin(), timesteps_.end()); + return timesteps_; +} + +void Device::SetTime(double time) { time_ = time; } + +void Device::SetTimestepNode() { + std::stringstream time_stream; + time_stream << time_; + try { + timestep_node_ = &node_->fetch_child(name_ + "/" + time_stream.str()); + } catch (...) { + } +} + +const conduit::Node* Device::GetTimestepNode() const { return timestep_node_; } + +} // namespace consumer +} // namespace niv diff --git a/niv/src/conduit_receiver.cpp b/niv/src/consumer/multimeter.cpp similarity index 53% rename from niv/src/conduit_receiver.cpp rename to niv/src/consumer/multimeter.cpp index 4e15be3..924b62b 100644 --- a/niv/src/conduit_receiver.cpp +++ b/niv/src/consumer/multimeter.cpp @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // nest in situ vis // -// Copyright (c) 2017 RWTH Aachen University, Germany, +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, // Virtual Reality & Immersive Visualisation Group. //------------------------------------------------------------------------------ // License @@ -19,16 +19,41 @@ // limitations under the License. //------------------------------------------------------------------------------ -#include "niv/conduit_receiver.hpp" +#include "niv/consumer/multimeter.hpp" #include +#include namespace niv { +namespace consumer { -void ConduitReceiver::Start() { relay_.Listen(&node_); } +Multimeter::Multimeter(const std::string& name) : consumer::Device{name} {} -double ConduitReceiver::Get(const std::string& path) const { - return node_.fetch(path).as_double(); +void Multimeter::SetAttribute(const std::string& attribute) { + attribute_ = attribute; } +void Multimeter::Update() { + SetTimestepNode(); + SetValues(); +} + +void Multimeter::SetValues() { + values_.clear(); + if (GetTimestepNode() == nullptr) { + return; + } + try { + const conduit::Node* attribute_node = + &GetTimestepNode()->fetch_child(attribute_); + for (auto i = 0u; i < attribute_node->number_of_children(); ++i) { + values_.push_back(attribute_node->child(i).as_double()); + } + } catch (...) { + } +} + +const std::vector& Multimeter::GetValues() const { return values_; } + +} // namespace consumer } // namespace niv diff --git a/niv/src/shared_memory_access.cpp b/niv/src/consumer/receiver.cpp similarity index 82% rename from niv/src/shared_memory_access.cpp rename to niv/src/consumer/receiver.cpp index edbb51c..6c49946 100644 --- a/niv/src/shared_memory_access.cpp +++ b/niv/src/consumer/receiver.cpp @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // nest in situ vis // -// Copyright (c) 2017 RWTH Aachen University, Germany, +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, // Virtual Reality & Immersive Visualisation Group. //------------------------------------------------------------------------------ // License @@ -19,10 +19,12 @@ // limitations under the License. //------------------------------------------------------------------------------ -#include "niv/shared_memory_access.hpp" +#include "niv/consumer/receiver.hpp" namespace niv { +namespace consumer { -SharedMemoryAccess::SharedMemoryAccess() : SharedMemory{Access()} {} +void Receiver::Receive() { node_->update(relay_.Receive()); } +} // namespace consumer } // namespace niv diff --git a/niv/src/exchange/relay_shared_memory.cpp b/niv/src/exchange/relay_shared_memory.cpp new file mode 100644 index 0000000..a58b7ad --- /dev/null +++ b/niv/src/exchange/relay_shared_memory.cpp @@ -0,0 +1,89 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include "niv/exchange/relay_shared_memory.hpp" + +#include +#include +#include + +#include "conduit/conduit_core.hpp" +#include "conduit/conduit_schema.hpp" + +#include "niv/exchange/shared_memory.hpp" +#include "niv/exchange/shared_memory_synchronization.hpp" + +namespace niv { +namespace exchange { + +RelaySharedMemory::RelaySharedMemory() { + try { + shared_memory_ = std::make_unique(SharedMemory::Create()); + synchronization_ = std::make_unique( + SharedMemorySynchronization::Create()); + } catch (...) { + shared_memory_ = std::make_unique(SharedMemory::Access()); + synchronization_ = std::make_unique( + SharedMemorySynchronization::Access()); + } +} + +RelaySharedMemory::RelaySharedMemory(const CreateSharedMemory&) + : shared_memory_{std::make_unique(SharedMemory::Create())}, + synchronization_{std::make_unique( + SharedMemorySynchronization::Create())} {} + +RelaySharedMemory::RelaySharedMemory(const AccessSharedMemory&) + : shared_memory_{std::make_unique(SharedMemory::Access())}, + synchronization_{std::make_unique( + SharedMemorySynchronization::Access())} {} + +RelaySharedMemory::~RelaySharedMemory() { + if (shared_memory_->GetReferenceCount() == 0) { + shared_memory_->Destroy(); + synchronization_->Destroy(); + } +} + +void RelaySharedMemory::Send(const conduit::Node& node) { + auto lock = synchronization_->ScopedLock(); + if (IsEmpty()) { + shared_memory_->Store(node); + } else { + SendUpdate(node); + } +} + +void RelaySharedMemory::SendUpdate(const conduit::Node& node) { + GetSharedMemory()->Update(node); +} + +conduit::Node RelaySharedMemory::Receive() { + auto lock = synchronization_->ScopedLock(); + auto received_data = shared_memory_->Read(); + GetSharedMemory()->Clear(); + return received_data; +} + +bool RelaySharedMemory::IsEmpty() const { return GetSharedMemory()->IsEmpty(); } + +} // namespace exchange +} // namespace niv diff --git a/niv/src/exchange/shared_memory.cpp b/niv/src/exchange/shared_memory.cpp new file mode 100644 index 0000000..13431a6 --- /dev/null +++ b/niv/src/exchange/shared_memory.cpp @@ -0,0 +1,102 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include "niv/exchange/shared_memory.hpp" + +#include +#include +#include + +#include "conduit/conduit_node.hpp" +#include "conduit/conduit_schema.hpp" + +namespace niv { +namespace exchange { + +SharedMemory::SharedMemory(const Create&) + : segment_{boost::interprocess::create_only, SegmentName(), InitialSize()}, + node_storage_{ConstructSchemaStorage(), ConstructDataStorage()}, + reference_count_{ConstructReferenceCount()} {} + +SharedMemory::SchemaStorage* SharedMemory::ConstructSchemaStorage() { + return segment_.construct(SchemaStorageName())( + SchemaStorage::allocator_type(segment_.get_segment_manager())); +} + +SharedMemory::DataStorage* SharedMemory::ConstructDataStorage() { + return segment_.construct(DataStorageName())( + DataStorage::allocator_type(segment_.get_segment_manager())); +} + +int* SharedMemory::ConstructReferenceCount() { + return segment_.construct(ReferenceCountName())(0); +} + +SharedMemory::SharedMemory(const Access&) + : segment_{boost::interprocess::open_only, SegmentName()}, + node_storage_{FindSchemaStorage(), FindDataStorage()}, + reference_count_{FindReferenceCount()} { + ++(*reference_count_); +} + +SharedMemory::SchemaStorage* SharedMemory::FindSchemaStorage() { + return segment_.find(SchemaStorageName()).first; +} + +SharedMemory::DataStorage* SharedMemory::FindDataStorage() { + return segment_.find(DataStorageName()).first; +} + +int* SharedMemory::FindReferenceCount() { + return segment_.find(ReferenceCountName()).first; +} + +SharedMemory::~SharedMemory() { --(*reference_count_); } + +void SharedMemory::Destroy() { + segment_.destroy(SchemaStorageName()); + segment_.destroy(DataStorageName()); + boost::interprocess::shared_memory_object::remove(SegmentName()); +} + +std::size_t SharedMemory::GetFreeSize() const { + return segment_.get_free_memory(); +} + +void SharedMemory::Store(const conduit::Node& node) { + node_storage_.Store(node); +} + +void SharedMemory::Update(const conduit::Node& node) { + node_storage_.Update(node); +} + +conduit::Node SharedMemory::Read() { return node_storage_.Read(); } + +conduit::Node SharedMemory::Listen() { return node_storage_.Listen(); } + +void SharedMemory::Clear() { node_storage_.Clear(); } +bool SharedMemory::IsEmpty() const { return node_storage_.IsEmpty(); } + +int SharedMemory::GetReferenceCount() const { return *reference_count_; } + +} // namespace exchange +} // namespace niv diff --git a/niv/src/exchange/shared_memory_synchronization.cpp b/niv/src/exchange/shared_memory_synchronization.cpp new file mode 100644 index 0000000..aa8e2d3 --- /dev/null +++ b/niv/src/exchange/shared_memory_synchronization.cpp @@ -0,0 +1,54 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include "niv/exchange/shared_memory_synchronization.hpp" + +SUPPRESS_WARNINGS_BEGIN +#include "boost/interprocess/sync/named_mutex.hpp" +#include "boost/interprocess/sync/scoped_lock.hpp" +SUPPRESS_WARNINGS_END + +namespace niv { +namespace exchange { + +SharedMemorySynchronization::SharedMemorySynchronization( + const SharedMemorySynchronization::Create&) + : mutex_{boost::interprocess::create_only, MutexName()} {} + +SharedMemorySynchronization::SharedMemorySynchronization( + const SharedMemorySynchronization::Access&) + : mutex_{boost::interprocess::open_only, MutexName()} {} + +void SharedMemorySynchronization::Destroy() { + ManagedMutex::remove(MutexName()); +} + +SharedMemorySynchronization::ManagedScopedLock +SharedMemorySynchronization::ScopedLock() { + return ManagedScopedLock(mutex_); +} + +bool SharedMemorySynchronization::TryLock() { return mutex_.try_lock(); } + +void SharedMemorySynchronization::Unlock() { mutex_.unlock(); } + +} // namespace exchange +} // namespace niv diff --git a/niv/src/nest_test_data.cpp b/niv/src/nest_test_data.cpp new file mode 100644 index 0000000..9e35f5b --- /dev/null +++ b/niv/src/nest_test_data.cpp @@ -0,0 +1,112 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include "niv/nest_test_data.hpp" + +#include +#include + +#include "niv/exchange/relay_shared_memory.hpp" + +namespace niv { +namespace testing { + +std::string AnyAttribute() { return "V_m"; } +std::string AnotherAttribute() { return "g_m"; } +std::string ThirdAttribute() { return "g_i"; } + +double AnyTime() { return 0.0; } + +std::vector AnyAttributesValues() { + return std::vector{0.0, -0.1, 0.2, -0.3, 0.4, -0.5}; +} +std::vector AnotherAttributesValues() { + return std::vector{1.0, -1.1, 1.2, -1.3, 1.4, -1.5}; +} +std::vector ThirdAttributesValues() { + return std::vector{-2.01, 3.12, -4.23, 5.34, -6.45, 7.56}; +} + +std::vector AnyValueNames() { + return std::vector{AnyAttribute(), AnotherAttribute(), + ThirdAttribute()}; +} + +std::string AnyMultimeterName() { return "multimeter A"; } + +conduit::Node AnyNestData() { + conduit::Node node; + niv::producer::Multimeter multimeter(AnyMultimeterName(), AnyValueNames(), + &node); + multimeter.SetRecordingTime(AnyTime()); + for (auto i = 0u; i < AnyAttributesValues().size(); ++i) { + multimeter.Record(i, std::vector{AnyAttributesValues()[i], + AnotherAttributesValues()[i], + ThirdAttributesValues()[i]}); + } + + return node; +} + +void Send(const conduit::Node& node) { + exchange::RelaySharedMemory relay; + relay.Send(node); +} + +conduit::Node AnyNode() { + conduit::Node node; + node["A/B/C"] = 3.1415; + node["A/B/D"] = 4.124; + node["A/E"] = 42.0; + return node; +} + +conduit::Node AnotherNode() { + conduit::Node node; + node["A/B/C"] = 2.0 * 3.1415; + node["A/B/D"] = 3.0 * 4.124; + node["A/E"] = 4.0 * 42.0; + return node; +} + +conduit::Node Update() { + conduit::Node node; + node["A/B/F"] = 2.0 * 3.1415; + node["A/B/G"] = 3.0 * 4.124; + node["A/H"] = 4.0 * 42.0; + return node; +} + +conduit::Node UpdatedNode() { + conduit::Node node; + node["A/B/C"] = 3.1415; + node["A/B/D"] = 4.124; + node["A/E"] = 42.0; + node["A/B/F"] = 2.0 * 3.1415; + node["A/B/G"] = 3.0 * 4.124; + node["A/H"] = 4.0 * 42.0; + return node; +} + +conduit::Node ADifferentNode() { return Update(); } + +} // namespace testing +} // namespace niv diff --git a/niv/src/niv.cpp b/niv/src/niv.cpp index 5174553..03294d1 100644 --- a/niv/src/niv.cpp +++ b/niv/src/niv.cpp @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // nest in situ vis // -// Copyright (c) 2017 RWTH Aachen University, Germany, +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, // Virtual Reality & Immersive Visualisation Group. //------------------------------------------------------------------------------ // License diff --git a/niv/include/niv/conduit_receiver.hpp b/niv/src/producer/device.cpp similarity index 59% rename from niv/include/niv/conduit_receiver.hpp rename to niv/src/producer/device.cpp index 3e061e9..d4ec352 100644 --- a/niv/include/niv/conduit_receiver.hpp +++ b/niv/src/producer/device.cpp @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // nest in situ vis // -// Copyright (c) 2017 RWTH Aachen University, Germany, +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, // Virtual Reality & Immersive Visualisation Group. //------------------------------------------------------------------------------ // License @@ -19,30 +19,33 @@ // limitations under the License. //------------------------------------------------------------------------------ -#ifndef NIV_INCLUDE_NIV_CONDUIT_RECEIVER_HPP_ -#define NIV_INCLUDE_NIV_CONDUIT_RECEIVER_HPP_ - -#include +#include #include +#include -#include "conduit/conduit_node.hpp" - -#include "niv/receiving_relay_shared_memory.hpp" -#include "niv/shared_memory_segment.hpp" +#include "niv/producer/device.hpp" namespace niv { +namespace producer { -class ConduitReceiver { - public: - void Start(); - double Get(const std::string& path) const; +Device::Device(const std::string& name, conduit::Node* node) + : node_(node), name_(name) {} - private: - niv::ReceivingRelaySharedMemory relay_{ - std::make_unique()}; - conduit::Node node_; -}; +void Device::SetRecordingTime(double time) { + std::stringstream time_stream; + time_stream << time; + timestep_node_ = &(*node_)[name_][time_stream.str()]; +} -} // namespace niv +void Device::Record(std::size_t) {} +void Device::Record(std::size_t, const std::vector&) {} -#endif // NIV_INCLUDE_NIV_CONDUIT_RECEIVER_HPP_ +conduit::Node& Device::GetTimestepNode() { + if (timestep_node_ == nullptr) { + SetRecordingTime(0.0); + } + return *timestep_node_; +} + +} // namespace producer +} // namespace niv diff --git a/niv/src/producer/multimeter.cpp b/niv/src/producer/multimeter.cpp new file mode 100644 index 0000000..53f92e9 --- /dev/null +++ b/niv/src/producer/multimeter.cpp @@ -0,0 +1,64 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include +#include +#include + +#include "niv/producer/multimeter.hpp" + +namespace niv { +namespace producer { + +Multimeter::Multimeter(const std::string& name, + const std::vector& value_names, + conduit::Node* node) + : Device{name, node}, value_names_{value_names} {} + +void Multimeter::Record(std::size_t id, const std::vector& values) { + const std::string id_string{IdString(id)}; + for (std::size_t i = 0; i < value_names_.size(); ++i) { + RecordValue(id_string, values, i); + } +} + +void Multimeter::RecordValue(std::string id_string, + const std::vector values, + std::size_t value_index) { + const std::string& value_name = value_names_[value_index]; + const double value = values[value_index]; + GetTimestepNode()[value_name][id_string] = value; +} + +std::string Multimeter::IdString(std::size_t id) const { + std::stringstream id_stream; + id_stream << id; + return id_stream.str(); +} + +std::unique_ptr Multimeter::New( + const std::string& name, const std::vector& value_names, + conduit::Node* node) { + return std::make_unique(name, value_names, node); +} + +} // namespace producer +} // namespace niv diff --git a/niv/src/producer/spike_detector.cpp b/niv/src/producer/spike_detector.cpp new file mode 100644 index 0000000..1998ebe --- /dev/null +++ b/niv/src/producer/spike_detector.cpp @@ -0,0 +1,62 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include +#include +#include + +#include "niv/producer/spike_detector.hpp" + +namespace niv { +namespace producer { + +SpikeDetector::SpikeDetector(const std::string& name, conduit::Node* node) + : Device{name, node} {} + +void SpikeDetector::Record(std::size_t id) { + std::vector data(GetData(GetTimestepNode())); + data.push_back(id); + GetTimestepNode().set_uint64_vector(data); +} + +std::vector SpikeDetector::GetData(const conduit::Node& node) { + if (node.total_bytes_allocated() != 0) { + return AsVector(node.as_uint64_array()); + } + return std::vector(); +} + +std::vector SpikeDetector::AsVector( + const conduit::uint64_array& array) { + const std::size_t num_elements = + static_cast(array.number_of_elements()); + const auto* begin = reinterpret_cast(array.data_ptr()); + const auto* end = begin + num_elements; + return std::vector(begin, end); +} + +std::unique_ptr SpikeDetector::New(const std::string& name, + conduit::Node* node) { + return std::make_unique(name, node); +} + +} // namespace producer +} // namespace niv diff --git a/niv/src/receiving_relay_shared_memory.cpp b/niv/src/receiving_relay_shared_memory.cpp deleted file mode 100644 index 9830240..0000000 --- a/niv/src/receiving_relay_shared_memory.cpp +++ /dev/null @@ -1,48 +0,0 @@ -//------------------------------------------------------------------------------ -// nest in situ vis -// -// Copyright (c) 2017 RWTH Aachen University, Germany, -// Virtual Reality & Immersive Visualisation Group. -//------------------------------------------------------------------------------ -// License -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -//------------------------------------------------------------------------------ - -#include "niv/receiving_relay_shared_memory.hpp" - -#include -#include - -#include "conduit/conduit_node.hpp" -#include "conduit/conduit_schema.hpp" - -namespace niv { - -ReceivingRelaySharedMemory::ReceivingRelaySharedMemory( - std::unique_ptr shared_memory) - : RelaySharedMemory{std::move(shared_memory)} {} - -void ReceivingRelaySharedMemory::Receive(conduit::Node* node) { - auto schema = shared_memory_->GetSchema(); - auto data = shared_memory_->GetData(); - node->set_data_using_schema(conduit::Schema(schema), data.data()); -} - -void ReceivingRelaySharedMemory::Listen(conduit::Node* node) { - auto schema = shared_memory_->GetSchema(); - auto raw_data = shared_memory_->GetRawData(); - node->set_external_data_using_schema(conduit::Schema(schema), raw_data); -} - -} // namespace niv diff --git a/niv/src/relay_shared_memory.cpp b/niv/src/relay_shared_memory.cpp deleted file mode 100644 index 9d3877e..0000000 --- a/niv/src/relay_shared_memory.cpp +++ /dev/null @@ -1,35 +0,0 @@ -//------------------------------------------------------------------------------ -// nest in situ vis -// -// Copyright (c) 2017 RWTH Aachen University, Germany, -// Virtual Reality & Immersive Visualisation Group. -//------------------------------------------------------------------------------ -// License -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -//------------------------------------------------------------------------------ - -#include "niv/relay_shared_memory.hpp" - -#include -#include - -#include "niv/shared_memory_segment.hpp" - -namespace niv { - -RelaySharedMemory::RelaySharedMemory( - std::unique_ptr shared_memory) - : shared_memory_{std::move(shared_memory)} {} - -} // namespace niv diff --git a/niv/src/shared_memory.cpp b/niv/src/shared_memory.cpp deleted file mode 100644 index d3f184a..0000000 --- a/niv/src/shared_memory.cpp +++ /dev/null @@ -1,72 +0,0 @@ -//------------------------------------------------------------------------------ -// nest in situ vis -// -// Copyright (c) 2017 RWTH Aachen University, Germany, -// Virtual Reality & Immersive Visualisation Group. -//------------------------------------------------------------------------------ -// License -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -//------------------------------------------------------------------------------ - -#include "niv/shared_memory.hpp" - -#include -#include -#include - -namespace niv { - -SharedMemory::SharedMemory(const Create&) - : segment_{boost::interprocess::create_only, SegmentName(), InitialSize()}, - data_vector_{segment_.construct(DataVectorName())( - DataVector::allocator_type(segment_.get_segment_manager()))}, - schema_string_{segment_.construct(SchemaStringName())( - SchemaString::allocator_type(segment_.get_segment_manager()))} {} -SharedMemory::SharedMemory(const Access&) - : segment_{boost::interprocess::open_only, SegmentName()}, - data_vector_{segment_.find(DataVectorName()).first}, - schema_string_{segment_.find(SchemaStringName()).first} {} - -void SharedMemory::Destroy() { - segment_.destroy(DataVectorName()); - segment_.destroy(SchemaStringName()); - boost::interprocess::shared_memory_object::remove(SegmentName()); -} - -std::size_t SharedMemory::GetFreeSize() const { - return segment_.get_free_memory(); -} - -void SharedMemory::Store(const std::vector& data) { - data_vector_->assign(data.begin(), data.end()); -} - -void SharedMemory::Store(const std::string& schema) { - schema_string_->assign(schema.begin(), schema.end()); -} - -std::vector SharedMemory::GetData() const { - return std::vector{data_vector_->begin(), - data_vector_->end()}; -} - -conduit::uint8* SharedMemory::GetRawData() const { - return data_vector_->data(); -} - -std::string SharedMemory::GetSchema() const { - return std::string{schema_string_->begin(), schema_string_->end()}; -} - -} // namespace niv diff --git a/niv/src/shared_memory_segment.cpp b/niv/src/shared_memory_segment.cpp deleted file mode 100644 index c1f8e43..0000000 --- a/niv/src/shared_memory_segment.cpp +++ /dev/null @@ -1,32 +0,0 @@ -//------------------------------------------------------------------------------ -// nest in situ vis -// -// Copyright (c) 2017 RWTH Aachen University, Germany, -// Virtual Reality & Immersive Visualisation Group. -//------------------------------------------------------------------------------ -// License -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -//------------------------------------------------------------------------------ - -#include "niv/shared_memory_segment.hpp" - -#include - -namespace niv { - -SharedMemorySegment::SharedMemorySegment() : SharedMemory{Create()} {} - -SharedMemorySegment::~SharedMemorySegment() { Destroy(); } - -} // namespace niv diff --git a/niv/tests/CMakeLists.txt b/niv/tests/CMakeLists.txt index f1cf95d..61394f0 100644 --- a/niv/tests/CMakeLists.txt +++ b/niv/tests/CMakeLists.txt @@ -1,7 +1,7 @@ #------------------------------------------------------------------------------- # nest in situ vis # -# Copyright (c) 2017 RWTH Aachen University, Germany, +# Copyright (c) 2017-2018 RWTH Aachen University, Germany, # Virtual Reality & Immersive Visualisation Group. #------------------------------------------------------------------------------- # License @@ -19,8 +19,8 @@ # limitations under the License. #------------------------------------------------------------------------------- -file(GLOB NIV_TEST_SOURCES src/*.cpp) -file(GLOB NIV_TEST_HEADERS src/*.hpp) +file(GLOB NIV_TEST_SOURCES src/*.cpp src/**/*.cpp) +file(GLOB NIV_TEST_HEADERS src/*.hpp src/**/*.hpp) file(GLOB NIV_TEST_UTILITIES_TEST_SOURCES test_utilities/tests/src/*.cpp) file(GLOB NIV_TEST_UTILITIES_HEADERS test_utilities/*.hpp) @@ -29,10 +29,15 @@ add_test_catch(NAME "niv-tests" SOURCES ${NIV_TEST_SOURCES} ${NIV_TEST_UTILITIES_TEST_SOURCES} HEADERS ${NIV_TEST_HEADERS} ${NIV_TEST_UTILITIES_HEADERS} CATCH_MAIN src/tests.cpp - INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR} + INCLUDE_DIRECTORIES + ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src LINK_LIBRARIES niv PATH_TO_ADD ${PROJECT_BINARY_DIR}/niv ) + +set_tests_properties("src__exchange__test_relay_shared_memory_mutex" PROPERTIES TIMEOUT 2.0) +set_tests_properties("src__exchange__test_relay_shared_memory_threaded" PROPERTIES TIMEOUT 0.1) + add_test_cpplint(NAME "niv-tests--cpplint" ${NIV_TEST_SOURCES} ${NIV_TEST_HEADERS} diff --git a/niv/tests/src/conduit_node_helper.hpp b/niv/tests/src/conduit_node_helper.hpp new file mode 100644 index 0000000..f80ed51 --- /dev/null +++ b/niv/tests/src/conduit_node_helper.hpp @@ -0,0 +1,65 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#ifndef NIV_TESTS_SRC_CONDUIT_NODE_HELPER_HPP_ +#define NIV_TESTS_SRC_CONDUIT_NODE_HELPER_HPP_ + +#include + +#include "catch/catch.hpp" + +#include "conduit/conduit_node.hpp" + +namespace Catch { +template <> +struct StringMaker { + static std::string convert(const conduit::Node& node) { + return node.to_json(); + } +}; + +namespace Matchers { + +class ConduitNodeEquals : public Catch::MatcherBase { + public: + explicit ConduitNodeEquals(const conduit::Node& node) : node_{node} {} + bool match(const conduit::Node& node) const override { + return Catch::Matchers::Equals(node.to_json()).match(node_.to_json()); + } + std::string describe() const override { + return Catch::Matchers::Equals(node_.to_json()).describe(); + } + + private: + const conduit::Node& node_; +}; + +inline ConduitNodeEquals Equals(const conduit::Node& node) { + return ConduitNodeEquals(node); +} + +} // namespace Matchers + +} // namespace Catch + +using Catch::Matchers::Equals; + +#endif // NIV_TESTS_SRC_CONDUIT_NODE_HELPER_HPP_ diff --git a/niv/tests/src/consumer/test_backend.cpp b/niv/tests/src/consumer/test_backend.cpp new file mode 100644 index 0000000..cea21b1 --- /dev/null +++ b/niv/tests/src/consumer/test_backend.cpp @@ -0,0 +1,87 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include "catch/catch.hpp" + +#include "conduit/conduit_node.hpp" + +#include "niv/consumer/backend.hpp" +#include "niv/consumer/device.hpp" +#include "niv/consumer/receiver.hpp" + +namespace { + +class Receiver : public niv::consumer::Receiver { + public: + void Receive() override { ++count_receives_; } + + std::size_t GetCountReceives() const { return count_receives_; } + + private: + std::size_t count_receives_{0u}; +}; + +class Device : public niv::consumer::Device { + public: + Device() : niv::consumer::Device("any_name") {} + void Update() override { ++count_updates_; } + + std::size_t GetCountUpdates() const { return count_updates_; } + + private: + std::size_t count_updates_{0u}; +}; + +} // namespace + +SCENARIO("A consumer::Backend feeds data into the connected devices", + "[niv][niv::consumer][niv::consumer::Backend]") { + GIVEN("A backend") { + niv::consumer::Backend backend; + + WHEN("a receiver is connected") { + ::Receiver receiver; + backend.Connect(&receiver); + + WHEN("the backend receives") { + backend.Receive(); + THEN("the receiver has received once") { + REQUIRE(receiver.GetCountReceives() == 1); + } + + WHEN("when a device is connected") { + ::Device device; + backend.Connect(&device); + + WHEN("the backend receives again") { + backend.Receive(); + THEN("the receiver was updated twice") { + REQUIRE(receiver.GetCountReceives() == 2); + } + THEN("the device was updated once") { + REQUIRE(device.GetCountUpdates() == 1); + } + } + } + } + } + } +} diff --git a/niv/tests/src/consumer/test_device.cpp b/niv/tests/src/consumer/test_device.cpp new file mode 100644 index 0000000..e25ef2b --- /dev/null +++ b/niv/tests/src/consumer/test_device.cpp @@ -0,0 +1,61 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include +#include + +#include "catch/catch.hpp" + +#include "niv/consumer/device.hpp" +#include "niv/nest_test_data.hpp" + +namespace { + +class Device : public niv::consumer::Device { + public: + explicit Device(const std::string& name) : niv::consumer::Device(name) {} + Device(const Device&) = delete; + Device(Device&&) = delete; + ~Device() = default; + + Device& operator=(const Device&) = delete; + Device& operator=(Device&&) = delete; + + void Update() override {} +}; + +} // namespace + +SCENARIO("A consumer::Device can list its timesteps", + "[niv][niv::consumer][niv::consumer::Device]") { + GIVEN("A device accessing a node") { + conduit::Node any_data{niv::testing::AnyNestData()}; + ::Device device(niv::testing::AnyMultimeterName()); + device.SetNode(&any_data); + WHEN("The device is asked for the timesteps") { + auto timesteps(device.GetTimesteps()); + THEN("the list of timesteps is correct") { + REQUIRE_THAT(timesteps, + Catch::Matchers::Equals(std::vector{0})); + } + } + } +} diff --git a/niv/tests/src/consumer/test_integration.cpp b/niv/tests/src/consumer/test_integration.cpp new file mode 100644 index 0000000..8ff1d1a --- /dev/null +++ b/niv/tests/src/consumer/test_integration.cpp @@ -0,0 +1,60 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include + +#include "catch/catch.hpp" + +#include "niv/consumer/backend.hpp" +#include "niv/consumer/multimeter.hpp" +#include "niv/consumer/receiver.hpp" +#include "niv/nest_test_data.hpp" + +SCENARIO("Consumer integration", "[niv][integration]") { + GIVEN("The required objects") { + niv::consumer::Backend backend; + + niv::consumer::Receiver receiver; + backend.Connect(&receiver); + + niv::consumer::Multimeter multimeter(niv::testing::AnyMultimeterName()); + multimeter.SetAttribute(niv::testing::AnyValueNames()[0]); + backend.Connect(&multimeter); + + WHEN("The data producer sends data") { + niv::testing::Send(niv::testing::AnyNestData()); + + WHEN("the consuming side receives") { + backend.Receive(); + + WHEN("the multimeter queries the data") { + multimeter.SetTime(niv::testing::AnyTime()); + multimeter.Update(); + std::vector values{multimeter.GetValues()}; + + THEN("the received values are correct") { + REQUIRE(values == niv::testing::AnyAttributesValues()); + } + } + } + } + } +} diff --git a/niv/tests/src/consumer/test_multimeter.cpp b/niv/tests/src/consumer/test_multimeter.cpp new file mode 100644 index 0000000..b3f4d2d --- /dev/null +++ b/niv/tests/src/consumer/test_multimeter.cpp @@ -0,0 +1,61 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include + +#include "catch/catch.hpp" + +#include "conduit/conduit_node.hpp" + +#include "niv/consumer/multimeter.hpp" +#include "niv/nest_test_data.hpp" + +SCENARIO("a Multimeter provides access to data stored in a conduit node", + "[niv][niv::consumer][niv::consumer::Multimeter]") { + GIVEN("A Multimeter with some data") { + const conduit::Node nest_data{niv::testing::AnyNestData()}; + niv::consumer::Multimeter multimeter(niv::testing::AnyMultimeterName()); + multimeter.SetNode(&nest_data); + + WHEN("The time step is set") { + multimeter.SetTime(niv::testing::AnyTime()); + WHEN("one attribute is queried") { + multimeter.SetAttribute(niv::testing::AnyValueNames()[0]); + multimeter.Update(); + auto result = multimeter.GetValues(); + THEN("the result is correct") { + REQUIRE_THAT(result, Catch::Matchers::Equals( + niv::testing::AnyAttributesValues())); + } + } + WHEN("another attribute is queried") { + multimeter.SetAttribute(niv::testing::AnyValueNames()[1]); + multimeter.Update(); + + auto result = multimeter.GetValues(); + THEN("the result is correct") { + REQUIRE_THAT(result, Catch::Matchers::Equals( + niv::testing::AnotherAttributesValues())); + } + } + } + } +} diff --git a/niv/tests/src/consumer/test_receiver.cpp b/niv/tests/src/consumer/test_receiver.cpp new file mode 100644 index 0000000..b66ed83 --- /dev/null +++ b/niv/tests/src/consumer/test_receiver.cpp @@ -0,0 +1,54 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include "catch/catch.hpp" + +#include "niv/consumer/receiver.hpp" +#include "niv/exchange/relay_shared_memory.hpp" +#include "niv/nest_test_data.hpp" + +#include "conduit_node_helper.hpp" + +SCENARIO("received data is aggregated in the consumer::Receiver", + "[niv][niv::consumer][niv::consumer::Receiver]") { + GIVEN("A SchnchronizedAggregatingReceiver and a sending relay") { + niv::consumer::Receiver receiver; + conduit::Node receiving_node; + receiver.SetNode(&receiving_node); + niv::exchange::RelaySharedMemory sender; + + WHEN("Data is sent and a receive is triggered") { + sender.Send(niv::testing::AnyNode()); + receiver.Receive(); + THEN("it is received correctly") { + REQUIRE_THAT(receiving_node, Equals(niv::testing::AnyNode())); + } + + WHEN("an update is sent and a receive is triggered") { + sender.Send(niv::testing::Update()); + receiver.Receive(); + THEN("then the data has been updated") { + REQUIRE_THAT(receiving_node, Equals(niv::testing::UpdatedNode())); + } + } + } + } +} diff --git a/niv/tests/src/exchange/test_node_storage.cpp b/niv/tests/src/exchange/test_node_storage.cpp new file mode 100644 index 0000000..7d0a40d --- /dev/null +++ b/niv/tests/src/exchange/test_node_storage.cpp @@ -0,0 +1,99 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include +#include +#include + +#include "catch/catch.hpp" + +#include "niv/exchange/node_storage.hpp" +#include "niv/nest_test_data.hpp" + +#include "conduit_node_helper.hpp" + +namespace { + +using NodeStorageBase = + niv::exchange::NodeStorage>; + +class NodeStorage : public NodeStorageBase { + public: + NodeStorage() + : NodeStorageBase{new std::string, new std::vector}, + owned_schema_storage_{GetSchemaStorage()}, + owned_data_storage_{GetDataStorage()} {} + NodeStorage(const NodeStorage&) = delete; + NodeStorage(NodeStorage&&) = default; + ~NodeStorage() = default; + + NodeStorage& operator=(const NodeStorage&) = delete; + NodeStorage& operator=(NodeStorage&&) = default; + + private: + std::unique_ptr owned_schema_storage_; + std::unique_ptr> owned_data_storage_; +}; + +} // namespace + +SCENARIO("storing and reading a node", "[niv][niv::NodeStorage]") { + GIVEN("a node storage") { + ::NodeStorage storage; + WHEN("a node is stored") { + storage.Store(niv::testing::AnyNode()); + THEN("it can be read") { + REQUIRE_THAT(storage.Read(), Equals(niv::testing::AnyNode())); + } + } + } +} + +SCENARIO("a node can be stored and read multiple times", + "[niv][niv::NodeStorage]") { + GIVEN("a node stored and read back") { + ::NodeStorage storage; + storage.Store(niv::testing::AnyNode()); + storage.Store(storage.Read()); + + WHEN("the node is read") { + conduit::Node read_node{storage.Read()}; + THEN("it is equal to the initial one") { + REQUIRE_THAT(read_node, Equals(niv::testing::AnyNode())); + } + } + } +} + +SCENARIO("a node can be listening to changes", "[niv][niv::NodeStorage]") { + GIVEN("a node listening to data") { + ::NodeStorage storage; + storage.Store(niv::testing::AnyNode()); + conduit::Node listening_node{storage.Listen()}; + + WHEN("stored data is changed") { + storage.Store(niv::testing::AnotherNode()); + THEN("the listening node gets the change") { + REQUIRE_THAT(listening_node, Equals(niv::testing::AnotherNode())); + } + } + } +} diff --git a/niv/tests/src/exchange/test_relay_shared_memory.cpp b/niv/tests/src/exchange/test_relay_shared_memory.cpp new file mode 100644 index 0000000..8aec570 --- /dev/null +++ b/niv/tests/src/exchange/test_relay_shared_memory.cpp @@ -0,0 +1,185 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include + +#include "catch/catch.hpp" + +#include "conduit/conduit_node.hpp" + +#include "niv/exchange/relay_shared_memory.hpp" +#include "niv/exchange/shared_memory.hpp" +#include "niv/nest_test_data.hpp" + +#include "conduit_node_helper.hpp" + +SCENARIO("Data gets transported", "[niv][niv::RelaySharedMemory]") { + GIVEN("a simulation relay, and a visualization relay") { + niv::exchange::RelaySharedMemory visualization_relay; + niv::exchange::RelaySharedMemory simulation_relay; + + WHEN("a node is sent via the simulation relay") { + simulation_relay.Send(niv::testing::AnyNode()); + + WHEN("data is received via the visualization relay") { + conduit::Node received_node{visualization_relay.Receive()}; + + THEN("received data matches original data") { + REQUIRE_THAT(received_node, Equals(niv::testing::AnyNode())); + } + } + } + } +} + +SCENARIO("data in relay gets updated on sending update", + "[niv][niv::RelaySharedMemory]") { + GIVEN("a relay storing data") { + niv::exchange::RelaySharedMemory simulation_relay; + simulation_relay.Send(niv::testing::AnyNode()); + + WHEN("an update gets sent to the relay") { + simulation_relay.Send(niv::testing::Update()); + WHEN("the node is received from the relay") { + conduit::Node received_node{simulation_relay.Receive()}; + THEN("the received node includes the update") { + REQUIRE_THAT(received_node, Equals(niv::testing::UpdatedNode())); + } + } + } + } +} + +SCENARIO("Data in relay is cleared on receive", + "[niv][niv::RelaySharedMemory]") { + GIVEN("A synchronized relay with some data") { + niv::exchange::RelaySharedMemory relay; + relay.Send(niv::testing::AnyNode()); + + WHEN("Data is received") { + auto node{relay.Receive()}; + THEN("the node is not empty") { REQUIRE_FALSE(node.dtype().is_empty()); } + + WHEN("data is read a second time") { + auto node2{relay.Receive()}; + THEN("the node is empty") { REQUIRE(node2.dtype().is_empty()); } + } + } + } +} + +SCENARIO("Relay's emptyness is passed throug shared memory", + "[niv][niv::RelaySharedMemory]") { + GIVEN("a pair of relays") { + niv::exchange::RelaySharedMemory relay_segment; + niv::exchange::RelaySharedMemory relay_access; + + THEN("both relays are empty") { + REQUIRE(relay_segment.IsEmpty()); + REQUIRE(relay_access.IsEmpty()); + } + + WHEN("Data is sent") { + relay_segment.Send(niv::testing::AnyNode()); + THEN("both relays are not empty.") { + REQUIRE_FALSE(relay_segment.IsEmpty()); + REQUIRE_FALSE(relay_access.IsEmpty()); + } + + WHEN("Data is received") { + relay_access.Receive(); + THEN("both relays are empty again.") { + REQUIRE(relay_segment.IsEmpty()); + REQUIRE(relay_access.IsEmpty()); + } + } + } + } +} + +SCENARIO("ordered destruction of relays is properly reflectes by shared memory", + "[niv][niv::RelaySharedMemory]") { + GIVEN("no relay") { + THEN("accessing shared throws an exception") { + REQUIRE_THROWS_WITH( + niv::exchange::SharedMemory(niv::exchange::SharedMemory::Access()), + "No such file or directory"); + } + } + + GIVEN("a relay in a new scope") { + { // new scope + niv::exchange::RelaySharedMemory relay; + THEN("accessing shared memory does not throw") { + REQUIRE_NOTHROW( + niv::exchange::SharedMemory(niv::exchange::SharedMemory::Access())); + } + + THEN("creating a second relay does not throw") { + REQUIRE_NOTHROW(niv::exchange::RelaySharedMemory()); + } + + WHEN("a second relay gets out of scope") { + { // new scope + niv::exchange::RelaySharedMemory relay2; + } + THEN("accessing shared memory does not throw") { + REQUIRE_NOTHROW(niv::exchange::SharedMemory( + niv::exchange::SharedMemory::Access())); + } + } + } + + WHEN("the first relay is out of scope") { + THEN("accessing shared memory throws an exception") { + REQUIRE_THROWS_WITH( + niv::exchange::SharedMemory(niv::exchange::SharedMemory::Access()), + "No such file or directory"); + } + } + } +} + +SCENARIO( + "unorderd destruction of relays is properly reflected by shared memory", + "[niv][niv::RelaySharedMemory]") { + GIVEN("two relays") { + auto relay1 = std::make_unique(); + auto relay2 = std::make_unique(); + + WHEN("deleting the first relay") { + relay1.reset(); + THEN("accessing shared memory does not throw") { + REQUIRE_NOTHROW( + niv::exchange::SharedMemory(niv::exchange::SharedMemory::Access())); + } + + WHEN("deleting the second relay") { + relay2.reset(); + THEN("accessing shared memory throws an exception") { + REQUIRE_THROWS_WITH(niv::exchange::SharedMemory( + niv::exchange::SharedMemory::Access()), + "No such file or directory"); + } + } + } + } +} diff --git a/niv/tests/src/exchange/test_relay_shared_memory_mutex.cpp b/niv/tests/src/exchange/test_relay_shared_memory_mutex.cpp new file mode 100644 index 0000000..28bcbb5 --- /dev/null +++ b/niv/tests/src/exchange/test_relay_shared_memory_mutex.cpp @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include + +#include "catch/catch.hpp" + +#include "niv/exchange/relay_shared_memory.hpp" +#include "niv/nest_test_data.hpp" + +SCENARIO("Mutex does not stall multiple sends/receives", + "[niv][niv::RelaySharedMemory]") { + GIVEN("a pair of relays") { + niv::exchange::RelaySharedMemory relay_segment; + niv::exchange::RelaySharedMemory relay_access; + + THEN("send, receive works") { + relay_segment.Send(niv::testing::AnyNode()); + relay_access.Receive(); + } + + THEN("receive, send works") { + relay_access.Receive(); + relay_segment.Send(niv::testing::AnyNode()); + } + + THEN("send, send, receive works") { + relay_segment.Send(niv::testing::AnyNode()); + relay_segment.Send(niv::testing::AnotherNode()); + relay_access.Receive(); + } + + THEN("send, receive, send, receive works") { + relay_segment.Send(niv::testing::AnyNode()); + relay_access.Receive(); + relay_segment.Send(niv::testing::AnotherNode()); + } + + THEN("receive, send, send, receive works") { + relay_segment.Send(niv::testing::AnyNode()); + relay_segment.Send(niv::testing::AnotherNode()); + relay_access.Receive(); + } + } +} diff --git a/niv/tests/src/exchange/test_relay_shared_memory_threaded.cpp b/niv/tests/src/exchange/test_relay_shared_memory_threaded.cpp new file mode 100644 index 0000000..5c84bfa --- /dev/null +++ b/niv/tests/src/exchange/test_relay_shared_memory_threaded.cpp @@ -0,0 +1,77 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include +#include +#include + +#include "catch/catch.hpp" + +#include "niv/exchange/relay_shared_memory.hpp" +#include "niv/nest_test_data.hpp" + +namespace { + +void Send(niv::exchange::RelaySharedMemory* relay) { + std::random_device random_seed; + std::mt19937 generator(random_seed()); + std::uniform_int_distribution<> distribution(1, 4); // define the range + + for (auto i = 0u; i < 10; ++i) { + const int wait = distribution(generator); + std::this_thread::sleep_for(std::chrono::milliseconds(wait)); + relay->Send(niv::testing::AnyNode()); + } +} + +void Receive(niv::exchange::RelaySharedMemory* relay) { + std::random_device random_seed; + std::mt19937 generator(random_seed()); + std::uniform_int_distribution<> distribution(3, 6); // define the range + + for (auto i = 0u; i < 10; ++i) { + const int wait = distribution(generator); + std::this_thread::sleep_for(std::chrono::milliseconds(wait)); + relay->Receive(); + } +} + +constexpr bool we_reach_this_before_timeout = true; + +} // namespace + +SCENARIO("Synchronization across separate threads does not accidently block", + "[niv][niv::RelaySharedMemory]") { + GIVEN("A pair of sync relays") { + niv::exchange::RelaySharedMemory relay_segment; + niv::exchange::RelaySharedMemory relay_access; + + WHEN("These send and receive in separate threads") { + std::thread sender(::Send, &relay_access); + std::thread receiver(::Receive, &relay_segment); + THEN("they do not block each other") { + sender.join(); + receiver.join(); + REQUIRE(::we_reach_this_before_timeout); + } + } + } +} diff --git a/niv/tests/src/exchange/test_shared_memory.cpp b/niv/tests/src/exchange/test_shared_memory.cpp new file mode 100644 index 0000000..fcdecec --- /dev/null +++ b/niv/tests/src/exchange/test_shared_memory.cpp @@ -0,0 +1,243 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include "catch/catch.hpp" + +#include "conduit/conduit_node.hpp" + +#include "niv/exchange/shared_memory.hpp" +#include "niv/nest_test_data.hpp" + +#include "conduit_node_helper.hpp" + +SCENARIO("Shared memory creation", "[niv][niv::SharedMemory]") { + GIVEN("A shared memory segment") { + niv::exchange::SharedMemory segment{niv::exchange::SharedMemory::Create()}; + WHEN("I ask it for its free size") { + auto free_size_after_creation = segment.GetFreeSize(); + THEN("it is > 0") { REQUIRE(free_size_after_creation > 0); } + } + + WHEN("I read data from the new segment") { + THEN("it does not throw") { REQUIRE_NOTHROW(segment.Read()); } + THEN("it is empty") { REQUIRE(segment.Read().dtype().is_empty()); } + } + + WHEN("I store data in the segment") { + auto free_size_before = segment.GetFreeSize(); + segment.Store(niv::testing::AnyNode()); + auto free_size_after = segment.GetFreeSize(); + THEN("we have less free space in the segment") { + REQUIRE(free_size_after < free_size_before); + } + THEN("I can read the data") { + REQUIRE_THAT(segment.Read(), Equals(niv::testing::AnyNode())); + } + } + + WHEN("I request a second shared memory segment") { + THEN("It throws an exception") { + REQUIRE_THROWS_WITH( + []() { + niv::exchange::SharedMemory segment2{ + niv::exchange::SharedMemory::Create()}; + segment2.Destroy(); + }(), + "File exists"); + } + } + segment.Destroy(); + } +} + +SCENARIO("write updated node to shared memory segment", + "[niv][niv::SharedMemory]") { + GIVEN("a shared memory segment with some data") { + niv::exchange::SharedMemory segment{niv::exchange::SharedMemory::Create()}; + segment.Store(niv::testing::AnyNode()); + WHEN("a larger node is stored") { + segment.Store(niv::testing::UpdatedNode()); + WHEN("the node is read") { + conduit::Node read_node{segment.Read()}; + THEN("the content is equal to the written one") { + REQUIRE_THAT(read_node, Equals(niv::testing::UpdatedNode())); + } + } + } + segment.Destroy(); + } +} + +SCENARIO("Shared memory access", "[niv][niv::SharedMemory]") { + GIVEN("No shared memory segment") { + THEN("Creating a shared memory access throws an exception.") { + REQUIRE_THROWS_WITH( + niv::exchange::SharedMemory{niv::exchange::SharedMemory::Access()}, + "No such file or directory"); + } + } + + GIVEN("A shared memory segment") { + niv::exchange::SharedMemory segment{niv::exchange::SharedMemory::Create()}; + + WHEN("I create shared memory access") { + THEN("It does not throw an exception") { + REQUIRE_NOTHROW( + niv::exchange::SharedMemory{niv::exchange::SharedMemory::Access()}); + } + niv::exchange::SharedMemory segment_access{ + niv::exchange::SharedMemory::Access()}; + + WHEN("data is stored in the shared memory access") { + segment_access.Store(niv::testing::AnyNode()); + + THEN("it can be read") { + REQUIRE_THAT(segment_access.Read(), Equals(niv::testing::AnyNode())); + } + } + } + segment.Destroy(); + } +} + +SCENARIO("storing and retrieving conduit nodes to/from shared memory", + "[niv][niv:SharedMemory][niv:SharedMemorySegment][niv:" + "SharedMemoryAccess]") { + GIVEN("a shared memory segment and access") { + niv::exchange::SharedMemory shared_memory_segment{ + niv::exchange::SharedMemory::Create()}; + niv::exchange::SharedMemory shared_memory_access{ + niv::exchange::SharedMemory::Access()}; + + WHEN("a node is stored in the shared memory segment") { + shared_memory_segment.Store(niv::testing::AnyNode()); + + THEN("it can be read via access") { + REQUIRE_THAT(shared_memory_access.Read(), + Equals(niv::testing::AnyNode())); + } + + GIVEN("a node listening to shared memory") { + conduit::Node listening_node{shared_memory_access.Listen()}; + WHEN("the first node is updated and stored again") { + shared_memory_segment.Store(niv::testing::AnotherNode()); + THEN("the result arrives at the listening node") { + REQUIRE_THAT(listening_node, Equals(niv::testing::AnotherNode())); + } + } + } + } + + WHEN("a node is stored in the shared memory access") { + shared_memory_access.Store(niv::testing::AnyNode()); + + THEN("it can be read from the segment") { + REQUIRE_THAT(shared_memory_segment.Read(), + Equals(niv::testing::AnyNode())); + } + + GIVEN("a node listening to shared memory") { + conduit::Node listening_node{shared_memory_segment.Listen()}; + WHEN("the first node is updated and stored again") { + shared_memory_segment.Store(niv::testing::AnotherNode()); + THEN("the result arrives at the listening node") { + REQUIRE_THAT(listening_node, Equals(niv::testing::AnotherNode())); + } + } + } + } + shared_memory_segment.Destroy(); + } +} + +SCENARIO("Overwriting data in shared memory", + "[niv][niv::SharedMemory][niv::SharedMemorySegment][niv::" + "SharedMemoryAccess") { + GIVEN("A shared memory segment + access, with some data in it") { + niv::exchange::SharedMemory shared_memory_segment{ + niv::exchange::SharedMemory::Create()}; + niv::exchange::SharedMemory shared_memory_access{ + niv::exchange::SharedMemory::Access()}; + shared_memory_segment.Store(niv::testing::AnyNode()); + WHEN("when new data is stored in the segment") { + shared_memory_segment.Store(niv::testing::ADifferentNode()); + WHEN("that data is read") { + conduit::Node read_node{shared_memory_access.Read()}; + THEN("the read data is equal to the stored one") { + REQUIRE_THAT(read_node, Equals(niv::testing::ADifferentNode())); + } + } + } + shared_memory_segment.Destroy(); + } +} + +SCENARIO("data can be updated in shared memory", + "[niv][niv::SharedMemory][niv::SharedMemorySegment][niv::" + "SharedMemoryAccess") { + std::cout << "SCENARIO(\"data can be updated in shared memory\")" + << std::endl; + GIVEN("a shared memory segment with data in it, and shared memory access") { + niv::exchange::SharedMemory segment{niv::exchange::SharedMemory::Create()}; + niv::exchange::SharedMemory segment_access{ + niv::exchange::SharedMemory::Access()}; + segment.Store(niv::testing::AnyNode()); + + WHEN("the data in the shared memory is updated") { + segment.Update(niv::testing::Update()); + THEN("the updated data can be read from the segment") { + REQUIRE_THAT(segment.Read(), Equals(niv::testing::UpdatedNode())); + } + THEN("the updated data can be read from the segment access") { + REQUIRE_THAT(segment.Read(), Equals(niv::testing::UpdatedNode())); + } + } + segment.Destroy(); + } +} + +SCENARIO("Shared memory provides correct reference counts", + "[niv][niv::SharedMemory]") { + GIVEN("a shared memory segment") { + niv::exchange::SharedMemory segment{niv::exchange::SharedMemory::Create()}; + THEN("the reference count is correct") { + REQUIRE(segment.GetReferenceCount() == 0); + } + + WHEN("a shared memory access is created in a new scope") { + { // new scope + niv::exchange::SharedMemory access{ + niv::exchange::SharedMemory::Access()}; + THEN("the reference count is correct") { + REQUIRE(segment.GetReferenceCount() == 1); + REQUIRE(access.GetReferenceCount() == 1); + } + } + WHEN("that shared memory access gets out of scope") { + THEN("the reference count is correct") { + REQUIRE(segment.GetReferenceCount() == 0); + } + } + } + + segment.Destroy(); + } +} diff --git a/niv/tests/src/exchange/test_shared_memory_synchronization.cpp b/niv/tests/src/exchange/test_shared_memory_synchronization.cpp new file mode 100644 index 0000000..76e52a1 --- /dev/null +++ b/niv/tests/src/exchange/test_shared_memory_synchronization.cpp @@ -0,0 +1,105 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include "catch/catch.hpp" + +#include "niv/exchange/shared_memory_synchronization.hpp" + +SCENARIO("SharedMemorySynchronization locks and releases properly", + "[niv][niv::SharedMemorySynchronization]") { + GIVEN("A SharedMemorySynchronization") { + niv::exchange::SharedMemorySynchronization sync{ + niv::exchange::SharedMemorySynchronization::Create()}; + WHEN("scoped lock is acquired") { + auto lock{sync.ScopedLock()}; + THEN("the mutex cannot be locked a second time") { + const bool lockable = sync.TryLock(); + REQUIRE_FALSE(lockable); + if (lockable) { + sync.Unlock(); + } + } + } + WHEN("The lock is out of scope") { + THEN("the mutex can be locked a second time") { + const bool lockable = sync.TryLock(); + REQUIRE(lockable); + if (lockable) { + sync.Unlock(); + } + } + } + sync.Destroy(); + } +} + +SCENARIO("SharedMemorySynchronization locks and releases through shared mem", + "[niv][niv::SharedMemorySynchronization]") { + GIVEN("A pair of shared memory synchronization") { + niv::exchange::SharedMemorySynchronization sync_object{ + niv::exchange::SharedMemorySynchronization::Create()}; + niv::exchange::SharedMemorySynchronization sync_access{ + niv::exchange::SharedMemorySynchronization::Access()}; + + WHEN("scoped lock is acquired on the first one") { + auto lock = sync_object.ScopedLock(); + THEN("the second one is locked") { + const bool lockable = sync_access.TryLock(); + REQUIRE_FALSE(lockable); + if (lockable) { + sync_access.Unlock(); + } + } + } + + WHEN("The lock is out of scope") { + THEN("the second mutex can be locked") { + const bool lockable = sync_access.TryLock(); + REQUIRE(lockable); + if (lockable) { + sync_access.Unlock(); + } + } + } + + WHEN("scoped lock is acquired on the second one") { + auto lock = sync_access.ScopedLock(); + THEN("the first one is locked") { + const bool lockable = sync_object.TryLock(); + REQUIRE_FALSE(lockable); + if (lockable) { + sync_object.Unlock(); + } + } + } + + WHEN("The lock is out of scope") { + THEN("the first mutex can be locked") { + const bool lockable = sync_object.TryLock(); + REQUIRE(lockable); + if (lockable) { + sync_object.Unlock(); + } + } + } + sync_object.Destroy(); + } +} diff --git a/niv/tests/src/producer/test_multimeter.cpp b/niv/tests/src/producer/test_multimeter.cpp new file mode 100644 index 0000000..e36813d --- /dev/null +++ b/niv/tests/src/producer/test_multimeter.cpp @@ -0,0 +1,69 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include +#include +#include + +#include "catch/catch.hpp" + +#include "conduit/conduit_node.hpp" + +#include "niv/producer/multimeter.hpp" + +SCENARIO("A unique multimeter ptr can be constructed via its factory", + "[niv][niv::Multimeter]") { + WHEN("a new multimeter is constructed") { + std::unique_ptr multimeter{ + niv::producer::Multimeter::New("name", std::vector(), + nullptr)}; + THEN("a pointer was obtained") { REQUIRE(multimeter.get() != nullptr); } + } +} + +SCENARIO("A multimeter records to a conduit node", "[niv][niv::Multimeter]") { + const std::string any_name{"multimeter1"}; + constexpr double any_time{1.5}; + const std::string any_time_string{"1.5"}; + constexpr std::size_t any_id{3}; + const std::string any_id_string{"3"}; + const std::vector any_value_names{"A", "B", "C"}; + const std::vector any_values{3.1415, 4.123, 42.0}; + + GIVEN("a conduit node and a multimeter") { + conduit::Node node; + niv::producer::Multimeter multimeter(any_name, any_value_names, &node); + + WHEN("setting the recording time") { + multimeter.SetRecordingTime(any_time); + WHEN("recording") { + multimeter.Record(any_id, any_values); + THEN("data is recorded in the node") { + for (std::size_t i = 0; i < any_value_names.size(); ++i) { + REQUIRE(node[any_name][any_time_string][any_value_names[i]] + [any_id_string] + .to_double() == Approx(any_values[i])); + } + } + } + } + } +} diff --git a/niv/tests/src/producer/test_spike_detector.cpp b/niv/tests/src/producer/test_spike_detector.cpp new file mode 100644 index 0000000..825e289 --- /dev/null +++ b/niv/tests/src/producer/test_spike_detector.cpp @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include +#include + +#include "catch/catch.hpp" + +#include "conduit/conduit_node.hpp" + +#include "niv/producer/spike_detector.hpp" + +SCENARIO("A unique spike detector ptr can be constructed via its factory", + "[niv][niv::SpikeDetector]") { + WHEN("a new spike detector is constructed") { + std::unique_ptr spike_detector{ + niv::producer::SpikeDetector::New("name", nullptr)}; + THEN("a pointer was obtained") { REQUIRE(spike_detector.get() != nullptr); } + } +} + +SCENARIO("A spike detector records to a conduit node", + "[niv][niv::SpikeDetector]") { + const std::string any_name{"spikes1"}; + constexpr double any_time{1.5}; + const std::string any_time_string{"1.5"}; + constexpr std::size_t any_id{3}; + constexpr std::size_t another_id{5}; + + GIVEN("a conduit node and a spike detector") { + conduit::Node node; + niv::producer::SpikeDetector spike_detector(any_name, &node); + + WHEN("setting the recording time") { + spike_detector.SetRecordingTime(any_time); + WHEN("recording") { + spike_detector.Record(any_id); + THEN("data is recorded in the node") { + REQUIRE(node[any_name][any_time_string].as_uint64_array()[0] == + any_id); + } + WHEN("recording another spike") { + spike_detector.Record(another_id); + THEN("data is recorded in the node") { + REQUIRE(node[any_name][any_time_string].as_uint64_array()[0] == + any_id); + REQUIRE(node[any_name][any_time_string].as_uint64_array()[1] == + another_id); + } + } + } + } + } +} diff --git a/niv/tests/src/test_conduit.cpp b/niv/tests/src/test_conduit.cpp new file mode 100644 index 0000000..97d4d1c --- /dev/null +++ b/niv/tests/src/test_conduit.cpp @@ -0,0 +1,261 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include +#include +#include + +#include "catch/catch.hpp" + +#include "conduit/conduit_node.hpp" + +#include "niv/exchange/node_storage.hpp" +#include "niv/nest_test_data.hpp" + +#include "conduit_node_helper.hpp" + +namespace conduit { + +template <> +DataArray::~DataArray(); + +} // namespace conduit + +SCENARIO("update inserts new nodes", "[conduit]") { + GIVEN("A conduit tree") { + conduit::Node a = niv::testing::AnyNode(); + + WHEN("A second node updates the tree") { + conduit::Node b = niv::testing::Update(); + a.update(b); + + THEN("the first node contains also the content of the second") { + REQUIRE_THAT(a, Equals(niv::testing::UpdatedNode())); + } + } + } +} + +SCENARIO("conduit array leafs are compatible to std::vector", "[conduit]") { + const std::string some_path{"root/inner/leaf"}; + + GIVEN("a std vector assigning data to a node") { + const std::vector some_data{1, 5, 7, 9}; + conduit::Node node; + node[some_path].set(some_data); + + THEN("data is stored in the node") { + for (std::size_t i = 0; i < some_data.size(); ++i) { + REQUIRE(node[some_path].as_uint64_array()[i] == some_data[i]); + } + } + + WHEN("data is retrieved from the node into a vector") { + const auto& node_data = node[some_path].as_uint64_array(); + const std::size_t num_elements = node_data.number_of_elements(); + const auto* begin = reinterpret_cast(node_data.data_ptr()); + const auto* end = begin + num_elements; + std::vector retrieved_data(begin, end); + + THEN("the vector and the original data are the same") { + REQUIRE(retrieved_data == some_data); + } + } + } +} + +namespace { + +void SerializeConstRef(const conduit::Node& node, std::string* schema, + std::vector* bytes) { + conduit::Schema compact_schema; + node.schema().compact_to(compact_schema); + const std::string compact_schema_json(compact_schema.to_json()); + schema->assign(compact_schema_json.begin(), compact_schema_json.end()); + + // intermediate vector not required in general, but our use case requires it + std::vector data; + node.serialize(data); + bytes->assign(data.begin(), data.end()); +} + +} // namespace + +SCENARIO( + "multiple serialization and re-read works. Conduit's issues #226, #229 are " + "fixed." + "[conduit]") { + INFO( + "This test's failing indicates that something might have broken the " + "fixes for conduit's issues #226, #229.\n" + "See \n" + "* https://github.com/LLNL/conduit/issues/226 \n" + "* https://github.com/LLNL/conduit/issues/229 \n") + GIVEN("a node that is serialized and read back") { + std::string schema; + std::vector bytes; + + SerializeConstRef(niv::testing::AnyNode(), &schema, &bytes); + + conduit::Node second_node; + second_node.set_data_using_schema(conduit::Schema(schema), bytes.data()); + + WHEN("the second node is serialized and read into a third node") { + SerializeConstRef(second_node, &schema, &bytes); + + conduit::Node third_node; + third_node.set_data_using_schema(conduit::Schema(schema), bytes.data()); + + REQUIRE(third_node.to_json() == niv::testing::AnyNode().to_json()); + } + } +} + +SCENARIO( + "Node copy does not preserve externalness as intended by conduit." + "[conduit]") { + INFO( + "This test's failing indicates that the intended behaviour of conduit " + "has changed. \n" + "On failure: Copying preserves externalness. \n" + "* Check back with the conduit developers, if this is intended. \n" + "* Simplify listening, e.g., in niv::ConduitReceiver::Start(...) " + "to not use set_external(...) anymore.") + GIVEN("An external conduit node") { + std::string schema; + std::vector data; + niv::exchange::NodeStorage> + storage(&schema, &data); + storage.Store(niv::testing::AnyNode()); + + constexpr bool external{true}; + conduit::Node external_node(schema, data.data(), external); + const std::string original_json{external_node.to_json()}; + + GIVEN("a copy of the external node") { + conduit::Node copied_node(external_node); + std::string original_json_copied{copied_node.to_json()}; + + THEN("The two nodes are equal") { + REQUIRE_THAT(external_node, Equals(copied_node)); + } + + WHEN("the data is changed") { + data[0] = ~data[0]; + data[7] = ~data[7]; + THEN("the external node has changed data") { + REQUIRE(external_node.to_json() != original_json); + } + THEN("the copied node has changed data") { + REQUIRE_FALSE(copied_node.to_json() != original_json_copied); + } + THEN("the copied node is still equal to the external one") { + REQUIRE_FALSE(external_node.to_json() == copied_node.to_json()); + } + } + } + + GIVEN("the external node assigned to a new one") { + conduit::Node assigned_node; + assigned_node = external_node; + std::string original_json_assigned{assigned_node.to_json()}; + + THEN("The two nodes are equal") { + REQUIRE_THAT(external_node, Equals(assigned_node)); + } + + WHEN("the data is changed") { + data[0] = ~data[0]; + data[7] = ~data[7]; + THEN("the external node has changed data") { + REQUIRE(external_node.to_json() != original_json); + } + THEN("the assigned node has changed data") { + REQUIRE_FALSE(assigned_node.to_json() != original_json_assigned); + } + THEN("the copied node is still equal to the external one") { + REQUIRE_FALSE(external_node.to_json() == assigned_node.to_json()); + } + } + } + } +} + +SCENARIO( + "node changes are not yet reflected by nodes using external data " + "(conduit's #240)", + "[conduit]") { + INFO( + "This test's failing indicates that conduit's #240 might be addressed. \n" + "On failure: \n" + "* Nodes referring to others via external might reflect changes. \n" + "* Update niv's code to utilize this") + GIVEN("a node and one referring to it") { + conduit::Node original_node; + conduit::Node referring_node; + + WHEN("the refering node uses the original one as external data") { + referring_node.set_external(original_node); + + WHEN("the original node is changed") { + original_node["A"] = 3.1415; + + THEN("the change is visible in the referring node") { + REQUIRE_THAT(referring_node, !Equals(original_node)); + REQUIRE(referring_node.dtype().is_empty()); + } + } + + WHEN("the referring node is changed") { + referring_node["B"] = 4.123; + + THEN("the change is visible in the original node") { + REQUIRE_THAT(original_node, !Equals(referring_node)); + REQUIRE(original_node.dtype().is_empty()); + } + } + + WHEN("the original node is updated") { + conduit::Node update; + update["A"] = 3.1415; + original_node.update(update); + + THEN("the change is visible in the referring node") { + REQUIRE_THAT(referring_node, !Equals(original_node)); + REQUIRE(referring_node.dtype().is_empty()); + } + } + + WHEN("the referring node is updated") { + conduit::Node update; + update["B"] = 4.123; + referring_node.update(update); + + THEN("the change is visible in the original node") { + REQUIRE_THAT(original_node, !Equals(referring_node)); + REQUIRE(original_node.dtype().is_empty()); + } + } + } + } +} + +SCENARIO("node updates ", "[]") {} diff --git a/niv/tests/src/test_conduit_receiver.cpp b/niv/tests/src/test_conduit_receiver.cpp deleted file mode 100644 index ebf0ff6..0000000 --- a/niv/tests/src/test_conduit_receiver.cpp +++ /dev/null @@ -1,53 +0,0 @@ -//------------------------------------------------------------------------------ -// nest in situ vis -// -// Copyright (c) 2017 RWTH Aachen University, Germany, -// Virtual Reality & Immersive Visualisation Group. -//------------------------------------------------------------------------------ -// License -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -//------------------------------------------------------------------------------ - -#include - -#include "catch/catch.hpp" - -#include "conduit/conduit_node.hpp" - -#include "niv/conduit_receiver.hpp" -#include "niv/sending_relay_shared_memory.hpp" -#include "niv/shared_memory_access.hpp" - -SCENARIO("Conduit data is received by a conduit receiver", - "[niv][niv::ConduitReceiver]") { - GIVEN("A ConduitReceiver") { - niv::ConduitReceiver receiver; - - WHEN("I send some data") { - conduit::Node data; - data["A"]["B"] = 17.0; - data["A"]["C"] = 42.0; - - niv::SendingRelaySharedMemory relay( - std::make_unique()); - relay.Send(data); - - THEN("I receive the data") { - receiver.Start(); - REQUIRE(receiver.Get("A/B") == 17.0); - REQUIRE(receiver.Get("A/C") == 42.0); - } - } - } -} diff --git a/niv/tests/src/test_relay_shared_memory.cpp b/niv/tests/src/test_relay_shared_memory.cpp deleted file mode 100644 index b07e5aa..0000000 --- a/niv/tests/src/test_relay_shared_memory.cpp +++ /dev/null @@ -1,156 +0,0 @@ -//------------------------------------------------------------------------------ -// nest in situ vis -// -// Copyright (c) 2017 RWTH Aachen University, Germany, -// Virtual Reality & Immersive Visualisation Group. -//------------------------------------------------------------------------------ -// License -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -//------------------------------------------------------------------------------ - -#include - -#include "catch/catch.hpp" - -#include "conduit/conduit_node.hpp" - -#include "niv/receiving_relay_shared_memory.hpp" -#include "niv/sending_relay_shared_memory.hpp" -#include "niv/shared_memory_access.hpp" -#include "niv/shared_memory_segment.hpp" - -namespace { -conduit::Node AnyNode() { - conduit::Node node; - node["A"]["B"]["E"] = 1.1; - node["A"]["C"]["F"] = 2.1; - node["A"]["C"]["G"] = 2.2; - node["A"]["D"]["H"] = 3.1; - node["A"]["D"]["I"] = 3.2; - node["A"]["D"]["J"] = 3.3; - return node; -} - -void REQUIRE_EQ(const conduit::Node& left, const conduit::Node& right) { - REQUIRE(left["A"]["B"]["E"].as_double() == right["A"]["B"]["E"].as_double()); - REQUIRE(left["A"]["C"]["F"].as_double() == right["A"]["C"]["F"].as_double()); - REQUIRE(left["A"]["C"]["G"].as_double() == right["A"]["C"]["G"].as_double()); - REQUIRE(left["A"]["D"]["H"].as_double() == right["A"]["D"]["H"].as_double()); - REQUIRE(left["A"]["D"]["I"].as_double() == right["A"]["D"]["I"].as_double()); - REQUIRE(left["A"]["D"]["J"].as_double() == right["A"]["D"]["J"].as_double()); -} - -conduit::Node& AnyLeaf(conduit::Node* node) { return (*node)["A"]["D"]["H"]; } - -constexpr double kAnyOtherValue{42.0f}; - -} // namespace - -SCENARIO("Communicate a conduit node from shared mem segment to access", - "[niv][nvi::RelaySharedMemory]") { - GIVEN( - "A conduit node with some data, a sending shared memory segment relay, a " - "receiving shared memory access relay, and a receiving node") { - conduit::Node any_node{::AnyNode()}; - niv::SendingRelaySharedMemory sending_relay{ - std::make_unique()}; - niv::ReceivingRelaySharedMemory receiving_relay{ - std::make_unique()}; - conduit::Node receiving_node; - - WHEN("I send the data via the sending relay") { - sending_relay.Send(any_node); - - THEN("I receive the data on the receiving relay") { - receiving_relay.Receive(&receiving_node); - REQUIRE_EQ(receiving_node, any_node); - } - - WHEN("I change one value and send again") { - ::AnyLeaf(&any_node) = ::kAnyOtherValue; - sending_relay.Send(any_node); - - THEN("I receive the data on the receiving relay") { - receiving_relay.Receive(&receiving_node); - REQUIRE_EQ(receiving_node, any_node); - } - } - - WHEN("I listen to the data on the receiving relay") { - receiving_relay.Listen(&receiving_node); - THEN("I receive the data on the receiving relay") { - REQUIRE_EQ(receiving_node, any_node); - } - - WHEN("I change one value and send again") { - ::AnyLeaf(&any_node) = ::kAnyOtherValue; - sending_relay.Send(any_node); - - THEN("I receive the data on the receiving relay") { - REQUIRE_EQ(receiving_node, any_node); - } - } - } - } - } -} - -SCENARIO("Communicate a conduit node from shared mem access to segment", - "[niv][nvi::RelaySharedMemory]") { - GIVEN( - "A conduit node with some data, a sending shared memory access relay, a " - "receiving shared memory segment relay, and a receiving node") { - niv::ReceivingRelaySharedMemory receiving_relay{ - std::make_unique()}; - conduit::Node receiving_node; - conduit::Node any_node{::AnyNode()}; - niv::SendingRelaySharedMemory sending_relay{ - std::make_unique()}; - - WHEN("I send the data via the sending relay") { - sending_relay.Send(any_node); - - THEN("I receive the data on the receiving relay") { - receiving_relay.Receive(&receiving_node); - REQUIRE_EQ(receiving_node, any_node); - } - - WHEN("I change one value and send again") { - ::AnyLeaf(&any_node) = ::kAnyOtherValue; - sending_relay.Send(any_node); - - THEN("I receive the data on the receiving relay") { - receiving_relay.Receive(&receiving_node); - REQUIRE_EQ(receiving_node, any_node); - } - } - - WHEN("I listen to the data on the receiving relay") { - receiving_relay.Listen(&receiving_node); - THEN("I receive the data on the receiving relay") { - REQUIRE_EQ(receiving_node, any_node); - } - - WHEN("I change one value and send again") { - ::AnyLeaf(&any_node) = ::kAnyOtherValue; - sending_relay.Send(any_node); - - THEN("I receive the data on the receiving relay") { - REQUIRE_EQ(receiving_node, any_node); - } - } - } - } - } -} diff --git a/niv/tests/src/test_shared_memory_access.cpp b/niv/tests/src/test_shared_memory_access.cpp deleted file mode 100644 index 4eb4002..0000000 --- a/niv/tests/src/test_shared_memory_access.cpp +++ /dev/null @@ -1,64 +0,0 @@ -//------------------------------------------------------------------------------ -// nest in situ vis -// -// Copyright (c) 2017 RWTH Aachen University, Germany, -// Virtual Reality & Immersive Visualisation Group. -//------------------------------------------------------------------------------ -// License -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -//------------------------------------------------------------------------------ - -#include -#include - -#include "catch/catch.hpp" - -#include "niv/shared_memory_access.hpp" -#include "niv/shared_memory_segment.hpp" - -SCENARIO("Shared memory access", "[niv][niv::SharedMemoryAccess]") { - GIVEN("No shared memory segment") { - THEN("Creating a shared memory access throws an exception.") { - REQUIRE_THROWS_WITH([]() { niv::SharedMemoryAccess segment_access; }(), - "No such file or directory"); - } - } - - GIVEN("A shared memory segment with some data in it") { - niv::SharedMemorySegment segment; - std::vector some_data{1u, 2u, 3u}; - segment.Store(some_data); - std::string some_schema{"foo bar"}; - segment.Store(some_schema); - - WHEN("I create shared memory access") { - THEN("It does not throw an exception") { - REQUIRE_NOTHROW([]() { niv::SharedMemoryAccess segment_access; }()); - } - niv::SharedMemoryAccess segment_access; - - WHEN("I read the data from shared memory access") { - auto read_data = segment_access.GetData(); - THEN("I get the original data") { REQUIRE(read_data == some_data); } - } - - WHEN("I read the schema from shared memory access") { - auto read_schema = segment_access.GetSchema(); - THEN("I get the original schema") { - REQUIRE(read_schema == some_schema); - } - } - } - } -} diff --git a/niv/tests/src/test_shared_memory_segment.cpp b/niv/tests/src/test_shared_memory_segment.cpp deleted file mode 100644 index fab68b4..0000000 --- a/niv/tests/src/test_shared_memory_segment.cpp +++ /dev/null @@ -1,82 +0,0 @@ -//------------------------------------------------------------------------------ -// nest in situ vis -// -// Copyright (c) 2017 RWTH Aachen University, Germany, -// Virtual Reality & Immersive Visualisation Group. -//------------------------------------------------------------------------------ -// License -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -//------------------------------------------------------------------------------ - -#include -#include - -#include "catch/catch.hpp" - -#include "niv/shared_memory_segment.hpp" - -SCENARIO("Shared memory creation", "[niv][niv::SharedMemorySegment]") { - GIVEN("A shared memory segment") { - niv::SharedMemorySegment segment; - WHEN("I ask it for its free size") { - auto free_size_after_creation = segment.GetFreeSize(); - THEN("it is > 0") { REQUIRE(free_size_after_creation > 0); } - } - - WHEN("I retrieve data from the new segment") { - auto retrieved_data = segment.GetData(); - THEN("it is empty") { REQUIRE(retrieved_data.size() == 0); } - } - - WHEN("I store data in the segment") { - std::vector some_data{1u, 2u, 3u}; - auto free_size_before = segment.GetFreeSize(); - segment.Store(some_data); - auto free_size_after = segment.GetFreeSize(); - THEN("we have less free space in the segment") { - REQUIRE(free_size_after < free_size_before); - } - THEN("I can retrieve the data") { - auto retrieved_data = segment.GetData(); - REQUIRE(retrieved_data == some_data); - } - } - - WHEN("I retrieve the schema from the new segment") { - auto retrieved_schema = segment.GetSchema(); - THEN("it is empty") { REQUIRE(retrieved_schema.size() == 0); } - } - - WHEN("I store a schema in the segment") { - std::string some_schema{"foo bar"}; - auto free_size_before = segment.GetFreeSize(); - segment.Store(some_schema); - auto free_size_after = segment.GetFreeSize(); - THEN("we have less free space in the segment") { - REQUIRE(free_size_after < free_size_before); - } - THEN("I can retrieve it") { - auto retrieved_schema = segment.GetSchema(); - REQUIRE(retrieved_schema == some_schema); - } - } - - WHEN("I request a second shared memory segment") { - THEN("It throws an exception") { - REQUIRE_THROWS_WITH([]() { niv::SharedMemorySegment segment2; }(), - "File exists"); - } - } - } -} diff --git a/niv/tests/src/tests.cpp b/niv/tests/src/tests.cpp index f45a0ae..834cf6a 100644 --- a/niv/tests/src/tests.cpp +++ b/niv/tests/src/tests.cpp @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // nest in situ vis // -// Copyright (c) 2017 RWTH Aachen University, Germany, +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, // Virtual Reality & Immersive Visualisation Group. //------------------------------------------------------------------------------ // License diff --git a/niv/tests/test_utilities/cout_capture.hpp b/niv/tests/test_utilities/cout_capture.hpp index b5fbb7d..c9d8385 100644 --- a/niv/tests/test_utilities/cout_capture.hpp +++ b/niv/tests/test_utilities/cout_capture.hpp @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // nest in situ vis // -// Copyright (c) 2017 RWTH Aachen University, Germany, +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, // Virtual Reality & Immersive Visualisation Group. //------------------------------------------------------------------------------ // License diff --git a/niv/tests/test_utilities/tests/src/test_cout_capture.cpp b/niv/tests/test_utilities/tests/src/test_cout_capture.cpp index b306734..dce1b4d 100644 --- a/niv/tests/test_utilities/tests/src/test_cout_capture.cpp +++ b/niv/tests/test_utilities/tests/src/test_cout_capture.cpp @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // nest in situ vis // -// Copyright (c) 2017 RWTH Aachen University, Germany, +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, // Virtual Reality & Immersive Visualisation Group. //------------------------------------------------------------------------------ // License diff --git a/pyniv/CMakeLists.txt b/pyniv/CMakeLists.txt index fbce4ef..98a808b 100644 --- a/pyniv/CMakeLists.txt +++ b/pyniv/CMakeLists.txt @@ -1,7 +1,7 @@ #------------------------------------------------------------------------------- # nest in situ vis # -# Copyright (c) 2017 RWTH Aachen University, Germany, +# Copyright (c) 2017-2018 RWTH Aachen University, Germany, # Virtual Reality & Immersive Visualisation Group. #------------------------------------------------------------------------------- # License @@ -21,16 +21,19 @@ -file(GLOB PYNIV_SOURCES src/*.cpp) +file(GLOB PYNIV_SOURCES src/*.cpp src/**/*.cpp) +file(GLOB PYNIV_HEADERS src/*.hpp src/**/*.hpp) python_add_module(pyniv ${PYNIV_SOURCES} + ${PYNIV_HEADERS} ) target_link_libraries(pyniv niv) target_include_directories(pyniv PRIVATE ${Boost_INCLUDE_DIRS} PRIVATE ${PYTHON_INCLUDE_DIRS} + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src ) target_link_libraries(pyniv ${Boost_LIBRARIES} diff --git a/pyniv/src/conduit_data.hpp b/pyniv/src/conduit_data.hpp deleted file mode 100644 index 33f6518..0000000 --- a/pyniv/src/conduit_data.hpp +++ /dev/null @@ -1,47 +0,0 @@ -//------------------------------------------------------------------------------ -// nest in situ vis -// -// Copyright (c) 2017 RWTH Aachen University, Germany, -// Virtual Reality & Immersive Visualisation Group. -//------------------------------------------------------------------------------ -// License -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -//------------------------------------------------------------------------------ - -#ifndef PYNIV_SRC_CONDUIT_DATA_HPP_ -#define PYNIV_SRC_CONDUIT_DATA_HPP_ - -#include "conduit/conduit.hpp" - -namespace pyniv { - -class ConduitData { - public: - ConduitData(); - ~ConduitData() = default; - ConduitData(const ConduitData&) = default; - ConduitData(ConduitData&&) = default; - - void Set(const char* attribute, double value); - std::size_t Pointer() const; - - const conduit::Node& GetNode() const { return node_; } - - private: - conduit::Node node_; -}; - -} // namespace pyniv - -#endif // PYNIV_SRC_CONDUIT_DATA_HPP_ diff --git a/pyniv/src/conduit_data_sender.hpp b/pyniv/src/conduit_data_sender.hpp deleted file mode 100644 index 8fa5a83..0000000 --- a/pyniv/src/conduit_data_sender.hpp +++ /dev/null @@ -1,44 +0,0 @@ -//------------------------------------------------------------------------------ -// nest in situ vis -// -// Copyright (c) 2017 RWTH Aachen University, Germany, -// Virtual Reality & Immersive Visualisation Group. -//------------------------------------------------------------------------------ -// License -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -//------------------------------------------------------------------------------ - -#ifndef PYNIV_SRC_CONDUIT_DATA_SENDER_HPP_ -#define PYNIV_SRC_CONDUIT_DATA_SENDER_HPP_ - -#include "niv/sending_relay_shared_memory.hpp" -#include "niv/shared_memory_access.hpp" - -#include "conduit_data.hpp" - -namespace pyniv { - -class ConduitDataSender { - public: - void Send(const ConduitData& data); - - private: - niv::SendingRelaySharedMemory relay_{ - std::make_unique()}; - conduit::Node node_; -}; - -} // namespace pyniv - -#endif // PYNIV_SRC_CONDUIT_DATA_SENDER_HPP_ diff --git a/pyniv/src/conduit_data.cpp b/pyniv/src/conduit_node.cpp similarity index 64% rename from pyniv/src/conduit_data.cpp rename to pyniv/src/conduit_node.cpp index 34d84d3..1ff69b3 100644 --- a/pyniv/src/conduit_data.cpp +++ b/pyniv/src/conduit_node.cpp @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // nest in situ vis // -// Copyright (c) 2017 RWTH Aachen University, Germany, +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, // Virtual Reality & Immersive Visualisation Group. //------------------------------------------------------------------------------ // License @@ -19,32 +19,28 @@ // limitations under the License. //------------------------------------------------------------------------------ -#include "conduit_data.hpp" - -#include +#include #include "pyniv.hpp" -namespace pyniv { +#include "conduit/conduit_node.hpp" -ConduitData::ConduitData() { - node_["V_m"] = 1.2; - std::cout << "Ptr. to conduit node: " << Pointer() << std::endl; -} +namespace pyniv { -void ConduitData::Set(const char* attribute, double value) { - node_[attribute] = value; +double GetDoubleAtPath(const conduit::Node& node, const std::string& path) { + return node[path].as_double(); } -std::size_t ConduitData::Pointer() const { - return reinterpret_cast(&node_); +void SetDoubleAtPath(const conduit::Node& node, const std::string& path, + double value) { + const_cast(node)[path] = value; } template <> -void expose() { - class_("ConduitData") - .def("Set", &ConduitData::Set) - .def("Pointer", &ConduitData::Pointer); +void expose() { + class_("ConduitNode") + .def("GetDoubleAtPath", &pyniv::GetDoubleAtPath) + .def("SetDoubleAtPath", &pyniv::SetDoubleAtPath); } } // namespace pyniv diff --git a/pyniv/src/conduit_data_sender.cpp b/pyniv/src/consumer/backend.cpp similarity index 61% rename from pyniv/src/conduit_data_sender.cpp rename to pyniv/src/consumer/backend.cpp index ad085c6..b4b5cee 100644 --- a/pyniv/src/conduit_data_sender.cpp +++ b/pyniv/src/consumer/backend.cpp @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // nest in situ vis // -// Copyright (c) 2017 RWTH Aachen University, Germany, +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, // Virtual Reality & Immersive Visualisation Group. //------------------------------------------------------------------------------ // License @@ -19,21 +19,24 @@ // limitations under the License. //------------------------------------------------------------------------------ -#include "conduit_data_sender.hpp" - -#include "conduit_data.hpp" #include "pyniv.hpp" -namespace pyniv { +#include "niv/consumer/backend.hpp" +#include "niv/consumer/device.hpp" -void ConduitDataSender::Send(const ConduitData& data) { - relay_.Send(data.GetNode()); -} +namespace pyniv { template <> -void expose() { - class_("ConduitDataSender") - .def("Send", &ConduitDataSender::Send, args("data")); +void expose() { + class_("ConsumerBackend") + .def("Connect", + static_cast(&niv::consumer::Backend::Connect)) + .def( + "Connect", + static_cast( + &niv::consumer::Backend::Connect)) + .def("Receive", &niv::consumer::Backend::Receive); } } // namespace pyniv diff --git a/pyniv/src/consumer/device.cpp b/pyniv/src/consumer/device.cpp new file mode 100644 index 0000000..2c3e59b --- /dev/null +++ b/pyniv/src/consumer/device.cpp @@ -0,0 +1,62 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include "pyniv.hpp" + +#include // NOLINT + +SUPPRESS_WARNINGS_BEGIN +#include "boost/python/numpy.hpp" +SUPPRESS_WARNINGS_END + +#include "niv/consumer/device.hpp" + +namespace pyniv { +namespace consumer { + +struct DeviceWrap : niv::consumer::Device, wrapper { + explicit DeviceWrap(const std::string& name) : niv::consumer::Device(name) {} + void Update() { this->get_override("Update")(); } + + static boost::python::numpy::ndarray GetTimesteps( + niv::consumer::Device* device) { + auto& timesteps{device->GetTimesteps()}; + + return boost::python::numpy::from_data( + timesteps.data(), boost::python::numpy::dtype::get_builtin(), + boost::python::make_tuple(timesteps.size()), + boost::python::make_tuple(sizeof(double)), boost::python::object()); + } +}; + +} // namespace consumer + +template <> +void expose() { + class_("ConsumerDevice", + init()) + .def("GetTimesteps", &pyniv::consumer::DeviceWrap::GetTimesteps) + .def("SetNode", &niv::consumer::Device::SetNode) + .def("SetTime", &niv::consumer::Device::SetTime) + .def("Update", pure_virtual(&niv::consumer::Device::Update)); +} + +} // namespace pyniv diff --git a/pyniv/src/consumer/multimeter.cpp b/pyniv/src/consumer/multimeter.cpp new file mode 100644 index 0000000..b7e9a71 --- /dev/null +++ b/pyniv/src/consumer/multimeter.cpp @@ -0,0 +1,61 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include "pyniv.hpp" + +#include // NOLINT +#include // NOLINT +#include // NOLINT + +SUPPRESS_WARNINGS_BEGIN +#include "boost/python/numpy.hpp" +SUPPRESS_WARNINGS_END + +#include "niv/consumer/device.hpp" +#include "niv/consumer/multimeter.hpp" + +namespace pyniv { +namespace consumer { + +boost::python::numpy::ndarray GetValues( + const niv::consumer::Multimeter& multimeter); +boost::python::numpy::ndarray GetValues( + const niv::consumer::Multimeter& multimeter) { + const auto& values{multimeter.GetValues()}; + + return boost::python::numpy::from_data( + values.data(), boost::python::numpy::dtype::get_builtin(), + boost::python::make_tuple(values.size()), + boost::python::make_tuple(sizeof(double)), boost::python::object()); +} + +} // namespace consumer + +template <> +void expose() { + class_>( + "ConsumerMultimeter", init()) + .def("GetValues", &pyniv::consumer::GetValues) + .def("SetAttribute", &niv::consumer::Multimeter::SetAttribute) + .def("Update", &niv::consumer::Multimeter::Update); +} + +} // namespace pyniv diff --git a/pyniv/src/conduit_receiver.cpp b/pyniv/src/consumer/receiver.cpp similarity index 59% rename from pyniv/src/conduit_receiver.cpp rename to pyniv/src/consumer/receiver.cpp index 73943e5..67614f7 100644 --- a/pyniv/src/conduit_receiver.cpp +++ b/pyniv/src/consumer/receiver.cpp @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // nest in situ vis // -// Copyright (c) 2017 RWTH Aachen University, Germany, +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, // Virtual Reality & Immersive Visualisation Group. //------------------------------------------------------------------------------ // License @@ -19,17 +19,32 @@ // limitations under the License. //------------------------------------------------------------------------------ -#include "niv/conduit_receiver.hpp" - #include "pyniv.hpp" +#include "niv/consumer/receiver.hpp" + namespace pyniv { +namespace consumer { + +struct ReceiverWrap : niv::consumer::Receiver, + wrapper { + void Receive() { + boost::python::override receive = this->get_override("Receive"); + if (receive) { + receive(); + } else { + niv::consumer::Receiver::Receive(); + } + } +}; + +} // namespace consumer template <> -void expose() { - class_("ConduitReceiver") - .def("Start", &niv::ConduitReceiver::Start) - .def("Get", &niv::ConduitReceiver::Get, args("path")); +void expose() { + class_("ConsumerReceiver") + .def("Receive", &pyniv::consumer::ReceiverWrap::Receive) + .def("SetNode", &niv::consumer::Receiver::SetNode); } } // namespace pyniv diff --git a/pyniv/src/nest_test_data.cpp b/pyniv/src/nest_test_data.cpp new file mode 100644 index 0000000..30a63ac --- /dev/null +++ b/pyniv/src/nest_test_data.cpp @@ -0,0 +1,97 @@ +//------------------------------------------------------------------------------ +// nest in situ vis +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualisation Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include "pyniv.hpp" + +#include // NOLINT +#include // NOLINT + +SUPPRESS_WARNINGS_BEGIN +#include "boost/python/numpy.hpp" +SUPPRESS_WARNINGS_END + +#include "conduit/conduit_node.hpp" + +#include "niv/nest_test_data.hpp" + +namespace pyniv { + +namespace testing { +bool Equal(const conduit::Node& node1, const conduit::Node& node2) { + bool is_equal = (node1.to_json() == node2.to_json()); + if (!is_equal) { + std::cout << "Nodes are not equal:" << std::endl; + std::cout << "Node 1:" << std::endl; + std::cout << node1.to_json() << std::endl; + std::cout << "----------" << std::endl; + std::cout << "Node 2:" << std::endl; + std::cout << node2.to_json() << std::endl; + } + return is_equal; +} + +std::string AnyValueNames(std::size_t index) { + return niv::testing::AnyValueNames()[index]; +} + +boost::python::numpy::ndarray AnyAttributesValues() { + static std::vector values{niv::testing::AnyAttributesValues()}; + + return boost::python::numpy::from_data( + values.data(), boost::python::numpy::dtype::get_builtin(), + boost::python::make_tuple(values.size()), + boost::python::make_tuple(sizeof(double)), boost::python::object()); +} + +boost::python::numpy::ndarray AnotherAttributesValues() { + static std::vector values{niv::testing::AnotherAttributesValues()}; + + return boost::python::numpy::from_data( + values.data(), boost::python::numpy::dtype::get_builtin(), + boost::python::make_tuple(values.size()), + boost::python::make_tuple(sizeof(double)), boost::python::object()); +} + +} // namespace testing + +template <> +void expose() { + def("TestingAnyAttribute", &niv::testing::AnyAttribute); + def("TestingAnotherAttribute", &niv::testing::AnotherAttribute); + def("TestingThirdAttribute", &niv::testing::ThirdAttribute); + def("TestingAnyTime", &niv::testing::AnyTime); + def("TestingAnyAttributesValues", &pyniv::testing::AnyAttributesValues); + def("TestingAnotherAttributesValues", + &pyniv::testing::AnotherAttributesValues); + def("TestingThirdAttributesValues", &niv::testing::ThirdAttributesValues); + def("TestingAnyValueNames", &pyniv::testing::AnyValueNames); + def("TestingAnyMultimeterName", &niv::testing::AnyMultimeterName); + def("TestingAnyNestData", &niv::testing::AnyNestData); + def("TestingSend", &niv::testing::Send); + def("TestingAnyNode", &niv::testing::AnyNode); + def("TestingAnotherNode", &niv::testing::AnotherNode); + def("TestingUpdate", &niv::testing::Update); + def("TestingUpdatedNode", &niv::testing::UpdatedNode); + def("TestingADifferentNode", &niv::testing::ADifferentNode); + def("TestingEqual", &pyniv::testing::Equal); +} + +} // namespace pyniv diff --git a/pyniv/src/pyniv.cpp b/pyniv/src/pyniv.cpp index f0f7a70..185e507 100644 --- a/pyniv/src/pyniv.cpp +++ b/pyniv/src/pyniv.cpp @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // nest in situ vis // -// Copyright (c) 2017 RWTH Aachen University, Germany, +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, // Virtual Reality & Immersive Visualisation Group. //------------------------------------------------------------------------------ // License @@ -21,15 +21,28 @@ #include "pyniv.hpp" -#include "niv/conduit_receiver.hpp" -#include "niv/niv.hpp" +SUPPRESS_WARNINGS_BEGIN +#include "boost/python/numpy.hpp" +SUPPRESS_WARNINGS_END + +#include "conduit/conduit_node.hpp" -#include "conduit_data.hpp" -#include "conduit_data_sender.hpp" +#include "niv/consumer/backend.hpp" +#include "niv/consumer/multimeter.hpp" +#include "niv/consumer/receiver.hpp" +#include "niv/nest_test_data.hpp" +#include "niv/niv.hpp" BOOST_PYTHON_MODULE(pyniv) { + boost::python::numpy::initialize(); def("Greet", niv::Greet); - pyniv::expose(); - pyniv::expose(); - pyniv::expose(); + + pyniv::expose(); + + pyniv::expose(); + pyniv::expose(); + pyniv::expose(); + pyniv::expose(); + + pyniv::expose(); } diff --git a/pyniv/src/pyniv.hpp b/pyniv/src/pyniv.hpp index 360bb67..45ab35c 100644 --- a/pyniv/src/pyniv.hpp +++ b/pyniv/src/pyniv.hpp @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // nest in situ vis // -// Copyright (c) 2017 RWTH Aachen University, Germany, +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, // Virtual Reality & Immersive Visualisation Group. //------------------------------------------------------------------------------ // License @@ -24,13 +24,21 @@ SUPPRESS_WARNINGS_BEGIN #pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#if __GNUC__ >= 7 +#pragma GCC diagnostic ignored "-Wregister" +#endif #include "boost/python.hpp" SUPPRESS_WARNINGS_END +using boost::noncopyable; using boost::python::args; +using boost::python::bases; using boost::python::class_; using boost::python::def; using boost::python::init; +using boost::python::no_init; +using boost::python::pure_virtual; +using boost::python::wrapper; namespace pyniv { diff --git a/pyniv/tests/CMakeLists.txt b/pyniv/tests/CMakeLists.txt index dcf1703..1575632 100644 --- a/pyniv/tests/CMakeLists.txt +++ b/pyniv/tests/CMakeLists.txt @@ -1,7 +1,7 @@ #------------------------------------------------------------------------------- # nest in situ vis # -# Copyright (c) 2017 RWTH Aachen University, Germany, +# Copyright (c) 2017-2018 RWTH Aachen University, Germany, # Virtual Reality & Immersive Visualisation Group. #------------------------------------------------------------------------------- # License @@ -21,7 +21,7 @@ -file(GLOB PYNIV_TEST_SOURCES src/*.py) +file(GLOB PYNIV_TEST_SOURCES src/*.py src/**/*.py) add_test_py_test(NAME test_pyniv ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/pyniv/tests/src/consumer/test_backend.py b/pyniv/tests/src/consumer/test_backend.py new file mode 100644 index 0000000..36056f9 --- /dev/null +++ b/pyniv/tests/src/consumer/test_backend.py @@ -0,0 +1,52 @@ +#------------------------------------------------------------------------------- +# nest in situ vis +# +# Copyright (c) 2017-2018 RWTH Aachen University, Germany, +# Virtual Reality & Immersive Visualisation Group. +#------------------------------------------------------------------------------- +# License +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#------------------------------------------------------------------------------- + +import pyniv + +class Receiver(pyniv.ConsumerReceiver): + def __init__(self): + pyniv.ConsumerReceiver.__init__(self) + self.count_receives = 0; + + def Receive(self): + self.count_receives += 1; + +class Device(pyniv.ConsumerDevice): + def __init__(self): + pyniv.ConsumerDevice.__init__(self, "any_name") + self.count_updates = 0; + + def Update(self): + self.count_updates += 1; + +def test_pyniv_consumer_backend(): + backend = pyniv.ConsumerBackend() + + receiver = Receiver() + backend.Connect(receiver) + backend.Receive() + assert receiver.count_receives == 1 + + device = Device() + backend.Connect(device) + backend.Receive() + assert receiver.count_receives == 2 + assert device.count_updates == 1 diff --git a/pyniv/tests/src/consumer/test_device.py b/pyniv/tests/src/consumer/test_device.py new file mode 100644 index 0000000..b904089 --- /dev/null +++ b/pyniv/tests/src/consumer/test_device.py @@ -0,0 +1,34 @@ +#------------------------------------------------------------------------------- +# nest in situ vis +# +# Copyright (c) 2017-2018 RWTH Aachen University, Germany, +# Virtual Reality & Immersive Visualisation Group. +#------------------------------------------------------------------------------- +# License +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#------------------------------------------------------------------------------- + +import pyniv + +class Device(pyniv.ConsumerDevice): + def __init__(self, name): + pyniv.ConsumerDevice.__init__(self, name) + +def test_consumer_device_lists_timesteps(): + any_data = pyniv.TestingAnyNestData() + device = Device(pyniv.TestingAnyMultimeterName()) + device.SetNode(any_data) + timesteps = device.GetTimesteps() + assert (timesteps == [0.0]).all() + assert len(timesteps) == 1 diff --git a/pyniv/tests/src/consumer/test_integration.py b/pyniv/tests/src/consumer/test_integration.py new file mode 100644 index 0000000..9e2996e --- /dev/null +++ b/pyniv/tests/src/consumer/test_integration.py @@ -0,0 +1,42 @@ +#------------------------------------------------------------------------------- +# nest in situ vis +# +# Copyright (c) 2017-2018 RWTH Aachen University, Germany, +# Virtual Reality & Immersive Visualisation Group. +#------------------------------------------------------------------------------- +# License +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#------------------------------------------------------------------------------- + +import pyniv + +def test_integration_consumer(): + backend = pyniv.ConsumerBackend() + + receiver = pyniv.ConsumerReceiver() + backend.Connect(receiver) + + multimeter = pyniv.ConsumerMultimeter(pyniv.TestingAnyMultimeterName()) + multimeter.SetAttribute(pyniv.TestingAnyValueNames(0)) + backend.Connect(multimeter) + + pyniv.TestingSend(pyniv.TestingAnyNestData()) + + backend.Receive() + + multimeter.SetTime(pyniv.TestingAnyTime()) + multimeter.Update() + + values = multimeter.GetValues() + assert (values == pyniv.TestingAnyAttributesValues()).all() diff --git a/pyniv/tests/src/consumer/test_multimeter.py b/pyniv/tests/src/consumer/test_multimeter.py new file mode 100644 index 0000000..69c85dc --- /dev/null +++ b/pyniv/tests/src/consumer/test_multimeter.py @@ -0,0 +1,39 @@ +#------------------------------------------------------------------------------- +# nest in situ vis +# +# Copyright (c) 2017-2018 RWTH Aachen University, Germany, +# Virtual Reality & Immersive Visualisation Group. +#------------------------------------------------------------------------------- +# License +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#------------------------------------------------------------------------------- + +import pyniv + +def test_multimeter_provides_access_to_data_stored_in_a_conduit_node(): + nest_data = pyniv.TestingAnyNestData() + multimeter = pyniv.ConsumerMultimeter(pyniv.TestingAnyMultimeterName()) + multimeter.SetNode(nest_data) + + multimeter.SetTime(pyniv.TestingAnyTime()) + + multimeter.SetAttribute(pyniv.TestingAnyValueNames(0)) + multimeter.Update() + result = multimeter.GetValues() + assert (result == pyniv.TestingAnyAttributesValues()).all() + + multimeter.SetAttribute(pyniv.TestingAnyValueNames(1)) + multimeter.Update() + result = multimeter.GetValues() + assert (result == pyniv.TestingAnotherAttributesValues()).all() diff --git a/pyniv/tests/src/consumer/test_receiver.py b/pyniv/tests/src/consumer/test_receiver.py new file mode 100644 index 0000000..e210187 --- /dev/null +++ b/pyniv/tests/src/consumer/test_receiver.py @@ -0,0 +1,36 @@ +#------------------------------------------------------------------------------- +# nest in situ vis +# +# Copyright (c) 2017-2018 RWTH Aachen University, Germany, +# Virtual Reality & Immersive Visualisation Group. +#------------------------------------------------------------------------------- +# License +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#------------------------------------------------------------------------------- + +import pyniv + +def test_receiver_aggregates_data(): + receiver = pyniv.ConsumerReceiver() + receiving_node = pyniv.ConduitNode() + receiver.SetNode(receiving_node) + + pyniv.TestingSend(pyniv.TestingAnyNode()) + receiver.Receive() + assert pyniv.TestingEqual(receiving_node, pyniv.TestingAnyNode()) + + pyniv.TestingSend(pyniv.TestingUpdate()) + receiver.Receive() + assert pyniv.TestingEqual(receiving_node, pyniv.TestingUpdatedNode()) + diff --git a/pyniv/tests/src/test_conduit_node.py b/pyniv/tests/src/test_conduit_node.py new file mode 100644 index 0000000..4fa0bea --- /dev/null +++ b/pyniv/tests/src/test_conduit_node.py @@ -0,0 +1,30 @@ +#------------------------------------------------------------------------------- +# nest in situ vis +# +# Copyright (c) 2017-2018 RWTH Aachen University, Germany, +# Virtual Reality & Immersive Visualisation Group. +#------------------------------------------------------------------------------- +# License +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#------------------------------------------------------------------------------- + +import pyniv + +def test_conduit_node(): + any_path = "any/path" + any_double = 42.0 + + node = pyniv.ConduitNode() + node.SetDoubleAtPath(any_path, any_double) + assert node.GetDoubleAtPath(any_path) == any_double diff --git a/pyniv/tests/src/test_cout_capture.py b/pyniv/tests/src/test_cout_capture.py new file mode 100644 index 0000000..798ff3d --- /dev/null +++ b/pyniv/tests/src/test_cout_capture.py @@ -0,0 +1,32 @@ +#------------------------------------------------------------------------------- +# nest in situ vis +# +# Copyright (c) 2017-2018 RWTH Aachen University, Germany, +# Virtual Reality & Immersive Visualisation Group. +#------------------------------------------------------------------------------- +# License +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#------------------------------------------------------------------------------- + +import sys + +def test_cout_capture(capsys): + print("hello") + sys.stderr.write("world\n") + out, err = capsys.readouterr() + assert out == "hello\n" + assert err == "world\n" + print ("next") + out, err = capsys.readouterr() + assert out == "next\n" diff --git a/pyniv/tests/src/test_pynpv.py b/pyniv/tests/src/test_pynpv.py index 3f8fd95..8dd3e6c 100644 --- a/pyniv/tests/src/test_pynpv.py +++ b/pyniv/tests/src/test_pynpv.py @@ -1,7 +1,7 @@ #------------------------------------------------------------------------------- # nest in situ vis # -# Copyright (c) 2017 RWTH Aachen University, Germany, +# Copyright (c) 2017-2018 RWTH Aachen University, Germany, # Virtual Reality & Immersive Visualisation Group. #------------------------------------------------------------------------------- # License @@ -19,42 +19,10 @@ # limitations under the License. #------------------------------------------------------------------------------- -import ctypes -import sys -import time - import pyniv -import pytest_utilities - def test_pypvt(): assert True -def test_cout_capture(capsys): - print("hello") - sys.stderr.write("world\n") - out, err = capsys.readouterr() - assert out == "hello\n" - assert err == "world\n" - print ("next") - out, err = capsys.readouterr() - assert out == "next\n" - def test_pyniv_greet(): assert pyniv.Greet() == "G'day!" - -def test_pyniv_receive_via_shared_mem_segment_relay(): - r = pyniv.ConduitReceiver() - - d = pyniv.ConduitData() - s = pyniv.ConduitDataSender() - s.Send(d) - - r.Start() - - assert r.Get("V_m") == 1.2 - - d.Set("V_m", 42.0) - s.Send(d) - - assert r.Get("V_m") == 42.0 diff --git a/pytest_utilities/CMakeLists.txt b/pytest_utilities/CMakeLists.txt index acecd98..cfe1aeb 100644 --- a/pytest_utilities/CMakeLists.txt +++ b/pytest_utilities/CMakeLists.txt @@ -1,7 +1,7 @@ #------------------------------------------------------------------------------- # nest in situ vis # -# Copyright (c) 2017 RWTH Aachen University, Germany, +# Copyright (c) 2017-2018 RWTH Aachen University, Germany, # Virtual Reality & Immersive Visualisation Group. #------------------------------------------------------------------------------- # License diff --git a/pytest_utilities/src/cout_capture.cpp b/pytest_utilities/src/cout_capture.cpp index 65fc1b3..5936377 100644 --- a/pytest_utilities/src/cout_capture.cpp +++ b/pytest_utilities/src/cout_capture.cpp @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // nest in situ vis // -// Copyright (c) 2017 RWTH Aachen University, Germany, +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, // Virtual Reality & Immersive Visualisation Group. //------------------------------------------------------------------------------ // License @@ -19,14 +19,17 @@ // limitations under the License. //------------------------------------------------------------------------------ -#include -#include - SUPPRESS_WARNINGS_BEGIN #pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#if __GNUC__ >= 7 +#pragma GCC diagnostic ignored "-Wregister" +#endif #include "boost/python.hpp" SUPPRESS_WARNINGS_END +#include // NOLINT +#include // NOLINT + namespace test_utilities { class CoutCapture {