feat(hp): enable OHIF to run with partial metadata for large studies at the cost of less effective hanging protocol (#3804)
Co-authored-by: rodrigobasilio2022 <rodrigo.basilio@radicalimaging.com> Co-authored-by: rodrigobasilio2022 <114958722+rodrigobasilio2022@users.noreply.github.com>
This commit is contained in:
parent
b5cdf88a57
commit
0049f4c030
@ -28,17 +28,35 @@ const EXPLICIT_VR_LITTLE_ENDIAN = '1.2.840.10008.1.2.1';
|
||||
const metadataProvider = classes.MetadataProvider;
|
||||
|
||||
/**
|
||||
* Creates a DICOM Web API based on the provided configuration.
|
||||
*
|
||||
* @param {string} name - Data source name
|
||||
* @param {string} wadoUriRoot - Legacy? (potentially unused/replaced)
|
||||
* @param {string} qidoRoot - Base URL to use for QIDO requests
|
||||
* @param {string} wadoRoot - Base URL to use for WADO requests
|
||||
* @param {boolean} qidoSupportsIncludeField - Whether QIDO supports the "Include" option to request additional fields in response
|
||||
* @param {string} imageRengering - wadors | ? (unsure of where/how this is used)
|
||||
* @param {string} thumbnailRendering - wadors | ? (unsure of where/how this is used)
|
||||
* @param {bool} supportsReject - Whether the server supports reject calls (i.e. DCM4CHEE)
|
||||
* @param {bool} lazyLoadStudy - "enableStudyLazyLoad"; Request series meta async instead of blocking
|
||||
* @param {string|bool} singlepart - indicates of the retrieves can fetch singlepart. Options are bulkdata, video, image or boolean true
|
||||
* @param {object} dicomWebConfig - Configuration for the DICOM Web API
|
||||
* @param {string} dicomWebConfig.name - Data source name
|
||||
* @param {string} dicomWebConfig.wadoUriRoot - Legacy? (potentially unused/replaced)
|
||||
* @param {string} dicomWebConfig.qidoRoot - Base URL to use for QIDO requests
|
||||
* @param {string} dicomWebConfig.wadoRoot - Base URL to use for WADO requests
|
||||
* @param {string} dicomWebConfig.wadoUri - Base URL to use for WADO URI requests
|
||||
* @param {boolean} dicomWebConfig.qidoSupportsIncludeField - Whether QIDO supports the "Include" option to request additional fields in response
|
||||
* @param {string} dicomWebConfig.imageRendering - wadors | ? (unsure of where/how this is used)
|
||||
* @param {string} dicomWebConfig.thumbnailRendering - wadors | ? (unsure of where/how this is used)
|
||||
* @param {boolean} dicomWebConfig.supportsReject - Whether the server supports reject calls (i.e. DCM4CHEE)
|
||||
* @param {boolean} dicomWebConfig.lazyLoadStudy - "enableStudyLazyLoad"; Request series meta async instead of blocking
|
||||
* @param {string|boolean} dicomWebConfig.singlepart - indicates if the retrieves can fetch singlepart. Options are bulkdata, video, image, or boolean true
|
||||
* @param {string} dicomWebConfig.requestTransferSyntaxUID - Transfer syntax to request from the server
|
||||
* @param {object} dicomWebConfig.acceptHeader - Accept header to use for requests
|
||||
* @param {boolean} dicomWebConfig.omitQuotationForMultipartRequest - Whether to omit quotation marks for multipart requests
|
||||
* @param {boolean} dicomWebConfig.supportsFuzzyMatching - Whether the server supports fuzzy matching
|
||||
* @param {boolean} dicomWebConfig.supportsWildcard - Whether the server supports wildcard matching
|
||||
* @param {boolean} dicomWebConfig.supportsNativeDICOMModel - Whether the server supports the native DICOM model
|
||||
* @param {boolean} dicomWebConfig.enableStudyLazyLoad - Whether to enable study lazy loading
|
||||
* @param {boolean} dicomWebConfig.enableRequestTag - Whether to enable request tag
|
||||
* @param {boolean} dicomWebConfig.enableStudyLazyLoad - Whether to enable study lazy loading
|
||||
* @param {boolean} dicomWebConfig.bulkDataURI - Whether to enable bulkDataURI
|
||||
* @param {function} dicomWebConfig.onConfiguration - Function that is called after the configuration is initialized
|
||||
* @param {boolean} dicomWebConfig.staticWado - Whether to use the static WADO client
|
||||
* @param {object} userAuthenticationService - User authentication service
|
||||
* @param {object} userAuthenticationService.getAuthorizationHeader - Function that returns the authorization header
|
||||
* @returns {object} - DICOM Web API object
|
||||
*/
|
||||
function createDicomWebApi(dicomWebConfig, servicesManager) {
|
||||
const { userAuthenticationService, customizationService } = servicesManager.services;
|
||||
@ -191,6 +209,7 @@ function createDicomWebApi(dicomWebConfig, servicesManager) {
|
||||
sortCriteria,
|
||||
sortFunction,
|
||||
madeInClient = false,
|
||||
returnPromises = false,
|
||||
} = {}) => {
|
||||
if (!StudyInstanceUID) {
|
||||
throw new Error('Unable to query for SeriesMetadata without StudyInstanceUID');
|
||||
@ -202,7 +221,8 @@ function createDicomWebApi(dicomWebConfig, servicesManager) {
|
||||
filters,
|
||||
sortCriteria,
|
||||
sortFunction,
|
||||
madeInClient
|
||||
madeInClient,
|
||||
returnPromises
|
||||
);
|
||||
}
|
||||
|
||||
@ -336,7 +356,8 @@ function createDicomWebApi(dicomWebConfig, servicesManager) {
|
||||
filters,
|
||||
sortCriteria,
|
||||
sortFunction,
|
||||
madeInClient = false
|
||||
madeInClient = false,
|
||||
returnPromises = false
|
||||
) => {
|
||||
const enableStudyLazyLoad = true;
|
||||
wadoDicomWebClient.headers = generateWadoHeader();
|
||||
@ -416,7 +437,7 @@ function createDicomWebApi(dicomWebConfig, servicesManager) {
|
||||
const naturalizedInstances = instances.map(addRetrieveBulkData);
|
||||
|
||||
// Adding instanceMetadata to OHIF MetadataProvider
|
||||
naturalizedInstances.forEach((instance, index) => {
|
||||
naturalizedInstances.forEach(instance => {
|
||||
instance.wadoRoot = dicomWebConfig.wadoRoot;
|
||||
instance.wadoUri = dicomWebConfig.wadoUri;
|
||||
|
||||
@ -443,7 +464,7 @@ function createDicomWebApi(dicomWebConfig, servicesManager) {
|
||||
}
|
||||
|
||||
function setSuccessFlag() {
|
||||
const study = DicomMetadataStore.getStudy(StudyInstanceUID, madeInClient);
|
||||
const study = DicomMetadataStore.getStudy(StudyInstanceUID);
|
||||
if (!study) {
|
||||
return;
|
||||
}
|
||||
@ -458,13 +479,22 @@ function createDicomWebApi(dicomWebConfig, servicesManager) {
|
||||
|
||||
DicomMetadataStore.addSeriesMetadata(seriesSummaryMetadata, madeInClient);
|
||||
|
||||
const seriesDeliveredPromises = seriesPromises.map(promise =>
|
||||
promise.then(instances => {
|
||||
const seriesDeliveredPromises = seriesPromises.map(promise => {
|
||||
if (!returnPromises) {
|
||||
promise?.start();
|
||||
}
|
||||
return promise.then(instances => {
|
||||
storeInstances(instances);
|
||||
})
|
||||
);
|
||||
await Promise.all(seriesDeliveredPromises);
|
||||
setSuccessFlag();
|
||||
});
|
||||
});
|
||||
|
||||
if (returnPromises) {
|
||||
Promise.all(seriesDeliveredPromises).then(() => setSuccessFlag());
|
||||
return seriesPromises;
|
||||
} else {
|
||||
await Promise.all(seriesDeliveredPromises);
|
||||
setSuccessFlag();
|
||||
}
|
||||
|
||||
return seriesSummaryMetadata;
|
||||
},
|
||||
@ -496,7 +526,7 @@ function createDicomWebApi(dicomWebConfig, servicesManager) {
|
||||
|
||||
return imageIds;
|
||||
},
|
||||
getImageIdsForInstance({ instance, frame }) {
|
||||
getImageIdsForInstance({ instance, frame = undefined }) {
|
||||
const imageIds = getImageId({
|
||||
instance,
|
||||
frame,
|
||||
|
||||
@ -7,6 +7,7 @@ import { api } from 'dicomweb-client';
|
||||
* performing searches doesn't work. This version fixes the query issue
|
||||
* by manually implementing a query option.
|
||||
*/
|
||||
|
||||
export default class StaticWadoClient extends api.DICOMwebClient {
|
||||
static studyFilterKeys = {
|
||||
studyinstanceuid: '0020000D',
|
||||
|
||||
@ -11,10 +11,17 @@ export default class RetrieveMetadataLoader {
|
||||
* @param {Object} client The dicomweb-client.
|
||||
* @param {Array} studyInstanceUID Study instance ui to be retrieved
|
||||
* @param {Object} [filters] - Object containing filters to be applied on retrieve metadata process
|
||||
* @param {string} [filter.seriesInstanceUID] - series instance uid to filter results against
|
||||
* @param {Function} [sortSeries] - Custom sort function for series
|
||||
* @param {string} [filters.seriesInstanceUID] - series instance uid to filter results against
|
||||
* @param {Object} [sortCriteria] - Custom sort criteria used for series
|
||||
* @param {Function} [sortFunction] - Custom sort function for series
|
||||
*/
|
||||
constructor(client, studyInstanceUID, filters = {}, sortCriteria, sortFunction) {
|
||||
constructor(
|
||||
client,
|
||||
studyInstanceUID,
|
||||
filters = {},
|
||||
sortCriteria = undefined,
|
||||
sortFunction = undefined
|
||||
) {
|
||||
this.client = client;
|
||||
this.studyInstanceUID = studyInstanceUID;
|
||||
this.filters = filters;
|
||||
@ -26,7 +33,6 @@ export default class RetrieveMetadataLoader {
|
||||
const preLoadData = await this.preLoad();
|
||||
const loadData = await this.load(preLoadData);
|
||||
const postLoadData = await this.posLoad(loadData);
|
||||
|
||||
return postLoadData;
|
||||
}
|
||||
|
||||
@ -37,13 +43,9 @@ export default class RetrieveMetadataLoader {
|
||||
async runLoaders(loaders) {
|
||||
let result;
|
||||
for (const loader of loaders) {
|
||||
try {
|
||||
result = await loader();
|
||||
if (result && result.length) {
|
||||
break; // closes iterator in case data is retrieved successfully
|
||||
}
|
||||
} catch (e) {
|
||||
throw e;
|
||||
result = await loader();
|
||||
if (result && result.length) {
|
||||
break; // closes iterator in case data is retrieved successfully
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,58 @@
|
||||
import dcmjs from 'dcmjs';
|
||||
import { sortStudySeries, sortingCriteria } from '@ohif/core/src/utils/sortStudy';
|
||||
import { sortStudySeries } from '@ohif/core/src/utils/sortStudy';
|
||||
import RetrieveMetadataLoader from './retrieveMetadataLoader';
|
||||
|
||||
// Series Date, Series Time, Series Description and Series Number to be included
|
||||
// in the series metadata query result
|
||||
const includeField = ['00080021', '00080031', '0008103E', '00200011'].join(',');
|
||||
|
||||
export class DeferredPromise {
|
||||
metadata = undefined;
|
||||
processFunction = undefined;
|
||||
internalPromise = undefined;
|
||||
thenFunction = undefined;
|
||||
rejectFunction = undefined;
|
||||
|
||||
setMetadata(metadata) {
|
||||
this.metadata = metadata;
|
||||
}
|
||||
setProcessFunction(func) {
|
||||
this.processFunction = func;
|
||||
}
|
||||
getPromise() {
|
||||
return this.start();
|
||||
}
|
||||
start() {
|
||||
if (this.internalPromise) {
|
||||
return this.internalPromise;
|
||||
}
|
||||
this.internalPromise = this.processFunction();
|
||||
// in case then and reject functions called before start
|
||||
if (this.thenFunction) {
|
||||
this.then(this.thenFunction);
|
||||
this.thenFunction = undefined;
|
||||
}
|
||||
if (this.rejectFunction) {
|
||||
this.reject(this.rejectFunction);
|
||||
this.rejectFunction = undefined;
|
||||
}
|
||||
return this.internalPromise;
|
||||
}
|
||||
then(func) {
|
||||
if (this.internalPromise) {
|
||||
return this.internalPromise.then(func);
|
||||
} else {
|
||||
this.thenFunction = func;
|
||||
}
|
||||
}
|
||||
reject(func) {
|
||||
if (this.internalPromise) {
|
||||
return this.internalPromise.reject(func);
|
||||
} else {
|
||||
this.rejectFunction = func;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Creates an immutable series loader object which loads each series sequentially using the iterator interface.
|
||||
*
|
||||
@ -16,12 +67,17 @@ function makeSeriesAsyncLoader(client, studyInstanceUID, seriesInstanceUIDList)
|
||||
hasNext() {
|
||||
return seriesInstanceUIDList.length > 0;
|
||||
},
|
||||
async next() {
|
||||
const seriesInstanceUID = seriesInstanceUIDList.shift();
|
||||
return client.retrieveSeriesMetadata({
|
||||
studyInstanceUID,
|
||||
seriesInstanceUID,
|
||||
next() {
|
||||
const { seriesInstanceUID, metadata } = seriesInstanceUIDList.shift();
|
||||
const promise = new DeferredPromise();
|
||||
promise.setMetadata(metadata);
|
||||
promise.setProcessFunction(() => {
|
||||
return client.retrieveSeriesMetadata({
|
||||
studyInstanceUID,
|
||||
seriesInstanceUID,
|
||||
});
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
});
|
||||
}
|
||||
@ -40,15 +96,22 @@ export default class RetrieveMetadataLoaderAsync extends RetrieveMetadataLoader
|
||||
const preLoaders = [];
|
||||
const { studyInstanceUID, filters: { seriesInstanceUID } = {}, client } = this;
|
||||
|
||||
// asking to include Series Date, Series Time, Series Description
|
||||
// and Series Number in the series metadata returned to better sort series
|
||||
// in preLoad function
|
||||
let options = {
|
||||
studyInstanceUID,
|
||||
queryParams: {
|
||||
includefield: includeField,
|
||||
},
|
||||
};
|
||||
|
||||
if (seriesInstanceUID) {
|
||||
const options = {
|
||||
studyInstanceUID,
|
||||
queryParams: { SeriesInstanceUID: seriesInstanceUID },
|
||||
};
|
||||
options.queryParams.SeriesInstanceUID = seriesInstanceUID;
|
||||
preLoaders.push(client.searchForSeries.bind(client, options));
|
||||
}
|
||||
// Fallback preloader
|
||||
preLoaders.push(client.searchForSeries.bind(client, { studyInstanceUID }));
|
||||
preLoaders.push(client.searchForSeries.bind(client, options));
|
||||
|
||||
yield* preLoaders;
|
||||
}
|
||||
@ -62,24 +125,23 @@ export default class RetrieveMetadataLoaderAsync extends RetrieveMetadataLoader
|
||||
const { naturalizeDataset } = dcmjs.data.DicomMetaDictionary;
|
||||
const naturalized = result.map(naturalizeDataset);
|
||||
|
||||
return sortStudySeries(
|
||||
naturalized,
|
||||
sortCriteria || sortingCriteria.seriesSortCriteria.seriesInfoSortingCriteria,
|
||||
sortFunction
|
||||
);
|
||||
return sortStudySeries(naturalized, sortCriteria, sortFunction);
|
||||
}
|
||||
|
||||
async load(preLoadData) {
|
||||
const { client, studyInstanceUID } = this;
|
||||
|
||||
const seriesInstanceUIDs = preLoadData.map(s => s.SeriesInstanceUID);
|
||||
const seriesInstanceUIDs = preLoadData.map(seriesMetadata => {
|
||||
return { seriesInstanceUID: seriesMetadata.SeriesInstanceUID, metadata: seriesMetadata };
|
||||
});
|
||||
|
||||
const seriesAsyncLoader = makeSeriesAsyncLoader(client, studyInstanceUID, seriesInstanceUIDs);
|
||||
|
||||
const promises = [];
|
||||
|
||||
while (seriesAsyncLoader.hasNext()) {
|
||||
promises.push(seriesAsyncLoader.next());
|
||||
const promise = seriesAsyncLoader.next();
|
||||
promises.push(promise);
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@ -14,6 +14,7 @@ const defaultProtocol = {
|
||||
editableBy: {},
|
||||
protocolMatchingRules: [],
|
||||
toolGroupIds: ['default'],
|
||||
hpInitiationCriteria: { minSeriesLoaded: 1 },
|
||||
// -1 would be used to indicate active only, whereas other values are
|
||||
// the number of required priors referenced - so 0 means active with
|
||||
// 0 or more priors.
|
||||
|
||||
@ -635,4 +635,4 @@ function _findTabAndStudyOfDisplaySet(displaySetInstanceUID, tabs) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -13,7 +13,7 @@ import { history } from '../../utils/history';
|
||||
import loadModules from '../../pluginImports';
|
||||
import isSeriesFilterUsed from '../../utils/isSeriesFilterUsed';
|
||||
|
||||
const { getSplitParam } = utils;
|
||||
const { getSplitParam, sortingCriteria } = utils;
|
||||
const { TimingEnum } = Types;
|
||||
|
||||
/**
|
||||
@ -26,11 +26,33 @@ const { TimingEnum } = Types;
|
||||
* @returns array of subscriptions to cancel
|
||||
*/
|
||||
function defaultRouteInit(
|
||||
{ servicesManager, studyInstanceUIDs, dataSource, filters },
|
||||
{ servicesManager, studyInstanceUIDs, dataSource, filters, appConfig },
|
||||
hangingProtocolId
|
||||
) {
|
||||
const { displaySetService, hangingProtocolService, uiNotificationService } =
|
||||
const { displaySetService, hangingProtocolService, uiNotificationService, customizationService } =
|
||||
servicesManager.services;
|
||||
/**
|
||||
* Function to apply the hanging protocol when the minimum number of display sets were
|
||||
* received or all display sets retrieval were completed
|
||||
* @returns
|
||||
*/
|
||||
function applyHangingProtocol() {
|
||||
const displaySets = displaySetService.getActiveDisplaySets();
|
||||
|
||||
if (!displaySets || !displaySets.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Gets the studies list to use
|
||||
const studies = getStudies(studyInstanceUIDs, displaySets);
|
||||
|
||||
// study being displayed, and is thus the "active" study.
|
||||
const activeStudy = studies[0];
|
||||
|
||||
// run the hanging protocol matching on the displaySets with the predefined
|
||||
// hanging protocol in the mode configuration
|
||||
hangingProtocolService.run({ studies, activeStudy, displaySets }, hangingProtocolId);
|
||||
}
|
||||
|
||||
const unsubscriptions = [];
|
||||
const issuedWarningSeries = [];
|
||||
@ -55,6 +77,7 @@ function defaultRouteInit(
|
||||
duration: 7000,
|
||||
});
|
||||
}
|
||||
|
||||
displaySetService.makeDisplaySets(seriesMetadata.instances, madeInClient);
|
||||
}
|
||||
);
|
||||
@ -63,10 +86,15 @@ function defaultRouteInit(
|
||||
|
||||
log.time(TimingEnum.STUDY_TO_DISPLAY_SETS);
|
||||
log.time(TimingEnum.STUDY_TO_FIRST_IMAGE);
|
||||
|
||||
const allRetrieves = studyInstanceUIDs.map(StudyInstanceUID =>
|
||||
dataSource.retrieve.series.metadata({
|
||||
StudyInstanceUID,
|
||||
filters,
|
||||
returnPromises: true,
|
||||
sortCriteria:
|
||||
customizationService.get('sortingCriteria') ||
|
||||
sortingCriteria.seriesSortCriteria.seriesInfoSortingCriteria,
|
||||
})
|
||||
);
|
||||
|
||||
@ -77,31 +105,34 @@ function defaultRouteInit(
|
||||
});
|
||||
});
|
||||
|
||||
// The hanging protocol matching service is fairly expensive to run multiple
|
||||
// times, and doesn't allow partial matches to be made (it will simply fail
|
||||
// to display anything if a required match fails), so we wait here until all metadata
|
||||
// is retrieved (which will synchronously trigger the display set creation)
|
||||
// until we run the hanging protocol matching service.
|
||||
|
||||
Promise.allSettled(allRetrieves).then(() => {
|
||||
Promise.allSettled(allRetrieves).then(promises => {
|
||||
log.timeEnd(TimingEnum.STUDY_TO_DISPLAY_SETS);
|
||||
log.time(TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE);
|
||||
log.time(TimingEnum.DISPLAY_SETS_TO_ALL_IMAGES);
|
||||
const displaySets = displaySetService.getActiveDisplaySets();
|
||||
|
||||
if (!displaySets || !displaySets.length) {
|
||||
return;
|
||||
const allPromises = [];
|
||||
const remainingPromises = [];
|
||||
|
||||
function startRemainingPromises(remainingPromises) {
|
||||
remainingPromises.forEach(p => p.forEach(p => p.start()));
|
||||
}
|
||||
|
||||
// Gets the studies list to use
|
||||
const studies = getStudies(studyInstanceUIDs, displaySets);
|
||||
promises.forEach(promise => {
|
||||
const retrieveSeriesMetadataPromise = promise.value;
|
||||
if (Array.isArray(retrieveSeriesMetadataPromise)) {
|
||||
const { requiredSeries, remaining } = hangingProtocolService.filterSeriesRequiredForRun(
|
||||
hangingProtocolId,
|
||||
retrieveSeriesMetadataPromise
|
||||
);
|
||||
const requiredSeriesPromises = requiredSeries.map(promise => promise.start());
|
||||
allPromises.push(Promise.allSettled(requiredSeriesPromises));
|
||||
remainingPromises.push(remaining);
|
||||
}
|
||||
});
|
||||
|
||||
// study being displayed, and is thus the "active" study.
|
||||
const activeStudy = studies[0];
|
||||
|
||||
// run the hanging protocol matching on the displaySets with the predefined
|
||||
// hanging protocol in the mode configuration
|
||||
hangingProtocolService.run({ studies, activeStudy, displaySets }, hangingProtocolId);
|
||||
Promise.allSettled(allPromises).then(applyHangingProtocol);
|
||||
startRemainingPromises(remainingPromises);
|
||||
applyHangingProtocol();
|
||||
});
|
||||
|
||||
return unsubscriptions;
|
||||
@ -401,6 +432,7 @@ export default function ModeRoute({
|
||||
studyInstanceUIDs,
|
||||
dataSource,
|
||||
filters,
|
||||
appConfig,
|
||||
},
|
||||
hangingProtocolIdToUse
|
||||
);
|
||||
@ -482,4 +514,5 @@ ModeRoute.propTypes = {
|
||||
extensionManager: PropTypes.object,
|
||||
servicesManager: PropTypes.object,
|
||||
hotkeysManager: PropTypes.object,
|
||||
commandsManager: PropTypes.object,
|
||||
};
|
||||
|
||||
@ -214,6 +214,32 @@ export default class HangingProtocolService extends PubSubService {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the series required for running a hanging protocol.
|
||||
*
|
||||
* This can be extended in the future with more complex selection rules e.g.
|
||||
* N series of a given type, and M of a different type, such as all CT series,
|
||||
* and all SR, and then everything else.
|
||||
*
|
||||
* @param protocolId - The ID of the hanging protocol.
|
||||
* @param seriesPromises - An array of promises representing the series.
|
||||
* @returns An object containing the required series and the remaining series.
|
||||
*/
|
||||
public filterSeriesRequiredForRun(protocolId, seriesPromises) {
|
||||
if (Array.isArray(protocolId)) {
|
||||
protocolId = protocolId[0];
|
||||
}
|
||||
const minSeriesLoadedToRunHP =
|
||||
this.getProtocolById(protocolId)?.hpInitiationCriteria?.minSeriesLoaded ||
|
||||
seriesPromises.length;
|
||||
const requiredSeries = seriesPromises.slice(0, minSeriesLoadedToRunHP);
|
||||
const remaining = seriesPromises.slice(minSeriesLoadedToRunHP);
|
||||
return {
|
||||
requiredSeries,
|
||||
remaining,
|
||||
};
|
||||
}
|
||||
|
||||
/** Gets the protocol with id 'default' */
|
||||
public getDefaultProtocol(): HangingProtocol.Protocol {
|
||||
return this.getProtocolById('default');
|
||||
@ -543,8 +569,8 @@ export default class HangingProtocolService extends PubSubService {
|
||||
viewportId: existingViewportId
|
||||
? existingViewportId
|
||||
: index === 0
|
||||
? 'default'
|
||||
: uuidv4(),
|
||||
? 'default'
|
||||
: uuidv4(),
|
||||
},
|
||||
displaySets: viewport.displaySets || [],
|
||||
};
|
||||
|
||||
@ -293,6 +293,17 @@ export type Protocol = {
|
||||
*/
|
||||
numberOfPriorsReferenced?: number;
|
||||
syncDataForViewports?: boolean;
|
||||
/**
|
||||
* Set of minimal conditions necessary to run the hanging protocol.
|
||||
*/
|
||||
hpInitiationCriteria?: {
|
||||
/* If configured, sets the minimum number of series needed to run the hanging
|
||||
* protocol and start displaying images. Used when OHIF needs to handle studies
|
||||
* with several series and it is required that the first image should be loaded
|
||||
* faster.
|
||||
*/
|
||||
minSeriesLoaded: number;
|
||||
};
|
||||
};
|
||||
|
||||
/** Used to dynamically generate protocols.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user