LT-67; OHIF-78: Merging server configuration and CINE dialog changes from downstream (Ascend)

This commit is contained in:
Bruno Alves de Faria committed 2016-09-29 12:25:04 -03:00
1 parent da7202de0f
commit 8d5d1ee360
47 files changed
+516 -333

No files matched your search

+7 -1
View File
@@ -1,3 +1,9 @@
import { Mongo } from 'meteor/mongo';
StudyImportStatus = new Mongo.Collection('studyImportStatus');
StudyImportStatus = new Mongo.Collection('studyImportStatus');
// Servers describe the DICOM servers configurations
Servers = new Mongo.Collection('servers');
// CurrentServer is a single document collection to describe which of the Servers is being used
CurrentServer = new Mongo.Collection('currentServer');
@@ -0,0 +1,14 @@
/**
* Retrieves the current server configuration used to retrieve studies
*/
getCurrentServer = () => {
const currentServer = CurrentServer.findOne();
if (!currentServer) {
return;
}
const serverConfiguration = Servers.findOne({ _id: currentServer.serverId });
return serverConfiguration;
};
+14 -6
View File
@@ -14,6 +14,12 @@ const serverTypeDefinitions = {
optional: true
};
const wadoUriRootDefinitions = {
type: String,
label: 'WADO URI root',
max: 1000
};
export const DICOMWebRequestOptions = new SimpleSchema({
auth: {
type: String,
@@ -40,11 +46,7 @@ export const DICOMWebRequestOptions = new SimpleSchema({
export const DICOMWebServer = new SimpleSchema({
name: serverNameDefinitions,
type: serverTypeDefinitions,
wadoUriRoot: {
type: String,
label: 'WADO URI root',
max: 1000
},
wadoUriRoot: wadoUriRootDefinitions,
wadoRoot: {
type: String,
label: 'WADO root',
@@ -106,7 +108,7 @@ export const DIMSEPeer = new SimpleSchema({
},
supportsInstanceRetrievalByStudyUid: {
type: Boolean,
label: 'Supports instance retrieval by StudyUid?',
label: 'Supports instance retrieval by StudyUid',
defaultValue: true
}
});
@@ -114,6 +116,7 @@ export const DIMSEPeer = new SimpleSchema({
export const DIMSEServer = new SimpleSchema({
name: serverNameDefinitions,
type: serverTypeDefinitions,
wadoUriRoot: wadoUriRootDefinitions,
peers: {
type: [DIMSEPeer],
label: 'Peer List',
@@ -141,6 +144,11 @@ export const UISettings = new SimpleSchema({
type: Boolean,
label: 'The UP/DOWN display set navigation buttons will iterate over all the viewports at once?',
defaultValue: false
},
showCineDialogOnRendered: {
type: Boolean,
label: 'The Cine dialog will be displayed by default',
defaultValue: false
}
});
@@ -0,0 +1,2 @@
Meteor.subscribe('servers');
Meteor.subscribe('currentServer');
@@ -0,0 +1,50 @@
<template name="serverInformationDicomWeb">
<div class="row">
<div class="col-lg-6">
{{>inputText labelClass='form-group' key='wadoUriRoot'}}
</div>
<div class="col-lg-6">
{{>inputText labelClass='form-group' key='wadoRoot'}}
</div>
<div class="col-lg-6">
{{>inputSelect labelClass='form-group' key='imageRendering' hideSearch=true
options=(clone placeholder='Select an option')}}
</div>
<div class="col-lg-6">
{{>inputText labelClass='form-group' key='qidoRoot'}}
</div>
<div class="col-lg-6">
<strong>QIDO options</strong>
<div class="form-group">
<div class="form-control">
<div class="checkbox">
{{>inputCheckbox key='qidoSupportsIncludeField'}}
</div>
</div>
</div>
</div>
</div>
<hr>
<h4>Request options</h4>
{{#group class='row' key='requestOptions'}}
<div class="col-lg-6">
{{>inputText labelClass='form-group' key='auth'}}
</div>
<div class="col-lg-6">
<strong>Logging options</strong>
<div class="form-group">
<div class="form-control clearfix">
<div class="checkbox pull-left">
{{>inputCheckbox key='logRequests'}}
</div>
<div class="checkbox pull-left">
{{>inputCheckbox key='logResponses'}}
</div>
<div class="checkbox pull-left">
{{>inputCheckbox key='logTiming'}}
</div>
</div>
</div>
</div>
{{/group}}
</template>
@@ -0,0 +1,10 @@
Template.serverInformationDicomWeb.onRendered(() => {
const instance = Template.instance();
instance.autorun(function() {
const mode = instance.data.mode.get();
if (mode === 'edit') {
const data = instance.data.currentItem.get();
instance.data.form.value(data);
}
});
});
@@ -0,0 +1,60 @@
<template name="serverInformationDimse">
<div class="row">
<div class="col-lg-6">
{{>inputText labelClass='form-group' key='wadoUriRoot'}}
</div>
</div>
<div class="panel panel-default panel-body">
<div class="clearfix">
<h4 class="pull-left">Peer List</h4>
{{#button class='btn btn-primary pull-right'
action='newPeer' params=peer
}}New peer{{/button}}
</div>
<hr>
{{#group class='row' key='peers' arrayValues=true}}
{{#each peer in instance.peers.get}}
{{#group class='col-lg-6'}}
<div class="panel panel-default panel-body">
<div class="row">
<div class="col-lg-6">
{{>inputText class='form-control' key='aeTitle'}}
</div>
<div class="col-lg-6">
{{>inputText class='form-control' key='hostAE'}}
</div>
<div class="col-lg-6">
{{>inputText class='form-control' key='host'}}
</div>
<div class="col-lg-6">
{{>inputText class='form-control' key='port'}}
</div>
<div class="col-lg-12">
<strong>Settings</strong>
<div class="form-group">
<div class="form-control height-auto clearfix">
<div class="checkbox pull-left">
{{>inputCheckbox key='default'}}
</div>
<div class="checkbox pull-left">
{{>inputCheckbox key='server'}}
</div>
<div class="checkbox pull-left">
{{>inputCheckbox key='supportsInstanceRetrievalByStudyUid'}}
</div>
</div>
</div>
</div>
<div class="col-lg-12 text-right">
{{#button class='btn btn-sm btn-danger'
action='removePeer' params=@index
}}Remove{{/button}}
</div>
</div>
</div>
{{/group}}
{{/each}}
{{/group}}
</div>
<hr>
</template>
@@ -0,0 +1,38 @@
Template.serverInformationDimse.onCreated(() => {
const instance = Template.instance();
instance.peers = new ReactiveVar([]);
instance.autorun(() => {
const currentItem = instance.data.currentItem.get();
if (currentItem) {
instance.peers.set(currentItem.peers || []);
}
});
const api = instance.data.api;
_.extend(api, {
newPeer() {
const peers = instance.peers.get();
peers.push({});
instance.peers.set(peers);
},
removePeer(peerIndex) {
const peers = instance.peers.get();
peers.splice(peerIndex, 1);
instance.peers.set(peers);
const data = instance.data.currentItem.get();
data.peers = peers;
Tracker.afterFlush(() => instance.data.form.value(data));
}
});
});
Template.serverInformationDimse.onRendered(() => {
const instance = Template.instance();
instance.autorun(() => {
const mode = instance.data.mode.get();
if (mode === 'edit') {
const data = instance.data.currentItem.get();
instance.data.form.value(data);
}
});
});
@@ -0,0 +1,27 @@
<template name="serverInformationForm">
{{#form class='server-information-form' schema=instance.currentSchema api=this.api}}
{{>inputHidden key='_id'}}
<div class="row">
<div class="col-lg-6">
{{>inputText labelClass='form-group' key='name'}}
</div>
<div class="col-lg-6">
{{>inputSelect labelClass='form-group' key='type'
hideSearch=true disabled=(eq this.mode.get 'edit')
options=(clone placeholder='Choose a server type')}}
</div>
</div>
<hr>
{{#if eq this.serverType.get 'dicomWeb'}}
{{>serverInformationDicomWeb this}}
{{/if}}
{{#if eq this.serverType.get 'dimse'}}
{{>serverInformationDimse this}}
{{/if}}
{{#if this.serverType.get}}
<div>
{{#button class='btn-primary' action='save'}}Save{{/button}}
</div>
{{/if}}
{{/form}}
</template>
@@ -0,0 +1,72 @@
import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';
import { Tracker } from 'meteor/tracker';
import { DICOMWebServer as dicomSchema } from 'meteor/worklist/both/schema.js';
import { DIMSEServer as dimseSchema } from 'meteor/worklist/both/schema.js';
Template.serverInformationForm.onCreated(() => {
const instance = Template.instance();
instance.data.api = {
save() {
// Stop here if the form validation fails
if (!instance.data.form.validate()) {
return;
}
// Get the current form data
const formData = instance.data.form.value();
// Call the save method
Meteor.call('serverSave', formData, function(error) {
if (error) {
// TODO: check for errors: not-authorized, data-write
}
instance.data.resetState();
});
}
};
instance.currentSchema = new ReactiveVar(dicomSchema);
});
Template.serverInformationForm.onRendered(() => {
const instance = Template.instance();
instance.data.$form = instance.$('form:first');
instance.data.form = instance.data.$form.data('component');
// Handle the server type
instance.autorun(() => {
// Get the server type component
const typeComponent = instance.$('[data-key=type] :input').data('component');
// Run this computation every time the user change the server type
typeComponent.depend();
// Get the current server type value
const type = typeComponent.value();
// Set the serverType reactive value
instance.data.serverType.set(type);
// Change the schema based on the selected server type
if (type === 'dimse') {
instance.currentSchema.set(dimseSchema);
} else {
instance.currentSchema.set(dicomSchema);
}
});
// Handle the form mode (edit or add)
instance.autorun(() => {
const mode = instance.data.mode.get();
// Check if it is on edit mode and load the saved data
if (mode === 'edit') {
const data = instance.data.currentItem.get();
instance.data.form.value(data);
}
});
});
@@ -0,0 +1,10 @@
<template name="serverInformationFormField">
<div class="col-lg-6">
<div class="form-group">
<label class="wrapper">
<span>{{this.label}}</span>
<input type="text" name="{{this.name}}" class="form-control">
</label>
</div>
</div>
</template>
@@ -0,0 +1,45 @@
<template name="serverInformationList">
{{#form api=instance.api}}
<table id="tblServer" class="table">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th class="text-right">Actions</th>
</tr>
</thead>
<tbody>
{{#each server in servers}}
<tr>
<td>{{server.name}}</td>
<td>{{server.type}}</td>
<td class="text-right">
<div class="btn-group" role="group" aria-label="Actions">
{{#button class='btn-sm btn-default' action='use' params=server title='Use this server' disabled=(isActive server)}}
{{#if isActive server}}
<i class="fa fa-check-square-o"></i>
{{else}}
<i class="fa fa-square-o"></i>
{{/if}}
{{/button}}
{{#button class='btn-sm btn-default' action='edit' params=server title='Edit' disabled=(eq server.origin 'json')}}
<i class="fa fa-pencil"></i>
{{/button}}
{{#button class='btn-sm btn-danger' action='delete' params=server title='Remove' disabled=(eq server.origin 'json')}}
<i class="fa fa-trash"></i>
{{/button}}
</div>
</td>
</tr>
{{else}}
<tr>
<td class="text-center" colspan="3">No servers found</td>
</tr>
{{/each}}
</tbody>
</table>
{{#button class='btn-sm btn-default' action='add'}}
<i class="fa fa-plus"></i> Add a new server
{{/button}}
{{/form}}
</template>
@@ -0,0 +1,47 @@
Template.serverInformationList.onCreated(() => {
const instance = Template.instance();
instance.api = {
add: () => instance.data.mode.set('create'),
edit(server) {
instance.data.currentItem.set(server);
instance.data.mode.set('edit');
},
delete(server) {
// TODO: Replace this for confirmation dialog after LT-refactor is merged back to master
if (!confirm('Are you sure you want to remove this peer?')) {
return;
}
Meteor.call('serverRemove', server._id, error => {
// TODO: check for errors: data-write
});
},
use(server) {
Meteor.call('serverSetActive', server._id, error => {
// TODO: check for errors: data-write
});
}
};
});
Template.serverInformationList.helpers({
isActive: function(server) {
const currentServer = CurrentServer.findOne();
if (!currentServer) {
return;
}
return server._id === currentServer.serverId;
},
servers: function() {
const options = {
sort: {
origin: -1,
type: 1,
name: 1
}
};
return Servers.find({}, options).fetch();
}
});
@@ -0,0 +1,27 @@
<template name="serverInformationModal">
<div class="modal" id="serverInformationModal" tabindex="-1" role="dialog" aria-labelledby="serverInformationModalLabel">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
{{#let isList=(eq instance.container.mode.get "list")}}
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="serverInformationModalLabel">Server Information</h4>
</div>
<div class="modal-body">
{{#if isList}}
{{>serverInformationList instance.container}}
{{else}}
{{>serverInformationForm instance.container}}
{{/if}}
</div>
<div class="modal-footer">
{{#unless isList}}
<button type="button" class="btn btn-default js-back">Back</button>
{{/unless}}
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
{{/let}}
</div>
</div>
</div>
</template>
@@ -0,0 +1,20 @@
Template.serverInformationModal.onCreated(function() {
var instance = Template.instance();
instance.container = {
mode: new ReactiveVar('list'),
serverType: new ReactiveVar(null),
currentItem: new ReactiveVar(null),
$form: null,
resetState: function() {
instance.container.mode.set('list');
instance.container.serverType.set(null);
instance.container.currentItem.set(null);
}
};
});
Template.serverInformationModal.events({
'click .js-back, click [data-dismiss=modal]': function(event, instance) {
instance.container.resetState();
}
});
@@ -0,0 +1,5 @@
.server-information-form
label.wrapper
display: block
.checkbox
margin: 0 8px 0 0
@@ -34,6 +34,11 @@ switchToTab = function(contentId) {
var container;
if (contentId === 'worklistTab') {
container = $('.tab-content').find('#worklistTab').get(0);
if (!container) {
return;
}
var worklistContainer = document.createElement('div');
worklistContainer.classList.add('worklistContainer');
container.appendChild(worklistContainer);
+19 -1
View File
@@ -1,3 +1,6 @@
import { Meteor } from 'meteor/meteor';
import { Tracker } from 'meteor/tracker';
Worklist = {
functions: {},
callbacks: {}
@@ -10,4 +13,19 @@ function dblClickOnStudy(data) {
// Use the formatPN template helper to clean up the patient name
var title = formatPN(data.patientName);
openNewTab(data.studyInstanceUid, title);
}
}
let currentServerChangeHandlerFirstRun = true;
const currentServerChangeHandler = () => {
if (currentServerChangeHandlerFirstRun) {
currentServerChangeHandlerFirstRun = false;
return;
}
switchToTab('worklistTab');
};
CurrentServer.find().observe({
added: currentServerChangeHandler,
changed: currentServerChangeHandler
});
+37 -6
View File
@@ -16,11 +16,12 @@ Package.onUse(function (api) {
api.use('rwatts:uuid');
api.use('silentcicero:jszip');
api.use('aldeed:simple-schema');
api.use('accounts-base');
// Note: MomentJS appears to be required for Bootstrap3 Datepicker, but not a dependency for some reason
api.use('momentjs:moment');
api.use('gilbertwat:bootstrap3-daterangepicker');
api.use('gilbertwat:bootstrap3-daterangepicker');
// Our custom packages
api.use('ohif:core');
@@ -39,9 +40,13 @@ Package.onUse(function (api) {
// console for debugging purposes
api.addFiles('log.js');
api.addFiles('both/collections.js', [ 'client', 'server' ]);
api.addFiles('both/schema.js', [ 'client', 'server' ]);
api.addFiles('both/collections.js', ['client', 'server']);
api.addFiles('both/schema.js', ['client', 'server']);
api.addFiles('both/lib/getCurrentServer.js', ['client', 'server']);
// Client collections and subscriptions
api.addFiles('client/collections/subscriptions.js', 'client');
// Components
api.addFiles('client/components/worklist.html', 'client');
api.addFiles('client/components/worklist.js', 'client');
@@ -80,7 +85,24 @@ Package.onUse(function (api) {
api.addFiles('client/components/seriesDetailsTable/seriesDetailsTable.html', 'client');
api.addFiles('client/components/seriesDetailsTable/seriesDetailsTable.styl', 'client');
api.addFiles('client/components/seriesDetailsTable/seriesDetailsTable.js', 'client');
api.addFiles('client/components/serverInformation/serverInformationDicomWeb/serverInformationDicomWeb.html', 'client');
api.addFiles('client/components/serverInformation/serverInformationDicomWeb/serverInformationDicomWeb.js', 'client');
api.addFiles('client/components/serverInformation/serverInformationDimse/serverInformationDimse.html', 'client');
api.addFiles('client/components/serverInformation/serverInformationDimse/serverInformationDimse.js', 'client');
api.addFiles('client/components/serverInformation/serverInformationForm/serverInformationForm.html', 'client');
api.addFiles('client/components/serverInformation/serverInformationForm/serverInformationForm.js', 'client');
api.addFiles('client/components/serverInformation/serverInformationForm/serverInformationFormField.html', 'client');
api.addFiles('client/components/serverInformation/serverInformationList/serverInformationList.html', 'client');
api.addFiles('client/components/serverInformation/serverInformationList/serverInformationList.js', 'client');
api.addFiles('client/components/serverInformation/serverInformationModal/serverInformationModal.html', 'client');
api.addFiles('client/components/serverInformation/serverInformationModal/serverInformationModal.styl', 'client');
api.addFiles('client/components/serverInformation/serverInformationModal/serverInformationModal.js', 'client');
// Client-side library functions
api.addFiles('client/lib/getStudyMetadata.js', 'client');
api.addFiles('client/lib/getStudiesMetadata.js', 'client');
@@ -95,6 +117,7 @@ Package.onUse(function (api) {
// Server-side functions
api.addFiles('server/publications.js', 'server');
api.addFiles('server/validateServerConfiguration.js', 'server');
api.addFiles('server/servers.js', 'server');
api.addFiles('server/lib/remoteGetValue.js', 'server');
api.addFiles('server/lib/encodeQueryData.js', 'server');
@@ -113,12 +136,20 @@ Package.onUse(function (api) {
api.addFiles('server/services/dimse/instances.js', 'server');
api.addFiles('server/services/dimse/studies.js', 'server');
api.addFiles('server/services/dimse/retrieveMetadata.js', 'server');
api.addFiles('server/services/dimse/setup.js', 'server');
// Study, instance, and metadata retrieval from remote PACS via Orthanc as a proxy
api.addFiles('server/services/remote/instances.js', 'server');
api.addFiles('server/services/remote/studies.js', 'server');
api.addFiles('server/services/remote/retrieveMetadata.js', 'server');
// Export Servers and CurrentServer Collections
api.export('Servers', ['client', 'server']);
api.export('CurrentServer', ['client', 'server']);
// Export shared lib functions
api.export('getCurrentServer', ['client', 'server']);
api.export('Services', 'server');
// Export Worklist helper functions for usage in Routes
@@ -136,4 +167,4 @@ Package.onUse(function (api) {
api.export('WorklistTabs', 'client');
api.export('WorklistStudies', 'client');
api.export('WorklistSelectedStudies', 'client')
});
});
@@ -6,16 +6,17 @@ Meteor.methods({
GetStudyMetadata: function(studyInstanceUid) {
log.info('GetStudyMetadata(%s)', studyInstanceUid);
if (!Meteor.settings.servers.dicomWeb) {
throw 'No properly configured server was available over DICOMWeb';
// Get the server data. This is user-defined in the config.json files or through servers
// configuration modal
const server = getCurrentServer();
if (!server) {
throw 'No properly configured server was available over DICOMWeb or DIMSE.';
}
if (Meteor.settings.servers.dicomWeb && Meteor.settings.defaultServiceType === 'dicomWeb') {
// Get the server data. This is user-defined in the
// config.json files used to run the Meteor server
var server = Meteor.settings.servers.dicomWeb[0];
if (server.type === 'dicomWeb') {
return Services.WADO.RetrieveMetadata(server, studyInstanceUid);
} else if (Meteor.settings.servers.dimse && Meteor.settings.defaultServiceType === 'dimse') {
} else if (server.type === 'dimse') {
return Services.DIMSE.RetrieveMetadata(studyInstanceUid);
}
}
@@ -39,7 +39,8 @@ Meteor.methods({
* @returns {boolean}
*/
importSupported: function() {
if (Meteor.settings.servers.dimse && Meteor.settings.defaultServiceType === 'dimse') {
const server = getCurrentServer();
if (server && server.type === 'dimse') {
return true;
}
},
@@ -53,13 +54,17 @@ Meteor.methods({
return;
}
if (Meteor.settings.servers.dicomWeb && Meteor.settings.defaultServiceType === 'dicomWeb') {
const server = getCurrentServer();
if (!server) {
throw 'No properly configured server was available over DICOMWeb or DIMSE.';
}
if (server.type === 'dicomWeb') {
//TODO: Support importing studies into dicomWeb
console.log('Importing studies into dicomWeb is currently not supported.');
} else if (Meteor.settings.servers.dimse && Meteor.settings.defaultServiceType === 'dimse') {
} else if (server.type === 'dimse') {
importStudiesDIMSE(studiesToImport, studyImportStatusId);
} else {
throw 'No properly configured server was available over DICOMWeb or DIMSE.';
}
},
/**
@@ -1,19 +1,22 @@
Meteor.methods({
/**
* Use the specified filter to conduct a search from the DICOM server
*
* @param filter
*/
WorklistSearch: function(filter) {
if (Meteor.settings.servers.dicomWeb && Meteor.settings.defaultServiceType === 'dicomWeb') {
// Get the server data. This is user-defined in the
// config.json files used to run the Meteor server
var server = Meteor.settings.servers.dicomWeb[0];
WorklistSearch(filter) {
// Get the server data. This is user-defined in the config.json files or through servers
// configuration modal
const server = getCurrentServer();
return Services.QIDO.Studies(server, filter);
} else if (Meteor.settings.servers.dimse && Meteor.settings.defaultServiceType === 'dimse') {
return Services.DIMSE.Studies(filter);
} else {
if (!server) {
throw 'No properly configured server was available over DICOMWeb or DIMSE.';
}
if (server.type === 'dicomWeb') {
return Services.QIDO.Studies(server, filter);
} else if (server.type === 'dimse') {
return Services.DIMSE.Studies(filter);
}
}
});
+9 -1
View File
@@ -1,3 +1,11 @@
Meteor.publish('studyImportStatus', () => {
return StudyImportStatus.find();
});
});
Meteor.publish('servers', () => {
return Servers.find();
});
Meteor.publish('currentServer', () => {
return CurrentServer.find();
});
+92
View File
@@ -0,0 +1,92 @@
import { Meteor } from 'meteor/meteor';
Meteor.startup(function() {
Servers.remove({
origin: 'json'
});
_.each(Meteor.settings.servers, function(endpoints, serverType) {
_.each(endpoints, function(endpoint) {
var server = _.clone(endpoint);
server.origin = 'json';
server.type = serverType;
Servers.insert(server);
});
});
ServersControl.resetCurrentServer();
});
class ServersControl {
static writeCallback(error, affected) {
if (error) {
throw new Meteor.Error('data-write', error);
}
}
static resetCurrentServer() {
const currentServer = CurrentServer.findOne();
if (currentServer && Servers.find({ _id: currentServer.serverId }).count()) {
return;
}
const newServer = Servers.findOne({
origin: 'json',
type: Meteor.settings.defaultServiceType || 'dicomWeb'
});
if (newServer) {
CurrentServer.remove({});
CurrentServer.insert({
serverId: newServer._id
});
}
}
static find(query) {
return Servers.find(query).fetch();
}
static save(serverSettings) {
const query = {
_id: serverSettings._id
};
const options = {
upsert: true
};
if (!serverSettings._id) {
delete serverSettings._id;
}
return Servers.update(query, serverSettings, options, this.writeCallback);
}
static setActive(serverId) {
CurrentServer.remove({});
CurrentServer.insert({
serverId: serverId
});
}
static remove(serverId) {
var query = {
_id: serverId
};
const removeStatus = Servers.remove(query, this.writeCallback);
ServersControl.resetCurrentServer();
return removeStatus;
}
}
Meteor.methods({
serverFind: query => ServersControl.find(query),
serverSave: serverSettings => ServersControl.save(serverSettings),
serverSetActive: serverId => ServersControl.setActive(serverId),
serverRemove: serverId => ServersControl.remove(serverId)
});
@@ -1,68 +1,66 @@
/**
* Parses data returned from a study search and transforms it into
* an array of series that are present in the study
*
* @param resultData
* @returns {Array} Series List
*/
function resultDataToStudyMetadata(resultData) {
var seriesMap = {};
var seriesList = [];
resultData.forEach(function(instanceRaw) {
var instance = instanceRaw.toObject();
// Use seriesMap to cache series data
// If the series instance UID has already been used to
// process series data, continue using that series
var seriesInstanceUid = instance[0x0020000E];
var series = seriesMap[seriesInstanceUid];
// If no series data exists in the seriesMap cache variable,
// process any available series data
if (!series) {
series = {
seriesInstanceUid: seriesInstanceUid,
seriesNumber: instance[0x00200011],
instances: []
};
// Save this data in the seriesMap cache variable
seriesMap[seriesInstanceUid] = series;
seriesList.push(series);
}
// TODO: [custom-servers] Check which peer it should point to
var server = Meteor.settings.servers.dimse[0].peers[0];
var host = Meteor.settings.servers.dimse.host,
port = Meteor.settings.servers.dimse.port;
var serverRoot = host + ':' + port;
var sopInstanceUid = instance[0x00080018];
var uri = serverRoot + '/studies/' + studyInstanceUid + '/series/' + seriesInstanceUid + '/instances/' + sopInstanceUid + '/frames/1';
// Add this instance to the current series
series.instances.push({
sopClassUid: instance[0x00080016],
sopInstanceUid: sopInstanceUid,
uri: uri,
instanceNumber: instance[0x00200013]
});
});
return seriesList;
}
/**
* Retrieve a set of instances using a DIMSE call
* @param studyInstanceUid
* @returns {{wadoUriRoot: String, studyInstanceUid: String, seriesList: Array}}
*/
Services.DIMSE.Instances = function(studyInstanceUid) {
//var url = buildUrl(server, studyInstanceUid);
var result = DIMSE.retrieveInstances(studyInstanceUid);
return {
studyInstanceUid: studyInstanceUid,
seriesList: resultDataToStudyMetadata(result)
};
};
/**
* Parses data returned from a study search and transforms it into
* an array of series that are present in the study
*
* @param resultData
* @returns {Array} Series List
*/
function resultDataToStudyMetadata(resultData) {
var seriesMap = {};
var seriesList = [];
resultData.forEach(function(instanceRaw) {
var instance = instanceRaw.toObject();
// Use seriesMap to cache series data
// If the series instance UID has already been used to
// process series data, continue using that series
var seriesInstanceUid = instance[0x0020000E];
var series = seriesMap[seriesInstanceUid];
// If no series data exists in the seriesMap cache variable,
// process any available series data
if (!series) {
series = {
seriesInstanceUid: seriesInstanceUid,
seriesNumber: instance[0x00200011],
instances: []
};
// Save this data in the seriesMap cache variable
seriesMap[seriesInstanceUid] = series;
seriesList.push(series);
}
// TODO: Check which peer it should point to
var server = getCurrentServer().peers[0];
var serverRoot = server.host + ':' + server.port;
var sopInstanceUid = instance[0x00080018];
var uri = serverRoot + '/studies/' + studyInstanceUid + '/series/' + seriesInstanceUid + '/instances/' + sopInstanceUid + '/frames/1';
// Add this instance to the current series
series.instances.push({
sopClassUid: instance[0x00080016],
sopInstanceUid: sopInstanceUid,
uri: uri,
instanceNumber: instance[0x00200013]
});
});
return seriesList;
}
/**
* Retrieve a set of instances using a DIMSE call
* @param studyInstanceUid
* @returns {{wadoUriRoot: String, studyInstanceUid: String, seriesList: Array}}
*/
Services.DIMSE.Instances = function(studyInstanceUid) {
//var url = buildUrl(server, studyInstanceUid);
var result = DIMSE.retrieveInstances(studyInstanceUid);
return {
studyInstanceUid: studyInstanceUid,
seriesList: resultDataToStudyMetadata(result)
};
};
@@ -110,8 +110,8 @@ function resultDataToStudyMetadata(studyInstanceUid, resultData) {
};
// Retrieve the actual data over WADO-URI
var server = Meteor.settings.servers.dicomWeb[0];
instanceSummary.wadouri = WADOProxy.convertURL(server.wadoUriRoot + '?requestType=WADO&studyUID=' + studyInstanceUid + '&seriesUID=' + seriesInstanceUid + '&objectUID=' + sopInstanceUid + "&contentType=application%2Fdicom", server.requestOptions);
var server = getCurrentServer();
instanceSummary.wadouri = WADOProxy.convertURL(server.wadoUriRoot + '?requestType=WADO&studyUID=' + studyInstanceUid + '&seriesUID=' + seriesInstanceUid + '&objectUID=' + sopInstanceUid + '&contentType=application%2Fdicom', server.requestOptions);
series.instances.push(instanceSummary);
});
@@ -127,8 +127,8 @@ function resultDataToStudyMetadata(studyInstanceUid, resultData) {
* @returns {{seriesList: Array, patientName: *, patientId: *, accessionNumber: *, studyDate: *, modalities: *, studyDescription: *, imageCount: *, studyInstanceUid: *}}
*/
Services.DIMSE.RetrieveMetadata = function(studyInstanceUid) {
// TODO: Find active server
const activeServer = Meteor.settings.servers.dimse[0].peers[0];
// TODO: Check which peer it should point to
const activeServer = getCurrentServer().peers[0];
const supportsInstanceRetrievalByStudyUid = activeServer.supportsInstanceRetrievalByStudyUid;
let results;
@@ -0,0 +1,38 @@
import { Meteor } from 'meteor/meteor';
const setupDIMSE = () => {
// Terminate existing DIMSE servers and sockets and clean up the connection object
DIMSE.connection.reset();
// Get the new server configuration
const server = getCurrentServer();
// Stop here if the new server is not of DIMSE type
if (server.type !== 'dimse') {
return;
}
// Check if peers were defined in the server configuration and throw an error if not
const peers = server.peers;
if (!peers || !peers.length) {
console.error('dimse-config: ' + 'No DIMSE Peers provided.');
throw new Meteor.Error('dimse-config', 'No DIMSE Peers provided.');
}
// Add all the DIMSE peers, establishing the connections
console.log('Adding DIMSE peers');
try {
peers.forEach(peer => DIMSE.connection.addPeer(peer));
} catch(error) {
console.error('dimse-addPeers: ' + error);
throw new Meteor.Error('dimse-addPeers', error);
}
};
// Setup the DIMSE connections on startup or when the current server is changed
Meteor.startup(() => {
CurrentServer.find().observe({
added: setupDIMSE,
changed: setupDIMSE
});
});