iop_simplenet.h
Go to the documentation of this file.
1/** @file iop_simplenet.h Simple networked events via TCP/IP */
2
3/*
4 FAU Discrete Event Systems Library (libfaudes)
5
6 Copyright (C) 2008, 2024 Thomas Moor
7 Exclusive copyright is granted to Klaus Schmidt
8
9*/
10
11
12
13#ifndef FAUDES_IOP_SIMPLENET_H
14#define FAUDES_IOP_SIMPLENET_H
15
16#include "corefaudes.h"
17#include "iop_vdevice.h"
18
19
20namespace faudes {
21
22
23/**
24 * Simplenet node address.
25 *
26 * A node address consists of an IP address and a TCP port.
27 * This convenience class implements string based access to both components.
28 *
29 */
30
32
33public:
34
35 /** Default constructor */
36 SimplenetAddress(void);
37
38 /** Copy construct */
40
41 /** Construct from string */
42 SimplenetAddress(const std::string& rString);
43
44 /** Return true if valid */
45 bool Valid(void) const;
46
47 /** Get IP address */
48 std::string Ip(void) const { return mIp; };
49
50 /** Get TCP port */
51 int Port(void) const { return mPort; };
52
53 /** Get as colon seperated string */
54 std::string IpColonPort(void) const;
55
56 /** Set IP address */
57 void Ip(std::string ip) { mIp=ip; };
58
59 /** Set TCP port */
60 void Port(int port) { mPort=port; };
61
62 /** Set from colon seperated string */
63 void IpColonPort(std::string ipcolonport);
64
65 /** Order for sorting containers of addresses */
66 bool operator < (const SimplenetAddress& rOther) const;
67
68 protected:
69
70 /** Ip address */
71 std::string mIp;
72
73 /** TCP port */
74 int mPort;
75
76};
77
78
79
80// only compile for simplenet support
81#ifdef FAUDES_IODEVICE_SIMPLENET
82
83
84
85
86/**
87 * Configuration of a network output mapping.
88 *
89 * There is nothing to configure here: any client that
90 * connects to this device can subscribe to any output event.
91 * Future versions of libFAUDES may specify a set of receipent
92 * clients.
93 *
94 */
95
97
99
100public:
101
102 /** Default constructor */
104
105 /** Test for default value (always, since we have no data) */
106 virtual bool IsDefault(void) const {return true;};
107
108 /** Clear to default */
109 virtual void Clear(void) {};
110
111protected:
112
113 /** Copy method
114 *
115 * @param rSrcAttr
116 * Source to copy from
117 */
118 void DoAssign(const AttributeSimplenetOutput& rSrcAttr)
119 {(void) rSrcAttr;};
120
121 /**
122 * Reads the attribute from TokenReader, see AttributeVoid for public wrappers.
123 *
124 * If the current token indicates a input mapping, the method reads that
125 * section. Else it does nothing. Exceptions may only be thrown
126 * on invalid data within the section. The label argument is ignored, we use the
127 * hardcoded section "Output" for output attributes. The context argument is ignored.
128 *
129 * @param rTr
130 * TokenReader to read from
131 * @param rLabel
132 * Section to read
133 * @param pContext
134 * Read context to provide contextual information
135 *
136 * @exception Exception
137 * - IO error (id 1)
138 */
139 virtual void DoRead(TokenReader& rTr,const std::string& rLabel="", const Type* pContext=0);
140
141 /**
142 * Writes the attribute to TokenWriter, see AttributeVoid for public wrappers.
143 *
144 * Writes the output mapping data. The label argument is ignored, we use
145 * the hardcoded section "Output". The context argument is ignored.
146 *
147 * @param rTw
148 * TokenWriter to write to
149 * @param rLabel
150 * Section to write
151 * @param pContext
152 * Read context to provide contextual information
153 *
154 * @exception Exception
155 * - IO error (id 2)
156 */
157 virtual void DoWrite(TokenWriter& rTw,const std::string& rLabel="", const Type* pContext=0) const;
158
159}; // end class AttributeSimplenetOutput
160
161
162/**
163 * Configuration of a network input mapping.
164 *
165 * There is nothing to configure here: any server that
166 * provides the repective event will be accepted. Future versions
167 * of libFAUDES may specify a set of servers here.
168 *
169 */
170
172
174
175public:
176
177 /** Default constructor (no triggers) */
179
180 /** Test for default value (always, since we have no data) */
181 virtual bool IsDefault(void) const {return true;};
182
183 /** Clear to default */
184 virtual void Clear(void) {};
185
186protected:
187
188 /** Copy method
189 *
190 * @param rSrcAttr
191 * Source to copy from
192 */
193 void DoAssign(const AttributeSimplenetInput& rSrcAttr)
194 {(void) rSrcAttr;};
195
196
197 /**
198 * Reads the attribute from TokenReader, see AttributeVoid for public wrappers.
199 *
200 * If the current token indicates a input mapping, the method reads that
201 * section. Else it does nothing. Exceptions may only be thrown
202 * on invalid data within the section. The label argument is ignored, we the hardcoded
203 * section "Input" for input device attributes. The context argument is ignored.
204 *
205 * @param rTr
206 * TokenReader to read from
207 * @param rLabel
208 * Section to read
209 * @param pContext
210 * Read context to provide contextual information
211 *
212 * @exception Exception
213 * - IO error (id 1)
214 */
215 virtual void DoRead(TokenReader& rTr,const std::string& rLabel="", const Type* pContext=0);
216
217 /**
218 * Writes the attribute to TokenWriter, see AttributeVoid for public wrappers.
219 *
220 * Writes the input mapping data.The label argument is ignored, we use
221 * the hardcoded section "Input". The context argument is ignored.
222 *
223 * @param rTw
224 * TokenWriter to write to
225 * @param rLabel
226 * Section to write
227 * @param pContext
228 * Read context to provide contextual information
229 *
230 * @exception Exception
231 * - IO error (id 2)
232 */
233 virtual void DoWrite(TokenWriter& rTw,const std::string& rLabel="", const Type* pContext=0) const;
234
235}; // end class AttributeSimplenetInput
236
237
238
239/**
240 * Configuration of a networked input or output
241 *
242 * This class is derived from the AttributeDeviceEvent to specialise
243 * for networked input and output mapping.
244 *
245 */
246
248
250
251public:
252
253 /** Default constructor (no mapping at all) */
255
256 /** Copy constructor */
258
259 /** Test for default value (never) */
260 virtual bool IsDefault(void) const {return false;};
261
262 /** Clear */
263 virtual void Clear(void) { AttributeDeviceEvent::Clear(); };
264
265 /** Get output mapping */
267 return static_cast<AttributeSimplenetOutput*>(mpOutputAttribute); };
268
269 /** Get input mapping */
270 const AttributeSimplenetInput* Inputp(void) const {
271 return static_cast<AttributeSimplenetInput*>(mpInputAttribute); };
272
273protected:
274
275 /** DoAssign */
277 { AttributeDeviceEvent::DoAssign(rSrc);};
278
279 /** Prototype, input (construct on first use static) */
280 static const AttributeSimplenetInput* InputPrototypep(void);
281
282 /** Prototype, output (construct on first use static) */
283 static const AttributeSimplenetOutput* OutputPrototypep(void);
284
285}; // class AttributeSimplenetEvent
286
287
288/**
289 * An nDevice implements networked IO via a simple TCP/IP protocol.
290 *
291 * @section SecIodeviceNDev1 Networking
292 *
293 * The purpose of this implementation of networked discrete events is to provide
294 * a basis for case studies. However, simple networked events via nDevice must not
295 * be confused with ongoing research that addresses synchronisation and real
296 * time behaviour. We take a really pragmatic approach here.
297 *
298 * Our <em>network</em> is identified by its <em>network name</em>. It consists
299 * a number of <em>nodes</em>, each identified by its <em>node name</em>. In the current implementation,
300 * each node knows about the entire network ie knows about all other nodes. Each node plays two roles:
301 * - the <em>server</em> role to send event notifications, and
302 * - the <em>client</em> role to receive event notifications.
303 *
304 * In their server role, each node is configured to listen on its <em>server port</em> for incomming
305 * TCP connections. When a client (i.e. some other node in client role) connects to the TCP port, the server
306 * replies to a simple command set in order to allow the client to subscribe to the nodes output events.
307 * When the application executes an output event on the node, this is notified to all connected clients and
308 * the clients will interpret the event as an input reading. Vice versa, the each node itself has the
309 * role of a client and subscribes to relevant output events served by other nodes in their server role.
310 *
311 * @section SecIodeviceNDev2 Protocol Details
312 *
313 * The protocol for commands and notification is libFAUDES-token based and can be inspected eg via nc
314 * or telnet; see the shell script in the tutorial.
315 * Event notifications are sent spontaneous to all connected clients in the format
316 * <tt><Notify> Ev </Notify></tt>.
317 * Commands are accepted in the format <tt><Cmd> Command </Cmd></tt> and will be
318 * answered accordingly.
319 *
320 * <table valign="top">
321 * <tr>
322 * <td> Command via TCP</td>
323 * <td> Reply </td>
324 * </tr>
325 * <tr>
326 * <td> <tt><Cmd> Info </Cmd></tt> </td>
327 * <td> configuration of node as token string (<tt><SimplenetDevice name="SimpleMachine"> ...
328 * </SimplenetDevice> </tt> ) </td>
329 * </tr>
330 * <tr>
331 * <td> <tt><Cmd> Status </Cmd></tt> </td>
332 * <td> acknowledgement with status string (<tt><Ack> Up </Ack></tt>,
333 * <tt><Ack> StartUp </Ack></tt> or <tt><Ack> ShutDown </Ack></tt>, respectively;
334 * will not connect/reply while Down) </td>
335 * </tr>
336 * <tr>
337 * <td> <tt><Cmd> ResetRequest </Cmd></tt> </td>
338 * <td> no acknowledgement </td>
339 * </tr>
340 * <tr>
341 * <td> <tt><Subscribe> Ev_1 ... Ev_N </Subscribe></tt> </td>
342 * <td> subset of available events (e.g. <tt><Subscribed> Ev_2 Ev_7 Ev_9 </Subscribed></tt>)</td>
343 * </tr>
344 * </table>
345 *
346 * A minimal alternative implementation for a node consists of (1) a TCP server that ignores all
347 * incomming messages and issues event notifications to any relevant events; and, (2) a TCP client
348 * that subscribes to all events and then listens to event notifications. All other commands are
349 * optional and may change in future revisions of this protocol.
350 *
351 * @section SecIodeviceNDev3 Name Resolution
352 *
353 * On the technical side, each node needs to figure the IP addresses incl TCP ports on which the
354 * other nodes provide their service. To ease configuration, this information is distributed by
355 * UDP datagrams. In order to locate the other nodes within the network, a node may broadcasts a
356 * network <em>request</em> datagramm. Each node that receives such a request, replies with an
357 * <em>advert</em> datagramm to provide its address. Thus, the simple net nodes rely on some
358 * underlying name resolution by DNS services, but node configuration itself refers to simple-net node names
359 * only. Since each node knows about the names of all participating nodes, each node will know when
360 * all connections are up.
361 *
362 * By default, UDP broadcasts are addressed to 255.255.255.255:40000. Since
363 * routers are not meant to pass-on broadcasts, nDevice name resolution is restricted to
364 * one subnet. If the local host is connected to multiple subnets, you need to specify the
365 * relevant subset explicitly by setting the appropriate broadcast address, e.g. 192.168.2.255:40000.
366 * To restrict networks to the local machine, set the broadcast address to the loopback device
367 * 127.0.0.1:40000. To span a network accross multiple subnets, server addresses can be
368 * explicitly specified as an attribute in the node configuration;
369 * e.g. <tt> <Node> name=\"SimpleSupervisor\" address=\"192.168.2.1:40000\"</Node></tt>.
370 * This is also the preferred
371 * fallback when address resolution fails for other reasons.
372 *
373 * <table valign="top">
374 * <tr>
375 * <td> Broadcast via UDP</td>
376 * <td> Reply </td>
377 * </tr>
378 * <tr>
379 * <td> <tt><Request> network reqnode </Request></tt> </td>
380 * <td> advertisement of networkname, nodename and ip-address:port, e.g.,
381 * <tt> <Advert> SimpleLoop SimpleSupervisor 192.168.2.1:40000 </Advert></tt>
382 * </td>
383 * </tr>
384 * </table>
385 * <p>
386 *
387 * @section SecIodeviceNDev4 File IO
388 *
389 * For token IO, the nDevice reads and writes a section with label
390 * "SimplenetDevice". There are no relevant attributes yet. Simple machine example:
391 *
392 * @code
393 * <SimplenetDevice name="SimpleMachine">
394 *
395 * <!-- Time scale in ms/ftiu -->
396 * <TimeScale value="1000"/>
397 *
398 * <!-- IP address of this node, incl. server TCP port -->
399 * <ServerAddress value="localhost:40000"/>
400
401 * <!-- Broadcaset address for node resolution (optional)
402 * <BroadcastAddress value="255.255.255.255:40000"/>
403 *
404 * <!-- Network topology -->
405 * <Network name="SimpleLoop">
406 * <Node name="SimpleMachine"/>
407 * <Node name="SimpleSupervisor"/>
408 * </Network>
409 *
410 * <!-- Event configuration -->
411 * <EventConfiguration>
412 * <Event name="alpha" iotype="input"/>
413 * <Event name="beta" iotype="output"/>
414 * <Event name="mue" iotype="output"/>
415 * <Event name="lambda" iotype="input"/>
416 * </EventConfiguration>
417 *
418 * </SimplenetDevice>
419 * @endcode
420 *
421 * @section SecIodeviceNDev5 Implementation Notes
422 *
423 * The current status of the code is premature; network io
424 * assumes reasonably large buffers; thread/select mechanism is inefficient;
425 * exception handling wont work; etc etc
426 *
427 *
428 * @ingroup IODevicePlugin
429 */
430
431class FAUDES_API nDevice : public vDevice {
432
434
435 // provide all access to background tasks
436 friend void* NDeviceListen(void*);
437 friend void* NDeviceServer(void*);
438 friend void* NDeviceReply(void*);
439
440 public:
441 /**
442 * Default constructor
443 */
444 nDevice(void);
445
446 /**
447 * Explicit destructor.
448 */
449 virtual ~nDevice(void);
450
451 /**
452 * Clear all configuration.
453 * This implies Stop().
454 */
455 virtual void Clear(void);
456
457 /**
458 * Set server address of this node.
459 * Note: you can only set th server address while the
460 * device is down.
461 *
462 * @param rAddr
463 * Address on which to run this server, e.g. "localhost:40000"
464 * @exception Exception
465 * - No valid address (id 551) (NOT IMPLEMENTED)
466 */
467 void ServerAddress(const std::string& rAddr);
468
469 /**
470 * Set broadcast address for address resolution
471 * Note: you can only set the broadcast address while the
472 * device is down.
473 *
474 * @param rAddr
475 * Address for UDP broadcasts e.g. "255.255.255.255:40000"
476 * @exception Exception
477 * - No valid address (id 551) (NOT IMPLEMENTED)
478 */
479 void BroadcastAddress(const std::string& rAddr);
480
481 /**
482 * Set network name to participate.
483 * Note: you can only set the network name
484 * while the device is down.
485 *
486 * @param rNetwork
487 * Name of network, e.g. "ElevatorNetwork"
488 */
489 void NetworkName(const std::string& rNetwork);
490
491 /**
492 * Add a node to the network configuration.
493 * Note: you can only configure the network
494 * while the device is down.
495 *
496 * @param rNodeName
497 * Node to participate in wthe network.
498 */
499 void InsNode(const std::string& rNodeName);
500
501 /**
502 * Add entry to node name resolution
503 *
504 * Note: you can only configure the network
505 * while the device is down.
506 * @param rNode
507 * Name of node to register
508 * @param rAddress
509 * Address incl port
510 */
511 void InsNodeAddress(const std::string& rNode, const std::string& rAddress);
512
513
514 /**
515 * Add a node to the network configuration.
516 * Note: you can only configure the network
517 * while the device is down.
518 *
519 */
520 void ClearNodes(void);
521
522 /**
523 * Insert event as input event.
524 * Note: you can only configure events
525 * while the device is down.
526 *
527 * @param event
528 * Event by name.
529 */
530 void InsInputEvent(const std::string& event);
531
532
533 /**
534 * Insert event as output event.
535 * Note: you can only configure event
536 * while the device is down.
537 *
538 * @param event
539 * Event by name.
540 */
541 void InsOutputEvent(const std::string& event);
542
543
544 /**
545 * Set up internal data structures
546 *
547 */
548 virtual void Compile(void);
549
550
551 /**
552 * Activate the device. This function enables output execution and input reading.
553 * It starts the background thread for incomming connections and tries to connect to relevant servers.
554 *
555 * @exception Exception
556 * - Not yet configured (id 551)
557 * - Fatal network error (id 553)
558 * - Fatal thread error (id 554)
559 */
560 virtual void Start(void);
561
562 /**
563 * Deactivate the device. This function disables output execution and input reading.
564 * It stops the backhround thread to provide connections and disconnects from any servers.
565 */
566 virtual void Stop(void);
567
568 /**
569 * Reset device. Resets buffered input events and current time.
570 *
571 */
572 virtual void Reset(void);
573
574 /**
575 * Run output command.
576 *
577 * @exception Exception
578 * - unknown output event (id 65)
579 */
580 virtual void WriteOutput(Idx output);
581
582
583
584protected:
585
586 /**
587 * Actual method to read device configuration from tokenreader.
588 *
589 * This method calls the base class to read the device name and the
590 * the timescale. It then reads address, networkid and nodes.
591 * Overall device configuration is consists of DoReadPreface,
592 * DoReadConfiguration and Compile. It isimplemented in vDevice.
593 * The label and context parameters are ignored.
594 *
595 * @param rTr
596 * TokenReader to read from
597 * @param rLabel
598 * Section to read
599 * @param pContext
600 * Read context to provide contextual information
601 *
602 * @exception Exception
603 * - IO error (id 1)
604 */
605 virtual void DoReadPreface(TokenReader& rTr, const std::string& rLabel = "", const Type* pContext=0);
606
607 /**
608 * Actual method to write the device configuration to a TokenWriter.
609 *
610 * This method calls the base class vDevice to write the device name and
611 * the time scale. It then writes network related data. The label and
612 * context parameters are ignored.
613 *
614 * @param rTw
615 * Reference to TokenWriter
616 * @param rLabel
617 * Label of section to write
618 * @param pContext
619 * Read context to provide contextual information
620 * @exception Exception
621 * - IO errors (id 2)
622 */
623 virtual void DoWritePreface(TokenWriter& rTw, const std::string& rLabel="", const Type* pContext=0) const;
624
625
626 /** Overall configuration (with actual type) */
628
629 /** Simplenet: network id */
630 std::string mNetwork;
631
632 /** Simplenet: address of my server incl port (localhost:40000)*/
634
635 /** Simplenet: address for udp broadcast (255.255.255.255:40000*/
637
638 /** Simplenet: effective address of my server port */
640
641 /** Simplenet: list of nodes in this network incl default addresses*/
642 std::map<std::string,std::string> mNetworkNodes;
643
644 /** Compiled data: map subscriptions */
645 std::map<std::string,EventSet> mInputSubscriptions;
646
647 /** Background: mutex for below shared variables*/
648 faudes_mutex_t mMutex;
649
650 /** Background: thread handle (global) */
651 faudes_thread_t mThreadListen;
652
653 /** Background: request to join via flag (mutexed) */
655
656 /** Background: server socket to listen (background only) */
658
659 /** Background: udp broadcast socket (background only) */
661
662 /** Background: state of a connection to a client (shared) */
663 typedef struct {
664 int mClientSocket; // the socket the client is connected to
665 EventSet mEvents; // events the client has subscribed to
666 bool mConnected; // set to true, if a subscription has been seen
667 std::string mLineBuffer; // buffer to receive one line
668 } ClientState;
669
670 /** Background: map sockets to connection states (shared) */
671 std::map<int, ClientState> mOutputClientStates;
672
673 /** Background: state of a connection to an event server (shared)*/
674 typedef struct {
675 SimplenetAddress mAddress; // actual IP address incl TCP port of remote server
676 SimplenetAddress mDefaultAddress; // default IP address incl TCP port of remote server
677 EventSet mEvents; // events we have subscribed to
678 int mServerSocket; // socket used to connect with provider
679 std::string mLineBuffer; // buffer to receive one line
680 } ServerState;
681
682 /** Background: connection states to event servers (by node name)*/
683 std::map<std::string,ServerState> mInputServerStates;
684
685
686
687}; // end class nDevice
688
689// declare background threads
690void* NDeviceListen(void*);
691
692#endif // configured for simplenet
693
694
695
696} // namespace faudes
697
698#endif // include
#define FAUDES_API
#define FAUDES_TYPE_DECLARATION(ftype, ctype, cbase)
Definition cfl_types.h:879
const AttributeSimplenetOutput * Outputp(void) const
const AttributeSimplenetInput * Inputp(void) const
void DoAssign(const AttributeSimplenetEvent &rSrc)
virtual bool IsDefault(void) const
virtual bool IsDefault(void) const
void DoAssign(const AttributeSimplenetInput &rSrcAttr)
virtual bool IsDefault(void) const
void DoAssign(const AttributeSimplenetOutput &rSrcAttr)
void Ip(std::string ip)
std::string Ip(void) const
friend void * NDeviceServer(void *)
SimplenetAddress mListenAddress
TaNameSet< AttributeSimplenetEvent > * pConfiguration
std::string mNetwork
friend void * NDeviceReply(void *)
std::map< std::string, EventSet > mInputSubscriptions
faudes_thread_t mThreadListen
std::map< std::string, ServerState > mInputServerStates
std::map< int, ClientState > mOutputClientStates
faudes_mutex_t mMutex
SimplenetAddress mEffectiveListenAddress
SimplenetAddress mBroadcastAddress
std::map< std::string, std::string > mNetworkNodes
uint32_t Idx
void * NDeviceListen(void *arg)
SimplenetAddress mDefaultAddress

libFAUDES 2.33k --- 2025.09.16 --- c++ api documentaion by doxygen