diff --git a/src/CamBooth/.idea/.idea.CamBooth/.idea/vcs.xml b/src/CamBooth/.idea/.idea.CamBooth/.idea/vcs.xml index fdf1fc8..b2bdec2 100644 --- a/src/CamBooth/.idea/.idea.CamBooth/.idea/vcs.xml +++ b/src/CamBooth/.idea/.idea.CamBooth/.idea/vcs.xml @@ -2,6 +2,5 @@ - \ No newline at end of file diff --git a/src/CamBooth/CamBooth.App/CamBooth.App.csproj b/src/CamBooth/CamBooth.App/CamBooth.App.csproj index 320efae..9033bb4 100644 --- a/src/CamBooth/CamBooth.App/CamBooth.App.csproj +++ b/src/CamBooth/CamBooth.App/CamBooth.App.csproj @@ -29,7 +29,6 @@ - diff --git a/src/CamBooth/CamBooth.App/Core/AppSettings/AppSettingsService.cs b/src/CamBooth/CamBooth.App/Core/AppSettings/AppSettingsService.cs index 613db2b..0d4c6c8 100644 --- a/src/CamBooth/CamBooth.App/Core/AppSettings/AppSettingsService.cs +++ b/src/CamBooth/CamBooth.App/Core/AppSettings/AppSettingsService.cs @@ -32,7 +32,11 @@ public class AppSettingsService } public string? AppName => configuration["AppSettings:AppName"]; + + public bool IsDebugConsoleVisible => bool.Parse(configuration["AppSettings:DebugConsoleVisible"] ?? string.Empty); + public string? PictureLocation => configuration["AppSettings:PictureLocation"]; + public string? ConnectionString => configuration.GetConnectionString("DefaultConnection"); public string ConfigFileName => loadedConfigFile; diff --git a/src/CamBooth/CamBooth.App/Core/AppSettings/app.settings.dev.json b/src/CamBooth/CamBooth.App/Core/AppSettings/app.settings.dev.json index 96c84d2..99f6f84 100644 --- a/src/CamBooth/CamBooth.App/Core/AppSettings/app.settings.dev.json +++ b/src/CamBooth/CamBooth.App/Core/AppSettings/app.settings.dev.json @@ -2,7 +2,8 @@ "AppSettings": { "AppName": "Meine Anwendung", "Version": "1.0.0", - "PictureLocation": "C:\\tmp\\cambooth" + "PictureLocation": "C:\\tmp\\cambooth", + "DebugConsoleVisible": "true" }, "ConnectionStrings": { "DefaultConnection": "Server=myServer;Database=myDB;User Id=myUser;Password=myPassword;" diff --git a/src/CamBooth/CamBooth.App/Core/AppSettings/app.settings.json b/src/CamBooth/CamBooth.App/Core/AppSettings/app.settings.json index 8657b50..bc47f37 100644 --- a/src/CamBooth/CamBooth.App/Core/AppSettings/app.settings.json +++ b/src/CamBooth/CamBooth.App/Core/AppSettings/app.settings.json @@ -2,7 +2,8 @@ "AppSettings": { "AppName": "Meine Anwendung", "Version": "1.0.0", - "PictureLocation": "C:\\cambooth\\pictures" + "PictureLocation": "C:\\cambooth\\pictures", + "DebugConsoleVisible": "true" }, "ConnectionStrings": { "DefaultConnection": "Server=myServer;Database=myDB;User Id=myUser;Password=myPassword;" diff --git a/src/CamBooth/CamBooth.App/Features/Camera/CameraService.cs b/src/CamBooth/CamBooth.App/Features/Camera/CameraService.cs index 8f09395..6ef452a 100644 --- a/src/CamBooth/CamBooth.App/Features/Camera/CameraService.cs +++ b/src/CamBooth/CamBooth.App/Features/Camera/CameraService.cs @@ -1,4 +1,5 @@ -using System.IO; +using System.IO; +using System.Threading.Tasks; using System.Windows; using CamBooth.App.Core.AppSettings; @@ -192,6 +193,53 @@ public class CameraService : IDisposable throw; } } + public async Task PrepareFocusAsync(int focusTimeoutMs = 1500) + { + if (this._mainCamera is not EOSDigital.API.Camera sdkCamera) + { + await Task.Delay(200); + return; + } + + var focusCompleted = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + void FocusStateChanged(EOSDigital.API.Camera sender, StateEventID eventId, int parameter) + { + if (eventId == StateEventID.AfResult) + { + focusCompleted.TrySetResult(true); + } + } + + sdkCamera.StateChanged += FocusStateChanged; + + try + { + await Task.Run(() => sdkCamera.SendCommand(CameraCommand.PressShutterButton, (int)ShutterButton.Halfway)); + var completedTask = await Task.WhenAny(focusCompleted.Task, Task.Delay(focusTimeoutMs)); + if (completedTask != focusCompleted.Task) + { + this._logger.Info("Autofocus timeout reached, continuing with countdown."); + } + } + catch (Exception ex) + { + this.ReportError(ex.Message); + } + finally + { + try + { + await Task.Run(() => sdkCamera.SendCommand(CameraCommand.PressShutterButton, (int)ShutterButton.OFF)); + } + catch (Exception ex) + { + this.ReportError(ex.Message); + } + + sdkCamera.StateChanged -= FocusStateChanged; + } + } #region API Events @@ -290,4 +338,5 @@ public class CameraService : IDisposable } #endregion -} \ No newline at end of file +} + diff --git a/src/CamBooth/CamBooth.App/MainWindow.xaml b/src/CamBooth/CamBooth.App/MainWindow.xaml index f12942c..544cc64 100644 --- a/src/CamBooth/CamBooth.App/MainWindow.xaml +++ b/src/CamBooth/CamBooth.App/MainWindow.xaml @@ -41,8 +41,16 @@ Panel.ZIndex="1" /> - + - \ No newline at end of file + diff --git a/src/CamBooth/CamBooth.App/MainWindow.xaml.cs b/src/CamBooth/CamBooth.App/MainWindow.xaml.cs index d1634bc..db0636d 100644 --- a/src/CamBooth/CamBooth.App/MainWindow.xaml.cs +++ b/src/CamBooth/CamBooth.App/MainWindow.xaml.cs @@ -1,5 +1,7 @@ -using System.ComponentModel; +using System.ComponentModel; +using System.Threading.Tasks; using System.Windows; +using System.Windows.Threading; using CamBooth.App.Core.AppSettings; using CamBooth.App.Core.Logging; @@ -28,6 +30,12 @@ public partial class MainWindow : Window private bool _isPicturePanelVisible = false; private LiveViewPage? _liveViewPage; + + private bool _isPhotoProcessRunning; + + private readonly DispatcherTimer _focusStatusAnimationTimer = new() { Interval = TimeSpan.FromMilliseconds(250) }; + + private int _focusStatusDots; public MainWindow( @@ -41,11 +49,16 @@ public partial class MainWindow : Window this._pictureGalleryService = pictureGalleryService; this._cameraService = cameraService; InitializeComponent(); - this.SetVisibilityDebugConsole(this._isDebugConsoleVisible); + this.SetVisibilityDebugConsole(_appSettings.IsDebugConsoleVisible); this.SetVisibilityPicturePanel(this._isPicturePanelVisible); _ = this._pictureGalleryService.LoadThumbnailsToCache(); this.Closing += OnClosing; TimerControlRectangleAnimation.OnTimerEllapsed += TimerControlRectangleAnimation_OnTimerEllapsed; + this._focusStatusAnimationTimer.Tick += (_, _) => + { + this._focusStatusDots = (this._focusStatusDots + 1) % 4; + this.CaptureStatusText.Text = $"Scharfstellen{new string('.', this._focusStatusDots)}"; + }; logger.Info($"config file loaded: '{appSettings.ConfigFileName}'"); logger.Info("MainWindow initialized"); } @@ -65,7 +78,10 @@ public partial class MainWindow : Window } finally { + this.StopFocusStatusAnimation(); + this.CaptureStatusText.Visibility = Visibility.Collapsed; SwitchButtonAndTimerPanel(); + this._isPhotoProcessRunning = false; } } @@ -106,6 +122,11 @@ public partial class MainWindow : Window private void SetVisibilityDebugConsole(bool visibility) { + if (!_appSettings.IsDebugConsoleVisible) + { + return; + } + if (visibility) { this.DebugFrame.Navigate(new DebugConsolePage(this._logger)); @@ -119,19 +140,56 @@ public partial class MainWindow : Window } - private void StartTakePhotoProcess(object sender, RoutedEventArgs e) + private async void StartTakePhotoProcess(object sender, RoutedEventArgs e) { + if (this._isPhotoProcessRunning) + { + return; + } + + this._isPhotoProcessRunning = true; + try { -#if DEBUG - TimerControlRectangleAnimation.StartTimer(1); -#else - TimerControlRectangleAnimation.StartTimer(5); -#endif SwitchButtonAndTimerPanel(); + +#if DEBUG + TimerControlRectangleAnimation.StartTimer(2); + this.StartFocusStatusAnimation(); + this.CaptureStatusText.Visibility = Visibility.Visible; + await Task.Delay(TimeSpan.FromSeconds(1)); + if (!this._isPhotoProcessRunning) + { + return; + } + + await this._cameraService.PrepareFocusAsync(focusTimeoutMs: 1000); + this.StopFocusStatusAnimation(); + this.CaptureStatusText.Visibility = Visibility.Collapsed; +#else + TimerControlRectangleAnimation.StartTimer(5); + this.StartFocusStatusAnimation(); + this.CaptureStatusText.Visibility = Visibility.Visible; + await Task.Delay(TimeSpan.FromSeconds(2)); + if (!this._isPhotoProcessRunning) + { + return; + } + + await this._cameraService.PrepareFocusAsync(focusTimeoutMs: 3000); + this.StopFocusStatusAnimation(); + this.CaptureStatusText.Visibility = Visibility.Collapsed; +#endif } catch (Exception exception) { + this._isPhotoProcessRunning = false; + this.StopFocusStatusAnimation(); + this.CaptureStatusText.Visibility = Visibility.Collapsed; + if (this.TimerPanel.Visibility == Visibility.Visible) + { + SwitchButtonAndTimerPanel(); + } this._logger.Error(exception.Message); } } @@ -152,4 +210,17 @@ public partial class MainWindow : Window { this.Close(); } -} \ No newline at end of file + + private void StartFocusStatusAnimation() + { + this._focusStatusDots = 0; + this.CaptureStatusText.Text = "Scharfstellen"; + this._focusStatusAnimationTimer.Start(); + } + + private void StopFocusStatusAnimation() + { + this._focusStatusAnimationTimer.Stop(); + this._focusStatusDots = 0; + } +} diff --git a/src/CamBooth/CamBooth.App/ToDos.txt b/src/CamBooth/CamBooth.App/ToDos.txt new file mode 100644 index 0000000..10fe566 --- /dev/null +++ b/src/CamBooth/CamBooth.App/ToDos.txt @@ -0,0 +1,8 @@ +- Rotate Flick Picture 180° +- Printer anschließen +- Galerie schließen +- Debug Window schließen +- Kiosk Modus einrichten +- Energiesparmodus abschalten +- Starbildschirm mit freundlicher Begrüßung, kurzer Erklärung, und viel Spaß wünschen mit der FotoCam +- Verschiedene Hinweise anzeigen beim Fotofrafieren (lächeln, Hasensohren, Zunge raus, Grimasse, usw.)