Ejemplo n.º 1
0
        static void Main(string[] args)
        {
            Timer tokenRenewalTimer = null;
            var   signalEvent       = new ManualResetEvent(false);
            var   signalHandler     = new UnixSignalHandler {
                Logger = Logger
            };

            try
            {
                Logger.Info("LightNotificationClient starting...");

                // intercept Unix signals (ie. CTRL-C) so we can shut down gracefully
                signalHandler.HandleSignal(signal =>
                {
                    Logger.Info("Signal received: " + signal);
                    signalEvent.Set();
                });

                // connect to pin 11
                InitBoardGpio();

                // get the initial daylight state and turn the light on or off accordingly
                Logger.Info("Getting initial light state...");
                RetryUntilSuccess(() =>
                {
                    var lightState = (IsDaylight() ? Light.Off : Light.On);
                    Logger.Info("Setting light to " + lightState.ToString().ToLower());
                    ToggleLightSwitch(lightState);
                }, numRetries: 10, delayBetweenRetriesMs: 5000);

                // get service bus settings
                var serviceBusNamespace = ConfigurationManager.AppSettings["ServiceBusNamespace"];
                var sasKeyName          = ConfigurationManager.AppSettings["SasKeyName"];
                var sasKey           = ConfigurationManager.AppSettings["SasKey"];
                var topicName        = ConfigurationManager.AppSettings["TopicName"];
                var subscriptionName = Dns.GetHostName();

                var topicAddress        = string.Format("https://{0}.servicebus.windows.net/{1}", serviceBusNamespace, topicName);
                var subscriptionAddress = string.Format("{0}/Subscriptions/{1}", topicAddress, subscriptionName);

                // create service bus http client with SAS credentials good for 60 minutes
                HttpClientHelper.DisableServerCertificateValidation = true;
                var httpClientHelper = new HttpClientHelper(
                    serviceBusNamespace, sasKeyName, sasKey, AuthTokenExpirationMinutes)
                {
                    Logger = Logger
                };

                // setup SAS token auto-renewal timer
                tokenRenewalTimer = new Timer(x =>
                {
                    httpClientHelper.RenewSasToken(AuthTokenExpirationMinutes);
                    Logger.Info("SAS token renewed");
                }, null, RenewalInterval, RenewalInterval);

                // verify the topic exists
                Logger.Info("Querying service bus topic \"{0}\"...", topicName);
                RetryUntilSuccess(() =>
                {
                    var queryTopicResponse = httpClientHelper.GetEntity(topicAddress).Result;
                    var content            = Encoding.UTF8.GetString(queryTopicResponse);
                    if (!content.Contains("TopicDescription"))
                    {
                        throw new Exception(string.Format("Service bus topic \"{0}\" does not exist.", topicName));
                    }
                }, (exception) =>
                {       // anonymous function to handle 401 unauthorized error, manually kick off the SAS renewal
                    if (exception is UnauthorizedAccessException)
                    {
                        HandleUnauthorizedAccessException(httpClientHelper, tokenRenewalTimer);
                    }
                });

                // get subscription configuration
                var subscriptionDescriptionPath = Path.Combine(Path.GetDirectoryName(
                                                                   Assembly.GetExecutingAssembly().Location), "SubscriptionDescription.xml");
                var subscriptionDescription = Encoding.UTF8.GetBytes(File.ReadAllText(subscriptionDescriptionPath));

                // create subscription
                Logger.Info("Registering subscription \"{0}\"...", subscriptionName);
                RetryUntilSuccess(() => httpClientHelper.CreateEntity(subscriptionAddress, subscriptionDescription).Wait(),
                                  (exception) =>
                {       // anonymous function to handle 401 unauthorized error, manually kick off the SAS renewal
                    if (exception is UnauthorizedAccessException)
                    {
                        HandleUnauthorizedAccessException(httpClientHelper, tokenRenewalTimer);
                    }
                });

                // delete current subscription rule
                Logger.Info("Deleting current subscription rule...");
                RetryUntilSuccess(() => httpClientHelper.DeleteEntity(subscriptionAddress + "/Rules/RecipientFilter").Wait(),
                                  (exception) =>
                {       // anonymous function to handle 401 unauthorized error, manually kick off the SAS renewal
                    if (exception is UnauthorizedAccessException)
                    {
                        HandleUnauthorizedAccessException(httpClientHelper, tokenRenewalTimer);
                    }
                });

                // create rule configuration
                var ruleDescriptionPath = Path.Combine(Path.GetDirectoryName(
                                                           Assembly.GetExecutingAssembly().Location), "RuleDescription.xml");
                var filterQuery     = string.Format("RecipientId = '*' OR RecipientId = '{0}'", Dns.GetHostName().ToLower());
                var ruleDescription = Encoding.UTF8.GetBytes(string.Format(File.ReadAllText(ruleDescriptionPath), filterQuery));

                // create subscription rule
                Logger.Info("Creating subscription rule...");
                RetryUntilSuccess(() => httpClientHelper.CreateEntity(subscriptionAddress + "/Rules/RecipientFilter", ruleDescription).Wait(),
                                  (exception) =>
                {       // anonymous function to handle 401 unauthorized error, manually kick off the SAS renewal
                    if (exception is UnauthorizedAccessException)
                    {
                        HandleUnauthorizedAccessException(httpClientHelper, tokenRenewalTimer);
                    }
                });

                // now delete default subscription rule
                Logger.Info("Deleting default subscription rule...");
                RetryUntilSuccess(() => httpClientHelper.DeleteEntity(subscriptionAddress + "/Rules/$Default").Wait(),
                                  (exception) =>
                {       // anonymous function to handle 401 unauthorized error, manually kick off the SAS renewal
                    if (exception is UnauthorizedAccessException)
                    {
                        HandleUnauthorizedAccessException(httpClientHelper, tokenRenewalTimer);
                    }
                });

                var       consecutiveErrors = 0;
                Exception lastError         = null;
                Logger.Info("Waiting for messages...");

                // start the message receiver loop
                while (!signalEvent.WaitOne(TimeSpan.Zero))
                {
                    try
                    {
#if DEBUG
                        const int timeoutInSeconds = 5;
#else
                        const int timeoutInSeconds = 60;
#endif
                        // Receive and delete message from the subscription.
                        var message = httpClientHelper.ReceiveAndDeleteMessage(subscriptionAddress, timeoutInSeconds).Result;
                        if (message != null)
                        {
                            Logger.Info("Received message: " + message.BrokerProperties.MessageId);
                            var light = (Light)Enum.Parse(typeof(Light), message.CustomProperties["LightState"]);
                            Logger.Info("Turning light " + light.ToString().ToLower());
                            ToggleLightSwitch(light);
                        }
                        consecutiveErrors = 0;
                    }
                    catch (AggregateException ae)
                    {
                        foreach (var ex in ae.InnerExceptions)
                        {
                            if (ex is UnauthorizedAccessException)
                            {
                                HandleUnauthorizedAccessException(httpClientHelper, tokenRenewalTimer);
                            }
                            else
                            {
                                Logger.Error(ex.Message);
                            }

                            lastError = ex;
                        }

                        Thread.Sleep(1000);
                        consecutiveErrors++;
                    }
                    catch (Exception ex)
                    {
                        Logger.Error(ex.Message);
                        lastError = ex;
                        Thread.Sleep(1000);
                        consecutiveErrors++;
                    }

                    if (consecutiveErrors >= 100)
                    {
                        throw new Exception("Unrecoverable error, giving up", lastError); // something REALLY wrong, abort mission
                    }
                    if (consecutiveErrors >= 10)
                    {
                        Thread.Sleep(TimeSpan.FromMinutes(1)); // something wrong here (network issues?), let's throttle down
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Error("Fatal error: " + ex.Message);
                var errorMessage = ex.Message;
                if (ex.InnerException != null)
                {
                    errorMessage += string.Format(": {0}", ex.InnerException.Message);
                }

                SendErrorMessage(errorMessage);
            }
            finally
            {
                if (tokenRenewalTimer != null)
                {
                    tokenRenewalTimer.Dispose();
                }

                if (_gpio != null)
                {
                    _gpio.Close();
                }

                signalHandler.Dispose();
                Logger.Info("Exiting program.");
            }
        }