RTXI  2.4
The Real-Time eXperiment Interface Documentation
io.cpp
Go to the documentation of this file.
1 /*
2  The Real-Time eXperiment Interface (RTXI)
3  Copyright (C) 2011 Georgia Institute of Technology, University of Utah, Weill Cornell Medical College
4 
5  This program is free software: you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation, either version 3 of the License, or
8  (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program. If not, see <http://www.gnu.org/licenses/>.
17 
18 */
19 
20 #include <compiler.h>
21 #include <debug.h>
22 #include <event.h>
23 #include <io.h>
24 #include <algorithm>
25 #include <sstream>
26 #include <string>
27 
28 Mutex IO::Block::mutex = Mutex(Mutex::RECURSIVE);
29 
30 IO::Block::Block(std::string n,IO::channel_t *channel,size_t size):name(n)
31 {
32  for (size_t i=0; i<inputs.size(); ++i)
33  while (inputs[i].links.size())
34  disconnect(inputs[i].links.front().block,inputs[i].links.front().channel,this,i);
35  for (size_t i=0; i<outputs.size(); ++i)
36  while (outputs[i].links.size())
37  disconnect(this,i,outputs[i].links.front().block,outputs[i].links.front().channel);
38 
39  int num_inputs = 0, num_outputs = 0;
40  for (size_t i = 0; i < size; ++i)
41  if (channel[i].flags & INPUT)
42  num_inputs++;
43  else if (channel[i].flags & OUTPUT)
44  num_outputs++;
45 
46  inputs = std::vector<struct input_t>(num_inputs);
47  outputs = std::vector<struct output_t>(num_outputs);
48 
49  size_t in = 0, out = 0;
50  for (size_t i = 0; i < size; ++i)
51  if (channel[i].flags & INPUT)
52  {
53  inputs[in].name = channel[i].name;
54  inputs[in++].description = channel[i].description;
55  }
56  else if (channel[i].flags & OUTPUT)
57  {
58  outputs[out].name = channel[i].name;
59  outputs[out].description = channel[i].description;
60  outputs[out++].value = 0.0;
61  }
62 
63  IO::Connector::getInstance()->insertBlock(this);
64 }
65 
67 {
68  IO::Connector::getInstance()->removeBlock(this);
69 
70  Mutex::Locker lock(&mutex);
71  for (size_t i=0; i<inputs.size(); ++i)
72  while (inputs[i].links.size())
73  disconnect(inputs[i].links.front().block,inputs[i].links.front().channel,this,i);
74  for (size_t i=0; i<outputs.size(); ++i)
75  while (outputs[i].links.size())
76  disconnect(this,i,outputs[i].links.front().block,outputs[i].links.front().channel);
77 
78  inputs = std::vector<struct input_t>();
79  outputs = std::vector<struct output_t>();
80 }
81 
82 size_t IO::Block::getCount(IO::flags_t type) const
83 {
84  if (type & INPUT)
85  return inputs.size();
86  if (type & OUTPUT)
87  return outputs.size();
88  return 0;
89 }
90 
91 std::string IO::Block::getName(IO::flags_t type,size_t n) const
92 {
93  if (type & INPUT && n < inputs.size())
94  return inputs[n].name;
95  if (type & OUTPUT && n < outputs.size())
96  return outputs[n].name;
97  return "";
98 }
99 
100 std::string IO::Block::getDescription(IO::flags_t type,size_t n) const
101 {
102  if (type & INPUT && n < inputs.size())
103  return inputs[n].description;
104  if (type & OUTPUT && n < outputs.size())
105  return outputs[n].description;
106  return "";
107 }
108 
109 double IO::Block::getValue(IO::flags_t type,size_t n) const
110 {
111  if (type & INPUT)
112  return input(n);
113  if (type & OUTPUT)
114  return output(n);
115  return 0.0;
116 }
117 
118 double IO::Block::input(size_t n) const
119 {
120  if (unlikely(n >= inputs.size()))
121  return 0.0;
122 
123  double v = 0.0;
124  for (std::list<struct link_t>::const_iterator i = inputs[n].links.begin(),end = inputs[n].links.end(); i != end; ++i)
125  v += i->block->output(i->channel);
126  return v;
127 }
128 
129 double IO::Block::output(size_t n) const
130 {
131  if (unlikely(n >= outputs.size()))
132  return 0.0;
133  return outputs[n].value;
134 }
135 
136 double IO::Block::yogi = 0.0;
137 
138 double &IO::Block::output(size_t n)
139 {
140 
141  /*********************************************************
142  * This seems kinda sketchy, but we have to return *
143  * something if the user requests the wrong channel... *
144  *********************************************************/
145 
146  if (unlikely(n >= outputs.size()))
147  return yogi = 0.0;
148  return outputs[n].value;
149 }
150 
151 void IO::Block::connect(IO::Block *src,size_t src_num,IO::Block *dest,size_t dest_num)
152 {
153  Mutex::Locker lock(&mutex);
154 
155  if (!src)
156  {
157  ERROR_MSG("Block::connect : invalid source\n");
158  return;
159  }
160  if (src_num >= src->outputs.size())
161  {
162  ERROR_MSG("Block::connect : invalid source channel\n");
163  return;
164  }
165 
166  if (!dest)
167  {
168  ERROR_MSG("Block::connect : invalid destination\n");
169  return;
170  }
171  if (dest_num >= dest->inputs.size())
172  {
173  ERROR_MSG("Block::connect : invalid destination channel\n");
174  return;
175  }
176 
177  // Check if the connection exists
178  for (std::list<struct link_t>::const_iterator i=src->outputs[src_num].links.begin(); i != src->outputs[src_num].links.end(); ++i)
179  if (i->block == dest && i->channel == dest_num)
180  {
181  ERROR_MSG("Block::connect : connection exists\n");
182  return;
183  }
184 
185  struct link_t link;
186 
187  // Make the forward connection (src => dest)
188  link.block = dest;
189  link.channel = dest_num;
190  src->outputs[src_num].links.push_back(link);
191 
192  // Make the backward connection (src <= dest)
193  link.block = src;
194  link.channel = src_num;
195  dest->inputs[dest_num].links.push_back(link);
196 
198  event.setParam("src",src);
199  event.setParam("src_num",&src_num);
200  event.setParam("dest",dest);
201  event.setParam("dest_num",&dest_num);
203 }
204 
205 void IO::Block::disconnect(IO::Block *src,size_t src_num,IO::Block *dest,size_t dest_num)
206 {
207  Mutex::Locker lock(&mutex);
208 
209  if (!src)
210  {
211  ERROR_MSG("Block::disconnect : invalid source\n");
212  return;
213  }
214  if (src_num >= src->outputs.size())
215  {
216  ERROR_MSG("Block::disconnect : invalid source channel\n");
217  return;
218  }
219 
220  if (!dest)
221  {
222  ERROR_MSG("Block::disconnect : invalid destination\n");
223  return;
224  }
225  if (dest_num >= dest->inputs.size())
226  {
227  ERROR_MSG("Block::disconnect : invalid destination channel\n");
228  return;
229  }
230 
232  event.setParam("src",src);
233  event.setParam("src_num",&src_num);
234  event.setParam("dest",dest);
235  event.setParam("dest_num",&dest_num);
237 
238  // Remove the forward connection (src => dest)
239 start_forward_remove:
240  for (std::list<struct link_t>::iterator i=src->outputs[src_num].links.begin(); i != src->outputs[src_num].links.end(); ++i)
241  if (i->block == dest && i->channel == dest_num)
242  {
243  src->outputs[src_num].links.erase(i);
244  goto start_forward_remove;
245  }
246 
247  // Remove the backward connection (src <= dest)
248 start_backward_remove:
249  for (std::list<struct link_t>::iterator i=dest->inputs[dest_num].links.begin(); i != dest->inputs[dest_num].links.end(); ++i)
250  if (i->block == src && i->channel == src_num)
251  {
252  dest->inputs[dest_num].links.erase(i);
253  goto start_backward_remove;
254  }
255 }
256 
257 void IO::Connector::foreachBlock(void (*callback)(Block *,void *),void *param)
258 {
259  Mutex::Locker lock(&mutex);
260  for (std::list<Block *>::iterator i = blockList.begin(); i != blockList.end(); ++i)
261  callback(*i,param);
262 }
263 
264 void IO::Connector::foreachConnection(void (*callback)(Block *,size_t,Block *,size_t,void *),void *param)
265 {
266  Mutex::Locker lock(&mutex);
267 
268  for (std::list<Block *>::iterator i = blockList.begin(), iend = blockList.end(); i != iend; ++i)
269  for (size_t j = 0, jend = (*i)->outputs.size(); j < jend; ++j)
270  for (std::list<Block::link_t>::iterator k = (*i)->outputs[j].links.begin(), kend = (*i)->outputs[j].links.end(); k != kend; ++k)
271  callback(*i,j,k->block,k->channel,param);
272 }
273 
274 void IO::Connector::connect(IO::Block *src,size_t out,IO::Block *dest,size_t in)
275 {
276  Mutex::Locker lock(&mutex);
277 
278  if (!src)
279  {
280  ERROR_MSG("IO::Connector::connect : invalid output block\n");
281  return;
282  }
283 
284  if (!dest)
285  {
286  ERROR_MSG("IO::Connector::connect : invalid input block\n");
287  return;
288  }
289 
290  IO::Block::connect(src,out,dest,in);
291 }
292 
293 void IO::Connector::disconnect(IO::Block *src,size_t out,IO::Block *dest,size_t in)
294 {
295  Mutex::Locker lock(&mutex);
296 
297  if (!src)
298  {
299  ERROR_MSG("IO::Connector::disconnect : invalid output block\n");
300  return;
301  }
302 
303  if (!dest)
304  {
305  ERROR_MSG("IO::Connector::disconnect : invalid input block\n");
306  return;
307  }
308 
309  IO::Block::disconnect(src,out,dest,in);
310 }
311 
312 bool IO::Connector::connected(IO::Block *src,size_t src_num,IO::Block *dest,size_t dest_num)
313 {
314  if (!src)
315  {
316  ERROR_MSG("Block::connected : invalid source\n");
317  return false;
318  }
319  if (src_num >= src->outputs.size())
320  {
321  ERROR_MSG("Block::connected : invalid source channel\n");
322  return false;
323  }
324 
325  if (!dest)
326  {
327  ERROR_MSG("Block::connected : invalid destination\n");
328  return false;
329  }
330  if (dest_num >= dest->inputs.size())
331  {
332  ERROR_MSG("Block::connected : invalid destination channel\n");
333  return false;
334  }
335 
336  for (std::list<IO::Block::link_t>::iterator i=src->outputs[src_num].links.begin(); i != src->outputs[src_num].links.end(); ++i)
337  if (i->block == dest && i->channel == dest_num)
338  return true;
339 
340  return false;
341 }
342 
343 //void IO::Connector::doDeferred(const Settings::Object::State &s)
344 //{
345 // Block *src, *dest;
346 //
347 // for (size_t i = 0,end = s.loadInteger("Num Links"); i < end; ++i)
348 // {
349 // std::ostringstream str;
350 // str << i;
351 //
352 // src = dynamic_cast<Block *>(Settings::Manager::getInstance()->getObject(s.loadInteger(str.str()+" Source ID")));
353 // dest = dynamic_cast<Block *>(Settings::Manager::getInstance()->getObject(s.loadInteger(str.str()+" Destination ID")));
354 // if (src && dest)
355 // connect(src,s.loadInteger(str.str()+" Source channel"),
356 // dest,s.loadInteger(str.str()+" Destination channel"));
357 // }
358 //}
359 //
360 //void IO::Connector::doSave(Settings::Object::State &s) const
361 //{
362 // size_t n = 0;
363 //
364 // for (std::list<Block *>::const_iterator i = blockList.begin(),iend = blockList.end(); i != iend; ++i)
365 // for (size_t j = 0,jend = (*i)->getCount(INPUT); j < jend; ++j)
366 // for (std::list<Block::link_t>::const_iterator k = (*i)->inputs[j].links.begin(),kend = (*i)->inputs[j].links.end(); k != kend; ++k)
367 // {
368 // std::ostringstream str;
369 // str << n++;
370 // s.saveInteger(str.str()+" Source ID",k->block->getID());
371 // s.saveInteger(str.str()+" Source channel",k->channel);
372 // s.saveInteger(str.str()+" Destination ID",(*i)->getID());
373 // s.saveInteger(str.str()+" Destination channel",j);
374 // }
375 // s.saveInteger("Num Links",n);
376 //}
377 
378 void IO::Connector::insertBlock(IO::Block *block)
379 {
380  if (!block)
381  {
382  ERROR_MSG("IO::Connector::insertBlock : invalid block\n");
383  return;
384  }
385 
386  Mutex::Locker lock(&mutex);
387 
388  if (std::find(blockList.begin(),blockList.end(),block) != blockList.end())
389  {
390  ERROR_MSG("IO::Connector::insertBlock : block already present\n");
391  return;
392  }
393 
395  event.setParam("block",block);
397 
398  blockList.push_back(block);
399 }
400 
401 void IO::Connector::removeBlock(IO::Block *block)
402 {
403  if (!block)
404  {
405  ERROR_MSG("IO::Connector::insertBlock : invalid block\n");
406  return;
407  }
408 
409  Mutex::Locker lock(&mutex);
410 
412  event.setParam("block",block);
414 
415  blockList.remove(block);
416 }
417 
418 static Mutex mutex;
419 IO::Connector *IO::Connector::instance = 0;
420 
422 {
423  if (instance)
424  return instance;
425 
426  /*************************************************************************
427  * Seems like alot of hoops to jump through, but static allocation isn't *
428  * thread-safe. So effort must be taken to ensure mutual exclusion. *
429  *************************************************************************/
430 
431  Mutex::Locker lock(&::mutex);
432  if (!instance)
433  {
434  static Connector connector;
435  instance = &connector;
436  }
437 
438  return instance;
439 }
IO::Connector::disconnect
void disconnect(IO::Block *outputBlock, size_t outputChannel, IO::Block *inputBlock, size_t inputChannel)
Definition: io.cpp:293
Event::Manager::postEvent
void postEvent(const Object *event)
Definition: event.cpp:110
Connector
Definition: connector.h:33
IO::Connector::connect
void connect(IO::Block *outputBlock, size_t outputChannel, IO::Block *inputBlock, size_t inputChannel)
Definition: io.cpp:274
IO::Connector
Definition: io.h:71
IO::channel_t::description
std::string description
Definition: io.h:59
Mutex::RECURSIVE
@ RECURSIVE
Definition: mutex.h:59
ERROR_MSG
void ERROR_MSG(const std::string &errmsg,...)
Definition: debug.cpp:27
unlikely
#define unlikely(x)
Definition: compiler.h:24
IO::flags_t
unsigned long flags_t
Definition: io.h:40
IO::Connector::getInstance
static Connector * getInstance(void)
Definition: io.cpp:421
Event::Manager::getInstance
static Manager * getInstance(void)
Definition: event.cpp:149
Event::IO_BLOCK_INSERT_EVENT
const char * IO_BLOCK_INSERT_EVENT
Definition: event.cpp:31
IO::Block::~Block
virtual ~Block(void)
Definition: io.cpp:66
IO::Block::getValue
virtual double getValue(flags_t type, size_t index) const
Definition: io.cpp:109
IO::Connector::connected
bool connected(IO::Block *outputBlock, size_t outputChannel, IO::Block *inputBlock, size_t inputChannel)
Definition: io.cpp:312
IO::Block::getCount
virtual size_t getCount(flags_t type) const
Definition: io.cpp:82
IO::Block::getName
std::string getName(void) const
Definition: io.h:215
Mutex
Definition: mutex.h:28
IO::Block::Block
Block(std::string name, channel_t *channels, size_t size)
Definition: io.cpp:30
IO::Block::output
double output(size_t index) const
Definition: io.cpp:129
IO::Block
Definition: io.h:188
compiler.h
event.h
IO::Connector::foreachBlock
void foreachBlock(void(*callback)(Block *, void *), void *param)
Definition: io.cpp:257
IO::channel_t
Definition: io.h:56
Event::IO_LINK_REMOVE_EVENT
const char * IO_LINK_REMOVE_EVENT
Definition: event.cpp:34
IO::Block::input
double input(size_t index) const
Definition: io.cpp:118
IO::Block::getDescription
virtual std::string getDescription(flags_t type, size_t index) const
Definition: io.cpp:100
Mutex::Locker
Definition: mutex.h:36
io.h
Event::IO_BLOCK_REMOVE_EVENT
const char * IO_BLOCK_REMOVE_EVENT
Definition: event.cpp:32
IO::channel_t::name
std::string name
Definition: io.h:58
IO::Connector::foreachConnection
void foreachConnection(void(*callback)(Block *, size_t, Block *, size_t, void *), void *param)
Definition: io.cpp:264
Event::Object
Definition: event.h:128
Event::IO_LINK_INSERT_EVENT
const char * IO_LINK_INSERT_EVENT
Definition: event.cpp:33
debug.h