File: tutorial-dotnet.md

package info (click to toggle)
lcm 1.3.1%2Brepack1-9
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,848 kB
  • sloc: ansic: 16,186; java: 6,843; cs: 2,266; cpp: 1,594; python: 989; makefile: 352; xml: 252; sh: 59
file content (324 lines) | stat: -rw-r--r-- 12,244 bytes parent folder | download | duplicates (5)
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
.NET Tutorial {#tut_dotnet}
====
\brief An example use case in C#.NET

# Introduction {#tut_dotnet_intro}

This tutorial will guide you through the basics of using LCM .NET port. As
the .NET port is basically a transcription of the original Java library, it
tries to be functionally equivalent while maintaining C#.NET naming
conventions and other platform specifics. All sample code is written in C# (as
well as the port itself), but the principles are applicable to any of the
languages supported by the .NET Framework.
    
The tutorial doesn't cover the very basics of the LCM (message transmision
principles, message definition format etc.) - please see the rest of the
documentation before further reading. 
    
\section Generating C#.NET-specific message files
  
To demonstrate basic functionality, this tutorial will use the same message
format and application logic as the \ref tut_java to accent similarities and
differences between Java and .NET ports. Let's have the following type
specification, saved to a file named \p temperature_t.lcm:
  
\code
package exlcm;

struct example_t
{
    int64_t  timestamp;
    double   position[3];
    double   orientation[4]; 
    int32_t  num_ranges;
    int16_t  ranges[num_ranges];
    string   name;
    boolean  enabled;
}
\endcode

In order to obtain C#.NET-specific handler class, we need to call \p lcm-gen with
the \p --csharp flag:
\code
lcm-gen --csharp example_t.lcm
\endcode

Besides, the \p lcm-gen utility accepts the following .NET-specific options:

<table>
    <tr><th>Option</th><th>Default value</th><th>Description</th></tr>
    <tr><td>--csharp-path</td><td></td><td>C#.NET file destination
        directory</td></tr>
    <tr><td>--csharp-mkdir</td><td>1</td><td>Make C#.NET source
        directories automatically</td></tr>
    <tr><td>--csharp-strip-dirs</td><td>0</td><td>Do not generate folders
        for default and root namespace - unlike Java sources, the .NET source files'
        directory structure does not have to be analogic to their namespace structure.
        It is often advantageous to omit the top-most directories common to all
        generated files to simplify the directory layout.</td></tr>
    <tr><td>--csharp-decl</td><td>:&nbsp;LCM.LCM.LCMEncodable</td><td>String
        added to class declarations - similar to Java option \p --jdecl</td></tr>
    <tr><td>--csharp-root-nsp</td><td></td><td>Root C#.NET namespace
        (wrapper) added before LCM package name - this comes handy when you
        want to place generated .NET bindings into a specific part of your .NET
        namespace hierarchy and do not want to affect other languages by embedding
        the wrapper namespace directly into the source \p .lcm files</td></tr>
    <tr><td>--csharp-default-nsp</td><td>LCMTypes</td><td>Default NET
        namespace if the LCM type has no package defined</td></tr>
</table>
  
As with all tutorials, we will publish and subscribe to the "EXAMPLE" channel.
  
# Initializing LCM {#tut_dotnet_initialize}
  
There are at least two ways how to use the .NET port of LCM:
  
\li if you don't want to modify the library and just use it, the simplest way
is to build the library and copy resulting lcm.dll to your application; you
then need to add a reference to it (Project -> Add Reference... -> Browse) and
you are ready to start communicating!
\li if you plan to do some changes to library source code, the recommended way
is to add library Visual Studio project to your solution and reference it
(Project -> Add Reference...  -> Projects)
  
Main classes of the library are put in the LCM.LCM namespace (while helper
code is in LCM.Util). This results in quite funny fully qualified name of the
master class - LCM.LCM.LCM (its constructor is even funnier -
LCM.LCM.LCM.LCM() :-) ).  It's logical to use the \p 'using LCM.LCM' statement to
shorten calls to the library, but the naming scheme (chosen to agree with the
Java variant) makes it a little difficult - you cannot use bare 'LCM' as class
name - the compiler considers it to be the namespace. Instead, you need to
write LCM.LCM to denote the main class.
  
Generated message handlers are placed in the \p LCMTypes namespace by default
(you can change this by specifying lcm-gen option \p --csharp-default-nsp).
  
LCM itself has a mechanism to maintain single instance of the main class -
static property LCM.Singleton:
  
\code
LCM.LCM myLCM = LCM.LCM.Singleton;
\endcode

You can also instantiate the class and take care of the object's singularity
by yourself:
  
\code
LCM.LCM myLCM = new LCM.LCM();
\endcode

In situations where the default connection URL (fetched by LCM from the
environment variable \p LCM_DEFAULT_URL or defined by the constant
\p "udpm://239.255.76.67:7667" when the former is empty) is not suitable,
the constructor can accept variable number of parameters specifying individual
connection strings.

For detailed information on the LCM .NET API please see the
[.NET API reference](lcm-dotnet/index.html).

# Publishing a message {#tut_dotnet_publish}
  
