/WP7.1/BabelCam/C#/BabelCam.Worker/WorkerRole.cs
C# | 356 lines | 277 code | 50 blank | 29 comment | 19 complexity | 0766663b96b91888d04e7772621941a9 MD5 | raw file
- // ----------------------------------------------------------------------------------
- // Microsoft Developer & Platform Evangelism
- //
- // Copyright (c) Microsoft Corporation. All rights reserved.
- //
- // THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
- // EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
- // OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
- // ----------------------------------------------------------------------------------
- // The example companies, organizations, products, domain names,
- // e-mail addresses, logos, people, places, and events depicted
- // herein are fictitious. No association with any real company,
- // organization, product, domain name, email address, logo, person,
- // places, or events is intended or should be inferred.
- // ----------------------------------------------------------------------------------
-
- namespace Microsoft.Samples.BabelCam.Worker
- {
- using System;
- using System.Diagnostics;
- using System.Globalization;
- using System.IO;
- using System.Linq;
- using System.Net;
- using System.ServiceModel;
- using System.Text;
- using System.Threading;
- using Microsoft.Samples.BabelCam.Infrastructure.Helpers;
- using Microsoft.Samples.BabelCam.Infrastructure.Models;
- using Microsoft.Samples.BabelCam.Worker.Helpers;
- using Microsoft.Samples.BabelCam.Worker.Models;
- using Microsoft.Samples.Hawaii.Services.Client;
- using Microsoft.Samples.Hawaii.Services.Client.Ocr;
- using Microsoft.WindowsAzure;
- using Microsoft.WindowsAzure.ServiceRuntime;
- using Microsoft.WindowsAzure.StorageClient;
- using WindowsPhone.Recipes.Push.Messages;
-
- public class WorkerRole : RoleEntryPoint
- {
- private string ocrServiceHostName;
- private string ocrServiceSignature;
- private string hawaiiApplicationId;
- private string translatorBaseAddress;
- private string translatorAppId;
-
- private string logContainerName;
- private string errorContainerName;
-
- private int batchSize;
- private int pollIntervalMiliseconds;
-
- private CloudStorageAccount account;
- private CloudBlobClient cloudBlobclient;
- private CloudQueue requestQueue;
-
- private ITranslatedWordsRepository translatedWordsRepository;
- private IPushUserEndpointsRepository pushUserEndpointsRepository;
-
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "This method initializes the Worker role.")]
- public override bool OnStart()
- {
- // Set the maximum number of concurrent connections
- ServicePointManager.DefaultConnectionLimit = 12;
-
- // For information on handling configuration changes
- // see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.
- RoleEnvironment.Changing += this.RoleEnvironmentChanging;
-
- // This code sets up a handler to update CloudStorageAccount instances when their corresponding
- // configuration settings change in the service configuration file.
- CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) =>
- {
- // Provide the configSetter with the initial value
- configSetter(RoleEnvironment.GetConfigurationSettingValue(configName));
-
- RoleEnvironment.Changed += (sender, arg) =>
- {
- if (arg.Changes.OfType<RoleEnvironmentConfigurationSettingChange>()
- .Any((change) => (change.ConfigurationSettingName == configName)))
- {
- // The corresponding configuration setting has changed, propagate the value
- if (!configSetter(RoleEnvironment.GetConfigurationSettingValue(configName)))
- {
- // In this case, the change to the storage account credentials in the
- // service configuration is significant enough that the role needs to be
- // recycled in order to use the latest settings. (for example, the
- // endpoint has changed)
- RoleEnvironment.RequestRecycle();
- }
- }
- };
- });
-
- this.account = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
- this.CreateCloudContainers(this.account, ConfigReader.GetConfigValue("RequestQueueName"), ConfigReader.GetConfigValue("ImageContainerName"));
-
- this.translatedWordsRepository = new TranslatedWordsServiceContext();
- this.pushUserEndpointsRepository = new UserTablesServiceContext();
-
- this.ocrServiceHostName = ConfigReader.GetConfigValue("OcrServiceHostName");
- this.ocrServiceSignature = ConfigReader.GetConfigValue("OcrServiceSignature");
- this.hawaiiApplicationId = ConfigReader.GetConfigValue("HawaiiApplicationId");
- this.translatorAppId = ConfigReader.GetConfigValue("TranslatorServiceAppId");
- this.translatorBaseAddress = ConfigReader.GetConfigValue("TranslatorServiceBaseAddress");
-
- this.logContainerName = ConfigReader.GetConfigValue("LogContainerName");
- this.errorContainerName = ConfigReader.GetConfigValue("ErrorContainerName");
-
- this.batchSize = int.Parse(ConfigReader.GetConfigValue("BatchSize"), CultureInfo.InvariantCulture);
- this.pollIntervalMiliseconds = int.Parse(ConfigReader.GetConfigValue("PollIntervalMiliseconds"), CultureInfo.InvariantCulture);
-
- return base.OnStart();
- }
-
- public override void Run()
- {
- // This is a sample worker implementation. Replace with your logic.
- Trace.WriteLine("BabelCam.Worker entry point called", "Information");
-
- while (true)
- {
- try
- {
- foreach (var rawMessage in this.requestQueue.GetMessages(this.batchSize))
- {
- this.requestQueue.DeleteMessage(rawMessage);
- var request = Extensions.Deserialize<OcrRequest>(rawMessage.AsString);
-
- if (string.IsNullOrWhiteSpace(request.BlobName) || !request.BlobName.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase))
- {
- continue;
- }
-
- var container = this.cloudBlobclient.GetContainerReference(request.ContainerName);
- var blob = container.GetBlobReference(request.BlobName);
-
- try
- {
- ProcessOutput processOutput = null;
- using (var stream = new MemoryStream())
- {
- blob.DownloadToStream(stream);
-
- processOutput = this.ProcessImage(stream);
- }
-
- var words = string.Empty;
- var wordCount = 0;
- var status = string.Empty;
- if (processOutput != null)
- {
- if (!processOutput.Attributes.TryGetValue("Status", out status))
- {
- status = string.Empty;
- }
-
- if (!processOutput.Attributes.TryGetValue("Words", out words))
- {
- words = string.Empty;
- }
-
- var wordCountString = string.Empty;
- processOutput.Attributes.TryGetValue("WordCount", out wordCountString);
- int.TryParse(wordCountString, out wordCount);
- }
-
- var message = string.Empty;
- var translationRowKey = string.Empty;
- if (status.Equals(Status.Success.ToString(), StringComparison.OrdinalIgnoreCase))
- {
- if (wordCount > 0)
- {
- var translated = this.Translate(words, request.FromLanguage, request.ToLanguage);
-
- var wordEntity = new WordsEntity(translated, blob.Uri.ToString(), request.UserNameIdentifier);
- this.translatedWordsRepository.AddTranslatedWords(wordEntity);
- translationRowKey = wordEntity.RowKey;
-
- this.LogInformation(string.Concat("orig (", request.FromLanguage, ") for ", request.UserNameIdentifier, ": ", words));
- this.LogInformation(string.Concat("trans (", request.ToLanguage, ") for ", request.UserNameIdentifier, ": ", translated));
-
- message = translated;
- }
- else
- {
- message = "[No text was found]";
- translationRowKey = string.Empty;
- }
- }
- else
- {
- message = "[The OCR conversion failed]";
- translationRowKey = string.Empty;
- }
-
- this.SendPushNotification(request.UserNameIdentifier, message, translationRowKey);
- }
- catch (Exception exception)
- {
- this.SendPushNotification(request.UserNameIdentifier, "There was an error processing your translation request.");
- this.LogError(exception);
- }
- }
- }
- catch (Exception exception)
- {
- this.LogError(exception);
- }
-
- Thread.Sleep(this.pollIntervalMiliseconds);
- Trace.WriteLine("Working", "Information");
- }
- }
-
- private void CreateCloudContainers(CloudStorageAccount account, string requestQueueName, string imageContainerName)
- {
- this.cloudBlobclient = account.CreateCloudBlobClient();
- var imageContainer = this.cloudBlobclient.GetContainerReference(imageContainerName);
- imageContainer.CreateIfNotExist();
- imageContainer.SetPermissions(new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Blob });
-
- this.requestQueue = account.CreateCloudQueueClient().GetQueueReference(requestQueueName);
- this.requestQueue.CreateIfNotExist();
-
- CloudStorageInitializer.InitializeCloudStorage(account);
- }
-
- private OcrServiceResult ConvertToText(byte[] photoBuffer)
- {
- var ocrService = new OcrService(this.ocrServiceHostName, this.ocrServiceSignature);
- return (OcrServiceResult)ocrService.RecognizeImage(this.hawaiiApplicationId, photoBuffer);
- }
-
- private ProcessOutput ProcessImage(Stream imageStream)
- {
- var processOutput = new ProcessOutput();
- try
- {
- var words = string.Empty;
- byte[] buffer = new byte[imageStream.Length];
-
- imageStream.Seek(0, SeekOrigin.Begin);
- imageStream.Read(buffer, 0, buffer.Length);
-
- var result = this.ConvertToText(buffer);
- if ((result.Status == Status.Success) && (result.OcrResult.OcrTexts.Count > 0))
- {
- var wordCount = 0;
- var textSkew = 0;
- var sb = new StringBuilder();
- foreach (var item in result.OcrResult.OcrTexts)
- {
- wordCount += item.Words.Count;
- sb.AppendLine(item.Text);
- textSkew += int.Parse(item.Skew, System.Globalization.NumberStyles.Integer);
- }
-
- var avgTextSkew = textSkew / result.OcrResult.OcrTexts.Count;
- if (wordCount != 0)
- {
- processOutput.Attributes["WordCount"] = wordCount.ToString();
- processOutput.Attributes["Words"] = sb.ToString();
- processOutput.Attributes["ConfidenceAvg"] = avgTextSkew.ToString();
- }
- }
-
- processOutput.Attributes["Status"] = result.Status.ToString();
- }
- catch (Exception exception)
- {
- this.LogError(exception);
- }
-
- return processOutput;
- }
-
- private string Translate(string words, string fromLanguage, string toLanguage)
- {
- var translated = words;
- if (!fromLanguage.Equals(toLanguage, StringComparison.OrdinalIgnoreCase))
- {
- try
- {
- using (var client = new TranslatorService.LanguageServiceClient(new BasicHttpBinding(), new EndpointAddress(this.translatorBaseAddress)))
- {
- var options = new TranslatorService.TranslateOptions();
- var response = client.GetTranslations(this.translatorAppId, words, fromLanguage, toLanguage, 1, options);
- translated = response.Translations[0].TranslatedText;
- }
- }
- catch (Exception exception)
- {
- this.LogError(exception);
- }
- }
-
- return translated;
- }
-
- private void SendPushNotification(string userName, string message, string translationRowKey = "")
- {
- var uris = this.pushUserEndpointsRepository.GetPushUsersByName(userName).Select(u => u.ChannelUri);
- var rawData = Encoding.UTF8.GetBytes(message);
- var rawMessage = new RawPushNotificationMessage
- {
- SendPriority = MessageSendPriority.High,
- RawData = (rawData.Length <= RawPushNotificationMessage.MaxPayloadSize)
- ? rawData
- : Encoding.UTF8.GetBytes(Extensions.Serialize<string>(translationRowKey))
- };
-
- foreach (var uri in uris)
- {
- try
- {
- rawMessage.Send(new Uri(uri));
- }
- catch (Exception exception)
- {
- this.LogError(exception);
- }
- }
- }
-
- private void LogInformation(string message)
- {
- var infoContainer = this.cloudBlobclient.GetContainerReference(this.logContainerName);
- infoContainer.CreateIfNotExist();
-
- var infoBlob = infoContainer.GetBlobReference((DateTime.MaxValue - DateTime.UtcNow).Ticks.ToString("d19") + ".txt");
- infoBlob.Properties.ContentType = "text/plain";
- infoBlob.UploadText(message);
- }
-
- private void LogError(Exception exception)
- {
- var errorsContainer = this.cloudBlobclient.GetContainerReference(this.errorContainerName);
- errorsContainer.CreateIfNotExist();
-
- var errorBlob = errorsContainer.GetBlobReference(string.Concat((DateTime.MaxValue - DateTime.UtcNow).Ticks.ToString("d19"), ".txt"));
- errorBlob.Properties.ContentType = "text/plain";
- errorBlob.UploadText(exception.ToString());
- }
-
- private void RoleEnvironmentChanging(object sender, RoleEnvironmentChangingEventArgs e)
- {
- // If a configuration setting is changing
- if (e.Changes.Any(change => change is RoleEnvironmentConfigurationSettingChange))
- {
- // Set e.Cancel to true to restart this role instance
- e.Cancel = true;
- }
- }
- }
- }