ruby_astm 1.4.1 → 1.4.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -24,8 +24,9 @@ class Poller
24
24
  RUNNING = "running"
25
25
  COMPLETED = "completed"
26
26
 
27
- def initialize(mpg=nil)
27
+ def initialize(mpg=nil,real_time_db=nil)
28
28
  $redis = Redis.new
29
+ $real_time_db = real_time_db
29
30
  ## this mapping is from MACHINE CODE AS THE KEY
30
31
  $mappings = JSON.parse(IO.read(mpg || AstmServer.default_mappings))
31
32
  ## INVERTING THE MAPPINGS, GIVES US THE LIS CODE AS THE KEY.
@@ -177,12 +178,18 @@ class Poller
177
178
  puts package_components.to_s
178
179
 
179
180
  package_components.each do |component|
180
- puts "doing component: #{component}"
181
+ #puts "doing component: #{component}"
181
182
  ## these are the machine codes.
182
183
  ## so to get the tube, you have to get it from the inverted mappings.
183
184
  ## cant get directly like this.
185
+ #puts "inverted mappings"
186
+ #puts $inverted_mappings
184
187
  component_machine_code = $inverted_mappings[component]
185
- puts "component machine code: #{component_machine_code}"
188
+ #puts "component machine code: #{component_machine_code}"
189
+
190
+ ## for eg plasma tube can do all the tests
191
+ ## so can serum
192
+ ## but we use the plasma tube only for some.
186
193
 
187
194
  tube = $mappings[component_machine_code]["TUBE"]
188
195
  tube_key = nil
@@ -215,6 +222,8 @@ class Poller
215
222
 
216
223
  ## @param[Integer] epoch : the epoch at which these tests were requested.
217
224
  ## @param[Hash] tests : {"EDTA:barcode" => [MCV,MCH,MCHC...]}
225
+ ## the test codes here are the lis_codes
226
+ ## so we need the inverted mappings for this
218
227
  def merge_with_requisitions_hash(epoch,tests)
219
228
  ## so we basically now add this to the epoch ?
220
229
  ## or a sorted set ?
