forked from usnistgov/rcslib
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathNMLcpp.html
1415 lines (1102 loc) · 53.7 KB
/
NMLcpp.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US">
<head>
<title>NML Guide (C++ Version)</title>
<meta name="description" content="The Neutral Message Language (NML)is
a mechanism to allow control modules to communicate using standard set(s) of
messages. It is built upon the Communications Management System a flexible,
portable and configurable means of communicating across either a
bus or a network. " />
<meta name="keywords" content="real-time control programming manufacturing"/>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="stylesheet" href="http://www.isd.mel.nist.gov/mel2.css" type="text/css" />
<style type="text/css">
div.user{
width: 0
font-family: monospaced;
font-size: small;
background: rgb(235,235,255);
padding: 0.1em;
overflow: auto;
overflow-y: visible;
}
div.output{
width: 0
font-family: monospaced;
font-size: small;
background: rgb(235,255,235);
padding: 0.1em;
overflow: auto;
overflow-y: visible;
}
div.file{
width: 0
font-family: monospaced;
font-size: small;
background: rgb(255,235,235);
padding: 0.1em;
border: thin solid black;
overflow: auto;
overflow-y: visible;
}
em.var {
font-style: italic;
font-family: fantasy;
font-size: larger;
}
strong.bnfterminal {
font-weight:bolder;
}
</style>
</head>
<body>
<h1>The NML Programmer's Guide (C++ Version) </h1>
<p><a name="TOP_OF_FILE"></a> </p>
<ul>
<li><a href="index.html">See other RCS Library Documents.</a></li>
<li><a href="NMLtoc.html">Go To Table of Contents</a></li>
</ul>
<hr/>
<h2><a name="Intro_Header"> Introduction</a></h2>
<p>The Real-Time Control System (RCS) library is a C++ class library intended
for multi-platform real-time distributed applications. It has been compiled and tested on several platforms including MS Windows, Mac OS X, Linux, and several UNIX systems. This document describes the use of the Neutral Message Language (NML) components of the library.</p>
<p>The Communication Management System
(CMS) provides access to a fixed-size buffer of general data to
multiple reader or writer processes on the same processor, across a
backplane, or over a network. Regardless of the communication method
required, the interface to CMS is uniform. Methods are provided to
encode all of the basic C data types in a machine independent or
neutral format, and to return them to the native format. A CMS_HEADER
is added to each buffer which provides information about whether the
buffer has been written or read from last, whether the buffer is new
to a particular process and the size of the last write to the buffer.
CMS uses a configuration file so that users can change communications
protocols or parameters without recompiling or relinking the
applications. </p>
<p>The Neutral Message Language (NML), formerly known as the Neutral
Manufacturing Language, provides a higher level interface to CMS. It provides a mechanism for
handling multiple types of messages in the same buffer as well as
simplifying the interface for encoding and decoding buffers in neutral
format and the configuration mechanism. <!-- (Eventually it may also
include sets of standard message formats and routines to handle them
more easily. See <A HREF="nml_emc.html#TOP_OF_FILE">"The EMC-NML
Message Set"</a>) --></p>
<p> Most of the examples have corresponding text
files which can be down-loaded and compiled. The examples are included
both directly in this document for easy reading and as separate
text-only files which are ready to be compiled. (WWW Users: You may
have to use your browser's "Save" or "SaveAs"
command to get the files.) Unfortunately, given the variety of systems
and compilers that are available it is impossible for me to give
detailed compiling instructions here. However the following form
should work on most systems.(All typed on one line.)</p>
<pre>
[C++ Compiler] -I[Location of RCS Include Files] [Example C++ File(s)] [RCS Library for Platform] -o [Executable File]
</pre>
<p>Since a working knowledge of C++ will be very helpful for understanding or using the RCS library utilities you may want to review
<a href="quickC++.html">"A Quick C++ Introduction for RCS Library Users"</a>.</p>
<h3><a name="NOTATION">Notation</a></h3>
<p>I like lots of examples.</p>
<div class="user">
<pre>
Commands users are expected to enter at a command prompt will look like this.
</pre>
</div>
<div class="output">
<pre>
Computer program generated example output will look like this.
</pre>
</div>
<div class="file">
<pre>
Text files listed in line look like this.
</pre>
</div>
<p>All of the example files referred to are in
<a href="rcslib_doc_examples.zip">rcslib_doc_examples.zip</a>.</p>
<h2> <a name="Terminology_Header">Terminology</a></h2>
<p>The figure below illustrates the structure of a typical RCS application using NML. The
application is distributed across three computers. Processes 1, 2, and
3 are able to write directly into the shared memory buffers they use
because they are located in the same computer or backplane. It is for
this reason that they are labeled "LOCAL". Processes 4,5 and 6 can
only access the buffers through an NML Server and are therefore
labeled "REMOTE". The description might need to be complicated in a
system with buffers in more than one machine. Processes would then
need to be described as local or remote with respect to a particular
buffer.</p>
<p> <img src="fig1.gif" alt="NML Example System"/></p>
<p>NML servers must be run for
each buffer that will be accessed by remote processes. They read and
write to the buffer in the same way as local processes on the behalf
of remote processes.</p>
<p>NML uses configuration files to store information about which processes communicate with
which buffers and how. Most of the options available to NML programmers are chosen by specifying them in the
configuration file. (The configuration files are ascii text files with a format described under <a href="NMLcfg.html">"Writing NML Configuration Files"</a>.)</p>
<p>NML is message-based rather than
stream-based. Each successful read operation retrieves the data sent
in exactly one write operation. Unless queuing is enabled, each write
operation moves one message into the buffer replacing any previous
message. </p>
<p> More than one type of message can be sent to the same buffer so a
unique type identifier is always contained in the message. After a
read operation, the process must use this identifier to determine the
type of message before using any of the data in the message. Each type
of message implies a particular data structure. Most messages are
user-defined.</p>
<p>Messages are called encoded if they have been
translated into a machine-independent or neutral format such as the
eXternal Data Representation (XDR). Buffers are called encoded if the
messages in them are to be encoded which is established in the
configuration file. NML servers can encode and decode messages on
behalf of remote processes. An NML vocabulary defines the set of
messages that may be used in an application and provides the necessary
functions for encoding and decoding the messages.</p>
<p>Header File</p>
<p>All of the necessary header files will be included if
rcs.hh is included.</p>
<p>Classes</p>
<p>The following classes provide the programming interface for CMS and NML:</p>
<p>NML<br/>NMLmsg<br/>NML_SERVER</p>
<p>CMS<br/>CMS_HEADER<br/>CMS_SERVER</p>
<p>CMS_USER</p>
<p>These classes are detailed in the following sections.</p>
<p>NML Application
Structure</p>
<p>The next figure shows the structure of a single
concurrent process module using NML (the memory buffer appears to be
local to the application)</p>
<p><img src="appstrut.gif" alt="On the left is a key. Light blue-green boxes are modules from the RCS library. White boxes are modules the user is expected to complete. Green rectancgle with rounded corners is a shared memory buffer. Arrows chow the communications channels. On the right : The white box Application Routines (user) over light blue-green box NML library Routines(rcslib) over white box User-defined Format function over white box user defined update functions over light blue-green box CMS update functions over light green box CMS Communications Functions over arrow to green outline labeled shared memory buffer."/> </p>
<p>The applications routines initialize and use objects from class NML and
NMLmsg which depend on some user-defined functions. The format
function selects from a set of user defined update functions for each
aggregate type the user will need to pass to the memory buffer. The
update function for each aggregate type is built by updating each
member inpidually using CMS routines for the basic C data types.
These basic update routines write to and read from internal CMS
buffers which are themselves read or written to memory buffers that
are available to other concurrent processes using the CMS
Communications Routines.</p>
<h2><a name="Design_Header">Designing an NML Application.</a></h2>
<p>Because NML is configurable, programmers can choose between
protocols with higher performance but which may be more restrictive or
require more expensive hardware or those that are less restrictive or
require less expensive more widely available hardware. By making a
buffer local to a process you can improve the performance of that
process. By moving processes you may be able to reduce the load on one
CPU or increase the number of processes able to use the faster local
protocol. Using servers to provide remote access to buffers frees
local processes from being slowed down by the communications with
remote processes. </p>
<h4>Example: Robot Controller/Supervisor Design </h4>
<p>A controller for a robot must poll a variety of inputs
and perform some computations every "n" milliseconds and a
remote supervisor should be able to check the status of the robot when
needed. </p>
<p>The next figure shows one possible design for this
application. Because the controller can write directly to the shared
memory buffer, writing the status takes a minimum time for the
controller. Using the NML server allows the supervisor to be located
almost anywhere and on almost any host. </p>
<p> <img src="nml_ex.gif" alt="Controller/Supervisor System Diagram"/></p>
<p></p>
<h3>Summary of Design Suggestions. </h3>
<ol>
<li>Avoid
overloading any CPU by assigning too many processes to it or building
a single process which must do too much work. </li>
<li>Place buffers
so that they may be accessed locally by the most time-critical
process(es). </li>
<li>Use the "LOCAL" protocol whenever possible.
</li>
<li>Only use neutrally encoded buffers when necessary.(i.e.
backplane communications between different types of processors) or buffers
with dynamic length arrays.
</li>
</ol>
<h2><a name="Programming_Header">Programming with NML</a></h2>
<p>NML applications programmers need to create a message
vocabulary and associated format function, write a configuration file,
create an NML object, and use the read and write member functions.
</p>
<h3><a name="NML_Vocab_Header">Creating an NML Vocabulary (Format Functions, Update Functions, and Message Definitions)</a></h3>
<p>The message vocabulary is a set of C++ classes, derived from
NMLmsg, which can be thought of as data structures that are copied
into the NML buffer during a write operation, and copied out during a
read operation. Each class is associated with a unique identifier, a
positive integer, that allows readers to identify which message is
present in the buffer. Besides the particular data members of the
class, each class also needs an update function which calls CMS
methods to convert the data members to types CMS can handle.
Currently, CMS provides support for the basic C language built-in
types. "long long" and "unsigned long long" types
are not really standard built in types and are not supported. One should read
<a href="l64notes.html">NML 64bit long notes</a> before assuming a long or unsigned long will store 64 bits insead of 32 bits, or before assuming a long double stores more than 64 bits.
(See <a href="NML.html#Insufficient_Args_Error">"Trouble Shooting -
Insufficient Arguments Error"</a>) </p>
<p>To enable CMS to neutrally
format the data in the buffer or to allow NML servers to encode and
decode the data for remote processes, a format function is required.
This format function is nothing more than a switch statement,
associating NML identifiers with the update functions of particular
NML message classes. The format function can be manually programmed as but it is recommended to have it be automatically generated using the <a href="CodeGen-Instructions.html">NML Code Generator</a>.</p>
<h4>Variable Length Arrays</h4>
<p>Some advanced users define messages with variable length arrays.
There are several ways to do this, but the simplest and most convenient way
is ussually to use the DECLARE_NML_DYNAMIC_LENGTH_ARRAY macro. The macro
has special meaning to the NML Code Generator. The result is an array with
a constant maximum size but where only the first name_length elements are sent
across the network with each remote read or write. Local reads and writes can be forced to use the condensed version by setting the neutral configuration
file flag to 1.
</p>
<p>For your information the text of the macro is:</p>
<pre>
#define DECLARE_NML_DYNAMIC_LENGTH_ARRAY(type, name, size) int name##_length; type name[size];
</pre>
<h4>Example: Message Definition. </h4>
<p>Files needed for this example include: <a href="nml_ex1.hh">nml_ex1.hh</a>, <a href="nml_ex1.cc">nml_ex1.cc</a></p>
<h5> Here is <a href="nml_ex1.hh">nml_ex1.hh</a></h5>
<div class="file">
<pre>
/* nml_ex1.hh */
#ifndef NML_EX1_HH
#define NML_EX1_HH
#include "rcs.hh"
/* Give the new structure a unique id number */
#define EXAMPLE_MSG_TYPE 101
/* The id number must be unique within a CMS
buffer, i.e. the number must be different than the id of any other
type that might be written to a particular buffer. For simplicity it
is recommended that the id number also be unique within an
application. */
/* Define the new message structure */
struct EXAMPLE_MSG: public NMLmsg {
/* The constructor needs to store the id number */
/* and the size of the new structure */
/* by passing them as arguments to the base class constructor. */
EXAMPLE_MSG():NMLmsg(EXAMPLE_MSG_TYPE, sizeof(EXAMPLE_MSG)){};
/* Each new type needs to overload the update function. */
void update(CMS *cms);
/* Data in this new message format. */
float f;
char c;
int i;
DECLARE_NML_DYNAMIC_LENGTH_ARRAY(int, da, 100);
};
/* Declare the NML Format function. */
int ex_format(NMLTYPE type, void *buf, CMS *cms);
#endif /* End of NML_EMC_HH */
</pre>
</div>
<h3><a name="Creating_Object_Header">Creating an NML Object</a></h3>
<p>NML has several constructors, but most users will use the
following. </p>
<p>NML(NML_FORMAT_PTR <em class="var">f_ptr</em>, char *
<em class="var">buf</em>, char *<em class="var">proc</em>, char *<em class="var">file</em>); </p>
<p>The parameters are: <br/><em class="var">f_ptr</em> = <address of format
function to use>; <br/><em class="var">buf</em> = <name of the buffer to
connect to as specified in configuration file>; <br/><em class="var">proc</em>
= <name under which to access the buffer>; <br/><em class="var">file</em> =
<name of the configuration file>; </p>
<p></p>
<p>There is also a constructor that takes a buffer line and process line as
arguments rather than expecting to retrieve them from a configuration file.
It is important for the application to ensure that the arguments contain
consistant parameters given the options passed by other applications with
which it will communicate.
</p>
<pre>
NML(const char *bufferline, const char *processline);
</pre>
<p>If the second constructor is used the format function needs to be set
before any read or write, or even before one checks for a valid connection. The
function to use for this is:</p>
<pre>
int NML::prefix_format_chain (NML_FORMAT_PTR);
</pre>
<p>This function can also be used multiple times regardless of which constructor is called. Then each time the format function would need to be called each
format function in the list will be called until one returns a non-zero value. This might be useful for a channel that will collect messages from multiple types of applications each with its own format function or distrubute messages to multiple types of applications each with its own format function. One needs to be careful to ensure that message types do not conflict. Another option would be to have the java code generator load multiple header files and then generate a single format function combining all the message updates.
</p>
<h3><a name="Reading_NML_Header">Reading NML Data </a></h3>
<p>If you examine
many of the communications interfaces you'll find a function that
looks like the UNIX read function. </p>
<p>/* UNIX general purpose
read. */ </p>
<p>int read(int fd, char *buf, int nbyte); </p>
<p>/*
UNIX Read from message queue. */ </p>
<p>int msgrcv(int msqid, struct
msgbuf *, int msgsz, long msgtyp, int msgflg); </p>
<p>/* Read from a
socket (often used for TCP/IP) */ </p>
<p>int recv(int socket, char
*buf, int len, int flags); </p>
<p>Notice that the first parameter is
an identifier of the source to read, the second parameter is some type
of pointer to a buffer, and the third parameter is a measure of the
size of the buffer. Unfortunately this is a rather poor model for
configurable message-based communication because the program receiving
a message must be able to create a buffer large enough to hold the
incoming message even though there is no way it can know the size of
the new message. NML solves this problem by providing users access to
a local buffer that was created based on the size specified in the
configuration file and will contain a copy of the incoming message
after a read operation.
</p>
<p>These are the member functions used to perform read: </p>
<p>NMLTYPE NML::read(); </p>
<p>If the read is successful
the message currently in the global CMS buffer will be copied into a
local buffer for this process. The address of this local buffer is
available through the member function get_address. If the buffer is
encoded the format function will be called to return the message to
native format. The message should be some user defined type derived
from NMLmsg.The member NML::error_type can be examined to see the
cause of NML::read returning -1.
(See <a href="NML.html#Handling_Errors_Header">"Handling Errors"</a> ) If
queuing is enabled on this buffer this read will remove the message
from the queue so that other processes that are reading
from this buffer will see the next message on the queue and
potentially miss this one. </p>
<p></p>
<p>Returns: </p>
<p>0 if the
buffer has not been written to since the last read or <br/>-1 if an error
occurred; or<br/>the type id of the message received if the buffer
contains new data. </p>
<p>NMLmsg *NML::get_address(); </p>
<p>This function returns a pointer to the NML data stored during an
NML::read() operation. </p>
<p></p>
<h4>Example: Reading from an NML Channel.</h4>
<p>Files needed for this example include:
<a href="nml_ex3.cc">nml_ex3.cc</a>, <a href="nml_ex1.hh">nml_ex1.hh</a>,
<a href="nml_ex1.cc">nml_ex1.cc</a>, <a href="ex_cfg.nml">ex_cfg.nml</a></p>
<h5><a href="nml_ex3.cc">nml_ex3.cc</a></h5>
<div class="file">
<pre>
/* nml_ex3.cc */
#include "rcs.hh"
#include "<a href="nml_ex1.hh">nml_ex1.hh</a>"
int main(int argc, const char **argv)
{
RCS_TIMER timer(0.1);
NML example_nml(ex_format, "ex_buf1","ex3_proc", "<a href="ex_cfg.nml">ex_cfg.nml</a>");
EXAMPLE_MSG *example_msg_ptr;
int quit = 0;
while(!quit)
{
switch(example_nml.read())
{
case -1:
rcs_print( "A communications error occurred.\n");
quit = 1;
break;
case 0:
/* The buffer contains the same message */
/* you read last time. */
break;
case EXAMPLE_MSG_TYPE:
example_msg_ptr = (EXAMPLE_MSG *)example_nml.get_address();
rcs_print(" We have a new example message. \n");
rcs_print(" The value of its members are:\n ");
rcs_print(" f=%f, c=%c, i=%d\n ",
example_msg_ptr->f,
example_msg_ptr->c,
example_msg_ptr->i);
quit = 1;
break;
}
timer.wait();
}
}
</pre>
</div>
<p>Compile with:<a href="#UNIX_COMMANDS">*</a></p>
<div class="user">
<pre>
## Set RCS lib install directory.
## likely needs to adjusted for your system.
RCSLIB_DIR=~/rcslib/;
## Set the path for finding librcs.so
LD_LIBRARY_PATH="${RCSLIB_DIR}/lib/:${LD_LIBRARY_PATH}";
export LD_LIBRARY_PATH;
## Create <a href="nml_ex1.cc">nml_ex1.cc</a> from <a href="nml_ex1.hh">nml_ex1.hh</a>
java -jar "${RCSLIB_DIR}"/bin/<a href="CodeGenCmdLine.jar">CodeGenCmdLine.jar</a> <a href="nml_ex1.hh">nml_ex1.hh</a> -o <a href="nml_ex1.cc">nml_ex1.cc</a>
g++ <a href="nml_ex3.cc">nml_ex3.cc</a> <a href="nml_ex1.cc">nml_ex1.cc</a> -I"${RCSLIB_DIR}"/include -L "${RCSLIB_DIR}"/lib -lrcs -o nml_ex3
</pre>
</div>
<p>This example also uses the <a href="utils.html#RCS_TIMER_Header">RCS_TIMER</a> class and the <a href="utils.html#rcs_print_header">rcs_print</a> function described in
the guide for the <a href="utils.html">RCS Library Lower Level
Utilities</a>. The symbolic constant VXWORKS should be defined only if
you wish to compile the example for VxWorks. If you try to run this
example, it will wait for something to be written into the buffer. To
write something into the buffer, you can use the example in
<a href="NML.html#Writing_NML_Header">Writing NML Data</a></p>
<p>NMLTYPE NML::blocking_read(double timeout); </p>
<p>This performs the same function as the read() above except that if there
is no new data the calling process will be put to sleep until either another process writes to the buffer or the timeout occurs. The timeout is given in seconds. to wait indefinitely provide a negative timeout. It is necessary to add
"bsem=<key>" to the buffer line of the NML configuration file in order to be able to perform a blocking read.</p>
<p></p>
<p>Returns: </p>
<p>-1 if an error
occurred; or<br/>the type id of the message received if/when the buffer
contains new data. </p>
<p>NMLTYPE NML::peek(); </p>
<p>/* Read an NML message from a CMS buffer without
changing the was_read flag */ </p>
<p>Peek works exactly the
same as read except that the flag that lets others know when the
buffer is read is not changed and if queuing is enabled the message is
not removed from the queue. This could be useful if you need to monitor
a buffer without letting other processes using the buffer know. The
member NML::error_type can be examined to see the cause of NML::peek
returning -1.(See <a href="NML.html#Handling_Errors_Header">"Handling
Errors"</a>) </p>
<p>Returns: <br/>0 if the buffer has not been
written to since the last read; <br/>-1 if an error occurred; or<br/>the
type id of the message received if the buffer contains new data. </p>
<h3><a name="Writing_NML_Header">Writing NML Data </a></h3>
<p>If you examine many of the communications interfaces you'll find a
function that looks like the UNIX write function. </p>
<p>/* UNIX general purpose write. */ </p>
<p>int write(int fd, char *buf, int
nbyte); </p>
<p>/* UNIX Send to a message queue. */ </p>
<p>int msgsnd(int msqid, struct msgbuf *, int msgsz, long msgtyp, int
msgflg); </p>
<p>/* Output to a socket (often used for TCP/IP) */ </p>
<p>int send(int socket, char *msg, int len, int flags); </p>
<p>Notice
that the first parameter is an identifier of the destination to write,
the second parameter is some type of pointer to a buffer, and the
third parameter is a measure of the size of the buffer. However C++
allows us to considerably simplify this interface, by storing the size
of a message when its constructed and by overloading the write
function to accept either references or pointers to messages. All of
the NML write functions are non-blocking. </p>
<p>These are the
member functions used to perform writes: </p>
<p>/* Write an NML
message into a CMS buffer */ </p>
<p>int NML::write(NMLmsg
&<em class="var">nml_msg</em>); /* Reference version. */ </p>
<p>int
NML::write(NMLmsg *<em class="var">nml_msg</em>); /* Pointer version. */ </p>
<p><em class="var">nml_msg</em> should be a pointer or reference to an object of
some user defined type derived from NMLmsg. If the buffer is
configured to be in a neutral format the message will be encoded
before it is written to the CMS buffer. The write functions overwrite
the message currently in the buffer if queuing is not enabled. The
member NML::error_type can be examined to see the cause of NML write
returning -1.(See <a href="NML.html#Handling_Errors_Header">"Handling
Errors"</a> ) </p>
<p>Returns:
<br/>0 if successful;
<br/>-1 otherwise. </p>
<h4>Example: Writing to an NML Channel.</h4>
<p>Files needed for this example include: <a href="nml_ex4.cc">nml_ex4.cc</a>,
<a href="nml_ex1.hh">nml_ex1.hh</a>, <a href="nml_ex1.cc">nml_ex1.cc</a>, <a href="ex_cfg.nml">ex_cfg.nml</a></p>
<h5> Here is <a href="nml_ex4.cc">nml_ex4.cc</a></h5>
<div class="file">
<pre>
/* nml_ex4.cc */
#include "rcs.hh"
#include "<a href="nml_ex1.hh">nml_ex1.hh</a>"
int main()
{
NML example_nml(ex_format, "ex_buf1", "ex4_proc", "ex_cfg.nml");
EXAMPLE_MSG example_msg;
example_msg.f = 123.456;
example_msg.c = 'c';
example_msg.i = 99;
example_nml.write(example_msg);
}
</pre>
</div>
<p>Compile with:<a href="#UNIX_COMMANDS">*</a></p>
<div class="user">
<pre>
## Set RCS lib install directory.
## likely needs to adjusted for your system.
RCSLIB_DIR=~/rcslib/;
## Set the path for finding librcs.so
LD_LIBRARY_PATH="${RCSLIB_DIR}/lib/:${LD_LIBRARY_PATH}";
export LD_LIBRARY_PATH;
## Create <a href="nml_ex1.cc">nml_ex1.cc</a> from <a href="nml_ex1.hh">nml_ex1.hh</a>
java -jar "${RCSLIB_DIR}"/bin/<a href="CodeGenCmdLine.jar">CodeGenCmdLine.jar</a> <a href="nml_ex1.hh">nml_ex1.hh</a> -o <a href="nml_ex1.cc">nml_ex1.cc</a>
g++ <a href="nml_ex4.cc">nml_ex4.cc</a> <a href="nml_ex1.cc">nml_ex1.cc</a> -I"${RCSLIB_DIR}"/include -L "${RCSLIB_DIR}"/lib -lrcs -o nml_ex4
</pre>
</div>
<p>This example writes a message into a buffer. To
read the message use the example in <a href="NML.html#Reading_NML_Header">Reading NML Data</a></p>
<p>/* Write an NML message into a CMS buffer if it has been read. */ </p>
<p>int NML::write_if_read(NMLmsg &nml_msg); /* Reference version. */ </p>
<p>int NML::write_if_read(NMLmsg *nml_msg); /* Pointer version. */ </p>
<p>These functions combine the operations of
writing and checking if a buffer has been read. It checks to see if
the buffer has been read. If it has then it writes the message into
the buffer just as write would, but if not it returns -1. Since there
is only one access to the buffer, there is no way for another process
to write into the buffer between the check and the write. The member
NML::error_type can be examined to see the cause of NML write_if_read
returning -1.
(See <a href="NML.html#Handling_Errors_Header">"Handling Errors"</a>) </p>
<p>Returns:
<br/>0 if successful;
<br/>-1 otherwise. </p>
<h4>Note:</h4>
<p>This may not work as expected with remote processes unless "CONFIRM_WRITE"
is added the the corresponding BufferLine in the NML file. From "<a href="NMLcfg.html">The NML Configuration Files</a>" documentation:</p>
<p>To force the server to send confirmation messages back to a remote writer add "confirm_write". This allows a remote writer to detect problems such as a full queue or if a write_if_read failed because the buffer was not read but it reduces performance. </p>
<h3> <a name="Check_If_Read_Header">Checking If Data Has Been Read. </a></h3>
<p>If queuing is not enabled then a
flag is kept in every CMS buffer called was_read. Every time a write
is performed on the buffer the flag is set to 0. Every time a read is
performed on the buffer the flag is set to 1. The check_if_read
function just returns the value of that flag. To avoid overwriting a
buffer that has not been read yet, it is better to use the
write_if_read function. The member NML::error_type can be examined to
see the cause of NML::check_if_read returning -1.
(See<a href="NML.html#Handling_Errors_Header">"Handling Errors"</a> ) </p>
<p>If queuing is enabled then the check_if_read function returns 1
only if all of the messages in the buffer have been read meaning that
the queue is empty. </p>
<p>Here is the prototype: </p>
<p>int NML::check_if_read(); </p>
<p>Returns:
<br/>0 The buffer contains a message that has never been read.
<br/>1 The buffer contains a message that has been read at least once.
<br/>-1 An error occurred that prevented NML from determining
whether the buffer has been read. </p>
<h3><a name="Get_Msg_Count_Header">Getting the Message Count. </a></h3>
<p>The number of messages that have been successfully written to the buffer over its lifetime is saved in the buffer and can be retreived for diagnostic or monitoring purposes.</p>
<p>Here is the prototype: </p>
<p>int NML::get_msg_count(); </p>
<p>Returns:
<br/>-1 Some error prevented the message count from being retrieved .
<br/> otherwise the number of messages written to that buffer over its lifetime.
</p>
<h3><a name="Get_Queue_Length_Header">Getting the Queue Length</a></h3>
<p>If queing is enabled the number of messages queued in the buffer, can
be obtained. </p>
<p>Here is the prototype: </p>
<p>int NML::get_queue_length(); </p>
<p>Returns:
<br/>-1 Some error prevented the queue length from being retrieved .
<br/> otherwise the number of messages currently queued in the buffer.
</p>
<h3><a name="Get_Space_Available_Header">Getting the Space Left In A Queued Buffer. </a></h3>
<p>The mechanism for queuing in NML stores the queue in a fixed length buffer. It could perhaps store many small messages or only a few larger ones, or some combination. The number of bytes free in the buffer can be used to estimate how many more messages can be written to the buffer before it is full. </p>
<p>Here is the prototype: </p>
<p>int NML::get_space_available(); </p>
<p>Returns:
<br/>-1 Some error prevented the space available from being retrieved .
<br/> otherwise the number of bytes that are free in a queued buffer is returned.
</p>
<h3><a name="Clearing_Buffer_Header">Clearing a buffer.</a></h3>
<p>You may
want to clear a buffer to preempt previously sent messages still in
the queue or to ensure that residual data in a buffer is not mistaken
for NML messages. </p>
<p>int NML::clear(); </p>
<p>Returns:
<br/>0 The buffer was successfully cleared.
<br/>-1 An error occurred. </p>
<h3><a name="LOADCFG">Loading an NML Configuration file to memory.</a></h3>
<p> In order to start up a process faster, it is possible to load the configuration file in to memory before creating several NML channels that use the same
file. The file can later be unloaded to free the previously allocated memory.</p>
<h4>Example: Loading a config file for faster startup.</h4>
<div class="file">
<pre>
. . .
load_nml_config_file( "ex_cfg.nml" );
NML example_nml1(ex_format, "ex_buf1","ex4_proc", "ex_cfg.nml");
NML example_nml2(ex_format, "ex_buf2","ex4_proc", "ex_cfg.nml");
NML example_nml3(ex_format, "ex_buf3","ex4_proc", "ex_cfg.nml");
NML example_nml4(ex_format, "ex_buf4","ex4_proc", "ex_cfg.nml");
. . .
unload_nml_config_file( "ex_cfg.nml" );
. . .
</pre>
</div>
<h3><a name="MSG2STR">Converting an NMLmsg to a string.</a></h3>
<p>It is occasionally helpful to be able to display the contents of any
NMLmsg in a string. To accomplish this you will need an NML object which
was initialized with a format function that handles your message type.</p>
<p>const char * NML::msg2str(NMLmsg *);</p>
<p>const char * NML::msg2str(NMLmsg &);</p>
<p>Returns:
<br/>This function returns a pointer to a string with each member of the NML message converted to a string and separated with commas if successful or NULL otherwise. The first two members will be the type and size of the message. The string may be cleared on the next call to read, write, check_if_read, peek, write_if_read, clear, or msg2str with that NML object, or when that NML object is deleted, so the string should be displayed or copied before any of these operations occur.</p>
<h3><a name="OVERCONFIG">Overriding the NML configuration file.</a></h3>
<p> Occasionally, it may be helpful to override some of the options in
the configuration file. For example a program will only connect to one host
each time it is run, but each time it might be a different host, instead of changing the config file each time, it might be convenient to let the host be specified on the command line. The following functions set static global variables
common to all tasks in multithreaded environments like VxWorks, but unique to each process in more traditional environments like sunos5</p>
<pre>
extern void nmlSetHostAlias(const char *hostName, const char *hostAlias);
extern void nmlClearHostAliases();
extern void nmlAllowNormalConnection();
extern void nmlForceRemoteConnection();
extern void nmlForceLocalConnection();
</pre>
<p> nmlSetHostAlias adds another host/alias pair to a linked list. Whenever
a hostname is read from the configuration file this list is checked to see
if it matches one of the aliases, and if so it is effectively replaced with
the corresponding host name. nmlClearHostAliases deletes the entire list.
nmlForceRemoteConnection forces the ProcessType to be read as REMOTE for
all following connections. nmlForceLocalConnection forces the ProcessType to be read as LOCAL for all following connection. nmlAllowNormalConnection returns it to the default behavior where the value in the configuration file is honored.
</p>
<p>Here's an example using it to allow the user to select a host from the
command line. NOTE: The example only works when localhost is specified in
the config file and the process type for "bReader" is REMOTE.</p>
<div class="file">
<pre>
#include "rcs.hh"
#include "<a href="nml_ex1.hh">nml_ex1.hh</a>"
int main(int argc, char **argv)
{
if(argc > 1)
{
nmlSetHostAlias(argv[1],"localhost");
}
NML example_nml(ex_format, "ex_buf1", "bReader", "ex_cfg.nml");
if(!example_nml.valid())
{
rcs_print_error("Bad.\n");
}
else
{
rcs_print("Good.\n");
}
}
</pre>
</div>
<p>Compile with:<a href="#UNIX_COMMANDS">*</a></p>
<div class="user">
<pre>
## Set RCS lib install directory.
## likely needs to adjusted for your system.
RCSLIB_DIR=~/rcslib/;
## Set the path for finding librcs.so
LD_LIBRARY_PATH="${RCSLIB_DIR}/lib/:${LD_LIBRARY_PATH}";
export LD_LIBRARY_PATH;
## Create <a href="nml_ex1.cc">nml_ex1.cc</a> from <a href="nml_ex1.hh">nml_ex1.hh</a>
java -jar "${RCSLIB_DIR}"/bin/<a href="CodeGenCmdLine.jar">CodeGenCmdLine.jar</a> <a href="nml_ex1.hh">nml_ex1.hh</a> -o <a href="nml_ex1.cc">nml_ex1.cc</a>
g++ <a href="nml_set_host_alias_ex.cc">nml_set_host_alias_ex.cc</a> <a href="nml_ex1.cc">nml_ex1.cc</a> -I"${RCSLIB_DIR}"/include -L "${RCSLIB_DIR}"/lib -lrcs -o nml_set_host_alias_ex
</pre>
</div>
<h3><a name="Phantom_Buffers_Header">Using Phantom Buffers. </a></h3>
<p>Use of PHANTOM Buffers has been deprecated.</p>
<!--
<p>Occasionally users may wish to temporarily redirect messages
intended for a CMS buffer. One way of doing this is setting up
functions to simulate the behavior of the NML commands and set the
pointers NML::phantom_clear, NML::phantom_check_if_read,
NML::phantom_read, NML::phantom_peek, NML::phantom_write, and/or
NML::phantom_write_if_read to those functions. If the buffer type or
process type specified in the configuration file is "PHANTOM" then
every NML call of that type will result in calling your phantom
function. Otherwise, NML will access the buffer normally.(See the
section <a href="NMLcfg.html">"Writing NML Configuration Files"</a>) </p>
<h4> Example: Using PHANTOM overrides. </h4>
<p>Files needed for this example include: <A
href="nml_ex5.cc">nml_ex5.cc</a>, <a href="nml_ex1.hh">nml_ex1.hh</a>,
<a href="nml_ex1.cc">nml_ex1.cc</a>, <a href="ex_cfg.nml">ex_cfg.nml</a></p>
<h5> Here is <a href="nml_ex5.cc">nml_ex5.cc</a></h5>
<PRE>
/* nml_ex5.cc */
#include "rcs.hh"
#include "nml_ex1.hh"
/* This example uses the PHANTOM writes to send all messages to stdout. */
int my_write(NMLmsg *msg)
{
printf("Message %d written.\n", msg->type);
}
main()
{
NML example_nml(ex_format, "ex_buf1","ex5_proc", "ex_cfg.nml");
example_nml.phantom_write = my_write;
EXAMPLE_MSG example_msg;
/* . . . */
example_nml.write(example_msg);
}
</PRE>
-->
<h3> <a name="Handling_Errors_Header">Handling Errors </a></h3>
<p>When the
NML member functions cannot perform their task they try to provide
developers with some information that may allow them to resolve the
problem. This information is available in several forms. </p>
<p>The functions NML::read(), NML::write(), NML::peek(), and
NML::write_if_read() return -1 if an error occurred.
NML::get_address() returns NULL if an error occurs. </p>
<p>Messages are printed to character display devices or stored in a linked list
with the rcs_print_error facility. (See the document "RCS Lower Level
Utilities" for more information on the rcs_print_error facility. )
Often several messages are issued for the same error, because if an
error occurs at a low level the low level function will print an error
and return a value indicating an error to a higher level function
which may then also print an error. This allows the user to see the
detail available at the lower level and the context available at the
higher level. </p>
<p>NML::error_type is a variable set by NML functions that fail. It may
have one the following values: </p>
<ul>
<li>NML_NO_ERROR - No error was recognized. </li>
<li>NML_INVALID_CONFIGURATION - A problem with the
configuration file is indicated. </li>
<li>NML_BUFFER_NOT_READ -
Operations like write_if_read will only succeed if the message
currently in the buffer has been read. </li>
<li>NML_TIMED_OUT - An operation timed out. </li>
<li>NML_FORMAT_ERROR - Could indicate that
there was a problem with the user defined format and update functions,
that the size of the buffer is not large enough for one of the
messages, or that a message was received that no format function
recognizes. </li>
<li>NML_NO_MASTER_ERROR - Something needs to be
initialized by the process that is configured to be the buffer master.
Check that a master is configured and running. </li>
<li>NML_INTERNAL_CMS_ERROR - A CMS operation failed for a reason not
previously given. </li>
</ul>
<p>int NML::valid(); </p>
<p>NML::valid() returns 0 if the object was not properly constructed
or if an error has occurred severe enough that it is unlikely that any
of the other NML operations on this object will succeed or 1 if
everything seems to be in order. </p>
<h4>Example: Checking Error Status </h4>
<p>Files needed for this example include:
<a href="nml_ex6.cc">nml_ex6.cc</a>,
<a href="nml_ex1.hh">nml_ex1.hh</a>,
<a href="nml_ex1.cc">nml_ex1.cc</a>,
<a href="ex_cfg.nml">ex_cfg.nml</a></p>
<h5> Here is <a href="nml_ex6.cc">nml_ex6.cc</a></h5>
<div class="file">
<pre>
/* nml_ex6.cc */
#include "rcs.hh"
#include "nml_ex1.hh"
#include <stdio.h>
/* This example prompts the user when NML times out to see if it
should try again. */
main() {
NML example_nml(ex_format, "ex_buf1","ex6_proc", "ex_cfg.nml");
EXAMPLE_MSG *example_msg_ptr;
char input_array[10];
TRY_AGAIN:
switch(example_nml.read())
{
case -1:
if(example_nml.error_type == NML_TIMED_OUT)
{
rcs_print("NML timed out\n");
rcs_print("Do you want to try again? (y/n)");
fgets(input_array,sizeof(input_array),stdin);