PageRenderTime 49ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/WP7.1/BabelCam/C#/BabelCam.Worker/WorkerRole.cs

#
C# | 356 lines | 277 code | 50 blank | 29 comment | 19 complexity | 0766663b96b91888d04e7772621941a9 MD5 | raw file
  1. // ----------------------------------------------------------------------------------
  2. // Microsoft Developer & Platform Evangelism
  3. //
  4. // Copyright (c) Microsoft Corporation. All rights reserved.
  5. //
  6. // THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
  7. // EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
  8. // OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
  9. // ----------------------------------------------------------------------------------
  10. // The example companies, organizations, products, domain names,
  11. // e-mail addresses, logos, people, places, and events depicted
  12. // herein are fictitious. No association with any real company,
  13. // organization, product, domain name, email address, logo, person,
  14. // places, or events is intended or should be inferred.
  15. // ----------------------------------------------------------------------------------
  16. namespace Microsoft.Samples.BabelCam.Worker
  17. {
  18. using System;
  19. using System.Diagnostics;
  20. using System.Globalization;
  21. using System.IO;
  22. using System.Linq;
  23. using System.Net;
  24. using System.ServiceModel;
  25. using System.Text;
  26. using System.Threading;
  27. using Microsoft.Samples.BabelCam.Infrastructure.Helpers;
  28. using Microsoft.Samples.BabelCam.Infrastructure.Models;
  29. using Microsoft.Samples.BabelCam.Worker.Helpers;
  30. using Microsoft.Samples.BabelCam.Worker.Models;
  31. using Microsoft.Samples.Hawaii.Services.Client;
  32. using Microsoft.Samples.Hawaii.Services.Client.Ocr;
  33. using Microsoft.WindowsAzure;
  34. using Microsoft.WindowsAzure.ServiceRuntime;
  35. using Microsoft.WindowsAzure.StorageClient;
  36. using WindowsPhone.Recipes.Push.Messages;
  37. public class WorkerRole : RoleEntryPoint
  38. {
  39. private string ocrServiceHostName;
  40. private string ocrServiceSignature;
  41. private string hawaiiApplicationId;
  42. private string translatorBaseAddress;
  43. private string translatorAppId;
  44. private string logContainerName;
  45. private string errorContainerName;
  46. private int batchSize;
  47. private int pollIntervalMiliseconds;
  48. private CloudStorageAccount account;
  49. private CloudBlobClient cloudBlobclient;
  50. private CloudQueue requestQueue;
  51. private ITranslatedWordsRepository translatedWordsRepository;
  52. private IPushUserEndpointsRepository pushUserEndpointsRepository;
  53. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "This method initializes the Worker role.")]
  54. public override bool OnStart()
  55. {
  56. // Set the maximum number of concurrent connections
  57. ServicePointManager.DefaultConnectionLimit = 12;
  58. // For information on handling configuration changes
  59. // see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.
  60. RoleEnvironment.Changing += this.RoleEnvironmentChanging;
  61. // This code sets up a handler to update CloudStorageAccount instances when their corresponding
  62. // configuration settings change in the service configuration file.
  63. CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) =>
  64. {
  65. // Provide the configSetter with the initial value
  66. configSetter(RoleEnvironment.GetConfigurationSettingValue(configName));
  67. RoleEnvironment.Changed += (sender, arg) =>
  68. {
  69. if (arg.Changes.OfType<RoleEnvironmentConfigurationSettingChange>()
  70. .Any((change) => (change.ConfigurationSettingName == configName)))
  71. {
  72. // The corresponding configuration setting has changed, propagate the value
  73. if (!configSetter(RoleEnvironment.GetConfigurationSettingValue(configName)))
  74. {
  75. // In this case, the change to the storage account credentials in the
  76. // service configuration is significant enough that the role needs to be
  77. // recycled in order to use the latest settings. (for example, the
  78. // endpoint has changed)
  79. RoleEnvironment.RequestRecycle();
  80. }
  81. }
  82. };
  83. });
  84. this.account = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
  85. this.CreateCloudContainers(this.account, ConfigReader.GetConfigValue("RequestQueueName"), ConfigReader.GetConfigValue("ImageContainerName"));
  86. this.translatedWordsRepository = new TranslatedWordsServiceContext();
  87. this.pushUserEndpointsRepository = new UserTablesServiceContext();
  88. this.ocrServiceHostName = ConfigReader.GetConfigValue("OcrServiceHostName");
  89. this.ocrServiceSignature = ConfigReader.GetConfigValue("OcrServiceSignature");
  90. this.hawaiiApplicationId = ConfigReader.GetConfigValue("HawaiiApplicationId");
  91. this.translatorAppId = ConfigReader.GetConfigValue("TranslatorServiceAppId");
  92. this.translatorBaseAddress = ConfigReader.GetConfigValue("TranslatorServiceBaseAddress");
  93. this.logContainerName = ConfigReader.GetConfigValue("LogContainerName");
  94. this.errorContainerName = ConfigReader.GetConfigValue("ErrorContainerName");
  95. this.batchSize = int.Parse(ConfigReader.GetConfigValue("BatchSize"), CultureInfo.InvariantCulture);
  96. this.pollIntervalMiliseconds = int.Parse(ConfigReader.GetConfigValue("PollIntervalMiliseconds"), CultureInfo.InvariantCulture);
  97. return base.OnStart();
  98. }
  99. public override void Run()
  100. {
  101. // This is a sample worker implementation. Replace with your logic.
  102. Trace.WriteLine("BabelCam.Worker entry point called", "Information");
  103. while (true)
  104. {
  105. try
  106. {
  107. foreach (var rawMessage in this.requestQueue.GetMessages(this.batchSize))
  108. {
  109. this.requestQueue.DeleteMessage(rawMessage);
  110. var request = Extensions.Deserialize<OcrRequest>(rawMessage.AsString);
  111. if (string.IsNullOrWhiteSpace(request.BlobName) || !request.BlobName.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase))
  112. {
  113. continue;
  114. }
  115. var container = this.cloudBlobclient.GetContainerReference(request.ContainerName);
  116. var blob = container.GetBlobReference(request.BlobName);
  117. try
  118. {
  119. ProcessOutput processOutput = null;
  120. using (var stream = new MemoryStream())
  121. {
  122. blob.DownloadToStream(stream);
  123. processOutput = this.ProcessImage(stream);
  124. }
  125. var words = string.Empty;
  126. var wordCount = 0;
  127. var status = string.Empty;
  128. if (processOutput != null)
  129. {
  130. if (!processOutput.Attributes.TryGetValue("Status", out status))
  131. {
  132. status = string.Empty;
  133. }
  134. if (!processOutput.Attributes.TryGetValue("Words", out words))
  135. {
  136. words = string.Empty;
  137. }
  138. var wordCountString = string.Empty;
  139. processOutput.Attributes.TryGetValue("WordCount", out wordCountString);
  140. int.TryParse(wordCountString, out wordCount);
  141. }
  142. var message = string.Empty;
  143. var translationRowKey = string.Empty;
  144. if (status.Equals(Status.Success.ToString(), StringComparison.OrdinalIgnoreCase))
  145. {
  146. if (wordCount > 0)
  147. {
  148. var translated = this.Translate(words, request.FromLanguage, request.ToLanguage);
  149. var wordEntity = new WordsEntity(translated, blob.Uri.ToString(), request.UserNameIdentifier);
  150. this.translatedWordsRepository.AddTranslatedWords(wordEntity);
  151. translationRowKey = wordEntity.RowKey;
  152. this.LogInformation(string.Concat("orig (", request.FromLanguage, ") for ", request.UserNameIdentifier, ": ", words));
  153. this.LogInformation(string.Concat("trans (", request.ToLanguage, ") for ", request.UserNameIdentifier, ": ", translated));
  154. message = translated;
  155. }
  156. else
  157. {
  158. message = "[No text was found]";
  159. translationRowKey = string.Empty;
  160. }
  161. }
  162. else
  163. {
  164. message = "[The OCR conversion failed]";
  165. translationRowKey = string.Empty;
  166. }
  167. this.SendPushNotification(request.UserNameIdentifier, message, translationRowKey);
  168. }
  169. catch (Exception exception)
  170. {
  171. this.SendPushNotification(request.UserNameIdentifier, "There was an error processing your translation request.");
  172. this.LogError(exception);
  173. }
  174. }
  175. }
  176. catch (Exception exception)
  177. {
  178. this.LogError(exception);
  179. }
  180. Thread.Sleep(this.pollIntervalMiliseconds);
  181. Trace.WriteLine("Working", "Information");
  182. }
  183. }
  184. private void CreateCloudContainers(CloudStorageAccount account, string requestQueueName, string imageContainerName)
  185. {
  186. this.cloudBlobclient = account.CreateCloudBlobClient();
  187. var imageContainer = this.cloudBlobclient.GetContainerReference(imageContainerName);
  188. imageContainer.CreateIfNotExist();
  189. imageContainer.SetPermissions(new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Blob });
  190. this.requestQueue = account.CreateCloudQueueClient().GetQueueReference(requestQueueName);
  191. this.requestQueue.CreateIfNotExist();
  192. CloudStorageInitializer.InitializeCloudStorage(account);
  193. }
  194. private OcrServiceResult ConvertToText(byte[] photoBuffer)
  195. {
  196. var ocrService = new OcrService(this.ocrServiceHostName, this.ocrServiceSignature);
  197. return (OcrServiceResult)ocrService.RecognizeImage(this.hawaiiApplicationId, photoBuffer);
  198. }
  199. private ProcessOutput ProcessImage(Stream imageStream)
  200. {
  201. var processOutput = new ProcessOutput();
  202. try
  203. {
  204. var words = string.Empty;
  205. byte[] buffer = new byte[imageStream.Length];
  206. imageStream.Seek(0, SeekOrigin.Begin);
  207. imageStream.Read(buffer, 0, buffer.Length);
  208. var result = this.ConvertToText(buffer);
  209. if ((result.Status == Status.Success) && (result.OcrResult.OcrTexts.Count > 0))
  210. {
  211. var wordCount = 0;
  212. var textSkew = 0;
  213. var sb = new StringBuilder();
  214. foreach (var item in result.OcrResult.OcrTexts)
  215. {
  216. wordCount += item.Words.Count;
  217. sb.AppendLine(item.Text);
  218. textSkew += int.Parse(item.Skew, System.Globalization.NumberStyles.Integer);
  219. }
  220. var avgTextSkew = textSkew / result.OcrResult.OcrTexts.Count;
  221. if (wordCount != 0)
  222. {
  223. processOutput.Attributes["WordCount"] = wordCount.ToString();
  224. processOutput.Attributes["Words"] = sb.ToString();
  225. processOutput.Attributes["ConfidenceAvg"] = avgTextSkew.ToString();
  226. }
  227. }
  228. processOutput.Attributes["Status"] = result.Status.ToString();
  229. }
  230. catch (Exception exception)
  231. {
  232. this.LogError(exception);
  233. }
  234. return processOutput;
  235. }
  236. private string Translate(string words, string fromLanguage, string toLanguage)
  237. {
  238. var translated = words;
  239. if (!fromLanguage.Equals(toLanguage, StringComparison.OrdinalIgnoreCase))
  240. {
  241. try
  242. {
  243. using (var client = new TranslatorService.LanguageServiceClient(new BasicHttpBinding(), new EndpointAddress(this.translatorBaseAddress)))
  244. {
  245. var options = new TranslatorService.TranslateOptions();
  246. var response = client.GetTranslations(this.translatorAppId, words, fromLanguage, toLanguage, 1, options);
  247. translated = response.Translations[0].TranslatedText;
  248. }
  249. }
  250. catch (Exception exception)
  251. {
  252. this.LogError(exception);
  253. }
  254. }
  255. return translated;
  256. }
  257. private void SendPushNotification(string userName, string message, string translationRowKey = "")
  258. {
  259. var uris = this.pushUserEndpointsRepository.GetPushUsersByName(userName).Select(u => u.ChannelUri);
  260. var rawData = Encoding.UTF8.GetBytes(message);
  261. var rawMessage = new RawPushNotificationMessage
  262. {
  263. SendPriority = MessageSendPriority.High,
  264. RawData = (rawData.Length <= RawPushNotificationMessage.MaxPayloadSize)
  265. ? rawData
  266. : Encoding.UTF8.GetBytes(Extensions.Serialize<string>(translationRowKey))
  267. };
  268. foreach (var uri in uris)
  269. {
  270. try
  271. {
  272. rawMessage.Send(new Uri(uri));
  273. }
  274. catch (Exception exception)
  275. {
  276. this.LogError(exception);
  277. }
  278. }
  279. }
  280. private void LogInformation(string message)
  281. {
  282. var infoContainer = this.cloudBlobclient.GetContainerReference(this.logContainerName);
  283. infoContainer.CreateIfNotExist();
  284. var infoBlob = infoContainer.GetBlobReference((DateTime.MaxValue - DateTime.UtcNow).Ticks.ToString("d19") + ".txt");
  285. infoBlob.Properties.ContentType = "text/plain";
  286. infoBlob.UploadText(message);
  287. }
  288. private void LogError(Exception exception)
  289. {
  290. var errorsContainer = this.cloudBlobclient.GetContainerReference(this.errorContainerName);
  291. errorsContainer.CreateIfNotExist();
  292. var errorBlob = errorsContainer.GetBlobReference(string.Concat((DateTime.MaxValue - DateTime.UtcNow).Ticks.ToString("d19"), ".txt"));
  293. errorBlob.Properties.ContentType = "text/plain";
  294. errorBlob.UploadText(exception.ToString());
  295. }
  296. private void RoleEnvironmentChanging(object sender, RoleEnvironmentChangingEventArgs e)
  297. {
  298. // If a configuration setting is changing
  299. if (e.Changes.Any(change => change is RoleEnvironmentConfigurationSettingChange))
  300. {
  301. // Set e.Cancel to true to restart this role instance
  302. e.Cancel = true;
  303. }
  304. }
  305. }
  306. }