init
This commit is contained in:
+702
@@ -0,0 +1,702 @@
|
||||
<?xml version="1.0"?>
|
||||
<interface>
|
||||
<!-- interface-requires gtk+ 2.12 -->
|
||||
<!-- interface-naming-policy toplevel-contextual -->
|
||||
<object class="GtkAdjustment" id="adjustment1">
|
||||
<property name="value">1025</property>
|
||||
<property name="upper">49151</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="adjustment2">
|
||||
<property name="upper">100</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="adjustment3">
|
||||
<property name="upper">100</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="adjustment4">
|
||||
<property name="value">1</property>
|
||||
<property name="lower">1</property>
|
||||
<property name="upper">1024</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkListStore" id="model1">
|
||||
<columns>
|
||||
<!-- column-name gchararray -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">unspecified</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">female</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">male</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">unknown</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkListStore" id="model2">
|
||||
<columns>
|
||||
<!-- column-name gchararray -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">128</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">256</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">512</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">1024</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">2048</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">4096</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkDialog" id="openvibe-acquisition-server-settings">
|
||||
<property name="border_width">5</property>
|
||||
<property name="title" translatable="yes">Device configuration</property>
|
||||
<property name="window_position">center</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<property name="gravity">center</property>
|
||||
<property name="has_separator">False</property>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkVBox" id="dialog-vbox">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK</property>
|
||||
<property name="spacing">8</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_title">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Fieldtrip</property>
|
||||
<property name="justify">center</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHSeparator" id="hseparator">
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox">
|
||||
<property name="visible">True</property>
|
||||
<property name="border_width">8</property>
|
||||
<property name="spacing">8</property>
|
||||
<child>
|
||||
<object class="GtkHBox" id="hbox">
|
||||
<property name="visible">True</property>
|
||||
<property name="spacing">8</property>
|
||||
<child>
|
||||
<object class="GtkTable" id="table2">
|
||||
<property name="visible">True</property>
|
||||
<property name="n_rows">7</property>
|
||||
<property name="n_columns">2</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spinbutton_host_port">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="adjustment">adjustment1</property>
|
||||
<property name="snap_to_ticks">True</property>
|
||||
<property name="numeric">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">4</property>
|
||||
<property name="bottom_attach">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="entry_host_name">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="max_length">15</property>
|
||||
<property name="invisible_char">•</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_host_port">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Buffer port number</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">4</property>
|
||||
<property name="bottom_attach">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_host_name">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Buffer host name</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_identifier">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Identifier :</property>
|
||||
<property name="justify">right</property>
|
||||
<property name="single_line_mode">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_age">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Age :</property>
|
||||
<property name="justify">right</property>
|
||||
<property name="single_line_mode">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_gender">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Gender :</property>
|
||||
<property name="justify">right</property>
|
||||
<property name="single_line_mode">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spinbutton_identifier">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="adjustment">adjustment2</property>
|
||||
<property name="snap_to_ticks">True</property>
|
||||
<property name="numeric">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spinbutton_age">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="adjustment">adjustment3</property>
|
||||
<property name="snap_to_ticks">True</property>
|
||||
<property name="numeric">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="combobox_gender">
|
||||
<property name="visible">True</property>
|
||||
<property name="model">model1</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="renderer1"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_minSamples">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Min nb of samples in buffer</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">5</property>
|
||||
<property name="bottom_attach">6</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spinbutton_minSamples">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="invisible_char">●</property>
|
||||
<property name="adjustment">adjustment5</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">5</property>
|
||||
<property name="bottom_attach">6</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="checkbutton_SRCorrection">
|
||||
<property name="label" translatable="yes">Correct manually drift due to non-integer sampling rate</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">6</property>
|
||||
<property name="bottom_attach">7</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHSeparator" id="hseparator1">
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkHButtonBox" id="dialog-action_area">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="button_apply">
|
||||
<property name="label">gtk-apply</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="button_cancel">
|
||||
<property name="label">gtk-cancel</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<action-widgets>
|
||||
<action-widget response="-10">button_apply</action-widget>
|
||||
<action-widget response="-6">button_cancel</action-widget>
|
||||
</action-widgets>
|
||||
</object>
|
||||
<object class="GtkWindow" id="dummy-widgets">
|
||||
<child>
|
||||
<object class="GtkTable" id="table1">
|
||||
<property name="visible">True</property>
|
||||
<property name="n_rows">10</property>
|
||||
<property name="n_columns">10</property>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spinbutton_number_of_channels">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="adjustment">adjustment4</property>
|
||||
<property name="snap_to_ticks">True</property>
|
||||
<property name="numeric">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="combobox_sampling_frequency">
|
||||
<property name="visible">True</property>
|
||||
<property name="model">model2</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="renderer2"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="button_change_channel_names">
|
||||
<property name="label" translatable="yes">Change channel names</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">False</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="adjustment5">
|
||||
<property name="value">1</property>
|
||||
<property name="lower">1</property>
|
||||
<property name="upper">10000</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
</interface>
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "windows.h"
|
||||
#include "ov_defines.h"
|
||||
#include "ovCString.h"
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
// Get Cpu Time //
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
namespace OpenViBE
|
||||
{
|
||||
OV_API bool GetEnvVar( CString& sVar, CString& sValue );
|
||||
OV_API bool PutEnvVar( CString& sVar, CString& sValue );
|
||||
|
||||
OV_API inline double GetCPUTimeInMilliseconds()
|
||||
{
|
||||
static bool bGotCPUFreq = false;
|
||||
static double timerFrequency = 1;
|
||||
if ( !bGotCPUFreq )
|
||||
{
|
||||
unsigned __int64 cpufreq = 1;
|
||||
QueryPerformanceFrequency((LARGE_INTEGER*)&cpufreq);
|
||||
timerFrequency = (1000.0/cpufreq);
|
||||
}
|
||||
unsigned __int64 curTime = 0;
|
||||
QueryPerformanceCounter((LARGE_INTEGER *)&curTime);
|
||||
return timerFrequency * curTime;
|
||||
};
|
||||
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2008, Robert Oostenveld & Christian Hesse
|
||||
* F.C. Donders Centre for Cognitive Neuroimaging, Radboud University Nijmegen,
|
||||
* Kapittelweg 29, 6525 EN Nijmegen, The Netherlands
|
||||
*
|
||||
*/
|
||||
/* prevent double include */
|
||||
#ifndef BUFFER_H
|
||||
#define BUFFER_H
|
||||
|
||||
#include <cstdlib>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "platform_includes.h"
|
||||
#include "message.h"
|
||||
|
||||
#ifndef POLLRDNORM
|
||||
#define POLLRDNORM POLLIN
|
||||
#endif
|
||||
|
||||
#ifndef POLLRDBAND
|
||||
#define POLLRDBAND POLLPRI
|
||||
#endif
|
||||
|
||||
#ifndef POLLWRNORM
|
||||
#define POLLWRNORM POLLOUT
|
||||
#endif
|
||||
|
||||
#ifndef POLLWRBAND
|
||||
#define POLLWRBAND POLLOUT
|
||||
#endif
|
||||
|
||||
#define BACKLOG 16
|
||||
#define DEFAULT_HOSTNAME "localhost"
|
||||
#define DEFAULT_PORT 1972
|
||||
|
||||
#define SO_RCVBUF_SIZE 16384
|
||||
#define SO_SNDBUF_SIZE 16384
|
||||
|
||||
/* this is because the function has been renamed, but is perhaps already in use in other software */
|
||||
#define open_remotehost open_connection
|
||||
|
||||
/* FIXME these should be variable */
|
||||
#define MAXNUMBYTE (512*1024*1024)
|
||||
#define MAXNUMSAMPLE 600000
|
||||
#define MAXNUMEVENT 100
|
||||
|
||||
#define WRAP(x,y) ((x) - (int(float(x)/(y)))*(y))
|
||||
#define FREE(x) {if (x) {free(x); x= nullptr;}}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
||||
#endif
|
||||
|
||||
/* declaration of "public" buffer API functions */
|
||||
/* SK: where are these, and what are they for ? */
|
||||
int read_header( const char *hostname, int port, void **ppw);
|
||||
int read_data( const char *hostname, int port, int *pnw, void **ppw);
|
||||
int read_event( const char *hostname, int port, int *pnw, void **ppw);
|
||||
int write_header(const char *hostname, int port, void **ppw);
|
||||
int write_data( const char *hostname, int port, int *pnw, void **ppw);
|
||||
int write_event( const char *hostname, int port, int *pnw, void **ppw);
|
||||
int flush_header(const char *hostname, int port);
|
||||
int flush_data( const char *hostname, int port);
|
||||
int flush_event( const char *hostname, int port);
|
||||
void cleanup_buffer();
|
||||
|
||||
/* definition of the functions that implement the network transparent server */
|
||||
void *tcpserver(void *);
|
||||
void *tcpsocket(void *);
|
||||
|
||||
/* definition of test functions that emulate an acquisition system */
|
||||
void *sinewave_thread(void *);
|
||||
void *event_thread(void *);
|
||||
|
||||
/* definition of the functions used in thread cancelation, see cleanup.c */
|
||||
void cleanup_message(void **arg);
|
||||
void cleanup_header(void **arg);
|
||||
void cleanup_data(void **arg);
|
||||
void cleanup_event(void **arg);
|
||||
void cleanup_buf(void **arg);
|
||||
void cleanup_socket(int *);
|
||||
|
||||
/* definition of helper functions for debugging and printing the content of various structures */
|
||||
void print_request(messagedef_t *);
|
||||
void print_response(messagedef_t *);
|
||||
void print_headerdef(headerdef_t *);
|
||||
void print_datadef(datadef_t *);
|
||||
void print_eventdef(eventdef_t *);
|
||||
void print_datasel(datasel_t *);
|
||||
void print_eventsel(eventsel_t *);
|
||||
void print_buf(void *, int);
|
||||
|
||||
/* definition of even more helper functions, see util.c */
|
||||
int open_connection(const char*, int);
|
||||
int open_unix_connection(const char *name);
|
||||
int close_connection(int);
|
||||
unsigned int append(void **, unsigned int, void *, unsigned int);
|
||||
unsigned int bufread(int, void *, unsigned int);
|
||||
unsigned int bufwrite(int, const void *, unsigned int);
|
||||
int clientrequest(int, const message_t *, message_t**);
|
||||
int dmarequest(const message_t *, message_t**);
|
||||
int tcprequest(int, const message_t *, message_t**);
|
||||
unsigned int wordsize_from_type(UINT32_T data_type);
|
||||
void check_datatypes();
|
||||
int check_event_array(unsigned int size, const void *buf);
|
||||
const ft_chunk_t *find_chunk(const void *buf, unsigned int offset0, unsigned int size, UINT32_T chunk_type);
|
||||
|
||||
void ft_swap16(unsigned int numel, void *data);
|
||||
void ft_swap32(unsigned int numel, void *data);
|
||||
void ft_swap64(unsigned int numel, void *data);
|
||||
int ft_swap_buf_to_native(UINT16_T command, UINT32_T bufsize, void *buf);
|
||||
int ft_convert_chunks_from_native(UINT32_T size, UINT32_T nchans, void *buf);
|
||||
int ft_swap_from_native(UINT16_T orgCommand, message_t *msg);
|
||||
|
||||
typedef struct {
|
||||
char name[256];
|
||||
int port;
|
||||
} host_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2008, Robert Oostenveld
|
||||
* F.C. Donders Centre for Cognitive Neuroimaging, Radboud University Nijmegen,
|
||||
* Kapittelweg 29, 6525 EN Nijmegen, The Netherlands
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "buffer.h"
|
||||
|
||||
/* this is used for debugging */
|
||||
int verbose = 0;
|
||||
|
||||
void cleanup_socket(int *arg) {
|
||||
if (verbose>0) fprintf(stderr, "cleanup_socket: s = %d\n", *arg);
|
||||
if ((*arg)>0) {
|
||||
close_connection(*arg);
|
||||
}
|
||||
*arg = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
void cleanup_message(void **arg) {
|
||||
message_t *message = (message_t *)*arg;
|
||||
if (verbose>0) fprintf(stderr, "cleanup_message()\n");
|
||||
if (message) {
|
||||
FREE(message->def);
|
||||
FREE(message->buf);
|
||||
FREE(message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void cleanup_header(void **arg) {
|
||||
header_t *header = (header_t *)*arg;
|
||||
if (verbose>0) fprintf(stderr, "cleanup_header()\n");
|
||||
if (header) {
|
||||
FREE(header->def);
|
||||
FREE(header->buf);
|
||||
FREE(header);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void cleanup_data(void **arg) {
|
||||
data_t *data = (data_t *)*arg;
|
||||
if (verbose>0) fprintf(stderr, "cleanup_data()\n");
|
||||
if (data) {
|
||||
FREE(data->def);
|
||||
FREE(data->buf);
|
||||
FREE(data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void cleanup_event(void **arg) {
|
||||
event_t *event = (event_t *)*arg;
|
||||
if (verbose>0) fprintf(stderr, "cleanup_event()\n");
|
||||
if (event) {
|
||||
FREE(event->def);
|
||||
FREE(event->buf);
|
||||
FREE(event);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void cleanup_buf(void **arg) {
|
||||
if (verbose>0) fprintf(stderr, "cleanup_buf()\n");
|
||||
if (*arg) {
|
||||
FREE(*arg);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2008, Robert Oostenveld
|
||||
* F.C. Donders Centre for Cognitive Neuroimaging, Radboud University Nijmegen,
|
||||
* Kapittelweg 29, 6525 EN Nijmegen, The Netherlands
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "buffer.h"
|
||||
|
||||
/*******************************************************************************
|
||||
* this function is called by the client
|
||||
* it takes care that the request is processed by the buffer
|
||||
*******************************************************************************/
|
||||
int clientrequest(int server, const message_t *request, message_t **response_ptr) {
|
||||
int verbose = 0;
|
||||
|
||||
if (verbose>0) fprintf(stderr, "clientrequest: server = %d\n", server);
|
||||
if (verbose>0) print_request(request->def);
|
||||
|
||||
if (server<0) {
|
||||
fprintf(stderr, "clientrequest: invalid value for server (%d)\n", server);
|
||||
return -1;
|
||||
}
|
||||
|
||||
else if (server==0) {
|
||||
/* use direct memory acces to the buffer */
|
||||
if (dmarequest(request, response_ptr)!=0)
|
||||
return -2;
|
||||
}
|
||||
|
||||
else if (server>0) {
|
||||
/* use TCP connection to the buffer */
|
||||
if (tcprequest(server, request, response_ptr)!=0)
|
||||
return -3;
|
||||
}
|
||||
|
||||
if (verbose>0) print_response((*response_ptr)->def);
|
||||
|
||||
/* everything went fine */
|
||||
return 0;
|
||||
}
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/* prevent double include */
|
||||
#pragma once
|
||||
|
||||
#if defined (__BORLANDC__)
|
||||
#define COMPILER_BORLAND
|
||||
|
||||
#elif defined (_MSC_VER)
|
||||
#define COMPILER_MSVC
|
||||
|
||||
#elif defined (__CYGWIN32__)
|
||||
#define COMPILER_CYGWIN
|
||||
|
||||
#elif defined (__MINGW32__)
|
||||
#define COMPILER_MINGW
|
||||
|
||||
#elif defined (__LCC__)
|
||||
#define COMPILER_LCC
|
||||
|
||||
#endif
|
||||
+735
@@ -0,0 +1,735 @@
|
||||
/*
|
||||
* Copyright (C) 2008, Robert Oostenveld
|
||||
* F.C. Donders Centre for Cognitive Neuroimaging, Radboud University Nijmegen,
|
||||
* Kapittelweg 29, 6525 EN Nijmegen, The Netherlands
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "buffer.h"
|
||||
#include <pthread.h>
|
||||
|
||||
#ifdef TARGET_OS_Linux
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
/* FIXME should these be static? */
|
||||
static header_t *header = NULL;
|
||||
static data_t *data = NULL;
|
||||
static event_t *event = NULL;
|
||||
|
||||
static unsigned int current_max_num_sample = 0;
|
||||
|
||||
static int thissample = 0; /* points at the buffer */
|
||||
static int thisevent = 0; /* points at the buffer */
|
||||
|
||||
/* Note that there have been problems with the order of the mutexes (e.g.
|
||||
* http://bugzilla.fcdonders.nl/show_bug.cgi?id=933).
|
||||
* I have attempted to make the order of locking consistent, but can't give
|
||||
* guarantees. A more long term solution could be:
|
||||
* - find the dependencies between modifications of volatile data (e.g. events
|
||||
* depend on header),
|
||||
* - keep locks as shortly as possible (get info, release again).
|
||||
*
|
||||
* This could results in a global lock (robust, probably not optimal in terms
|
||||
* of speed), or a series of locks sandwiching modification code in dedicated
|
||||
* functions.
|
||||
*
|
||||
* -- Boris
|
||||
*/
|
||||
pthread_mutex_t mutexheader = PTHREAD_MUTEX_INITIALIZER;
|
||||
pthread_mutex_t mutexdata = PTHREAD_MUTEX_INITIALIZER;
|
||||
pthread_mutex_t mutexevent = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
pthread_cond_t getData_cond = PTHREAD_COND_INITIALIZER;
|
||||
pthread_mutex_t getData_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
#define DIE_BAD_MALLOC(ptr) if ((ptr) == nullptr) { fprintf(stderr,"Out of memory with unchecked malloc in line %d",__LINE__); exit(1); }
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
void free_header() {
|
||||
int verbose = 0;
|
||||
if (verbose>0) fprintf(stderr, "free_header: freeing header buffer\n");
|
||||
if (header) {
|
||||
FREE(header->def);
|
||||
FREE(header->buf);
|
||||
FREE(header);
|
||||
}
|
||||
}
|
||||
|
||||
void free_data() {
|
||||
int verbose = 0;
|
||||
if (verbose>0) fprintf(stderr, "free_data: freeing data buffer\n");
|
||||
if (data) {
|
||||
FREE(data->def);
|
||||
FREE(data->buf);
|
||||
FREE(data);
|
||||
}
|
||||
thissample = 0;
|
||||
if (header) header->def->nsamples = 0;
|
||||
}
|
||||
|
||||
void free_event() {
|
||||
int verbose = 0;
|
||||
int i;
|
||||
if (verbose>0) fprintf(stderr, "free_event: freeing event buffer\n");
|
||||
if (event) {
|
||||
for (i=0; i<MAXNUMEVENT; ++i) {
|
||||
FREE(event[i].def);
|
||||
FREE(event[i].buf);
|
||||
}
|
||||
FREE(event);
|
||||
}
|
||||
thisevent = 0;
|
||||
if (header) header->def->nevents = 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
void init_data() {
|
||||
int verbose = 0;
|
||||
if (verbose>0) fprintf(stderr, "init_data: creating data buffer\n");
|
||||
if (header) {
|
||||
unsigned int wordsize = wordsize_from_type(header->def->data_type);
|
||||
|
||||
if (wordsize==0) {
|
||||
fprintf(stderr, "init_data: unsupported data type (%u)\n", header->def->data_type);
|
||||
return;
|
||||
}
|
||||
/* heuristic of choosing size of buffer:
|
||||
set current_max_num_sample to MAXNUMSAMPLE if nchans <= 256
|
||||
otherwise, allocate about MAXNUMBYTE and calculate current_max_num_sample from nchans + wordsize
|
||||
*/
|
||||
if (header->def->nchans <= 256) {
|
||||
current_max_num_sample = MAXNUMSAMPLE;
|
||||
} else {
|
||||
current_max_num_sample = MAXNUMBYTE / (wordsize * header->def->nchans);
|
||||
}
|
||||
data = (data_t*)malloc(sizeof(data_t));
|
||||
|
||||
DIE_BAD_MALLOC(data);
|
||||
|
||||
data->def = (datadef_t*)malloc(sizeof(datadef_t));
|
||||
|
||||
DIE_BAD_MALLOC(data->def);
|
||||
|
||||
data->def->nchans = header->def->nchans;
|
||||
data->def->nsamples = current_max_num_sample;
|
||||
data->def->data_type = header->def->data_type;
|
||||
data->buf = malloc(header->def->nchans*current_max_num_sample*wordsize);
|
||||
|
||||
DIE_BAD_MALLOC(data->buf);
|
||||
}
|
||||
}
|
||||
|
||||
void init_event() {
|
||||
int verbose = 0;
|
||||
int i;
|
||||
if (verbose>0) fprintf(stderr, "init_event: creating event buffer\n");
|
||||
if (header) {
|
||||
event = (event_t*)malloc(MAXNUMEVENT*sizeof(event_t));
|
||||
DIE_BAD_MALLOC(event);
|
||||
for (i=0; i<MAXNUMEVENT; ++i) {
|
||||
event[i].def = NULL;
|
||||
event[i].buf = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* this function handles the direct memory access to the buffer
|
||||
* and copies objects to and from memory
|
||||
*****************************************************************************/
|
||||
int dmarequest(const message_t *request, message_t **response_ptr) {
|
||||
unsigned int offset;
|
||||
/*
|
||||
int blockrequest = 0;
|
||||
*/
|
||||
int verbose = 0;
|
||||
|
||||
/* these are used for blocking the read requests */
|
||||
struct timeval tp;
|
||||
struct timespec ts;
|
||||
|
||||
/* use a local variable for datasel (in GET_DAT) */
|
||||
datasel_t datasel;
|
||||
|
||||
/* these are for typecasting */
|
||||
headerdef_t *headerdef;
|
||||
datadef_t *datadef;
|
||||
eventdef_t *eventdef;
|
||||
eventsel_t *eventsel;
|
||||
|
||||
/* this will hold the response */
|
||||
message_t *response;
|
||||
response = (message_t*)malloc(sizeof(message_t));
|
||||
|
||||
/* check for "out of memory" problems */
|
||||
if (response == nullptr) {
|
||||
*response_ptr = NULL;
|
||||
return -1;
|
||||
}
|
||||
response->def = (messagedef_t*)malloc(sizeof(messagedef_t));
|
||||
|
||||
/* check for "out of memory" problems */
|
||||
if (response->def == nullptr) {
|
||||
*response_ptr = NULL;
|
||||
free(response);
|
||||
return -1;
|
||||
}
|
||||
response->buf = NULL;
|
||||
/* the response should be passed to the calling function, where it should be freed */
|
||||
*response_ptr = response;
|
||||
|
||||
if (verbose>1) print_request(request->def);
|
||||
|
||||
switch (request->def->command) {
|
||||
|
||||
case PUT_HDR:
|
||||
if (verbose>1) fprintf(stderr, "dmarequest: PUT_HDR\n");
|
||||
pthread_mutex_lock(&mutexheader);
|
||||
pthread_mutex_lock(&mutexdata);
|
||||
pthread_mutex_lock(&mutexevent);
|
||||
|
||||
headerdef = (headerdef_t*)request->buf;
|
||||
if (verbose>1) print_headerdef(headerdef);
|
||||
|
||||
/* delete the old header, data and events */
|
||||
free_header();
|
||||
free_data();
|
||||
free_event();
|
||||
|
||||
/* store the header and re-initialize */
|
||||
header = (header_t*)malloc(sizeof(header_t));
|
||||
DIE_BAD_MALLOC(header);
|
||||
header->def = (headerdef_t*)malloc(sizeof(headerdef_t));
|
||||
DIE_BAD_MALLOC(header->def);
|
||||
header->buf = malloc(headerdef->bufsize);
|
||||
DIE_BAD_MALLOC(header->buf);
|
||||
memcpy(header->def, request->buf, sizeof(headerdef_t));
|
||||
memcpy(header->buf, (char*)request->buf+sizeof(headerdef_t), headerdef->bufsize);
|
||||
header->def->nsamples = 0;
|
||||
header->def->nevents = 0;
|
||||
|
||||
init_data();
|
||||
init_event();
|
||||
|
||||
response->def->version = VERSION;
|
||||
response->def->bufsize = 0;
|
||||
/* check whether memory could indeed be allocated */
|
||||
if (data!= NULL && data->buf != nullptr && data->def != nullptr) {
|
||||
response->def->command = PUT_OK;
|
||||
} else {
|
||||
/* let's at least tell the client that something's wrong */
|
||||
response->def->command = PUT_ERR;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&mutexevent);
|
||||
pthread_mutex_unlock(&mutexdata);
|
||||
pthread_mutex_unlock(&mutexheader);
|
||||
break;
|
||||
|
||||
case PUT_DAT:
|
||||
if (verbose>1) fprintf(stderr, "dmarequest: PUT_DAT\n");
|
||||
pthread_mutex_lock(&mutexheader);
|
||||
pthread_mutex_lock(&mutexdata);
|
||||
|
||||
datadef = (datadef_t*)request->buf;
|
||||
if (verbose>1) print_datadef(datadef);
|
||||
if (verbose>2) print_buf(request->buf, request->def->bufsize);
|
||||
|
||||
response->def->version = VERSION;
|
||||
response->def->bufsize = 0;
|
||||
if (request->def->bufsize < sizeof(datadef_t))
|
||||
response->def->command = PUT_ERR;
|
||||
else if (header == nullptr || data == nullptr)
|
||||
response->def->command = PUT_ERR;
|
||||
else if (header->def->nchans != datadef->nchans)
|
||||
response->def->command = PUT_ERR;
|
||||
else if (header->def->data_type != datadef->data_type)
|
||||
response->def->command = PUT_ERR;
|
||||
else if (datadef->nsamples > current_max_num_sample)
|
||||
response->def->command = PUT_ERR;
|
||||
else {
|
||||
unsigned int i;
|
||||
unsigned int wordsize = wordsize_from_type(header->def->data_type);
|
||||
unsigned int datasize = wordsize * datadef->nsamples * datadef->nchans;
|
||||
|
||||
response->def->command = PUT_OK;
|
||||
|
||||
if (wordsize == 0) {
|
||||
fprintf(stderr, "dmarequest: unsupported data type (%d)\n", datadef->data_type);
|
||||
response->def->command = PUT_ERR;
|
||||
} else if (datasize > datadef->bufsize || (datadef->bufsize + sizeof(datadef_t)) > request->def->bufsize) {
|
||||
fprintf(stderr, "dmarequest: invalid size definitions in PUT_DAT request\n");
|
||||
response->def->command = PUT_ERR;
|
||||
} else {
|
||||
/* number of bytes per sample (all channels) is given by wordsize x number of channels */
|
||||
unsigned int chansize = wordsize * data->def->nchans;
|
||||
/* request_data points to actual data samples within the request, use char* for convenience */
|
||||
const char *request_data = (const char *) request->buf + sizeof(datadef_t);
|
||||
char *buffer_data = (char *)data->buf;
|
||||
|
||||
for (i=0; i<datadef->nsamples; ++i) {
|
||||
memcpy(buffer_data+(thissample*chansize), request_data+(i*chansize), chansize);
|
||||
header->def->nsamples++;
|
||||
thissample++;
|
||||
thissample = WRAP(thissample, current_max_num_sample);
|
||||
}
|
||||
/* Signal possibly waiting threads that we have received data */
|
||||
pthread_cond_broadcast(&getData_cond);
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&mutexdata);
|
||||
pthread_mutex_unlock(&mutexheader);
|
||||
break;
|
||||
|
||||
case PUT_EVT:
|
||||
if (verbose>1) fprintf(stderr, "dmarequest: PUT_EVT\n");
|
||||
pthread_mutex_lock(&mutexheader);
|
||||
pthread_mutex_lock(&mutexevent);
|
||||
|
||||
/* Give an error message if there is no header, or if the given event array is defined badly */
|
||||
if (header == nullptr || event == nullptr || check_event_array(request->def->bufsize, request->buf) < 0) {
|
||||
response->def->version = VERSION;
|
||||
response->def->command = PUT_ERR;
|
||||
response->def->bufsize = 0;
|
||||
}
|
||||
else { /* go over all events and store them one by one */
|
||||
response->def->version = VERSION;
|
||||
response->def->command = PUT_OK;
|
||||
response->def->bufsize = 0;
|
||||
|
||||
offset = 0; /* this represents the offset of the event in the buffer */
|
||||
while (offset<request->def->bufsize) {
|
||||
FREE(event[thisevent].def);
|
||||
FREE(event[thisevent].buf);
|
||||
|
||||
eventdef = (eventdef_t*)((char*)request->buf+offset);
|
||||
if (verbose>1) print_eventdef(eventdef);
|
||||
|
||||
event[thisevent].def = (eventdef_t*)malloc(sizeof(eventdef_t));
|
||||
DIE_BAD_MALLOC(event[thisevent].def);
|
||||
memcpy(event[thisevent].def, (char*)request->buf+offset, sizeof(eventdef_t));
|
||||
|
||||
/* automatically convert event->def->sample to current sample number
|
||||
(thus this event "belongs" to the first sample of the next block from PUT_DAT)
|
||||
*/
|
||||
if (event[thisevent].def->sample == EVENT_AUTO_SAMPLE) {
|
||||
event[thisevent].def->sample = header->def->nsamples;
|
||||
}
|
||||
|
||||
offset += sizeof(eventdef_t);
|
||||
event[thisevent].buf = malloc(eventdef->bufsize);
|
||||
DIE_BAD_MALLOC(event[thisevent].buf);
|
||||
memcpy(event[thisevent].buf, (char*)request->buf+offset, eventdef->bufsize);
|
||||
offset += eventdef->bufsize;
|
||||
if (verbose>1) print_eventdef(event[thisevent].def);
|
||||
thisevent++;
|
||||
thisevent = WRAP(thisevent, MAXNUMEVENT);
|
||||
header->def->nevents++;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&mutexevent);
|
||||
pthread_mutex_unlock(&mutexheader);
|
||||
break;
|
||||
|
||||
case GET_HDR:
|
||||
if (verbose>1) fprintf(stderr, "dmarequest: GET_HDR\n");
|
||||
if (header == nullptr) {
|
||||
response->def->version = VERSION;
|
||||
response->def->command = GET_ERR;
|
||||
response->def->bufsize = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&mutexheader);
|
||||
|
||||
response->def->version = VERSION;
|
||||
response->def->command = GET_OK;
|
||||
response->def->bufsize = 0;
|
||||
response->def->bufsize = append(&response->buf, response->def->bufsize, header->def, sizeof(headerdef_t));
|
||||
response->def->bufsize = append(&response->buf, response->def->bufsize, header->buf, header->def->bufsize);
|
||||
|
||||
pthread_mutex_unlock(&mutexheader);
|
||||
break;
|
||||
|
||||
case GET_DAT:
|
||||
if (verbose>1) fprintf(stderr, "dmarequest: GET_DAT\n");
|
||||
if (header == nullptr || data == nullptr) {
|
||||
response->def->version = VERSION;
|
||||
response->def->command = GET_ERR;
|
||||
response->def->bufsize = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&mutexheader);
|
||||
pthread_mutex_lock(&mutexdata);
|
||||
|
||||
if (request->def->bufsize) {
|
||||
/* the selection has been specified */
|
||||
memcpy(&datasel, request->buf, sizeof(datasel_t));
|
||||
/* If endsample is -1 read the buffer to the end */
|
||||
if(datasel.endsample == UINT32_T(-1))
|
||||
{
|
||||
datasel.endsample = header->def->nsamples - 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* determine a valid selection */
|
||||
if (header->def->nsamples>current_max_num_sample) {
|
||||
/* the ringbuffer is completely full */
|
||||
datasel.begsample = header->def->nsamples - current_max_num_sample;
|
||||
datasel.endsample = header->def->nsamples - 1;
|
||||
}
|
||||
else {
|
||||
/* the ringbuffer is not yet completely full */
|
||||
datasel.begsample = 0;
|
||||
datasel.endsample = header->def->nsamples - 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
// if the read should block...
|
||||
if(blockrequest == 1)
|
||||
{
|
||||
// check whether data is available
|
||||
while((datasel.begsample >= (datasel.endsample+1)) || (datasel.endsample > header->def->nsamples - 1))
|
||||
{
|
||||
// if not unlock all mutexes
|
||||
pthread_mutex_unlock(&mutexdata);
|
||||
pthread_mutex_unlock(&mutexheader);
|
||||
|
||||
// wait for the condition to be signaled
|
||||
pthread_mutex_lock(&getData_mutex);
|
||||
gettimeofday(&tp, nullptr);
|
||||
ts.tv_sec = tp.tv_sec;
|
||||
ts.tv_nsec = tp.tv_usec * 1000;
|
||||
ts.tv_sec += 1;
|
||||
pthread_cond_timedwait(&getData_cond, &getData_mutex, &ts);
|
||||
pthread_mutex_unlock(&getData_mutex);
|
||||
|
||||
// Lock the mutexes again
|
||||
pthread_mutex_lock(&mutexheader);
|
||||
pthread_mutex_lock(&mutexdata);
|
||||
if(datasel.begsample == (datasel.endsample+1))
|
||||
datasel.endsample = header->def->nsamples - 1;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if (verbose>1) print_headerdef(header->def);
|
||||
if (verbose>1) print_datasel(&datasel);
|
||||
|
||||
if (datasel.begsample < 0 || datasel.endsample < 0) {
|
||||
fprintf(stderr, "dmarequest: err1\n");
|
||||
response->def->version = VERSION;
|
||||
response->def->command = GET_ERR;
|
||||
response->def->bufsize = 0;
|
||||
}
|
||||
else if (datasel.begsample >= header->def->nsamples || datasel.endsample >= header->def->nsamples) {
|
||||
fprintf(stderr, "dmarequest: err2\n");
|
||||
response->def->version = VERSION;
|
||||
response->def->command = GET_ERR;
|
||||
response->def->bufsize = 0;
|
||||
}
|
||||
else if ((header->def->nsamples - datasel.begsample) > current_max_num_sample) {
|
||||
fprintf(stderr, "dmarequest: err3\n");
|
||||
response->def->version = VERSION;
|
||||
response->def->command = GET_ERR;
|
||||
response->def->bufsize = 0;
|
||||
}
|
||||
else {
|
||||
unsigned int wordsize = wordsize_from_type(data->def->data_type);
|
||||
if (wordsize==0) {
|
||||
fprintf(stderr, "dmarequest: unsupported data type (%d)\n", data->def->data_type);
|
||||
response->def->version = VERSION;
|
||||
response->def->command = GET_ERR;
|
||||
response->def->bufsize = 0;
|
||||
} else {
|
||||
unsigned int n;
|
||||
response->def->version = VERSION;
|
||||
response->def->command = GET_OK;
|
||||
response->def->bufsize = 0;
|
||||
|
||||
/* determine the number of samples to return */
|
||||
n = datasel.endsample - datasel.begsample + 1;
|
||||
|
||||
response->buf = malloc(sizeof(datadef_t) + n*data->def->nchans*wordsize);
|
||||
if (response->buf == nullptr) {
|
||||
/* not enough space for copying data into response */
|
||||
fprintf(stderr, "dmarequest: out of memory\n");
|
||||
response->def->command = GET_ERR;
|
||||
}
|
||||
else {
|
||||
/* number of bytes per sample (all channels) */
|
||||
unsigned int chansize = data->def->nchans * wordsize;
|
||||
|
||||
/* convenience pointer to start of actual data in response */
|
||||
char *resp_data = ((char *) response->buf) + sizeof(datadef_t);
|
||||
|
||||
/* this is the location of begsample within the ringbuffer */
|
||||
unsigned int start_index = WRAP(datasel.begsample, current_max_num_sample);
|
||||
|
||||
/* have datadef point into the freshly allocated response buffer and directly
|
||||
fill in the information */
|
||||
datadef = (datadef_t *) response->buf;
|
||||
datadef->nchans = data->def->nchans;
|
||||
datadef->data_type = data->def->data_type;
|
||||
datadef->nsamples = n;
|
||||
datadef->bufsize = n*chansize;
|
||||
|
||||
response->def->bufsize = sizeof(datadef_t) + datadef->bufsize;
|
||||
|
||||
if (start_index + n <= current_max_num_sample) {
|
||||
/* we can copy everything in one go */
|
||||
memcpy(resp_data, (char*)(data->buf) + start_index*chansize, n*chansize);
|
||||
} else {
|
||||
/* need to wrap around at current_max_num_sample */
|
||||
unsigned int na = current_max_num_sample - start_index;
|
||||
unsigned int nb = n - na;
|
||||
|
||||
memcpy(resp_data, (char*)(data->buf) + start_index*chansize, na*chansize);
|
||||
memcpy(resp_data + na*chansize, (char*)(data->buf), nb*chansize);
|
||||
|
||||
/* std::cout << "Wrapped around!\n"; */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&mutexdata);
|
||||
pthread_mutex_unlock(&mutexheader);
|
||||
break;
|
||||
|
||||
case GET_EVT:
|
||||
if (verbose>1) fprintf(stderr, "dmarequest: GET_EVT\n");
|
||||
if (header == nullptr || event == nullptr || header->def->nevents==0) {
|
||||
response->def->version = VERSION;
|
||||
response->def->command = GET_ERR;
|
||||
response->def->bufsize = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&mutexheader);
|
||||
pthread_mutex_lock(&mutexevent);
|
||||
|
||||
eventsel = (eventsel_t*)malloc(sizeof(eventsel_t));
|
||||
DIE_BAD_MALLOC(eventsel);
|
||||
|
||||
/* determine the selection */
|
||||
if (request->def->bufsize) {
|
||||
/* the selection has been specified */
|
||||
memcpy(eventsel, request->buf, sizeof(eventsel_t));
|
||||
}
|
||||
else {
|
||||
/* determine a valid selection */
|
||||
if (header->def->nevents>MAXNUMEVENT) {
|
||||
/* the ringbuffer is completely full */
|
||||
eventsel->begevent = header->def->nevents - MAXNUMEVENT;
|
||||
eventsel->endevent = header->def->nevents - 1;
|
||||
}
|
||||
else {
|
||||
/* the ringbuffer is not yet completely full */
|
||||
eventsel->begevent = 0;
|
||||
eventsel->endevent = header->def->nevents - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (verbose>1) print_headerdef(header->def);
|
||||
if (verbose>1) print_eventsel(eventsel);
|
||||
|
||||
if (eventsel == nullptr) {
|
||||
response->def->version = VERSION;
|
||||
response->def->command = GET_ERR;
|
||||
response->def->bufsize = 0;
|
||||
}
|
||||
else if (eventsel->begevent < 0 || eventsel->endevent < 0) {
|
||||
fprintf(stderr, "dmarequest: err1\n");
|
||||
response->def->version = VERSION;
|
||||
response->def->command = GET_ERR;
|
||||
response->def->bufsize = 0;
|
||||
}
|
||||
else if (eventsel->begevent >= header->def->nevents || eventsel->endevent >= header->def->nevents) {
|
||||
fprintf(stderr, "dmarequest: err2\n");
|
||||
response->def->version = VERSION;
|
||||
response->def->command = GET_ERR;
|
||||
response->def->bufsize = 0;
|
||||
}
|
||||
else if ((header->def->nevents-eventsel->begevent) > MAXNUMEVENT) {
|
||||
fprintf(stderr, "dmarequest: err3\n");
|
||||
response->def->version = VERSION;
|
||||
response->def->command = GET_ERR;
|
||||
response->def->bufsize = 0;
|
||||
}
|
||||
else {
|
||||
unsigned int j,n;
|
||||
|
||||
response->def->version = VERSION;
|
||||
response->def->command = GET_OK;
|
||||
response->def->bufsize = 0;
|
||||
|
||||
/* determine the number of events to return */
|
||||
n = eventsel->endevent - eventsel->begevent + 1;
|
||||
|
||||
for (j=0; j<n; ++j) {
|
||||
if (verbose>1) print_eventdef(event[WRAP(eventsel->begevent+j, MAXNUMEVENT)].def);
|
||||
response->def->bufsize = append(&response->buf, response->def->bufsize, event[WRAP(eventsel->begevent+j, MAXNUMEVENT)].def, sizeof(eventdef_t));
|
||||
response->def->bufsize = append(&response->buf, response->def->bufsize, event[WRAP(eventsel->begevent+j, MAXNUMEVENT)].buf, event[WRAP(eventsel->begevent+j, MAXNUMEVENT)].def->bufsize);
|
||||
}
|
||||
}
|
||||
|
||||
FREE(eventsel);
|
||||
pthread_mutex_unlock(&mutexevent);
|
||||
pthread_mutex_unlock(&mutexheader);
|
||||
break;
|
||||
|
||||
case FLUSH_HDR:
|
||||
pthread_mutex_lock(&mutexheader);
|
||||
pthread_mutex_lock(&mutexdata);
|
||||
pthread_mutex_lock(&mutexevent);
|
||||
if (header) {
|
||||
free_header();
|
||||
free_data();
|
||||
free_event();
|
||||
response->def->version = VERSION;
|
||||
response->def->command = FLUSH_OK;
|
||||
response->def->bufsize = 0;
|
||||
}
|
||||
else {
|
||||
response->def->version = VERSION;
|
||||
response->def->command = FLUSH_ERR;
|
||||
response->def->bufsize = 0;
|
||||
}
|
||||
pthread_mutex_unlock(&mutexevent);
|
||||
pthread_mutex_unlock(&mutexdata);
|
||||
pthread_mutex_unlock(&mutexheader);
|
||||
break;
|
||||
|
||||
case FLUSH_DAT:
|
||||
pthread_mutex_lock(&mutexheader);
|
||||
pthread_mutex_lock(&mutexdata);
|
||||
if (header && data) {
|
||||
header->def->nsamples = thissample = 0;
|
||||
response->def->version = VERSION;
|
||||
response->def->command = FLUSH_OK;
|
||||
response->def->bufsize = 0;
|
||||
}
|
||||
else {
|
||||
response->def->version = VERSION;
|
||||
response->def->command = FLUSH_ERR;
|
||||
response->def->bufsize = 0;
|
||||
}
|
||||
pthread_mutex_unlock(&mutexdata);
|
||||
pthread_mutex_unlock(&mutexheader);
|
||||
break;
|
||||
|
||||
case FLUSH_EVT:
|
||||
pthread_mutex_lock(&mutexheader);
|
||||
pthread_mutex_lock(&mutexevent);
|
||||
if (header && event) {
|
||||
unsigned int i;
|
||||
|
||||
header->def->nevents = thisevent = 0;
|
||||
for (i=0; i<MAXNUMEVENT; ++i) {
|
||||
FREE(event[i].def);
|
||||
FREE(event[i].buf);
|
||||
}
|
||||
response->def->version = VERSION;
|
||||
response->def->command = FLUSH_OK;
|
||||
response->def->bufsize = 0;
|
||||
}
|
||||
else {
|
||||
response->def->version = VERSION;
|
||||
response->def->command = FLUSH_ERR;
|
||||
response->def->bufsize = 0;
|
||||
}
|
||||
pthread_mutex_unlock(&mutexevent);
|
||||
pthread_mutex_unlock(&mutexheader);
|
||||
break;
|
||||
|
||||
case WAIT_DAT:
|
||||
/* SK: This request means that the client wants to wait until
|
||||
MORE than waitdef_t.threshold.nsamples samples OR
|
||||
MORE THAN waitdef_t.threshold.nevents events
|
||||
are in the buffer, BUT
|
||||
only for the time given in waitdef_t.milliseconds.
|
||||
The response is just the number of samples and events
|
||||
in the buffer as described by samples_events_t.
|
||||
*/
|
||||
response->def->version = VERSION;
|
||||
if (header == nullptr || request->def->bufsize!=sizeof(waitdef_t)) {
|
||||
response->def->command = WAIT_ERR;
|
||||
response->def->bufsize = 0;
|
||||
} else {
|
||||
int waiterr;
|
||||
waitdef_t *wd = (waitdef_t *) request->buf;
|
||||
samples_events_t *nret = (samples_events_t*)malloc(sizeof(samples_events_t));
|
||||
UINT32_T nsmp, nevt;
|
||||
|
||||
if (nret == nullptr) {
|
||||
/* highly unlikely, but we cannot allocate a sample_event_t - return an error */
|
||||
response->def->command = WAIT_ERR;
|
||||
response->def->bufsize = 0;
|
||||
break;
|
||||
}
|
||||
/* Let response->buf point to the new sample_event_t structure */
|
||||
response->def->command = WAIT_OK;
|
||||
response->def->bufsize = sizeof(samples_events_t);
|
||||
response->buf = nret;
|
||||
|
||||
/* get current number of samples */
|
||||
pthread_mutex_lock(&mutexheader);
|
||||
nsmp = header->def->nsamples;
|
||||
nevt = header->def->nevents;
|
||||
pthread_mutex_unlock(&mutexheader);
|
||||
|
||||
if (wd->milliseconds == 0 || nsmp > wd->threshold.nsamples || nevt > wd->threshold.nevents) {
|
||||
/* the client doesn't want to wait, or
|
||||
we're already above the threshold:
|
||||
return immediately */
|
||||
nret->nsamples = nsmp;
|
||||
nret->nevents = nevt;
|
||||
break;
|
||||
}
|
||||
gettimeofday(&tp, nullptr);
|
||||
ts.tv_sec = tp.tv_sec + (wd->milliseconds/1000);
|
||||
ts.tv_nsec = 1000 * (tp.tv_usec + (wd->milliseconds % 1000)*1000);
|
||||
while (ts.tv_nsec >= 1000000000) {
|
||||
ts.tv_sec++;
|
||||
ts.tv_nsec-=1000000000;
|
||||
}
|
||||
|
||||
/* FIXME: The getData condition variable is only triggered by incoming data, not events */
|
||||
do {
|
||||
pthread_mutex_lock(&getData_mutex);
|
||||
waiterr = pthread_cond_timedwait(&getData_cond, &getData_mutex, &ts);
|
||||
pthread_mutex_unlock(&getData_mutex);
|
||||
|
||||
/* get current number of samples */
|
||||
pthread_mutex_lock(&mutexheader);
|
||||
nsmp = header->def->nsamples;
|
||||
nevt = header->def->nevents;
|
||||
pthread_mutex_unlock(&mutexheader);
|
||||
} while (nsmp <= wd->threshold.nsamples && nevt <= wd->threshold.nevents && waiterr==0);
|
||||
nret->nsamples = nsmp;
|
||||
nret->nevents = nevt;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "dmarequest: unknown command\n");
|
||||
}
|
||||
|
||||
if (verbose>0) fprintf(stderr, "dmarequest: thissample = %u, thisevent = %u\n", thissample, thisevent);
|
||||
|
||||
/* everything went fine */
|
||||
return 0;
|
||||
}
|
||||
|
||||
+240
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* Copyright (C) 2010, Stefan Klanke
|
||||
* F.C. Donders Centre for Cognitive Neuroimaging, Radboud University Nijmegen,
|
||||
* Kapittelweg 29, 6525 EN Nijmegen, The Netherlands
|
||||
*
|
||||
*/
|
||||
#include <buffer.h>
|
||||
|
||||
/* TODO: see if these can be optimized using compiler intrinsics etc. */
|
||||
void ft_swap16(unsigned int numel, void *data) {
|
||||
unsigned int n;
|
||||
char *d = (char *) data;
|
||||
for (n=0;n<numel;n++) {
|
||||
char t = d[0];
|
||||
d[0] = d[1];
|
||||
d[1] = t;
|
||||
d+=2;
|
||||
}
|
||||
}
|
||||
|
||||
void ft_swap32(unsigned int numel, void *data) {
|
||||
unsigned int n;
|
||||
char *d = (char *) data;
|
||||
for (n=0;n<numel;n++) {
|
||||
char t0 = d[0];
|
||||
char t1 = d[1];
|
||||
d[0] = d[3];
|
||||
d[1] = d[2];
|
||||
d[2] = t1;
|
||||
d[3] = t0;
|
||||
d+=4;
|
||||
}
|
||||
}
|
||||
|
||||
void ft_swap64(unsigned int numel, void *data) {
|
||||
unsigned int n;
|
||||
char *d = (char *) data;
|
||||
for (n=0;n<numel;n++) {
|
||||
char t0 = d[0];
|
||||
char t1 = d[1];
|
||||
char t2 = d[2];
|
||||
char t3 = d[3];
|
||||
d[0] = d[7];
|
||||
d[1] = d[6];
|
||||
d[2] = d[5];
|
||||
d[3] = d[4];
|
||||
d[4] = t3;
|
||||
d[5] = t2;
|
||||
d[6] = t1;
|
||||
d[7] = t0;
|
||||
d+=8;
|
||||
}
|
||||
}
|
||||
|
||||
void ft_swap_data(UINT32_T numel, UINT32_T datatype, void *data) {
|
||||
switch(datatype) {
|
||||
case DATATYPE_CHAR:
|
||||
case DATATYPE_UINT8:
|
||||
case DATATYPE_INT8:
|
||||
return;
|
||||
case DATATYPE_UINT16:
|
||||
case DATATYPE_INT16:
|
||||
ft_swap16(numel, data);
|
||||
return;
|
||||
case DATATYPE_UINT32:
|
||||
case DATATYPE_INT32:
|
||||
case DATATYPE_FLOAT32:
|
||||
ft_swap32(numel, data);
|
||||
return;
|
||||
case DATATYPE_UINT64:
|
||||
case DATATYPE_INT64:
|
||||
case DATATYPE_FLOAT64:
|
||||
ft_swap64(numel, data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int ft_swap_chunks_to_native(UINT32_T size, UINT32_T nchans, void *buf) {
|
||||
UINT32_T offset = 0;
|
||||
while (offset + sizeof(ft_chunkdef_t) <= size) {
|
||||
ft_chunk_t *chunk = (ft_chunk_t *) ((char *) buf + offset);
|
||||
|
||||
ft_swap32(2, &(chunk->def));
|
||||
|
||||
offset += sizeof(ft_chunkdef_t) + chunk->def.size;
|
||||
/* chunk definition fault (=too big) ? */
|
||||
if (offset > size) return -1;
|
||||
|
||||
switch(chunk->def.type) {
|
||||
case FT_CHUNK_RESOLUTIONS:
|
||||
if (chunk->def.size >= nchans*sizeof(FLOAT64_T)) {
|
||||
ft_swap64(nchans, chunk->data);
|
||||
}
|
||||
break;
|
||||
/* Add other cases here as needed */
|
||||
}
|
||||
|
||||
offset += sizeof(ft_chunkdef_t) + chunk->def.size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* returns 0 on success, -1 on error */
|
||||
int ft_swap_events_to_native(UINT32_T size, void *buf) {
|
||||
UINT32_T offset = 0;
|
||||
|
||||
while (offset + sizeof(eventdef_t) <= size) {
|
||||
unsigned int wst, wsv;
|
||||
|
||||
eventdef_t *edef = (eventdef_t *) ((char *) buf + offset);
|
||||
ft_swap32(8, edef); /* all fields are 32-bit */
|
||||
|
||||
/* Increase offset to beginning of next event */
|
||||
offset += sizeof(eventdef_t) + edef->bufsize;
|
||||
if (offset > size) return -1; /* this event is too big for "buf" */
|
||||
|
||||
wst = wordsize_from_type(edef->type_type);
|
||||
wsv = wordsize_from_type(edef->value_type);
|
||||
|
||||
/* check if type and value fit into this event's local buffer */
|
||||
if (wst*edef->type_numel + wsv*edef->value_numel > edef->bufsize) return -1;
|
||||
|
||||
ft_swap_data(edef->type_numel, edef->type_type, (char *) buf + offset);
|
||||
ft_swap_data(edef->value_numel, edef->value_type, (char *) buf + offset + wst*edef->type_numel);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* returns 0 on success, -1 on error */
|
||||
int ft_swap_buf_to_native(UINT16_T command, UINT32_T bufsize, void *buf) {
|
||||
datadef_t *ddef;
|
||||
|
||||
switch(command) {
|
||||
case GET_HDR:
|
||||
/* This should not have a buf attached */
|
||||
return 0;
|
||||
case GET_DAT:
|
||||
/* buf contains a datsel_t = 2x UINT32_T */
|
||||
if (bufsize == 8) ft_swap32(2, buf);
|
||||
return 0;
|
||||
case GET_EVT:
|
||||
/* buf contains a datsel_t = 2x UINT32_T */
|
||||
if (bufsize == 8) ft_swap32(2, buf);
|
||||
return 0;
|
||||
case WAIT_DAT:
|
||||
/* buf contains a waitdef_t = 3x UINT32_T */
|
||||
ft_swap32(3, buf);
|
||||
return 0;
|
||||
case PUT_DAT:
|
||||
/* buf contains a datadef_t and after that the data */
|
||||
ddef = (datadef_t *) buf;
|
||||
ft_swap32(4, ddef); /* this is for datadef_t */
|
||||
ft_swap_data(ddef->nchans*ddef->nsamples, ddef->data_type, ddef + 1); /* ddef+1 points to first data byte */
|
||||
return 0;
|
||||
case PUT_HDR:
|
||||
/* buf contains a headerdef_t and optionally chunks */
|
||||
ft_swap32(6, buf); /* all fields are 32-bit values */
|
||||
return ft_swap_chunks_to_native(bufsize - sizeof(headerdef_t), ((headerdef_t *) buf)->nchans, (char *) buf + sizeof(headerdef_t));
|
||||
case PUT_EVT:
|
||||
/* buf contains multiple eventdef_t and buf's */
|
||||
return ft_swap_events_to_native(bufsize, buf);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int ft_swap_chunks_from_native(UINT32_T size, UINT32_T nchans, void *buf) {
|
||||
UINT32_T offset = 0;
|
||||
while (offset + sizeof(ft_chunkdef_t) <= size) {
|
||||
ft_chunk_t *chunk = (ft_chunk_t *) ((char *) buf + offset);
|
||||
offset += sizeof(ft_chunkdef_t) + chunk->def.size;
|
||||
|
||||
/* chunk definition fault (=too big) ? */
|
||||
if (offset > size) return -1;
|
||||
|
||||
switch(chunk->def.type) {
|
||||
case FT_CHUNK_RESOLUTIONS:
|
||||
if (chunk->def.size >= nchans*sizeof(FLOAT64_T)) {
|
||||
ft_swap64(nchans, chunk->data);
|
||||
}
|
||||
break;
|
||||
/* Add other cases here as needed */
|
||||
}
|
||||
|
||||
ft_swap32(2, &(chunk->def));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ft_swap_events_from_native(UINT32_T size, void *buf) {
|
||||
UINT32_T offset = 0;
|
||||
|
||||
while (offset + sizeof(eventdef_t) <= size) {
|
||||
unsigned int wst;
|
||||
|
||||
eventdef_t *edef = (eventdef_t *) ((char *) buf + offset);
|
||||
offset += sizeof(eventdef_t) + edef->bufsize;
|
||||
|
||||
wst = wordsize_from_type(edef->type_type);
|
||||
|
||||
ft_swap_data(edef->type_numel, edef->type_type, (char *) buf + offset);
|
||||
ft_swap_data(edef->value_numel, edef->value_type, (char *) buf + offset + wst*edef->type_numel);
|
||||
ft_swap32(8, edef); /* all fields are 32-bit */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int ft_swap_from_native(UINT16_T orgCommand, message_t *msg) {
|
||||
datadef_t *ddef;
|
||||
UINT32_T nchans;
|
||||
|
||||
UINT32_T bufsize = msg->def->bufsize;
|
||||
|
||||
ft_swap16(1, &msg->def->version);
|
||||
ft_swap16(1, &msg->def->command);
|
||||
ft_swap32(1, &msg->def->bufsize);
|
||||
|
||||
if (bufsize == 0) return 0;
|
||||
|
||||
switch(orgCommand) {
|
||||
case GET_HDR:
|
||||
nchans = ((headerdef_t *) msg->buf)->nchans;
|
||||
ft_swap32(6, msg->buf); /* all fields are 32-bit values */
|
||||
return ft_swap_chunks_from_native(bufsize - sizeof(headerdef_t), nchans, (char *) msg->buf + sizeof(headerdef_t));
|
||||
case GET_DAT:
|
||||
ddef = (datadef_t *) msg->buf;
|
||||
ft_swap_data(ddef->nchans*ddef->nsamples, ddef->data_type, ddef + 1); /* ddef+1 points to first data byte */
|
||||
ft_swap32(5, ddef); /* all fields are 32-bit */
|
||||
return 0;
|
||||
case GET_EVT:
|
||||
return ft_swap_events_from_native(bufsize, msg->buf);
|
||||
case WAIT_DAT:
|
||||
ft_swap32(2, msg->buf); /* nsamples + nevents = 32bit */
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2008, Robert Oostenveld
|
||||
* F.C. Donders Centre for Cognitive Neuroimaging, Radboud University Nijmegen,
|
||||
* Kapittelweg 29, 6525 EN Nijmegen, The Netherlands
|
||||
*
|
||||
*/
|
||||
|
||||
#include "buffer.h"
|
||||
#include <pthread.h>
|
||||
|
||||
pthread_mutex_t mutexstatus = PTHREAD_MUTEX_INITIALIZER;
|
||||
int tcpserverStatus = 0;
|
||||
|
||||
pthread_mutex_t mutexthreadcount = PTHREAD_MUTEX_INITIALIZER;
|
||||
int threadcount = 0;
|
||||
|
||||
pthread_mutex_t mutexsocketcount = PTHREAD_MUTEX_INITIALIZER;
|
||||
int socketcount = 0;
|
||||
|
||||
pthread_mutex_t mutexappendcount = PTHREAD_MUTEX_INITIALIZER;
|
||||
int appendcount = 0;
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
#ifndef EXTERN_H
|
||||
#define EXTERN_H
|
||||
|
||||
#include "buffer.h"
|
||||
|
||||
extern pthread_mutex_t mutexstatus;
|
||||
extern int tcpserverStatus;
|
||||
|
||||
extern pthread_mutex_t mutexthreadcount;
|
||||
extern int threadcount;
|
||||
|
||||
extern pthread_mutex_t mutexsocketcount;
|
||||
extern int socketcount;
|
||||
|
||||
extern pthread_mutex_t mutexthreadcount;
|
||||
extern int threadcount;
|
||||
|
||||
extern pthread_mutex_t mutexappendcount;
|
||||
extern int appendcount;
|
||||
|
||||
#endif
|
||||
+266
@@ -0,0 +1,266 @@
|
||||
/*
|
||||
* Copyright (C) 2008, Robert Oostenveld & Christian Hesse
|
||||
* F.C. Donders Centre for Cognitive Neuroimaging, Radboud University Nijmegen,
|
||||
* Kapittelweg 29, 6525 EN Nijmegen, The Netherlands
|
||||
*
|
||||
*/
|
||||
|
||||
/* prevent double include */
|
||||
#ifndef MESSAGE_H
|
||||
#define MESSAGE_H
|
||||
|
||||
#include "platform_includes.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
||||
#endif
|
||||
|
||||
/* FIXME these are obvious at the moment, but should be formally defined */
|
||||
typedef char CHAR_T;
|
||||
typedef float FLOAT32_T;
|
||||
typedef double FLOAT64_T;
|
||||
|
||||
/* the following types should be according to "ISO C99: 7.18 Integer types" (see /usr/include/stdint.h on OSX and Linux) */
|
||||
/* FIXME different endianness between client/server is not supported at the moment */
|
||||
|
||||
#ifndef INT8_T
|
||||
typedef int8_t INT8_T;
|
||||
#endif
|
||||
|
||||
#ifndef INT16_T
|
||||
typedef int16_t INT16_T;
|
||||
#endif
|
||||
|
||||
#ifndef INT32_T
|
||||
typedef int INT32_T;
|
||||
#endif
|
||||
|
||||
#ifndef INT64_T
|
||||
typedef int64_t INT64_T;
|
||||
#endif
|
||||
|
||||
#ifndef UINT8_T
|
||||
typedef uint8_t UINT8_T;
|
||||
#endif
|
||||
|
||||
#ifndef UINT16_T
|
||||
typedef uint16_t UINT16_T;
|
||||
#endif
|
||||
|
||||
#ifndef UINT32_T
|
||||
typedef uint32_t UINT32_T;
|
||||
#endif
|
||||
|
||||
#ifndef UINT64_T
|
||||
typedef uint64_t UINT64_T;
|
||||
#endif
|
||||
|
||||
/* these can be used for indexing the buffer pointer as array */
|
||||
#define WORDSIZE_CHAR sizeof(CHAR_T )
|
||||
#define WORDSIZE_UINT8 sizeof(UINT8_T )
|
||||
#define WORDSIZE_UINT16 sizeof(UINT16_T )
|
||||
#define WORDSIZE_UINT32 sizeof(UINT32_T )
|
||||
#define WORDSIZE_UINT64 sizeof(UINT64_T )
|
||||
#define WORDSIZE_INT8 sizeof(INT8_T )
|
||||
#define WORDSIZE_INT16 sizeof(INT16_T )
|
||||
#define WORDSIZE_INT32 sizeof(INT32_T )
|
||||
#define WORDSIZE_INT64 sizeof(INT64_T )
|
||||
#define WORDSIZE_FLOAT32 sizeof(FLOAT32_T)
|
||||
#define WORDSIZE_FLOAT64 sizeof(FLOAT64_T)
|
||||
|
||||
/* define the version of the message packet */
|
||||
#define VERSION (UINT16_T)0x0001
|
||||
|
||||
/* the same version number in the "other" endianness */
|
||||
#define VERSION_OE (UINT16_T) (((VERSION & 0x00FF) << 8) | ((VERSION & 0xFF00) >> 8))
|
||||
|
||||
/* these define the commands that can be used, which are split over the two available bytes */
|
||||
#define PUT_HDR (UINT16_T)0x0101
|
||||
#define PUT_DAT (UINT16_T)0x0102
|
||||
#define PUT_EVT (UINT16_T)0x0103
|
||||
#define PUT_OK (UINT16_T)0x0104
|
||||
#define PUT_ERR (UINT16_T)0x0105
|
||||
|
||||
#define GET_HDR (UINT16_T)0x0201
|
||||
#define GET_DAT (UINT16_T)0x0202
|
||||
#define GET_EVT (UINT16_T)0x0203
|
||||
#define GET_OK (UINT16_T)0x0204
|
||||
#define GET_ERR (UINT16_T)0x0205
|
||||
|
||||
#define FLUSH_HDR (UINT16_T)0x0301
|
||||
#define FLUSH_DAT (UINT16_T)0x0302
|
||||
#define FLUSH_EVT (UINT16_T)0x0303
|
||||
#define FLUSH_OK (UINT16_T)0x0304
|
||||
#define FLUSH_ERR (UINT16_T)0x0305
|
||||
|
||||
#define WAIT_DAT (UINT16_T)0x0402
|
||||
#define WAIT_OK (UINT16_T)0x0404
|
||||
#define WAIT_ERR (UINT16_T)0x0405
|
||||
|
||||
/* these are used in the data_t and event_t structure */
|
||||
#define DATATYPE_CHAR (UINT32_T)0
|
||||
#define DATATYPE_UINT8 (UINT32_T)1
|
||||
#define DATATYPE_UINT16 (UINT32_T)2
|
||||
#define DATATYPE_UINT32 (UINT32_T)3
|
||||
#define DATATYPE_UINT64 (UINT32_T)4
|
||||
#define DATATYPE_INT8 (UINT32_T)5
|
||||
#define DATATYPE_INT16 (UINT32_T)6
|
||||
#define DATATYPE_INT32 (UINT32_T)7
|
||||
#define DATATYPE_INT64 (UINT32_T)8
|
||||
#define DATATYPE_FLOAT32 (UINT32_T)9
|
||||
#define DATATYPE_FLOAT64 (UINT32_T)10
|
||||
/* this should never be used to put data into the buffer,
|
||||
but is handy for handling conversions of other data types
|
||||
*/
|
||||
#define DATATYPE_UNKNOWN (UINT32_T)0xFFFFFFFF
|
||||
|
||||
/* these are used in the specification of the event selection criteria */
|
||||
#define EVENTSEL_TYPE 1
|
||||
#define EVENTSEL_VALUE 2
|
||||
#define EVENTSEL_SAMPLE 3 /* for an exact match */
|
||||
#define EVENTSEL_MINSAMPLE 4
|
||||
#define EVENTSEL_MAXSAMPLE 5
|
||||
|
||||
|
||||
/* if event->def->sample == EVENT_AUTO_SAMPLE, automatically insert
|
||||
current sample index instead
|
||||
*/
|
||||
#define EVENT_AUTO_SAMPLE -1
|
||||
|
||||
/** The following enumeration is for specifying types of chunks that may be present
|
||||
in the "buf" part of the Fieldtrip header.
|
||||
*/
|
||||
enum {
|
||||
/** FT_CHUNK_UNSPECIFIED refers to a binary blob of known length, but unknown contents.
|
||||
Clients encountering this can try to use auto-detection, or just ignore this chunk.
|
||||
Unknown chunk types should be treated in the same manner. */
|
||||
FT_CHUNK_UNSPECIFIED = 0,
|
||||
|
||||
/** FT_CHUNK_CHANNEL_NAMES contains the channel names in ASCII format. Each channel is
|
||||
represented as a 0-terminated string (includes the case of just a 0 for an empty string).
|
||||
Example: chunk_data = "Left\0Right\0" for stereo sound signals. */
|
||||
FT_CHUNK_CHANNEL_NAMES = 1,
|
||||
|
||||
/** FT_CHUNK_CHANNEL_FLAGS contains a 0-terminated string describing the type of flags,
|
||||
and after that N (=#channels) bytes describing each channel. This is useful for
|
||||
specifying that a channel can have a discrete number of different types, e.g.
|
||||
chunk_data = "meg_ad_eog\0\1\1\1\1\3\3\2\2" should be used for a system with 8 channels,
|
||||
the first four of which are for MEG, then 2 channels EOG, then 2 channels A/D. */
|
||||
FT_CHUNK_CHANNEL_FLAGS = 2,
|
||||
|
||||
/** FT_CHUNK_RESOLUTIONS contains N double precision values mapping from A/D values to physical
|
||||
quantities such as micro-Volts in EEG. */
|
||||
FT_CHUNK_RESOLUTIONS = 3,
|
||||
|
||||
/** FT_CHUNK_ASCII_KEYVAL contains an arbitrary number of key/value pairs, each of
|
||||
which is given as a 0-terminated string. An empty key (=double 0) indicates the
|
||||
end of the list. Example: "amplifier_gain\0high\0noise_reduction\0active\0\0". */
|
||||
FT_CHUNK_ASCII_KEYVAL = 4,
|
||||
|
||||
/** FT_CHUNK_NIFTI1 contains a NIFTI-1 header (348 bytes long) */
|
||||
FT_CHUNK_NIFTI1 = 5,
|
||||
|
||||
/** FT_CHUNK_SIEMENS_AP contains Siemens Protocol data in ASCII format (string) */
|
||||
FT_CHUNK_SIEMENS_AP = 6,
|
||||
|
||||
/** FT_CHUNK_CTF_RES4 contains a .res4 file as written by the CTF MEG acquisition software (binary) */
|
||||
FT_CHUNK_CTF_RES4 = 7
|
||||
};
|
||||
|
||||
#pragma pack(push,1)
|
||||
|
||||
/* a packet that is sent over the network (or to disk) should contain the following */
|
||||
typedef struct {
|
||||
UINT16_T version; /* see VERSION */
|
||||
UINT16_T command; /* see PUT_xxx, GET_xxx and FLUSH_xxx */
|
||||
UINT32_T bufsize; /* size of the buffer in bytes */
|
||||
} messagedef_t;
|
||||
|
||||
/* the header definition is fixed, except for the channel labels */
|
||||
typedef struct {
|
||||
UINT32_T nchans;
|
||||
UINT32_T nsamples;
|
||||
UINT32_T nevents;
|
||||
FLOAT32_T fsample;
|
||||
UINT32_T data_type;
|
||||
UINT32_T bufsize; /* size of the buffer in bytes */
|
||||
} headerdef_t;
|
||||
|
||||
/* the data definition is fixed */
|
||||
typedef struct {
|
||||
UINT32_T nchans;
|
||||
UINT32_T nsamples;
|
||||
UINT32_T data_type;
|
||||
UINT32_T bufsize; /* size of the buffer in bytes */
|
||||
} datadef_t;
|
||||
|
||||
/* the event definition is fixed */
|
||||
typedef struct {
|
||||
UINT32_T type_type; /* usual would be DATATYPE_CHAR */
|
||||
UINT32_T type_numel; /* length of the type string */
|
||||
UINT32_T value_type;
|
||||
UINT32_T value_numel;
|
||||
INT32_T sample;
|
||||
INT32_T offset;
|
||||
INT32_T duration;
|
||||
UINT32_T bufsize; /* size of the buffer in bytes */
|
||||
} eventdef_t;
|
||||
|
||||
typedef struct {
|
||||
messagedef_t *def;
|
||||
void *buf;
|
||||
} message_t;
|
||||
|
||||
typedef struct {
|
||||
headerdef_t *def;
|
||||
void *buf; /* FIXME this should contain the channel names */
|
||||
} header_t;
|
||||
|
||||
typedef struct {
|
||||
datadef_t *def;
|
||||
void *buf;
|
||||
} data_t;
|
||||
|
||||
typedef struct {
|
||||
eventdef_t *def;
|
||||
void *buf;
|
||||
} event_t;
|
||||
|
||||
typedef struct {
|
||||
UINT32_T begsample; /* indexing starts with 0, should be >=0 */
|
||||
UINT32_T endsample; /* indexing starts with 0, should be <header.nsamples */
|
||||
} datasel_t;
|
||||
|
||||
typedef struct {
|
||||
UINT32_T begevent;
|
||||
UINT32_T endevent;
|
||||
} eventsel_t;
|
||||
|
||||
typedef struct {
|
||||
UINT32_T nsamples;
|
||||
UINT32_T nevents;
|
||||
} samples_events_t;
|
||||
|
||||
typedef struct {
|
||||
samples_events_t threshold;
|
||||
UINT32_T milliseconds;
|
||||
} waitdef_t;
|
||||
|
||||
typedef struct {
|
||||
UINT32_T type; /* One of FT_CHUNK_** (see above) */
|
||||
UINT32_T size; /* Size of chunk.data, total size is given by adding sizeof(ft_chunkdef_t)=8 */
|
||||
} ft_chunkdef_t;
|
||||
|
||||
typedef struct {
|
||||
ft_chunkdef_t def; /* See above. Note that this is not a pointer! */
|
||||
char data[1]; /* Data contained in this chunk */
|
||||
} ft_chunk_t;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
|
||||
/* prevent double include */
|
||||
#pragma once
|
||||
|
||||
#if defined(linux) || defined(__linux) || defined(__linux__) || defined(__GNU__) || defined(__GLIBC__)
|
||||
/* linux, also other platforms (Hurd etc) that use GLIBC */
|
||||
#define PLATFORM_LINUX
|
||||
|
||||
#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
|
||||
/* BSD */
|
||||
#define PLATFORM_BSD
|
||||
|
||||
#elif defined(sun) || defined(__sun)
|
||||
/* Solaris */
|
||||
#define PLATFORM_SUN
|
||||
|
||||
#elif defined(__sgi)
|
||||
/* SGI Irix */
|
||||
#define PLATFORM_SGI
|
||||
|
||||
#elif defined(__hpux)
|
||||
/* hp unix */
|
||||
#define PLATFORM_HP
|
||||
|
||||
#elif defined(__CYGWIN__)
|
||||
/* cygwin is not win32 */
|
||||
#define PLATFORM_CYGWIN
|
||||
|
||||
#elif defined(_WIN64) || defined(__WIN64__) || defined(WIN64)
|
||||
/* win64 */
|
||||
#define PLATFORM_WIN64
|
||||
#define PLATFORM_WINDOWS
|
||||
|
||||
#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
|
||||
/* win32 */
|
||||
#define PLATFORM_WIN32
|
||||
#define PLATFORM_WINDOWS
|
||||
|
||||
#elif defined(__BEOS__)
|
||||
/* BeOS */
|
||||
#define PLATFORM_BEOS
|
||||
|
||||
#elif defined (__APPLE__) && defined (__MACH__)
|
||||
/* MacOSX */
|
||||
#define PLATFORM_OSX
|
||||
|
||||
#elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__)
|
||||
/* MacOS classic */
|
||||
#define PLATFORM_MAC
|
||||
|
||||
#elif defined(__IBMCPP__) || defined(_AIX)
|
||||
/* IBM */
|
||||
#define PLATFORM_AIX
|
||||
|
||||
#elif defined(__amigaos__)
|
||||
/* AmigaOS */
|
||||
#define PLATFORM_AMIGA
|
||||
|
||||
#elif defined(__QNXNTO__)
|
||||
/* QNX */
|
||||
#define PLATFORM_QNX
|
||||
|
||||
#elif defined(__VXWORKS__)
|
||||
/* vxWorks */
|
||||
#define PLATFORM_VXWORKS
|
||||
|
||||
#elif defined(unix) || defined(__unix) || defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE)
|
||||
/* generic unix platform */
|
||||
#define PLATFORM_UNIX
|
||||
|
||||
#else
|
||||
/* the platform cannot be determined at compile time */
|
||||
#error "Unknown platform - please report the platform details to http://fieldtrip.fcdonders.nl"
|
||||
|
||||
#endif
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
#include "platform.h"
|
||||
#include "compiler.h"
|
||||
|
||||
#if defined (COMPILER_BORLAND)
|
||||
|
||||
#endif
|
||||
|
||||
/* these platforms will always use a similar gcc compiler */
|
||||
#if defined (PLATFORM_LINUX) || defined (PLATFORM_OSX)
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include <strings.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define closesocket(s) (close(s))
|
||||
|
||||
#elif defined (PLATFORM_WIN64)
|
||||
|
||||
#if defined (COMPILER_MSVC)
|
||||
#include <winsock2.h> /* for timeval */
|
||||
#include "win32/gettimeofday.h"
|
||||
#include "win32/stdint.h"
|
||||
|
||||
#define bzero(b,len) (memset((b), '\0', (len)), (void) 0)
|
||||
#define usleep(x) (Sleep((x)/1000))
|
||||
#define strcasecmp(a,b) (strcmpi(a,b))
|
||||
#elif defined (COMPILER_MINGW)
|
||||
#include <winsock2.h>
|
||||
#include <sys/time.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define bzero(b,len) memset(b,0,len)
|
||||
#define usleep(x) (Sleep((x)/1000))
|
||||
|
||||
#ifndef strcasecmp
|
||||
#define strcasecmp(a,b) (strcmpi(a,b))
|
||||
#endif
|
||||
#else
|
||||
#error "Unsupported compiler"
|
||||
#endif
|
||||
|
||||
#elif defined (PLATFORM_WIN32)
|
||||
/* there are various compiler options for windows */
|
||||
#if defined (COMPILER_BORLAND)
|
||||
#include <windows.h>
|
||||
#include "win32/gettimeofday.h"
|
||||
|
||||
#define bzero(b,len) (memset((b), '\0', (len)), (void) 0)
|
||||
#define usleep(x) (Sleep((x)/1000))
|
||||
#define strcasecmp(a,b) (strcmpi(a,b))
|
||||
/* without the following, compilation with the Borland command line tools fails -- SK */
|
||||
typedef __int8 int8_t;
|
||||
typedef __int16 int16_t;
|
||||
typedef __int32 int;
|
||||
typedef __int64 int64_t;
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
|
||||
#elif defined (COMPILER_MSVC)
|
||||
#include <winsock2.h> /* for timeval */
|
||||
#if (_MSC_VER >= 1600 )
|
||||
#include <stdint.h>
|
||||
#else
|
||||
typedef __int8 int8_t;
|
||||
typedef __int16 int16_t;
|
||||
typedef __int32 int;
|
||||
typedef __int64 int64_t;
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
#endif
|
||||
|
||||
|
||||
#include "win32/gettimeofday.h"
|
||||
|
||||
#define bzero(b,len) (memset((b), '\0', (len)), (void) 0)
|
||||
#define usleep(x) (Sleep((x)/1000))
|
||||
#define strcasecmp(a,b) (strcmpi(a,b))
|
||||
|
||||
#elif defined (COMPILER_MINGW)
|
||||
#include <winsock2.h>
|
||||
#include <sys/time.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define bzero(b,len) (memset((b), '\0', (len)), (void) 0)
|
||||
#define usleep(x) (Sleep((x)/1000))
|
||||
#define strcasecmp(a,b) (strcmpi(a,b))
|
||||
|
||||
#elif defined (COMPILER_CYGWIN)
|
||||
#include <winsock2.h>
|
||||
|
||||
#define strcasecmp(a,b) (strcmpi(a,b))
|
||||
#elif defined (COMPILER_LCC)
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include "win32/gettimeofday.h"
|
||||
|
||||
#define strcasecmp(a,b) (strcmpi(a,b))
|
||||
|
||||
#define bzero(b,len) (memset((b), '\0', (len)), (void) 0)
|
||||
#define usleep(x) (Sleep((x)/1000))
|
||||
|
||||
#ifndef UINT8_T
|
||||
#define UINT8_T unsigned char
|
||||
#endif
|
||||
|
||||
#ifndef INT8_T
|
||||
#define INT8_T char
|
||||
#endif
|
||||
|
||||
#ifndef UINT16_T
|
||||
#define UINT16_T unsigned short
|
||||
#endif
|
||||
|
||||
#ifndef INT16_T
|
||||
#define INT16_T short
|
||||
#endif
|
||||
|
||||
#ifndef UINT32_T
|
||||
#define UINT32_T unsigned int
|
||||
#endif
|
||||
|
||||
#ifndef INT32_T
|
||||
#define INT32_T int
|
||||
#endif
|
||||
|
||||
#ifndef UINT64_T
|
||||
#define UINT64_T unsigned long long
|
||||
#endif
|
||||
|
||||
#ifndef INT64_T
|
||||
#define INT64_T long long
|
||||
#endif
|
||||
|
||||
/* #define PTW32_STATIC_LIB
|
||||
#define __cdecl
|
||||
#define PTW_CDECL
|
||||
*/
|
||||
#endif /* compiler */
|
||||
|
||||
#endif /* platform */
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2008, Robert Oostenveld & Christian Hesse
|
||||
* F.C. Donders Centre for Cognitive Neuroimaging, Radboud University Nijmegen,
|
||||
* Kapittelweg 29, 6525 EN Nijmegen, The Netherlands
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "buffer.h"
|
||||
|
||||
void print_request(messagedef_t *request) {
|
||||
fprintf(stderr, "request.version = 0x%04x\n", request->version);
|
||||
fprintf(stderr, "request.command = 0x%04x\n", request->command);
|
||||
fprintf(stderr, "request.bufsize = %u\n", request->bufsize);
|
||||
}
|
||||
|
||||
void print_response(messagedef_t *response) {
|
||||
fprintf(stderr, "response.version = 0x%04x\n", response->version);
|
||||
fprintf(stderr, "response.command = 0x%04x\n", response->command);
|
||||
fprintf(stderr, "response.bufsize = %u\n", response->bufsize);
|
||||
}
|
||||
|
||||
void print_headerdef(headerdef_t *headerdef) {
|
||||
if (headerdef == nullptr)
|
||||
fprintf(stderr, "headerdef == nullptr\n");
|
||||
else {
|
||||
fprintf(stderr, "headerdef.nchans = %u\n", headerdef->nchans);
|
||||
fprintf(stderr, "headerdef.nsamples = %u\n", headerdef->nsamples);
|
||||
fprintf(stderr, "headerdef.nevents = %u\n", headerdef->nevents);
|
||||
fprintf(stderr, "headerdef.fsample = %f\n", headerdef->fsample);
|
||||
fprintf(stderr, "headerdef.data_type = %u\n", headerdef->data_type);
|
||||
fprintf(stderr, "headerdef.bufsize = %u\n", headerdef->bufsize);
|
||||
}
|
||||
}
|
||||
|
||||
void print_datadef(datadef_t *datadef) {
|
||||
if (datadef == nullptr)
|
||||
fprintf(stderr, "datadef == nullptr\n");
|
||||
else {
|
||||
fprintf(stderr, "datadef.nchans = %u\n", datadef->nchans);
|
||||
fprintf(stderr, "datadef.nsamples = %u\n", datadef->nsamples);
|
||||
fprintf(stderr, "datadef.data_type = %u\n", datadef->data_type);
|
||||
fprintf(stderr, "datadef.bufsize = %u\n", datadef->bufsize);
|
||||
}
|
||||
}
|
||||
|
||||
void print_eventdef(eventdef_t *eventdef) {
|
||||
if (eventdef == nullptr)
|
||||
fprintf(stderr, "eventdef == nullptr\n");
|
||||
else {
|
||||
fprintf(stderr, "eventdef.type_type = %u\n", eventdef->type_type );
|
||||
fprintf(stderr, "eventdef.type_numel = %u\n", eventdef->type_numel );
|
||||
fprintf(stderr, "eventdef.value_type = %u\n", eventdef->value_type );
|
||||
fprintf(stderr, "eventdef.value_numel = %u\n", eventdef->value_numel );
|
||||
fprintf(stderr, "eventdef.sample = %d\n", eventdef->sample );
|
||||
fprintf(stderr, "eventdef.offset = %d\n", eventdef->offset );
|
||||
fprintf(stderr, "eventdef.duration = %d\n", eventdef->duration );
|
||||
fprintf(stderr, "eventdef.bufsize = %u\n", eventdef->bufsize );
|
||||
}
|
||||
}
|
||||
|
||||
void print_datasel(datasel_t *datasel) {
|
||||
fprintf(stderr, "datasel.begsample = %u\n", datasel->begsample);
|
||||
fprintf(stderr, "datasel.endsample = %u\n", datasel->endsample);
|
||||
}
|
||||
|
||||
void print_eventsel(eventsel_t *eventsel) {
|
||||
fprintf(stderr, "eventsel.begevent = %u\n", eventsel->begevent);
|
||||
fprintf(stderr, "eventsel.endevent = %u\n", eventsel->endevent);
|
||||
}
|
||||
|
||||
void print_buf(void *buf, int bufsize) {
|
||||
int i;
|
||||
fprintf(stderr, "buf =");
|
||||
if (buf == nullptr)
|
||||
fprintf(stderr, " NULL");
|
||||
else
|
||||
for (i=0; i<bufsize; ++i)
|
||||
fprintf(stderr, " %02x", ((unsigned char*)buf)[i]);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2010 S. Klanke
|
||||
* F.C. Donders Centre for Cognitive Neuroimaging, Radboud University Nijmegen,
|
||||
* Kapittelweg 29, 6525 EN Nijmegen, The Netherlands
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __rdadefs_h
|
||||
#define __rdadefs_h
|
||||
|
||||
#include "message.h" /* for the integer datytypes */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Message types as sent to RDA clients */
|
||||
#define RDA_START_MSG 1
|
||||
#define RDA_INT_MSG 2
|
||||
#define RDA_STOP_MSG 3
|
||||
#define RDA_FLOAT_MSG 4
|
||||
|
||||
#pragma pack(push,1)
|
||||
|
||||
/** Structure of the first 24 bytes of all RDA messages */
|
||||
typedef struct {
|
||||
UINT8_T guid[16];
|
||||
UINT32_T nSize; /* Size of the message block in bytes including this header */
|
||||
UINT32_T nType; /* 1:start 2:int16_t 3:stop 4:float */
|
||||
} rda_msg_hdr_t;
|
||||
|
||||
/** Describes the structure of RDA data messages (fixed part only) */
|
||||
typedef struct {
|
||||
rda_msg_hdr_t hdr;
|
||||
UINT32_T nBlock; /* Block number, i.e. acquired blocks since acquisition started. */
|
||||
UINT32_T nPoints; /* Number of data points (samples) in this block */
|
||||
UINT32_T nMarkers; /* Number of markers in this block */
|
||||
/* after this, you get the data, and then an array of markers */
|
||||
} rda_msg_data_t;
|
||||
|
||||
/** Describes the structure of an RDA start message (fixed part only) */
|
||||
typedef struct {
|
||||
rda_msg_hdr_t hdr;
|
||||
UINT32_T nChannels; /* Number of channels */
|
||||
double dSamplingInterval; /* Sampling interval in microseconds */
|
||||
/* after this, you have double dResolutions[] and
|
||||
channels names come after this as 0-terminated strings */
|
||||
} rda_msg_start_t;
|
||||
|
||||
/* TODO: we never send a STOP packet, because it's hard to tell when the buffer will
|
||||
stop receiving data. We should maybe try to detect if a new header is put into the
|
||||
buffer, and send a STOP and a START in that case.
|
||||
*/
|
||||
|
||||
/** Describes the structure of an RDA marker (fixed part only) */
|
||||
typedef struct {
|
||||
UINT32_T nSize; /* Size of this marker */
|
||||
UINT32_T nPosition; /* Relative position in the data block */
|
||||
UINT32_T nPoints; /* Number of points of this marker */
|
||||
INT32_T nChannel; /* Associated channel number (-1 = all channels) */
|
||||
/* char sTypeDesc[1]; Type description in ASCII delimited by '\0', variable length actually */
|
||||
} rda_marker_t;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __rdadefs_h */
|
||||
+1111
File diff suppressed because it is too large
Load Diff
+119
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (C) 2010 S. Klanke
|
||||
* F.C. Donders Centre for Cognitive Neuroimaging, Radboud University Nijmegen,
|
||||
* Kapittelweg 29, 6525 EN Nijmegen, The Netherlands
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __rdaserver_h
|
||||
#define __rdaserver_h
|
||||
|
||||
#include <pthread.h>
|
||||
#include "buffer.h"
|
||||
#include "rdadefs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* On Windows, sockets are not described by a plain int, but by the type SOCKET, which has
|
||||
the size of a pointer. On WIN64, sizeof(SOCKET) != sizeof(int), although some people still
|
||||
argue that it is safe to cast between those. To be sure, the RDA server implementation
|
||||
always uses SOCKET as the base type, and defines this as an 'int' on POSIX systems.
|
||||
*/
|
||||
#ifndef WIN32
|
||||
typedef int SOCKET;
|
||||
#define INVALID_SOCKET -1
|
||||
#endif
|
||||
|
||||
/** Error values as returned by rda_start_server */
|
||||
#define FT_NO_ERROR 0
|
||||
#define FT_ERR_OUT_OF_MEM 1
|
||||
#define FT_ERR_SOCKET 2
|
||||
#define FT_ERR_THREADING 3
|
||||
|
||||
/** 'select' cannot handle more than 64 elements on Windows, but this
|
||||
should really be enough for all practical purposes. Depending on
|
||||
the sampling rate and number of channels, you would probably hit
|
||||
other performance boundaries first. Since the server socket itself
|
||||
also needs listening to (taking 1 away from the available 64),
|
||||
NEVER set the following number to more than 63!!!
|
||||
*/
|
||||
#define RDA_MAX_NUM_CLIENTS 32
|
||||
|
||||
/** Number of blocks any client can lag behind before being disconnected */
|
||||
#define RDA_MAX_LAG 5
|
||||
|
||||
/** RDA server control structure for starting, inspecting, and stopping a server */
|
||||
typedef struct {
|
||||
pthread_t thread; ///< Thread handle
|
||||
pthread_mutex_t mutex; ///< Mutex for protecting num_clients (actually not really necessary)
|
||||
SOCKET server_socket; ///< The server socket that clients connect to
|
||||
int ft_buffer; ///< Connection to FieldTrip buffer (socket or 0 for dmarequests)
|
||||
volatile int num_clients; ///< Current number of clients
|
||||
volatile int should_exit; ///< Flag to notify the server thread that it should stop
|
||||
volatile int is_running; ///< Flag that indicates whether the thread is still running
|
||||
int blocksize; ///< Block size for streaming out samples, 0 => adapt to incoming data
|
||||
int use16bit; ///< Flag that indicates whether 16 bit data should be streamed
|
||||
int verbosity; ///< Option that determines how much status information is printed during operation
|
||||
} rda_server_ctrl_t;
|
||||
|
||||
/** Internally used data structure to keep a linked list of
|
||||
data packets that need to be sent out */
|
||||
typedef struct rda_buffer_item {
|
||||
void *data; ///< Points to complete RDA packet
|
||||
size_t size; ///< Size of the packet (=allocated memory block)
|
||||
int blockNumber; ///< Number of this data block (or -1 for start packet)
|
||||
unsigned int refCount; ///< Reference count (multiple clients get the same data)
|
||||
struct rda_buffer_item *next; ///< Next item in list or NULL
|
||||
} rda_buffer_item_t;
|
||||
|
||||
/** Internally used data structure to describe a client and its pending jobs */
|
||||
typedef struct {
|
||||
SOCKET sock; ///< Client socket
|
||||
rda_buffer_item_t *item; ///< Points to the current/next packet to be written
|
||||
size_t written; ///< Number of bytes that have been written (from item->data)
|
||||
} rda_client_job_t;
|
||||
|
||||
/** Helper function for converting any FieldTrip data type to single precision floats
|
||||
@param N number of values to convert
|
||||
@param dest destination, must point to an array of at least N floats
|
||||
@param data_type data type as described by FieldTrip DATATYPE_** constants
|
||||
@param src source buffer
|
||||
*/
|
||||
void rda_aux_convert_to_float(UINT32_T N, void *dest, UINT32_T data_type, const void *src);
|
||||
|
||||
/** Starts an RDA server with a given FieldTrip connection (usually 0 for DMA), serving
|
||||
either single precision or 16 bit integer data.
|
||||
@param ft_buffer FieldTrip connection (0 for DMA, or socket for TCP connection)
|
||||
@param use16bit pass 0 to serve single precision data (with conversion as necessary)
|
||||
pass non-zero to serve 16 bit integers (only works if the FieldTrip buffer
|
||||
also contains 16 bit data)
|
||||
@param port Port number to bind to, or 0 for default port (51244 for 16 bit, 51344 for single precision)
|
||||
@param blocksize Block size for streaming out samples (0=send out variable blocks depending on incoming data)
|
||||
@param errval Optional pointer to an integer error value. Will contain either
|
||||
FT_NO_ERROR, FT_ERR_SOCKET, FT_OUT_OF_MEM or FT_THREADING on exit.
|
||||
@return Pointer to RDA server control structure, or NULL if an error occurred
|
||||
*/
|
||||
rda_server_ctrl_t *rda_start_server(int ft_buffer, int use16bit, int port, int blocksize, int *errval);
|
||||
|
||||
/** Stops an RDA server by closing the associated connections, stopping the background thread, and
|
||||
deallocating its memory (including the control structure pointed to by the argument)
|
||||
@param SC Must point to an RDA server control structure as created by rda_start_server
|
||||
@return -1 if SC is NULL
|
||||
0 on success
|
||||
*/
|
||||
int rda_stop_server(rda_server_ctrl_t *SC);
|
||||
|
||||
/** Simple helper function for retrieving the current number of clients of an RDA server
|
||||
@param SC Must point to an RDA server control structure as created by rda_start_server
|
||||
@return The number of clients
|
||||
*/
|
||||
int rda_get_num_clients(rda_server_ctrl_t *SC);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __rdaserver_h */
|
||||
+474
@@ -0,0 +1,474 @@
|
||||
/*
|
||||
* Copyright (C) 2010, Stefan Klanke
|
||||
* F.C. Donders Centre for Cognitive Neuroimaging, Radboud University Nijmegen,
|
||||
* Kapittelweg 29, 6525 EN Nijmegen, The Netherlands
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <socketserver.h>
|
||||
|
||||
/************************************************************************
|
||||
* This function deals with the incoming client requests in a loop until
|
||||
* the user requests to stop the server, or until the remote side closes
|
||||
* the connection. The implementation follows the idea of a state machine
|
||||
* with the four different states:
|
||||
* state = 0 means we are waiting for a request to come in, or we are
|
||||
* in the process of reading the first 8 bytes (the "def" part)
|
||||
* state = 1 means we are in the process of reading the remainder of
|
||||
* the request (the "buf" part")
|
||||
* state = 2 means we are in the process of writing the response (def)
|
||||
* state = 3 means ... writing the 2nd. part of the response ("buf")
|
||||
*
|
||||
* On top of those 4 states, we maintain two variables "bytesDone" and
|
||||
* "bytesTotal" that determine how many bytes we've read/written within
|
||||
* the current state, and how many bytes we need to process in total,
|
||||
* and a variable "curPtr" which points to the memory region we currently
|
||||
* need to read into, or write from. Whether any action is actually taken
|
||||
* inside the while loop also depends on the state of the socket which
|
||||
* we find out using "select" (and then set "canRead" + "canWrite" flags).
|
||||
*
|
||||
* Depending on the nature of the request, we might skip states 1 and 3.
|
||||
* This is the case if there is no "buf" attached to the message, or if
|
||||
* an outgoing message can be merged in to a single packet.
|
||||
*
|
||||
* The actual processing of the message happens before moving to state 2
|
||||
* and consists of
|
||||
* 1) possibly swapping the message to native endianness
|
||||
* 2) calling dmarequest or the user-supplied callback function
|
||||
* 3) possibly swapping back to remote endianness
|
||||
************************************************************************/
|
||||
void *_buffer_socket_func(void *arg) {
|
||||
SOCKET sock;
|
||||
ft_buffer_server_t *SC;
|
||||
int mergePackets;
|
||||
messagedef_t reqdef;
|
||||
message_t request;
|
||||
message_t *response = nullptr;
|
||||
int state=0; /* 0 = reading def, 1=reading buf, 2=writing def, 3=writing buf */
|
||||
int bytesDone, bytesTotal;
|
||||
char mergeBuffer[MERGE_THRESHOLD];
|
||||
char *curPtr; /* points at buffer that needs to be filled or written out */
|
||||
int swap = 0;
|
||||
int canRead, canWrite;
|
||||
UINT16_T reqCommand;
|
||||
UINT32_T respBufSize;
|
||||
fd_set readSet, writeSet;
|
||||
|
||||
if (arg == nullptr) return NULL;
|
||||
|
||||
/* copy over necessary variables and free the given structure */
|
||||
SC = ((ft_buffer_socket_t *) arg)->server;
|
||||
sock = ((ft_buffer_socket_t *) arg)->clientSocket;
|
||||
mergePackets = ((ft_buffer_socket_t *) arg)->mergePackets;
|
||||
free(arg);
|
||||
|
||||
if (SC->verbosity > 0) {
|
||||
printf("Started new client thread with packet merging = %i\n", mergePackets);
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&SC->lock);
|
||||
SC->numClients++;
|
||||
pthread_mutex_unlock(&SC->lock);
|
||||
|
||||
request.def = &reqdef;
|
||||
request.buf = NULL;
|
||||
bytesDone = 0;
|
||||
bytesTotal = sizeof(messagedef_t);
|
||||
curPtr = (char *) request.def;
|
||||
|
||||
while (SC->keepRunning) {
|
||||
int sel, res, n;
|
||||
struct timeval tv = {0, 10000}; /* 10ms */
|
||||
|
||||
FD_ZERO(&readSet);
|
||||
FD_ZERO(&writeSet);
|
||||
if (state < 2) {
|
||||
FD_SET(sock, &readSet);
|
||||
} else {
|
||||
FD_SET(sock, &writeSet);
|
||||
}
|
||||
sel = select((int) sock+1, &readSet, &writeSet, nullptr, &tv);
|
||||
if (sel == 0) continue;
|
||||
if (sel < 0) {
|
||||
fprintf(stderr, "Error in 'select' operation - closing client connection.\n");
|
||||
break;
|
||||
}
|
||||
canRead = FD_ISSET(sock, &readSet);
|
||||
canWrite = FD_ISSET(sock, &writeSet);
|
||||
|
||||
if (canRead) {
|
||||
n = recv(sock, curPtr + bytesDone, bytesTotal - bytesDone, 0);
|
||||
if (n<=0) {
|
||||
/* socket was closed */
|
||||
if (SC->verbosity>0) {
|
||||
std::cout << "Remote side closed client connection\n";
|
||||
}
|
||||
break;
|
||||
}
|
||||
bytesDone+=n;
|
||||
if (bytesDone<bytesTotal) continue;
|
||||
|
||||
if (state == 0) {
|
||||
/* we've read the request.def completely */
|
||||
if (reqdef.version==VERSION_OE) {
|
||||
swap = 1;
|
||||
ft_swap16(2, &reqdef.version); /* version + command */
|
||||
ft_swap32(1, &reqdef.bufsize);
|
||||
reqCommand = reqdef.command;
|
||||
}
|
||||
if (reqdef.version!=VERSION) {
|
||||
fprintf(stderr,"Incorrect version requested - closing socket.\n");
|
||||
break;
|
||||
}
|
||||
if (reqdef.bufsize > 0) {
|
||||
request.buf = malloc(reqdef.bufsize);
|
||||
if (request.buf == nullptr) {
|
||||
fprintf(stderr, "Out of memory\n");
|
||||
break;
|
||||
}
|
||||
curPtr = request.buf;
|
||||
bytesDone = 0;
|
||||
bytesTotal = reqdef.bufsize;
|
||||
state = 1;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
/* Reaching this point means that the state=1, and that we've
|
||||
read request.buf completely, so swap the endianness if
|
||||
necessary, and then move on to handling the request.
|
||||
*/
|
||||
if (swap) ft_swap_buf_to_native(reqCommand, reqdef.bufsize, request.buf);
|
||||
}
|
||||
|
||||
/* Request has been read completely, now deal with it */
|
||||
if (SC->callback != nullptr) {
|
||||
/* User supplied a callback function in ft_start_buffer_server */
|
||||
res = SC->callback(&request, &response, SC->user_data);
|
||||
if (res != 0 || response == nullptr || response->def == nullptr) {
|
||||
fprintf(stderr, "buffer_socket_func: an unexpected error occurred in user-defined request handler\n");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* No callback, use normal dmarequest */
|
||||
res = dmarequest(&request, &response);
|
||||
if (res != 0 || response == nullptr || response->def == nullptr) {
|
||||
fprintf(stderr, "buffer_socket_func: an unexpected error occurred in dmarequest\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ok, the request has been handled, results are in response.
|
||||
We can free the memory pointed to by request.buf ...
|
||||
*/
|
||||
if (request.buf != nullptr) {
|
||||
free(request.buf);
|
||||
request.buf = NULL;
|
||||
}
|
||||
|
||||
/* ... swap the response to the remote endianness, if necessary ... */
|
||||
respBufSize = response->def->bufsize;
|
||||
if (swap) ft_swap_from_native(reqCommand, response);
|
||||
|
||||
/* ... and then start writing back the response. To reduce latency,
|
||||
we try to merge response->def and response->buf if they are small,
|
||||
so we can send it in one go over TCP. To fit the merged packet into
|
||||
our state machine logic, we apply a trick and jump to state=3 directly,
|
||||
where "curPtr" points to the merged packet.
|
||||
Otherwise, we move to state=2, transmit response->def, move to state=3,
|
||||
and there transmit response->buf.
|
||||
*/
|
||||
if (mergePackets && respBufSize > 0 && respBufSize + sizeof(messagedef_t) <= MERGE_THRESHOLD) {
|
||||
memcpy(mergeBuffer, response->def, sizeof(messagedef_t));
|
||||
memcpy(mergeBuffer + sizeof(messagedef_t), response->buf, respBufSize);
|
||||
|
||||
curPtr = mergeBuffer;
|
||||
bytesDone = 0;
|
||||
bytesTotal = respBufSize + sizeof(messagedef_t);
|
||||
state = 3;
|
||||
} else {
|
||||
curPtr = (char *) response->def;
|
||||
bytesDone = 0;
|
||||
bytesTotal = sizeof(messagedef_t);
|
||||
state = 2;
|
||||
}
|
||||
canWrite = 1;
|
||||
}
|
||||
|
||||
if (state >= 2 && canWrite) {
|
||||
n = send(sock, curPtr + bytesDone, bytesTotal - bytesDone, 0);
|
||||
if (n<=0) {
|
||||
/* socket was closed */
|
||||
fprintf(stderr, "Cannot write to socket -- closing client connection.\n");
|
||||
break;
|
||||
}
|
||||
bytesDone+=n;
|
||||
if (bytesDone < bytesTotal) continue;
|
||||
if (state==2 && respBufSize > 0) {
|
||||
curPtr = (char *) response->buf;
|
||||
bytesDone = 0;
|
||||
bytesTotal = respBufSize;
|
||||
state = 3;
|
||||
continue;
|
||||
}
|
||||
/* Reaching this point means we are done with writing out the response,
|
||||
so we will now free the allocated memory, and reset to state=0.
|
||||
*/
|
||||
if (response->buf) free(response->buf);
|
||||
free(response->def);
|
||||
free(response);
|
||||
response = NULL;
|
||||
state = 0;
|
||||
curPtr = (char *) request.def;
|
||||
bytesDone = 0;
|
||||
bytesTotal = sizeof(messagedef_t);
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&SC->lock);
|
||||
SC->numClients--;
|
||||
pthread_mutex_unlock(&SC->lock);
|
||||
|
||||
closesocket(sock);
|
||||
if (request.buf != nullptr) free(request.buf);
|
||||
if (response != nullptr) {
|
||||
if (response->buf != nullptr) free(response->buf);
|
||||
if (response->def != nullptr) free(response->def);
|
||||
free(response);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* this thread listens to incoming TCP/UNIX domain socket connections
|
||||
* if a connection is made by a client, it starts _buffer_socket_func
|
||||
***********************************************************************/
|
||||
void *_buffer_server_func(void *arg) {
|
||||
ft_buffer_server_t *SC = (ft_buffer_server_t *) arg;
|
||||
int n;
|
||||
|
||||
if (SC == nullptr) {
|
||||
fprintf(stderr, "FieldTrip buffer server thread started with invalid argument\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
while (SC->keepRunning) {
|
||||
SOCKET c;
|
||||
fd_set readSet;
|
||||
int sel, rc, merge;
|
||||
pthread_t tid;
|
||||
ft_buffer_socket_t *CC;
|
||||
struct timeval tv = {0,10000}; /* 10 ms for select timeout */
|
||||
|
||||
FD_ZERO(&readSet);
|
||||
FD_SET(SC->serverSocket, &readSet);
|
||||
|
||||
sel = select((int) SC->serverSocket + 1, &readSet, nullptr, nullptr, &tv);
|
||||
|
||||
if (sel == 0) continue;
|
||||
|
||||
/* The following code portion looks weird because of the preprocessor
|
||||
defines splitting an if/else clause, but it's just what we want:
|
||||
On Windows, there is no if/else clause, and the second (TCP) bit
|
||||
is always called. On POSIX systems, we branch depending on whether
|
||||
the server runs a local domain socket or TCP socket.
|
||||
*/
|
||||
#ifndef WIN32
|
||||
if (SC->isUnixDomain) {
|
||||
struct sockaddr_un sa;
|
||||
socklen_t size_sa = sizeof(sa);
|
||||
|
||||
c = accept(SC->serverSocket, (struct sockaddr *)&sa, &size_sa);
|
||||
|
||||
if (c == INVALID_SOCKET) {
|
||||
perror("buffer_server, accept");
|
||||
continue;
|
||||
}
|
||||
/* never merge packets for (local) UNIX sockets */
|
||||
merge = 0;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
struct sockaddr_in sa;
|
||||
socklen_t size_sa = sizeof(sa);
|
||||
|
||||
c = accept(SC->serverSocket, (struct sockaddr *)&sa, &size_sa);
|
||||
|
||||
if (c == INVALID_SOCKET) {
|
||||
perror("buffer_server, accept");
|
||||
continue;
|
||||
}
|
||||
/* enable packet merging only if it's not localhost */
|
||||
merge = (sa.sin_addr.s_addr == htonl(INADDR_LOOPBACK)) ? 0 : 1;
|
||||
}
|
||||
|
||||
CC = (ft_buffer_socket_t *) malloc(sizeof(ft_buffer_socket_t));
|
||||
if (CC == nullptr) {
|
||||
fprintf(stderr, "Out of memory\n");
|
||||
closesocket(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
CC->server = SC;
|
||||
CC->clientSocket = c;
|
||||
CC->mergePackets = merge;
|
||||
|
||||
rc = pthread_create(&tid, nullptr, _buffer_socket_func, CC);
|
||||
if (rc) {
|
||||
fprintf(stderr, "tcpserver: return code from pthread_create() is %d\n", rc);
|
||||
closesocket(c);
|
||||
free(CC);
|
||||
}
|
||||
}
|
||||
while ((n=SC->numClients)>0) {
|
||||
printf("Waiting for %i remaining client threads to stop...\n", n);
|
||||
usleep(10000);
|
||||
}
|
||||
pthread_mutex_lock(&SC->lock);
|
||||
SC->numClients--;
|
||||
pthread_mutex_unlock(&SC->lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
ft_buffer_server_t *ft_start_buffer_server(int port, const char *name, ft_request_callback_t callback, void *user_data) {
|
||||
ft_buffer_server_t *SC;
|
||||
int optval;
|
||||
SOCKET s = INVALID_SOCKET;
|
||||
|
||||
SC = (ft_buffer_server_t *) malloc(sizeof(ft_buffer_server_t));
|
||||
if (SC == nullptr) return NULL;
|
||||
|
||||
SC->callback = callback;
|
||||
SC->user_data = user_data;
|
||||
|
||||
#ifdef WIN32
|
||||
{
|
||||
/* We only need to do this once ... and actually have a corresponding WSACleanup call somewhere */
|
||||
static WSADATA wsa = {0,0};
|
||||
if (wsa.wVersion == 0) {
|
||||
if(WSAStartup(MAKEWORD(1, 1), &wsa)) {
|
||||
fprintf(stderr, "ft_start_buffer_server: cannot start WIN32 sockets.\n");
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* setup socket */
|
||||
if (port == 0) {
|
||||
#ifdef WIN32
|
||||
fprintf(stderr, "ft_start_buffer_server: invalid port number given.\n");
|
||||
goto cleanup;
|
||||
#else
|
||||
struct sockaddr_un sa;
|
||||
/* UNIX domain socket */
|
||||
s = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (s == INVALID_SOCKET) {
|
||||
perror("ft_start_buffer_server, socket");
|
||||
goto cleanup;
|
||||
}
|
||||
sa.sun_family = AF_UNIX;
|
||||
strncpy(sa.sun_path, name, sizeof(sa.sun_path));
|
||||
if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) == -1) {
|
||||
perror("ft_start_buffer_server, bind");
|
||||
goto cleanup;
|
||||
}
|
||||
SC->isUnixDomain = 0;
|
||||
#endif
|
||||
} else {
|
||||
/* TCP socket */
|
||||
struct sockaddr_in sa;
|
||||
|
||||
s = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (s == INVALID_SOCKET) {
|
||||
perror("ft_start_buffer_server, socket");
|
||||
goto cleanup;
|
||||
}
|
||||
/* prevend "bind: address already in use" */
|
||||
optval = 1;
|
||||
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*)&optval, sizeof(optval)) < 0) {
|
||||
perror("ft_start_buffer_server, setsockopt");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
bzero(&sa, sizeof(sa));
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons(port);
|
||||
sa.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
|
||||
perror("ft_start_buffer_server, bind");
|
||||
goto cleanup;
|
||||
}
|
||||
SC->isUnixDomain = 0;
|
||||
}
|
||||
|
||||
/* place the socket in non-blocking mode, required to do thread cancelation */
|
||||
#ifdef WIN32
|
||||
{
|
||||
unsigned long enable = 0;
|
||||
ioctlsocket(s, FIONBIO, &enable);
|
||||
}
|
||||
#else
|
||||
optval = fcntl(s, F_GETFL, nullptr);
|
||||
optval = optval | O_NONBLOCK;
|
||||
if (fcntl(s, F_SETFL, optval)<0) {
|
||||
perror("ft_start_buffer_server, fcntl");
|
||||
goto cleanup;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (listen(s, BACKLOG)<0) {
|
||||
perror("ft_start_buffer_server, listen");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* set some control variables */
|
||||
SC->numClients = 0;
|
||||
SC->keepRunning = 1;
|
||||
SC->serverSocket = s;
|
||||
SC->verbosity = 10; /* TODO: specify proper values */
|
||||
|
||||
/* create the mutex */
|
||||
if (pthread_mutex_init(&SC->lock, nullptr) != 0) {
|
||||
fprintf(stderr,"start_tcp_server: mutex could not be initialised\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* create thread with default attributes */
|
||||
if (pthread_create(&SC->threadID, nullptr, _buffer_server_func, SC) == 0) {
|
||||
/* everything went fine - thread should be running now */
|
||||
return SC;
|
||||
}
|
||||
|
||||
fprintf(stderr,"start_tcp_server: could not spawn thread\n");
|
||||
pthread_mutex_destroy(&SC->lock);
|
||||
|
||||
cleanup:
|
||||
if (SC != nullptr) free(SC);
|
||||
if (s != INVALID_SOCKET) {
|
||||
#ifdef WIN32
|
||||
shutdown(s, SD_BOTH);
|
||||
#else
|
||||
shutdown(s, SHUT_RDWR);
|
||||
#endif
|
||||
closesocket(s);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ft_stop_buffer_server(ft_buffer_server_t *S) {
|
||||
if (S == nullptr) return;
|
||||
|
||||
S->keepRunning = 0;
|
||||
pthread_join(S->threadID, nullptr);
|
||||
pthread_detach(S->threadID);
|
||||
free(S);
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2010, Stefan Klanke
|
||||
* F.C. Donders Centre for Cognitive Neuroimaging, Radboud University Nijmegen,
|
||||
* Kapittelweg 29, 6525 EN Nijmegen, The Netherlands
|
||||
*/
|
||||
#ifndef __socketserver_h
|
||||
#define __socketserver_h
|
||||
|
||||
#include "buffer.h"
|
||||
#include <pthread.h>
|
||||
|
||||
#define MERGE_THRESHOLD 4096 /* TODO: optimize this value? Maybe look at MTU size */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef WIN32
|
||||
#define INVALID_SOCKET -1
|
||||
typedef int SOCKET;
|
||||
#include <sys/un.h>
|
||||
#else
|
||||
typedef int socklen_t;
|
||||
#endif
|
||||
|
||||
typedef int (*ft_request_callback_t)(const message_t *request, message_t **response, void *user_data);
|
||||
|
||||
/** The following structure is used for managing a server. The structure is
|
||||
allocated and filled in ft_start_buffer_server and then passed on to the
|
||||
actual server thread, as well as to all threads handling the client
|
||||
connections. Thread functions are written such that they monitor the
|
||||
"keepRunning" member of this structure, and once this is set to 0, the
|
||||
threads stop and exit.
|
||||
*/
|
||||
typedef struct {
|
||||
SOCKET serverSocket; ///< Socket the server listens on (TCP or UNIX domain)
|
||||
int keepRunning; ///< Flag=1: thread functions keep looping, 0: threads exit
|
||||
int numClients; ///< Current number of clients connected to the server
|
||||
int verbosity; ///< Determines how much information is being printed during operation
|
||||
int isUnixDomain; ///< 1: UNIX domain socket, 0: TCP socket
|
||||
pthread_t threadID; ///< POSIX thread identifier of the server thread, client threads are detached immediately
|
||||
pthread_mutex_t lock; ///< Mutex to protect the "numClients" member, commonly used by all threads
|
||||
ft_request_callback_t callback; ///< Callback function to be called *instead* of dmarequest
|
||||
void *user_data; ///< Pointer to user-defined data structure, passed on to callback
|
||||
} ft_buffer_server_t;
|
||||
|
||||
/** Small helper structure that is passed to client threads. Get's allocated
|
||||
using malloc() in the server thread, and disposed using free() in the client
|
||||
thread.
|
||||
*/
|
||||
typedef struct {
|
||||
ft_buffer_server_t *server; ///< Pointer to the common control structure
|
||||
SOCKET clientSocket; ///< The newly created socket (from "accept")
|
||||
int mergePackets; ///< 1: merge packets if total size below threshold, 0: never merge (=>local host)
|
||||
} ft_buffer_socket_t;
|
||||
|
||||
/** Creates a server socket, binds it to the specified UNIX domain name or port,
|
||||
starts listening on this socket, and spawns a background thread to serve
|
||||
requests on this socket.
|
||||
TCP sockets are created for positive port numbers (name is ignored),
|
||||
UNIX domain sockets are created for port=0 (name needs to be a UNIX pathname).
|
||||
|
||||
If callback != nullptr, that function is called like
|
||||
callback(request, &response, user_data)
|
||||
for every request coming in over the socket, *instead* of the normal dmarequest.
|
||||
Usually the user will call the latter function internally. Make sure your
|
||||
callback is re-entrant!!!
|
||||
|
||||
Returns allocated control structure, or NULL in case of errors.
|
||||
*/
|
||||
ft_buffer_server_t *ft_start_buffer_server(int port, const char *name, ft_request_callback_t callback, void *user_data);
|
||||
|
||||
/** Stops background thread(s), closes the sockets, and disposes the control structure S.
|
||||
S cannot be used anymore after this call.
|
||||
*/
|
||||
void ft_stop_buffer_server(ft_buffer_server_t *S);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (C) 2008, Robert Oostenveld
|
||||
* F.C. Donders Centre for Cognitive Neuroimaging, Radboud University Nijmegen,
|
||||
* Kapittelweg 29, 6525 EN Nijmegen, The Netherlands
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "buffer.h"
|
||||
|
||||
#define MERGE_THRESHOLD 4096 /* TODO: optimize this value? Maybe look at MTU size */
|
||||
|
||||
/*******************************************************************************
|
||||
* communicate with the buffer through TCP
|
||||
*******************************************************************************/
|
||||
int tcprequest(int server, const message_t *request, message_t **response_ptr) {
|
||||
unsigned int n, total;
|
||||
|
||||
/* this will hold the response */
|
||||
message_t *response;
|
||||
response = (message_t*)malloc(sizeof(message_t));
|
||||
response->def = (messagedef_t*)malloc(sizeof(messagedef_t));
|
||||
response->buf = NULL;
|
||||
/* the response should be passed to the calling function, where it should be freed */
|
||||
*response_ptr = response;
|
||||
|
||||
total = sizeof(messagedef_t) + request->def->bufsize;
|
||||
|
||||
/* Check whether request->def and request->buf are already contiguous in memory,
|
||||
or whether request->buf is empty. If that's the case, we can write the request in one go.
|
||||
*/
|
||||
if (request->def->bufsize == 0 || (request->def+1) == (messagedef_t *) request->buf) {
|
||||
if ((n = bufwrite(server, request->def, total)) != total) {
|
||||
fprintf(stderr, "write size = %d, should be %d\n", n, total);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
/* Now check whether the total size is below the merge threshold, in which case
|
||||
we'll copy it to contiguous memory and again send it in one go
|
||||
*/
|
||||
else if (total <= MERGE_THRESHOLD) {
|
||||
char merged[MERGE_THRESHOLD];
|
||||
|
||||
memcpy(merged, request->def, sizeof(messagedef_t));
|
||||
memcpy(merged + sizeof(messagedef_t), request->buf, request->def->bufsize);
|
||||
|
||||
if ((n = bufwrite(server, merged, total)) != total) {
|
||||
fprintf(stderr, "write size = %d, should be %d\n", n, total);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
/* Otherwise, send "def" and "buf" in separate pieces. This might introduce latencies
|
||||
if the other end runs Windows :-(
|
||||
*/
|
||||
else {
|
||||
/* send the request to the server, first the message definition */
|
||||
if ((n = bufwrite(server, request->def, sizeof(messagedef_t)))!=sizeof(messagedef_t)) {
|
||||
fprintf(stderr, "write size = %d, should be %lu\n", n, (long unsigned int)sizeof(messagedef_t));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* send the request to the server, then the message payload */
|
||||
if ((n = bufwrite(server, request->buf, request->def->bufsize))!=request->def->bufsize) {
|
||||
fprintf(stderr, "write size = %d, should be %d\n", n, request->def->bufsize);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
/* read the response from the server, first the message definition */
|
||||
if ((n = bufread(server, response->def, sizeof(messagedef_t))) != sizeof(messagedef_t)) {
|
||||
fprintf(stderr, "packet size = %d, should be %lu\n", n, (long unsigned int)sizeof(messagedef_t));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (response->def->version!=VERSION) {
|
||||
fprintf(stderr, "incorrect version\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* read the response from the server, then the message payload */
|
||||
if (response->def->bufsize>0) {
|
||||
response->buf = malloc(response->def->bufsize);
|
||||
if ((n = bufread(server, response->buf, response->def->bufsize)) != response->def->bufsize) {
|
||||
fprintf(stderr, "read size = %d, should be %d\n", n, response->def->bufsize);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
/* everything went fine, return with the response */
|
||||
/* print_response(response->def); */
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
/* there was a problem, clear the response and return */
|
||||
FREE(response->def);
|
||||
FREE(response->buf);
|
||||
FREE(response);
|
||||
*response_ptr = NULL; /* SK: this was missing a "*", effectively never really returning 0 */
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
/*
|
||||
* This will contain the implementation of the TiA server on top of the FieldTrip buffer, similar to the RDA server.
|
||||
*
|
||||
*/
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
/*
|
||||
* This will contain the implementation of the TiA server on top of the FieldTrip buffer, similar to the RDA server.
|
||||
*
|
||||
*/
|
||||
+482
@@ -0,0 +1,482 @@
|
||||
/*
|
||||
* Copyright (C) 2008, Robert Oostenveld & Christian Hesse
|
||||
* F.C. Donders Centre for Cognitive Neuroimaging, Radboud University Nijmegen,
|
||||
* Kapittelweg 29, 6525 EN Nijmegen, The Netherlands
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h> /* for strerror */
|
||||
|
||||
#include "buffer.h"
|
||||
#include <pthread.h>
|
||||
#include "extern.h"
|
||||
|
||||
unsigned int bufread(int s, void* buf, unsigned int numel)
|
||||
{
|
||||
unsigned int numcall = 0, numread = 0, verbose = 0;
|
||||
int numthis = 0;
|
||||
|
||||
while (numread < numel)
|
||||
{
|
||||
numthis = recv(s, (char*)buf + numread, numel - numread, 0);
|
||||
if (numthis < 0)
|
||||
{
|
||||
perror("bufread");
|
||||
break;
|
||||
}
|
||||
else if (numthis == 0) { break; }
|
||||
|
||||
if (verbose > 0) { fprintf(stderr, "bufread: read %d bytes\n", numthis); }
|
||||
numread += numthis;
|
||||
numcall++;
|
||||
#ifndef PLATFORM_WIN32 /* SK: I think this shouldn't be necessary on any platform: the sockets are blocking */
|
||||
if (numread < numel) { usleep(1000); }
|
||||
#endif
|
||||
}
|
||||
if (verbose > 1) { fprintf(stderr, "bufread: reading the complete buffer required %d calls\n", numcall); }
|
||||
return numread;
|
||||
}
|
||||
|
||||
unsigned int bufwrite(int s, const void* buf, unsigned int numel)
|
||||
{
|
||||
int numthis = 0;
|
||||
unsigned int numcall = 0, numwrite = 0, verbose = 0;
|
||||
|
||||
while (numwrite < numel)
|
||||
{
|
||||
numthis = send(s, (char*)buf + numwrite, numel - numwrite, 0);
|
||||
if (numthis < 0)
|
||||
{
|
||||
perror("bufwrite");
|
||||
break;
|
||||
}
|
||||
else if (numthis == 0) { break; }
|
||||
|
||||
if (verbose) fprintf(stderr, "bufwrite: wrote %d bytes\n", numthis);
|
||||
numwrite += numthis;
|
||||
numcall++;
|
||||
#ifndef PLATFORM_WIN32 /* SK: I think this shouldn't be necessary on any platform: the sockets are blocking */
|
||||
if (numwrite < numel) { usleep(1000); }
|
||||
#endif
|
||||
}
|
||||
if (verbose > 1) { fprintf(stderr, "bufwrite: writing the complete buffer required %d calls\n", numcall); }
|
||||
return numwrite;
|
||||
}
|
||||
|
||||
unsigned int append(void** buf1, unsigned int bufsize1, void* buf2, unsigned int bufsize2)
|
||||
{
|
||||
int verbose = 0;
|
||||
|
||||
if (verbose > 1)
|
||||
{
|
||||
pthread_mutex_lock(&mutexappendcount);
|
||||
appendcount++;
|
||||
fprintf(stderr, "append: appendcount = %d\n", appendcount);
|
||||
pthread_mutex_unlock(&mutexappendcount);
|
||||
}
|
||||
|
||||
if (((*buf1) != nullptr) && (bufsize1 == 0))
|
||||
{
|
||||
perror("append err1");
|
||||
return 0; /* was -1, but this is never checked anyway */
|
||||
}
|
||||
else if (((*buf1) == nullptr) && (bufsize1 != 0))
|
||||
{
|
||||
perror("append err2");
|
||||
return 0; /* was -1, but this is never checked anyway */
|
||||
}
|
||||
|
||||
if ((*buf1) == nullptr)
|
||||
{
|
||||
if (verbose > 0) { fprintf(stderr, "append: allocating %d bytes\n", bufsize2); }
|
||||
(*buf1) = malloc(bufsize2);
|
||||
}
|
||||
else if ((*buf1) != nullptr)
|
||||
{
|
||||
if (verbose > 0) { fprintf(stderr, "append: reallocating from %d to %d bytes\n", bufsize1, bufsize1 + bufsize2); }
|
||||
(*buf1) = realloc((*buf1), bufsize1 + bufsize2);
|
||||
}
|
||||
|
||||
memcpy((char*)(*buf1) + bufsize1, buf2, bufsize2);
|
||||
return (bufsize1 + bufsize2);
|
||||
}
|
||||
|
||||
int close_connection(int s)
|
||||
{
|
||||
int status = 0, verbose = 0;
|
||||
if (verbose > 0) { fprintf(stderr, "close_connection: socket = %d\n", s); }
|
||||
if (s > 0) status = closesocket(s); /* it is a TCP connection */
|
||||
if (status != 0) { perror("close_connection"); }
|
||||
return status;
|
||||
}
|
||||
|
||||
int open_connection(const char* hostname, int port)
|
||||
{
|
||||
int verbose = 0;
|
||||
int s, retry;
|
||||
struct sockaddr_in sa;
|
||||
struct hostent* host;
|
||||
#ifdef WIN32
|
||||
static WSADATA wsa = { 0, 0 }; /* check version fields to only initialise once */
|
||||
#endif
|
||||
|
||||
if (port == 0)
|
||||
{
|
||||
if (verbose > 0) { fprintf(stderr, "open_connection: using direct memory copy\n"); }
|
||||
return 0;
|
||||
}
|
||||
else { if (verbose > 0) { fprintf(stderr, "open_connection: server = %s, port = %d\n", hostname, port); } }
|
||||
|
||||
#ifdef WIN32
|
||||
if (wsa.wVersion == 0)
|
||||
{
|
||||
/* We only need to do this once ... and actually have a corresponding WSACleanup call somewhere */
|
||||
if (WSAStartup(MAKEWORD(1, 1), &wsa))
|
||||
{
|
||||
fprintf(stderr, "open_connection: cannot start sockets\n");
|
||||
/* FIXME should this exception be handled more explicitely? */
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((host = gethostbyname(hostname)) == nullptr)
|
||||
{
|
||||
fprintf(stderr, "open_connection: nslookup1 failed on '%s'\n", hostname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (host->h_length == 0)
|
||||
{
|
||||
fprintf(stderr, "open_connection: nslookup2 failed on '%s'\n", hostname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bzero(&sa, sizeof(sa));
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons(port);
|
||||
|
||||
memcpy(&(sa.sin_addr.s_addr), host->h_addr_list[0], sizeof(sa.sin_addr.s_addr));
|
||||
|
||||
if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0)
|
||||
{
|
||||
if (verbose > 0) { fprintf(stderr, "open_connection: socket = %d\n", s); }
|
||||
perror("open_connection");
|
||||
return -1;
|
||||
}
|
||||
|
||||
retry = 10;
|
||||
while (retry > 0)
|
||||
{
|
||||
if (connect(s, (struct sockaddr*)&sa, sizeof sa) < 0)
|
||||
{
|
||||
/* wait 5 miliseconds and try again */
|
||||
usleep(5000);
|
||||
retry--;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* this signals that the connection has been made */
|
||||
retry = -1;
|
||||
}
|
||||
}
|
||||
if (retry == 0)
|
||||
{
|
||||
/* close the socket */
|
||||
closesocket(s);
|
||||
/* it failed on mutliple attempts, give up */
|
||||
return -2;
|
||||
}
|
||||
|
||||
/*
|
||||
while (connect(s, (struct sockaddr *)&sa, sizeof sa) < 0) {
|
||||
perror("open_connection connect");
|
||||
usleep(1000000);
|
||||
}
|
||||
*/
|
||||
|
||||
if (verbose > 0) { fprintf(stderr, "open_connection: connected to %s:%d on socket %d\n", hostname, port, s); }
|
||||
|
||||
#ifdef DISABLE_NAGLE
|
||||
{
|
||||
int optval = 1;
|
||||
setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval));
|
||||
}
|
||||
#endif
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void check_datatypes()
|
||||
{
|
||||
/* check datatypes */
|
||||
if (WORDSIZE_CHAR != 1)
|
||||
{
|
||||
fprintf(stderr, "invalid size of CHAR (%d)\n", (int)WORDSIZE_CHAR);
|
||||
exit(-1);
|
||||
}
|
||||
if (WORDSIZE_UINT8 != 1)
|
||||
{
|
||||
fprintf(stderr, "invalid size of UINT8 (%d)\n", (int)WORDSIZE_UINT8);
|
||||
exit(-1);
|
||||
}
|
||||
if (WORDSIZE_UINT16 != 2)
|
||||
{
|
||||
fprintf(stderr, "invalid size of UINT16 (%d)\n", (int)WORDSIZE_UINT16);
|
||||
exit(-1);
|
||||
}
|
||||
if (WORDSIZE_UINT32 != 4)
|
||||
{
|
||||
fprintf(stderr, "invalid size of UINT32 (%d)\n", (int)WORDSIZE_UINT32);
|
||||
exit(-1);
|
||||
}
|
||||
if (WORDSIZE_UINT64 != 8)
|
||||
{
|
||||
fprintf(stderr, "invalid size of UINT64 (%d)\n", (int)WORDSIZE_UINT64);
|
||||
exit(-1);
|
||||
}
|
||||
if (WORDSIZE_INT8 != 1)
|
||||
{
|
||||
fprintf(stderr, "invalid size of INT8 (%d)\n", (int)WORDSIZE_INT8);
|
||||
exit(-1);
|
||||
}
|
||||
if (WORDSIZE_INT16 != 2)
|
||||
{
|
||||
fprintf(stderr, "invalid size of INT16 (%d)\n", (int)WORDSIZE_INT16);
|
||||
exit(-1);
|
||||
}
|
||||
if (WORDSIZE_INT32 != 4)
|
||||
{
|
||||
fprintf(stderr, "invalid size of INT32 (%d)\n", (int)WORDSIZE_INT32);
|
||||
exit(-1);
|
||||
}
|
||||
if (WORDSIZE_INT64 != 8)
|
||||
{
|
||||
fprintf(stderr, "invalid size of INT64 (%d)\n", (int)WORDSIZE_INT64);
|
||||
exit(-1);
|
||||
}
|
||||
if (WORDSIZE_FLOAT32 != 4)
|
||||
{
|
||||
fprintf(stderr, "invalid size of FLOAT32 (%d)\n", (int)WORDSIZE_FLOAT32);
|
||||
exit(-1);
|
||||
}
|
||||
if (WORDSIZE_FLOAT64 != 8)
|
||||
{
|
||||
fprintf(stderr, "invalid size of FLOAT64 (%d)\n", (int)WORDSIZE_FLOAT64);
|
||||
exit(-1);
|
||||
}
|
||||
if (sizeof(messagedef_t) != 8)
|
||||
{
|
||||
fprintf(stderr, "invalid size of messagedef_t\n");
|
||||
exit(-1);
|
||||
}
|
||||
if (sizeof(headerdef_t) != 24)
|
||||
{
|
||||
fprintf(stderr, "invalid size of headerdef_t \n");
|
||||
exit(-1);
|
||||
}
|
||||
if (sizeof(datadef_t) != 16)
|
||||
{
|
||||
fprintf(stderr, "invalid size of datadef_t \n");
|
||||
exit(-1);
|
||||
}
|
||||
if (sizeof(eventdef_t) != 32)
|
||||
{
|
||||
fprintf(stderr, "invalid size of eventdef_t \n");
|
||||
exit(-1);
|
||||
}
|
||||
if (sizeof(datasel_t) != 8)
|
||||
{
|
||||
fprintf(stderr, "invalid size of datasel_t \n");
|
||||
exit(-1);
|
||||
}
|
||||
if (sizeof(eventsel_t) != 8)
|
||||
{
|
||||
fprintf(stderr, "invalid size of eventsel_t \n");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
unsigned int wordsize_from_type(UINT32_T data_type)
|
||||
{
|
||||
switch (data_type)
|
||||
{
|
||||
case DATATYPE_CHAR: return WORDSIZE_CHAR;
|
||||
case DATATYPE_UINT8:
|
||||
case DATATYPE_INT8: return WORDSIZE_INT8;
|
||||
case DATATYPE_UINT16:
|
||||
case DATATYPE_INT16: return WORDSIZE_INT16;
|
||||
case DATATYPE_UINT32:
|
||||
case DATATYPE_INT32: return WORDSIZE_INT32;
|
||||
case DATATYPE_UINT64:
|
||||
case DATATYPE_INT64: return WORDSIZE_INT64;
|
||||
case DATATYPE_FLOAT32: return WORDSIZE_FLOAT32;
|
||||
case DATATYPE_FLOAT64: return WORDSIZE_FLOAT64;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const ft_chunk_t* find_chunk(const void* buf, unsigned int offset0, unsigned int size, UINT32_T chunk_type)
|
||||
{
|
||||
unsigned int bufpos = offset0;
|
||||
while (bufpos + sizeof(ft_chunkdef_t) <= size)
|
||||
{
|
||||
const ft_chunk_t* chunk = (ft_chunk_t*)((char*)buf + bufpos);
|
||||
if (chunk->def.type == chunk_type) { return chunk; }
|
||||
bufpos += sizeof(ft_chunkdef_t) + chunk->def.size;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
/** Iterate through an array of events and check whether all of them are properly defined,
|
||||
that is, whether the "type" and "value" fields are of valid type and size, and whether the
|
||||
"bufsize" fields are correct (that is, fully contained in the passed buffer, and big enough
|
||||
to hold "type" and "value".
|
||||
Returns the number of events on success (might also be 0), or
|
||||
a negative number that indicates in which event definition an error happend,
|
||||
for example, a return value of -2 means that the first event was ok, but the second event
|
||||
definition was invalid. This function returns at the first error.
|
||||
*/
|
||||
int check_event_array(unsigned int size, const void* buf)
|
||||
{
|
||||
unsigned int offset = 0;
|
||||
int numEvents = 0;
|
||||
|
||||
while (offset + sizeof(eventdef_t) <= size)
|
||||
{
|
||||
const eventdef_t* E;
|
||||
unsigned int wsType, wsValue;
|
||||
|
||||
/* Set our event pointer to the current location within the array */
|
||||
E = (const eventdef_t*)((char*)buf + offset);
|
||||
|
||||
/* Increase the offset by the size of this event, and check whether it's fully
|
||||
contained within the given array.
|
||||
*/
|
||||
offset += sizeof(eventdef_t) + E->bufsize;
|
||||
if (offset > size) { goto error; }
|
||||
|
||||
/* Check whether "type" and "value" are of known type */
|
||||
wsType = wordsize_from_type(E->type_type);
|
||||
if (wsType == 0) { goto error; }
|
||||
wsValue = wordsize_from_type(E->value_type);
|
||||
if (wsValue == 0) { goto error; }
|
||||
|
||||
/* Check whether "type" and "value" are contained in this event's "buf" */
|
||||
if (wsType * E->type_numel + wsValue * E->value_numel > E->bufsize) { goto error; }
|
||||
|
||||
/* all checks passed, continue at next offset */
|
||||
++numEvents;
|
||||
}
|
||||
return numEvents;
|
||||
error:
|
||||
return -(1 + numEvents);
|
||||
}
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
int open_unix_connection(const char* name) { return -1; }
|
||||
#else
|
||||
int open_unix_connection(const char* name)
|
||||
{
|
||||
int verbose = 0;
|
||||
int s, retry;
|
||||
struct sockaddr_un sa;
|
||||
|
||||
bzero(&sa, sizeof(sa));
|
||||
sa.sun_family = AF_UNIX;
|
||||
strncpy(sa.sun_path, name, sizeof(sa.sun_path));
|
||||
|
||||
s = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (s < 0) {
|
||||
perror("open_unix_connection, socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
retry = 10;
|
||||
while (retry > 0) {
|
||||
if (connect(s, (struct sockaddr*) & sa, sizeof(sa)) < 0) {
|
||||
/* wait 5 miliseconds and try again */
|
||||
perror("open_connection");
|
||||
usleep(5000);
|
||||
retry--;
|
||||
}
|
||||
else {
|
||||
/* this signals that the connection has been made */
|
||||
retry = -1;
|
||||
}
|
||||
}
|
||||
if (retry == 0) {
|
||||
/* it failed on mutliple attempts, give up */
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (verbose > 0)
|
||||
fprintf(stderr, "open_unix_connection: connected to %s on socket %d\n", name, s);
|
||||
|
||||
return s;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
#ifndef COMPILER_MINGW
|
||||
|
||||
/*
|
||||
* timeval.h 1.0 01/12/19
|
||||
*
|
||||
* Defines gettimeofday, timeval, etc. for Win32
|
||||
*
|
||||
* By Wu Yongwei
|
||||
*
|
||||
*/
|
||||
|
||||
//#define EPOCHFILETIME (116444736000000000i64)
|
||||
#define EPOCHFILETIME ((INT64_T) 116444736000000000LL)
|
||||
|
||||
#ifdef COMPILER_LCC
|
||||
VOID STDCALL GetSystemTimeAsFileTime(LPFILETIME);
|
||||
#endif
|
||||
|
||||
int gettimeofday(struct timeval* tv, struct timezone* tz)
|
||||
{
|
||||
FILETIME ft;
|
||||
LARGE_INTEGER li;
|
||||
INT64_T t;
|
||||
static int tzflag;
|
||||
|
||||
if (tv)
|
||||
{
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
li.LowPart = ft.dwLowDateTime;
|
||||
li.HighPart = ft.dwHighDateTime;
|
||||
t = li.QuadPart; /* In 100-nanosecond intervals */
|
||||
t -= EPOCHFILETIME; /* Offset to the Epoch time */
|
||||
t /= 10; /* In microseconds */
|
||||
tv->tv_sec = (long)(t / 1000000);
|
||||
tv->tv_usec = (long)(t % 1000000);
|
||||
}
|
||||
|
||||
#ifndef COMPILER_LCC
|
||||
/* LCC that comes with Matlab has problems with _timezone and _daylight,
|
||||
and we don't need it anyway */
|
||||
if (tz)
|
||||
{
|
||||
if (!tzflag)
|
||||
{
|
||||
_tzset();
|
||||
tzflag++;
|
||||
}
|
||||
tz->tz_minuteswest = _timezone / 60;
|
||||
tz->tz_dsttime = _daylight;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
/* SK: This has now been moved into util.c for simplification of compile scripts and Makefiles */
|
||||
|
||||
/*
|
||||
* timeval.h 1.0 01/12/19
|
||||
*
|
||||
* Defines gettimeofday, timeval, etc. for Win32
|
||||
*
|
||||
* By Wu Yongwei
|
||||
*
|
||||
*/
|
||||
#ifndef _TIMEVAL_H
|
||||
#define _TIMEVAL_H
|
||||
|
||||
#if TIME_WITH_SYS_TIME
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#elif HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#else
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
#if ! HAVE_GETTIMEOFDAY || MINGW
|
||||
|
||||
#if __WIN32__
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#ifndef __GNUC__
|
||||
#define EPOCHFILETIME (116444736000000000i64)
|
||||
#else
|
||||
#define EPOCHFILETIME (116444736000000000LL)
|
||||
#endif
|
||||
|
||||
#include "gettimeofday.h"
|
||||
|
||||
/*!
|
||||
\brief A Windows gettimeofday implementation.
|
||||
*/
|
||||
|
||||
int gettimeofday(struct timeval *tv, struct timezone *tz)
|
||||
{
|
||||
#if __WIN32__
|
||||
FILETIME ft;
|
||||
LARGE_INTEGER li;
|
||||
__int64 t;
|
||||
static int tzflag;
|
||||
|
||||
if (tv) {
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
li.LowPart = ft.dwLowDateTime;
|
||||
li.HighPart = ft.dwHighDateTime;
|
||||
t = li.QuadPart; /* In 100-nanosecond intervals */
|
||||
t -= EPOCHFILETIME; /* Offset to the Epoch time */
|
||||
t /= 10; /* In microseconds */
|
||||
tv->tv_sec = (long)(t / 1000000);
|
||||
tv->tv_usec = (long)(t % 1000000);
|
||||
}
|
||||
|
||||
if (tz) {
|
||||
if (!tzflag) {
|
||||
_tzset();
|
||||
tzflag++;
|
||||
}
|
||||
tz->tz_minuteswest = _timezone / 60;
|
||||
tz->tz_dsttime = _daylight;
|
||||
}
|
||||
|
||||
return 0;
|
||||
#else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
#ifndef __gettimeofday_h
|
||||
#define __gettimeofday_h
|
||||
#include <time.h>
|
||||
|
||||
struct timezone {
|
||||
int tz_minuteswest; /* minutes W of Greenwich */
|
||||
int tz_dsttime; /* type of dst correction */
|
||||
};
|
||||
|
||||
int gettimeofday(struct timeval *tv, struct timezone *tz);
|
||||
|
||||
#endif
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
#include <winsock2.h>
|
||||
#include "poll.h"
|
||||
|
||||
int poll (struct pollfd *p, int num, int timeout)
|
||||
{
|
||||
struct timeval tv;
|
||||
fd_set read, write, except;
|
||||
int i, n, ret;
|
||||
char buf[1024];
|
||||
|
||||
FD_ZERO (&read);
|
||||
FD_ZERO (&write);
|
||||
FD_ZERO (&except);
|
||||
|
||||
n = -1;
|
||||
for (i = 0; i < num; ++i)
|
||||
{
|
||||
if (p[i].fd < 0)
|
||||
continue;
|
||||
if (p[i].events & POLLIN)
|
||||
FD_SET (p[i].fd, &read);
|
||||
if (p[i].events & POLLOUT)
|
||||
FD_SET (p[i].fd, &write);
|
||||
if (p[i].events & POLLERR)
|
||||
FD_SET (p[i].fd, &except);
|
||||
if (p[i].fd > n)
|
||||
n = p[i].fd;
|
||||
}
|
||||
|
||||
if (n == -1)
|
||||
return (0);
|
||||
|
||||
if (timeout < 0)
|
||||
ret = select (n+1, &read, &write, &except, nullptr);
|
||||
else
|
||||
{
|
||||
tv.tv_sec = timeout / 1000;
|
||||
tv.tv_usec = 1000 * (timeout % 1000);
|
||||
ret = select (n+1, &read, &write, &except, &tv);
|
||||
}
|
||||
|
||||
for (i = 0; ret >= 0 && i < num; ++i)
|
||||
{
|
||||
p[i].revents = 0;
|
||||
if (FD_ISSET (p[i].fd, &read))
|
||||
{
|
||||
|
||||
int j = recv(p[i].fd, buf, 1024, MSG_PEEK);
|
||||
if(j>0)
|
||||
p[i].revents |= POLLIN;
|
||||
else
|
||||
p[i].revents |= POLLHUP;
|
||||
|
||||
}
|
||||
if (FD_ISSET (p[i].fd, &write))
|
||||
p[i].revents |= POLLOUT;
|
||||
if (FD_ISSET (p[i].fd, &except))
|
||||
p[i].revents |= POLLERR;
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
#if defined(TARGET_HAS_PThread)
|
||||
|
||||
#include "ovasCConfigurationFieldtrip.h"
|
||||
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <list>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
|
||||
bool CConfigurationFieldtrip::preConfigure()
|
||||
{
|
||||
const bool res = CConfigurationBuilder::preConfigure();
|
||||
|
||||
m_pHostName = GTK_WIDGET(gtk_builder_get_object(m_builder, "entry_host_name"));
|
||||
m_pHostPort = GTK_WIDGET(gtk_builder_get_object(m_builder, "spinbutton_host_port"));
|
||||
m_pMinSamples = GTK_WIDGET(gtk_builder_get_object(m_builder, "spinbutton_minSamples"));
|
||||
m_pSRCorrection = GTK_WIDGET(gtk_builder_get_object(m_builder, "checkbutton_SRCorrection"));
|
||||
|
||||
gtk_spin_button_set_range(GTK_SPIN_BUTTON(m_pMinSamples), 1.0, 10000.0);
|
||||
gtk_spin_button_set_value(GTK_SPIN_BUTTON(m_pMinSamples), m_minSamples);
|
||||
gtk_spin_button_set_value(GTK_SPIN_BUTTON(m_pHostPort), m_hostPort);
|
||||
gtk_entry_set_text(GTK_ENTRY(m_pHostName), m_hostName.toASCIIString());
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_pSRCorrection), m_srCorrection);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool CConfigurationFieldtrip::postConfigure()
|
||||
{
|
||||
if (m_applyConfig)
|
||||
{
|
||||
gtk_spin_button_update(GTK_SPIN_BUTTON(m_pMinSamples));
|
||||
gtk_spin_button_update(GTK_SPIN_BUTTON(m_pHostPort));
|
||||
|
||||
m_minSamples = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(m_pMinSamples));
|
||||
m_hostPort = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(m_pHostPort));
|
||||
m_hostName = gtk_entry_get_text(GTK_ENTRY(m_pHostName));
|
||||
m_srCorrection = (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(m_pSRCorrection)) > 0);
|
||||
}
|
||||
|
||||
return CConfigurationBuilder::postConfigure();
|
||||
}
|
||||
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
#endif //#if defined(TARGET_HAS_PThread)
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(TARGET_HAS_PThread)
|
||||
|
||||
#include "../ovasCConfigurationBuilder.h"
|
||||
|
||||
namespace OpenViBE
|
||||
{
|
||||
namespace AcquisitionServer
|
||||
{
|
||||
/**
|
||||
* \class CConfigurationFieldtrip
|
||||
* \author Amelie Serpollet (CEA/LETI/CLINATEC)
|
||||
* \date Mon May 23 09:48:21 2011
|
||||
* \brief The CDriverFieldtrip allows the acquisition server to acquire data from a Fieldtrip buffer.
|
||||
*
|
||||
*/
|
||||
class CConfigurationFieldtrip final : public CConfigurationBuilder
|
||||
{
|
||||
public:
|
||||
|
||||
CConfigurationFieldtrip(const char* gtkBuilderFilename) : CConfigurationBuilder(gtkBuilderFilename) { }
|
||||
~CConfigurationFieldtrip() override { }
|
||||
|
||||
void setHostName(const CString& hostName) { m_hostName = hostName; }
|
||||
void setHostPort(const uint32_t hostPort) { m_hostPort = hostPort; }
|
||||
void setMinSamples(const uint32_t minSamples) { m_minSamples = minSamples; }
|
||||
void setSRCorrection(const bool bSRCorrection) { m_srCorrection = bSRCorrection; }
|
||||
|
||||
CString getHostName() const { return m_hostName; }
|
||||
uint32_t getHostPort() const { return m_hostPort; }
|
||||
uint32_t getMinSamples() const { return m_minSamples; }
|
||||
bool getSRCorrection() const { return m_srCorrection; }
|
||||
|
||||
protected:
|
||||
|
||||
bool preConfigure() override;
|
||||
bool postConfigure() override;
|
||||
|
||||
private:
|
||||
|
||||
CConfigurationFieldtrip();
|
||||
|
||||
protected:
|
||||
|
||||
GtkWidget* m_pHostName = nullptr;
|
||||
GtkWidget* m_pHostPort = nullptr;
|
||||
GtkWidget* m_pMinSamples = nullptr;
|
||||
GtkWidget* m_pSRCorrection = nullptr;
|
||||
|
||||
CString m_hostName = "localhost";
|
||||
uint32_t m_hostPort = 4000;
|
||||
uint32_t m_minSamples = 1;
|
||||
bool m_srCorrection = true;
|
||||
};
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif // #if defined(TARGET_HAS_PThread)
|
||||
+533
@@ -0,0 +1,533 @@
|
||||
/* This driver uses the FieldTrip buffer open source library.
|
||||
* See http://www.ru.nl/fcdonders/fieldtrip for details.
|
||||
*/
|
||||
#if defined(TARGET_HAS_PThread)
|
||||
|
||||
#include "ovasCDriverFieldtrip.h"
|
||||
#include "ovasCConfigurationFieldtrip.h"
|
||||
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include "fieldtrip/buffer.h"
|
||||
#include "fieldtrip/extern.h"
|
||||
#include "fieldtrip/extern.c"
|
||||
#include "fieldtrip/util.c"
|
||||
#include "fieldtrip/printstruct.c"
|
||||
#include "fieldtrip/tcprequest.c"
|
||||
#include "fieldtrip/dmarequest.c"
|
||||
#include "fieldtrip/clientrequest.c"
|
||||
|
||||
#include <system/ovCTime.h>
|
||||
//#include "GetCpuTime.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace AcquisitionServer {
|
||||
|
||||
CDriverFieldtrip::CDriverFieldtrip(IDriverContext& ctx)
|
||||
: IDriver(ctx), m_settings("AcquisitionServer_Driver_FieldTrip", m_driverCtx.getConfigurationManager())
|
||||
{
|
||||
m_header.setSamplingFrequency(0);
|
||||
m_header.setChannelCount(0);
|
||||
|
||||
m_waitDataRequest = new message_t();
|
||||
m_waitDataRequest->def = new messagedef_t();
|
||||
m_waitDataRequest->buf = nullptr;
|
||||
|
||||
m_getDataRequest = new message_t();
|
||||
m_getDataRequest->def = new messagedef_t();
|
||||
m_getDataRequest->buf = nullptr;
|
||||
|
||||
m_settings.add("Header", &m_header);
|
||||
m_settings.add("MinSamples", &m_minSamples);
|
||||
m_settings.add("PortNumber", &m_portNumber);
|
||||
m_settings.add("HostName", &m_hostName);
|
||||
m_settings.add("CorrectNonIntegerSR", &m_correctNonIntegerSR);
|
||||
m_settings.load();
|
||||
}
|
||||
|
||||
CDriverFieldtrip::~CDriverFieldtrip()
|
||||
{
|
||||
if (m_waitDataRequest)
|
||||
{
|
||||
//m_waitDataRequest->buf deleted with m_waitDataRequest->def
|
||||
if (m_waitDataRequest->def) { delete m_waitDataRequest->def; }
|
||||
delete m_waitDataRequest;
|
||||
}
|
||||
|
||||
if (m_getDataRequest)
|
||||
{
|
||||
//m_getDataRequest->buf deleted with m_getDataRequest->def
|
||||
if (m_getDataRequest->def) { delete m_getDataRequest->def; }
|
||||
delete m_getDataRequest;
|
||||
}
|
||||
}
|
||||
|
||||
const char* CDriverFieldtrip::getName() { return "Fieldtrip Driver"; }
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
|
||||
bool CDriverFieldtrip::initialize(const uint32_t nSamplePerSentBlock, IDriverCallback& callback)
|
||||
{
|
||||
if (m_driverCtx.isConnected()) { return false; }
|
||||
|
||||
// ...
|
||||
// initialize hardware and get available header information
|
||||
// from it :
|
||||
|
||||
// connect to buffer
|
||||
if (m_connectionID != -1)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Already connected to Fieldtrip buffer " << m_hostName << ":" << m_portNumber << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_connectionID = open_connection(m_hostName.toASCIIString(), int(m_portNumber));
|
||||
if (m_connectionID < 0)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Failed to connect to Fieldtrip buffer :\n" << m_hostName << ":" << m_portNumber << "\n";
|
||||
m_connectionID = -1;
|
||||
return false;
|
||||
}
|
||||
else { m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Connection to Fieldtrip buffer succeeded !\n"; }
|
||||
|
||||
// request header
|
||||
if (!requestHeader())
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Request header failed, disconnecting.\n";
|
||||
if (close_connection(m_connectionID) != 0)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Failed to disconnect correctly from Fieldtrip buffer\n";
|
||||
}
|
||||
m_connectionID = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!m_header.isChannelCountSet() || !m_header.isSamplingFrequencySet()) { return false; }
|
||||
|
||||
// Builds up a buffer to store acquired samples. This buffer
|
||||
// will be sent to the acquisition server later...
|
||||
m_sample = new float[m_header.getChannelCount() * nSamplePerSentBlock];
|
||||
if (!m_sample)
|
||||
{
|
||||
delete [] m_sample;
|
||||
m_sample = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Saves parameters
|
||||
m_callback = &callback;
|
||||
m_nSamplePerSentBlock = nSamplePerSentBlock;
|
||||
|
||||
if (m_minSamples < 1) { m_minSamples = 1; }
|
||||
if (m_minSamples > m_nSamplePerSentBlock) { m_minSamples = m_nSamplePerSentBlock; }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverFieldtrip::start()
|
||||
{
|
||||
if (!m_driverCtx.isConnected() || m_driverCtx.isStarted()) { return false; }
|
||||
|
||||
// ...
|
||||
// request hardware to start
|
||||
// sending data
|
||||
// ...
|
||||
m_firstGetDataRequest = true;
|
||||
m_waitingTimeMs = (m_header.getSamplingFrequency() > 1000 ? 1 : (1000 / m_header.getSamplingFrequency())
|
||||
); //time for 1 sample if >= 1ms //(1000*m_nSamplePerSentBlock)
|
||||
m_nTotalSample = 0;
|
||||
|
||||
m_diffPerSample = (m_realSampling - m_header.getSamplingFrequency()) / m_realSampling;
|
||||
if (m_diffPerSample <= 0.0) { m_diffPerSample = 0.0; }
|
||||
m_driftSinceLastCorrection = 0.0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverFieldtrip::loop()
|
||||
{
|
||||
if (!m_driverCtx.isConnected()) { return false; }
|
||||
if (!m_driverCtx.isStarted()) { return true; }
|
||||
|
||||
CStimulationSet stimSet;
|
||||
stimSet.setStimulationCount(0);
|
||||
|
||||
// ...
|
||||
// receive samples from hardware
|
||||
// put them the correct way in the sample array
|
||||
// whether the buffer is full, send it to the acquisition server
|
||||
//...
|
||||
const int count = requestChunk(stimSet);
|
||||
if (count < 0) { return false; }
|
||||
if (count == 0) { return true; }
|
||||
m_callback->setSamples(m_sample, count);
|
||||
m_callback->setStimulationSet(stimSet);
|
||||
|
||||
m_driverCtx.correctDriftSampleCount(m_driverCtx.getSuggestedDriftCorrectionSampleCount());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverFieldtrip::stop()
|
||||
{
|
||||
if (!m_driverCtx.isConnected()) { return false; }
|
||||
if (!m_driverCtx.isStarted()) { return false; }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDriverFieldtrip::uninitialize()
|
||||
{
|
||||
if (!m_driverCtx.isConnected()) { return false; }
|
||||
if (m_driverCtx.isStarted()) { return false; }
|
||||
|
||||
if (close_connection(m_connectionID) != 0) { m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Failed to disconnect correctly from Fieldtrip buffer\n"; }
|
||||
m_connectionID = -1;
|
||||
|
||||
delete [] m_sample;
|
||||
m_sample = nullptr;
|
||||
m_callback = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
bool CDriverFieldtrip::isConfigurable()
|
||||
{
|
||||
return true; // change to false if your device is not configurable
|
||||
}
|
||||
|
||||
bool CDriverFieldtrip::configure()
|
||||
{
|
||||
CConfigurationFieldtrip config(Directories::getDataDir() + "/applications/acquisition-server/interface-Fieldtrip.ui");
|
||||
config.setMinSamples(m_minSamples);
|
||||
config.setHostPort(m_portNumber);
|
||||
config.setHostName(m_hostName);
|
||||
config.setSRCorrection(m_correctNonIntegerSR);
|
||||
|
||||
if (config.configure(m_header))
|
||||
{
|
||||
m_minSamples = config.getMinSamples();
|
||||
m_portNumber = config.getHostPort();
|
||||
m_hostName = config.getHostName();
|
||||
m_correctNonIntegerSR = config.getSRCorrection();
|
||||
|
||||
m_settings.save();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
bool CDriverFieldtrip::requestHeader()
|
||||
{
|
||||
m_waitDataRequest->def->command = GET_HDR;
|
||||
m_waitDataRequest->def->version = VERSION;
|
||||
m_waitDataRequest->def->bufsize = 0;
|
||||
m_waitDataRequest->buf = nullptr;
|
||||
|
||||
message_t* response = nullptr;
|
||||
|
||||
const int res = clientrequest(m_connectionID, m_waitDataRequest, &response);
|
||||
|
||||
if (res != 0)
|
||||
{
|
||||
FreeResponse(response, "Error while asking for header. Buffer aborted ?");
|
||||
return false;
|
||||
}
|
||||
else if (response == nullptr || response->def == nullptr)
|
||||
{
|
||||
FreeResponse(response, "Error while asking for header");
|
||||
return false;
|
||||
}
|
||||
else if (response->def->command != GET_OK || response->def->bufsize == 0)
|
||||
{
|
||||
FreeResponse(response, "No header in the buffer");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
const unsigned int size = response->def->bufsize;
|
||||
headerdef_t* headerDef = (headerdef_t*)response->buf;
|
||||
|
||||
if (size < sizeof(headerdef_t))
|
||||
{
|
||||
FreeResponse(response, "Header received has wrong format");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_header.setSamplingFrequency(uint32_t(headerDef->fsample));
|
||||
m_realSampling = headerDef->fsample;
|
||||
m_header.setChannelCount(headerDef->nchans);
|
||||
m_dataType = headerDef->data_type;
|
||||
|
||||
if (m_dataType != DATATYPE_FLOAT32 && m_dataType != DATATYPE_FLOAT64)
|
||||
{
|
||||
FreeResponse(response, "Data type is not supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (size == sizeof(headerdef_t)) //no chunk attached to the header
|
||||
{
|
||||
for (uint32_t i = 0; i < headerDef->nchans; i++) { m_header.setChannelName(i, ("Channel " + std::to_string(i)).c_str()); }
|
||||
}
|
||||
else //chunk(s) attached to the header, maybe channel names
|
||||
{
|
||||
int bytesInHeaderBuffer = headerDef->bufsize;
|
||||
void* chunk = (headerdef_t*)headerDef + 1;
|
||||
bool foundChannelNames = false;
|
||||
|
||||
while (bytesInHeaderBuffer > 0)
|
||||
{
|
||||
if (((ft_chunk_t*)chunk)->def.type == FT_CHUNK_CHANNEL_NAMES)
|
||||
{
|
||||
foundChannelNames = true;
|
||||
char* chunkdata = ((ft_chunk_t*)chunk)->data;
|
||||
for (uint32_t i = 0; i < headerDef->nchans; i++)
|
||||
{
|
||||
std::string name = chunkdata;
|
||||
m_header.setChannelName(i, name.c_str());
|
||||
chunkdata = (char*)chunkdata + name.size() + 1;
|
||||
}
|
||||
}
|
||||
|
||||
bytesInHeaderBuffer -= ((ft_chunk_t*)chunk)->def.size + sizeof(ft_chunkdef_t);
|
||||
if (bytesInHeaderBuffer > 0) { chunk = (char*)chunk + ((ft_chunk_t*)chunk)->def.size + sizeof(ft_chunkdef_t); }
|
||||
}
|
||||
|
||||
if (!foundChannelNames)
|
||||
{
|
||||
for (uint32_t i = 0; i < headerDef->nchans; i++) { m_header.setChannelName(i, ("Channel " + std::to_string(i)).c_str()); }
|
||||
}
|
||||
}
|
||||
} /* end valid header */
|
||||
|
||||
FreeResponse(response, nullptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int CDriverFieldtrip::requestChunk(CStimulationSet& oStimulationSet)
|
||||
{
|
||||
//There are two basic opertations:
|
||||
// - configure m_getDataRequest and m_waitDataRequest
|
||||
// - use m_getDataRequest to call fieldtrip clientrequest() that gets the data from fieldtrip
|
||||
// - transpose the data to fill m_sample in the correct way expected by OpenVube
|
||||
//The rest are data validity checks and correction for non-integer sampling frequency
|
||||
|
||||
//configure "wait data" request
|
||||
m_waitDataRequest->def->command = WAIT_DAT;
|
||||
m_waitDataRequest->def->version = VERSION;
|
||||
|
||||
if (m_waitDataRequest->buf == nullptr)
|
||||
{
|
||||
m_waitDataRequest->def->bufsize = 0;
|
||||
waitdef_t* waitDef = new waitdef_t();
|
||||
unsigned int requestSize = 0;
|
||||
requestSize = append((void**)&m_waitDataRequest->def, sizeof(messagedef_t), waitDef, sizeof(waitdef_t));
|
||||
m_waitDataRequest->def->bufsize = requestSize - sizeof(messagedef_t);
|
||||
m_waitDataRequest->buf = (messagedef_t*)m_waitDataRequest->def + 1;
|
||||
}
|
||||
|
||||
waitdef_t* waitDef = (waitdef_t*)m_waitDataRequest->buf;
|
||||
waitDef->threshold.nevents = 0xFFFFFFFF;
|
||||
waitDef->threshold.nsamples = m_nSamplePerSentBlock;
|
||||
waitDef->milliseconds = m_waitingTimeMs;
|
||||
|
||||
message_t* response = nullptr;
|
||||
int res = clientrequest(m_connectionID, m_waitDataRequest, &response);
|
||||
|
||||
uint32_t nDataReceived = 0;
|
||||
uint32_t nDataToSend = 0;
|
||||
|
||||
if (res)
|
||||
{
|
||||
FreeResponse(response, "Error while asking for data. Buffer aborted ?");
|
||||
return -1;
|
||||
}
|
||||
else if (response == nullptr || response->def == nullptr)
|
||||
{
|
||||
FreeResponse(response, "Error while asking for data");
|
||||
return -1;
|
||||
}
|
||||
else if (response->def->command != WAIT_OK || response->def->bufsize != 8 || response->buf == nullptr)
|
||||
{
|
||||
FreeResponse(response, "No header in buffer anymore");
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// new header received ? stop acquisition
|
||||
if (((samples_events_t*)response->buf)->nsamples < m_nTotalSample)
|
||||
{
|
||||
FreeResponse(response, "End of data");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// no new data
|
||||
if (((samples_events_t*)response->buf)->nsamples <= m_nTotalSample + m_minSamples)
|
||||
{
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "No new data\n";
|
||||
FreeResponse(response, nullptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// get data
|
||||
uint32_t lastSample = ((samples_events_t*)response->buf)->nsamples;
|
||||
if (lastSample > m_nTotalSample + m_nSamplePerSentBlock)
|
||||
{
|
||||
if (m_firstGetDataRequest) { m_nTotalSample = lastSample - m_nSamplePerSentBlock; }
|
||||
else { lastSample = m_nTotalSample + m_nSamplePerSentBlock; }
|
||||
}
|
||||
|
||||
if (m_firstGetDataRequest) { m_firstGetDataRequest = false; }
|
||||
|
||||
FreeResponse(response, nullptr); //prevents memory leak
|
||||
|
||||
// "get data" request
|
||||
m_getDataRequest->def->command = GET_DAT;
|
||||
m_getDataRequest->def->version = VERSION;
|
||||
|
||||
if (m_getDataRequest->buf == nullptr)
|
||||
{
|
||||
m_getDataRequest->def->bufsize = 0;
|
||||
datasel_t* dataSel = new datasel_t();
|
||||
unsigned int requestSize = 0;
|
||||
requestSize = append((void**)&m_getDataRequest->def, sizeof(messagedef_t), dataSel, sizeof(datasel_t));
|
||||
m_getDataRequest->def->bufsize = requestSize - sizeof(messagedef_t);
|
||||
m_getDataRequest->buf = (messagedef_t*)m_getDataRequest->def + 1;
|
||||
}
|
||||
|
||||
datasel_t* dataSel = (datasel_t*)m_getDataRequest->buf;
|
||||
dataSel->begsample = m_nTotalSample;
|
||||
dataSel->endsample = lastSample - 1;
|
||||
|
||||
//actual data acquisition
|
||||
res = clientrequest(m_connectionID, m_getDataRequest, &response);
|
||||
|
||||
if (res || !response || !response->def || response->def->version != VERSION)
|
||||
{
|
||||
FreeResponse(response, "Error while asking for data");
|
||||
return -1;
|
||||
}
|
||||
else if (response->def->command != GET_OK || response->def->bufsize == 0 || response->buf == nullptr)
|
||||
{
|
||||
FreeResponse(response, "Data are not available anymore");
|
||||
return -1;
|
||||
}
|
||||
else // data received
|
||||
{
|
||||
datadef_t* datadef = (datadef_t*)response->buf;
|
||||
void* databuf = (datadef_t*)response->buf + 1;
|
||||
if (datadef->bufsize / (wordsize_from_type(datadef->data_type) * datadef->nchans) != lastSample - m_nTotalSample)
|
||||
{
|
||||
FreeResponse(response, "Data received from buffer are invalid");
|
||||
return -1;
|
||||
}
|
||||
else // data correct
|
||||
{
|
||||
nDataReceived = lastSample - m_nTotalSample;
|
||||
|
||||
// Delete some samples if necessary.
|
||||
// Sampling rate is converted into integer in openvibe,
|
||||
// so we can have up to 1 sample too many per second.
|
||||
nDataToSend = nDataReceived;
|
||||
|
||||
if (m_correctNonIntegerSR)
|
||||
{
|
||||
m_driftSinceLastCorrection += (m_diffPerSample * nDataReceived);
|
||||
if (m_driftSinceLastCorrection >= 1.0)
|
||||
{
|
||||
// delete samples
|
||||
const uint32_t diffSamples = uint32_t(m_driftSinceLastCorrection);
|
||||
nDataToSend -= diffSamples;
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "Correction for non-integer sampling rate : " << diffSamples << " samples deleted\n";
|
||||
m_driftSinceLastCorrection -= double(diffSamples);
|
||||
//oStimulationSet.appendStimulation(OVTK_GDF_Missing, CTime(m_header.getSamplingFrequency(), nDataToSend).time(), CTime(m_header.getSamplingFrequency(), diffSamples)).time();
|
||||
}
|
||||
}
|
||||
|
||||
// set data in m_sample
|
||||
double* buffer64;
|
||||
float* buffer32;
|
||||
switch (m_dataType)
|
||||
{
|
||||
case DATATYPE_FLOAT64:
|
||||
buffer64 = (double*)databuf;
|
||||
for (size_t j = 0; j < m_header.getChannelCount(); j++)
|
||||
{
|
||||
for (uint32_t i = 0; i < nDataToSend; i++)
|
||||
{
|
||||
const double value = buffer64[i * m_header.getChannelCount() + j];
|
||||
/*if ( _isnan(value) || !_finite(value) || value==DBL_MAX )
|
||||
{
|
||||
m_sample[j*nDataToSend + i] = FLT_MAX;
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "NaN or infinite sample received.\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
//data from IHM implant are in volts, must be in uvolts in openvibe
|
||||
m_sample[j*nDataToSend + i] = (float) 1000000.0f*value;
|
||||
}*/
|
||||
m_sample[j * nDataToSend + i] = float(value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DATATYPE_FLOAT32:
|
||||
|
||||
buffer32 = (float*)databuf;
|
||||
for (size_t j = 0; j < m_header.getChannelCount(); j++)
|
||||
{
|
||||
for (uint32_t i = 0; i < nDataToSend; i++)
|
||||
{
|
||||
const float value = buffer32[i * m_header.getChannelCount() + j];
|
||||
/*if ( _isnan(value) || !_finite(value) || value==FLT_MAX )
|
||||
{
|
||||
m_sample[j*nDataToSend + i] = FLT_MAX;
|
||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Trace << "NaN or infinite sample received.\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
//data from IHM implant are in volts, must be in uvolts in openvibe
|
||||
m_sample[j*nDataToSend + i] = 1000000.0f*value;
|
||||
}*/
|
||||
m_sample[j * nDataToSend + i] = value;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
FreeResponse(response, "DEV ERROR : data type not suppported");
|
||||
return -1;
|
||||
}//end switch
|
||||
}//end data correct
|
||||
}//end data received
|
||||
|
||||
FreeResponse(response, nullptr);//we copied the data from response, so now we need to release this memory to avoid memory leak
|
||||
|
||||
m_nTotalSample = lastSample;
|
||||
}
|
||||
|
||||
return nDataToSend; // no error
|
||||
}
|
||||
|
||||
void CDriverFieldtrip::FreeResponse(message_t* response, const char* message)
|
||||
{
|
||||
if (message != nullptr) { m_driverCtx.getLogManager() << Kernel::LogLevel_Error << message << "\n"; }
|
||||
|
||||
if (response->buf) { free(response->buf); }
|
||||
if (response->def) { free(response->def); }
|
||||
free(response);
|
||||
response = nullptr;
|
||||
}
|
||||
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
#endif
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
/* This driver uses the FieldTrip buffer open source library.
|
||||
* See http://www.ru.nl/fcdonders/fieldtrip for details.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#if defined(TARGET_HAS_PThread)
|
||||
|
||||
#include "ovasIDriver.h"
|
||||
#include "../ovasCHeader.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "fieldtrip/message.h"
|
||||
|
||||
#include "../ovasCSettingsHelper.h"
|
||||
#include "../ovasCSettingsHelperOperators.h"
|
||||
|
||||
// for GET_CPU_TIME
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
namespace OpenViBE
|
||||
{
|
||||
namespace AcquisitionServer
|
||||
{
|
||||
/**
|
||||
* \class CDriverFieldtrip
|
||||
* \author Amelie Serpollet (CEA/LETI/CLINATEC)
|
||||
* \date Mon May 23 09:48:21 2011
|
||||
* \brief The CDriverFieldtrip allows the acquisition server to acquire data from a Fieldtrip buffer.
|
||||
*
|
||||
* TODO: details
|
||||
*
|
||||
*/
|
||||
class CDriverFieldtrip final : public IDriver
|
||||
{
|
||||
public:
|
||||
|
||||
CDriverFieldtrip(IDriverContext& ctx);
|
||||
~CDriverFieldtrip() override;
|
||||
const char* getName() override;
|
||||
|
||||
bool initialize(const uint32_t nSamplePerSentBlock, IDriverCallback& callback) override;
|
||||
bool uninitialize() override;
|
||||
|
||||
bool start() override;
|
||||
bool stop() override;
|
||||
bool loop() override;
|
||||
|
||||
bool isConfigurable() override;
|
||||
bool configure() override;
|
||||
const IHeader* getHeader() override { return &m_header; }
|
||||
|
||||
void FreeResponse(message_t* response, const char* message);
|
||||
|
||||
protected:
|
||||
|
||||
bool requestHeader();
|
||||
int requestChunk(CStimulationSet& oStimulationSet);
|
||||
|
||||
IDriverCallback* m_callback = nullptr;
|
||||
CHeader m_header;
|
||||
SettingsHelper m_settings;
|
||||
uint32_t m_nSamplePerSentBlock = 0;
|
||||
float* m_sample = nullptr;
|
||||
uint32_t m_dataType = DATATYPE_UNKNOWN;
|
||||
|
||||
// Connection to Fieldtrip buffer
|
||||
CString m_hostName = "localhost";
|
||||
uint32_t m_portNumber = 1979;
|
||||
int m_connectionID = -1;
|
||||
uint32_t m_minSamples = 1;
|
||||
|
||||
// Avoid frequent memory allocation
|
||||
message_t* m_waitDataRequest = nullptr;
|
||||
message_t* m_getDataRequest = nullptr;
|
||||
|
||||
uint32_t m_nTotalSample = 0;
|
||||
uint32_t m_waitingTimeMs = 0;
|
||||
|
||||
bool m_firstGetDataRequest = false;
|
||||
|
||||
bool m_correctNonIntegerSR = true; // ???
|
||||
double m_realSampling = 0;
|
||||
double m_diffPerSample = 0; // ???
|
||||
double m_driftSinceLastCorrection = 0;
|
||||
|
||||
// count time lost for "get cpu time" :
|
||||
//double m_mesureLostTime = 0;
|
||||
//uint32_t m_mesureNumber = 0;
|
||||
};
|
||||
} // namespace AcquisitionServer
|
||||
} // namespace OpenViBE
|
||||
#endif // #if defined(TARGET_HAS_PThread)
|
||||
Reference in New Issue
Block a user