Class: DICOM::DServer
Overview
This class contains code for setting up a Service Class Provider (SCP), which will act as a simple storage node (a DICOM server that receives images).
Instance Attribute Summary collapse
-
#accepted_abstract_syntaxes ⇒ Object
readonly
A hash containing the abstract syntaxes that will be accepted.
-
#accepted_transfer_syntaxes ⇒ Object
readonly
A hash containing the transfer syntaxes that will be accepted.
-
#file_handler ⇒ Object
A customized FileHandler class to use instead of the default FileHandler included with ruby-dicom.
-
#host ⇒ Object
The hostname that the TCPServer binds to.
-
#host_ae ⇒ Object
The name of the server (application entity).
-
#max_package_size ⇒ Object
The maximum allowed size of network packages (in bytes).
-
#port ⇒ Object
The network port to be used.
-
#timeout ⇒ Object
The maximum period the server will wait on an answer from a client before aborting the communication.
Class Method Summary collapse
-
.run(port = 104, path = './received/', &block) ⇒ Object
Runs the server and takes a block for initializing.
Instance Method Summary collapse
-
#add_abstract_syntax(uid) ⇒ Object
Adds an abstract syntax to the list of abstract syntaxes that the server will accept.
-
#add_transfer_syntax(uid) ⇒ Object
Adds a transfer syntax to the list of transfer syntaxes that the server will accept.
-
#clear_abstract_syntaxes ⇒ Object
Completely clears the list of abstract syntaxes that the server will accept.
-
#clear_transfer_syntaxes ⇒ Object
Completely clears the list of transfer syntaxes that the server will accept.
-
#delete_abstract_syntax(uid) ⇒ Object
Deletes a specific abstract syntax from the list of abstract syntaxes that the server will accept.
-
#delete_transfer_syntax(uid) ⇒ Object
Deletes a specific transfer syntax from the list of transfer syntaxes that the server will accept.
-
#initialize(port = 104, options = {}) ⇒ DServer
constructor
Creates a DServer instance.
-
#print_abstract_syntaxes ⇒ Object
Prints the list of accepted abstract syntaxes to the screen.
-
#print_transfer_syntaxes ⇒ Object
Prints the list of accepted transfer syntaxes to the screen.
-
#start_scp(path = './received/') ⇒ Object
Starts the Service Class Provider (SCP).
Methods included from Logging
Constructor Details
#initialize(port = 104, options = {}) ⇒ DServer
To customize logging behaviour, refer to the Logging module documentation.
Creates a DServer instance.
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/dicom/d_server.rb', line 66 def initialize(port=104, ={}) require 'socket' # Required parameters: @port = port # Optional parameters (and default values): @file_handler = [:file_handler] || FileHandler @host = [:host] || '0.0.0.0' @host_ae = [:host_ae] || "RUBY_DICOM" @max_package_size = [:max_package_size] || 32768 # 16384 @timeout = [:timeout] || 10 # seconds @min_length = 12 # minimum number of bytes to expect in an incoming transmission # Variables used for monitoring state of transmission: @connection = nil # TCP connection status @association = nil # DICOM Association status @request_approved = nil # Status of our DICOM request @release = nil # Status of received, valid release response set_default_accepted_syntaxes end |
Instance Attribute Details
#accepted_abstract_syntaxes ⇒ Object (readonly)
A hash containing the abstract syntaxes that will be accepted.
44 45 46 |
# File 'lib/dicom/d_server.rb', line 44 def accepted_abstract_syntaxes @accepted_abstract_syntaxes end |
#accepted_transfer_syntaxes ⇒ Object (readonly)
A hash containing the transfer syntaxes that will be accepted.
46 47 48 |
# File 'lib/dicom/d_server.rb', line 46 def accepted_transfer_syntaxes @accepted_transfer_syntaxes end |
#file_handler ⇒ Object
A customized FileHandler class to use instead of the default FileHandler included with ruby-dicom.
31 32 33 |
# File 'lib/dicom/d_server.rb', line 31 def file_handler @file_handler end |
#host ⇒ Object
The hostname that the TCPServer binds to.
33 34 35 |
# File 'lib/dicom/d_server.rb', line 33 def host @host end |
#host_ae ⇒ Object
The name of the server (application entity).
35 36 37 |
# File 'lib/dicom/d_server.rb', line 35 def host_ae @host_ae end |
#max_package_size ⇒ Object
The maximum allowed size of network packages (in bytes).
37 38 39 |
# File 'lib/dicom/d_server.rb', line 37 def max_package_size @max_package_size end |
#port ⇒ Object
The network port to be used.
39 40 41 |
# File 'lib/dicom/d_server.rb', line 39 def port @port end |
#timeout ⇒ Object
The maximum period the server will wait on an answer from a client before aborting the communication.
41 42 43 |
# File 'lib/dicom/d_server.rb', line 41 def timeout @timeout end |
Class Method Details
Instance Method Details
#add_abstract_syntax(uid) ⇒ Object
Adds an abstract syntax to the list of abstract syntaxes that the server will accept.
89 90 91 92 93 |
# File 'lib/dicom/d_server.rb', line 89 def add_abstract_syntax(uid) lib_uid = LIBRARY.uid(uid) raise "Invalid/unknown UID: #{uid}" unless lib_uid @accepted_abstract_syntaxes[uid] = lib_uid.name end |
#add_transfer_syntax(uid) ⇒ Object
Adds a transfer syntax to the list of transfer syntaxes that the server will accept.
99 100 101 102 103 |
# File 'lib/dicom/d_server.rb', line 99 def add_transfer_syntax(uid) lib_uid = LIBRARY.uid(uid) raise "Invalid/unknown UID: #{uid}" unless lib_uid @accepted_transfer_syntaxes[uid] = lib_uid.name end |
#clear_abstract_syntaxes ⇒ Object
Completely clears the list of abstract syntaxes that the server will accept.
Following such a clearance, the user must ensure to add the specific abstract syntaxes that are to be accepted by the server.
158 159 160 |
# File 'lib/dicom/d_server.rb', line 158 def clear_abstract_syntaxes @accepted_abstract_syntaxes = Hash.new end |
#clear_transfer_syntaxes ⇒ Object
Completely clears the list of transfer syntaxes that the server will accept.
Following such a clearance, the user must ensure to add the specific transfer syntaxes that are to be accepted by the server.
167 168 169 |
# File 'lib/dicom/d_server.rb', line 167 def clear_transfer_syntaxes @accepted_transfer_syntaxes = Hash.new end |
#delete_abstract_syntax(uid) ⇒ Object
Deletes a specific abstract syntax from the list of abstract syntaxes that the server will accept.
132 133 134 135 136 137 138 |
# File 'lib/dicom/d_server.rb', line 132 def delete_abstract_syntax(uid) if uid.is_a?(String) @accepted_abstract_syntaxes.delete(uid) else raise "Invalid type of UID. Expected String, got #{uid.class}!" end end |
#delete_transfer_syntax(uid) ⇒ Object
Deletes a specific transfer syntax from the list of transfer syntaxes that the server will accept.
145 146 147 148 149 150 151 |
# File 'lib/dicom/d_server.rb', line 145 def delete_transfer_syntax(uid) if uid.is_a?(String) @accepted_transfer_syntaxes.delete(uid) else raise "Invalid type of UID. Expected String, got #{uid.class}!" end end |
#print_abstract_syntaxes ⇒ Object
Prints the list of accepted abstract syntaxes to the screen.
107 108 109 110 111 112 113 114 |
# File 'lib/dicom/d_server.rb', line 107 def print_abstract_syntaxes # Determine length of longest key to ensure pretty print: max_uid = @accepted_abstract_syntaxes.keys.collect{|k| k.length}.max puts "Abstract syntaxes which are accepted by this SCP:" @accepted_abstract_syntaxes.sort.each do |pair| puts "#{pair[0]}#{' '*(max_uid-pair[0].length)} #{pair[1]}" end end |
#print_transfer_syntaxes ⇒ Object
Prints the list of accepted transfer syntaxes to the screen.
118 119 120 121 122 123 124 125 |
# File 'lib/dicom/d_server.rb', line 118 def print_transfer_syntaxes # Determine length of longest key to ensure pretty print: max_uid = @accepted_transfer_syntaxes.keys.collect{|k| k.length}.max puts "Transfer syntaxes which are accepted by this SCP:" @accepted_transfer_syntaxes.sort.each do |pair| puts "#{pair[0]}#{' '*(max_uid-pair[0].length)} #{pair[1]}" end end |
#start_scp(path = './received/') ⇒ Object
Starts the Service Class Provider (SCP).
This service acts as a simple storage node, which receives DICOM files and stores them in the specified folder.
Customized storage actions can be set my modifying or replacing the FileHandler class.
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 |
# File 'lib/dicom/d_server.rb', line 180 def start_scp(path='./received/') if @accepted_abstract_syntaxes.size > 0 and @accepted_transfer_syntaxes.size > 0 logger.info("Started DICOM SCP server on port #{@port}.") logger.info("Waiting for incoming transmissions...\n\n") # Initiate server: @scp = TCPServer.new(@host, @port) # Use a loop to listen for incoming messages: loop do Thread.start(@scp.accept) do |session| # Initialize the network package handler for this session: link = Link.new(:host_ae => @host_ae, :max_package_size => @max_package_size, :timeout => @timeout, :file_handler => @file_handler) link.set_session(session) # Note who has contacted us: logger.info("Connection established with: #{session.peeraddr[2]} (IP: #{session.peeraddr[3]})") # Receive an incoming message: segments = link.receive_multiple_transmissions info = segments.first # Interpret the received message: if info[:valid] association_error = check_association_request(info) unless association_error info, approved, rejected = process_syntax_requests(info) link.handle_association_accept(info) context = (LIBRARY.uid(info[:pc].first[:abstract_syntax]) ? LIBRARY.uid(info[:pc].first[:abstract_syntax]).name : 'Unknown UID!') if approved > 0 if approved == 1 logger.info("Accepted the association request with context: #{context}") else if rejected == 0 logger.info("Accepted all #{approved} proposed contexts in the association request.") else logger.warn("Accepted only #{approved} of #{approved+rejected} of the proposed contexts in the association request.") end end # Process the incoming data. This method will also take care of releasing the association: success, = link.handle_incoming_data(path) # Pass along any messages that has been recorded: .each { |m| logger.public_send(m.first, m.last) } if .first else # No abstract syntaxes in the incoming request were accepted: if rejected == 1 logger.warn("Rejected the association request with proposed context: #{context}") else logger.warn("Rejected all #{rejected} proposed contexts in the association request.") end # Since the requested abstract syntax was not accepted, the association must be released. link.await_release end else # The incoming association was not formally correct. link.handle_rejection end else # The incoming message was not recognised as a valid DICOM message. Abort: link.handle_abort end # Terminate the connection: link.stop_session logger.info("Connection closed.\n\n") end end else raise "Unable to start SCP server as no accepted abstract syntaxes have been set!" if @accepted_abstract_syntaxes.length == 0 raise "Unable to start SCP server as no accepted transfer syntaxes have been set!" if @accepted_transfer_syntaxes.length == 0 end end |