Implemented Queues and folder monitors.

This commit is contained in:
Patrick Fic
2020-01-17 10:01:06 -08:00
parent 2b3d9ac76b
commit 572f409176
13 changed files with 388 additions and 41 deletions

View File

@@ -248,6 +248,8 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Compile Include="Models\DTO_Base.cs" />
<Compile Include="Models\DTO_QueueItem.cs" />
<Compile Include="Properties\Resources.es-MX.Designer.cs">
<DependentUpon>Resources.es-MX.resx</DependentUpon>
<AutoGen>True</AutoGen>
@@ -262,7 +264,11 @@
<Compile Include="Utils\Auth.cs" />
<Compile Include="Utils\BaseModel.cs" />
<Compile Include="Utils\BaseViewModel.cs" />
<Compile Include="Utils\CIECAMonitor.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Utils\GraphQL.cs" />
<Compile Include="Utils\JobProcessingQueue.cs" />
<Compile Include="Utils\LoginHelpers.cs" />
<Compile Include="Utils\RelayCommand.cs" />
<Compile Include="Utils\TrayIcon.cs" />
@@ -334,9 +340,7 @@
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<Folder Include="Models\" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.7.2">
<Visible>False</Visible>

View File

@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace BodyshopUploader.Models
{
public abstract class DTO_Base : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
{
if (object.Equals(storage, value)) return false;
storage = value;
this.OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var eventHandler = this.PropertyChanged;
if (eventHandler != null)
{
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}

View File

@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BodyshopUploader.Models
{
public class DTO_QueueItem : DTO_Base
{
public DTO_QueueItem()
{
}
private string _filePath;
public string FilePath
{
get { return _filePath; }
set { SetProperty(ref _filePath, value); }
}
}
}

View File

@@ -114,6 +114,24 @@ namespace BodyshopUploader.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Start All Monitors.
/// </summary>
public static string Label_StartAllMonitors {
get {
return ResourceManager.GetString("Label_StartAllMonitors", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Stop All Monitors.
/// </summary>
public static string Label_StopAllMonitors {
get {
return ResourceManager.GetString("Label_StopAllMonitors", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Login.
/// </summary>

View File

@@ -135,6 +135,12 @@
<data name="Label_AddMonitoringPath" xml:space="preserve">
<value>Add Path</value>
</data>
<data name="Label_StartAllMonitors" xml:space="preserve">
<value>Start All Monitors</value>
</data>
<data name="Label_StopAllMonitors" xml:space="preserve">
<value>Stop All Monitors</value>
</data>
<data name="Login" xml:space="preserve">
<value>Login</value>
</data>

View File

@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BodyshopUploader.Models;
namespace BodyshopUploader.Utils
{
public class CIECAMonitor : FileSystemWatcher
{
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
public CIECAMonitor()
{
Init();
}
public CIECAMonitor(String inDirectoryPath)
: base(inDirectoryPath)
{
Init();
}
public CIECAMonitor(String inDirectoryPath, string inFilter)
: base(inDirectoryPath, inFilter)
{
Init();
}
private void Init()
{
IncludeSubdirectories = false;
// Eliminate duplicates when timestamp doesn't change
Filter = "*.env";
NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite;
//NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size; // The default also has NotifyFilters.LastWrite
EnableRaisingEvents = true;
Created += Watcher_Created;
Changed += Watcher_Changed;
//Deleted += Watcher_Deleted;
//Renamed += Watcher_Renamed;
Error += Wathcer_Error;
logger.Debug("CIECA Folder watcher started for path: {0}", Path);
}
private void Wathcer_Error(object sender, ErrorEventArgs e)
{
logger.Error("CIECA monitor encountered an error. Trying to restart. {0}", e.ToString());
((FileSystemWatcher)sender).EnableRaisingEvents = true;
}
public void Watcher_Created(object source, FileSystemEventArgs inArgs)
{
logger.Trace("New CIECA fileset detected | {0}", inArgs.FullPath, inArgs.ChangeType);
JobProcessingQueue.Enqueue(new DTO_QueueItem() { FilePath = inArgs.FullPath });
}
public void Watcher_Changed(object sender, FileSystemEventArgs inArgs)
{
logger.Trace("Updated CIECA fileset detected | {0}", inArgs.FullPath);
JobProcessingQueue.Enqueue(new DTO_QueueItem() { FilePath = inArgs.FullPath });
}
public void Watcher_Deleted(object sender, FileSystemEventArgs inArgs)
{
}
public void Watcher_Renamed(object sender, RenamedEventArgs inArgs)
{
}
private void InitializeComponent()
{
((System.ComponentModel.ISupportInitialize)(this)).BeginInit();
//
// FolderMonitor
//
this.IncludeSubdirectories = false;
((System.ComponentModel.ISupportInitialize)(this)).EndInit();
}
}
}

View File

@@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using BodyshopUploader.Utils;
using BodyshopUploader.Models;
namespace BodyshopUploader.Utils
{
public static class JobProcessingQueue
{
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
private static Queue<DTO_QueueItem> _jobs = new Queue<DTO_QueueItem>();
private static bool _delegateQueuedOrRunning = false;
public static void Enqueue(DTO_QueueItem item)
{
lock (_jobs)
{
//See if the job is already in the queue based on the file name. If it is, ignore it.
if (!(_jobs.Any(_ => _.FilePath == item.FilePath)))
{
logger.Debug("Adding job to the queue. | {0}", item.FilePath);
_jobs.Enqueue(item);
if (!_delegateQueuedOrRunning)
{
_delegateQueuedOrRunning = true;
ThreadPool.UnsafeQueueUserWorkItem(ProcessQueuedItems, null);
}
}
else
{
logger.Debug("Ignoring job {0}, it's already in the queue.", item.FilePath);
}
}
}
private static void ProcessQueuedItems(object ignored)
{
while (true)
{
DTO_QueueItem item;
lock (_jobs)
{
if (_jobs.Count == 0)
{
_delegateQueuedOrRunning = false;
break;
}
//Only peek at the first item in the queue so that it does not get added again while it is still getting processed.
item = _jobs.Peek();
}
try
{
Thread.Sleep(1000);//Allow a small amount of time to pass before processing the queue item so that any writes can finish.
DecodeQueueItemJob(item);
UpsertQueueItem(item);
}
catch
{
ThreadPool.UnsafeQueueUserWorkItem(ProcessQueuedItems, null);
throw;
}
}
}
private static void DecodeQueueItemJob(DTO_QueueItem item)
{
//Process the job.
logger.Info("Should process the job here. {0}", item.FilePath);
}
private static void UpsertQueueItem(DTO_QueueItem item)
{
//Save the job to the DB.
logger.Info("Should upsert the job graphqlly here. {0}", item.FilePath);
_jobs.Dequeue();
}
private static void MoveFile(string FullPath)
{
try
{
System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(FullPath) + @"\Processed\");
File.Move(FullPath, System.IO.Path.GetDirectoryName(FullPath) + @"\Processed\" + System.IO.Path.GetFileNameWithoutExtension(FullPath) + DateTime.Now.Ticks.ToString() + ".bak");
}
catch (Exception Ex)
{
logger.Error(Ex, "Can't move file {0} - it's gone!", FullPath);
}
}
}
}

View File

@@ -11,8 +11,10 @@ namespace BodyshopUploader.Utils
{
public void Execute(object parameter)
{
new Views.Main().Show();
}
var m = App.Current.Windows.OfType<Views.Main>().FirstOrDefault();
m?.Show();
m?.Focus();
}
public bool CanExecute(object parameter)
{

View File

@@ -70,5 +70,37 @@ namespace BodyshopUploader.ViewModels
return _removeMonitoringPathCommand;
}
}
private ICommand _startFolderMonitorsCommand;
public ICommand StartFolderMonitorsCommand
{
get
{
if (_startFolderMonitorsCommand == null)
{
_startFolderMonitorsCommand = new RelayCommand(
p => MonitoringPaths.Count > 0,
p => StartAllFolderMonitors()
);
}
return _startFolderMonitorsCommand;
}
}
private ICommand _stopFolderMonitorsCommand;
public ICommand StopFolderMonitorsCommand
{
get
{
if (_stopFolderMonitorsCommand == null)
{
_stopFolderMonitorsCommand = new RelayCommand(
p => FolderMonitors.Count > 0,
p => StopAllFolderMonitors()
);
}
return _stopFolderMonitorsCommand;
}
}
}
}

View File

@@ -48,31 +48,51 @@ namespace BodyshopUploader.ViewModels
Properties.Settings.Default.Save();
}
public void StartAllFolderMonitors()
{
if (FolderMonitors.Count > 0)
foreach (var m in FolderMonitors)
{
m.Dispose();
}
foreach (var p in MonitoringPaths)
{
//Ensure the directory exists, then start monitoring for CIECA files.
System.IO.Directory.CreateDirectory(p);
FolderMonitors.Add(new Utils.CIECAMonitor(p));
}
}
public void StopAllFolderMonitors()
{
if (FolderMonitors.Count > 0)
foreach (var m in FolderMonitors)
{
m.Dispose();
}
}
public async Task TestGql()
{
if (MonitoringPaths == null) MonitoringPaths = new ObservableCollection<string>();
MonitoringPaths.Add(@"C:\IMEX");
Properties.Settings.Default.MonitoringPaths = MonitoringPaths;
Properties.Settings.Default.Save();
var r = new GraphQLRequest
{
Query = @"
query {
jobs {
id
est_number
ro_number
}
}
"
};
//var r = new GraphQLRequest
//{
// Query = @"
// query {
// jobs {
// id
// est_number
// ro_number
// }
// }
// "
//};
//using (var g = Utils.GraphQL.CreateGQLClient())
//{
// var graphQLResponse = await g.PostAsync(r);
// logger.Info(graphQLResponse.Data.jobs);
//}
using (var g = Utils.GraphQL.CreateGQLClient())
{
var graphQLResponse = await g.PostAsync(r);
logger.Info(graphQLResponse.Data.jobs);
}
}
}
}

View File

@@ -16,6 +16,13 @@ namespace BodyshopUploader.ViewModels
set { SetProperty(ref _progress, value); }
}
private ObservableCollection<Utils.CIECAMonitor> _folderMonitors = new ObservableCollection<Utils.CIECAMonitor>() ;
public ObservableCollection<Utils.CIECAMonitor> FolderMonitors
{
get { return _folderMonitors; }
set { SetProperty(ref _folderMonitors, value); }
}
private ObservableCollection<string> _monitoringPaths = Properties.Settings.Default.MonitoringPaths ;
public ObservableCollection<string> MonitoringPaths
{

View File

@@ -20,15 +20,17 @@
Background="{DynamicResource MaterialDesignPaper}"
FontFamily="{DynamicResource MaterialDesignFont}"
Loaded="Window_Loaded"
xmlns:util="clr-namespace:BodyshopUploader.Utils">
xmlns:util="clr-namespace:BodyshopUploader.Utils"
Closing="Window_Closing">
<Window.DataContext>
<vm:MainViewModel />
</Window.DataContext>
<Window.Resources>
<util:OpenMainWindowCommand x:Key="OpenMainWindowCommand"/>
<util:OpenMainWindowCommand x:Key="OpenMainWindowCommand" />
</Window.Resources>
<StackPanel>
<tb:TaskbarIcon IconSource="../favicon.ico"
<DockPanel>
<tb:TaskbarIcon DockPanel.Dock="Top"
IconSource="../favicon.ico"
DoubleClickCommand="{StaticResource OpenMainWindowCommand}"
PopupActivation="LeftClick"
MenuActivation="RightClick"
@@ -45,13 +47,15 @@
</Border>
</tb:TaskbarIcon.TrayPopup>
<tb:TaskbarIcon.ContextMenu>
<ContextMenu Background="LightCoral">
<ContextMenu>
<MenuItem Header="First Menu Item" />
<MenuItem Header="Second Menu Item" />
</ContextMenu>
</tb:TaskbarIcon.ContextMenu>
</tb:TaskbarIcon>
<Button Command="{Binding TestCommand}"
<Button DockPanel.Dock="Top"
Command="{Binding TestCommand}"
Content="Test Command" />
<Button Style="{StaticResource MaterialDesignFloatingActionMiniDarkButton}"
@@ -60,8 +64,15 @@
<materialDesign:PackIcon Kind="Add"
Height="24"
Width="24" />
</Button>
</Button>
<StackPanel DockPanel.Dock="Right">
<Button Command="{Binding StartFolderMonitorsCommand}"
Content="{x:Static p:Resources.Label_StartAllMonitors}"/>
<Button Command="{Binding StopFolderMonitorsCommand}"
Content="{x:Static p:Resources.Label_StopAllMonitors}" />
</StackPanel>
<ItemsControl ItemsSource="{Binding MonitoringPaths}">
<ItemsControl.ItemTemplate>
<DataTemplate>
@@ -72,12 +83,11 @@
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="{Binding .}" />
<materialDesign:PackIcon Kind="RemoveCircle"
Grid.Column="1"
Height="16"
Width="16" >
<materialDesign:PackIcon Kind="RemoveCircle"
Grid.Column="1"
Height="16"
Width="16">
<materialDesign:PackIcon.InputBindings>
<MouseBinding Gesture="LeftClick"
Command="{Binding DataContext.RemoveMonitoringPathCommand,
@@ -91,5 +101,5 @@
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DockPanel>
</Window>

View File

@@ -29,5 +29,11 @@ namespace BodyshopUploader.Views
{
Application.Current.MainWindow = this;
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
e.Cancel = true ;
this.Hide();
}
}
}