dcm4chee 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +45 -0
- data/Rakefile +9 -0
- data/app/controllers/dcm4chee/api/v1/application_entities_controller.rb +178 -0
- data/app/controllers/dcm4chee/api/v1/base_controller.rb +15 -0
- data/app/controllers/dcm4chee/api/v1/dicom_objects_controller.rb +58 -0
- data/app/controllers/dcm4chee/api/v1/file_systems_controller.rb +37 -0
- data/app/controllers/dcm4chee/api/v1/instances_controller.rb +68 -0
- data/app/controllers/dcm4chee/api/v1/modalities_controller.rb +29 -0
- data/app/controllers/dcm4chee/api/v1/patients_controller.rb +77 -0
- data/app/controllers/dcm4chee/api/v1/series_controller.rb +69 -0
- data/app/controllers/dcm4chee/api/v1/source_aets_controller.rb +29 -0
- data/app/controllers/dcm4chee/api/v1/studies_controller.rb +69 -0
- data/app/controllers/dcm4chee/api/v1/trashed_instances_controller.rb +82 -0
- data/app/controllers/dcm4chee/api/v1/trashed_patients_controller.rb +84 -0
- data/app/controllers/dcm4chee/api/v1/trashed_series_controller.rb +81 -0
- data/app/controllers/dcm4chee/api/v1/trashed_studies_controller.rb +81 -0
- data/app/controllers/dcm4chee/api/v1/trashes_controller.rb +23 -0
- data/app/controllers/dcm4chee/application_controller.rb +4 -0
- data/app/models/dcm4chee/application_entity.rb +129 -0
- data/app/models/dcm4chee/dicom_file.rb +62 -0
- data/app/models/dcm4chee/file_system.rb +95 -0
- data/app/models/dcm4chee/instance.rb +69 -0
- data/app/models/dcm4chee/modality.rb +17 -0
- data/app/models/dcm4chee/patient.rb +42 -0
- data/app/models/dcm4chee/series.rb +78 -0
- data/app/models/dcm4chee/source_aet.rb +17 -0
- data/app/models/dcm4chee/study.rb +60 -0
- data/app/models/dcm4chee/trashed_dicom_file.rb +58 -0
- data/app/models/dcm4chee/trashed_instance.rb +56 -0
- data/app/models/dcm4chee/trashed_patient.rb +40 -0
- data/app/models/dcm4chee/trashed_series.rb +50 -0
- data/app/models/dcm4chee/trashed_study.rb +40 -0
- data/config/routes.rb +27 -0
- data/lib/dcm4chee.rb +58 -0
- data/lib/dcm4chee/api_constraints.rb +12 -0
- data/lib/dcm4chee/dicom_object_manager.rb +48 -0
- data/lib/dcm4chee/engine.rb +5 -0
- data/lib/dcm4chee/models/has_dicom_object.rb +71 -0
- data/lib/dcm4chee/services/application_entity_service.rb +113 -0
- data/lib/dcm4chee/services/content_edit_service.rb +37 -0
- data/lib/dcm4chee/services/file_system_management.rb +16 -0
- data/lib/dcm4chee/services/mbean.rb +11 -0
- data/lib/dcm4chee/services/move_scu_service.rb +54 -0
- data/lib/dcm4chee/version.rb +3 -0
- data/lib/tasks/dcm4chee_tasks.rake +4 -0
- metadata +241 -0
data/config/routes.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Dcm4chee::Engine.routes.draw do
|
2
|
+
namespace :api, defaults: { format: 'json' } do
|
3
|
+
scope module: :v1, constraints: Dcm4chee::ApiConstraints.new(version: 1, default: true) do
|
4
|
+
resources :patients, only: [:index, :create]
|
5
|
+
resources :studies, only: [:index, :create]
|
6
|
+
resources :series, only: [:index, :create]
|
7
|
+
resources :instances, only: [:index, :create]
|
8
|
+
|
9
|
+
resources :modalities, only: :index
|
10
|
+
resources :source_aets, only: :index
|
11
|
+
|
12
|
+
resources :trashed_patients, only: [:index, :create, :destroy]
|
13
|
+
resources :trashed_studies, only: [:index, :create, :destroy]
|
14
|
+
resources :trashed_series, only: [:index, :create, :destroy]
|
15
|
+
resources :trashed_instances, only: [:index, :create, :destroy]
|
16
|
+
|
17
|
+
resources :file_systems, only: :index
|
18
|
+
|
19
|
+
resource :trash, only: :destroy
|
20
|
+
|
21
|
+
resources :application_entities, only: [:index, :create, :update, :destroy]
|
22
|
+
resources :dicom_objects, only: [:move] do
|
23
|
+
post :move, on: :collection
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/dcm4chee.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'data_mapper'
|
2
|
+
require 'confstruct'
|
3
|
+
require 'jolokia'
|
4
|
+
require 'sys/filesystem'
|
5
|
+
require 'dm-searcher'
|
6
|
+
require 'dm-pager'
|
7
|
+
|
8
|
+
require 'dcm4chee/engine'
|
9
|
+
require 'dcm4chee/api_constraints'
|
10
|
+
require 'dcm4chee/dicom_object_manager'
|
11
|
+
|
12
|
+
require 'dcm4chee/services/mbean'
|
13
|
+
require 'dcm4chee/services/application_entity_service'
|
14
|
+
require 'dcm4chee/services/content_edit_service'
|
15
|
+
require 'dcm4chee/services/file_system_management'
|
16
|
+
require 'dcm4chee/services/move_scu_service'
|
17
|
+
|
18
|
+
module Dcm4chee
|
19
|
+
REPOSITORY_NAME = :dcm4chee
|
20
|
+
|
21
|
+
class << self
|
22
|
+
def jolokia
|
23
|
+
@jolokia ||= ::Jolokia.new(url: config.jolokia_url)
|
24
|
+
end
|
25
|
+
|
26
|
+
def config
|
27
|
+
@config ||= Confstruct::Configuration.new
|
28
|
+
end
|
29
|
+
|
30
|
+
def configure(&block)
|
31
|
+
config.configure(&block)
|
32
|
+
|
33
|
+
config.repository_name = REPOSITORY_NAME unless config.repository_name
|
34
|
+
DataMapper.setup(:dcm4chee, config.repository_uri) if config.repository_uri
|
35
|
+
end
|
36
|
+
|
37
|
+
def respond_to?(method)
|
38
|
+
Dcm4chee::Service.constants.include?(method.to_s.camelize.to_sym) || super(method)
|
39
|
+
end
|
40
|
+
|
41
|
+
def method_missing(name, *args, &block)
|
42
|
+
service = instance_variable_get("@#{name}")
|
43
|
+
return service if service
|
44
|
+
|
45
|
+
begin
|
46
|
+
service_class = "Dcm4chee::Service::#{name.to_s.camelize}".constantize
|
47
|
+
service = service_class.new(jolokia)
|
48
|
+
instance_variable_set("@#{name}", service)
|
49
|
+
|
50
|
+
service
|
51
|
+
rescue
|
52
|
+
super(name, *args, &block)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
require 'dcm4chee/models/has_dicom_object'
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Dcm4chee
|
2
|
+
class ApiConstraints
|
3
|
+
def initialize(options)
|
4
|
+
@version = options[:version]
|
5
|
+
@default = options[:default]
|
6
|
+
end
|
7
|
+
|
8
|
+
def matches?(req)
|
9
|
+
@default || req.headers['Accept'].include?("application/vnd.menglifang.org; version=#{@version}")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Dcm4chee
|
2
|
+
class DicomObjectManager
|
3
|
+
def move(dest_aet, study_iuids, series_iuids, instance_iuids)
|
4
|
+
move_studies(dest_aet, study_iuids)
|
5
|
+
|
6
|
+
series_iuids.each { |k, v| move_series(dest_aet, k, v) }
|
7
|
+
|
8
|
+
instance_iuids.each do |key, val|
|
9
|
+
val.each { |k, v| move_instances(dest_aet, key, k, v) }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def move_studies(dest_aet, study_iuids)
|
16
|
+
Dcm4chee.move_scu_service.schedule_move(
|
17
|
+
nil,
|
18
|
+
dest_aet,
|
19
|
+
0,
|
20
|
+
nil,
|
21
|
+
study_iuids
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
def move_series(dest_aet, study_iuid, series_iuids)
|
26
|
+
Dcm4chee.move_scu_service.schedule_move(
|
27
|
+
nil,
|
28
|
+
dest_aet,
|
29
|
+
0,
|
30
|
+
nil,
|
31
|
+
[study_iuid],
|
32
|
+
series_iuids
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
def move_instances(dest_aet, study_iuid, series_iuid, instance_iuids)
|
37
|
+
Dcm4chee.move_scu_service.schedule_move(
|
38
|
+
nil,
|
39
|
+
dest_aet,
|
40
|
+
0,
|
41
|
+
nil,
|
42
|
+
[study_iuid],
|
43
|
+
[series_iuid],
|
44
|
+
instance_iuids
|
45
|
+
)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'hex_string'
|
2
|
+
require 'virtus'
|
3
|
+
require 'dicom'
|
4
|
+
|
5
|
+
module Dcm4chee
|
6
|
+
module HasDicomObject
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
class Element
|
10
|
+
include ::Virtus
|
11
|
+
|
12
|
+
attribute :name, String
|
13
|
+
attribute :value, String
|
14
|
+
attribute :tag, String
|
15
|
+
attribute :value_representation, String
|
16
|
+
attribute :length, Integer
|
17
|
+
end
|
18
|
+
|
19
|
+
def dcm
|
20
|
+
tsuid = defined?(dicom_file) ? dicom_file.transfer_syntax_uid : ::DICOM::EXPLICIT_LITTLE_ENDIAN
|
21
|
+
|
22
|
+
return nil unless dicom_attributes
|
23
|
+
|
24
|
+
# TODO Cache it
|
25
|
+
adapter = repository.adapter.options[:adapter]
|
26
|
+
attrs = nil
|
27
|
+
if adapter == 'postgres'
|
28
|
+
attrs = dicom_attributes.gsub(/\\x/, '').to_byte_string
|
29
|
+
elsif adapter == 'mysql'
|
30
|
+
attrs = dicom_attributes.to_hex_string.to_byte_string
|
31
|
+
end
|
32
|
+
|
33
|
+
::DICOM::DObject.parse(attrs, syntax: tsuid) if attrs
|
34
|
+
end
|
35
|
+
|
36
|
+
def dcm_elements
|
37
|
+
return [] unless dcm
|
38
|
+
|
39
|
+
@elements ||= begin
|
40
|
+
@elements = []
|
41
|
+
dcm.elements.each do |e|
|
42
|
+
@elements << Element.new(name: e.name,
|
43
|
+
value: e.value.to_s,
|
44
|
+
tag: e.tag,
|
45
|
+
value_representation: e.vr,
|
46
|
+
length: e.length)
|
47
|
+
end
|
48
|
+
|
49
|
+
@elements
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def as_json(opts = {})
|
54
|
+
opts[:exclude] ||= []
|
55
|
+
opts[:exclude] << :dicom_attributes
|
56
|
+
|
57
|
+
opts[:methods] ||= []
|
58
|
+
opts[:methods] << :dcm_elements
|
59
|
+
|
60
|
+
super(opts)
|
61
|
+
end
|
62
|
+
|
63
|
+
module ClassMethods
|
64
|
+
def dicom_field(name)
|
65
|
+
property :dicom_attributes,
|
66
|
+
::DataMapper::Property::Binary, field: name,
|
67
|
+
accessor: :private
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
module Dcm4chee
|
3
|
+
module Service
|
4
|
+
class ApplicationEntityService < MBean
|
5
|
+
|
6
|
+
MBEAN_NAME = 'dcm4chee.archive:service=AE'
|
7
|
+
|
8
|
+
# Create application entities.
|
9
|
+
#
|
10
|
+
# @param [Array] attr_values Attributes of an application entity
|
11
|
+
# 0. [String] title
|
12
|
+
# 1. [String] host
|
13
|
+
# 2. [Integer] port
|
14
|
+
# 3. [String] cipher_suites, SSL_RSA_WITH_NULL_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA
|
15
|
+
# 4. [String] patient_id_issuer
|
16
|
+
# 5. [String] accession_number_issuer
|
17
|
+
# 6. [String] username
|
18
|
+
# 7. [String] password
|
19
|
+
# 8. [String] fs_group
|
20
|
+
# 9. [String] ae_group
|
21
|
+
# 10. [String] description
|
22
|
+
# 11. [String] wado_url
|
23
|
+
# 12. [String] station_name
|
24
|
+
# 13. [String] institution
|
25
|
+
# 14. [String] department
|
26
|
+
# 15. [Boolean] installed
|
27
|
+
# 16. [Boolean] check_host
|
28
|
+
def add_ae(attr_values)
|
29
|
+
signatures = [
|
30
|
+
'java.lang.String',
|
31
|
+
'java.lang.String',
|
32
|
+
'int',
|
33
|
+
'java.lang.String',
|
34
|
+
'java.lang.String',
|
35
|
+
'java.lang.String',
|
36
|
+
'java.lang.String',
|
37
|
+
'java.lang.String',
|
38
|
+
'java.lang.String',
|
39
|
+
'java.lang.String',
|
40
|
+
'java.lang.String',
|
41
|
+
'java.lang.String',
|
42
|
+
'java.lang.String',
|
43
|
+
'java.lang.String',
|
44
|
+
'java.lang.String',
|
45
|
+
'boolean',
|
46
|
+
'boolean'
|
47
|
+
]
|
48
|
+
|
49
|
+
jolokia.execute(MBEAN_NAME,
|
50
|
+
"addAE(#{signatures.join(',')})",
|
51
|
+
attr_values)
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
# Update application entities.
|
56
|
+
#
|
57
|
+
# @param [Array] attr_values Attributes of an application entity.
|
58
|
+
# 0. [Integer] ID,Add a new one if id is -1. if not, update an existed one
|
59
|
+
# 1. [String] title
|
60
|
+
# 2. [String] host
|
61
|
+
# 3. [Integer] port
|
62
|
+
# 4. [String] cipher_suites, SSL_RSA_WITH_NULL_SHA,TLS_RSA_WITH_AES_128_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA
|
63
|
+
# 5. [String] patient_id_issuer
|
64
|
+
# 6. [String] accession_number_issuer
|
65
|
+
# 7. [String] username
|
66
|
+
# 8. [String] password
|
67
|
+
# 9. [String] fs_group
|
68
|
+
# 10. [String] ae_group
|
69
|
+
# 11. [String] description
|
70
|
+
# 12. [String] wado_url
|
71
|
+
# 13. [String] station_name
|
72
|
+
# 14. [String] institution
|
73
|
+
# 15. [String] department
|
74
|
+
# 16. [Boolean] installed
|
75
|
+
# 17. [Boolean] check_host
|
76
|
+
def update_ae(attr_values)
|
77
|
+
signatures = [
|
78
|
+
'long',
|
79
|
+
'java.lang.String',
|
80
|
+
'java.lang.String',
|
81
|
+
'int',
|
82
|
+
'java.lang.String',
|
83
|
+
'java.lang.String',
|
84
|
+
'java.lang.String',
|
85
|
+
'java.lang.String',
|
86
|
+
'java.lang.String',
|
87
|
+
'java.lang.String',
|
88
|
+
'java.lang.String',
|
89
|
+
'java.lang.String',
|
90
|
+
'java.lang.String',
|
91
|
+
'java.lang.String',
|
92
|
+
'java.lang.String',
|
93
|
+
'java.lang.String',
|
94
|
+
'boolean',
|
95
|
+
'boolean'
|
96
|
+
]
|
97
|
+
|
98
|
+
jolokia.execute(MBEAN_NAME,
|
99
|
+
"updateAE(#{signatures.join(',')})",
|
100
|
+
attr_values)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Delete an application entity.
|
104
|
+
#
|
105
|
+
# @param [String] title the title of the deleting AE.
|
106
|
+
def remove_ae(title)
|
107
|
+
jolokia.execute(MBEAN_NAME,
|
108
|
+
"removeAE(java.lang.String)",
|
109
|
+
title)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Dcm4chee
|
2
|
+
module Service
|
3
|
+
class ContentEditService < MBean
|
4
|
+
|
5
|
+
MBEAN_NAME = 'dcm4chee.archive:service=ContentEditService'
|
6
|
+
|
7
|
+
def empty_trash
|
8
|
+
jolokia.execute(MBEAN_NAME, 'emptyTrash')
|
9
|
+
end
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def define_component(name)
|
13
|
+
name = name.to_s
|
14
|
+
|
15
|
+
define_method("undelete_#{name}".to_sym) do |id|
|
16
|
+
jolokia.execute(MBEAN_NAME,
|
17
|
+
"undelete#{name.camelize}(long)", id)
|
18
|
+
end
|
19
|
+
|
20
|
+
define_method("delete_#{name}".to_sym) do |id|
|
21
|
+
jolokia.execute(MBEAN_NAME,
|
22
|
+
"delete#{name.camelize}(long)", id)
|
23
|
+
end
|
24
|
+
define_method("move_#{name}_to_trash".to_sym) do |id|
|
25
|
+
jolokia.execute(MBEAN_NAME,
|
26
|
+
"move#{name.camelize}ToTrash(long)", id)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
define_component :patient
|
32
|
+
define_component :study
|
33
|
+
define_component :series
|
34
|
+
define_component :instance
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Dcm4chee
|
2
|
+
module Service
|
3
|
+
class FileSystemManagement < MBean
|
4
|
+
|
5
|
+
MBEAN_NAME = 'dcm4chee.archive:group=ONLINE_STORAGE,service=FileSystemMgt'
|
6
|
+
|
7
|
+
def minimum_free_disk_space_bytes
|
8
|
+
jolokia.get_attribute(MBEAN_NAME, 'MinimumFreeDiskSpaceBytes') || 0
|
9
|
+
end
|
10
|
+
|
11
|
+
def expected_data_volume_per_day_bytes
|
12
|
+
jolokia.get_attribute(MBEAN_NAME, 'ExpectedDataVolumePerDayBytes') || 1_000_000_000
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
module Dcm4chee
|
3
|
+
module Service
|
4
|
+
class MoveScuService < MBean
|
5
|
+
|
6
|
+
MBEAN_NAME = 'dcm4chee.archive:service=MoveScu'
|
7
|
+
|
8
|
+
# Move data to AE
|
9
|
+
#
|
10
|
+
# @param [String] retrieve_aet
|
11
|
+
# @param [String] dest_aet
|
12
|
+
# @param [Integer] priority DICOM priority(0: Medium, 1: High, 2: Low)
|
13
|
+
# @param [String] pid
|
14
|
+
# @param [Array] study_iuids
|
15
|
+
# @param [Array] series_iuids
|
16
|
+
# @param [Array] sop_iuids
|
17
|
+
# @param [Integer] scheduled_time(milliseconds from 1970-01-01 00:00:00)
|
18
|
+
#
|
19
|
+
# @return [Hash] Result
|
20
|
+
def schedule_move(retrieve_aet,
|
21
|
+
dest_aet,
|
22
|
+
priority,
|
23
|
+
pid,
|
24
|
+
study_iuids,
|
25
|
+
series_iuids = nil,
|
26
|
+
sop_iuids = nil,
|
27
|
+
scheduled_time = 0)
|
28
|
+
signatures = [
|
29
|
+
'java.lang.String',
|
30
|
+
'java.lang.String',
|
31
|
+
'int',
|
32
|
+
'java.lang.String',
|
33
|
+
'[Ljava.lang.String;',
|
34
|
+
'[Ljava.lang.String;',
|
35
|
+
'[Ljava.lang.String;',
|
36
|
+
'long'
|
37
|
+
]
|
38
|
+
|
39
|
+
jolokia.execute(MBEAN_NAME,
|
40
|
+
"scheduleMove(#{signatures.join(',')})",
|
41
|
+
[
|
42
|
+
retrieve_aet,
|
43
|
+
dest_aet,
|
44
|
+
priority || 0,
|
45
|
+
pid,
|
46
|
+
study_iuids,
|
47
|
+
series_iuids,
|
48
|
+
sop_iuids,
|
49
|
+
scheduled_time
|
50
|
+
])
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|