In order to use LCM types, you can either build an assembly containing
generated classes (needed when using LCM from other .NET language then C#), or
include the classes directly to application project.  Utilization of the
generated classes is then fairly straightforward:
  
\code
exlcm.example_t msg = new exlcm.example_t();
TimeSpan span = DateTime.Now - new DateTime(1970, 1, 1);
msg.timestamp = span.Ticks * 100;
msg.position = new double[] { 1, 2, 3 };
msg.orientation = new double[] { 1, 0, 0, 0 };
msg.ranges = new short[] { 0, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };
msg.num_ranges = msg.ranges.Length;
msg.name = "example string";
msg.enabled = true;

myLCM.Publish("EXAMPLE", msg);
\endcode

The data are simply assigned to appropriate fields inside the message container.
Passing the message object to the \p Publish method of the LCM object places
it to specified channel of the communication bus (channel "EXAMPLE" here).

# Subscribing to messages {#tut_dotnet_subscribe}
  
In order to receive messages, you have two options:
  
\li write a class implementing the LCMSubscriber interface (just one
    handler-method MessageReceived) to assynchronously handle incoming
messages
\li use standard class MessageAggregator (that internally implements
    LCMSubscriber interface) for synchronous blocking or not blocking
     message delivery
      
This tutorial exploits the former option - the class \p SimpleSubscriber is defined
as internal inside the demo application class:
  
\code
internal class SimpleSubscriber : LCM.LCMSubscriber
{
    public void MessageReceived(LCM.LCM lcm, string channel, LCM.LCMDataInputStream dins)
    {
        Console.WriteLine("RECV: " + channel);

        if (channel == "EXAMPLE")
        {
            exlcm.example_t msg = new exlcm.example_t(dins);

            Console.WriteLine("Received message of the type example_t:");
            Console.WriteLine("  timestamp   = {0:D}", msg.timestamp);
            Console.WriteLine("  position    = ({0:N}, {1:N}, {2:N})",
                    msg.position[0], msg.position[1], msg.position[2]);
            Console.WriteLine("  orientation = ({0:N}, {1:N}, {2:N}, {3:N})",
                    msg.orientation[0], msg.orientation[1], msg.orientation[2],
                    msg.orientation[3]);
            Console.Write("  ranges      = [ ");
            for (int i = 0; i < msg.num_ranges; i++)
            {
                Console.Write(" {0:D}", msg.ranges[i]);
                if (i < msg.num_ranges-1)
                    Console.Write(", ");
            }
            Console.WriteLine(" ]");
            Console.WriteLine("  name         = '" + msg.name + "'");
            Console.WriteLine("  enabled      = '" + msg.enabled + "'");
        }
    }
}
\endcode

The class instance is then passed to LCM method \p SubscribeAll that passes all
received messages to our subscriber class. When selective subscription is needed
(i.e. in almost all real-world cases as we usually don't to listen to all channels),
method \p Subscribe that takes the channel name pattern as an argument is to
be used.
  
\code
myLCM.SubscribeAll(new SimpleSubscriber());
\endcode

# Putting it all together {#tut_dotnet_together}

Distribution of the LCM library includes a directory of examples.  One of them
is a couple of programs implementing all described features. Please go to
\p examples/csharp/ to find Visual Studio solution ready to be built.

The complete example transmitter application:
  
\code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LCM;

namespace LCM.Examples
{
    /// <summary>
    /// Demo transmitter, see LCM .NET tutorial for more information
    /// </summary>
    class ExampleTransmit
    {
        public static void Main(string[] args)
        {
            try
            {
                LCM.LCM myLCM = LCM.LCM.Singleton;

                exlcm.example_t msg = new exlcm.example_t();
                TimeSpan span = DateTime.Now - new DateTime(1970, 1, 1);
                msg.timestamp = span.Ticks * 100;
                msg.position = new double[] { 1, 2, 3 };
                msg.orientation = new double[] { 1, 0, 0, 0 };
                msg.ranges = new short[] { 0, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };
                msg.num_ranges = msg.ranges.Length;
                msg.name = "example string";
                msg.enabled = true;

                myLCM.Publish("EXAMPLE", msg);
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine("Ex: " + ex);
            }
        }
    }
}
\endcode

The complete example receiver application:

\code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LCM;

namespace LCM.Examples
{
    /// <summary>
    /// Demo listener, demonstrating interoperability with other implementations
    /// Just run this listener and use any of the example_t message senders
    /// </summary>
    class ExampleDisplay
    {
        public static void Main(string[] args)
        {
            LCM.LCM myLCM;

            try
            {
                myLCM = new LCM.LCM();

                myLCM.SubscribeAll(new SimpleSubscriber());

                while (true)
                    System.Threading.Thread.Sleep(1000);
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine("Ex: " + ex);
                Environment.Exit(1);
            }
        }

        internal class SimpleSubscriber : LCM.LCMSubscriber
        {
            public void MessageReceived(LCM.LCM lcm, string channel, LCM.LCMDataInputStream dins)
            {
                Console.WriteLine("RECV: " + channel);

                if (channel == "EXAMPLE")
                {
                    exlcm.example_t msg = new exlcm.example_t(dins);

                    Console.WriteLine("Received message of the type example_t:");
                    Console.WriteLine("  timestamp   = {0:D}", msg.timestamp);
                    Console.WriteLine("  position    = ({0:N}, {1:N}, {2:N})",
                            msg.position[0], msg.position[1], msg.position[2]);
                    Console.WriteLine("  orientation = ({0:N}, {1:N}, {2:N}, {3:N})",
                            msg.orientation[0], msg.orientation[1], msg.orientation[2],
                            msg.orientation[3]);
                    Console.Write("  ranges      = [ ");
                    for (int i = 0; i < msg.num_ranges; i++)
                    {
                        Console.Write(" {0:D}", msg.ranges[i]);
                        if (i < msg.num_ranges-1)
                            Console.Write(", ");
                    }
                    Console.WriteLine(" ]");
                    Console.WriteLine("  name         = '" + msg.name + "'");
                    Console.WriteLine("  enabled      = '" + msg.enabled + "'");
                }
            }
        }
    }
}
\endcode

# Conclusion {#tut_dotnet_conclusion}

The tutorial has provided a basic working demonstration of the LCM library
.NET port. For further information, please see the LCM documentation.