public bool TryCreateMonitor(AcquisitionMonitor monitor, out string message)
 {
     if (Directory.Exists(monitor.Location))
     {
         if (monitor.ExpectedChannelCount == 2)
         {
             if (_managerSource.AddMonitor(monitor))
             {
                 message = "";
                 return true;
             }
             else
             {
                 message = "A worker for that location is already active.";
                 return false;
             }
         }
         else
         {
             message = "Only 2-channel acquisitions are supported at this time.";
             return false;
         }
     }
     else
     {
         message = "The directory does not exist.";
         return false;
     }
 }
        public void PostMonitor_ShouldAddMonitor()
        {
            var context = new TestAcquisitionContext();
            var controller = new MonitorController(context);
            var mon = new AcquisitionMonitor() { Id = 1, Location = @"C:\Test\Location1" };

            controller.PostMonitor(mon);

            Assert.AreEqual(1, context.Monitors.Count);
        }
        public void TestDeltaZBasic()
        {
            AcquisitionMonitor monitor = new AcquisitionMonitor();

            bool dayWasCreated, groupWasCreated;

            TileDay day = monitor.CreateOrGetDay("2016-06-29", out dayWasCreated);

            TileGroup group = day.CreateOrGetTileGroup("2016-06-29" + Path.DirectorySeparatorChar  + "00", out groupWasCreated);

            Tile tile1 = CreateTile(group, "00001", 1, 1.5, false);

            // Create tile should be initialized to NaN
            Assert.IsTrue(double.IsNaN(tile1.Contents.DeltaZ));

            group.AddTile(tile1);

            // Tile without acquisition (no Z position) should have deltaZ NaN
            Assert.IsTrue(double.IsNaN(tile1.Contents.DeltaZ));

            Tile tile2 = CreateTile(group, "00002", 2, 3.3);

            group.AddTile(tile2);

            // First entry in zIndex map - deltaZ should be zero
            Assert.AreEqual<double>(0, tile2.Contents.DeltaZ);

            Tile tile3 = CreateTile(group, "00003", 3, 4.3);

            group.AddTile(tile3);

            // Standard behavior - next lattice position
            Assert.AreEqual<double>(4.3 - 3.3, tile3.Contents.DeltaZ);

            // Add a tile from a lower lattice position - update as appropriate

            tile1.HaveAcquisition = true;
            Assert.AreEqual<double>(0, tile1.Contents.DeltaZ);
            Assert.AreEqual<double>(3.3 - 1.5, tile2.Contents.DeltaZ);
            Assert.AreEqual<double>(4.3 - 3.3, tile3.Contents.DeltaZ);

            // A new tile comes in layer 2 with a lower Z - deltaZ in layer 3 should update.
            Tile tile4 = CreateTile(group, "00004", 2, 3.2);

            group.AddTile(tile4);
            Assert.AreEqual<double>(0, tile1.Contents.DeltaZ);
            Assert.AreEqual<double>(3.3 - 1.5, tile2.Contents.DeltaZ);
            Assert.AreEqual<double>(3.2 - 1.5, tile4.Contents.DeltaZ);
            Assert.AreEqual<double>(4.3 - 3.2, tile3.Contents.DeltaZ);
        }
        public void TestDuplicatesGeneral()
        {
            AcquisitionMonitor monitor = new AcquisitionMonitor() { Location = Path.Combine(TestFileData.LocalUnitTestDataLocation, "testsets", "duplicates"), ExpectedChannelCount = 2, MinTiffFileSize = 0.1 };

            AcquisitionWorker worker = new AcquisitionWorker(monitor);
            worker.MaxHistoricalDaysToMonitorEmerging = 1;

            bool res = worker.Start().Result;

            while (worker.ExistingQueueSize > 0)
                Thread.Sleep(250);

            Thread.Sleep(1000);

            worker.Stop();

            Assert.AreEqual(2, monitor.Days.Count, "Day count");
            Assert.AreEqual(10, monitor.Tiles.Count, "Tile count");
            Assert.AreEqual(1, monitor.IncompleteTiles.Count, "Incomplete count");
            Assert.AreEqual(4, monitor.Lattice.Duplicates.Count, "Duplicate count");

            Assert.AreEqual(100, monitor.Lattice.Duplicates[0].Position.X);
            Assert.AreEqual(50, monitor.Lattice.Duplicates[0].Position.Y);
            Assert.AreEqual(20, monitor.Lattice.Duplicates[0].Position.Z);
            Assert.AreEqual(3, monitor.Lattice.Duplicates[0].TileCount);

            Assert.AreEqual(100, monitor.Lattice.Duplicates[1].Position.X);
            Assert.AreEqual(55, monitor.Lattice.Duplicates[1].Position.Y);
            Assert.AreEqual(20, monitor.Lattice.Duplicates[1].Position.Z);
            Assert.AreEqual(2, monitor.Lattice.Duplicates[1].TileCount);

            Assert.AreEqual(20, monitor.Lattice.Duplicates[2].Position.X);
            Assert.AreEqual(60, monitor.Lattice.Duplicates[2].Position.Y);
            Assert.AreEqual(40, monitor.Lattice.Duplicates[2].Position.Z);
            Assert.AreEqual(2, monitor.Lattice.Duplicates[2].TileCount);

            Assert.AreEqual(70, monitor.Lattice.Duplicates[3].Position.X);
            Assert.AreEqual(80, monitor.Lattice.Duplicates[3].Position.Y);
            Assert.AreEqual(90, monitor.Lattice.Duplicates[3].Position.Z);
            Assert.AreEqual(2, monitor.Lattice.Duplicates[3].TileCount);
        }
        public void TestRestoreCacheNoValidate()
        {
            MetadataCache cache = new MetadataCache();

            AcquisitionMonitor monitor = new AcquisitionMonitor() { Location = Path.Combine(TestFileData.LocalUnitTestDataLocation, "metadata", "duplicates"), ExpectedChannelCount = 2, MinTiffFileSize = 0.1 };

            UserOptions.Default.VerifyDeserializationPaths = false;

            DeserializationContext context = cache.Restore(monitor);

            Assert.AreEqual(9, context.CompleteCount, "Context Complete count");
            Assert.AreEqual(1, context.Incomplete.Count, "Context Incomplete count");
            Assert.AreEqual(19, context.PendingImages.Count, "Context PendingImages count");
            Assert.IsTrue(context.ImplicitChannelImageFileSize > 0.0, "Context ImplicitChannelImageFileSize");
            Assert.AreEqual(2, context.ImplicitChannelImageFrameCount, "Context ImplicitChannelImageFrameCount");

            Assert.AreEqual(2, monitor.Days.Count, "Day count");
            Assert.AreEqual(10, monitor.Tiles.Count, "Tile count");
            Assert.AreEqual(1, monitor.IncompleteTiles.Count, "Incomplete count");
            Assert.AreEqual(4, monitor.Lattice.Duplicates.Count, "Duplicate count");

            Assert.AreEqual(100, monitor.Lattice.Duplicates[0].Position.X);
            Assert.AreEqual(50, monitor.Lattice.Duplicates[0].Position.Y);
            Assert.AreEqual(20, monitor.Lattice.Duplicates[0].Position.Z);
            Assert.AreEqual(3, monitor.Lattice.Duplicates[0].TileCount);

            Assert.AreEqual(100, monitor.Lattice.Duplicates[1].Position.X);
            Assert.AreEqual(55, monitor.Lattice.Duplicates[1].Position.Y);
            Assert.AreEqual(20, monitor.Lattice.Duplicates[1].Position.Z);
            Assert.AreEqual(2, monitor.Lattice.Duplicates[1].TileCount);

            Assert.AreEqual(20, monitor.Lattice.Duplicates[2].Position.X);
            Assert.AreEqual(60, monitor.Lattice.Duplicates[2].Position.Y);
            Assert.AreEqual(40, monitor.Lattice.Duplicates[2].Position.Z);
            Assert.AreEqual(2, monitor.Lattice.Duplicates[2].TileCount);

            Assert.AreEqual(70, monitor.Lattice.Duplicates[3].Position.X);
            Assert.AreEqual(80, monitor.Lattice.Duplicates[3].Position.Y);
            Assert.AreEqual(90, monitor.Lattice.Duplicates[3].Position.Z);
            Assert.AreEqual(2, monitor.Lattice.Duplicates[3].TileCount);
        }
        public void TestRestoreCacheValidate()
        {
            MetadataCache cache = new MetadataCache();

            AcquisitionMonitor monitor = new AcquisitionMonitor() { Location = Path.Combine(TestFileData.LocalUnitTestDataLocation, "metadata", "duplicates"), ExpectedChannelCount = 2, MinTiffFileSize = 0.1 };

            UserOptions.Default.VerifyDeserializationPaths = true;

            DeserializationContext context = cache.Restore(monitor);

            Assert.AreEqual(0, context.CompleteCount, "Context Complete count");
            Assert.AreEqual(0, context.Incomplete.Count, "Context Incomplete count");
            Assert.AreEqual(0, context.PendingImages.Count, "Context PendingImages count");
            Assert.AreEqual(0.0, context.ImplicitChannelImageFileSize, "Context ImplicitChannelImageFileSize");
            Assert.AreEqual(Int32.MinValue, context.ImplicitChannelImageFrameCount, "Context ImplicitChannelImageFrameCount");

            Assert.AreEqual(0, monitor.Days.Count, "Day count");
            Assert.AreEqual(0, monitor.Tiles.Count, "Tile count");
            Assert.AreEqual(0, monitor.IncompleteTiles.Count, "Incomplete count");
            Assert.AreEqual(0, monitor.Lattice.Duplicates.Count, "Duplicate count");
        }
        public void Backup(AcquisitionMonitor monitor, bool isSessionBackup = false)
        {
            Contract.Requires(monitor != null);

            var metaDataFileName = Path.Combine(monitor.Location, FileName + ".json");

            if (!File.Exists(metaDataFileName))
            {
                return;
            }

            MonitorStatus currentStatus = monitor.Status;

            try
            {
                monitor.Status = MonitorStatus.BackupCache;

                if (!Directory.Exists(monitor.Location))
                {
                    throw new Exception(String.Format("{0} is no longer reachable.", monitor.Location));
                }

                var metaDataBackupFileName = metaDataFileName + (isSessionBackup ? LastSessionExtension : BackupExtension);

                AcquireFilePermissions(new string[] { metaDataFileName, metaDataBackupFileName });

                File.Copy(metaDataFileName, metaDataBackupFileName, true);
                Trace.TraceInformation("Existing metadata backup to to {0}", metaDataBackupFileName);
            }
            catch (Exception e)
            {
                Trace.TraceError("An unexpected error during metadata backup: {0}", e.Message);
            }

            monitor.Status = currentStatus;
        }
        public void TestIncompleteGeneral()
        {
            AcquisitionMonitor monitor = new AcquisitionMonitor() { Location = Path.Combine(TestFileData.LocalUnitTestDataLocation, "testsets", "incomplete"), ExpectedChannelCount = 2, MinTiffFileSize = 0.1 };

            AcquisitionWorker worker = new AcquisitionWorker(monitor);
            worker.MaxHistoricalDaysToMonitorEmerging = 1;

            bool res = worker.Start().Result;

            while (worker.ExistingQueueSize > 0)
                Thread.Sleep(250);

            worker.Stop();

            Thread.Sleep(1000);

            // Assumes inclusion of an complete empty folder: Test.Unit\data\testsets\incomplete\2015-07-01\00\00011
            // that does not get checked into GitHub as an empty folder.  Create this folder in your local repository
            // to enable this test to pass.
            Assert.AreEqual(2, monitor.Days.Count);
            Assert.AreEqual(13, monitor.Tiles.Count);
            Assert.AreEqual(11, monitor.IncompleteTiles.Count);
            Assert.AreEqual(0, monitor.Lattice.Duplicates.Count);
        }
        public DeserializationContext Restore(AcquisitionMonitor monitor)
        {
            Contract.Requires(monitor != null);

            var metaDataFileName = Path.Combine(monitor.Location, FileName + ".json");

            if (!File.Exists(metaDataFileName))
            {
                return null;
            }

            DeserializationContext result = null;

            MonitorStatus currentStatus = monitor.Status;

            monitor.Status = MonitorStatus.LoadingCache;

            try
            {
                if (!Directory.Exists(monitor.Location))
                {
                    throw new Exception(String.Format("{0} is no longer reachable.", monitor.Location));
                }

                AcquireFilePermissions(new string[] { metaDataFileName }, FileIOPermissionAccess.Read);

                Trace.TraceInformation("Loading cached tile data from {0}.", metaDataFileName);

                SerializedMonitor data = JsonConvert.DeserializeObject<SerializedMonitor>(File.ReadAllText(metaDataFileName));

                Trace.TraceInformation("\tLoaded {0} tiles over {1} days.", data.NumberOfTiles, data.NumberOfDays);

                if (data.Version < SerializationVersion.V01)
                {
                    throw new Exception(String.Format("Ignoring metadata cache version {0}.  Minimum version is {1}.", data.Version, SerializationVersion.V01));
                }

                if (data.WhenCached == DateTime.MinValue)
                {
                    data.WhenCached = File.GetCreationTimeUtc(metaDataFileName);
                }

                monitor.Status = MonitorStatus.ParsingCache;

                Trace.TraceInformation("Restoring loaded tile data.");

                result = monitor.Deserialize(data);

                Trace.TraceInformation("\tData restored.");
            }
            catch (Exception e)
            {
                Trace.TraceError("Unexpected error reading metadata file. {0}", e.Message);
            }

            monitor.Status = currentStatus;

            return result;
        }
        public void Save(AcquisitionMonitor monitor)
        {
            Contract.Requires(monitor != null);

            MonitorStatus currentStatus = monitor.Status;

            monitor.Status = MonitorStatus.SaveCache;

            DateTime lastCached = monitor.WhenCached;

            try
            {
                if (!Directory.Exists(monitor.Location))
                {
                    throw new Exception(String.Format("{0} is no longer reachable.", monitor.Location));
                }

                Backup(monitor);

                var metaDataFileName = Path.Combine(monitor.Location, FileName + ".json");
                var metaDataFileNameTemp = metaDataFileName + ".temp";

                AcquireFilePermissions(new string[] { metaDataFileName, metaDataFileNameTemp });

                monitor.WhenCached = DateTime.Now;

                var txt = JsonConvert.SerializeObject(monitor.Serialize(), new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() });

                File.WriteAllText(metaDataFileNameTemp, txt);

                File.Delete(metaDataFileName);

                File.Move(metaDataFileNameTemp, metaDataFileName);
            }
            catch (Exception e)
            {
                Trace.TraceError("An unexpected error while saving the metadata file: {0}", e.Message);
                monitor.WhenCached = lastCached;
            }

            monitor.Status = currentStatus;
        }
        /// <summary>
        /// Encapsulates the task of observing a root directory for tile additions and changes.
        /// </summary>
        /// <param name="monitor"></param>
        public AcquisitionWorker(AcquisitionMonitor monitor)
        {
            Contract.Requires(monitor != null);

            _model = monitor;
            _hasStarted = false;

            _deserializeCompleteCount = 0;

            _cts = new CancellationTokenSource();

            // Timers (not started)
            _topLevelMonitoringTimer = new System.Timers.Timer(UserOptions.Default.TopLevelDirectoryPollingIntervalMinutes * 60 * 1000);
            _topLevelMonitoringTimer.Elapsed += TopLevelMonitoringElapsed;

            _incompleteMonitoringTimer = new System.Timers.Timer(UserOptions.Default.IncompleteTilePollingIntervalMinutes * 60 * 1000);
            _incompleteMonitoringTimer.Elapsed += EvaluateIncompleteList;

            // Queues
            _incompleteMonitoringQueue = new TileQueue();
            _existingTileQueue = new TileQueue();

            // Discovery workers (not started)
            _discoveryWorkers = new ConcurrentDictionary<TileGroup, TileGroupEmergingWorker>();
            _existingOnlyWorkers = new ConcurrentDictionary<TileGroup, TileGroupExistingWorker>();

            // Evaluate workers (not started)
            _emergingProcessingWorker = new TileEvaluateWorker(null, _incompleteMonitoringQueue, true);

            _existingProcessingWorkers = new List<TileEvaluateWorker>();
            for (int idx = 0; idx < _existingProcessorCount; idx++)
            {
                _existingProcessingWorkers.Add(new TileEvaluateWorker(_existingTileQueue, _incompleteMonitoringQueue, false));
            }

            _incompleteProcessingWorker = new TileEvaluateWorker(null, _incompleteMonitoringQueue, false);

            _duplicateTileWorker = new DuplicateTileWorker();

            // Metadata storage.
            if (UserOptions.Default.SerializeMetadata)
            {
                _metadataCache = new MetadataCache();
            }
            else
            {
                Trace.TraceInformation("Metadata cache has been disabled.  Changes will not be recorded.");
            }
        }
        private async void AddCmd_Execute(object target, ExecutedRoutedEventArgs evt)
        {
            Properties.Settings.Default.LastDirectory = watchFolderTextBox.Text;
            Properties.Settings.Default.Save();

            string location = watchFolderTextBox.Text;

            double size;

            if (!Double.TryParse(minSizeTextBox.Text, out size) || size < 0)
            {
                MessageBox.Show("The minimum TIFF file size must be a positive numeric value.", "Failed to Add Monitor", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            var monitor = new AcquisitionMonitor() { Location = location, MinTiffFileSize = size, TargetCutThickness = 0 };

            Properties.Settings.Default.LastMinTiffFileSize = size;
            Properties.Settings.Default.Save();

            using (var client = new HttpClient())
            {
                client.BaseAddress = new Uri(String.Format("http://localhost:{0}/", Properties.Settings.Default.Port.ToString()));
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                var response = await client.PostAsJsonAsync("api/monitors", monitor);

                if (!response.IsSuccessStatusCode)
                {
                    string error;
                    try
                    {
                        var r = await response.Content.ReadAsStringAsync();

                        var defintion = new { message = "" };

                        var msg = JsonConvert.DeserializeAnonymousType(r, defintion);

                        error = msg.message;
                    }
                    catch (Exception ex)
                    {
                        error = "The directory can not be monitored due to an unknown error.  " + ex.Message;
                    }

                    MessageBox.Show(error, "Failed to Add Monitor", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }
        public IHttpActionResult PostMonitor(AcquisitionMonitor watcher)
        {
            string errorMessage;

            if (_context.TryCreateMonitor(watcher, out errorMessage))
            {
                return Ok();
            }

            return BadRequest(errorMessage);
        }