static void Main(string[] args) { var mre = new ManualResetEvent(false); var signalHandler = new UnixSignalHandler(); var handled = signalHandler.HandleSignal(x => { Console.WriteLine("Signal received: " + x); mre.Set(); }); if (handled) { Console.WriteLine("Waiting for a signal..."); mre.WaitOne(); } else { Console.WriteLine("Unix signals not supported on this platform"); } Console.WriteLine("Exiting program"); signalHandler.Dispose(); }
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."); } }