Buy me a cup of coffee 

Software development is powered by coffee... if you use any of our open source pojects, it would make my day if you'd buy me a cup (or several). Just click on the coffee cup.

Please note: parts of this site are under construction

Reactive Communications for ASCOM

Tags: ASCOM, rx, reactive-extensions, open-source, free software, free, project, astronomy, software, software-engineering

Reactive ASCOM

Reactive-style transactional device communications for ASCOM drivers with guaranteed sequencing and thread-safety

Reactive ASCOM Logo

TL;DR
Get the code from Bitbucket Git Repositories
Install the NuGet package from the NuGet Gallery
Read the blog post Introduction to Reactive ASCOM
Video Quick Start tutorial

Description

Reactive ASCOM (namespace TA.Ascom.ReactiveCommunications) is a library based on the Reactive Extensions for .NET for simplifying device communications over serial, Ethernet and other types of communications channel to embedded controllers, while guaranteeing correct sequencing and thread-safety of transactions. Transactions can use powerful LINQ features to filter and parse the receive data stream and to convert the raw data into more meaningful objects for use in upper layers. Once things like correct sequencing and thread safety can be taken for granted, the possibility of using asynchronous methods becomes much more realistic so that long running operations and command sequences can be implemented as asynchronous methods instead of having to write state machines or set flags. This leads to cleaner, more readable, more maintainable code that is easier to unit test.

Originally developed for use with ASCOM drivers, but may be useful for any software that needs to communicate with an embedded controller using a predominantly command-response protocol. The library has been used in at least 5 different commercial/production ASCOM drivers, including a telescope, a dome, a switch/power control device and a composite focuser and rotator.

This blog article describes the approach and gives a brief overview of how to use the library. The source code contains a sample console application demonstrating how to create and use the various classes. There are also quick-start video tutorials showing how to get started with the library and create custom transactions that work with your protocol.

Available as a NuGet package - simply Install-Package TA.Ascom.ReactiveCommunications

The main project page for Reactive ASCOM is at: http://tigra-astronomy.com/reactive-communications-for-ascom

Why?

Tigra Astronomy developed this library to address an operational problem that we had encountered in our own ASCOM drivers. We found it very difficult to guarantee correct sequencing of commands and responses under the wide range of conditions that an ASCOM driver has to operate in.

In particular, we found that thread locking constructs were useless when the driver runs in a single threaded apartment (STA thread). This is a pretty common situation for in-process ASCOM drivers, which are often loaded into VB6 applications such as POTH (an ASCOM hub component) and others. In that situation, we found that our serial code was being called re-entrantly, on the same thread, and just sailing through whatever locking mechanism we put in place.

We received several suggestions on how to deal with this and the most promising was to hand off the sending and receiving of serial data to a worker thread, which would be free-threaded and therefore would respect thread locking mechanisms. However, we weren't convinced that we wouldn't just be moving the problems over to another thread and adding a lot of thread synchronization complexities. We were making our code ever more complex to solve what should have been a simple problem. Further, we wanted a more general purpose solution that could work for other types of communication channel, such as Ethernet and USB and that we could re-use easily in all of our drivers. A completely different approach was needed.

We had been working on a commercial driver for a major astronomy equipment vendor in which we developed a communications architecture based around the concept of a DeviceTransaction. We liked how this led to a clean separation of concerns and wanted to spin this code out into a re-usable component. At about the same time, we also became intrigued by a technology known as the Reactive Extensions, developed by Microsoft for .NET but now also available for other platforms such as Java. Amongst other things, Rx makes certain guarantees about the order things happen in that we could use to build a thread-safe communications mechanism. I can't possibly do it justice in this short introduction, but I'll try to provide a flavour of the Reactive Extensions (or "Rx"). Rx is based on two core interfaces, IObservable<T> and IObserver<T>. Observers subscribe to observables and then nothing happens until the observable produces a value, whereupon the observer gets notified (called - not unlike a .NET event). This deceptively innocent concept leads to an architecture where no code runs until the very moment it is required. Instead of polling or blocking, you tell Rx what to call and then the code gets called when needed. Rx also provides a LINQ (Language INtegrated Query) provider, so that sequences of data can be filtered, projected and combined in many interesting ways. Just like normal LINQ queries, the query doesn't execute until the results are requested; in the case of Rx, queries execute whenever a value is produced by the observable being queried. Again it seems simple, until you realize that you are querying Data In Motion over time, that is, future data that you haven't received yet. The concept takes a bit of getting used to but once the light bulb turns on it is amazingly powerful!

