dicom 0.9.3 → 0.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.rdoc +312 -290
- data/COPYING +674 -674
- data/Gemfile +3 -0
- data/dicom.gemspec +31 -0
- data/lib/dicom.rb +53 -51
- data/lib/dicom/anonymizer.rb +98 -123
- data/lib/dicom/audit_trail.rb +104 -116
- data/lib/dicom/constants.rb +219 -170
- data/lib/dicom/d_client.rb +122 -150
- data/lib/dicom/d_library.rb +219 -287
- data/lib/dicom/d_object.rb +451 -539
- data/lib/dicom/d_read.rb +151 -245
- data/lib/dicom/d_server.rb +329 -359
- data/lib/dicom/d_write.rb +327 -395
- data/lib/dicom/deprecated.rb +1 -72
- data/lib/dicom/dictionary/elements.txt +3646 -0
- data/lib/dicom/dictionary/uids.txt +334 -0
- data/lib/dicom/dictionary_element.rb +61 -0
- data/lib/dicom/element.rb +278 -218
- data/lib/dicom/elemental.rb +21 -27
- data/lib/dicom/file_handler.rb +121 -121
- data/lib/dicom/image_item.rb +819 -861
- data/lib/dicom/image_processor.rb +24 -15
- data/lib/dicom/image_processor_mini_magick.rb +21 -23
- data/lib/dicom/image_processor_r_magick.rb +39 -34
- data/lib/dicom/item.rb +133 -120
- data/lib/dicom/link.rb +1531 -1532
- data/lib/dicom/logging.rb +155 -158
- data/lib/dicom/parent.rb +782 -847
- data/lib/dicom/ruby_extensions.rb +248 -229
- data/lib/dicom/sequence.rb +109 -92
- data/lib/dicom/stream.rb +480 -511
- data/lib/dicom/uid.rb +82 -0
- data/lib/dicom/variables.rb +9 -9
- data/lib/dicom/version.rb +5 -5
- data/rakefile.rb +29 -0
- metadata +130 -76
- data/lib/dicom/dictionary.rb +0 -3280
data/lib/dicom/d_server.rb
CHANGED
@@ -1,359 +1,329 @@
|
|
1
|
-
module DICOM
|
2
|
-
|
3
|
-
# This class contains code for setting up a Service Class Provider (SCP),
|
4
|
-
# which will act as a simple storage node (a DICOM server that receives images).
|
5
|
-
#
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
#
|
35
|
-
attr_accessor :
|
36
|
-
# The
|
37
|
-
attr_accessor :
|
38
|
-
# The
|
39
|
-
attr_accessor :
|
40
|
-
# The maximum
|
41
|
-
attr_accessor :
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
#
|
50
|
-
|
51
|
-
|
52
|
-
#
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
64
|
-
#
|
65
|
-
#
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
#
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
@
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
#
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
#
|
117
|
-
#
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
#
|
130
|
-
#
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
#
|
141
|
-
#
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
#
|
154
|
-
#
|
155
|
-
#
|
156
|
-
#
|
157
|
-
#
|
158
|
-
def
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
#
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
#
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
#
|
279
|
-
|
280
|
-
|
281
|
-
#
|
282
|
-
#
|
283
|
-
#
|
284
|
-
#
|
285
|
-
#
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
break
|
331
|
-
end
|
332
|
-
end
|
333
|
-
if accepted_transfer_syntax
|
334
|
-
# Both abstract and transfer syntax has been approved:
|
335
|
-
pc[:result] = ACCEPTANCE
|
336
|
-
pc[:selected_transfer_syntax] = accepted_transfer_syntax
|
337
|
-
# Update our status variables:
|
338
|
-
approved += 1
|
339
|
-
else
|
340
|
-
# No transfer syntax was accepted for this particular presentation context:
|
341
|
-
pc[:result] = TRANSFER_SYNTAX_REJECTED
|
342
|
-
rejected += 1
|
343
|
-
end
|
344
|
-
else
|
345
|
-
# Abstract syntax rejected:
|
346
|
-
pc[:result] = ABSTRACT_SYNTAX_REJECTED
|
347
|
-
end
|
348
|
-
end
|
349
|
-
return info, approved, rejected
|
350
|
-
end
|
351
|
-
|
352
|
-
# Sets the default accepted abstract syntaxes and transfer syntaxes for this SCP.
|
353
|
-
#
|
354
|
-
def set_default_accepted_syntaxes
|
355
|
-
@accepted_transfer_syntaxes, @accepted_abstract_syntaxes = LIBRARY.extract_transfer_syntaxes_and_sop_classes
|
356
|
-
end
|
357
|
-
|
358
|
-
end
|
359
|
-
end
|
1
|
+
module DICOM
|
2
|
+
|
3
|
+
# This class contains code for setting up a Service Class Provider (SCP),
|
4
|
+
# which will act as a simple storage node (a DICOM server that receives images).
|
5
|
+
#
|
6
|
+
class DServer
|
7
|
+
include Logging
|
8
|
+
|
9
|
+
# Runs the server and takes a block for initializing.
|
10
|
+
#
|
11
|
+
# @param [Integer] port the network port to be used (defaults to 104)
|
12
|
+
# @param [String] path the directory where incoming DICOM files will be stored (defaults to './received/')
|
13
|
+
# @param [&block] block a block of code that will be run on the DServer instance, between creation and the launch of the SCP itself
|
14
|
+
#
|
15
|
+
# @example Run a server instance with a custom file handler
|
16
|
+
# require 'dicom'
|
17
|
+
# require 'my_file_handler'
|
18
|
+
# include DICOM
|
19
|
+
# DServer.run(104, 'c:/temp/') do |s|
|
20
|
+
# s.timeout = 100
|
21
|
+
# s.file_handler = MyFileHandler
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
def self.run(port=104, path='./received/', &block)
|
25
|
+
server = DServer.new(port)
|
26
|
+
server.instance_eval(&block)
|
27
|
+
server.start_scp(path)
|
28
|
+
end
|
29
|
+
|
30
|
+
# A customized FileHandler class to use instead of the default FileHandler included with ruby-dicom.
|
31
|
+
attr_accessor :file_handler
|
32
|
+
# The hostname that the TCPServer binds to.
|
33
|
+
attr_accessor :host
|
34
|
+
# The name of the server (application entity).
|
35
|
+
attr_accessor :host_ae
|
36
|
+
# The maximum allowed size of network packages (in bytes).
|
37
|
+
attr_accessor :max_package_size
|
38
|
+
# The network port to be used.
|
39
|
+
attr_accessor :port
|
40
|
+
# The maximum period the server will wait on an answer from a client before aborting the communication.
|
41
|
+
attr_accessor :timeout
|
42
|
+
|
43
|
+
# A hash containing the abstract syntaxes that will be accepted.
|
44
|
+
attr_reader :accepted_abstract_syntaxes
|
45
|
+
# A hash containing the transfer syntaxes that will be accepted.
|
46
|
+
attr_reader :accepted_transfer_syntaxes
|
47
|
+
|
48
|
+
# Creates a DServer instance.
|
49
|
+
#
|
50
|
+
# @note To customize logging behaviour, refer to the Logging module documentation.
|
51
|
+
#
|
52
|
+
# @param [Integer] port the network port to be used
|
53
|
+
# @param [Hash] options the options to use for the DICOM server
|
54
|
+
# @option options [String] :file_handler a customized FileHandler class to use instead of the default FileHandler
|
55
|
+
# @option options [String] :host the hostname that the TCPServer binds to (defaults to '127.0.0.1')
|
56
|
+
# @option options [String] :host_ae the name of the server (application entity)
|
57
|
+
# @option options [String] :max_package_size the maximum allowed size of network packages (in bytes)
|
58
|
+
# @option options [String] :timeout the number of seconds the server will wait on an answer from a client before aborting the communication
|
59
|
+
#
|
60
|
+
# @example Create a server using default settings
|
61
|
+
# s = DICOM::DServer.new
|
62
|
+
# @example Create a server with a specific host name and a custom buildt file handler
|
63
|
+
# require 'MyFileHandler'
|
64
|
+
# server = DICOM::DServer.new(104, :host_ae => "RUBY_SERVER", :file_handler => DICOM::MyFileHandler)
|
65
|
+
#
|
66
|
+
def initialize(port=104, options={})
|
67
|
+
require 'socket'
|
68
|
+
# Required parameters:
|
69
|
+
@port = port
|
70
|
+
# Optional parameters (and default values):
|
71
|
+
@file_handler = options[:file_handler] || FileHandler
|
72
|
+
@host = options[:host] || '127.0.0.1'
|
73
|
+
@host_ae = options[:host_ae] || "RUBY_DICOM"
|
74
|
+
@max_package_size = options[:max_package_size] || 32768 # 16384
|
75
|
+
@timeout = options[:timeout] || 10 # seconds
|
76
|
+
@min_length = 12 # minimum number of bytes to expect in an incoming transmission
|
77
|
+
# Variables used for monitoring state of transmission:
|
78
|
+
@connection = nil # TCP connection status
|
79
|
+
@association = nil # DICOM Association status
|
80
|
+
@request_approved = nil # Status of our DICOM request
|
81
|
+
@release = nil # Status of received, valid release response
|
82
|
+
set_default_accepted_syntaxes
|
83
|
+
end
|
84
|
+
|
85
|
+
# Adds an abstract syntax to the list of abstract syntaxes that the server will accept.
|
86
|
+
#
|
87
|
+
# @param [String] uid an abstract syntax UID
|
88
|
+
#
|
89
|
+
def add_abstract_syntax(uid)
|
90
|
+
lib_uid = LIBRARY.uid(uid)
|
91
|
+
raise "Invalid/unknown UID: #{uid}" unless lib_uid
|
92
|
+
@accepted_abstract_syntaxes[uid] = lib_uid.name
|
93
|
+
end
|
94
|
+
|
95
|
+
# Adds a transfer syntax to the list of transfer syntaxes that the server will accept.
|
96
|
+
#
|
97
|
+
# @param [String] uid a transfer syntax UID
|
98
|
+
#
|
99
|
+
def add_transfer_syntax(uid)
|
100
|
+
lib_uid = LIBRARY.uid(uid)
|
101
|
+
raise "Invalid/unknown UID: #{uid}" unless lib_uid
|
102
|
+
@accepted_transfer_syntaxes[uid] = lib_uid.name
|
103
|
+
end
|
104
|
+
|
105
|
+
# Prints the list of accepted abstract syntaxes to the screen.
|
106
|
+
#
|
107
|
+
def print_abstract_syntaxes
|
108
|
+
# Determine length of longest key to ensure pretty print:
|
109
|
+
max_uid = @accepted_abstract_syntaxes.keys.collect{|k| k.length}.max
|
110
|
+
puts "Abstract syntaxes which are accepted by this SCP:"
|
111
|
+
@accepted_abstract_syntaxes.sort.each do |pair|
|
112
|
+
puts "#{pair[0]}#{' '*(max_uid-pair[0].length)} #{pair[1]}"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Prints the list of accepted transfer syntaxes to the screen.
|
117
|
+
#
|
118
|
+
def print_transfer_syntaxes
|
119
|
+
# Determine length of longest key to ensure pretty print:
|
120
|
+
max_uid = @accepted_transfer_syntaxes.keys.collect{|k| k.length}.max
|
121
|
+
puts "Transfer syntaxes which are accepted by this SCP:"
|
122
|
+
@accepted_transfer_syntaxes.sort.each do |pair|
|
123
|
+
puts "#{pair[0]}#{' '*(max_uid-pair[0].length)} #{pair[1]}"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# Deletes a specific abstract syntax from the list of abstract syntaxes
|
128
|
+
# that the server will accept.
|
129
|
+
#
|
130
|
+
# @param [String] uid an abstract syntax UID
|
131
|
+
#
|
132
|
+
def delete_abstract_syntax(uid)
|
133
|
+
if uid.is_a?(String)
|
134
|
+
@accepted_abstract_syntaxes.delete(uid)
|
135
|
+
else
|
136
|
+
raise "Invalid type of UID. Expected String, got #{uid.class}!"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Deletes a specific transfer syntax from the list of transfer syntaxes
|
141
|
+
# that the server will accept.
|
142
|
+
#
|
143
|
+
# @param [String] uid a transfer syntax UID
|
144
|
+
#
|
145
|
+
def delete_transfer_syntax(uid)
|
146
|
+
if uid.is_a?(String)
|
147
|
+
@accepted_transfer_syntaxes.delete(uid)
|
148
|
+
else
|
149
|
+
raise "Invalid type of UID. Expected String, got #{uid.class}!"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Completely clears the list of abstract syntaxes that the server will accept.
|
154
|
+
#
|
155
|
+
# Following such a clearance, the user must ensure to add the specific
|
156
|
+
# abstract syntaxes that are to be accepted by the server.
|
157
|
+
#
|
158
|
+
def clear_abstract_syntaxes
|
159
|
+
@accepted_abstract_syntaxes = Hash.new
|
160
|
+
end
|
161
|
+
|
162
|
+
# Completely clears the list of transfer syntaxes that the server will accept.
|
163
|
+
#
|
164
|
+
# Following such a clearance, the user must ensure to add the specific
|
165
|
+
# transfer syntaxes that are to be accepted by the server.
|
166
|
+
#
|
167
|
+
def clear_transfer_syntaxes
|
168
|
+
@accepted_transfer_syntaxes = Hash.new
|
169
|
+
end
|
170
|
+
|
171
|
+
# Starts the Service Class Provider (SCP).
|
172
|
+
#
|
173
|
+
# This service acts as a simple storage node, which receives DICOM files
|
174
|
+
# and stores them in the specified folder.
|
175
|
+
#
|
176
|
+
# Customized storage actions can be set my modifying or replacing the FileHandler class.
|
177
|
+
#
|
178
|
+
# @param [String] path the directory where incoming files are to be saved
|
179
|
+
#
|
180
|
+
def start_scp(path='./received/')
|
181
|
+
if @accepted_abstract_syntaxes.size > 0 and @accepted_transfer_syntaxes.size > 0
|
182
|
+
logger.info("Started DICOM SCP server on port #{@port}.")
|
183
|
+
logger.info("Waiting for incoming transmissions...\n\n")
|
184
|
+
# Initiate server:
|
185
|
+
@scp = TCPServer.new(@host, @port)
|
186
|
+
# Use a loop to listen for incoming messages:
|
187
|
+
loop do
|
188
|
+
Thread.start(@scp.accept) do |session|
|
189
|
+
# Initialize the network package handler for this session:
|
190
|
+
link = Link.new(:host_ae => @host_ae, :max_package_size => @max_package_size, :timeout => @timeout, :file_handler => @file_handler)
|
191
|
+
link.set_session(session)
|
192
|
+
# Note who has contacted us:
|
193
|
+
logger.info("Connection established with: #{session.peeraddr[2]} (IP: #{session.peeraddr[3]})")
|
194
|
+
# Receive an incoming message:
|
195
|
+
segments = link.receive_multiple_transmissions
|
196
|
+
info = segments.first
|
197
|
+
# Interpret the received message:
|
198
|
+
if info[:valid]
|
199
|
+
association_error = check_association_request(info)
|
200
|
+
unless association_error
|
201
|
+
info, approved, rejected = process_syntax_requests(info)
|
202
|
+
link.handle_association_accept(info)
|
203
|
+
context = (LIBRARY.uid(info[:pc].first[:abstract_syntax]) ? LIBRARY.uid(info[:pc].first[:abstract_syntax]).name : 'Unknown UID!')
|
204
|
+
if approved > 0
|
205
|
+
if approved == 1
|
206
|
+
logger.info("Accepted the association request with context: #{context}")
|
207
|
+
else
|
208
|
+
if rejected == 0
|
209
|
+
logger.info("Accepted all #{approved} proposed contexts in the association request.")
|
210
|
+
else
|
211
|
+
logger.warn("Accepted only #{approved} of #{approved+rejected} of the proposed contexts in the association request.")
|
212
|
+
end
|
213
|
+
end
|
214
|
+
# Process the incoming data. This method will also take care of releasing the association:
|
215
|
+
success, messages = link.handle_incoming_data(path)
|
216
|
+
# Pass along any messages that has been recorded:
|
217
|
+
messages.each { |m| logger.public_send(m.first, m.last) } if messages.first
|
218
|
+
else
|
219
|
+
# No abstract syntaxes in the incoming request were accepted:
|
220
|
+
if rejected == 1
|
221
|
+
logger.warn("Rejected the association request with proposed context: #{context}")
|
222
|
+
else
|
223
|
+
logger.warn("Rejected all #{rejected} proposed contexts in the association request.")
|
224
|
+
end
|
225
|
+
# Since the requested abstract syntax was not accepted, the association must be released.
|
226
|
+
link.await_release
|
227
|
+
end
|
228
|
+
else
|
229
|
+
# The incoming association was not formally correct.
|
230
|
+
link.handle_rejection
|
231
|
+
end
|
232
|
+
else
|
233
|
+
# The incoming message was not recognised as a valid DICOM message. Abort:
|
234
|
+
link.handle_abort
|
235
|
+
end
|
236
|
+
# Terminate the connection:
|
237
|
+
link.stop_session
|
238
|
+
logger.info("Connection closed.\n\n")
|
239
|
+
end
|
240
|
+
end
|
241
|
+
else
|
242
|
+
raise "Unable to start SCP server as no accepted abstract syntaxes have been set!" if @accepted_abstract_syntaxes.length == 0
|
243
|
+
raise "Unable to start SCP server as no accepted transfer syntaxes have been set!" if @accepted_transfer_syntaxes.length == 0
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
|
248
|
+
private
|
249
|
+
|
250
|
+
|
251
|
+
# Checks if the association request is formally correct, by matching against an exact application context UID.
|
252
|
+
# Returns nil if valid, and an error code if it is not approved.
|
253
|
+
#
|
254
|
+
# === Notes
|
255
|
+
#
|
256
|
+
# Other things can potentially be checked here too, if we want to make the server more strict with regards to what information is received:
|
257
|
+
# * Application context name, calling AE title, called AE title
|
258
|
+
# * Description of error codes are given in the DICOM Standard, PS 3.8, Chapter 9.3.4 (Table 9-21).
|
259
|
+
#
|
260
|
+
# === Parameters
|
261
|
+
#
|
262
|
+
# * <tt>info</tt> -- An information hash from the received association request.
|
263
|
+
#
|
264
|
+
def check_association_request(info)
|
265
|
+
unless info[:application_context] == APPLICATION_CONTEXT
|
266
|
+
error = 2 # (application context name not supported)
|
267
|
+
logger.error("The application context in the incoming association request was not recognized: (#{info[:application_context]})")
|
268
|
+
else
|
269
|
+
error = nil
|
270
|
+
end
|
271
|
+
return error
|
272
|
+
end
|
273
|
+
|
274
|
+
# Checks if the requested abstract syntax & its transfer syntax(es) are supported by this server instance,
|
275
|
+
# and inserts a corresponding result code for each presentation context.
|
276
|
+
# Returns the modified association information hash, as well as the number of abstract syntaxes that were accepted and rejected.
|
277
|
+
#
|
278
|
+
# === Notes
|
279
|
+
#
|
280
|
+
# * Description of error codes are given in the DICOM Standard, PS 3.8, Chapter 9.3.3.2 (Table 9-18).
|
281
|
+
#
|
282
|
+
# === Parameters
|
283
|
+
#
|
284
|
+
# * <tt>info</tt> -- An information hash from the received association request.
|
285
|
+
#
|
286
|
+
def process_syntax_requests(info)
|
287
|
+
# A couple of variables used to analyse the properties of the association:
|
288
|
+
approved = 0
|
289
|
+
rejected = 0
|
290
|
+
# Loop through the presentation contexts:
|
291
|
+
info[:pc].each do |pc|
|
292
|
+
if @accepted_abstract_syntaxes[pc[:abstract_syntax]]
|
293
|
+
# Abstract syntax accepted. Proceed to check its transfer syntax(es):
|
294
|
+
proposed_transfer_syntaxes = pc[:ts].collect{|t| t[:transfer_syntax]}.sort
|
295
|
+
# Choose the first proposed transfer syntax that exists in our list of accepted transfer syntaxes:
|
296
|
+
accepted_transfer_syntax = nil
|
297
|
+
proposed_transfer_syntaxes.each do |proposed_ts|
|
298
|
+
if @accepted_transfer_syntaxes.include?(proposed_ts)
|
299
|
+
accepted_transfer_syntax = proposed_ts
|
300
|
+
break
|
301
|
+
end
|
302
|
+
end
|
303
|
+
if accepted_transfer_syntax
|
304
|
+
# Both abstract and transfer syntax has been approved:
|
305
|
+
pc[:result] = ACCEPTANCE
|
306
|
+
pc[:selected_transfer_syntax] = accepted_transfer_syntax
|
307
|
+
# Update our status variables:
|
308
|
+
approved += 1
|
309
|
+
else
|
310
|
+
# No transfer syntax was accepted for this particular presentation context:
|
311
|
+
pc[:result] = TRANSFER_SYNTAX_REJECTED
|
312
|
+
rejected += 1
|
313
|
+
end
|
314
|
+
else
|
315
|
+
# Abstract syntax rejected:
|
316
|
+
pc[:result] = ABSTRACT_SYNTAX_REJECTED
|
317
|
+
end
|
318
|
+
end
|
319
|
+
return info, approved, rejected
|
320
|
+
end
|
321
|
+
|
322
|
+
# Sets the default accepted abstract syntaxes and transfer syntaxes for this SCP.
|
323
|
+
#
|
324
|
+
def set_default_accepted_syntaxes
|
325
|
+
@accepted_transfer_syntaxes, @accepted_abstract_syntaxes = LIBRARY.extract_transfer_syntaxes_and_sop_classes
|
326
|
+
end
|
327
|
+
|
328
|
+
end
|
329
|
+
end
|