rtp-connect 1.6 → 1.11

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.
@@ -8,8 +8,11 @@ module RTP
8
8
  #
9
9
  class DoseTracking < Record
10
10
 
11
+ # The number of attributes not having their own variable for this record (20 - 2).
12
+ NR_SURPLUS_ATTRIBUTES = 18
13
+
11
14
  # The Record which this instance belongs to.
12
- attr_reader :parent
15
+ attr_accessor :parent
13
16
  # The DoseAction records (if any) that belongs to this DoseTracking.
14
17
  attr_reader :dose_actions
15
18
  attr_reader :region_name
@@ -29,23 +32,8 @@ module RTP
29
32
  # @raise [ArgumentError] if given a string containing an invalid number of elements
30
33
  #
31
34
  def self.load(string, parent)
32
- # Get the quote-less values:
33
- values = string.to_s.values
34
- low_limit = 24
35
- high_limit = 26
36
- raise ArgumentError, "Invalid argument 'string': Expected at least #{low_limit} elements, got #{values.length}." if values.length < low_limit
37
- RTP.logger.warn "The number of elements (#{values.length}) for this DoseTracking record exceeds the known number of data items for this record (#{high_limit}). This may indicate an invalid record or that the RTP format has recently been expanded with new items." if values.length > high_limit
38
35
  d = self.new(parent)
39
- # Assign the values to attributes:
40
- d.keyword = values[0]
41
- d.region_name = values[1]
42
- d.region_prior_dose = values[2]
43
- d.field_ids = values.values_at(3, 5, 7, 9, 11, 13, 15, 17, 19, 21)
44
- d.region_coeffs = values.values_at(4, 6, 8, 10, 12, 14, 16, 18, 20, 22)
45
- d.actual_dose = values[23]
46
- d.actual_fractions = values[24]
47
- d.crc = values[-1]
48
- return d
36
+ d.load(string)
49
37
  end
50
38
 
51
39
  # Creates a new DoseTracking.
@@ -53,14 +41,25 @@ module RTP
53
41
  # @param [Record] parent a record which is used to determine the proper parent of this instance
54
42
  #
55
43
  def initialize(parent)
44
+ super('DOSE_DEF', 24, 26)
56
45
  # Child records:
57
46
  @dose_actions = Array.new
58
47
  # Parent relation (may get more than one type of record here):
59
48
  @parent = get_parent(parent.to_record, Plan)
60
49
  @parent.add_dose_tracking(self)
61
- @keyword = 'DOSE_DEF'
62
50
  @field_ids = Array.new(10)
63
51
  @region_coeffs = Array.new(10)
52
+ @attributes = [
53
+ # Required:
54
+ :keyword,
55
+ :region_name,
56
+ :region_prior_dose,
57
+ :field_ids,
58
+ :region_coeffs,
59
+ # Optional:
60
+ :actual_dose,
61
+ :actual_fractions
62
+ ]
64
63
  end
65
64
 
66
65
  # Checks for equality.
@@ -105,7 +104,7 @@ module RTP
105
104
  # @return [Array<String>] an array of attributes (in the same order as they appear in the RTP string)
106
105
  #
107
106
  def values
