dicom 0.7 → 0.8
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +55 -0
- data/README +51 -29
- data/init.rb +1 -0
- data/lib/dicom.rb +35 -21
- data/lib/dicom/{Anonymizer.rb → anonymizer.rb} +178 -80
- data/lib/dicom/constants.rb +121 -0
- data/lib/dicom/d_client.rb +888 -0
- data/lib/dicom/d_library.rb +208 -0
- data/lib/dicom/d_object.rb +424 -0
- data/lib/dicom/d_read.rb +433 -0
- data/lib/dicom/d_server.rb +397 -0
- data/lib/dicom/d_write.rb +420 -0
- data/lib/dicom/data_element.rb +175 -0
- data/lib/dicom/{Dictionary.rb → dictionary.rb} +390 -398
- data/lib/dicom/elements.rb +82 -0
- data/lib/dicom/file_handler.rb +116 -0
- data/lib/dicom/item.rb +87 -0
- data/lib/dicom/{Link.rb → link.rb} +749 -388
- data/lib/dicom/ruby_extensions.rb +44 -35
- data/lib/dicom/sequence.rb +62 -0
- data/lib/dicom/stream.rb +493 -0
- data/lib/dicom/super_item.rb +696 -0
- data/lib/dicom/super_parent.rb +615 -0
- metadata +25 -18
- data/DOCUMENTATION +0 -469
- data/lib/dicom/DClient.rb +0 -584
- data/lib/dicom/DLibrary.rb +0 -194
- data/lib/dicom/DObject.rb +0 -1579
- data/lib/dicom/DRead.rb +0 -532
- data/lib/dicom/DServer.rb +0 -304
- data/lib/dicom/DWrite.rb +0 -410
- data/lib/dicom/FileHandler.rb +0 -50
- data/lib/dicom/Stream.rb +0 -354
data/lib/dicom/DClient.rb
DELETED
@@ -1,584 +0,0 @@
|
|
1
|
-
# Copyright 2009-2010 Christoffer Lervag
|
2
|
-
|
3
|
-
module DICOM
|
4
|
-
|
5
|
-
# This class contains code for handling the client side of DICOM TCP/IP network communication.
|
6
|
-
class DClient
|
7
|
-
|
8
|
-
attr_accessor :ae, :host_ae, :host_ip, :max_package_size, :port, :timeout, :verbose
|
9
|
-
attr_reader :command_results, :data_results, :errors, :notices
|
10
|
-
|
11
|
-
# Initialize the instance with a host adress and a port number.
|
12
|
-
def initialize(host_ip, port, options={})
|
13
|
-
require 'socket'
|
14
|
-
# Required parameters:
|
15
|
-
@host_ip = host_ip
|
16
|
-
@port = port
|
17
|
-
# Optional parameters (and default values):
|
18
|
-
@ae = options[:ae] || "RUBY_DICOM"
|
19
|
-
@host_ae = options[:host_ae] || "DEFAULT"
|
20
|
-
@max_package_size = options[:max_package_size] || 32768 # 16384
|
21
|
-
@timeout = options[:timeout] || 10 # seconds
|
22
|
-
@min_length = 12 # minimum number of bytes to expect in an incoming transmission
|
23
|
-
@verbose = options[:verbose]
|
24
|
-
@verbose = true if @verbose == nil # Default verbosity is 'on'.
|
25
|
-
# Other instance variables:
|
26
|
-
@errors = Array.new # errors and warnings are put in this array
|
27
|
-
@notices = Array.new # information on successful transmissions are put in this array
|
28
|
-
# Variables used for monitoring state of transmission:
|
29
|
-
@connection = nil # TCP connection status
|
30
|
-
@association = nil # DICOM Association status
|
31
|
-
@request_approved = nil # Status of our DICOM request
|
32
|
-
@release = nil # Status of received, valid release response
|
33
|
-
# Results from a query:
|
34
|
-
@command_results = Array.new
|
35
|
-
@data_results = Array.new
|
36
|
-
# Set default values like transfer syntax, user information, endianness:
|
37
|
-
set_default_values
|
38
|
-
set_user_information_array
|
39
|
-
# Initialize the network package handler:
|
40
|
-
@link = Link.new(:ae => @ae, :host_ae => @host_ae, :max_package_size => @max_package_size, :timeout => @timeout)
|
41
|
-
end
|
42
|
-
|
43
|
-
|
44
|
-
# Query a service class provider for images that match the specified criteria.
|
45
|
-
# Example: find_images("0010,0020" => "123456789", "0020,000D" => "1.2.840.1145.342", "0020,000E" => "1.3.6.1.4.1.2452.6.687844") # (Patient ID, Study Instance UID & Series Instance UID)
|
46
|
-
def find_images(options={})
|
47
|
-
# Study Root Query/Retrieve Information Model - FIND:
|
48
|
-
@abstract_syntax = "1.2.840.10008.5.1.4.1.2.2.1"
|
49
|
-
# Prepare data elements for this operation:
|
50
|
-
set_data_fragment_find_images
|
51
|
-
set_data_options(options)
|
52
|
-
perform_find
|
53
|
-
return @data_results
|
54
|
-
end
|
55
|
-
|
56
|
-
|
57
|
-
# Query a service class provider for patients that match the specified criteria.
|
58
|
-
# Example: find_patients("0010,0010" => "James*") # (Patient's Name)
|
59
|
-
def find_patients(options={})
|
60
|
-
# Patient Root Query/Retrieve Information Model - FIND:
|
61
|
-
@abstract_syntax = "1.2.840.10008.5.1.4.1.2.1.1"
|
62
|
-
# Prepare data elements for this operation:
|
63
|
-
set_data_fragment_find_patients
|
64
|
-
set_data_options(options)
|
65
|
-
perform_find
|
66
|
-
return @data_results
|
67
|
-
end
|
68
|
-
|
69
|
-
|
70
|
-
# Query a service class provider for series that match the specified criteria.
|
71
|
-
# Example: find_series("0010,0020" => "123456789", "0020,000D" => "1.2.840.1145.342") # (Patient ID & Study Instance UID)
|
72
|
-
def find_series(options={})
|
73
|
-
# Study Root Query/Retrieve Information Model - FIND:
|
74
|
-
@abstract_syntax = "1.2.840.10008.5.1.4.1.2.2.1"
|
75
|
-
# Prepare data elements for this operation:
|
76
|
-
set_data_fragment_find_series
|
77
|
-
set_data_options(options)
|
78
|
-
perform_find
|
79
|
-
return @data_results
|
80
|
-
end
|
81
|
-
|
82
|
-
|
83
|
-
# Query a service class provider for studies that match the specified criteria.
|
84
|
-
# Example: find_studies("0008,0020" => "20090604-", "0010,000D" => "123456789") # (Study Date & Patient ID)
|
85
|
-
def find_studies(options={})
|
86
|
-
# Study Root Query/Retrieve Information Model - FIND:
|
87
|
-
@abstract_syntax = "1.2.840.10008.5.1.4.1.2.2.1"
|
88
|
-
# Prepare data elements for this operation:
|
89
|
-
set_data_fragment_find_studies
|
90
|
-
set_data_options(options)
|
91
|
-
perform_find
|
92
|
-
return @data_results
|
93
|
-
end
|
94
|
-
|
95
|
-
|
96
|
-
# Retrieve a dicom file from a service class provider (SCP/PACS).
|
97
|
-
# Example: get_image("c:/dicom/", "0008,0018" => sop_uid, "0020,000D" => study_uid, "0020,000E" => series_uid)
|
98
|
-
def get_image(path, options={})
|
99
|
-
# Study Root Query/Retrieve Information Model - GET:
|
100
|
-
@abstract_syntax = "1.2.840.10008.5.1.4.1.2.2.3"
|
101
|
-
# Transfer the current options to the data_elements hash:
|
102
|
-
set_command_fragment_get
|
103
|
-
# Prepare data elements for this operation:
|
104
|
-
set_data_fragment_get_image
|
105
|
-
set_data_options(options)
|
106
|
-
perform_get(path)
|
107
|
-
end
|
108
|
-
|
109
|
-
|
110
|
-
# Move an image to a dicom node other than yourself.
|
111
|
-
# Example: move_image("MYDICOM", "0008,0018" => sop_uid, "0020,000D" => study_uid, "0020,000E" => series_uid)
|
112
|
-
def move_image(destination, options={})
|
113
|
-
# Study Root Query/Retrieve Information Model - MOVE:
|
114
|
-
@abstract_syntax = "1.2.840.10008.5.1.4.1.2.2.2"
|
115
|
-
# Transfer the current options to the data_elements hash:
|
116
|
-
set_command_fragment_move(destination)
|
117
|
-
# Prepare data elements for this operation:
|
118
|
-
set_data_fragment_move_image
|
119
|
-
set_data_options(options)
|
120
|
-
perform_move
|
121
|
-
end
|
122
|
-
|
123
|
-
|
124
|
-
# Move an entire study to a dicom node other than yourself.
|
125
|
-
# Example: move_study("MYDICOM", "0010,0020" => pat_id, "0020,000D" => study_uid)
|
126
|
-
def move_study(destination, options={})
|
127
|
-
# Study Root Query/Retrieve Information Model - MOVE:
|
128
|
-
@abstract_syntax = "1.2.840.10008.5.1.4.1.2.2.2"
|
129
|
-
# Transfer the current options to the data_elements hash:
|
130
|
-
set_command_fragment_move(destination)
|
131
|
-
# Prepare data elements for this operation:
|
132
|
-
set_data_fragment_move_study
|
133
|
-
set_data_options(options)
|
134
|
-
perform_move
|
135
|
-
end
|
136
|
-
|
137
|
-
|
138
|
-
# Send a DICOM file to a service class provider (SCP/PACS).
|
139
|
-
def send(file_path)
|
140
|
-
# Load the DICOM file from the specified path:
|
141
|
-
obj = DObject.new(file_path, :verbose => false)
|
142
|
-
if obj.read_success
|
143
|
-
# Get the SOP Class UID (abstract syntax) from the DICOM obj:
|
144
|
-
@abstract_syntax = obj.get_value("0008,0016")
|
145
|
-
# Get the Transfer Syntax UID from the DICOM obj,
|
146
|
-
# and if not available, set to default: Implicit, Little endian
|
147
|
-
@transfer_syntax = [obj.get_value("0002,0010")] || ["1.2.840.10008.1.2"]
|
148
|
-
# Open a DICOM link:
|
149
|
-
establish_association
|
150
|
-
if @association
|
151
|
-
if @request_approved
|
152
|
-
# Continue with our c-store operation, since our request was accepted.
|
153
|
-
# Prepare the DICOM object for transmission:
|
154
|
-
obj.encode_segments(@max_pdu_length - 14)
|
155
|
-
# Handle the transmission:
|
156
|
-
perform_send(obj)
|
157
|
-
end
|
158
|
-
end
|
159
|
-
else
|
160
|
-
# Failed to read DICOM file. Can not transmit this file.
|
161
|
-
add_error("Error: The supplied file was not recognised as a valid DICOM file. File NOT transmitted. (file: #{file_path}")
|
162
|
-
end
|
163
|
-
# Close the DICOM link:
|
164
|
-
establish_release
|
165
|
-
end
|
166
|
-
|
167
|
-
|
168
|
-
# Tests the connection to the specified host by trying to negotiate an association, then releasing it.
|
169
|
-
def test
|
170
|
-
add_notice("TESTING CONNECTION...")
|
171
|
-
success = false
|
172
|
-
# Verification SOP Class:
|
173
|
-
@abstract_syntax = "1.2.840.10008.1.1"
|
174
|
-
# Open a DICOM link:
|
175
|
-
establish_association
|
176
|
-
if @association
|
177
|
-
if @request_approved
|
178
|
-
success = true
|
179
|
-
end
|
180
|
-
# Close the DICOM link:
|
181
|
-
establish_release
|
182
|
-
end
|
183
|
-
if success
|
184
|
-
add_notice("TEST SUCCSESFUL!")
|
185
|
-
else
|
186
|
-
add_error("TEST FAILED!")
|
187
|
-
end
|
188
|
-
return success
|
189
|
-
end
|
190
|
-
|
191
|
-
|
192
|
-
# Following methods are private:
|
193
|
-
private
|
194
|
-
|
195
|
-
|
196
|
-
# Adds a warning or error message to the instance array holding messages,
|
197
|
-
# and if verbose variable is true, prints the message as well.
|
198
|
-
def add_error(error)
|
199
|
-
puts error if @verbose
|
200
|
-
@errors << error
|
201
|
-
end
|
202
|
-
|
203
|
-
|
204
|
-
# Adds a notice (information regarding progress or successful communications) to the instance array,
|
205
|
-
# and if verbosity is set for these kinds of messages, prints it to the screen as well.
|
206
|
-
def add_notice(notice)
|
207
|
-
puts notice if @verbose
|
208
|
-
@notices << notice
|
209
|
-
end
|
210
|
-
|
211
|
-
|
212
|
-
# Open a TCP session with a specified server, and handle the association request along with its response.
|
213
|
-
def establish_association
|
214
|
-
# Reset some variables:
|
215
|
-
@association = false
|
216
|
-
@request_approved = false
|
217
|
-
# Initiate the association:
|
218
|
-
@link.build_association_request(@application_context_uid, @abstract_syntax, @transfer_syntax, @user_information)
|
219
|
-
@connection = TCPSocket.new(@host_ip, @port)
|
220
|
-
@link.transmit(@connection)
|
221
|
-
info = @link.receive_multiple_transmissions(@connection).first
|
222
|
-
# Interpret the results:
|
223
|
-
if info[:valid]
|
224
|
-
if info[:pdu] == "02"
|
225
|
-
# Values of importance are extracted and put into instance variables:
|
226
|
-
@association = true
|
227
|
-
@max_pdu_length = info[:max_pdu_length]
|
228
|
-
@presentation_context_id = info[:presentation_context_id]
|
229
|
-
add_notice("Association successfully negotiated with host #{host_ae} (#{host_ip}).")
|
230
|
-
else
|
231
|
-
add_error("Association was denied from host #{host_ae} (#{host_ip})!")
|
232
|
-
end
|
233
|
-
if info[:result] == 0
|
234
|
-
@request_approved = true
|
235
|
-
add_notice("Your request was accepted by host #{host_ae} (#{host_ip}).")
|
236
|
-
else
|
237
|
-
add_error("Your request was denied by host #{host_ae} (#{host_ip})!")
|
238
|
-
end
|
239
|
-
end
|
240
|
-
end
|
241
|
-
|
242
|
-
|
243
|
-
# Handle a release request and its response, as well as closing the TCP connection.
|
244
|
-
def establish_release
|
245
|
-
@release = false
|
246
|
-
if @abort
|
247
|
-
@connection.close unless @connection.closed?
|
248
|
-
add_notice("Association has been closed. (#{host_ae}, #{host_ip})")
|
249
|
-
else
|
250
|
-
unless @connection.closed?
|
251
|
-
@link.build_release_request
|
252
|
-
@link.transmit(@connection)
|
253
|
-
info = @link.receive_single_transmission(@connection).first
|
254
|
-
@connection.close
|
255
|
-
if info[:pdu] == "06"
|
256
|
-
add_notice("Association released properly from host #{host_ae} (#{host_ip}).")
|
257
|
-
else
|
258
|
-
add_error("Association was NOT released properly for some reason from host #{host_ae} (#{host_ip})!")
|
259
|
-
end
|
260
|
-
else
|
261
|
-
add_error("Connection was closed by the host (for some unknown reason) before the association could be released properly.")
|
262
|
-
end
|
263
|
-
end
|
264
|
-
@abort = false
|
265
|
-
end
|
266
|
-
|
267
|
-
|
268
|
-
# Handle the communication involved in DICOM query (C-FIND).
|
269
|
-
# Build the necessary strings and send the command and data element that makes up the query.
|
270
|
-
# Listens for and interpretes the incoming query responses.
|
271
|
-
def perform_find
|
272
|
-
# Open a DICOM link:
|
273
|
-
establish_association
|
274
|
-
if @association
|
275
|
-
if @request_approved
|
276
|
-
# Continue with our query, since the request was accepted.
|
277
|
-
# Set the query command elements array:
|
278
|
-
set_command_fragment_find
|
279
|
-
pdu="04"
|
280
|
-
#context = "01"
|
281
|
-
flags = "03"
|
282
|
-
@link.build_command_fragment(pdu, @presentation_context_id, flags, @command_elements)
|
283
|
-
@link.transmit(@connection)
|
284
|
-
@link.build_data_fragment(@data_elements)
|
285
|
-
@link.transmit(@connection)
|
286
|
-
# A query response will typically be sent in multiple, separate packets.
|
287
|
-
# Listen for incoming responses and interpret them individually, until we have received the last command fragment.
|
288
|
-
segments = @link.receive_multiple_transmissions(@connection)
|
289
|
-
process_returned_data(segments)
|
290
|
-
end
|
291
|
-
# Close the DICOM link:
|
292
|
-
establish_release
|
293
|
-
end
|
294
|
-
end
|
295
|
-
|
296
|
-
|
297
|
-
# Build and send command & data fragment, then receive the incoming file data.
|
298
|
-
def perform_get(path)
|
299
|
-
# Open a DICOM link:
|
300
|
-
establish_association
|
301
|
-
if @association
|
302
|
-
if @request_approved
|
303
|
-
# Continue with our operation, since the request was accepted.
|
304
|
-
pdu="04"
|
305
|
-
flags = "03"
|
306
|
-
@link.build_command_fragment(pdu, @presentation_context_id, flags, @command_elements)
|
307
|
-
@link.transmit(@connection)
|
308
|
-
@link.build_data_fragment(@data_elements) # (uses flag = 02)
|
309
|
-
@link.transmit(@connection)
|
310
|
-
# Listen for incoming file data:
|
311
|
-
success = @link.handle_incoming_data(@connection, path)
|
312
|
-
if success
|
313
|
-
# Send confirmation response:
|
314
|
-
@link.handle_response(@connection)
|
315
|
-
end
|
316
|
-
end
|
317
|
-
# Close the DICOM link:
|
318
|
-
establish_release
|
319
|
-
end
|
320
|
-
end
|
321
|
-
|
322
|
-
|
323
|
-
# Handle the communication involved in DICOM move request.
|
324
|
-
def perform_move
|
325
|
-
# Open a DICOM link:
|
326
|
-
establish_association
|
327
|
-
if @association
|
328
|
-
if @request_approved
|
329
|
-
# Continue with our operation, since the request was accepted.
|
330
|
-
pdu="04"
|
331
|
-
flags = "03"
|
332
|
-
@link.build_command_fragment(pdu, @presentation_context_id, flags, @command_elements)
|
333
|
-
@link.transmit(@connection)
|
334
|
-
flags = "02"
|
335
|
-
@link.build_data_fragment(@data_elements)
|
336
|
-
@link.transmit(@connection)
|
337
|
-
# Receive confirmation response:
|
338
|
-
segments = @link.receive_single_transmission(@connection)
|
339
|
-
process_returned_data(segments)
|
340
|
-
end
|
341
|
-
# Close the DICOM link:
|
342
|
-
establish_release
|
343
|
-
end
|
344
|
-
end
|
345
|
-
|
346
|
-
|
347
|
-
# Builds and sends the command fragment, then builds and sends the data fragments that
|
348
|
-
# conveys the information from the original DICOM file.
|
349
|
-
def perform_send(obj)
|
350
|
-
# Set the command array to be used:
|
351
|
-
sop_uid = obj.get_value("SOP Instance UID") # 0008,0018
|
352
|
-
if sop_uid
|
353
|
-
set_command_fragment_store(sop_uid)
|
354
|
-
pdu_type = "04"
|
355
|
-
flags = "03"
|
356
|
-
@link.build_command_fragment(pdu_type, @presentation_context_id, flags, @command_elements)
|
357
|
-
@link.transmit(@connection)
|
358
|
-
# Transmit all but the last segments:
|
359
|
-
flags = "00"
|
360
|
-
(0..obj.segments.length-2).each do |i|
|
361
|
-
@link.build_storage_fragment(pdu_type, @presentation_context_id, flags, obj.segments[i])
|
362
|
-
@link.transmit(@connection)
|
363
|
-
end
|
364
|
-
# Transmit the last segment:
|
365
|
-
flags = "02"
|
366
|
-
@link.build_storage_fragment(pdu_type, @presentation_context_id, flags, obj.segments.last)
|
367
|
-
@link.transmit(@connection)
|
368
|
-
# Receive confirmation response:
|
369
|
-
segments = @link.receive_single_transmission(@connection)
|
370
|
-
process_returned_data(segments)
|
371
|
-
else
|
372
|
-
add_error("Error: Unable to extract SOP Instance UID for the given DICOM file. File will not be sent to its destination.")
|
373
|
-
end
|
374
|
-
end
|
375
|
-
|
376
|
-
|
377
|
-
# Process the data that was returned from the interaction with the SCP and make it available to the user.
|
378
|
-
def process_returned_data(segments)
|
379
|
-
# Reset command results arrays:
|
380
|
-
@command_results = Array.new
|
381
|
-
@data_results = Array.new
|
382
|
-
# Try to extract data:
|
383
|
-
segments.each do |info|
|
384
|
-
if info[:valid]
|
385
|
-
# Determine if it is command or data:
|
386
|
-
if info[:presentation_context_flag] == "03"
|
387
|
-
# Command (last fragment):
|
388
|
-
@command_results << info[:results]
|
389
|
-
elsif info[:presentation_context_flag] == "02"
|
390
|
-
# Data (last fragment)
|
391
|
-
@data_results << info[:results]
|
392
|
-
end
|
393
|
-
end
|
394
|
-
end
|
395
|
-
end
|
396
|
-
|
397
|
-
|
398
|
-
# Reset the values of a array.
|
399
|
-
# It is assumed the arrays elements are an array in itself, where element[1]
|
400
|
-
# will be reset to the string value "".
|
401
|
-
def reset(array)
|
402
|
-
array.each do |element|
|
403
|
-
element[1] = ""
|
404
|
-
end
|
405
|
-
end
|
406
|
-
|
407
|
-
|
408
|
-
# Set command elements used in a C-GET-RQ:
|
409
|
-
def set_command_fragment_get
|
410
|
-
@command_elements = [
|
411
|
-
["0000,0002", "UI", @abstract_syntax], # Affected SOP Class UID
|
412
|
-
["0000,0100", "US", 16], # Command Field: 16 (C-GET-RQ)
|
413
|
-
["0000,0600", "AE", @ae], # Destination is ourselves
|
414
|
-
["0000,0700", "US", 0], # Priority: 0: medium
|
415
|
-
["0000,0800", "US", 1] # Data Set Type: 1
|
416
|
-
]
|
417
|
-
end
|
418
|
-
|
419
|
-
|
420
|
-
# Command elements used in a C-FIND-RQ.
|
421
|
-
# This seems to be the same, regardless of what we want to query.
|
422
|
-
def set_command_fragment_find
|
423
|
-
@command_elements = [
|
424
|
-
["0000,0002", "UI", @abstract_syntax], # Affected SOP Class UID
|
425
|
-
["0000,0100", "US", 32], # Command Field: 32 (C-FIND-RQ)
|
426
|
-
["0000,0110", "US", 1], # Message ID: 1
|
427
|
-
["0000,0700", "US", 0], # Priority: 0: medium
|
428
|
-
["0000,0800", "US", 1] # Data Set Type: 1
|
429
|
-
]
|
430
|
-
end
|
431
|
-
|
432
|
-
|
433
|
-
# Set command elements used in a C-MOVE-RQ:
|
434
|
-
def set_command_fragment_move(destination)
|
435
|
-
@command_elements = [
|
436
|
-
["0000,0002", "UI", @abstract_syntax], # Affected SOP Class UID
|
437
|
-
["0000,0100", "US", 33], # Command Field: 33 (C-MOVE-RQ)
|
438
|
-
["0000,0110", "US", 1], # Message ID: 1
|
439
|
-
["0000,0600", "AE", destination], # Move destination
|
440
|
-
["0000,0700", "US", 0], # Priority: 0: medium
|
441
|
-
["0000,0800", "US", 1] # Data Set Type: 1
|
442
|
-
]
|
443
|
-
end
|
444
|
-
|
445
|
-
|
446
|
-
# Command elements used in a p-data c-store-rq query command:
|
447
|
-
def set_command_fragment_store(sop_uid)
|
448
|
-
@command_elements = [
|
449
|
-
["0000,0002", "UI", @abstract_syntax], # Affected SOP Class UID
|
450
|
-
["0000,0100", "US", 1], # Command Field: 1 (C-STORE-RQ)
|
451
|
-
["0000,0110", "US", 1], # Message ID: 1
|
452
|
-
["0000,0700", "US", 0], # Priority: 0: medium
|
453
|
-
["0000,0800", "US", 1], # Data Set Type: 1
|
454
|
-
["0000,1000", "UI", sop_uid] # Affected SOP Instance UID
|
455
|
-
]
|
456
|
-
end
|
457
|
-
|
458
|
-
|
459
|
-
# Data elements used in a query for the images of a particular series:
|
460
|
-
def set_data_fragment_find_images
|
461
|
-
@data_elements = [
|
462
|
-
["0008,0018", ""], # SOP Instance UID
|
463
|
-
["0008,0052", "IMAGE"], # Query/Retrieve Level: "IMAGE"
|
464
|
-
["0020,000D", ""], # Study Instance UID
|
465
|
-
["0020,000E", ""], # Series Instance UID
|
466
|
-
["0020,0013", ""] # Instance Number
|
467
|
-
]
|
468
|
-
end
|
469
|
-
|
470
|
-
|
471
|
-
# Data elements used in a query for patients:
|
472
|
-
def set_data_fragment_find_patients
|
473
|
-
@data_elements = [
|
474
|
-
["0008,0052", "PATIENT"], # Query/Retrieve Level: "PATIENT"
|
475
|
-
["0010,0010", ""], # Patient's Name
|
476
|
-
["0010,0020", ""], # Patient ID
|
477
|
-
["0010,0030", ""], # Patient's Birth Date
|
478
|
-
["0010,0040", ""] # Patient's Sex
|
479
|
-
]
|
480
|
-
end
|
481
|
-
|
482
|
-
|
483
|
-
# Data elements used in a query for the series of a particular study:
|
484
|
-
def set_data_fragment_find_series
|
485
|
-
@data_elements = [
|
486
|
-
["0008,0052", "SERIES"], # Query/Retrieve Level: "SERIES"
|
487
|
-
["0008,0060", ""], # Modality
|
488
|
-
["0008,103E", ""], # Series Description
|
489
|
-
["0020,000D", ""], # Study Instance UID
|
490
|
-
["0020,000E", ""], # Series Instance UID
|
491
|
-
["0020,0011", ""] # Series Number
|
492
|
-
]
|
493
|
-
end
|
494
|
-
|
495
|
-
|
496
|
-
# Data elements used in a query for studies:
|
497
|
-
def set_data_fragment_find_studies
|
498
|
-
@data_elements = [
|
499
|
-
["0008,0020", ""], # Study Date
|
500
|
-
["0008,0030", ""], # Study Time
|
501
|
-
["0008,0050", ""], # Accession Number
|
502
|
-
["0008,0052", "STUDY"], # Query/Retrieve Level: "STUDY"
|
503
|
-
["0008,0090", ""], # Referring Physician's Name
|
504
|
-
["0008,1030", ""], # Study Description
|
505
|
-
["0008,1060", ""], # Name of Physician(s) Reading Study
|
506
|
-
["0010,0010", ""], # Patient's Name
|
507
|
-
["0010,0020", ""], # Patient ID
|
508
|
-
["0010,0030", ""], # Patient's Birth Date
|
509
|
-
["0010,0040", ""], # Patient's Sex
|
510
|
-
["0020,000D", ""], # Study Instance UID
|
511
|
-
["0020,0010", ""] # Study ID
|
512
|
-
]
|
513
|
-
end
|
514
|
-
|
515
|
-
|
516
|
-
# Set data elements used for an image C-GET-RQ:
|
517
|
-
def set_data_fragment_get_image
|
518
|
-
@data_elements = [
|
519
|
-
["0008,0018", ""], # SOP Instance UID
|
520
|
-
["0008,0052", "IMAGE"], # Query/Retrieve Level: "IMAGE"
|
521
|
-
["0020,000D", ""], # Study Instance UID
|
522
|
-
["0020,000E", ""] # Series Instance UID
|
523
|
-
]
|
524
|
-
end
|
525
|
-
|
526
|
-
|
527
|
-
# Set data elements used for an image C-MOVE-RQ:
|
528
|
-
def set_data_fragment_move_image
|
529
|
-
@data_elements = [
|
530
|
-
["0008,0018", ""], # SOP Instance UID
|
531
|
-
["0008,0052", "IMAGE"], # Query/Retrieve Level: "IMAGE"
|
532
|
-
["0020,000D", ""], # Study Instance UID
|
533
|
-
["0020,000E", ""] # Series Instance UID
|
534
|
-
]
|
535
|
-
end
|
536
|
-
|
537
|
-
|
538
|
-
# Set data elements used in a study C-MOVE-RQ:
|
539
|
-
def set_data_fragment_move_study
|
540
|
-
@data_elements = [
|
541
|
-
["0008,0052", "STUDY"], # Query/Retrieve Level: "STUDY"
|
542
|
-
["0010,0020", ""], # Patient ID
|
543
|
-
["0020,000D", ""] # Study Instance UID
|
544
|
-
]
|
545
|
-
end
|
546
|
-
|
547
|
-
|
548
|
-
# Transfer the user query options to the data elements array.
|
549
|
-
# NB: Only tags which are predefined for the specific query type will be updated
|
550
|
-
# (no new tags are allowed stored among the data elements)
|
551
|
-
def set_data_options(options)
|
552
|
-
options.each_pair do |key, value|
|
553
|
-
tags = @data_elements.transpose[0]
|
554
|
-
i = tags.index(key)
|
555
|
-
if i
|
556
|
-
@data_elements[i][1] = value
|
557
|
-
end
|
558
|
-
end
|
559
|
-
end
|
560
|
-
|
561
|
-
|
562
|
-
# Set default values for accepted transfer syntaxes:
|
563
|
-
def set_default_values
|
564
|
-
# DICOM Application Context Name (unknown if this will vary or is always the same):
|
565
|
-
@application_context_uid = "1.2.840.10008.3.1.1.1"
|
566
|
-
# Transfer syntax (preferred syntax appearing first)
|
567
|
-
@transfer_syntax = ["1.2.840.10008.1.2.1", # Explicit VR Little Endian
|
568
|
-
"1.2.840.10008.1.2.2", # Explicit VR Big Endian
|
569
|
-
"1.2.840.10008.1.2" # Implicit VR Little Endian
|
570
|
-
]
|
571
|
-
end
|
572
|
-
|
573
|
-
|
574
|
-
# Set user information [item type code, VR, value]
|
575
|
-
def set_user_information_array
|
576
|
-
@user_information = [
|
577
|
-
["51", "UL", @max_package_size], # Max PDU Length
|
578
|
-
["52", "STR", UID], # Implementation UID
|
579
|
-
["55", "STR", NAME] # Implementation Version (Name & version)
|
580
|
-
]
|
581
|
-
end
|
582
|
-
|
583
|
-
end # of class
|
584
|
-
end # of module
|