# Go Tutorial Sending and receiving LCM messages using Go ## Introduction This tutorial will walk you through the main tasks for exchanging LCM messages using the Go LCM library. Throughout this tutorial, we will look at how to: - Initialize an LCM instance. - Publish a message. - Subscribe to a channel and receive a message over it. Like the other tutorials, this one uses the `example_t` message type defined in the [type definition tutorial](./lcm-type-ref.md#lcm-type-specification-language), and assumes that you have generated the Go bindings by executing for it: ``` cd examples/go go generate ``` This should produce a few go files in `exlcm`, we will use the file called `exlcm/example_t.go`. The file contains everything you need to encode and decode data of type example_t, which is necessary in order to use LCM. ## Initializing LCM That was not too difficult, right? Time to actually get our hands dirty with some actual Go code. The first thing that you will need to do, is to fetch the actual library. You can use `go get` or any other dependency manager of your choice: `go get github.com/lcm-proj/lcm/lcm-go/lcm` After you have successfully executed that, you are able to use and import the just fetched library. Make sure that the following code is inside a subdirectory of `$GOPATH/src`. ```go package main import "github.com/lcm-proj/lcm/lcm-go/lcm" func main() { lc, err := lcm.New() if err != nil { panic(err) } defer lc.Destroy() } ``` This will result in a new lcm instance by the name `lc` that is ready for use. As we are dealing with Go bindings towards the C library, it is quite easy to use. Maybe even easier than the C library itself. `lc` contains all the methods that you will need to utilize LCM based communication. Compile and run the code to see, if it works: `go run path/to/subdirectory` As you can see, we are also using `defer` to destroy the LCM instance just right before main is done executing. ## Publishing a message Now that you have successfully created an LCM instance in Go, it is time to actually send some data to a channel. This is done by sending data down a Golang channel that the `Publisher(string)` method of the instance that we just created returns. The passed string is the channel's name that you want to send the data to. Alongside the actual Go channel that you can send the data to, another channel is returned. This is an error channel that you have to check for any errors that could potentially occur. Finally, make sure that you are actually sending (LCM) encoded data down the channel. So where do you get that from? *From the Go bindings that you have generated earlier!* ```go // Skipping the code above... // Create a pointer to a new ExampleT object. example := &exlcm.ExampleT{} publisher, errs := lc.Publisher("EXAMPLE") go func() { defer close(publisher) // Let's send that a couple of times for i := 0; i < 100; i++ { // Encode the data. encEx, err := example.Encode() if err != nil { panic(err) } // Send down a channel. publisher <- encEx } }() FOR_SELECT: for { select { err, ok := <- errs: if !ok { break FOR_SELECT } panic(err) } } ``` As you can see, it does not require much code to send something through LCM using Go. Obviously, in a real-world case, one would need to initialize `example` with appropriate values, instead of initializing everything to default values. An important note: as soon as you are done with sending to a channel, you should close the corresponding publisher channel, as we are doing in the `defer` statement. This is necessary, so that you do not end up in a potential deadlock. The errs channel is only closed *after* the publisher one is. ## Receiving LCM Messages Receiving messages is just as simple as sending them out. It's just the other way around. Needless to say, it is important that the sender and receiver "know" about the channel that is used for communication. Therefore, we will also use the channel called **EXAMPLE** that we used earlier to send out information, as the receiving channel. In order to receive data, you will need to check for data that comes down a Go channel. Usually, deserialize the received data back into a suitable object is a reasonable way to do here. ```go // Skipping the code from the initialization part. // Subscribe to the previously used EXAMPLE channel. The subscription Go // channel has a buffer size 5, whenever more messages are pending than the // buffer size they are dropped. if subscription, err := lc.Subscribe("EXAMPLE", 5); if err != nil { panic(err) } // Not explicitly necessary, as Destroy() also unsubscribes to all // subscriptions. defer lc.Unsubscribe(subscription) FOR: for { select { // Usually you have a timeout to not deadlock. case <-time.After(5 * time.Second): break FOR case data, ok := <- subscription.ReceiveChan: if !ok { // That means that the channel was closed. break FOR } fmt.Println("Received information on", channel) // Empty example_t exType := &exlcm.ExampleT{} // Decode the received data if err := exType.Decode(data); err != nil { panic(err) } // Simple print out of the whole object. fmt.Println(exType) } } ``` Done! As you can see, we are also unsubscribing right before we are done. This is only possible because we are timing out (and returning) after five seconds. ReceiveChan is only closed whenever one unsubscribes. Therefore:: **be aware of potential deadlocks that you could introduce to your application**. ## Conclusion and Important Information Obviously, this tutorial outlines just a really simple use-case of LCM in Go. Some of the practices used here ought to be avoided in a proper production application (such as the use of panic for error handling). Throughout the tutorial you might have noticed that the original type name which was called `example_t` got transferred into something like `ExampleT`. This is was an actively chosen design decision. As it is Go best-practice to use Camel-Casing, every _ will be removed and the following character will be capitalized. Furthermore, the casing describes if a member or function gets exported or not in Go: capitalized member/function name = exported function; small-cased member/function name = unexported function. Therefore, every type gets automatically capitalized by the code generator. You can find more complete and compilable executables here: [listener/main.go](https://github.com/lcm-proj/lcm/blob/master/examples/go/listener/main.go) | [sender/main.go](https://github.com/lcm-proj/lcm/blob/master/examples/go/sender/main.go) If you need more information about the language itself, you should check out [the official Go web pages](https://golang.org/), which are usually of great help.