108
- return [
107
+ [
109
108
  @keyword,
110
109
  @region_name,
111
110
  @region_prior_dose,
@@ -124,23 +123,6 @@ module RTP
124
123
  self
125
124
  end
126
125
 
127
- # Encodes the DoseTracking object + any hiearchy of child objects,
128
- # to a properly formatted RTPConnect ascii string.
129
- #
130
- # @return [String] an RTP string with a single or multiple lines/records
131
- #
132
- def to_s
133
- str = encode
134
- if children
135
- children.each do |child|
136
- str += child.to_s
137
- end
138
- end
139
- return str
140
- end
141
-
142
- alias :to_str :to_s
143
-
144
126
  # Sets the field_ids attribute.
145
127
  #
146
128
  # @note As opposed to the ordinary (string) attributes, this attribute
@@ -148,9 +130,7 @@ module RTP
148
130
  # @param [Array<nil, #to_s>] array the new attribute values
149
131
  #
150
132
  def field_ids=(array)
151
- array = array.to_a
152
- raise ArgumentError, "Invalid argument 'array'. Expected length 10, got #{array.length}." unless array.length == 10
153
- @field_ids = array.collect! {|e| e && e.to_s}
133
+ @field_ids = array.to_a.validate_and_process(10)
154
134
  end
155
135
 
156
136
  # Sets the region_coeffs attribute.
@@ -160,21 +140,7 @@ module RTP
160
140
  # @param [Array<nil, #to_s>] array the new attribute values
161
141
  #
162
142
  def region_coeffs=(array)
163
- array = array.to_a
164
- raise ArgumentError, "Invalid argument 'array'. Expected length 10, got #{array.length}." unless array.length == 10
165
- @region_coeffs = array.collect! {|e| e && e.to_s}
166
- end
167
-
168
- # Sets the keyword attribute.
169
- #
170
- # @note Since only a specific string is accepted, this is more of an argument check than a traditional setter method
171
- # @param [#to_s] value the new attribute value
172
- # @raise [ArgumentError] if given an unexpected keyword
173
- #
174
- def keyword=(value)
175
- value = value.to_s.upcase
176
- raise ArgumentError, "Invalid keyword. Expected 'DOSE_DEF', got #{value}." unless value == "DOSE_DEF"
177
- @keyword = value
143
+ @region_coeffs = array.to_a.validate_and_process(10)
178
144
  end
179
145
 
180
146
  # Sets the region_name attribute.
@@ -220,6 +186,23 @@ module RTP
220
186
  #
221
187
  alias_method :state, :values
222
188
 
189
+ # Gives an array of indices indicating where the attributes of this record gets its
190
+ # values from in the comma separated string which the instance is created from.
191
+ #
192
+ # @param [Integer] length the number of elements to create in the indices array
193
+ #
194
+ def import_indices(length)
195
+ # Note that this method is defined in the parent Record class, where it is
196
+ # used for most record types. However, because this record has two attributes
197
+ # which contain an array of values, we use a custom import_indices method.
198
+ ind = Array.new(length - NR_SURPLUS_ATTRIBUTES) { |i| i }
199
+ # Override indices for field_ids and region_coeffs:
200
+ ind[3] = [3, 5, 7, 9, 11, 13, 15, 17, 19, 21]
201
+ ind[4] = [4, 6, 8, 10, 12, 14, 16, 18, 20, 22]
202
+ ind[5, 6] = [23, 24]
203
+ ind
204
+ end
205
+
223
206
  end
224
207
 
225
208
  end
@@ -9,7 +9,7 @@ module RTP
9
9
  class ExtendedField < Record
10
10
 
11
11
  # The Record which this instance belongs to.
12
- attr_reader :parent
12
+ attr_accessor :parent
13
13
  attr_reader :field_id
14
14
  attr_reader :original_plan_uid
15
15
  attr_reader :original_beam_number
@@ -18,6 +18,8 @@ module RTP
18
18
  attr_reader :accessory_code
19
19
  attr_reader :accessory_type
20
20
  attr_reader :high_dose_authorization
21
+ attr_reader :referenced_rt_plan_uid
22
+ attr_reader :referenced_rt_plan_relationship
21
23
 
22
24
  # Creates a new (treatment) ExtendedField by parsing a RTPConnect string line.
23
25
  #
@@ -27,26 +29,8 @@ module RTP
27
29
  # @raise [ArgumentError] if given a string containing an invalid number of elements
28
30
  #
29
31
  def self.load(string, parent)
30
- # Get the quote-less values:
31
- values = string.to_s.values
32
- low_limit = 4
33
- high_limit = 10
34
- raise ArgumentError, "Invalid argument 'string': Expected at least #{low_limit} elements, got #{values.length}." if values.length < low_limit
35
- RTP.logger.warn "The number of elements (#{values.length}) for this ExtendedField record exceeds the known number of data items for this record (#{high_limit}). This may indicate an invalid record or that the RTP format has recently been expanded with new items." if values.length > high_limit
36
32
  ef = self.new(parent)
37
- # Mandatory attributes:
38
- ef.keyword = values[0]
39
- ef.field_id = values[1]
40
- ef.original_plan_uid = values[2]
41
- # Optional attributes:
42
- ef.original_beam_number = values[3]
43
- ef.original_beam_name = values[4]
44
- ef.is_fff = values[5] if values[5]
45
- ef.accessory_code = values[6]
46
- ef.accessory_type = values[7]
47
- ef.high_dose_authorization = values[8]
48
- ef.crc = values[-1]
49
- return ef
33
+ ef.load(string)
50
34
  end
51
35
 
52
36
  # Creates a new (treatment) ExtendedField.
@@ -54,10 +38,25 @@ module RTP
54
38
  # @param [Record] parent a record which is used to determine the proper parent of this instance
55
39
  #
56
40
  def initialize(parent)
41
+ super('EXTENDED_FIELD_DEF', 4, 12)
57
42
  # Parent relation (may get more than one type of record here):
58
43
  @parent = get_parent(parent.to_record, Field)
59
44
  @parent.add_extended_field(self)
60
- @keyword = 'EXTENDED_FIELD_DEF'
45
+ @attributes = [
46
+ # Required:
47
+ :keyword,
48
+ :field_id,
49
+ :original_plan_uid,
50
+ # Optional:
51
+ :original_beam_number,
52
+ :original_beam_name,
53
+ :is_fff,
54
+ :accessory_code,
55
+ :accessory_type,
56
+ :high_dose_authorization,
57
+ :referenced_rt_plan_uid,
58
+ :referenced_rt_plan_relationship
59
+ ]
61
60
  end
62
61
 
63
62
  # Checks for equality.
@@ -94,25 +93,6 @@ module RTP
94
93
  state.hash
95
94
  end
96
95
 
97
- # Collects the values (attributes) of this instance.
98
- #
99
- # @note The CRC is not considered part of the actual values and is excluded.
100
- # @return [Array<String>] an array of attributes (in the same order as they appear in the RTP string)
101
- #
102
- def values
103
- return [
104
- @keyword,
105
- @field_id,
106
- @original_plan_uid,
107
- @original_beam_number,
108
- @original_beam_name,
109
- @is_fff,
110
- @accessory_code,
111
- @accessory_type,
112
- @high_dose_authorization
113
- ]
114
- end
115
-
116
96
  # Returns self.
117
97
  #
118
98
  # @return [ExtendedField] self
@@ -121,35 +101,6 @@ module RTP
121
101
  self
122
102
  end
123
103
 
124
- # Encodes the ExtendedField object + any hiearchy of child objects,
125
- # to a properly formatted RTPConnect ascii string.
126
- #
127
- # @return [String] an RTP string with a single or multiple lines/records
128
- #
129
- def to_s
130
- str = encode
131
- if children
132
- children.each do |child|
133
- str += child.to_s
134
- end
135
- end
136
- return str
137
- end
138
-
139
- alias :to_str :to_s
140
-
141
- # Sets the keyword attribute.
142
- #
143
- # @note Since only a specific string is accepted, this is more of an argument check than a traditional setter method
144
- # @param [#to_s] value the new attribute value
145
- # @raise [ArgumentError] if given an unexpected keyword
146
- #
147
- def keyword=(value)
148
- value = value.to_s.upcase
149
- raise ArgumentError, "Invalid keyword. Expected 'EXTENDED_FIELD_DEF', got #{value}." unless value == "EXTENDED_FIELD_DEF"
150
- @keyword = value
151
- end
152
-
153
104
  # Sets the field_id attribute.
154
105
  #
155
106
  # @param [nil, #to_s] value the new attribute value
@@ -214,6 +165,22 @@ module RTP
214
165
  @high_dose_authorization = value && value.to_s
215
166
  end
216
167
 
168
+ # Sets the referenced_rt_plan_uid attribute.
169
+ #
170
+ # @param [nil, #to_s] value the new attribute value
171
+ #
172
+ def referenced_rt_plan_uid=(value)
173
+ @referenced_rt_plan_uid = value && value.to_s
174
+ end
175
+
176
+ # Sets the referenced_rt_plan_relationship attribute.
177
+ #
178
+ # @param [nil, #to_s] value the new attribute value
179
+ #
180
+ def referenced_rt_plan_relationship=(value)
181
+ @referenced_rt_plan_relationship = value && value.to_s
182
+ end
183
+
217
184
 
218
185
  private
219
186
 
@@ -0,0 +1,127 @@
1
+ module RTP
2
+
3
+ # The Extended plan class.
4
+ #
5
+ # @note Relations:
6
+ # * Parent: Plan
7
+ # * Children: none
8
+ #
9
+ class ExtendedPlan < Record
10
+
11
+ # The Record which this instance belongs to.
12
+ attr_accessor :parent
13
+ attr_reader :encoding
14
+ attr_reader :fullname
15
+ attr_reader :patient_comments
16
+
17
+ # Creates a new ExtendedPlan by parsing a RTPConnect string line.
18
+ #
19
+ # @param [#to_s] string the extended plan definition record string line
20
+ # @param [Record] parent a record which is used to determine the proper parent of this instance
21
+ # @return [ExtendedPlan] the created ExtendedPlan instance
22
+ # @raise [ArgumentError] if given a string containing an invalid number of elements
23
+ #
24
+ def self.load(string, parent)
25
+ ep = self.new(parent)
26
+ ep.load(string)
27
+ end
28
+
29
+ # Creates a new ExtendedPlan.
30
+ #
31
+ # @param [Record] parent a record which is used to determine the proper parent of this instance
32
+ #
33
+ def initialize(parent)
34
+ super('EXTENDED_PLAN_DEF', 4, 5)
35
+ # Parent relation (may get more than one type of record here):
36
+ @parent = get_parent(parent.to_record, Plan)
37
+ @parent.add_extended_plan(self)
38
+ @attributes = [
39
+ # Required:
40
+ :keyword,
41
+ :encoding,
42
+ :fullname,
43
+ # Optional:
44
+ :patient_comments
45
+ ]
46
+ end
47
+
48
+ # Checks for equality.
49
+ #
50
+ # Other and self are considered equivalent if they are
51
+ # of compatible types and their attributes are equivalent.
52
+ #
53
+ # @param other an object to be compared with self.
54
+ # @return [Boolean] true if self and other are considered equivalent
55
+ #
56
+ def ==(other)
57
+ if other.respond_to?(:to_extended_plan)
58
+ other.send(:state) == state
59
+ end
60
+ end
61
+
62
+ alias_method :eql?, :==
63
+
64
+ # Gives an empty array, as these instances are child-less by definition.
65
+ #
66
+ # @return [Array] an emtpy array
67
+ #
68
+ def children
69
+ return Array.new
70
+ end
71
+
72
+ # Computes a hash code for this object.
73
+ #
74
+ # @note Two objects with the same attributes will have the same hash code.
75
+ #
76
+ # @return [Fixnum] the object's hash code
77
+ #
78
+ def hash
79
+ state.hash
80
+ end
81
+
82
+ # Returns self.
83
+ #
84
+ # @return [ExtendedPlan] self
85
+ #
86
+ def to_extended_plan
87
+ self
88
+ end
89
+
90
+ # Sets the encoding attribute.
91
+ #
92
+ # @param [nil, #to_s] value the new attribute value
93
+ #
94
+ def encoding=(value)
95
+ @encoding = value && value.to_s
96
+ end
97
+
98
+ # Sets the fullname attribute.
99
+ #
100
+ # @param [nil, #to_s] value the new attribute value
101
+ #
102
+ def fullname=(value)
103
+ @fullname = value && value.to_s
104
+ end
105
+
106
+ # Sets the patient_comments attribute.
107
+ #
108
+ # @param [nil, #to_s] value the new attribute value
109
+ #
110
+ def patient_comments=(value)
111
+ @patient_comments = value && value.to_s
112
+ end
113
+
114
+
115
+ private
116
+
117
+
118
+ # Collects the attributes of this instance.
119
+ #
120
+ # @note The CRC is not considered part of the attributes of interest and is excluded
121
+ # @return [Array<String>] an array of attributes
122
+ #
123
+ alias_method :state, :values
124
+
125
+ end
126
+
127
+ end
@@ -9,7 +9,7 @@ module RTP
9
9
  class Field < Record
10
10
 
11
11
  # The Record which this instance belongs to.
12
- attr_reader :parent
12
+ attr_accessor :parent
13
13
  # The ExtendedField record (if any) that belongs to this Field.
14
14
  attr_reader :extended_field
15
15
  # An array of ControlPoint records (if any) that belongs to this Field.
@@ -61,6 +61,9 @@ module RTP
61
61
  attr_reader :portfilm_delta_open
62
62
  attr_reader :portfilm_mu_treat
63
63
  attr_reader :portfilm_coeff_treat
64
+ attr_reader :iso_pos_x
65
+ attr_reader :iso_pos_y
66
+ attr_reader :iso_pos_z
64
67
 
65
68
  # Creates a new (treatment) Field by parsing a RTPConnect string line.
66
69
  #
@@ -70,64 +73,8 @@ module RTP
70
73
  # @raise [ArgumentError] if given a string containing an invalid number of elements
71
74
  #
72
75
  def self.load(string, parent)
73
- # Get the quote-less values:
74
- values = string.to_s.values
75
- low_limit = 27
76
- high_limit = 49
77
- raise ArgumentError, "Invalid argument 'string': Expected at least #{low_limit} elements, got #{values.length}." if values.length < low_limit
78
- RTP.logger.warn "The number of elements (#{values.length}) for this Field record exceeds the known number of data items for this record (#{high_limit}). This may indicate an invalid record or that the RTP format has recently been expanded with new items." if values.length > high_limit
79
76
  f = self.new(parent)
80
- # Assign the values to attributes:
81
- f.keyword = values[0]
82
- f.rx_site_name = values[1]
83
- f.field_name = values[2]
84
- f.field_id = values[3]
85
- f.field_note = values[4]
86
- f.field_dose = values[5]
87
- f.field_monitor_units = values[6]
88
- f.wedge_monitor_units = values[7]
89
- f.treatment_machine = values[8]
90
- f.treatment_type = values[9]
91
- f.modality = values[10]
92
- f.energy = values[11]
93
- f.time = values[12]
94
- f.doserate = values[13]
95
- f.sad = values[14]
96
- f.ssd = values[15]
97
- f.gantry_angle = values[16]
98
- f.collimator_angle = values[17]
99
- f.field_x_mode = values[18]
100
- f.field_x = values[19]
101
- f.collimator_x1 = values[20]
102
- f.collimator_x2 = values[21]
103
- f.field_y_mode = values[22]
104
- f.field_y = values[23]
105
- f.collimator_y1 = values[24]
106
- f.collimator_y2 = values[25]
107
- f.couch_vertical = values[26]
108
- f.couch_lateral = values[27]
109
- f.couch_longitudinal = values[28]
110
- f.couch_angle = values[29]
111
- f.couch_pedestal = values[30]
112
- f.tolerance_table = values[31]
113
- f.arc_direction = values[32]
114
- f.arc_start_angle = values[33]
115
- f.arc_stop_angle = values[34]
116
- f.arc_mu_degree = values[35]
117
- f.wedge = values[36]
118
- f.dynamic_wedge = values[37]
119
- f.block = values[38]
120
- f.compensator = values[39]
121
- f.e_applicator = values[40]
122
- f.e_field_def_aperture = values[41]
123
- f.bolus = values[42]
124
- f.portfilm_mu_open = values[43]
125
- f.portfilm_coeff_open = values[44]
126
- f.portfilm_delta_open = values[45]
127
- f.portfilm_mu_treat = values[46]
128
- f.portfilm_coeff_treat = values[47]
129
- f.crc = values[-1]
130
- return f
77
+ f.load(string)
131
78
  end
132
79
 
133
80
  # Creates a new (treatment) Field.
@@ -135,13 +82,68 @@ module RTP
135
82
  # @param [Record] parent a record which is used to determine the proper parent of this instance
136
83
  #
137
84
  def initialize(parent)
85
+ super('FIELD_DEF', 27, 52)
138
86
  # Child records:
139
87
  @control_points = Array.new
140
88
  @extended_field = nil
141
89
  # Parent relation (may get more than one type of record here):
142
90
  @parent = get_parent(parent.to_record, Prescription)
143
91
  @parent.add_field(self)
144
- @keyword = 'FIELD_DEF'
92
+ @attributes = [
93
+ # Required:
94
+ :keyword,
95
+ :rx_site_name,
96
+ :field_name,
97
+ :field_id,
98
+ :field_note,
99
+ :field_dose,
100
+ :field_monitor_units,
101
+ :wedge_monitor_units,
102
+ :treatment_machine,
103
+ :treatment_type,
104
+ :modality,
105
+ :energy,
106
+ :time,
107
+ :doserate,
108
+ :sad,
109
+ :ssd,
110
+ :gantry_angle,
111
+ :collimator_angle,
112
+ :field_x_mode,
113
+ :field_x,
114
+ :collimator_x1,
115
+ :collimator_x2,
116
+ :field_y_mode,
117
+ :field_y,
118
+ :collimator_y1,
119
+ :collimator_y2,
120
+ # Optional:
121
+ :couch_vertical,
122
+ :couch_lateral,
123
+ :couch_longitudinal,
124
+ :couch_angle,
125
+ :couch_pedestal,
126
+ :tolerance_table,
127
+ :arc_direction,
128
+ :arc_start_angle,
129
+ :arc_stop_angle,
130
+ :arc_mu_degree,
131
+ :wedge,
132
+ :dynamic_wedge,
133
+ :block,
134
+ :compensator,
135
+ :e_applicator,
136
+ :e_field_def_aperture,
137
+ :bolus,
138
+ :portfilm_mu_open,
139
+ :portfilm_coeff_open,
140
+ :portfilm_delta_open,
141
+ :portfilm_mu_treat,
142
+ :portfilm_coeff_treat,
143
+ :iso_pos_x,
144
+ :iso_pos_y,
145
+ :iso_pos_z
146
+ ]
145
147
  end
146
148
 
147
149
  # Checks for equality.
@@ -166,6 +168,7 @@ module RTP
166
168
  #
167
169
  def add_control_point(child)
168
170
  @control_points << child.to_control_point
171
+ child.parent = self
169
172
  end
170
173
 
171
174
  # Adds an extended treatment field record to this instance.
@@ -174,6 +177,7 @@ module RTP
174
177
  #
175
178
  def add_extended_field(child)
176
179
  @extended_field = child.to_extended_field
180
+ child.parent = self
177
181
  end
178
182
 
179
183
  # Collects the child records of this instance in a properly sorted array.
@@ -184,109 +188,81 @@ module RTP
184
188
  return [@extended_field, @control_points].flatten.compact
185
189
  end
186
190
 
187
- # Computes a hash code for this object.
191
+ # Converts the collimator_x1 attribute to proper DICOM format.
188
192
  #
189
- # @note Two objects with the same attributes will have the same hash code.
193
+ # @return [Float] the DICOM-formatted collimator_x1 attribute
190
194
  #
191
- # @return [Fixnum] the object's hash code
195
+ def dcm_collimator_x1
196
+ dcm_collimator1(:x)
197
+ end
198
+
199
+ # Converts the collimator_x2 attribute to proper DICOM format.
192
200
  #
193
- def hash
194
- state.hash
201
+ # @return [Float] the DICOM-formatted collimator_x2 attribute
202
+ #
203
+ def dcm_collimator_x2
204
+ value = @collimator_x2.to_f * 10
195
205
  end
196
206
 
197
- # Collects the values (attributes) of this instance.
198
- #
199
- # @note The CRC is not considered part of the actual values and is excluded.
200
- # @return [Array<String>] an array of attributes (in the same order as they appear in the RTP string)
201
- #
202
- def values
203
- return [
204
- @keyword,
205
- @rx_site_name,
206
- @field_name,
207
- @field_id,
208
- @field_note,
209
- @field_dose,
210
- @field_monitor_units,
211
- @wedge_monitor_units,
212
- @treatment_machine,
213
- @treatment_type,
214
- @modality,
215
- @energy,
216
- @time,
217
- @doserate,
218
- @sad,
219
- @ssd,
220
- @gantry_angle,
221
- @collimator_angle,
222
- @field_x_mode,
223
- @field_x,
224
- @collimator_x1,
225
- @collimator_x2,
226
- @field_y_mode,
227
- @field_y,
228
- @collimator_y1,
229
- @collimator_y2,
230
- @couch_vertical,
231
- @couch_lateral,
232
- @couch_longitudinal,
233
- @couch_angle,
234
- @couch_pedestal,
235
- @tolerance_table,
236
- @arc_direction,
237
- @arc_start_angle,
238
- @arc_stop_angle,
239
- @arc_mu_degree,
240
- @wedge,
241
- @dynamic_wedge,
242
- @block,
243
- @compensator,
244
- @e_applicator,
245
- @e_field_def_aperture,
246
- @bolus,
247
- @portfilm_mu_open,
248
- @portfilm_coeff_open,
249
- @portfilm_delta_open,
250
- @portfilm_mu_treat,
251
- @portfilm_coeff_treat
252
- ]
207
+ # Converts the collimator_y1 attribute to proper DICOM format.
208
+ #
209
+ # @return [Float] the DICOM-formatted collimator_y1 attribute
210
+ #
211
+ def dcm_collimator_y1
212
+ dcm_collimator1(:y)
253
213
  end
254
214
 
255
- # Returns self.
215
+ # Converts the collimator_y2 attribute to proper DICOM format.
256
216
  #
257
- # @return [Field] self
217
+ # @return [Float] the DICOM-formatted collimator_y2 attribute
258
218
  #
259
- def to_field
260
- self
219
+ def dcm_collimator_y2
220
+ value = @collimator_y2.to_f * 10
261
221
  end
262
222
 
263
- # Encodes the Field object + any hiearchy of child objects,
264
- # to a properly formatted RTPConnect ascii string.
223
+ # Removes the reference of the given instance from this instance.
265
224
  #
266
- # @return [String] an RTP string with a single or multiple lines/records
225
+ # @param [ControlPoint, ExtendedField] record a child record to be removed from this instance
267
226
  #
268
- def to_s
269
- str = encode
270
- if children
271
- children.each do |child|
272
- str += child.to_s
273
- end
227
+ def delete(record)
228
+ case record
229
+ when ControlPoint
230
+ delete_child(:control_points, record)
231
+ when ExtendedField
232
+ delete_extended_field
233
+ else
234
+ logger.warn("Unknown class (record) given to Field#delete: #{record.class}")
274
235
  end
275
- return str
276
236
  end
277
237
 
278
- alias :to_str :to_s
238
+ # Removes all control point references from this instance.
239
+ #
240
+ def delete_control_points
241
+ delete_children(:control_points)
242
+ end
243
+
244
+ # Removes the extended field reference from this instance.
245
+ #
246
+ def delete_extended_field
247
+ delete_child(:extended_field)
248
+ end
279
249
 
280
- # Sets the keyword attribute.
250
+ # Computes a hash code for this object.
251
+ #
252
+ # @note Two objects with the same attributes will have the same hash code.
281
253
  #
282
- # @note Since only a specific string is accepted, this is more of an argument check than a traditional setter method
283
- # @param [#to_s] value the new attribute value
284
- # @raise [ArgumentError] if given an unexpected keyword
254
+ # @return [Fixnum] the object's hash code
285
255
  #
286
- def keyword=(value)
287
- value = value.to_s.upcase
288
- raise ArgumentError, "Invalid keyword. Expected 'FIELD_DEF', got #{value}." unless value == "FIELD_DEF"
289
- @keyword = value
256
+ def hash
257
+ state.hash
258
+ end
259
+
260
+ # Returns self.
261
+ #
262
+ # @return [Field] self
263
+ #
264
+ def to_field
265
+ self
290
266
  end
291
267
 
292
268
  # Sets the rx_site_name attribute.
@@ -665,6 +641,30 @@ module RTP
665
641
  @portfilm_coeff_treat = value && value.to_s
666
642
  end
667
643
 
644
+ # Sets the iso_pos_x attribute.
645
+ #
646
+ # @param [nil, #to_s] value the new attribute value
647
+ #
648
+ def iso_pos_x=(value)
649
+ @iso_pos_x = value && value.to_s.strip
650
+ end
651
+
652
+ # Sets the iso_pos_y attribute.
653
+ #
654
+ # @param [nil, #to_s] value the new attribute value
655
+ #
656
+ def iso_pos_y=(value)
657
+ @iso_pos_y = value && value.to_s.strip
658
+ end
659
+
660
+ # Sets the iso_pos_z attribute.
661
+ #
662
+ # @param [nil, #to_s] value the new attribute value
663
+ #
664
+ def iso_pos_z=(value)
665
+ @iso_pos_z = value && value.to_s.strip
666
+ end
667
+
668
668
 
669
669
  private
670
670
 
@@ -676,6 +676,21 @@ module RTP
676
676
  #
677
677
  alias_method :state, :values
678
678
 
679
+ # Converts the collimator attribute to proper DICOM format.
680
+ #
681
+ # @param [Symbol] axis a representation for the axis of interest (x or y)
682
+ # @return [Float] the DICOM-formatted collimator attribute
683
+ #
684
+ def dcm_collimator1(axis)
685
+ value = self.send("collimator_#{axis}1").to_f * 10
686
+ mode = self.send("field_#{axis}_mode")
687
+ if mode && mode.upcase == 'SYM' && value > 0
688
+ -value
689
+ else
690
+ value
691
+ end
692
+ end
693
+
679
694
  end
680
695
 
681
696
  end