We decided to combine our transaction based code with data handling based on the Reactive Extensions. The main benefit of Rx, apart from the interesting architectural choices it offers, is that it provides certain guarantees about sequencing and thread safety and makes it jaw-droppingly simple to just marshal data onto another thread. It also makes it surprisingly easy to parse a protocol into just the replies being waited for, which makes it possible to build a hierarchy of transaction classes that provide the right kind of response. This provides yet another level of safety net, in that a transaction can only receive the response it is looking for, which makes it much harder for commands and responses to become 'jumbled up'.

Having completed our own internal trials by retro-fitting the technology to a production telescope driver - and a complex one at that, we think this approach might be useful for other ASCOM developers, so we've wrapped it up into a general purpose re-usable component that we're calling Reactive Communications for ASCOM - this package.

We want other developers to make full use of this and we're happy to help out if you need some guidance. This technology has had a fair bit of road testing now and we think you can confidently rely on it. Once you start to take the sequencing and thread-safety guarantees for granted, then you may start to make different architectural choices for your drivers, such as implementing long-running operations and command sequences as asynchronous methods. Release 0.3.0 of the library added specific support for async driver code.

Quick Start Sample

(see also: video tutorial) The source code contains a console application that demonstrates the use of the library using some of the ready-made transaction types. Although this is a simple example, it does show how easy it is to get started and demonstrates some of the thread-safety features by submitting transactions on the thread pool. This is the entire program:

// This file is part of the TA.Ascom.ReactiveCommunications project
// 
// Copyright © 2015 Tigra Astronomy, all rights reserved.
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
// documentation files (the "Software"), to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so,. The Software comes with no warranty of any kind.
// You make use of the Software entirely at your own risk and assume all liability arising from your use thereof.
// 
// File: Program.cs  Last modified: 2015-05-25@18:23 by Tim Long

using System;
using System.Threading.Tasks;
using TA.Ascom.ReactiveCommunications.Sample.ConsoleApp.Properties;
using TA.Ascom.ReactiveCommunications.Transactions;

namespace TA.Ascom.ReactiveCommunications.Sample.ConsoleApp
    {
    /// <summary>
    ///     This simple console application demonstrates the bare minimum requirements to set up and use the reactive
    ///     communications library for ASCOM. It creates a connection to a device, which is assumed to implement a
    ///     Meade-style protocol, and queries the Right Ascension and Declination coordinates as string values.
    /// </summary>
    /// <remarks>
    ///     Please refer to the accompanying ReadMe.md markdown file for notes on this implementation.
    /// </remarks>
    internal class Program
        {
        static void Main(string[] args)
            {
            #region Setup for Reactive ASCOM
            var connectionString = Settings.Default.ConnectionString; // Edit in App.config, default is "COM1:"
            var endpoint = DeviceEndpoint.FromConnectionString(connectionString);
            ICommunicationChannel channel = new SerialCommunicationChannel(endpoint);
            var transactionObserver = new TransactionObserver(channel);
            var processor = new ReactiveTransactionProcessor();
            processor.SubscribeTransactionObserver(transactionObserver);
            channel.Open();
            #endregion Setup for Reactive ASCOM

            #region Submit some transactions
            // Ready to go. We are going to use tasks to submit the transactions, just to demonstrate thread safety.
            var raTransaction = new TerminatedStringTransaction(":GR#") {Timeout = TimeSpan.FromSeconds(2)};
            var decTransaction = new TerminatedStringTransaction(":GD#") {Timeout = TimeSpan.FromSeconds(2)};
            Task.Run(() => processor.CommitTransaction(raTransaction));
            Task.Run(() => processor.CommitTransaction(decTransaction));
            #endregion Submit some transactions

            #region Wait for the results
            // NOTE we are using the transactions in the reverse order that we committed them, just to prove a point.
            Console.WriteLine("Waiting for declination");
            decTransaction.WaitForCompletionOrTimeout();
            Console.WriteLine("Declination: {0}", decTransaction.Response);
            Console.WriteLine("Waiting for Right Ascensions");
            raTransaction.WaitForCompletionOrTimeout();
            Console.WriteLine("Right Ascension: {0}", raTransaction.Response);
            #endregion Wait for the results

            #region Cleanup
            // To clean up, we just need to dispose the TransactionObserver and the channel is closed automatically.
            // Not strictly necessary, but good practice.
            transactionObserver.OnCompleted(); // There will be no more transactions.
            transactionObserver = null; // not necessary, but good practice.
            #endregion Cleanup
            }
        }
    }

License

Reactive ASCOM uses the MIT License.

Tim Long - Tigra Astronomy, May 2015.

TeamCity Build Status

Let Us Help You

Find us on Facebook