private async void InterruptGpioPin_ValueChanged(GpioPin sender, GpioPinValueChangedEventArgs args) { DateTime currentTime = DateTime.UtcNow; Debug.WriteLine($"{DateTime.UtcNow.ToLongTimeString()} Digital Input Interrupt {sender.PinNumber} triggered {args.Edge}"); if (args.Edge == this.interruptTriggerOn) { return; } // Check that enough time has passed for picture to be taken if ((currentTime - this.imageLastCapturedAtUtc) < this.debounceTimeout) { return; } this.imageLastCapturedAtUtc = currentTime; // Just incase - stop code being called while photo already in progress if (this.cameraBusy) { return; } this.cameraBusy = true; try { string localFilenameLatest = string.Format(this.localStorageimageFilenameLatestFormat, Environment.MachineName.ToLower(), currentTime); string localFilenameHistory = string.Format(this.localStorageImageFilenameHistoryFormat, Environment.MachineName.ToLower(), currentTime); StorageFile photoFile = await KnownFolders.PicturesLibrary.CreateFileAsync(localFilenameLatest, CreationCollisionOption.ReplaceExisting); ImageEncodingProperties imageProperties = ImageEncodingProperties.CreateJpeg(); await this.mediaCapture.CapturePhotoToStorageFileAsync(imageProperties, photoFile); LoggingFields imageInformation = new LoggingFields(); imageInformation.AddDateTime("TakenAtUTC", currentTime); imageInformation.AddString("LocalFilename", photoFile.Path); imageInformation.AddString("LocalFilenameHistory", localFilenameHistory); this.logging.LogEvent("Saving image(s) to local storage", imageInformation); // copy the historic image to storage if (!string.IsNullOrWhiteSpace(localFilenameHistory)) { await photoFile.CopyAsync(KnownFolders.PicturesLibrary, localFilenameHistory); this.logging.LogEvent("Image history saved to local storage"); } } catch (Exception ex) { this.logging.LogMessage("Camera photo save or upload failed " + ex.Message, LoggingLevel.Error); } finally { this.cameraBusy = false; } }
private async void ImageUpdateTimerCallback(object state) { DateTime currentTime = DateTime.UtcNow; Debug.WriteLine($"{DateTime.UtcNow.ToLongTimeString()} Timer triggered"); // Just incase - stop code being called while photo already in progress if (cameraBusy) { return; } cameraBusy = true; try { string localFilename = string.Format(localImageFilenameLatestFormat, currentTime); string folderNameHistory = string.Format(localFolderNameHistoryFormat, currentTime); string filenameHistory = string.Format(localImageFilenameHistoryFormat, currentTime); StorageFile photoFile = await KnownFolders.PicturesLibrary.CreateFileAsync(localFilename, CreationCollisionOption.ReplaceExisting); ImageEncodingProperties imageProperties = ImageEncodingProperties.CreateJpeg(); await mediaCapture.CapturePhotoToStorageFileAsync(imageProperties, photoFile); LoggingFields imageInformation = new LoggingFields(); imageInformation.AddDateTime("TakenAtUTC", currentTime); imageInformation.AddString("LocalFilename", photoFile.Path); imageInformation.AddString("FolderNameHistory", folderNameHistory); imageInformation.AddString("FilenameHistory", filenameHistory); this.logging.LogEvent("Image saved to local storage", imageInformation); // Upload the historic image to storage if (!string.IsNullOrWhiteSpace(folderNameHistory) && !string.IsNullOrWhiteSpace(filenameHistory)) { // Check to see if historic images folder exists and if it doesn't create it IStorageFolder storageFolder = (IStorageFolder)await KnownFolders.PicturesLibrary.TryGetItemAsync(folderNameHistory); if (storageFolder == null) { storageFolder = await KnownFolders.PicturesLibrary.CreateFolderAsync(folderNameHistory); } await photoFile.CopyAsync(storageFolder, filenameHistory, NameCollisionOption.ReplaceExisting); this.logging.LogEvent("Image historic saved to local storage", imageInformation); } } catch (Exception ex) { this.logging.LogMessage("Image capture or save failed " + ex.Message, LoggingLevel.Error); } finally { cameraBusy = false; } }
private async void InterruptGpioPin_ValueChanged(GpioPin sender, GpioPinValueChangedEventArgs args) { DateTime currentTime = DateTime.UtcNow; Debug.WriteLine($"{DateTime.UtcNow.ToLongTimeString()} Digital Input Interrupt {sender.PinNumber} triggered {args.Edge}"); if (args.Edge == GpioPinEdge.RisingEdge) { return; } // Just incase - stop code being called while photo already in progress if (this.cameraBusy) { return; } this.cameraBusy = true; try { string filename = string.Format(ImageFilenameFormat, currentTime); IStorageFile photoFile = await KnownFolders.PicturesLibrary.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting); ImageEncodingProperties imageProperties = ImageEncodingProperties.CreateJpeg(); await this.mediaCapture.CapturePhotoToStorageFileAsync(imageProperties, photoFile); LoggingFields imageInformation = new LoggingFields(); imageInformation.AddDateTime("TakenAtUTC", currentTime); imageInformation.AddString("Filename", filename); imageInformation.AddString("Path", photoFile.Path); this.logging.LogEvent("Captured image saved to storage", imageInformation); } catch (Exception ex) { this.logging.LogMessage("Image capture or save failed " + ex.Message, LoggingLevel.Error); } this.cameraBusy = false; }
private void ImageUpdateTimerCallback(object state) { DateTime currentTime = DateTime.UtcNow; try { string filename = string.Format(ImageFilenameFormat, currentTime); IStorageFile photoFile = KnownFolders.PicturesLibrary.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting).AsTask().Result; ImageEncodingProperties imageProperties = ImageEncodingProperties.CreateJpeg(); this.mediaCapture.CapturePhotoToStorageFileAsync(imageProperties, photoFile).AsTask().Wait(); LoggingFields imageInformation = new LoggingFields(); imageInformation.AddDateTime("TakenAtUTC", currentTime); imageInformation.AddString("Filename", filename); imageInformation.AddString("Path", photoFile.Path); this.logging.LogEvent("Image saved to storage", imageInformation); } catch (Exception ex) { this.logging.LogMessage("Image capture or save to local storage failed " + ex.Message, LoggingLevel.Error); } }
private async void InterruptGpioPin_ValueChanged(GpioPin sender, GpioPinValueChangedEventArgs args) { DateTime currentTime = DateTime.UtcNow; Debug.WriteLine($"{DateTime.UtcNow.ToLongTimeString()} Digital Input Interrupt {sender.PinNumber} triggered {args.Edge}"); if (args.Edge == this.interruptTriggerOn) { return; } // Check that enough time has passed for picture to be taken if ((currentTime - this.imageLastCapturedAtUtc) < this.debounceTimeout) { return; } this.imageLastCapturedAtUtc = currentTime; // Just incase - stop code being called while photo already in progress if (this.cameraBusy) { return; } this.cameraBusy = true; try { StorageFile photoFile = await KnownFolders.PicturesLibrary.CreateFileAsync(ImageFilenameLocal, CreationCollisionOption.ReplaceExisting); ImageEncodingProperties imageProperties = ImageEncodingProperties.CreateJpeg(); await this.mediaCapture.CapturePhotoToStorageFileAsync(imageProperties, photoFile); string azureContainernameLatest = string.Format(this.azureStorageContainerNameLatestFormat, Environment.MachineName, this.deviceMacAddress, currentTime).ToLower(); string azureFilenameLatest = string.Format(this.azureStorageimageFilenameLatestFormat, Environment.MachineName, this.deviceMacAddress, currentTime); string azureContainerNameHistory = string.Format(this.azureStorageContainerNameHistoryFormat, Environment.MachineName, this.deviceMacAddress, currentTime).ToLower(); string azureFilenameHistory = string.Format(this.azureStorageImageFilenameHistoryFormat, Environment.MachineName.ToLower(), this.deviceMacAddress, currentTime); LoggingFields imageInformation = new LoggingFields(); imageInformation.AddDateTime("TakenAtUTC", currentTime); imageInformation.AddString("LocalFilename", photoFile.Path); imageInformation.AddString("AzureContainerNameLatest", azureContainernameLatest); imageInformation.AddString("AzureFilenameLatest", azureFilenameLatest); imageInformation.AddString("AzureContainerNameHistory", azureContainerNameHistory); imageInformation.AddString("AzureFilenameHistory", azureFilenameHistory); this.logging.LogEvent("Saving image(s) to Azure storage", imageInformation); CloudStorageAccount storageAccount = CloudStorageAccount.Parse(this.azureStorageConnectionString); CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient(); // Update the latest image in storage if (!string.IsNullOrWhiteSpace(azureContainernameLatest) && !string.IsNullOrWhiteSpace(azureFilenameLatest)) { CloudBlobContainer containerLatest = blobClient.GetContainerReference(azureContainernameLatest); await containerLatest.CreateIfNotExistsAsync(); CloudBlockBlob blockBlobLatest = containerLatest.GetBlockBlobReference(azureFilenameLatest); await blockBlobLatest.UploadFromFileAsync(photoFile); this.logging.LogEvent("Image latest saved to Azure storage"); } // Upload the historic image to storage if (!string.IsNullOrWhiteSpace(azureContainerNameHistory) && !string.IsNullOrWhiteSpace(azureFilenameHistory)) { CloudBlobContainer containerHistory = blobClient.GetContainerReference(azureContainerNameHistory); await containerHistory.CreateIfNotExistsAsync(); CloudBlockBlob blockBlob = containerHistory.GetBlockBlobReference(azureFilenameHistory); await blockBlob.UploadFromFileAsync(photoFile); this.logging.LogEvent("Image historic saved to Azure storage"); } } catch (Exception ex) { this.logging.LogMessage("Camera photo save or upload failed " + ex.Message, LoggingLevel.Error); } finally { this.cameraBusy = false; } }
/// <summary> /// This method demonstrates the LoggingChannel and LoggingActivity APIs. /// </summary> /// <param name="channel"> /// The channel to use for the demonstration. This channel may have been /// constructed using a Windows 8.1 constructor or a Windows 10 constructor. /// The same APIs are supported in both cases, but the ETL events will be /// formatted a bit differently depending on how the channel was constructed. /// </param> private void DemonstrateLogging(LoggingChannel channel) { // Whenever any ETW session changes the way it is listening to this // channel, the LoggingEnable event is fired. For example, this might // be called when a session begins listening, changes the level at // which it is listening, or stops listening. channel.LoggingEnabled += this.OnLoggingEnabled; // Log simple string events channel.LogMessage("Simple message"); // default level is Verbose channel.LogMessage("Simple error", LoggingLevel.Error); // Log simple string + integer events. channel.LogValuePair("Simple message", 123); // default level is Verbose channel.LogValuePair("Simple error", 456, LoggingLevel.Error); // The channel.Name property returns the name that was used when the // channel was constructed. When running in Windows 10 mode, the name // is already set as the provider name, so no LoggingChannelName is // automatically added to the event. channel.LogMessage(channel.Name); // The channel.Id property is new to Windows 10. channel.LogMessage(channel.Id.ToString()); // If you want to avoid the overhead of collecting data when nobody is // listening to your channel, check the Enabled property before logging. if (channel.Enabled) { channel.LogMessage(this.CollectExpensiveData()); } // The IsEnabled() method is exactly the same as the Enabled property, // except that it is a new Windows 10 API. if (channel.IsEnabled()) { channel.LogMessage(this.CollectExpensiveData()); } // If you want to only collect data if somebody is listening at a specific // level, you need to check both Enabled and Level. Note that the value of // the Level is unspecified when Enabled is false. if (channel.Enabled && channel.Level <= LoggingLevel.Warning) { channel.LogMessage(this.CollectExpensiveData(), LoggingLevel.Warning); } // The IsEnabled(LoggingLevel) method is a bit nicer than checking both // Enabled and Level, but it is only available on Windows 10 or later. if (channel.IsEnabled(LoggingLevel.Warning)) { channel.LogMessage(this.CollectExpensiveData(), LoggingLevel.Warning); } // You can also use IsEnabled to check for keywords. if (channel.IsEnabled(LoggingLevel.Information, 0x10)) { channel.LogMessage(this.CollectExpensiveData(), LoggingLevel.Information); } // Use LoggingFields with the LogEvent method to write complex events. var fields = new LoggingFields(); fields.AddDouble("pi", 3.14159); channel.LogEvent( "ComplexEvent", fields, LoggingLevel.Verbose, new LoggingOptions(0x10)); // Keywords = 0x10 // You can add any number of name-value pairs to a fields object, though // you may encounter ETW limitations if you add too many. For example, // ETW is limited to a maximum event size of 64KB, and the current // TraceLogging decoder can handle no more than 128 fields. // Performance optimization: You can reuse a LoggingFields object to // avoid unnecessary allocations. Don't forget to call Clear() // between uses, and don't try to share a LoggingFields object between // threads. fields.Clear(); fields.AddDateTime("Date", DateTimeOffset.Now); channel.LogEvent("Now", fields); fields.Clear(); // You can add a formatting hint to affect the way a value is decoded. // Not all combinations are useful, and the hint may be ignored. // For example, you can encode an MBCS string by writing a byte array // with a String hint. fields.AddUInt8Array( "AnsiString", new byte[] { 65, 66, 67, 49, 50, 51 }, // "ABC123" LoggingFieldFormat.String); // You can add "tag" bits to a field. These are user-defined bits that // can be used to communicate with an event processing tool. For example, // you might define a tag bit to indicate that a field contains private // data that should not be displayed on-screen. fields.AddString("Password", "12345", LoggingFieldFormat.Default, 0x10); // You can add a "structure" to an event. A structure is a name for a // group of fields. Structures can nest. Call BeginStruct to add a level // of nesting, and call EndStruct after the last field of the structure. fields.BeginStruct("Nested"); fields.AddInt16("Nested-1", 1); fields.AddInt16("Nested-2", 2); fields.BeginStruct("Nested-Nested"); fields.AddInt16("Nested-Nested-3", 3); fields.EndStruct(); fields.AddInt16("Nested-4", 4); fields.EndStruct(); // Advanced scenarios: you can use a LoggingOptions object to control // detailed event settings such as keywords, opcodes, and activity Ids. // These have their normal ETW semantics. You can also set event tags, // which are bit values that can be used to communicate with the event // processor. channel.LogEvent( "VeryComplexEvent", fields, LoggingLevel.Information, new LoggingOptions { Keywords = 0x123, Tags = 0x10 }); // Windows 10 introduces the ILoggingTarget interface. LoggingChannel // implements this interface. This interface allows components to accept // a logger as an parameter. this.DoSomething(channel); /* * If a LoggingActivity is created using a LoggingActivity constructor, * it will use Windows 8.1 semantics: * * - If an activity is destroyed (garbage-collected) without being closed * and the associated LoggingChannel is still open, the activity will * write a default Stop event. * - The default Stop event (written by the destructor or by the Close() * method) is encoded as a "simple" event. * * The 8.1 semantics are deprecated because the automatic generation of * a Stop event at garbage-collection can be misleading. The Stop event * is intended to mark the a precise point at which an activity is * completed, while the garbage-collection of an abandoned activity is * inherently imprecise and unpredictable. * * If a LoggingActivity is created using a StartActivity method, it will * use Windows 10 semantics: * * - If an activity is destroyed (garbage-collected) without being closed, * there will be no Stop event for the activity. * - The default Stop event (written by the Close() method) is encoded as * a TraceLogging event with name "ActivityClosed". */ // This activity is created with Windows 8.1 semantics. using (var a1 = new LoggingActivity("Activity1", channel)) { // The activity Start event is written by the LoggingActivity constructor. // You would do your activity's work here. // The activity Stop event is written when the activity is closed (disposed). // The Windows 10 LoggingActivity adds new methods for writing events // that are marked as associated with the activity. a1.LogEvent("Activity event"); // LoggingActivity also implements the ILoggingTarget interface, so you can // use either a channel or an activity as a logging target. this.DoSomething(a1); // The Windows 10 LoggingActivity adds new methods for creating nested activities. // Note that nested activities are always created with Windows 10 semantics, // even when nested under an activity that is using Windows 8.1 semantics. using (var a2 = a1.StartActivity("Activity2")) { // Nested task occurs here. // The Windows 10 LoggingActivity allows you to customize the Stop event. a2.StopActivity("Activity 2 stop"); } // Because a1 is using Windows 8.1 semantics, if we did not call Dispose(), // it would attempt to write a Stop event when it is garbage collected. // Writing Stop events during garbage collection is not useful, so be sure // to properly stop, close, or dispose activities. } // The Windows 10 StartActivity method creates a new activity, optionally with // specified fields and characteristics. // This activity is created with Windows 10 semantics. using (var a3 = channel.StartActivity("Activity3")) { // Because a3 is using Windows 10 semantics, if we did not call Dispose(), // there would be no Stop event (not even when the activity is garbage // collected). To get a Stop event, be sure to stop, close, or dispose the // activity. } }
/// <summary> /// This method demonstrates the LoggingChannel and LoggingActivity APIs. /// </summary> /// <param name="channel"> /// The channel to use for the demonstration. This channel may have been /// constructed using a Windows 8.1 constructor or a Windows 10 constructor. /// The same APIs are supported in both cases, but the ETL events will be /// formatted a bit differently depending on how the channel was constructed. /// </param> private void DemonstrateLogging(LoggingChannel channel) { // Whenever any ETW session changes the way it is listening to this // channel, the LoggingEnable event is fired. For example, this might // be called when a session begins listening, changes the level at // which it is listening, or stops listening. channel.LoggingEnabled += this.OnLoggingEnabled; // Log simple string events channel.LogMessage("Simple message"); // default level is Verbose channel.LogMessage("Simple error", LoggingLevel.Error); // Log simple string + integer events. channel.LogValuePair("Simple message", 123); // default level is Verbose channel.LogValuePair("Simple error", 456, LoggingLevel.Error); // The channel.Name property returns the name that was used when the // channel was constructed. When running in Windows 10 mode, the name // is already set as the provider name, so no LoggingChannelName is // automatically added to the event. channel.LogMessage(channel.Name); // The channel.Id property is new to Windows 10. channel.LogMessage(channel.Id.ToString()); // If you want to avoid the overhead of collecting data when nobody is // listening to your channel, check the Enabled property before logging. if (channel.Enabled) { channel.LogMessage(this.CollectExpensiveData()); } // The IsEnabled() method is exactly the same as the Enabled property, // except that it is a new Windows 10 API. if (channel.IsEnabled()) { channel.LogMessage(this.CollectExpensiveData()); } // If you want to only collect data if somebody is listening at a specific // level, you need to check both Enabled and Level. Note that the value of // the Level is unspecified when Enabled is false. if (channel.Enabled && channel.Level <= LoggingLevel.Warning) { channel.LogMessage(this.CollectExpensiveData(), LoggingLevel.Warning); } // The IsEnabled(LoggingLevel) method is a bit nicer than checking both // Enabled and Level, but it is only available on Windows 10 or later. if (channel.IsEnabled(LoggingLevel.Warning)) { channel.LogMessage(this.CollectExpensiveData(), LoggingLevel.Warning); } // You can also use IsEnabled to check for keywords. if (channel.IsEnabled(LoggingLevel.Information, 0x10)) { channel.LogMessage(this.CollectExpensiveData(), LoggingLevel.Information); } // Use LoggingFields with the LogEvent method to write complex events. var fields = new LoggingFields(); fields.AddDouble("pi", 3.14159); channel.LogEvent( "ComplexEvent", fields, LoggingLevel.Verbose, new LoggingOptions(0x10)); // Keywords = 0x10 // You can add any number of name-value pairs to a fields object, though // you may encounter ETW limitations if you add too many. For example, // ETW is limited to a maximum event size of 64KB, and the current // TraceLogging decoder can handle no more than 128 fields. // Performance optimization: You can reuse a LoggingFields object to // avoid unnecessary allocations. Don't forget to call Clear() // between uses, and don't try to share a LoggingFields object between // threads. fields.Clear(); fields.AddDateTime("Date", DateTimeOffset.Now); channel.LogEvent("Now", fields); fields.Clear(); // You can add a formatting hint to affect the way a value is decoded. // Not all combinations are useful, and the hint may be ignored. // For example, you can encode an MBCS string by writing a byte array // with a String hint. fields.AddUInt8Array( "AnsiString", new byte[] { 65, 66, 67, 49, 50, 51 }, // "ABC123" LoggingFieldFormat.String); // You can add "tag" bits to a field. These are user-defined bits that // can be used to communicate with an event processing tool. For example, // you might define a tag bit to indicate that a field contains private // data that should not be displayed on-screen. fields.AddString("Password", "12345", LoggingFieldFormat.Default, 0x10); // You can add a "structure" to an event. A structure is a name for a // group of fields. Structures can nest. Call BeginStruct to add a level // of nesting, and call EndStruct after the last field of the structure. fields.BeginStruct("Nested"); fields.AddInt16("Nested-1", 1); fields.AddInt16("Nested-2", 2); fields.BeginStruct("Nested-Nested"); fields.AddInt16("Nested-Nested-3", 3); fields.EndStruct(); fields.AddInt16("Nested-4", 4); fields.EndStruct(); // Advanced scenarios: you can use a LoggingOptions object to control // detailed event settings such as keywords, opcodes, and activity Ids. // These have their normal ETW semantics. You can also set event tags, // which are bit values that can be used to communicate with the event // processor. channel.LogEvent( "VeryComplexEvent", fields, LoggingLevel.Information, new LoggingOptions { Keywords = 0x123, Tags = 0x10 }); // Windows 10 introduces the ILoggingTarget interface. LoggingChannel // implements this interface. This interface allows components to accept // a logger as an parameter. this.DoSomething(channel); /* If a LoggingActivity is created using a LoggingActivity constructor, it will use Windows 8.1 semantics: - If an activity is destroyed (garbage-collected) without being closed and the associated LoggingChannel is still open, the activity will write a default Stop event. - The default Stop event (written by the destructor or by the Close() method) is encoded as a "simple" event. The 8.1 semantics are deprecated because the automatic generation of a Stop event at garbage-collection can be misleading. The Stop event is intended to mark the a precise point at which an activity is completed, while the garbage-collection of an abandoned activity is inherently imprecise and unpredictable. If a LoggingActivity is created using a StartActivity method, it will use Windows 10 semantics: - If an activity is destroyed (garbage-collected) without being closed, there will be no Stop event for the activity. - The default Stop event (written by the Close() method) is encoded as a TraceLogging event with name "ActivityClosed". */ // This activity is created with Windows 8.1 semantics. using (var a1 = new LoggingActivity("Activity1", channel)) { // The activity Start event is written by the LoggingActivity constructor. // You would do your activity's work here. // The activity Stop event is written when the activity is closed (disposed). // The Windows 10 LoggingActivity adds new methods for writing events // that are marked as associated with the activity. a1.LogEvent("Activity event"); // LoggingActivity also implements the ILoggingTarget interface, so you can // use either a channel or an activity as a logging target. this.DoSomething(a1); // The Windows 10 LoggingActivity adds new methods for creating nested activities. // Note that nested activities are always created with Windows 10 semantics, // even when nested under an activity that is using Windows 8.1 semantics. using (var a2 = a1.StartActivity("Activity2")) { // Nested task occurs here. // The Windows 10 LoggingActivity allows you to customize the Stop event. a2.StopActivity("Activity 2 stop"); } // Because a1 is using Windows 8.1 semantics, if we did not call Dispose(), // it would attempt to write a Stop event when it is garbage collected. // Writing Stop events during garbage collection is not useful, so be sure // to properly stop, close, or dispose activities. } // The Windows 10 StartActivity method creates a new activity, optionally with // specified fields and characteristics. // This activity is created with Windows 10 semantics. using (var a3 = channel.StartActivity("Activity3")) { // Because a3 is using Windows 10 semantics, if we did not call Dispose(), // there would be no Stop event (not even when the activity is garbage // collected). To get a Stop event, be sure to stop, close, or dispose the // activity. } }
private async void InterruptGpioPin_ValueChanged(GpioPin sender, GpioPinValueChangedEventArgs args) { DateTime currentTime = DateTime.UtcNow; Debug.WriteLine($"Digital Input Interrupt {sender.PinNumber} triggered {args.Edge}"); if (args.Edge != this.interruptTriggerOn) { return; } // Check that enough time has passed for picture to be taken if ((currentTime - this.imageLastCapturedAtUtc) < this.debounceTimeout) { this.displayGpioPin.Write(GpioPinValue.High); this.displayOffTimer.Change(this.timerPeriodDetectIlluminated, this.timerPeriodInfinite); return; } this.imageLastCapturedAtUtc = currentTime; // Just incase - stop code being called while photo already in progress if (this.cameraBusy) { this.displayGpioPin.Write(GpioPinValue.High); this.displayOffTimer.Change(this.timerPeriodDetectIlluminated, this.timerPeriodInfinite); return; } this.cameraBusy = true; try { using (Windows.Storage.Streams.InMemoryRandomAccessStream captureStream = new Windows.Storage.Streams.InMemoryRandomAccessStream()) { this.mediaCapture.CapturePhotoToStreamAsync(ImageEncodingProperties.CreateJpeg(), captureStream).AsTask().Wait(); captureStream.FlushAsync().AsTask().Wait(); captureStream.Seek(0); IStorageFile photoFile = await KnownFolders.PicturesLibrary.CreateFileAsync(ImageFilename, CreationCollisionOption.ReplaceExisting); ImageEncodingProperties imageProperties = ImageEncodingProperties.CreateJpeg(); await this.mediaCapture.CapturePhotoToStorageFileAsync(imageProperties, photoFile); IList <FaceAttributeType> returnfaceAttributes = new List <FaceAttributeType>(); returnfaceAttributes.Add(FaceAttributeType.Gender); returnfaceAttributes.Add(FaceAttributeType.Age); IList <DetectedFace> detectedFaces = await this.faceClient.Face.DetectWithStreamAsync(captureStream.AsStreamForRead(), returnFaceAttributes : returnfaceAttributes); Debug.WriteLine($"Count {detectedFaces.Count}"); if (detectedFaces.Count > 0) { this.displayGpioPin.Write(GpioPinValue.High); // Start the timer to turn the LED off this.displayOffTimer.Change(this.timerPeriodFaceIlluminated, this.timerPeriodInfinite); } LoggingFields imageInformation = new LoggingFields(); imageInformation.AddDateTime("TakenAtUTC", currentTime); imageInformation.AddInt32("Pin", sender.PinNumber); imageInformation.AddInt32("Faces", detectedFaces.Count); foreach (DetectedFace detectedFace in detectedFaces) { Debug.WriteLine("Face"); if (detectedFace.FaceId.HasValue) { imageInformation.AddGuid("FaceId", detectedFace.FaceId.Value); Debug.WriteLine($" Id:{detectedFace.FaceId.Value}"); } imageInformation.AddInt32("Left", detectedFace.FaceRectangle.Left); imageInformation.AddInt32("Width", detectedFace.FaceRectangle.Width); imageInformation.AddInt32("Top", detectedFace.FaceRectangle.Top); imageInformation.AddInt32("Height", detectedFace.FaceRectangle.Height); Debug.WriteLine($" L:{detectedFace.FaceRectangle.Left} W:{detectedFace.FaceRectangle.Width} T:{detectedFace.FaceRectangle.Top} H:{detectedFace.FaceRectangle.Height}"); if (detectedFace.FaceAttributes != null) { if (detectedFace.FaceAttributes.Gender.HasValue) { imageInformation.AddString("Gender", detectedFace.FaceAttributes.Gender.Value.ToString()); Debug.WriteLine($" Gender:{detectedFace.FaceAttributes.Gender.ToString()}"); } if (detectedFace.FaceAttributes.Age.HasValue) { imageInformation.AddDouble("Age", detectedFace.FaceAttributes.Age.Value); Debug.WriteLine($" Age:{detectedFace.FaceAttributes.Age.Value.ToString("F1")}"); } } } this.logging.LogEvent("Captured image processed by Cognitive Services", imageInformation); } } catch (Exception ex) { this.logging.LogMessage("Camera photo or save failed " + ex.Message, LoggingLevel.Error); } finally { this.cameraBusy = false; } }
private async void ImageUpdateTimerCallback(object state) { DateTime currentTime = DateTime.UtcNow; Debug.WriteLine($"{DateTime.UtcNow.ToLongTimeString()} Timer triggered"); // Just incase - stop code being called while photo already in progress if (this.cameraBusy) { return; } this.cameraBusy = true; try { StorageFile photoFile = await KnownFolders.PicturesLibrary.CreateFileAsync(ImageFilenameLocal, CreationCollisionOption.ReplaceExisting); ImageEncodingProperties imageProperties = ImageEncodingProperties.CreateJpeg(); await this.mediaCapture.CapturePhotoToStorageFileAsync(imageProperties, photoFile); string azureContainernameLatest = string.Format(this.azureStorageContainerNameLatestFormat, Environment.MachineName, this.deviceMacAddress, currentTime).ToLower(); string azureFilenameLatest = string.Format(this.azureStorageimageFilenameLatestFormat, Environment.MachineName, this.deviceMacAddress, currentTime); string azureContainerNameHistory = string.Format(this.azureStorageContainerNameHistoryFormat, Environment.MachineName, this.deviceMacAddress, currentTime).ToLower(); string azureFilenameHistory = string.Format(this.azureStorageImageFilenameHistoryFormat, Environment.MachineName.ToLower(), this.deviceMacAddress, currentTime); LoggingFields imageInformation = new LoggingFields(); imageInformation.AddDateTime("TakenAtUTC", currentTime); imageInformation.AddString("LocalFilename", photoFile.Path); imageInformation.AddString("AzureContainerNameLatest", azureContainernameLatest); imageInformation.AddString("AzureFilenameLatest", azureFilenameLatest); imageInformation.AddString("AzureContainerNameHistory", azureContainerNameHistory); imageInformation.AddString("AzureFilenameHistory", azureFilenameHistory); this.logging.LogEvent("Saving image(s) to Azure storage", imageInformation); CloudStorageAccount storageAccount = CloudStorageAccount.Parse(this.azureStorageConnectionString); CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient(); // Update the latest image in storage if (!string.IsNullOrWhiteSpace(azureContainernameLatest) && !string.IsNullOrWhiteSpace(azureFilenameLatest)) { CloudBlobContainer containerLatest = blobClient.GetContainerReference(azureContainernameLatest); await containerLatest.CreateIfNotExistsAsync(); CloudBlockBlob blockBlobLatest = containerLatest.GetBlockBlobReference(azureFilenameLatest); await blockBlobLatest.UploadFromFileAsync(photoFile); this.logging.LogEvent("Image latest saved to Azure storage"); } // Upload the historic image to storage if (!string.IsNullOrWhiteSpace(azureContainerNameHistory) && !string.IsNullOrWhiteSpace(azureFilenameHistory)) { CloudBlobContainer containerHistory = blobClient.GetContainerReference(azureContainerNameHistory); await containerHistory.CreateIfNotExistsAsync(); CloudBlockBlob blockBlob = containerHistory.GetBlockBlobReference(azureFilenameHistory); await blockBlob.UploadFromFileAsync(photoFile); this.logging.LogEvent("Image historic saved to Azure storage"); } } catch (Exception ex) { this.logging.LogMessage("Image capture or upload failed " + ex.Message, LoggingLevel.Error); } finally { this.cameraBusy = false; } }
private async void InterruptGpioPin_ValueChanged(GpioPin sender, GpioPinValueChangedEventArgs args) { DateTime currentTime = DateTime.UtcNow; Debug.WriteLine($"Digital Input Interrupt {sender.PinNumber} triggered {args.Edge}"); if (args.Edge != this.interruptTriggerOn) { return; } // Check that enough time has passed for picture to be taken if ((currentTime - this.imageLastCapturedAtUtc) < this.debounceTimeout) { this.displayGpioPin.Write(GpioPinValue.High); this.displayOffTimer.Change(this.timerPeriodDetectIlluminated, this.timerPeriodInfinite); return; } this.imageLastCapturedAtUtc = currentTime; // Just incase - stop code being called while photo already in progress if (this.cameraBusy) { this.displayGpioPin.Write(GpioPinValue.High); this.displayOffTimer.Change(this.timerPeriodDetectIlluminated, this.timerPeriodInfinite); return; } this.cameraBusy = true; try { using (Windows.Storage.Streams.InMemoryRandomAccessStream captureStream = new Windows.Storage.Streams.InMemoryRandomAccessStream()) { this.mediaCapture.CapturePhotoToStreamAsync(ImageEncodingProperties.CreateJpeg(), captureStream).AsTask().Wait(); captureStream.FlushAsync().AsTask().Wait(); captureStream.Seek(0); IStorageFile photoFile = await KnownFolders.PicturesLibrary.CreateFileAsync(ImageFilename, CreationCollisionOption.ReplaceExisting); ImageEncodingProperties imageProperties = ImageEncodingProperties.CreateJpeg(); await this.mediaCapture.CapturePhotoToStorageFileAsync(imageProperties, photoFile); ImagePrediction imagePrediction = await this.customVisionClient.ClassifyImageAsync(this.projectId, this.publishedName, captureStream.AsStreamForRead()); Debug.WriteLine($"Prediction count {imagePrediction.Predictions.Count}"); LoggingFields imageInformation = new LoggingFields(); imageInformation.AddDateTime("TakenAtUTC", currentTime); imageInformation.AddInt32("Pin", sender.PinNumber); imageInformation.AddInt32("Predictions", imagePrediction.Predictions.Count); foreach (var prediction in imagePrediction.Predictions) { Debug.WriteLine($" Tag:{prediction.TagName} {prediction.Probability}"); imageInformation.AddDouble($"Tag:{prediction.TagName}", prediction.Probability); } this.logging.LogEvent("Captured image processed by Cognitive Services", imageInformation); } } catch (Exception ex) { this.logging.LogMessage("Camera photo or save failed " + ex.Message, LoggingLevel.Error); } finally { this.cameraBusy = false; } }
private async Task ImageUpdate(bool isCommand) { DateTime currentTime = DateTime.UtcNow; // Just incase - stop code being called while photo already in progress if (this.cameraBusy) { return; } this.cameraBusy = true; this.displayGpioPin.Write(GpioPinValue.High); // Check that enough time has passed for picture to be taken if ((currentTime - this.imageLastCapturedAtUtc) < this.debounceTimeout) { this.displayOffTimer.Change(this.timerPeriodDetectIlluminated, this.timerPeriodInfinite); return; } this.imageLastCapturedAtUtc = currentTime; try { ImagePrediction imagePrediction; using (Windows.Storage.Streams.InMemoryRandomAccessStream captureStream = new Windows.Storage.Streams.InMemoryRandomAccessStream()) { this.mediaCapture.CapturePhotoToStreamAsync(ImageEncodingProperties.CreateJpeg(), captureStream).AsTask().Wait(); captureStream.FlushAsync().AsTask().Wait(); captureStream.Seek(0); IStorageFile photoFile = await KnownFolders.PicturesLibrary.CreateFileAsync(ImageFilename, CreationCollisionOption.ReplaceExisting); ImageEncodingProperties imageProperties = ImageEncodingProperties.CreateJpeg(); await this.mediaCapture.CapturePhotoToStorageFileAsync(imageProperties, photoFile); switch (modelType) { case ModelType.Classification: imagePrediction = await this.customVisionClient.ClassifyImageAsync(this.projectId, this.modelPublishedName, captureStream.AsStreamForRead()); break; case ModelType.Detection: imagePrediction = await this.customVisionClient.DetectImageAsync(this.projectId, this.modelPublishedName, captureStream.AsStreamForRead()); break; default: throw new ArgumentException("ModelType Invalid"); } Debug.WriteLine($"Prediction count {imagePrediction.Predictions.Count}"); } JObject telemetryDataPoint = new JObject(); LoggingFields imageInformation = new LoggingFields(); imageInformation.AddDateTime("TakenAtUTC", currentTime); imageInformation.AddBoolean("IsCommand", isCommand); imageInformation.AddDouble("Probability threshold", probabilityThreshold); imageInformation.AddInt32("Predictions", imagePrediction.Predictions.Count); // Display and log the results of the prediction foreach (var prediction in imagePrediction.Predictions) { Debug.WriteLine($" Tag:{prediction.TagName} {prediction.Probability}"); imageInformation.AddDouble($"Tag:{prediction.TagName}", prediction.Probability); } // Post process the predictions based on the type of model switch (modelType) { case ModelType.Classification: // Use only the tags above the specified minimum probability foreach (var prediction in imagePrediction.Predictions) { if (prediction.Probability >= probabilityThreshold) { // Display and log the individual tag probabilities Debug.WriteLine($" Tag valid:{prediction.TagName} {prediction.Probability:0.00}"); imageInformation.AddDouble($"Tag valid:{prediction.TagName}", prediction.Probability); telemetryDataPoint.Add(prediction.TagName, prediction.Probability); } } break; case ModelType.Detection: // Group the tags to get the count, include only the predictions above the specified minimum probability var groupedPredictions = from prediction in imagePrediction.Predictions where prediction.Probability >= probabilityThreshold group prediction by new { prediction.TagName } into newGroup select new { TagName = newGroup.Key.TagName, Count = newGroup.Count(), }; // Display and log the agregated predictions foreach (var prediction in groupedPredictions) { Debug.WriteLine($" Tag valid:{prediction.TagName} {prediction.Count}"); imageInformation.AddInt32($"Tag valid:{prediction.TagName}", prediction.Count); telemetryDataPoint.Add(prediction.TagName, prediction.Count); } break; default: throw new ArgumentException("ModelType Invalid"); } this.logging.LogEvent("Captured image processed by Cognitive Services", imageInformation); try { using (Message message = new Message(Encoding.ASCII.GetBytes(JsonConvert.SerializeObject(telemetryDataPoint)))) { Debug.WriteLine(" {0:HH:mm:ss} AzureIoTHubClient SendEventAsync start", DateTime.UtcNow); await this.azureIoTHubClient.SendEventAsync(message); Debug.WriteLine(" {0:HH:mm:ss} AzureIoTHubClient SendEventAsync finish", DateTime.UtcNow); } this.logging.LogEvent("SendEventAsync payload", imageInformation, LoggingLevel.Information); } catch (Exception ex) { imageInformation.AddString("Exception", ex.ToString()); this.logging.LogEvent("SendEventAsync payload", imageInformation, LoggingLevel.Error); } } catch (Exception ex) { this.logging.LogMessage("Camera photo or save failed " + ex.Message, LoggingLevel.Error); } finally { this.displayGpioPin.Write(GpioPinValue.Low); this.cameraBusy = false; } }
private async Task ImageUpdate(bool isCommand) { DateTime currentTime = DateTime.UtcNow; // Just incase - stop code being called while photo already in progress if (this.cameraBusy) { return; } this.cameraBusy = true; try { using (Windows.Storage.Streams.InMemoryRandomAccessStream captureStream = new Windows.Storage.Streams.InMemoryRandomAccessStream()) { await this.mediaCapture.CapturePhotoToStreamAsync(ImageEncodingProperties.CreateJpeg(), captureStream); await captureStream.FlushAsync(); #if DEBUG IStorageFile photoFile = await KnownFolders.PicturesLibrary.CreateFileAsync(ImageFilenameLocal, CreationCollisionOption.ReplaceExisting); ImageEncodingProperties imageProperties = ImageEncodingProperties.CreateJpeg(); await this.mediaCapture.CapturePhotoToStorageFileAsync(imageProperties, photoFile); #endif string azureFilenameLatest = string.Format(this.azureStorageimageFilenameLatestFormat, currentTime); string azureFilenameHistory = string.Format(this.azureStorageImageFilenameHistoryFormat, currentTime); LoggingFields imageInformation = new LoggingFields(); imageInformation.AddDateTime("TakenAtUTC", currentTime); imageInformation.AddBoolean("IsCommand", isCommand); #if DEBUG imageInformation.AddString("LocalFilename", photoFile.Path); #endif imageInformation.AddString("AzureFilenameLatest", azureFilenameLatest); imageInformation.AddString("AzureFilenameHistory", azureFilenameHistory); this.logging.LogEvent("Saving image(s) to Azure storage", imageInformation); // Update the latest image in storage if (!string.IsNullOrWhiteSpace(azureFilenameLatest)) { captureStream.Seek(0); Debug.WriteLine("AzureIoT Hub latest image upload start"); await this.azureIoTHubClient.UploadToBlobAsync(azureFilenameLatest, captureStream.AsStreamForRead()); Debug.WriteLine("AzureIoT Hub latest image upload done"); } // Upload the historic image to storage if (!string.IsNullOrWhiteSpace(azureFilenameHistory)) { captureStream.Seek(0); Debug.WriteLine("AzureIoT Hub historic image upload start"); await this.azureIoTHubClient.UploadToBlobAsync(azureFilenameHistory, captureStream.AsStreamForRead()); Debug.WriteLine("AzureIoT Hub historic image upload done"); } } } catch (Exception ex) { this.logging.LogMessage("Image capture or AzureIoTHub storage upload failed " + ex.Message, LoggingLevel.Error); } finally { this.cameraBusy = false; } }