@@ -0,0 +1,76 @@
1
+ require 'rest-firebase'
2
+ require "resolv-replace"
3
+ class RealTimeDb
4
+
5
+ SITE_URL = ENV["FIREBASE_SITE"]
6
+ SECRET = ENV["FIREBASE_SECRET"]
7
+ attr_accessor :connection
8
+ attr_accessor :work_allotment_hash
9
+ WORK_TYPES = {
10
+ "IMMUNO" => "",
11
+ "BIOCHEM" => "",
12
+ "BIOCHEM-EXL" => "",
13
+ "BIOCHEM-ELECTROLYTE" => "",
14
+ "HEMAT" => "",
15
+ "URINE" => "",
16
+ "OUTSOURCE" => ""
17
+ }
18
+
19
+ ## first i email myself the site and secret
20
+ ## then we proceed.
21
+
22
+ ## @param[Hash] work_allotment_hash :
23
+ ## key => one of the work types
24
+ ## value => name of a worker
25
+ def initialize(work_allotment_hash)
26
+ self.connection = RestFirebase.new :site => SITE_URL,
27
+ :secret => SECRET
28
+ puts "initialized"
29
+ self.work_allotment_hash = work_allotment_hash || WORK_TYPES
30
+ end
31
+
32
+ def open_event_stream
33
+ es = self.connection.event_source('users/tom')
34
+ es.onopen { |sock| p sock } # Called when connected
35
+ es.onmessage{ |event, data, sock| p event, data } # Called for each message
36
+ es.onerror { |error, sock| p error } # Called whenever there's an error
37
+ # Extra: If we return true in onreconnect callback, it would automatically
38
+ # reconnect the node for us if disconnected.
39
+ @reconnect = true
40
+
41
+ es.onreconnect{ |error, sock| p error; @reconnect }
42
+
43
+ # Start making the request
44
+ es.start
45
+
46
+ self.connection.wait
47
+ end
48
+
49
+
50
+
51
+
52
+ ## we pass the real_time_data instance into the
53
+
54
+ def assign_test(barcode,tests,mappings)
55
+ ## so do we get the name of the worker.
56
+ inverted_mappings = {}
57
+ mappings.keys.each do |machine_code|
58
+ lis_code = mappings[machine_code][LIS_CODE]
59
+ inverted_mappings[lis_code] = mappings[machine_code]
60
+ end
61
+ worker_hash = {}
62
+ tests.each do |lis_code|
63
+ worker_name = "NO_ONE"
64
+ unless inverted_mappings[lis_code].blank?
65
+ test_type = inverted_mappings[lis_code]["TYPE"]
66
+ worker_name = self.work_allotment_hash[test_type]
67
+ end
68
+ worker_hash[worker_name] ||= []
69
+ worker_hash[worker_name] << lis_code
70
+ end
71
+ worker_hash.keys.each do |worker_name|
72
+ #self.connection.post("lab/work/#{worker_name}", :tests => worker_hash[worker_name], :barcode => barcode, :timestamp => Time.now.to_i)
73
+ end
74
+ end
75
+
76
+ end
@@ -1,17 +1,21 @@
1
- require "ruby_astm/usb_module"
2
- require "ruby_astm/query"
3
- require "ruby_astm/frame"
4
- require "ruby_astm/header"
5
- require "ruby_astm/lab_interface"
6
- require "ruby_astm/line"
7
- require "ruby_astm/order"
8
- require "ruby_astm/patient"
9
- require "ruby_astm/result"
10
- require "ruby_astm/astm_server"
11
- require "publisher/adapter"
12
- require "publisher/google_lab_interface"
13
- require "publisher/poller"
14
- require "ruby_astm/HL7/hl7_header"
15
- require "ruby_astm/HL7/hl7_patient"
16
- require "ruby_astm/HL7/hl7_order"
17
- require "ruby_astm/HL7/hl7_observation"
1
+ require_relative "ruby_astm/usb_module"
2
+ require_relative "ruby_astm/query"
3
+ require_relative "ruby_astm/frame"
4
+ require_relative "ruby_astm/header"
5
+ require_relative "ruby_astm/lab_interface"
6
+ require_relative "ruby_astm/line"
7
+ require_relative "ruby_astm/order"
8
+ require_relative "ruby_astm/patient"
9
+ require_relative "ruby_astm/result"
10
+ require_relative "ruby_astm/astm_server"
11
+ require_relative "publisher/adapter"
12
+ require_relative "publisher/google_lab_interface"
13
+ require_relative "publisher/poller"
14
+ require_relative "publisher/real_time_db"
15
+ require_relative "publisher/pf_lab_interface"
16
+ require_relative "ruby_astm/HL7/hl7_header"
17
+ require_relative "ruby_astm/HL7/hl7_patient"
18
+ require_relative "ruby_astm/HL7/hl7_order"
19
+ require_relative "ruby_astm/HL7/hl7_observation"
20
+ require_relative "ruby_astm/custom/siemens_abg_electrolyte_server"
21
+
File without changes
File without changes
File without changes
File without changes
@@ -4,6 +4,7 @@ require 'em-rubyserial'
4
4
  require "active_support/all"
5
5
  require "json"
6
6
  require "redis"
7
+ require "rest-firebase"
7
8
 
8
9
  class AstmServer
9
10
 
