Пример #1
0
        private static void Main(string[] args)
        {
            #region Setup for Reactive ASCOM

            var connectionString     = Settings.Default.ConnectionString; // Edit in App.config, default is "COM1:"
            var channelFactory       = new ChannelFactory();
            var channel              = channelFactory.FromConnectionString(connectionString);
            var transactionObserver  = new TransactionObserver(channel);
            var transactionProcessor = new ReactiveTransactionProcessor();
            transactionProcessor.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)
            };
            // The terminator and initiator are optional parameters and default to values that work for Meade style protocols.
            var decTransaction = new TerminatedStringTransaction(":GD#")
            {
                Timeout = TimeSpan.FromSeconds(2)
            };
            Task.Run(() => transactionProcessor.CommitTransaction(raTransaction));
            Task.Run(() => transactionProcessor.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 transaction processor. This terminates the
             * sequence of transactions, which causes the TransactionObserver's OnCompleted method
             * to be called, which unsubscribes the receive sequence, which closes the
             * communications channel
             */
            transactionProcessor.Dispose();

            #endregion Cleanup

            Console.WriteLine("Press ENTER:");
            Console.ReadLine();
        }
 /// <summary>
 ///     Destroys the transaction processor and its dependencies. Ensures that the
 ///     <see cref="Channel" /> is closed. Once this method has been called, the
 ///     <see cref="Channel" /> and <see cref="Endpoint" /> properties will be null. A new
 ///     connection to the same endpoint can be created by calling
 ///     <see cref="CreateTransactionProcessor" /> again.
 /// </summary>
 public void DestroyTransactionProcessor()
 {
     processor?.Dispose();
     processor = null; // [Sentinel]
     observer  = null;
     if (Channel?.IsOpen ?? false)
     {
         Channel.Close();
     }
     Channel?.Dispose();
     Channel = null; // [Sentinel]
     GC.Collect(3, GCCollectionMode.Forced, blocking: true);
 }
Пример #3
0
        private static void Main(string[] args)
        {
            #region Setup for Reactive ASCOM

            /*
             * First create our channel factory.
             * This is also the place where we can inject a logging service.
             * If you don't provide a logging service here, there will be no logging!
             * Note that logging is configured in NLog.config.
             * See also: TA.Utils.Core.Diagnostics
             *
             * The channel factory knows about serial ports by default, but we are going to
             * register a custom channel implementation that contains a hardware simulator.
             * (the simulator is abstracted from the Digital Domeworks dome driver).
             * If you make your own custom channels, you can register them the same way.
             */
            var log = new LoggingService(); // TA.Utils.Logging.NLog
            log.Info()
            .Message("Program start. Version {gitversion}", GitVersion.GitInformationalVersion)
            .Property("CommitSha", GitVersion.GitCommitSha)
            .Property("CommitDate", GitVersion.GitCommitDate)
            .Write();

            var channelFactory = new ChannelFactory(log);
            channelFactory.RegisterChannelType(
                SimulatorEndpoint.IsConnectionStringValid,
                SimulatorEndpoint.FromConnectionString,
                endpoint => new SimulatorCommunicationsChannel((SimulatorEndpoint)endpoint, log));

            /*
             * Now get our connection string from the AppSettings.json file.
             */

            var connectionString = Configuration["ConnectionString"];

            /*
             * Create the communications channel and connect it to the transaction observer.
             * The TransactionObserver is the beating heart of Rx Comms and is where everything is synchronized.
             */
            var channel             = channelFactory.FromConnectionString(connectionString);
            var transactionObserver = new TransactionObserver(channel);

            /*
             * TransactionObsever is an IObservable<DeviceTransaction>, so we need a sequence for it to observe.
             * The transaction sequence can be created by any convenient means, and Rx Comms provides
             * a handy helper class for doing this, called ReactiveTransactionProcessor.
             *
             * However the sequence is created, it must be fed into the transaction observer
             * by creating a subscription. ReactiveTransactionProcessor has a helper method for that.
             * The helper method also gives us a way to rate-limit commands to the hardware, if needed.
             */
            var transactionProcessor = new ReactiveTransactionProcessor();
            transactionProcessor.SubscribeTransactionObserver(transactionObserver, TimeSpan.FromSeconds(1));
            channel.Open();
            #endregion Setup for Reactive ASCOM

            // All set up and ready to go.
            try
            {
                // First we'll create a StatusTransaction and get the device's status.
                var statusTransaction = new StatusTransaction();
                transactionProcessor.CommitTransaction(statusTransaction);
                statusTransaction.WaitForCompletionOrTimeout(); // There is also an async version

                // If the transaction failed, log and throw TransactionException.
                TransactionExtensions.ThrowIfFailed(statusTransaction, log);

                // Now we can retrieve the results and use them.
                // If the transaction succeeded, we should be assured of a valid result.
                // When you write your own transactions, make sure this is so!
                var status = statusTransaction.HardwareStatus;
                log.Debug().Message("Got status: {deviceStatus}", status).Write();

                // Get the user GPIO pins and flip the value of pin 0.
                var gpioPins        = status.UserPins;
                var gpioPinsToWrite = gpioPins.WithBitSetTo(0, !gpioPins[0]);

                // Now we'll use another transaction to set the new GPIO pin state.
                var gpioTransaction = new SetUserPinTransaction(gpioPinsToWrite);
                transactionProcessor.CommitTransaction(gpioTransaction);
                gpioTransaction.WaitForCompletionOrTimeout();

                TransactionExtensions.ThrowIfFailed(gpioTransaction, log);
                var newPinState = gpioTransaction.UserPins;
                if (newPinState != gpioPinsToWrite)
                {
                    throw new ApplicationException("Failed to write GPIO pins");
                }
                log.Info()
                .Message("Successfully changed GPIO pins {oldState} => {newState}", gpioPins, newPinState)
                .Write();
            }
            catch (TransactionException exception)
            {
                log.Error().Exception(exception)
                .Message("Transaction failed {transaction}", exception.Transaction)
                .Write();
                Console.WriteLine(exception);
            }
            catch (ApplicationException exception)
            {
                log.Error().Message("Failed to set teh GPIO pins.").Exception(exception).Write();
            }
            finally
            {
                /*
                 * We just need to dispose the transaction processor. This terminates the
                 * sequence of transactions, which causes the TransactionObserver's OnCompleted method
                 * to be called, which unsubscribes the receive sequence, which closes the
                 * communications channel.
                 */
                transactionProcessor.Dispose();
                log.Info().Message("Cleanup complete").Write();

                /*
                 * It is best practice to explicitly shut down the logging service,
                 * otherwise any buffered log entries might not be written to the log.
                 * This may take a few seconds if you are using a slow log target.
                 */
                log.Shutdown();
            }
        }