public void LoadRules() { var criteria = new ServerRuleSelectCriteria(); // only query for rule in this partition criteria.ServerPartitionKey.EqualTo(ServerPartition.GetKey()); criteria.ServerRuleTypeEnum.EqualTo(ServerRuleTypeEnum.DataAccess); if (StatusFilter.SelectedIndex != 0) { if (StatusFilter.SelectedIndex == 1) { criteria.Enabled.EqualTo(true); } else { criteria.Enabled.EqualTo(false); } } if (DefaultFilter.SelectedIndex != 0) { if (DefaultFilter.SelectedIndex == 1) { criteria.DefaultRule.EqualTo(true); } else { criteria.DefaultRule.EqualTo(false); } } DataRuleGridViewControl.DataRules = _controller.GetServerRules(criteria); DataRuleGridViewControl.DataBind(); }
/// <summary> /// Checks for a storage location for the study in the database, and creates a new location /// in the database if it doesn't exist. /// </summary> /// <param name="partition">The partition where the study is being sent to</param> /// <param name="created"></param> /// <param name="studyDate"></param> /// <param name="studyInstanceUid"></param> /// <param name="syntax"></param> /// <param name="updateContext">The update context to create the study on</param> /// <returns>A <see cref="StudyStorageLocation"/> instance.</returns> public StudyStorageLocation GetOrCreateWritableStudyStorageLocation(string studyInstanceUid, string studyDate, TransferSyntax syntax, IUpdateContext updateContext, ServerPartition partition, out bool created) { created = false; StudyStorageLocation location; try { GetWritableStudyStorageLocation(partition.Key, studyInstanceUid, StudyRestore.True, StudyCache.True, out location); return(location); } catch (StudyNotFoundException) { } FilesystemSelector selector = new FilesystemSelector(Instance); ServerFilesystemInfo filesystem = selector.SelectFilesystem(); if (filesystem == null) { throw new NoWritableFilesystemException(); } IInsertStudyStorage locInsert = updateContext.GetBroker <IInsertStudyStorage>(); InsertStudyStorageParameters insertParms = new InsertStudyStorageParameters { ServerPartitionKey = partition.GetKey(), StudyInstanceUid = studyInstanceUid, Folder = ResolveStorageFolder(partition.Key, studyInstanceUid, studyDate, updateContext, false /* set to false for optimization because we are sure it's not in the system */), FilesystemKey = filesystem.Filesystem.GetKey(), QueueStudyStateEnum = QueueStudyStateEnum.Idle }; if (syntax.LosslessCompressed) { insertParms.TransferSyntaxUid = syntax.UidString; insertParms.StudyStatusEnum = StudyStatusEnum.OnlineLossless; } else if (syntax.LossyCompressed) { insertParms.TransferSyntaxUid = syntax.UidString; insertParms.StudyStatusEnum = StudyStatusEnum.OnlineLossy; } else { insertParms.TransferSyntaxUid = syntax.UidString; insertParms.StudyStatusEnum = StudyStatusEnum.Online; } location = locInsert.FindOne(insertParms); created = true; return(location); }
/// <summary> /// Load the devices for the partition based on the filters specified in the filter panel. /// </summary> /// <remarks> /// This method only reloads and binds the list bind to the internal grid. <seealso cref="Refresh"/> should be called /// to explicit update the list in the grid. /// <para> /// This is intentionally so that the list can be reloaded so that it is available to other controls during postback. In /// some cases we may not want to refresh the list if there's no change. Calling <seealso cref="Refresh"/> will /// give performance hit as the data will be transfered back to the browser. /// /// </para> /// </remarks> public void LoadDevices() { if (ServerPartition == null) { return; } var criteria = new DeviceSelectCriteria(); // only query for device in this partition criteria.ServerPartitionKey.EqualTo(ServerPartition.GetKey()); if (!String.IsNullOrEmpty(AETitleFilter.TrimText)) { QueryHelper.SetGuiStringCondition(criteria.AeTitle, SearchHelper.LeadingAndTrailingWildCard(AETitleFilter.TrimText)); } if (!String.IsNullOrEmpty(DescriptionFilter.TrimText)) { QueryHelper.SetGuiStringCondition(criteria.Description, SearchHelper.LeadingAndTrailingWildCard(DescriptionFilter.TrimText)); } if (!String.IsNullOrEmpty(IPAddressFilter.TrimText)) { QueryHelper.SetGuiStringCondition(criteria.IpAddress, SearchHelper.TrailingWildCard(IPAddressFilter.TrimText)); } if (StatusFilter.SelectedIndex != 0) { criteria.Enabled.EqualTo(StatusFilter.SelectedIndex == 1); } if (DHCPFilter.SelectedIndex != 0) { criteria.Dhcp.EqualTo(DHCPFilter.SelectedIndex == 1); } if (DeviceTypeFilter.SelectedIndex > -1) { var types = new List <DeviceTypeEnum>(); foreach (ListItem item in DeviceTypeFilter.Items) { if (item.Selected) { types.Add(DeviceTypeEnum.GetEnum(item.Value)); } } criteria.DeviceTypeEnum.In(types); } DeviceGridViewControl1.Devices = _theController.GetDevices(criteria); DeviceGridViewControl1.RefreshCurrentPage(); }
/// <summary> /// Update the specified partition tab /// </summary> /// <param name="tabIndex">The server partition tab index</param> /// <remarks> /// /// </remarks> public void Update(int tabIndex) { if (_partitionList == null || _partitionList.Count == 0) { return; } ServerPartition partition = _partitionList[tabIndex]; Update(partition.GetKey()); }
private static StudyStorageLocation FindStudyStorageLocation(IUpdateContext context, ServerPartition partition, string studyInstanceUid) { var procedure = context.GetBroker <IQueryStudyStorageLocation>(); var parms = new StudyStorageLocationQueryParameters { ServerPartitionKey = partition.GetKey(), StudyInstanceUid = studyInstanceUid }; return(procedure.FindOne(parms)); }
private IList <PartitionSopClass> LoadPartitionSopClassesFromDatabase(ServerPartition partition) { using (IReadContext context = PersistentStoreRegistry.GetDefaultStore().OpenReadContext()) { // Set the input parameters for query var inputParms = new PartitionSopClassQueryParameters { ServerPartitionKey = partition.GetKey() }; var broker = context.GetBroker <IQueryServerPartitionSopClasses>(); return(broker.Find(inputParms)); } }
public void LoadRules() { var criteria = new ServerRuleSelectCriteria(); // only query for device in this partition criteria.ServerPartitionKey.EqualTo(ServerPartition.GetKey()); if (!String.IsNullOrEmpty(RuleApplyTimeDropDownList.Text)) { if (!RuleApplyTimeDropDownList.SelectedValue.Equals("ALL")) { ServerRuleApplyTimeEnum en = ServerRuleApplyTimeEnum.GetEnum(RuleApplyTimeDropDownList.SelectedItem.Value); criteria.ServerRuleApplyTimeEnum.EqualTo(en); } } if (!String.IsNullOrEmpty(RuleTypeDropDownList.Text)) { if (!RuleTypeDropDownList.SelectedValue.Equals("ALL")) { ServerRuleTypeEnum en = ServerRuleTypeEnum.GetEnum(RuleTypeDropDownList.SelectedItem.Value); criteria.ServerRuleTypeEnum.EqualTo(en); } else { criteria.ServerRuleTypeEnum.NotEqualTo(ServerRuleTypeEnum.DataAccess); } } else { criteria.ServerRuleTypeEnum.NotEqualTo(ServerRuleTypeEnum.DataAccess); } if (StatusFilter.SelectedIndex != 0) { criteria.Enabled.EqualTo(StatusFilter.SelectedIndex == 1); } if (DefaultFilter.SelectedIndex != 0) { criteria.DefaultRule.EqualTo(DefaultFilter.SelectedIndex == 1); } ServerRuleGridViewControl.ServerRules = _controller.GetServerRules(criteria); ServerRuleGridViewControl.DataBind(); }
/// <summary> /// Apply the Rules engine. /// </summary> /// <remarks> /// <para> /// This method applies the rules engine to the first image in each series within a study. /// The assumption is that the actions generated by the engine can handle being applied more /// than once for the same study. This is also done to handle the case of multi-modality /// studies where you may want the rules to be run against each series, because they may /// apply differently. /// </para> /// <para> /// Note that we are still applying series level moves, although there currently are not /// any series level rules. We've somewhat turned the study level rules into series /// level rules. /// </para> /// </remarks> public void Apply(ServerRuleApplyTimeEnum applyTime) { using (var theProcessor = new ServerCommandProcessor("Study Rule Processor") { PrimaryServerPartitionKey = _partition.GetKey(), PrimaryStudyKey = _location.Study.GetKey() }) { Apply(applyTime, theProcessor); if (false == theProcessor.Execute()) { Platform.Log(LogLevel.Error, "Unexpected failure processing Study level rules for study {0} on partition {1} for {2} apply time", _location.StudyInstanceUid, _partition.Description, applyTime.Description); } } }
/// <summary> /// Load <see cref="Device"/> information for a Move destination. /// </summary> /// <param name="read"></param> /// <param name="partition"></param> /// <param name="remoteAe"></param> /// <returns></returns> private static Device LoadRemoteHost(IPersistenceContext read, ServerPartition partition, string remoteAe) { var select = read.GetBroker <IDeviceEntityBroker>(); // Setup the select parameters. var selectParms = new DeviceSelectCriteria(); selectParms.AeTitle.EqualTo(remoteAe); selectParms.ServerPartitionKey.EqualTo(partition.GetKey()); var devices = select.Find(selectParms); foreach (var d in devices) { if (string.Compare(d.AeTitle, remoteAe, false, CultureInfo.InvariantCulture) == 0) { return(d); } } return(null); }
/// <summary> /// Reprocess a specific study. /// </summary> /// <param name="partition">The ServerPartition the study is on.</param> /// <param name="location">The storage location of the study to process.</param> /// <param name="engine">The rules engine to use when processing the study.</param> /// <param name="postArchivalEngine">The rules engine used for studies that have been archived.</param> /// <param name="dataAccessEngine">The rules engine strictly used for setting data acess.</param> protected static void ProcessStudy(ServerPartition partition, StudyStorageLocation location, ServerRulesEngine engine, ServerRulesEngine postArchivalEngine, ServerRulesEngine dataAccessEngine) { if (!location.QueueStudyStateEnum.Equals(QueueStudyStateEnum.Idle) || !location.AcquireWriteLock()) { Platform.Log(LogLevel.Error, "Unable to lock study {0}. The study is being processed. (Queue State: {1})", location.StudyInstanceUid, location.QueueStudyStateEnum.Description); } else { try { DicomFile msg = LoadInstance(location); if (msg == null) { Platform.Log(LogLevel.Error, "Unable to load file for study {0}", location.StudyInstanceUid); return; } bool archiveQueueExists; bool archiveStudyStorageExists; bool filesystemDeleteExists; using (IReadContext read = PersistentStoreRegistry.GetDefaultStore().OpenReadContext()) { // Check for existing archive queue entries var archiveQueueBroker = read.GetBroker <IArchiveQueueEntityBroker>(); var archiveQueueCriteria = new ArchiveQueueSelectCriteria(); archiveQueueCriteria.StudyStorageKey.EqualTo(location.Key); archiveQueueExists = archiveQueueBroker.Count(archiveQueueCriteria) > 0; var archiveStorageBroker = read.GetBroker <IArchiveStudyStorageEntityBroker>(); var archiveStudyStorageCriteria = new ArchiveStudyStorageSelectCriteria(); archiveStudyStorageCriteria.StudyStorageKey.EqualTo(location.Key); archiveStudyStorageExists = archiveStorageBroker.Count(archiveStudyStorageCriteria) > 0; var filesystemQueueBroker = read.GetBroker <IFilesystemQueueEntityBroker>(); var filesystemQueueCriteria = new FilesystemQueueSelectCriteria(); filesystemQueueCriteria.StudyStorageKey.EqualTo(location.Key); filesystemQueueCriteria.FilesystemQueueTypeEnum.EqualTo(FilesystemQueueTypeEnum.DeleteStudy); filesystemDeleteExists = filesystemQueueBroker.Count(filesystemQueueCriteria) > 0; } using (var commandProcessor = new ServerCommandProcessor("Study Rule Processor") { PrimaryServerPartitionKey = partition.GetKey(), PrimaryStudyKey = location.Study.GetKey() }) { var context = new ServerActionContext(msg, location.FilesystemKey, partition, location.Key, commandProcessor); // Check if the Study has been archived if (archiveStudyStorageExists && !archiveQueueExists && !filesystemDeleteExists) { // Add a command to delete the current filesystemQueue entries, so that they can // be reinserted by the rules engine. context.CommandProcessor.AddCommand(new DeleteFilesystemQueueCommand(location.Key, ServerRuleApplyTimeEnum.StudyArchived)); // How to deal with exiting FilesystemQueue entries is problematic here. If the study // has been migrated off tier 1, we probably don't want to modify the tier migration // entries. Compression entries may have been entered when the Study was initially // processed, we don't want to delete them, because they might still be valid. // We just re-run the rules engine at this point, and delete only the StudyPurge entries, // since those we know at least would only be applied for archived studies. var studyRulesEngine = new StudyRulesEngine(postArchivalEngine, location, location.ServerPartition, location.LoadStudyXml()); studyRulesEngine.Apply(ServerRuleApplyTimeEnum.StudyArchived, commandProcessor); // Post Archive doesn't allow data access rules. Force Data Access rules to be reapplied // to these studies also. dataAccessEngine.Execute(context); } else { // Add a command to delete the current filesystemQueue entries, so that they can // be reinserted by the rules engine. context.CommandProcessor.AddCommand(new DeleteFilesystemQueueCommand(location.Key, ServerRuleApplyTimeEnum.StudyProcessed)); // Execute the rules engine, insert commands to update the database into the command processor. // Due to ticket #11673, we create a new rules engine instance for each study, since the Study QC rules // don't work right now with a single rules engine. //TODO CR (Jan 2014) - Check if we can go back to caching the rules engine to reduce database hits on the rules var studyRulesEngine = new StudyRulesEngine(location, location.ServerPartition, location.LoadStudyXml()); studyRulesEngine.Apply(ServerRuleApplyTimeEnum.StudyProcessed, commandProcessor); } // Do the actual database updates. if (false == context.CommandProcessor.Execute()) { Platform.Log(LogLevel.Error, "Unexpected failure processing Study level rules for study {0}", location.StudyInstanceUid); } // Log the FilesystemQueue related entries location.LogFilesystemQueue(); } } finally { location.ReleaseWriteLock(); } } }
/// <summary> /// Lookup the device entity in the database corresponding to the remote AE of the association. /// </summary> /// <param name="partition">The partition to look up the devices</param> /// <param name="association">The association</param> /// <param name="isNew">Indicates whether the device returned is created by the call.</param> /// <returns>The device record corresponding to the called AE of the association</returns> public static Device LookupDevice(ServerPartition partition, AssociationParameters association, out bool isNew) { isNew = false; Device device; if (DeviceCache.TryGetValue(association.CallingAE + partition.Key, out device)) { return(device); } using ( IUpdateContext updateContext = PersistentStoreRegistry.GetDefaultStore().OpenUpdateContext(UpdateContextSyncMode.Flush)) { var deviceEntityBroker = updateContext.GetBroker <IDeviceEntityBroker>(); // Setup the select parameters. var queryParameters = new DeviceSelectCriteria(); queryParameters.AeTitle.EqualTo(association.CallingAE); queryParameters.ServerPartitionKey.EqualTo(partition.GetKey()); var devices = deviceEntityBroker.Find(queryParameters); foreach (var d in devices) { if (string.Compare(d.AeTitle, association.CallingAE, false, CultureInfo.InvariantCulture) == 0) { device = d; break; } } if (device == null) { if (!partition.AcceptAnyDevice) { return(null); } if (partition.AutoInsertDevice) { // Auto-insert a new entry in the table. var updateColumns = new DeviceUpdateColumns { AeTitle = association.CallingAE, Enabled = true, Description = String.Format("AE: {0}", association.CallingAE), Dhcp = false, IpAddress = association.RemoteEndPoint.Address.ToString(), ServerPartitionKey = partition.GetKey(), Port = partition.DefaultRemotePort, AllowQuery = true, AllowRetrieve = true, AllowStorage = true, ThrottleMaxConnections = ImageServerCommonConfiguration.Device.MaxConnections, DeviceTypeEnum = DeviceTypeEnum.Workstation }; var insert = updateContext.GetBroker <IDeviceEntityBroker>(); device = insert.Insert(updateColumns); updateContext.Commit(); isNew = true; } } if (device != null) { // For DHCP devices, we always update the remote ip address, if its changed from what is in the DB. if (device.Dhcp && !association.RemoteEndPoint.Address.ToString().Equals(device.IpAddress)) { var updateColumns = new DeviceUpdateColumns { IpAddress = association.RemoteEndPoint.Address.ToString(), LastAccessedTime = Platform.Time }; if (!deviceEntityBroker.Update(device.GetKey(), updateColumns)) { Platform.Log(LogLevel.Error, "Unable to update IP Address for DHCP device {0} on partition '{1}'", device.AeTitle, partition.Description); } else { updateContext.Commit(); } } else if (!isNew) { var updateColumns = new DeviceUpdateColumns { LastAccessedTime = Platform.Time }; if (!deviceEntityBroker.Update(device.GetKey(), updateColumns)) { Platform.Log(LogLevel.Error, "Unable to update LastAccessedTime device {0} on partition '{1}'", device.AeTitle, partition.Description); } else { updateContext.Commit(); } } DeviceCache.Add(device.AeTitle + partition.Key, device); } } return(device); }
/// <summary> /// Reprocess a file systems /// </summary> /// <param name="partition"></param> private void ReprocessPartition(ServerPartition partition) { var engine = new ServerRulesEngine(ServerRuleApplyTimeEnum.StudyProcessed, partition.Key); engine.Load(); var postArchivalEngine = new ServerRulesEngine(ServerRuleApplyTimeEnum.StudyArchived, partition.Key); postArchivalEngine.Load(); var dataAccessEngine = new ServerRulesEngine(ServerRuleApplyTimeEnum.StudyProcessed, partition.Key); dataAccessEngine.AddIncludeType(ServerRuleTypeEnum.DataAccess); dataAccessEngine.Load(); var filesystems = FilesystemMonitor.Instance.GetFilesystems(); foreach (var f in filesystems) { var partitionDir = Path.Combine(f.Filesystem.FilesystemPath, partition.PartitionFolder); var filesystemDir = new DirectoryInfo(partitionDir); foreach (DirectoryInfo dateDir in filesystemDir.GetDirectories()) { if (dateDir.FullName.EndsWith("Deleted") || dateDir.FullName.EndsWith(ServerPlatform.ReconcileStorageFolder)) { continue; } foreach (DirectoryInfo studyDir in dateDir.GetDirectories()) { String studyInstanceUid = studyDir.Name; try { StudyStorageLocation location = LoadReadableStorageLocation(partition.GetKey(), studyInstanceUid); if (location == null) { foreach (DirectoryInfo seriesDir in studyDir.GetDirectories()) { FileInfo[] sopInstanceFiles = seriesDir.GetFiles("*.dcm"); DicomFile file = null; foreach (FileInfo sopFile in sopInstanceFiles) { if (!sopFile.FullName.EndsWith(ServerPlatform.DicomFileExtension)) { continue; } try { file = new DicomFile(sopFile.FullName); file.Load(DicomTags.StudyId, DicomReadOptions.DoNotStorePixelDataInDataSet | DicomReadOptions.Default); break; } catch (Exception e) { Platform.Log(LogLevel.Warn, e, "Unexpected failure loading file: {0}. Continuing to next file.", sopFile.FullName); file = null; } } if (file != null) { studyInstanceUid = file.DataSet[DicomTags.StudyInstanceUid].ToString(); break; } } location = LoadReadableStorageLocation(partition.GetKey(), studyInstanceUid); if (location == null) { continue; } } ProcessStudy(partition, location, engine, postArchivalEngine, dataAccessEngine); //_stats.NumStudies++; if (CancelPending) { return; } } catch (Exception e) { Platform.Log(LogLevel.Error, e, "Unexpected error while processing study: {0} on partition {1}.", studyInstanceUid, partition.Description); } } // Cleanup the directory, if its empty. DirectoryUtility.DeleteIfEmpty(dateDir.FullName); } } }