@@ -0,0 +1,315 @@
1
+ class SiemensAbgElectrolyteServer < AstmServer
2
+
3
+ SIEMENS_ELECTROLYTE_END = [10,10,10,10]
4
+ ELECTROLYTE_START = [45, 45, 45, 32]
5
+ attr_accessor :current_text_segment
6
+
7
+ def get_po2
8
+ m = []
9
+ self.current_text_segment.scan(/pO2\s+(?<k>(\d+)(\.\d)*)(\^|v)?\s+mmHg/) do |l|
10
+ n = Regexp.last_match
11
+ m << n[:k].to_s
12
+ end
13
+ raise "more than one result #{m.to_s}" if (m.size > 1)
14
+ return m[0] if m.size == 1
15
+ return nil
16
+ end
17
+
18
+ def get_pco2
19
+ m = []
20
+ self.current_text_segment.scan(/pCO2\s+(?<k>(\d+)(\.\d)*)(\^|v)?\s+mmHg/) do |l|
21
+ n = Regexp.last_match
22
+ m << n[:k].to_s
23
+ end
24
+ raise "more than one result #{m.to_s}" if (m.size > 1)
25
+ return m[0] if m.size == 1
26
+ return nil
27
+ end
28
+
29
+ def get_ph
30
+ m = []
31
+ self.current_text_segment.scan(/pH\s+(?<k>(\d+)[\.\d]*)(\^|v)?\s+$/) do |l|
32
+ n = Regexp.last_match
33
+ m << n[:k].to_s
34
+ end
35
+ raise "more than one result #{m.to_s}" if (m.size > 1)
36
+ return m[0] if m.size == 1
37
+ return nil
38
+ end
39
+
40
+ def get_na
41
+ m = []
42
+ self.current_text_segment.scan(/Na\+\s+(?<k>(\d+)[\.\d]*)(\^|v)?\s+mmol\/L/) do |l|
43
+ n = Regexp.last_match
44
+ m << n[:k].to_s
45
+ end
46
+ raise "more than one result #{m.to_s}" if (m.size > 1)
47
+ return m[0] if m.size == 1
48
+ return nil
49
+ end
50
+
51
+ def get_k
52
+ m = []
53
+ self.current_text_segment.scan(/K\+\s+(?<k>(\d+)[\.\d]*)(\^|v)?\s+mmol\/L/) do |l|
54
+ n = Regexp.last_match
55
+ m << n[:k].to_s
56
+ end
57
+ raise "more than one result #{m.to_s}" if (m.size > 1)
58
+ return m[0] if m.size == 1
59
+ return nil
60
+ end
61
+
62
+ def get_cl
63
+ m = []
64
+ self.current_text_segment.scan(/Cl\-\s+(?<k>(\d+)[\.\d]*)(\^|v)?\s+mmol\/L/) do |l|
65
+ n = Regexp.last_match
66
+ m << n[:k].to_s
67
+ end
68
+ raise "more than one result #{m.to_s}" if (m.size > 1)
69
+ return m[0] if m.size == 1
70
+ return nil
71
+ end
72
+
73
+ def get_patient_id
74
+ m = []
75
+ self.current_text_segment.scan(/Patient\s+ID\s+(?<k>\d+)$/) do |l|
76
+ n = Regexp.last_match
77
+ m << n[:k].to_s
78
+ end
79
+ raise "more than one result #{m.to_s}" if (m.size > 1)
80
+ return m[0] if m.size == 1
81
+ return nil
82
+ end
83
+
84
+ ## we override the lab interface methods
85
+ ## and we don't pollute the lab interface itself.
86
+ ## as this is a custom analyzer.
87
+ ## @param[Array] :
88
+ ## [[bytes],[bytes]....]
89
+ def process_electrolytes(data_bytes)
90
+ #puts "came to process electrolytes_plain_text"
91
+ byte_arr = data_bytes.flatten
92
+ #puts "the end part of the arr is"
93
+ return if byte_arr[-4..-1] != SIEMENS_ELECTROLYTE_END
94
+ self.data_bytes = []
95
+ concat = ""
96
+ byte_arr.each do |byte|
97
+ x = [byte].pack('c*').force_encoding('UTF-8')
98
+ if x == "\r"
99
+ concat+="\n"
100
+ elsif x == "\n"
101
+ #puts "new line found --- "
102
+ concat+=x
103
+ #puts "last thing in concat."
104
+ #puts concat[-1].to_s
105
+ else
106
+ concat+=x
107
+ end
108
+ end
109
+
110
+ self.headers ||= [Header.new]
111
+ concat.split("--------------------------------").each do |record|
112
+
113
+ self.current_text_segment = record
114
+ if patient_id = get_patient_id
115
+ self.headers[-1].patients ||= []
116
+ p = Patient.new
117
+ p.patient_id = patient_id
118
+ p.orders ||= []
119
+ o = Order.new
120
+ o.results ||= {}
121
+ if sodium = get_na
122
+ r = Result.new
123
+ r.name = "SNATRIUM"
124
+ r.report_name = "Serum Electrolytes"
125
+ r.value = sodium
126
+ r.units = "mmol/L"
127
+ r.timestamp = Time.now.to_i
128
+ o.results["SNATRIUM"] = r
129
+ end
130
+
131
+ if potassium = get_k
132
+ r = Result.new
133
+ r.name = "SPOTASSIUM"
134
+ r.report_name = "Serum Electrolytes"
135
+ r.value = potassium
136
+ r.units = "mmol/L"
137
+ r.timestamp = Time.now.to_i
138
+ o.results["SPOTASSIUM"] = r
139
+ end
140
+
141
+ if chloride = get_cl
142
+ r = Result.new
143
+ r.name = "SCHLORIDE"
144
+ r.report_name = "Serum Electrolytes"
145
+ r.value = chloride
146
+ r.units = "mmol/L"
147
+ r.timestamp = Time.now.to_i
148
+ o.results["SCHLORIDE"] = r
149
+ end
150
+
151
+ if ph = get_ph
152
+ r = Result.new
153
+ r.name = "pH"
154
+ r.report_name = "Serum Electrolytes"
155
+ r.value = ph
156
+ r.units = "mmol/L"
157
+ r.timestamp = Time.now.to_i
158
+ o.results["pH"] = r
159
+ end
160
+
161
+ if po2 = get_po2
162
+ r = Result.new
163
+ r.name = "po2"
164
+ r.report_name = "Serum Electrolytes"
165
+ r.value = po2
166
+ r.units = "mmHg"
167
+ r.timestamp = Time.now.to_i
168
+ o.results["po2"] = r
169
+ end
170
+
171
+ if pco2 = get_pco2
172
+ r = Result.new
173
+ r.name = "pco2"
174
+ r.report_name = "Serum Electrolytes"
175
+ r.value = pco2
176
+ r.units = "mmHg"
177
+ r.timestamp = Time.now.to_i
178
+ o.results["pco2"] = r
179
+ end
180
+
181
+ p.orders << o
182
+ self.headers[-1].patients << p
183
+ end
184
+ end
185
+
186
+ end
187
+
188
+ def process_text_file(full_file_path)
189
+ k = IO.read(full_file_path)
190
+ process_electrolytes(k.bytes)
191
+ end
192
+
193
+
194
+ def receive_data(data)
195
+
196
+
197
+ begin
198
+
199
+
200
+ self.data_buffer ||= ''
201
+
202
+ concat = ""
203
+
204
+ byte_arr = data.bytes.to_a
205
+
206
+ self.test_data_bytes ||= []
207
+
208
+ self.data_bytes ||= []
209
+
210
+ self.test_data_bytes.push(byte_arr)
211
+
212
+ self.data_bytes.push(byte_arr)
213
+
214
+
215
+ concat = pre_process_bytes(byte_arr,concat)
216
+
217
+ #puts "concat is:"
218
+
219
+ #puts concat.to_s
220
+
221
+ self.data_buffer << concat
222
+
223
+ ## if the last byte is EOT, then call process text.
224
+ ## inside that split by line and process one at a time.
225
+ ##process_text(concat)
226
+ #puts "data bytes -1: #{self.data_bytes[-1]}"
227
+ #puts "data bytes 0: #{self.data_bytes[0]}"
228
+ #if self.data_bytes[0] == ELECTROLYTE_START
229
+ self.process_electrolytes(self.data_bytes)
230
+ #end
231
+ =begin
232
+ if data.bytes.to_a[-1] == 4
233
+ puts "GOT EOT --- PROCESSING BUFFER, AND CLEARING."
234
+ process_text(self.data_buffer)
235
+ #root_path = File.dirname __dir
236
+ #puts "root path #{root_path}"
237
+ #IO.write((File.join root_path,'test','resources','roche_multi_frame_bytes.txt'),self.test_data_bytes.to_s)
238
+ #puts self.test_data_bytes.flatten.to_s
239
+ self.data_buffer = ''
240
+ unless self.headers.blank?
241
+ if self.headers[-1].queries.blank?
242
+ #puts "no queries in header so sending ack after getting EOT and processing the buffer"
243
+ send_data(ACK)
244
+ else
245
+ #puts "sending ENQ"
246
+ send_data(ENQ)
247
+ end
248
+ else
249
+ puts "sending catch all --------------- ACK --------------"
250
+ send_data(ACK)
251
+ end
252
+ elsif data.bytes.to_a[0] == 6
253
+ puts "GOT ACK --- GENERATING RESPONSE"
254
+ unless self.headers.blank?
255
+ header_responses = self.headers[-1].build_one_response({machine_name: self.headers[-1].machine_name})
256
+ ## if no queries then, we have to send ack.
257
+ if header_responses.blank?
258
+ #puts "sending ACK since there are no queries in the header"
259
+ send_data(ACK)
260
+ end
261
+ header_responses.each_with_index {|response,key|
262
+ message_checksum = checksum(response + terminator + ETX)
263
+ final_resp = STX + response + terminator + ETX + message_checksum + "\r"
264
+ final_resp_arr = final_resp.bytes.to_a
265
+ final_resp_arr << 10
266
+ if (self.headers[-1].response_sent == false)
267
+ #puts "sending the data as follows----------------------------------------------"
268
+ #puts "response sent is:"
269
+ #puts self.headers[-1].response_sent
270
+ #puts final_resp_arr.pack('c*').gsub(/\r/,'\n')
271
+ send_data(final_resp_arr.pack('c*'))
272
+ self.headers[-1].response_sent = true if (key == (header_responses.size - 1))
273
+ else
274
+ #puts "sending EOT"
275
+ send_data(EOT)
276
+ end
277
+ }
278
+ else
279
+ #puts "NO HEADERS PRESENT --- "
280
+ end
281
+ elsif data.bytes.to_a[0] == 255
282
+ puts " ----------- got 255 data -----------, not sending anything back. "
283
+ else
284
+ #unless self.data_buffer.blank?
285
+ # puts self.data_buffer.gsub(/\r/,'\n').to_s
286
+ #end
287
+ ## send the header
288
+ #puts "--------- SENT ACK -----------"
289
+ ## strip non utf 8 characters from it.
290
+ self.data_buffer.encode!('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')
291
+ if self.data_buffer =~ /MSH\|/
292
+ #puts " -------------- HEADERS ARE BLANK WITH HL7, sending ack. ------------ "
293
+ process_text(self.data_buffer)
294
+ self.data_buffer = ''
295
+ if self.headers.size > 0
296
+ self.headers[-1].commit
297
+ send_data(self.headers[-1].generate_ack_success_response)
298
+ end
299
+ else
300
+ #puts " -------------- HEADERS ARE BLANK NOT HL7, sending ack. ------------ "
301
+ send_data(ACK)
302
+ end
303
+ end
304
+ =end
305
+ rescue => e
306
+
307
+ #self.headers = []
308
+ AstmServer.log("data was: " + self.data_buffer + "error is:" + e.backtrace.to_s)
309
+ #send_data(EOT)
310
+ end
311
+
312
+ end
313
+
314
+
315
+ end