From f299eb60e0224ad1b096ee5181413574b16b8972 Mon Sep 17 00:00:00 2001 From: Leonardo Campos Date: Wed, 26 Apr 2017 13:48:10 -0300 Subject: [PATCH] OHIF-26: StudyLoadingListener :: wadors --- OHIFViewer/client/components/viewer/viewer.js | 8 +- .../imageThumbnail/imageThumbnail.js | 11 ++ .../imageViewerViewport.js | 6 +- .../viewer/viewerMain/viewerMain.js | 3 + Packages/ohif-viewerbase/client/index.js | 4 + .../client/lib/StackManager.js | 15 ++ .../client/lib/studyLoadingListener.js | 184 ++++++++++++++++++ 7 files changed, 224 insertions(+), 7 deletions(-) create mode 100644 Packages/ohif-viewerbase/client/lib/studyLoadingListener.js diff --git a/OHIFViewer/client/components/viewer/viewer.js b/OHIFViewer/client/components/viewer/viewer.js index 883735646..a0a279b03 100644 --- a/OHIFViewer/client/components/viewer/viewer.js +++ b/OHIFViewer/client/components/viewer/viewer.js @@ -117,12 +117,16 @@ Template.viewer.onCreated(() => { OHIF.viewer.data.studyInstanceUids = []; instance.data.studies.forEach(study => { const studyMetadata = new OHIF.metadata.StudyMetadata(study, study.studyInstanceUid); - const displaySets = OHIF.viewerbase.sortingManager.getDisplaySets(studyMetadata); + let displaySets = study.displaySets; + + if(!displaySets) { + displaySets = OHIF.viewerbase.sortingManager.getDisplaySets(studyMetadata); + study.displaySets = displaySets; + } studyMetadata.setDisplaySets(displaySets); study.selected = true; - study.displaySets = displaySets; OHIF.viewer.Studies.insert(study); OHIF.viewer.StudyMetadataList.insert(studyMetadata); OHIF.viewer.data.studyInstanceUids.push(study.studyInstanceUid); diff --git a/Packages/ohif-viewerbase/client/components/studyBrowser/imageThumbnail/imageThumbnail.js b/Packages/ohif-viewerbase/client/components/studyBrowser/imageThumbnail/imageThumbnail.js index e8bdb3596..0679e8895 100644 --- a/Packages/ohif-viewerbase/client/components/studyBrowser/imageThumbnail/imageThumbnail.js +++ b/Packages/ohif-viewerbase/client/components/studyBrowser/imageThumbnail/imageThumbnail.js @@ -111,5 +111,16 @@ Template.imageThumbnail.helpers({ if (percentComplete && percentComplete !== 100) { return percentComplete + '%'; } + }, + + // Return how much the displaySet has already been loaded (%) + stackPercentComplete() { + const instance = Template.instance(); + const stack = instance.data.thumbnail.stack; + const displaySetInstanceUid = stack.displaySetInstanceUid; + const progress = Session.get('DisplaySetProgress:' + displaySetInstanceUid); + const percentComplete = parseFloat(progress ? progress.percentComplete : 0) * 100; + + return percentComplete.toFixed(2); } }); diff --git a/Packages/ohif-viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.js b/Packages/ohif-viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.js index 7f0ec5c91..aa40ff897 100644 --- a/Packages/ohif-viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.js +++ b/Packages/ohif-viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.js @@ -56,11 +56,7 @@ const loadDisplaySetIntoViewport = (data, templateData) => { const displaySet = data.displaySet; // Get stack from Stack Manager - let stack = StackManager.findStack(displaySet.displaySetInstanceUid); - // Make sure if the stack is already loaded in the stack manager, otherwise create it - if (!stack || !stack.imageIds) { - stack = StackManager.makeAndAddStack(data.study, displaySet); - } + let stack = StackManager.findOrCreateStack(data.study, displaySet); // If is a clip, updates the global FPS for cine dialog if (stack.isClip && stack.frameRate > 0) { diff --git a/Packages/ohif-viewerbase/client/components/viewer/viewerMain/viewerMain.js b/Packages/ohif-viewerbase/client/components/viewer/viewerMain/viewerMain.js index a5da1800e..06cfd17e2 100644 --- a/Packages/ohif-viewerbase/client/components/viewer/viewerMain/viewerMain.js +++ b/Packages/ohif-viewerbase/client/components/viewer/viewerMain/viewerMain.js @@ -37,6 +37,9 @@ Template.viewerMain.onRendered(() => { const parentElement = instance.$('#layoutManagerTarget').get(0); const studyPrefetcher = StudyPrefetcher.getInstance(); + OHIF.viewerbase.studyLoadingListener.clear(); + OHIF.viewerbase.studyLoadingListener.addStudies(studies); + OHIF.viewerbase.layoutManager = new LayoutManager(parentElement, studies); studyPrefetcher.setStudies(studies); diff --git a/Packages/ohif-viewerbase/client/index.js b/Packages/ohif-viewerbase/client/index.js index 0282a73ef..fdfaf0bd7 100644 --- a/Packages/ohif-viewerbase/client/index.js +++ b/Packages/ohif-viewerbase/client/index.js @@ -157,6 +157,10 @@ Viewerbase.createStacks = createStacks; import { StackManager } from './lib/StackManager'; Viewerbase.stackManager = StackManager; +// studyLoadingListener +import { studyLoadingListener } from './lib/studyLoadingListener'; +Viewerbase.studyLoadingListener = studyLoadingListener; + // toolManager import { toolManager } from './lib/toolManager'; Viewerbase.toolManager = toolManager; diff --git a/Packages/ohif-viewerbase/client/lib/StackManager.js b/Packages/ohif-viewerbase/client/lib/StackManager.js index 3df6c33b6..c446dcca0 100644 --- a/Packages/ohif-viewerbase/client/lib/StackManager.js +++ b/Packages/ohif-viewerbase/client/lib/StackManager.js @@ -93,6 +93,21 @@ const StackManager = { findStack(displaySetInstanceUid) { return stackMap[displaySetInstanceUid]; }, + /** + * Find a stack or reate one if it has not been created yet + * @param study The study who's metadata will be added + * @param displaySet The set of images to make the stack from + * @return {Array} Array with image IDs + */ + findOrCreateStack(study, displaySet) { + let stack = this.findStack(displaySet.displaySetInstanceUid); + + if (!stack || !stack.imageIds) { + stack = this.makeAndAddStack(study, displaySet); + } + + return stack; + }, /** * Gets the underlying map of displaySetInstanceUid to stack object. * WARNING: Do not change this object. It directly affects the manager. diff --git a/Packages/ohif-viewerbase/client/lib/studyLoadingListener.js b/Packages/ohif-viewerbase/client/lib/studyLoadingListener.js new file mode 100644 index 000000000..86bfa56a7 --- /dev/null +++ b/Packages/ohif-viewerbase/client/lib/studyLoadingListener.js @@ -0,0 +1,184 @@ +import { $ } from 'meteor/jquery'; +import { Session } from 'meteor/session'; + +class BaseLoadingListener { + constructor(stack) { + this.id = BaseLoadingListener.getNewId(); + this.stack = stack; + this.startListening(); + } + + _getProgressSessionId() { + const displaySetInstanceUid = this.stack.displaySetInstanceUid; + return 'DisplaySetProgress:' + displaySetInstanceUid; + } + + _clearSession() { + const progressSessionId = this._getProgressSessionId(); + Session.set(progressSessionId, undefined) + delete Session.keys.progressSessionId; + } + + destroy() { + this.stopListening(); + this._clearSession(); + } + + static getNewId() { + const timeSlice = (new Date()).getTime().toString().slice(-8); + const randomNumber = parseInt(Math.random() * 1000000000000); + + return timeSlice.toString() + randomNumber.toString(); + } +} + +class DICOMFileLoadingListener extends BaseLoadingListener { + constructor(stack) { + super(stack); + } +} + +class StackLoadingListener extends BaseLoadingListener { + constructor(stack) { + super(stack); + this.remainingImageIds = stack.imageIds.slice(); + this.loadedImageIds = []; + } + + getImageLoadedEventName() { + return 'CornerstoneImageLoaded.' + this.id; + } + + getImageCachePromiseRemoveEventName() { + return 'CornerstoneImageCachePromiseRemoved.' + this.id; + } + + startListening() { + const imageLoadedEventName = this.getImageLoadedEventName(); + const imageCachePromiseRemovedEventName = this.getImageCachePromiseRemoveEventName(); + + this.stopListening(); + + $(cornerstone).on(imageLoadedEventName, (e, eventData) => { + this._imageLoadedEventHandle(e, eventData); + }); + + $(cornerstone).on(imageCachePromiseRemovedEventName, (e, eventData) => { + this._imageCachePromiseRemovedEventHandle(e, eventData); + }); + } + + stopListening() { + const imageLoadedEventName = this.getImageLoadedEventName(); + const imageCachePromiseRemovedEventName = this.getImageCachePromiseRemoveEventName(); + + $(cornerstone).off(imageLoadedEventName); + $(cornerstone).off(imageCachePromiseRemovedEventName); + } + + _hasImageId(imageId) { + return this.stack.imageIds.indexOf(imageId) !== -1; + } + + _moveImageId(imageId, srcImageIds, dstImageIds) { + const imageIdIndex = srcImageIds.indexOf(imageId); + + if(imageIdIndex === -1) { + return; + } + + dstImageIds.push(imageId); + srcImageIds.splice(imageIdIndex, 1); + this._triggerProgress(); + } + + _imageLoadedEventHandle(e, eventData) { + const imageId = eventData.image.imageId; + this._moveImageId(imageId, this.remainingImageIds, this.loadedImageIds); + } + + _imageCachePromiseRemovedEventHandle(e, eventData) { + const imageId = eventData.imageId; + this._moveImageId(imageId, this.loadedImageIds, this.remainingImageIds); + } + + _triggerProgress() { + const loadedImagesCount = this.loadedImageIds.length; + const remainingImagesCount = this.remainingImageIds.length; + const totalImagesCount = loadedImagesCount + remainingImagesCount; + const percentComplete = loadedImagesCount / (totalImagesCount); + const progressSessionId = this._getProgressSessionId(); + + Session.set(progressSessionId, { + totalImagesCount, + loadedImagesCount, + remainingImagesCount, + percentComplete + }); + } +} + +class StudyLoadingListener { + constructor() { + this.listeners = {}; + } + + addStack(stack) { + const displaySetInstanceUid = stack.displaySetInstanceUid; + + if (!this.listeners[displaySetInstanceUid]) { + listener = this._createListener(stack); + if (listener) { + this.listeners[displaySetInstanceUid] = listener; + } + } + } + + addStudy(study) { + study.displaySets.forEach(displaySet => { + const stack = OHIF.viewerbase.stackManager.findOrCreateStack(study, displaySet); + this.addStack(stack); + }); + } + + addStudies(studies) { + if(!studies || !studies.length) { + return; + } + + for(let i = 0; i < studies.length; i++) { + this.addStudy(studies[i]); + } + } + + clear() { + const displaySetInstanceUids = Object.keys(this.listeners); + const length = displaySetInstanceUids.length; + + for(let i = 0; i < length; i++) { + const displaySetInstanceUid = displaySetInstanceUids[i]; + const displaySet = this.listeners[displaySetInstanceUid] + displaySet.destroy(); + } + + this.listener = {}; + } + + _createListener(stack) { + const schema = this._getSchema(stack); + + if(schema === 'wadors') { + return new StackLoadingListener(stack); + } + } + + _getSchema(stack) { + const imageId = stack.imageIds[0]; + const colonIndex = imageId.indexOf(":"); + return imageId.substring(0, colonIndex); + } +} + +const studyLoadingListener = new StudyLoadingListener(); + +export { studyLoadingListener }; \ No newline at end of file