rtp-connect 1.8 → 1.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -13
- data/CHANGELOG.md +124 -114
- data/COPYING +674 -674
- data/Gemfile +2 -2
- data/Gemfile.lock +23 -19
- data/README.md +149 -149
- data/lib/rtp-connect/constants.rb +58 -58
- data/lib/rtp-connect/control_point.rb +38 -95
- data/lib/rtp-connect/dose_tracking.rb +19 -33
- data/lib/rtp-connect/extended_field.rb +1 -20
- data/lib/rtp-connect/extended_plan.rb +115 -134
- data/lib/rtp-connect/field.rb +30 -20
- data/lib/rtp-connect/methods.rb +85 -77
- data/lib/rtp-connect/plan.rb +645 -624
- data/lib/rtp-connect/plan_to_dcm.rb +668 -656
- data/lib/rtp-connect/prescription.rb +39 -20
- data/lib/rtp-connect/record.rb +201 -143
- data/lib/rtp-connect/simulation_field.rb +606 -625
- data/lib/rtp-connect/site_setup.rb +1 -20
- data/lib/rtp-connect/version.rb +5 -5
- data/rtp-connect.gemspec +27 -27
- metadata +32 -32
@@ -8,8 +8,11 @@ module RTP
|
|
8
8
|
#
|
9
9
|
class ControlPoint < Record
|
10
10
|
|
11
|
+
# The number of attributes not having their own variable for this record (200 - 2).
|
12
|
+
NR_SURPLUS_ATTRIBUTES = 198
|
13
|
+
|
11
14
|
# The Record which this instance belongs to.
|
12
|
-
|
15
|
+
attr_accessor :parent
|
13
16
|
# The MLC shape record (if any) that belongs to this ControlPoint.
|
14
17
|
attr_reader :mlc_shape
|
15
18
|
attr_reader :field_id
|
@@ -144,15 +147,7 @@ module RTP
|
|
144
147
|
# @return [Float] the DICOM-formatted collimator_x1 attribute
|
145
148
|
#
|
146
149
|
def dcm_collimator_x1(scale=nil)
|
147
|
-
|
148
|
-
axis = :x
|
149
|
-
if scale == :elekta
|
150
|
-
axis = :y
|
151
|
-
coeff = -1
|
152
|
-
elsif scale == :varian
|
153
|
-
coeff = -1
|
154
|
-
end
|
155
|
-
dcm_collimator1(axis, coeff)
|
150
|
+
dcm_collimator_1(scale, default_axis=:x)
|
156
151
|
end
|
157
152
|
|
158
153
|
# Converts the collimator_x2 attribute to proper DICOM format.
|
@@ -162,7 +157,7 @@ module RTP
|
|
162
157
|
#
|
163
158
|
def dcm_collimator_x2(scale=nil)
|
164
159
|
axis = (scale == :elekta ? :y : :x)
|
165
|
-
|
160
|
+
dcm_collimator(axis, coeff=1, side=2)
|
166
161
|
end
|
167
162
|
|
168
163
|
# Converts the collimator_y1 attribute to proper DICOM format.
|
@@ -171,15 +166,7 @@ module RTP
|
|
171
166
|
# @return [Float] the DICOM-formatted collimator_y1 attribute
|
172
167
|
#
|
173
168
|
def dcm_collimator_y1(scale=nil)
|
174
|
-
|
175
|
-
axis = :y
|
176
|
-
if scale == :elekta
|
177
|
-
axis = :x
|
178
|
-
coeff = -1
|
179
|
-
elsif scale == :varian
|
180
|
-
coeff = -1
|
181
|
-
end
|
182
|
-
dcm_collimator1(axis, coeff)
|
169
|
+
dcm_collimator_1(scale, default_axis=:y)
|
183
170
|
end
|
184
171
|
|
185
172
|
# Converts the collimator_y2 attribute to proper DICOM format.
|
@@ -189,7 +176,7 @@ module RTP
|
|
189
176
|
#
|
190
177
|
def dcm_collimator_y2(scale=nil)
|
191
178
|
axis = (scale == :elekta ? :x : :y)
|
192
|
-
|
179
|
+
dcm_collimator(axis, coeff=1, side=2)
|
193
180
|
end
|
194
181
|
|
195
182
|
# Converts the mlc_lp_a & mlc_lp_b attributes to a proper DICOM formatted string.
|
@@ -276,25 +263,6 @@ module RTP
|
|
276
263
|
self
|
277
264
|
end
|
278
265
|
|
279
|
-
# Encodes the ControlPoint object + any hiearchy of child objects,
|
280
|
-
# to a properly formatted RTPConnect ascii string.
|
281
|
-
#
|
282
|
-
# @param [Hash] options an optional hash parameter
|
283
|
-
# @option options [Float] :version the Mosaiq compatibility version number (e.g. 2.4) used for the output
|
284
|
-
# @return [String] an RTP string with a single or multiple lines/records
|
285
|
-
#
|
286
|
-
def to_s(options={})
|
287
|
-
str = encode(options)
|
288
|
-
if children
|
289
|
-
children.each do |child|
|
290
|
-
str += child.to_s(options)
|
291
|
-
end
|
292
|
-
end
|
293
|
-
return str
|
294
|
-
end
|
295
|
-
|
296
|
-
alias :to_str :to_s
|
297
|
-
|
298
266
|
# Sets the mlc_lp_a attribute.
|
299
267
|
#
|
300
268
|
# @note As opposed to the ordinary (string) attributes, this attribute
|
@@ -578,74 +546,49 @@ module RTP
|
|
578
546
|
#
|
579
547
|
# @param [Symbol] axis a representation for the axis of interest (x or y)
|
580
548
|
# @param [Integer] coeff a coeffecient (of -1 or 1) which the attribute is multiplied with
|
549
|
+
# @param [Integer] nr collimator side/index (1 or 2)
|
581
550
|
# @return [Float] the DICOM-formatted collimator attribute
|
582
551
|
#
|
583
|
-
def
|
552
|
+
def dcm_collimator(axis, coeff, nr)
|
584
553
|
mode = self.send("field_#{axis}_mode")
|
585
554
|
if mode && !mode.empty?
|
586
555
|
target = self
|
587
556
|
else
|
588
557
|
target = @parent
|
589
558
|
end
|
590
|
-
target.send("collimator_#{axis}
|
559
|
+
target.send("collimator_#{axis}#{nr}").to_f * 10 * coeff
|
591
560
|
end
|
592
561
|
|
593
|
-
# Converts the
|
562
|
+
# Converts the collimator1 attribute to proper DICOM format.
|
594
563
|
#
|
595
|
-
# @param [Symbol]
|
596
|
-
# @
|
597
|
-
# @return [Float] the DICOM-formatted collimator attribute
|
564
|
+
# @param [Symbol] scale if set, relevant device parameters are converted from a native readout format to IEC1217 (supported values are :elekta & :varian)
|
565
|
+
# @return [Float] the DICOM-formatted collimator_x1 attribute
|
598
566
|
#
|
599
|
-
def
|
600
|
-
|
601
|
-
if
|
602
|
-
|
603
|
-
|
604
|
-
|
567
|
+
def dcm_collimator_1(scale=nil, axis)
|
568
|
+
coeff = 1
|
569
|
+
if scale == :elekta
|
570
|
+
axis = (axis == :x ? :y : :x)
|
571
|
+
coeff = -1
|
572
|
+
elsif scale == :varian
|
573
|
+
coeff = -1
|
605
574
|
end
|
606
|
-
|
607
|
-
end
|
608
|
-
|
609
|
-
#
|
610
|
-
#
|
611
|
-
#
|
612
|
-
#
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
@energy = values[9]
|
624
|
-
@doserate = values[10]
|
625
|
-
@ssd = values[11]
|
626
|
-
@scale_convention = values[12]
|
627
|
-
@gantry_angle = values[13]
|
628
|
-
@gantry_dir = values[14]
|
629
|
-
@collimator_angle = values[15]
|
630
|
-
@collimator_dir = values[16]
|
631
|
-
@field_x_mode = values[17]
|
632
|
-
@field_x = values[18]
|
633
|
-
@collimator_x1 = values[19]
|
634
|
-
@collimator_x2 = values[20]
|
635
|
-
@field_y_mode = values[21]
|
636
|
-
@field_y = values[22]
|
637
|
-
@collimator_y1 = values[23]
|
638
|
-
@collimator_y2 = values[24]
|
639
|
-
@couch_vertical = values[25]
|
640
|
-
@couch_lateral = values[26]
|
641
|
-
@couch_longitudinal = values[27]
|
642
|
-
@couch_angle = values[28]
|
643
|
-
@couch_dir = values[29]
|
644
|
-
@couch_pedestal = values[30]
|
645
|
-
@couch_ped_dir = values[31]
|
646
|
-
@mlc_lp_a = [*values[32..131]]
|
647
|
-
@mlc_lp_b = [*values[132..231]]
|
648
|
-
@crc = values[-1]
|
575
|
+
dcm_collimator(axis, coeff, side=1)
|
576
|
+
end
|
577
|
+
|
578
|
+
# Gives an array of indices indicating where the attributes of this record gets its
|
579
|
+
# values from in the comma separated string which the instance is created from.
|
580
|
+
#
|
581
|
+
# @param [Integer] length the number of elements to create in the indices array
|
582
|
+
#
|
583
|
+
def import_indices(length)
|
584
|
+
# Note that this method is defined in the parent Record class, where it is
|
585
|
+
# used for most record types. However, because this record has two attributes
|
586
|
+
# which contain an array of values, we use a custom import_indices method.
|
587
|
+
ind = Array.new(length - NR_SURPLUS_ATTRIBUTES) { |i| [i] }
|
588
|
+
# Override indices for mlc_pl_a and mlc_lp_b:
|
589
|
+
ind[32] = (32..131).to_a
|
590
|
+
ind[33] = (132..231).to_a
|
591
|
+
ind
|
649
592
|
end
|
650
593
|
|
651
594
|
end
|
@@ -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
|
-
|
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
|
@@ -120,25 +123,6 @@ module RTP
|
|
120
123
|
self
|
121
124
|
end
|
122
125
|
|
123
|
-
# Encodes the DoseTracking object + any hiearchy of child objects,
|
124
|
-
# to a properly formatted RTPConnect ascii string.
|
125
|
-
#
|
126
|
-
# @param [Hash] options an optional hash parameter
|
127
|
-
# @option options [Float] :version the Mosaiq compatibility version number (e.g. 2.4) used for the output
|
128
|
-
# @return [String] an RTP string with a single or multiple lines/records
|
129
|
-
#
|
130
|
-
def to_s(options={})
|
131
|
-
str = encode(options)
|
132
|
-
if children
|
133
|
-
children.each do |child|
|
134
|
-
str += child.to_s(options)
|
135
|
-
end
|
136
|
-
end
|
137
|
-
return str
|
138
|
-
end
|
139
|
-
|
140
|
-
alias :to_str :to_s
|
141
|
-
|
142
126
|
# Sets the field_ids attribute.
|
143
127
|
#
|
144
128
|
# @note As opposed to the ordinary (string) attributes, this attribute
|
@@ -202,19 +186,21 @@ module RTP
|
|
202
186
|
#
|
203
187
|
alias_method :state, :values
|
204
188
|
|
205
|
-
#
|
206
|
-
#
|
207
|
-
#
|
208
|
-
#
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
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
|
218
204
|
end
|
219
205
|
|
220
206
|
end
|
@@ -9,7 +9,7 @@ module RTP
|
|
9
9
|
class ExtendedField < Record
|
10
10
|
|
11
11
|
# The Record which this instance belongs to.
|
12
|
-
|
12
|
+
attr_accessor :parent
|
13
13
|
attr_reader :field_id
|
14
14
|
attr_reader :original_plan_uid
|
15
15
|
attr_reader :original_beam_number
|
@@ -97,25 +97,6 @@ module RTP
|
|
97
97
|
self
|
98
98
|
end
|
99
99
|
|
100
|
-
# Encodes the ExtendedField object + any hiearchy of child objects,
|
101
|
-
# to a properly formatted RTPConnect ascii string.
|
102
|
-
#
|
103
|
-
# @param [Hash] options an optional hash parameter
|
104
|
-
# @option options [Float] :version the Mosaiq compatibility version number (e.g. 2.4) used for the output
|
105
|
-
# @return [String] an RTP string with a single or multiple lines/records
|
106
|
-
#
|
107
|
-
def to_s(options={})
|
108
|
-
str = encode(options)
|
109
|
-
if children
|
110
|
-
children.each do |child|
|
111
|
-
str += child.to_s(options)
|
112
|
-
end
|
113
|
-
end
|
114
|
-
return str
|
115
|
-
end
|
116
|
-
|
117
|
-
alias :to_str :to_s
|
118
|
-
|
119
100
|
# Sets the field_id attribute.
|
120
101
|
#
|
121
102
|
# @param [nil, #to_s] value the new attribute value
|
@@ -1,135 +1,116 @@
|
|
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
|
-
|
13
|
-
attr_reader :encoding
|
14
|
-
attr_reader :fullname
|
15
|
-
|
16
|
-
# Creates a new ExtendedPlan by parsing a RTPConnect string line.
|
17
|
-
#
|
18
|
-
# @param [#to_s] string the extended plan definition record string line
|
19
|
-
# @param [Record] parent a record which is used to determine the proper parent of this instance
|
20
|
-
# @return [ExtendedPlan] the created ExtendedPlan instance
|
21
|
-
# @raise [ArgumentError] if given a string containing an invalid number of elements
|
22
|
-
#
|
23
|
-
def self.load(string, parent)
|
24
|
-
ep = self.new(parent)
|
25
|
-
ep.load(string)
|
26
|
-
end
|
27
|
-
|
28
|
-
# Creates a new ExtendedPlan.
|
29
|
-
#
|
30
|
-
# @param [Record] parent a record which is used to determine the proper parent of this instance
|
31
|
-
#
|
32
|
-
def initialize(parent)
|
33
|
-
super('EXTENDED_PLAN_DEF', 4, 4)
|
34
|
-
# Parent relation (may get more than one type of record here):
|
35
|
-
@parent = get_parent(parent.to_record, Plan)
|
36
|
-
@parent.add_extended_plan(self)
|
37
|
-
@attributes = [
|
38
|
-
# Required:
|
39
|
-
:keyword,
|
40
|
-
:encoding,
|
41
|
-
:fullname
|
42
|
-
]
|
43
|
-
end
|
44
|
-
|
45
|
-
# Checks for equality.
|
46
|
-
#
|
47
|
-
# Other and self are considered equivalent if they are
|
48
|
-
# of compatible types and their attributes are equivalent.
|
49
|
-
#
|
50
|
-
# @param other an object to be compared with self.
|
51
|
-
# @return [Boolean] true if self and other are considered equivalent
|
52
|
-
#
|
53
|
-
def ==(other)
|
54
|
-
if other.respond_to?(:to_extended_plan)
|
55
|
-
other.send(:state) == state
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
alias_method :eql?, :==
|
60
|
-
|
61
|
-
# Gives an empty array, as these instances are child-less by definition.
|
62
|
-
#
|
63
|
-
# @return [Array] an emtpy array
|
64
|
-
#
|
65
|
-
def children
|
66
|
-
return Array.new
|
67
|
-
end
|
68
|
-
|
69
|
-
# Computes a hash code for this object.
|
70
|
-
#
|
71
|
-
# @note Two objects with the same attributes will have the same hash code.
|
72
|
-
#
|
73
|
-
# @return [Fixnum] the object's hash code
|
74
|
-
#
|
75
|
-
def hash
|
76
|
-
state.hash
|
77
|
-
end
|
78
|
-
|
79
|
-
# Returns self.
|
80
|
-
#
|
81
|
-
# @return [ExtendedPlan] self
|
82
|
-
#
|
83
|
-
def to_extended_plan
|
84
|
-
self
|
85
|
-
end
|
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
|
-
# @param [nil, #to_s] value the new attribute value
|
117
|
-
#
|
118
|
-
def fullname=(value)
|
119
|
-
@fullname = value && value.to_s
|
120
|
-
end
|
121
|
-
|
122
|
-
|
123
|
-
private
|
124
|
-
|
125
|
-
|
126
|
-
# Collects the attributes of this instance.
|
127
|
-
#
|
128
|
-
# @note The CRC is not considered part of the attributes of interest and is excluded
|
129
|
-
# @return [Array<String>] an array of attributes
|
130
|
-
#
|
131
|
-
alias_method :state, :values
|
132
|
-
|
133
|
-
end
|
134
|
-
|
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
|
+
|
16
|
+
# Creates a new ExtendedPlan by parsing a RTPConnect string line.
|
17
|
+
#
|
18
|
+
# @param [#to_s] string the extended plan definition record string line
|
19
|
+
# @param [Record] parent a record which is used to determine the proper parent of this instance
|
20
|
+
# @return [ExtendedPlan] the created ExtendedPlan instance
|
21
|
+
# @raise [ArgumentError] if given a string containing an invalid number of elements
|
22
|
+
#
|
23
|
+
def self.load(string, parent)
|
24
|
+
ep = self.new(parent)
|
25
|
+
ep.load(string)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Creates a new ExtendedPlan.
|
29
|
+
#
|
30
|
+
# @param [Record] parent a record which is used to determine the proper parent of this instance
|
31
|
+
#
|
32
|
+
def initialize(parent)
|
33
|
+
super('EXTENDED_PLAN_DEF', 4, 4)
|
34
|
+
# Parent relation (may get more than one type of record here):
|
35
|
+
@parent = get_parent(parent.to_record, Plan)
|
36
|
+
@parent.add_extended_plan(self)
|
37
|
+
@attributes = [
|
38
|
+
# Required:
|
39
|
+
:keyword,
|
40
|
+
:encoding,
|
41
|
+
:fullname
|
42
|
+
]
|
43
|
+
end
|
44
|
+
|
45
|
+
# Checks for equality.
|
46
|
+
#
|
47
|
+
# Other and self are considered equivalent if they are
|
48
|
+
# of compatible types and their attributes are equivalent.
|
49
|
+
#
|
50
|
+
# @param other an object to be compared with self.
|
51
|
+
# @return [Boolean] true if self and other are considered equivalent
|
52
|
+
#
|
53
|
+
def ==(other)
|
54
|
+
if other.respond_to?(:to_extended_plan)
|
55
|
+
other.send(:state) == state
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
alias_method :eql?, :==
|
60
|
+
|
61
|
+
# Gives an empty array, as these instances are child-less by definition.
|
62
|
+
#
|
63
|
+
# @return [Array] an emtpy array
|
64
|
+
#
|
65
|
+
def children
|
66
|
+
return Array.new
|
67
|
+
end
|
68
|
+
|
69
|
+
# Computes a hash code for this object.
|
70
|
+
#
|
71
|
+
# @note Two objects with the same attributes will have the same hash code.
|
72
|
+
#
|
73
|
+
# @return [Fixnum] the object's hash code
|
74
|
+
#
|
75
|
+
def hash
|
76
|
+
state.hash
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns self.
|
80
|
+
#
|
81
|
+
# @return [ExtendedPlan] self
|
82
|
+
#
|
83
|
+
def to_extended_plan
|
84
|
+
self
|
85
|
+
end
|
86
|
+
|
87
|
+
# Sets the encoding attribute.
|
88
|
+
#
|
89
|
+
# @param [nil, #to_s] value the new attribute value
|
90
|
+
#
|
91
|
+
def encoding=(value)
|
92
|
+
@encoding = value && value.to_s
|
93
|
+
end
|
94
|
+
|
95
|
+
# Sets the fullname attribute.
|
96
|
+
#
|
97
|
+
# @param [nil, #to_s] value the new attribute value
|
98
|
+
#
|
99
|
+
def fullname=(value)
|
100
|
+
@fullname = value && value.to_s
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
|
107
|
+
# Collects the attributes of this instance.
|
108
|
+
#
|
109
|
+
# @note The CRC is not considered part of the attributes of interest and is excluded
|
110
|
+
# @return [Array<String>] an array of attributes
|
111
|
+
#
|
112
|
+
alias_method :state, :values
|
113
|
+
|
114
|
+
end
|
115
|
+
|
135
116
|
end
|