levinalex-liaison_labor 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +10 -0
- data/Manifest.txt +13 -0
- data/README.txt +49 -0
- data/Rakefile +29 -0
- data/bin/liaison_server +9 -0
- data/liaison_labor.gemspec +36 -0
- data/lib/liaison_labor/interface.rb +236 -0
- data/lib/liaison_labor/packets.rb +302 -0
- data/lib/liaison_labor.rb +8 -0
- data/lib/liaison_server.rb +169 -0
- data/spec/liaison_interface_spec.rb +43 -0
- data/spec/liaison_packet_spec.rb +234 -0
- data/spec/mock/mock_liaison.rb +43 -0
- metadata +78 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.txt
|
4
|
+
Rakefile
|
5
|
+
bin/liaison_server
|
6
|
+
liaison_labor.gemspec
|
7
|
+
lib/liaison_labor.rb
|
8
|
+
lib/liaison_labor/interface.rb
|
9
|
+
lib/liaison_labor/packets.rb
|
10
|
+
lib/liaison_server.rb
|
11
|
+
spec/liaison_interface_spec.rb
|
12
|
+
spec/liaison_packet_spec.rb
|
13
|
+
spec/mock/mock_liaison.rb
|
data/README.txt
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
LiaisonLabor
|
2
|
+
by Levin Alexander
|
3
|
+
http://levinalex.net/src/liaison_labor
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
An interface to the LIAISON® analyser by DiaSorin
|
8
|
+
<http://www.diasorin.com/en/productsandsystems/liaison>
|
9
|
+
|
10
|
+
== FEATURES/PROBLEMS:
|
11
|
+
|
12
|
+
* not yet written
|
13
|
+
|
14
|
+
== SYNOPSIS:
|
15
|
+
|
16
|
+
not yet written
|
17
|
+
|
18
|
+
== REQUIREMENTS:
|
19
|
+
|
20
|
+
* not yet written
|
21
|
+
|
22
|
+
== INSTALL:
|
23
|
+
|
24
|
+
* not yet written
|
25
|
+
|
26
|
+
== LICENSE:
|
27
|
+
|
28
|
+
(The MIT License)
|
29
|
+
|
30
|
+
Copyright (c) 2007-2008 Levin Alexander
|
31
|
+
|
32
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
33
|
+
a copy of this software and associated documentation files (the
|
34
|
+
'Software'), to deal in the Software without restriction, including
|
35
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
36
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
37
|
+
permit persons to whom the Software is furnished to do so, subject to
|
38
|
+
the following conditions:
|
39
|
+
|
40
|
+
The above copyright notice and this permission notice shall be
|
41
|
+
included in all copies or substantial portions of the Software.
|
42
|
+
|
43
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
44
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
45
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
46
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
47
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
48
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
49
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'hoe'
|
3
|
+
require 'spec/rake/spectask'
|
4
|
+
|
5
|
+
require './lib/liaison_labor.rb'
|
6
|
+
|
7
|
+
Hoe.new('liaison_labor', LiaisonLabor::VERSION) do |p|
|
8
|
+
p.rubyforge_name = 'liaison_labor'
|
9
|
+
p.summary = 'interfaces Liaison device to worklist_manager'
|
10
|
+
|
11
|
+
p.url = 'http://levinalex.net/src/liaison'
|
12
|
+
p.developer('Levin Alexander', 'mail@levinalex.net')
|
13
|
+
p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
|
14
|
+
|
15
|
+
# p.extra_deps ['levinalex-serial_interface']
|
16
|
+
end
|
17
|
+
|
18
|
+
Rake.application.instance_eval { @tasks["test"] = nil }
|
19
|
+
|
20
|
+
Spec::Rake::SpecTask.new do |t|
|
21
|
+
t.warning = true
|
22
|
+
t.spec_opts = %w(-c -f specdoc)
|
23
|
+
end
|
24
|
+
task :test => :spec
|
25
|
+
|
26
|
+
task :cultivate do
|
27
|
+
system "touch Manifest.txt; rake check_manifest | grep -v \"(in \" | patch"
|
28
|
+
system "rake debug_gem | grep -v \"(in \" > `basename \\`pwd\\``.gemspec"
|
29
|
+
end
|
data/bin/liaison_server
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{liaison_labor}
|
5
|
+
s.version = "0.1.8"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Levin Alexander"]
|
9
|
+
s.date = %q{2009-06-11}
|
10
|
+
s.default_executable = %q{liaison_server}
|
11
|
+
s.description = %q{An interface to the LIAISON® analyser by DiaSorin <http://www.diasorin.com/en/productsandsystems/liaison>}
|
12
|
+
s.email = ["mail@levinalex.net"]
|
13
|
+
s.executables = ["liaison_server"]
|
14
|
+
s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.txt"]
|
15
|
+
s.files = ["History.txt", "Manifest.txt", "README.txt", "Rakefile", "bin/liaison_server", "liaison_labor.gemspec", "lib/liaison_labor.rb", "lib/liaison_labor/interface.rb", "lib/liaison_labor/packets.rb", "lib/liaison_server.rb", "spec/liaison_interface_spec.rb", "spec/liaison_packet_spec.rb", "spec/mock/mock_liaison.rb"]
|
16
|
+
s.has_rdoc = true
|
17
|
+
s.homepage = %q{http://levinalex.net/src/liaison}
|
18
|
+
s.rdoc_options = ["--main", "README.txt"]
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
s.rubyforge_project = %q{liaison_labor}
|
21
|
+
s.rubygems_version = %q{1.3.1}
|
22
|
+
s.summary = %q{interfaces Liaison device to worklist_manager}
|
23
|
+
|
24
|
+
if s.respond_to? :specification_version then
|
25
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
26
|
+
s.specification_version = 2
|
27
|
+
|
28
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
29
|
+
s.add_development_dependency(%q<hoe>, [">= 1.12.1"])
|
30
|
+
else
|
31
|
+
s.add_dependency(%q<hoe>, [">= 1.12.1"])
|
32
|
+
end
|
33
|
+
else
|
34
|
+
s.add_dependency(%q<hoe>, [">= 1.12.1"])
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,236 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'serial_interface'
|
3
|
+
|
4
|
+
module Diasorin
|
5
|
+
module Liaison
|
6
|
+
|
7
|
+
class Interface < PacketIO
|
8
|
+
|
9
|
+
def initialize(read, write=read, options = {}, &blk)
|
10
|
+
super(LiaisonProtocol, read, write, options)
|
11
|
+
|
12
|
+
# define default handlers for import/export of patients
|
13
|
+
# and results
|
14
|
+
#
|
15
|
+
@on_order_request = lambda { nil }
|
16
|
+
|
17
|
+
# is called whenever a result is sent
|
18
|
+
#
|
19
|
+
@on_result = lambda { |patient, order, result| }
|
20
|
+
|
21
|
+
yield self if block_given?
|
22
|
+
|
23
|
+
add_sender( {
|
24
|
+
:message_header => Packets::MessageHeaderRecord,
|
25
|
+
:patient => Packets::PatientInformationRecord,
|
26
|
+
:order => Packets::TestOrderRecord,
|
27
|
+
:terminator => Packets::MessageTerminatorRecord
|
28
|
+
} )
|
29
|
+
|
30
|
+
add_receiver :message_header => Packets::MessageHeaderRecord do |p|
|
31
|
+
# delete the list of patients
|
32
|
+
@patient_information ||= {}
|
33
|
+
end
|
34
|
+
|
35
|
+
add_receiver :patient_record => Packets::PatientInformationRecord do |p|
|
36
|
+
@last_order_packet = nil
|
37
|
+
@last_result_record = nil
|
38
|
+
|
39
|
+
@last_patient_packet = p
|
40
|
+
end
|
41
|
+
|
42
|
+
add_receiver :comment => Packets::CommentRecord do |p|
|
43
|
+
@on_result.call(@last_patient_packet, @last_order_packet, @last_result_record, p)
|
44
|
+
end
|
45
|
+
|
46
|
+
add_receiver :order => Packets::TestOrderRecord do |p|
|
47
|
+
@last_result_record = nil
|
48
|
+
|
49
|
+
@last_order_packet = p
|
50
|
+
end
|
51
|
+
|
52
|
+
add_receiver :test_result => Packets::ResultRecord do |p|
|
53
|
+
@last_result_record = p
|
54
|
+
@on_result.call(@last_patient_packet, @last_order_packet, @last_result_record)
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
add_receiver :request_information => Packets::RequestInformationSegment do |p|
|
59
|
+
puts "RequestInformation: #{p.inspect}"
|
60
|
+
|
61
|
+
@patient_information ||= {}
|
62
|
+
requests = @on_order_request.call(p.starting_range)
|
63
|
+
@patient_information[p.sequence_number] = requests if requests
|
64
|
+
|
65
|
+
@mode = :request_information
|
66
|
+
end
|
67
|
+
|
68
|
+
add_receiver :ack => Packets::Acknowledge do |p|
|
69
|
+
if @mode == :request_information
|
70
|
+
@mode = @transmitting
|
71
|
+
send_packet(:message_header, "HOST", "Liaison")
|
72
|
+
transmit_requests
|
73
|
+
else
|
74
|
+
# keep on sending
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
add_receiver :nack => Packets::NotAcknowledge do |p|
|
79
|
+
puts "Nack!"
|
80
|
+
raise "NACK received"
|
81
|
+
end
|
82
|
+
|
83
|
+
add_receiver :message_terminator => Packets::MessageTerminatorRecord do |p|
|
84
|
+
puts "got MessageTerminatorRecord"
|
85
|
+
if @mode == :request_information
|
86
|
+
puts "should now send information"
|
87
|
+
# initiate transfer (ENQ)
|
88
|
+
send_packet("\x05", :raw => true)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
run
|
93
|
+
end
|
94
|
+
|
95
|
+
def on_order_request(&blk)
|
96
|
+
@on_order_request = blk
|
97
|
+
end
|
98
|
+
def on_result(&blk)
|
99
|
+
@on_result = blk
|
100
|
+
end
|
101
|
+
|
102
|
+
def transmit_requests
|
103
|
+
@patient_information.each do |sequence_nr, data|
|
104
|
+
p "sequence: #{sequence_nr.inspect}"
|
105
|
+
p "data: #{data.inspect}"
|
106
|
+
send_packet(:patient, sequence_nr, data["patient"]["number"], data["patient"]["last_name"], data["patient"]["first_name"])
|
107
|
+
data["types"].each do |request|
|
108
|
+
send_packet(:order, sequence_nr, data["id"], request)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
send_packet(:terminator, "1")
|
112
|
+
# EOT
|
113
|
+
send_packet("\x04", :raw => true)
|
114
|
+
|
115
|
+
@mode = :neutral
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
class LiaisonProtocol
|
122
|
+
STX = 0x02 # STX
|
123
|
+
ETX = 0x03 # ETX
|
124
|
+
NACK = 0x15 # NACK
|
125
|
+
ACK = 0x06 # ACK
|
126
|
+
ENQ = 0x05 # ENQ
|
127
|
+
CR = 0x0d
|
128
|
+
LF = 0x0a
|
129
|
+
EOT = 0x04
|
130
|
+
|
131
|
+
def initialize(send_callback, receive_callback, options = {})
|
132
|
+
@send_callback, @receive_callback = send_callback, receive_callback
|
133
|
+
@packet_buffer = ""
|
134
|
+
@send_buffer = []
|
135
|
+
@checksum = 0
|
136
|
+
@frame_number = 1
|
137
|
+
@state = :neutral
|
138
|
+
end
|
139
|
+
|
140
|
+
def self.checksum_valid?(string, expected)
|
141
|
+
sum = string.to_enum(:each_byte).inject(ETX) { |s,v| s+v } % 0x100
|
142
|
+
if sum == expected
|
143
|
+
return true
|
144
|
+
else
|
145
|
+
raise ::SerialProtocol::ChecksumMismatch, "expected #{expected}, got #{sum}"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def get_packet(str)
|
150
|
+
@receive_callback.call(str)
|
151
|
+
end
|
152
|
+
|
153
|
+
def add_char_to_packet(char)
|
154
|
+
|
155
|
+
case @state
|
156
|
+
when :neutral
|
157
|
+
if char == ENQ # establishment
|
158
|
+
send_packet(ACK.chr, :raw => true)
|
159
|
+
@state = :transfer_begin
|
160
|
+
elsif char == ACK
|
161
|
+
get_packet(ACK.chr)
|
162
|
+
elsif char == NACK
|
163
|
+
get_packet(NACK.chr)
|
164
|
+
else
|
165
|
+
# receive and ignore partial data
|
166
|
+
end
|
167
|
+
when :transfer_begin
|
168
|
+
if char == STX
|
169
|
+
# begin to receive a packet
|
170
|
+
@packet_buffer = ""
|
171
|
+
@state = :packet_data
|
172
|
+
elsif char == EOT
|
173
|
+
@state = :neutral
|
174
|
+
else
|
175
|
+
raise "unexpected data after ENQ, expected STX, got #{char.to_i}"
|
176
|
+
end
|
177
|
+
when :packet_data
|
178
|
+
if char == ETX
|
179
|
+
@state = :packet_checksum_1
|
180
|
+
else
|
181
|
+
@packet_buffer << char
|
182
|
+
end
|
183
|
+
when :packet_checksum_1
|
184
|
+
@checksum_str = "" << char
|
185
|
+
@state = :packet_checksum_2
|
186
|
+
when :packet_checksum_2
|
187
|
+
@checksum_str << char
|
188
|
+
@state = :packet_cr
|
189
|
+
when :packet_cr
|
190
|
+
if char == CR
|
191
|
+
@state = :packet_lf
|
192
|
+
else
|
193
|
+
raise "unexpected data after checksum, expected CR, got #{char.to_i}"
|
194
|
+
end
|
195
|
+
when :packet_lf
|
196
|
+
if char == LF
|
197
|
+
if self.class.checksum_valid?(@packet_buffer, @checksum_str.to_i(16))
|
198
|
+
send_packet(ACK.chr, :raw => true)
|
199
|
+
# cut the sequence ID
|
200
|
+
get_packet(@packet_buffer[1..-1])
|
201
|
+
else
|
202
|
+
send_packet(NACK.chr, :raw => true)
|
203
|
+
end
|
204
|
+
@state = :transfer_begin
|
205
|
+
else
|
206
|
+
raise "unexpected data after checksum, expected LF, got #{char.to_i}"
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def checksum(data)
|
212
|
+
val = data.to_enum(:each_byte).inject(0) { |sum,b| sum+b } % 0x100
|
213
|
+
val.to_s(16).rjust(2,'0')
|
214
|
+
end
|
215
|
+
|
216
|
+
def send_packet(data, options = {})
|
217
|
+
warn "--> #{data.inspect}"
|
218
|
+
if options[:raw]
|
219
|
+
if data == ENQ.chr
|
220
|
+
@frame_number = 1 # reset frame number on new transmission
|
221
|
+
end
|
222
|
+
@send_callback.call(data)
|
223
|
+
else
|
224
|
+
@frame_number ||= 1
|
225
|
+
|
226
|
+
data = "" << @frame_number.to_s << data
|
227
|
+
packet_str = "" << "\x02" << data << "\r" << "\x03" << checksum("" << data << "\r\x03") << "\r\n"
|
228
|
+
p "--~> #{packet_str.inspect}"
|
229
|
+
@send_callback.call(packet_str)
|
230
|
+
|
231
|
+
@frame_number = (@frame_number + 1) % 8
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
@@ -0,0 +1,302 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'serial_interface'
|
3
|
+
|
4
|
+
module Diasorin
|
5
|
+
module Liaison
|
6
|
+
module Packets
|
7
|
+
class LiaisonPacket < SerialPacket
|
8
|
+
FieldDelimiter = '|'
|
9
|
+
RepeatDelimiter = '\\'
|
10
|
+
ComponentDelimiter = '^'
|
11
|
+
EscapeDelimiter = "&"
|
12
|
+
|
13
|
+
header_format "A"
|
14
|
+
|
15
|
+
def sequence_number=(val)
|
16
|
+
@sequence_number = val.to_i
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_str
|
20
|
+
to_s
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Acknowledge < LiaisonPacket
|
25
|
+
def self.matches?(str)
|
26
|
+
str == "\x06" ? true : false
|
27
|
+
end
|
28
|
+
def to_s; "\x06"; end
|
29
|
+
end
|
30
|
+
|
31
|
+
class NotAcknowledge < LiaisonPacket
|
32
|
+
def self.matches?(str)
|
33
|
+
str == "\x15" ? true : false
|
34
|
+
end
|
35
|
+
def to_s; "\x15"; end
|
36
|
+
end
|
37
|
+
|
38
|
+
class MessageHeaderRecord < LiaisonPacket
|
39
|
+
header_format "A"
|
40
|
+
header_filter "H"
|
41
|
+
header ["H"]
|
42
|
+
|
43
|
+
attr_reader :sender_id
|
44
|
+
attr_reader :receiver_id
|
45
|
+
attr_reader :timestamp
|
46
|
+
|
47
|
+
def initialize(sender_id, receiver_id)
|
48
|
+
@sender_id = sender_id
|
49
|
+
@receiver_id = receiver_id
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_s
|
53
|
+
arr = []
|
54
|
+
arr[1] = "H"
|
55
|
+
arr[2] = "\\^&"
|
56
|
+
arr[5] = @sender_id
|
57
|
+
arr[10] = @receiver_id
|
58
|
+
arr.shift
|
59
|
+
|
60
|
+
arr.join("|")
|
61
|
+
end
|
62
|
+
|
63
|
+
def initialize_from_packet(str)
|
64
|
+
records = str.split(FieldDelimiter)
|
65
|
+
|
66
|
+
@sender_id = records[4]
|
67
|
+
@receiver_id = records[9]
|
68
|
+
@timestamp = DateTime.new(*records[13].scan(/(....)(..)(..)(..)(..)(..)/)[0].map {|x| x.to_i })
|
69
|
+
|
70
|
+
@version = records[12]
|
71
|
+
raise "Version is not supported" unless @version == '1'
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class MessageTerminatorRecord < LiaisonPacket
|
77
|
+
header_format "A"
|
78
|
+
header_filter "L"
|
79
|
+
header ["L"]
|
80
|
+
|
81
|
+
attr_reader :sequence_number
|
82
|
+
attr_reader :termination_code
|
83
|
+
|
84
|
+
def initialize_from_packet(str)
|
85
|
+
records = str.split(FieldDelimiter)
|
86
|
+
_, self.sequence_number, @termination_code = records
|
87
|
+
end
|
88
|
+
|
89
|
+
def initialize(sequence_number, termination_code = "N")
|
90
|
+
@sequence_number = sequence_number
|
91
|
+
@termination_code = termination_code
|
92
|
+
end
|
93
|
+
|
94
|
+
def to_s
|
95
|
+
arr = []
|
96
|
+
arr[1] = "L"
|
97
|
+
arr[2] = @sequence_number
|
98
|
+
arr[3] = @termination_code
|
99
|
+
arr.shift
|
100
|
+
|
101
|
+
arr.join("|")
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
class PatientInformationRecord < LiaisonPacket
|
106
|
+
header_format "A"
|
107
|
+
header_filter "P"
|
108
|
+
header ["P"]
|
109
|
+
|
110
|
+
attr_reader :sequence_number
|
111
|
+
attr_reader :patient_id
|
112
|
+
attr_reader :patient_last_name
|
113
|
+
attr_reader :patient_first_name
|
114
|
+
attr_reader :birthdate
|
115
|
+
attr_reader :sex
|
116
|
+
attr_reader :physician
|
117
|
+
|
118
|
+
def patient_name=(val)
|
119
|
+
@patient_last_name, @patient_first_name = val.split(ComponentDelimiter)
|
120
|
+
end
|
121
|
+
def birthdate=(val)
|
122
|
+
@birthdate = case val
|
123
|
+
when String
|
124
|
+
DateTime.new(*val.scan(/(....)(..)(..)/)[0].map {|x| x.to_i }) rescue nil
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def initialize(sequence_nr, patient_id, lastname = "", firstname = "")
|
129
|
+
@sequence_number = sequence_nr.to_i
|
130
|
+
@patient_id = patient_id
|
131
|
+
@patient_last_name, @patient_first_name = lastname, firstname
|
132
|
+
end
|
133
|
+
|
134
|
+
def to_s
|
135
|
+
arr = []
|
136
|
+
arr[1] = "P"
|
137
|
+
arr[2] = @sequence_number
|
138
|
+
arr[4] = @patient_id.to_s
|
139
|
+
arr[6] = [@patient_last_name, @patient_first_name].join("^")
|
140
|
+
arr.shift
|
141
|
+
|
142
|
+
arr.join("|")
|
143
|
+
end
|
144
|
+
|
145
|
+
def initialize_from_packet(str)
|
146
|
+
records = str.split(FieldDelimiter)
|
147
|
+
|
148
|
+
_1,
|
149
|
+
self.sequence_number,
|
150
|
+
_3,
|
151
|
+
@patient_id,
|
152
|
+
_5,
|
153
|
+
self.patient_name,
|
154
|
+
_7,
|
155
|
+
self.birthdate,
|
156
|
+
@sex,
|
157
|
+
_10,
|
158
|
+
_11,
|
159
|
+
_12,
|
160
|
+
_13,
|
161
|
+
@physician,
|
162
|
+
_rest = records
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
class RequestInformationSegment < LiaisonPacket
|
167
|
+
header_format "A"
|
168
|
+
header_filter "Q"
|
169
|
+
header ["Q"]
|
170
|
+
|
171
|
+
attr_reader :sequence_number
|
172
|
+
attr_reader :starting_range
|
173
|
+
|
174
|
+
def initialize_from_packet(str)
|
175
|
+
records = str.split(FieldDelimiter)
|
176
|
+
|
177
|
+
_1, self.sequence_number, @starting_range, _rest = records
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
class TestOrderRecord < LiaisonPacket
|
182
|
+
header_format "A"
|
183
|
+
header_filter "O"
|
184
|
+
header ["O"]
|
185
|
+
|
186
|
+
attr_reader :sequence_number
|
187
|
+
attr_reader :specimen_id
|
188
|
+
attr_reader :test_id
|
189
|
+
attr_reader :dilution
|
190
|
+
attr_reader :priority
|
191
|
+
attr_reader :timestamp
|
192
|
+
attr_reader :record_type
|
193
|
+
|
194
|
+
def sequence_number=(val)
|
195
|
+
@sequence_number = val.to_i
|
196
|
+
end
|
197
|
+
def test_order=(data)
|
198
|
+
_1, _2, _3, @test_id, @dilution = data.split(ComponentDelimiter)
|
199
|
+
end
|
200
|
+
def timestamp=(val)
|
201
|
+
@timestamp = case val
|
202
|
+
when String
|
203
|
+
DateTime.new(*val.scan(/(....)(..)(..)/)[0].map {|x| x.to_i }) rescue nil
|
204
|
+
end
|
205
|
+
end
|
206
|
+
def initialize_from_packet(str)
|
207
|
+
records = str.split(FieldDelimiter)
|
208
|
+
|
209
|
+
_1,
|
210
|
+
self.sequence_number,
|
211
|
+
@specimen_id,
|
212
|
+
_4,
|
213
|
+
self.test_order,
|
214
|
+
@priority,
|
215
|
+
self.timestamp, _, _, _10, _, _, _, _, _, _, _, _, _, _20, _, _, _, _, _, @record_type,
|
216
|
+
_rest = records
|
217
|
+
end
|
218
|
+
|
219
|
+
def initialize(sequence_number, specimen_id, test_id)
|
220
|
+
@sequence_number = sequence_number
|
221
|
+
@specimen_id = specimen_id
|
222
|
+
@test_id = test_id
|
223
|
+
end
|
224
|
+
|
225
|
+
|
226
|
+
def to_s
|
227
|
+
arr = []
|
228
|
+
arr[1] = "O"
|
229
|
+
arr[2] = @sequence_number
|
230
|
+
arr[3] = @specimen_id
|
231
|
+
arr[5] = [nil,nil,nil,@test_id].join("^")
|
232
|
+
arr.shift
|
233
|
+
|
234
|
+
arr.join("|")
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
class ResultRecord < LiaisonPacket
|
239
|
+
header_format "A"
|
240
|
+
header_filter "R"
|
241
|
+
header ["R"]
|
242
|
+
|
243
|
+
attr_reader :sequence_number
|
244
|
+
attr_reader :test_id
|
245
|
+
attr_reader :value
|
246
|
+
attr_reader :unit
|
247
|
+
attr_reader :abnormal_flags
|
248
|
+
attr_reader :result_status
|
249
|
+
attr_reader :timestamp
|
250
|
+
attr_reader :instrument
|
251
|
+
|
252
|
+
def test_result=(data)
|
253
|
+
_1, _2, _3, @test_id = data.split(ComponentDelimiter)
|
254
|
+
end
|
255
|
+
def timestamp=(val)
|
256
|
+
@timestamp = case val
|
257
|
+
when String
|
258
|
+
DateTime.new(*val.scan(/(....)(..)(..)(..)(..)(..)/)[0].map {|x| x.to_i }) rescue nil
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
def initialize_from_packet(str)
|
263
|
+
records = str.split(FieldDelimiter)
|
264
|
+
|
265
|
+
_1,
|
266
|
+
self.sequence_number,
|
267
|
+
self.test_result,
|
268
|
+
@value,
|
269
|
+
@unit,
|
270
|
+
_6,
|
271
|
+
@abnormal_flags,
|
272
|
+
_7,
|
273
|
+
@result_status,
|
274
|
+
_10, _11, _12,
|
275
|
+
self.timestamp,
|
276
|
+
@instrument,
|
277
|
+
rest = records
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
class CommentRecord < LiaisonPacket
|
282
|
+
header_format "A"
|
283
|
+
header_filter "C"
|
284
|
+
header ["C"]
|
285
|
+
|
286
|
+
attr_reader :comment
|
287
|
+
|
288
|
+
def initialize_from_packet(str)
|
289
|
+
records = str.split(FieldDelimiter)
|
290
|
+
|
291
|
+
_1,
|
292
|
+
self.sequence_number,
|
293
|
+
_3,
|
294
|
+
@comment,
|
295
|
+
rest = records
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'net/http'
|
5
|
+
require 'yaml'
|
6
|
+
|
7
|
+
require 'rubygems'
|
8
|
+
|
9
|
+
require File.join(File.dirname(__FILE__),'liaison_labor.rb')
|
10
|
+
|
11
|
+
class Hash
|
12
|
+
def symbolize_keys
|
13
|
+
inject({}) do |options, (key, value)|
|
14
|
+
options[(key.to_sym rescue key) || key] = value
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def stringify_keys
|
19
|
+
inject({}) do |options, (key, value)|
|
20
|
+
options[key.to_s] = value
|
21
|
+
options
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
module Diasorin
|
29
|
+
ConfigFilename = ".liaison_server"
|
30
|
+
ConfigFile = File.join(ENV['HOME'] || ENV['APPDATA'], ConfigFilename)
|
31
|
+
|
32
|
+
# defaults are used when they are not overwritten in a config file
|
33
|
+
# or with command line options
|
34
|
+
#
|
35
|
+
DefaultConfig = {
|
36
|
+
:serial_port => "/dev/ttyUSB0",
|
37
|
+
:baudrate => 9600,
|
38
|
+
:uri => "http://localhost/liaison/"
|
39
|
+
}
|
40
|
+
|
41
|
+
class LiaisonServer
|
42
|
+
|
43
|
+
# load configuration from configfile, merge with
|
44
|
+
# default options
|
45
|
+
#
|
46
|
+
def load_configuration
|
47
|
+
# try to read configuration from file
|
48
|
+
@options_from_file = YAML.load_file(ConfigFile) || {} rescue {}
|
49
|
+
@options_from_file = @options_from_file.symbolize_keys
|
50
|
+
|
51
|
+
# if an option is not given on the command line
|
52
|
+
# it is taken from the config file, or the default is used
|
53
|
+
@options = Hash.new() { |h,k| @options_from_file[k] || DefaultConfig[k] }
|
54
|
+
end
|
55
|
+
|
56
|
+
# parse command line options
|
57
|
+
#
|
58
|
+
def initialize
|
59
|
+
load_configuration
|
60
|
+
|
61
|
+
@opts = OptionParser.new do |opts|
|
62
|
+
|
63
|
+
opts.separator ""
|
64
|
+
opts.separator "liaison_labor is an interface between the Liaison device connected via "
|
65
|
+
opts.separator "serial port, and a worklist_manager HTTP-server"
|
66
|
+
opts.separator ""
|
67
|
+
opts.separator "configuration can be given in '#{ConfigFile}' "
|
68
|
+
opts.separator ""
|
69
|
+
|
70
|
+
opts.on "-V","--version","Display version and exit" do
|
71
|
+
puts "#{self.class} #{::LiaisonLabor::VERSION}"
|
72
|
+
exit
|
73
|
+
end
|
74
|
+
opts.on "-s", "--serial-port DEVICE", "serial port, default is /dev/ttyUSB0" do |arg|
|
75
|
+
@options[:serial_port] = arg
|
76
|
+
end
|
77
|
+
opts.on "--baudrate BAUDRATE", "baudrate of the serial connection, default is 9600" do |arg|
|
78
|
+
@options[:baudrate] = arg.to_i
|
79
|
+
end
|
80
|
+
opts.on "-u", "--uri URI", "URI of the HTTP-Endpoint where data is read or posted" do |arg|
|
81
|
+
@options[:endpoint] = arg
|
82
|
+
end
|
83
|
+
opts.on_tail "-p", "--print-config", "Print the current configuration",
|
84
|
+
"in a format that can be used as a configuration file" do
|
85
|
+
puts @options_from_file.merge(@options).stringify_keys.to_yaml
|
86
|
+
exit
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
# open the serial port for reading and writing
|
93
|
+
#
|
94
|
+
# make sure that output buffering is disabled so that data
|
95
|
+
# is sent immediately
|
96
|
+
#
|
97
|
+
def open_port(portfile, speed)
|
98
|
+
system("stty -echo raw ospeed #{speed} ispeed #{speed} < #{portfile}")
|
99
|
+
port = File.open(portfile, "w+")
|
100
|
+
port.sync = true
|
101
|
+
port
|
102
|
+
end
|
103
|
+
|
104
|
+
def communicate!
|
105
|
+
port = open_port(@options[:serial_port], @options[:baudrate])
|
106
|
+
|
107
|
+
client = Liaison::Interface.new(port) do |liaison|
|
108
|
+
|
109
|
+
# yields the barcode of a sample
|
110
|
+
#
|
111
|
+
# expects a hash consisting of patient information and a list
|
112
|
+
# of requests for this patient
|
113
|
+
#
|
114
|
+
liaison.on_order_request do |barcode|
|
115
|
+
begin
|
116
|
+
data = YAML.load(::Net::HTTP.get( URI.join(@options[:endpoint],"find_requests/",barcode) ))
|
117
|
+
rescue Exception => e
|
118
|
+
puts e
|
119
|
+
puts e.backtrace
|
120
|
+
data = nil
|
121
|
+
end
|
122
|
+
p data
|
123
|
+
data
|
124
|
+
end
|
125
|
+
|
126
|
+
liaison.on_result do |patient, order, result, comment|
|
127
|
+
if comment
|
128
|
+
comment_str = comment.comment
|
129
|
+
else
|
130
|
+
comment_str = nil
|
131
|
+
end
|
132
|
+
|
133
|
+
barcode = order.specimen_id
|
134
|
+
data = {
|
135
|
+
"test_name" => order.test_id,
|
136
|
+
"value" => result.value,
|
137
|
+
"unit" => result.unit,
|
138
|
+
"status" => result.result_status,
|
139
|
+
"flags" => result.abnormal_flags,
|
140
|
+
"comment" => comment_str,
|
141
|
+
"result_timestamp" => result.timestamp
|
142
|
+
}
|
143
|
+
|
144
|
+
::Net::HTTP.post_form(URI.join(@options[:endpoint], "result/", "#{URI.encode(barcode)}"), data.to_hash )
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
client.run
|
149
|
+
|
150
|
+
puts "Listening on #{File.expand_path(port.path)}"
|
151
|
+
|
152
|
+
begin
|
153
|
+
yield client if block_given?
|
154
|
+
client.join
|
155
|
+
rescue Interrupt => e
|
156
|
+
puts "exiting"
|
157
|
+
raise
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# run the application
|
162
|
+
#
|
163
|
+
def run!(args = ARGV)
|
164
|
+
@opts.parse!(args)
|
165
|
+
|
166
|
+
communicate!
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'lib/liaison_labor/interface.rb'
|
2
|
+
|
3
|
+
require 'stringio'
|
4
|
+
|
5
|
+
include Diasorin::Liaison
|
6
|
+
|
7
|
+
context "" do
|
8
|
+
setup do
|
9
|
+
@to_host = StringIO.new
|
10
|
+
@from_host = StringIO.new
|
11
|
+
|
12
|
+
@host = PacketIO.new(LiaisonProtocol, @to_host, @from_host)
|
13
|
+
@host.run
|
14
|
+
end
|
15
|
+
|
16
|
+
def transmit_string(str)
|
17
|
+
@to_host << str
|
18
|
+
@to_host.rewind
|
19
|
+
30.times { Thread.pass }
|
20
|
+
@from_host.rewind
|
21
|
+
end
|
22
|
+
|
23
|
+
specify "ENQ should be acknowledged" do
|
24
|
+
transmit_string "\x05"
|
25
|
+
response = @from_host.read
|
26
|
+
response.should == "\x06"
|
27
|
+
end
|
28
|
+
|
29
|
+
specify "Valid packet should be acknowledged" do
|
30
|
+
s = "\x05\x026L|1|N\x0d\x0309\x0d\x0a"
|
31
|
+
transmit_string s
|
32
|
+
response = @from_host.read
|
33
|
+
response.should == "\x06\x06"
|
34
|
+
end
|
35
|
+
|
36
|
+
specify "another valid packet" do
|
37
|
+
s = "\005\0023R|1|^^^TSH|0.182|mIU/l||L||F||||20070510161808|Liaison\r\003E2\r\n"
|
38
|
+
lambda {
|
39
|
+
transmit_string s
|
40
|
+
response = @from_host.read
|
41
|
+
}.should_not raise_error
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,234 @@
|
|
1
|
+
require 'lib/liaison_labor/packets.rb'
|
2
|
+
|
3
|
+
include Diasorin
|
4
|
+
|
5
|
+
|
6
|
+
def has_header(packet, char)
|
7
|
+
specify "should be matched by packets stating with #{char}" do
|
8
|
+
instance_variable_get("@#{packet}").matches?("#{char}...").should be_true
|
9
|
+
end
|
10
|
+
|
11
|
+
specify "different headers should not match" do
|
12
|
+
instance_variable_get("@#{packet}").matches?("random garbage").should be_false
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def has_example(packet, example_str)
|
17
|
+
specify "example should parse without errors" do
|
18
|
+
lambda {
|
19
|
+
instance_variable_get("@#{packet}").from_str(example_str)
|
20
|
+
}.should_not raise_error
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context "checksum for packets" do
|
25
|
+
setup do
|
26
|
+
@full_packet = "\0021O|1|00000023||^^^FT4^|||||||||||||||||||||F\r\003EE\r\n"
|
27
|
+
end
|
28
|
+
|
29
|
+
specify "data should have a correct checksum" do
|
30
|
+
LiaisonProtocol.checksum_valid?("1O|1|00000023||^^^FT4^|||||||||||||||||||||F\r","EE".to_i(16)).should be_true
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
context "The MessageHeaderRecord packet from/to Liaison" do
|
37
|
+
setup do
|
38
|
+
@packet = Liaison::Packets::MessageHeaderRecord
|
39
|
+
@str = "H|\^&|||LaborEDV|||||Liaison|||1|19971113154903"
|
40
|
+
end
|
41
|
+
|
42
|
+
has_header :packet, "H"
|
43
|
+
|
44
|
+
specify "should have a sender_id" do
|
45
|
+
@packet.from_str(@str).sender_id.should == "LaborEDV"
|
46
|
+
end
|
47
|
+
|
48
|
+
specify "should have a receiver_id" do
|
49
|
+
@packet.from_str(@str).receiver_id.should == "Liaison"
|
50
|
+
end
|
51
|
+
|
52
|
+
specify "should have a timestamp" do
|
53
|
+
@packet.from_str(@str).timestamp.should == DateTime.new(1997,11,13,15,49,03)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "Building a MessageHeaderRecord" do
|
58
|
+
setup do
|
59
|
+
@header = Liaison::Packets::MessageHeaderRecord
|
60
|
+
@packet = @header.new("Sender","Receiver")
|
61
|
+
end
|
62
|
+
|
63
|
+
specify "should build a correct packet" do
|
64
|
+
@packet.to_s.should == "H|\\^&|||Sender|||||Receiver"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context "the MessageTerminatorRecord packet to Liaison" do
|
69
|
+
setup do
|
70
|
+
@terminator_record = Liaison::Packets::MessageTerminatorRecord
|
71
|
+
@str = "L|1|N"
|
72
|
+
@packet = @terminator_record.from_str(@str)
|
73
|
+
end
|
74
|
+
|
75
|
+
has_header :terminator_record, "L"
|
76
|
+
|
77
|
+
specify "should have sequence number" do
|
78
|
+
@packet.sequence_number.should == 1
|
79
|
+
end
|
80
|
+
specify "should have termination code" do
|
81
|
+
@packet.termination_code.should == "N"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context "the PatientInformationRecord from/to Liaison" do
|
86
|
+
setup do
|
87
|
+
@packet = Liaison::Packets::PatientInformationRecord
|
88
|
+
@example = "P|1||PatID01||Meyer^Anna||19741001|F|||||MARTINEZ"
|
89
|
+
end
|
90
|
+
|
91
|
+
has_header :packet, "P"
|
92
|
+
|
93
|
+
specify "should have a sequence number" do
|
94
|
+
@packet.from_str(@example).sequence_number.should == 1
|
95
|
+
end
|
96
|
+
|
97
|
+
specify "should have a patient ID" do
|
98
|
+
@packet.from_str(@example).patient_id.should == "PatID01"
|
99
|
+
end
|
100
|
+
specify "should have a patient name" do
|
101
|
+
@packet.from_str(@example).patient_last_name.should == "Meyer"
|
102
|
+
@packet.from_str(@example).patient_first_name.should == "Anna"
|
103
|
+
end
|
104
|
+
specify "should have a birthday" do
|
105
|
+
@packet.from_str(@example).birthdate.should == Date.new(1974,10,01)
|
106
|
+
end
|
107
|
+
specify "should have sex" do
|
108
|
+
@packet.from_str(@example).sex.should == "F"
|
109
|
+
end
|
110
|
+
specify "should have attending physician" do
|
111
|
+
@packet.from_str(@example).physician.should == "MARTINEZ"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
context "building a PatientInformationRecord" do
|
116
|
+
setup do
|
117
|
+
@record = Liaison::Packets::PatientInformationRecord
|
118
|
+
@packet = @record.new(1, "000204060", "Sierra", "Rudolph")
|
119
|
+
end
|
120
|
+
|
121
|
+
specify "should just work" do
|
122
|
+
@packet.to_s.should == "P|1||000204060||Sierra^Rudolph"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context "the RequestInformationSegment" do
|
127
|
+
setup do
|
128
|
+
@request_information = Liaison::Packets::RequestInformationSegment
|
129
|
+
@str = "Q|1|Sample01||ALL||||||||O"
|
130
|
+
@packet = @request_information.from_str(@str)
|
131
|
+
end
|
132
|
+
|
133
|
+
has_header :request_information, "Q"
|
134
|
+
|
135
|
+
specify "should have a sequence number" do
|
136
|
+
@packet.sequence_number.should == 1
|
137
|
+
end
|
138
|
+
|
139
|
+
specify "should have starting range id" do
|
140
|
+
@packet.starting_range.should == "Sample01"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
context "the TestOrderRecord from/to Liaison" do
|
145
|
+
setup do
|
146
|
+
@order_record = Liaison::Packets::TestOrderRecord
|
147
|
+
@example = "O|1|SampleID01||^^^AFP^1:10|N|19980506|||||||||S||||||||||X"
|
148
|
+
@packet = @order_record.from_str(@example)
|
149
|
+
end
|
150
|
+
|
151
|
+
has_header :order_record, "O"
|
152
|
+
|
153
|
+
specify "should have a sequence number" do
|
154
|
+
@packet.sequence_number.should == 1
|
155
|
+
end
|
156
|
+
specify "should have a specimen id" do
|
157
|
+
@packet.specimen_id.should == "SampleID01"
|
158
|
+
end
|
159
|
+
specify "should have a universal test id" do
|
160
|
+
@packet.test_id.should == "AFP"
|
161
|
+
end
|
162
|
+
specify "should have a dilution" do
|
163
|
+
@packet.dilution.should == "1:10"
|
164
|
+
end
|
165
|
+
specify "should have a priority" do
|
166
|
+
@packet.priority.should == "N"
|
167
|
+
end
|
168
|
+
specify "should have timestamp" do
|
169
|
+
@packet.timestamp.should == DateTime.new(1998,5,6,0,0,0)
|
170
|
+
end
|
171
|
+
specify "should have report type" do
|
172
|
+
@packet.record_type.should == "X"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
context "building a TestOrderRecord" do
|
177
|
+
setup do
|
178
|
+
@order_record = Liaison::Packets::TestOrderRecord
|
179
|
+
end
|
180
|
+
|
181
|
+
specify "should generate a correct packet" do
|
182
|
+
@packet = @order_record.new(17, "BarcodeID", "TSH")
|
183
|
+
@packet.to_s.should == "O|17|BarcodeID||^^^TSH"
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
context "the ResultRecord" do
|
188
|
+
setup do
|
189
|
+
@result_record = Liaison::Packets::ResultRecord
|
190
|
+
@example = "R|1|^^^AFP|0.20|IU/ml||<||F||||19980506123145|Liaison"
|
191
|
+
@packet = @result_record.from_str(@example)
|
192
|
+
end
|
193
|
+
|
194
|
+
has_header :result_record, "R"
|
195
|
+
|
196
|
+
specify "should have sequence number" do
|
197
|
+
@packet.sequence_number.should == 1
|
198
|
+
end
|
199
|
+
specify "should have test id" do
|
200
|
+
@packet.test_id.should == "AFP"
|
201
|
+
end
|
202
|
+
specify "should have value" do
|
203
|
+
@packet.value.should == "0.20"
|
204
|
+
end
|
205
|
+
specify "should have unit" do
|
206
|
+
@packet.unit.should == "IU/ml"
|
207
|
+
end
|
208
|
+
specify "should have abnormal flags" do
|
209
|
+
@packet.abnormal_flags.should == "<"
|
210
|
+
end
|
211
|
+
specify "should have result status" do
|
212
|
+
@packet.result_status.should == "F"
|
213
|
+
end
|
214
|
+
specify "should have timestamp" do
|
215
|
+
@packet.timestamp.should == DateTime.new(1998,05,06,12,31,45)
|
216
|
+
end
|
217
|
+
specify "should have instrument" do
|
218
|
+
@packet.instrument.should == "Liaison"
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
# TODO: empty result data should be allowed
|
224
|
+
|
225
|
+
context "the CommentRecord" do
|
226
|
+
setup do
|
227
|
+
@packet = Liaison::Packets::CommentRecord
|
228
|
+
end
|
229
|
+
has_header :packet, "C"
|
230
|
+
end
|
231
|
+
|
232
|
+
|
233
|
+
|
234
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
|
2
|
+
module Diasorin
|
3
|
+
module Liaison
|
4
|
+
class MockInterface < PacketIO
|
5
|
+
def initialize(read, write, options = {})
|
6
|
+
super(SerialProtocol, read, write, options)
|
7
|
+
|
8
|
+
add_sender( {
|
9
|
+
:init => Packets::MessageHeaderRecord,
|
10
|
+
:next_patient => Packets::NextPatient,
|
11
|
+
:result => Packets::Result,
|
12
|
+
:end => Packets::EndOfData
|
13
|
+
} )
|
14
|
+
|
15
|
+
add_receiver :end => Packets::EndOfData do |d|
|
16
|
+
self.end_of_list
|
17
|
+
end
|
18
|
+
|
19
|
+
add_receiver :next_result => Packets::NextResult do |d|
|
20
|
+
end
|
21
|
+
|
22
|
+
run
|
23
|
+
end
|
24
|
+
|
25
|
+
def init
|
26
|
+
send_packet :init
|
27
|
+
end
|
28
|
+
|
29
|
+
def result(pat_with_results)
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
def next_patient nr
|
34
|
+
send_packet :next_patient, nr
|
35
|
+
end
|
36
|
+
|
37
|
+
def end_of_list
|
38
|
+
send_packet :end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: levinalex-liaison_labor
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.8
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Levin Alexander
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-06-11 00:00:00 -07:00
|
13
|
+
default_executable: liaison_server
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: hoe
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.12.1
|
24
|
+
version:
|
25
|
+
description: "An interface to the LIAISON\xC2\xAE analyser by DiaSorin <http://www.diasorin.com/en/productsandsystems/liaison>"
|
26
|
+
email:
|
27
|
+
- mail@levinalex.net
|
28
|
+
executables:
|
29
|
+
- liaison_server
|
30
|
+
extensions: []
|
31
|
+
|
32
|
+
extra_rdoc_files:
|
33
|
+
- History.txt
|
34
|
+
- Manifest.txt
|
35
|
+
- README.txt
|
36
|
+
files:
|
37
|
+
- History.txt
|
38
|
+
- Manifest.txt
|
39
|
+
- README.txt
|
40
|
+
- Rakefile
|
41
|
+
- bin/liaison_server
|
42
|
+
- liaison_labor.gemspec
|
43
|
+
- lib/liaison_labor.rb
|
44
|
+
- lib/liaison_labor/interface.rb
|
45
|
+
- lib/liaison_labor/packets.rb
|
46
|
+
- lib/liaison_server.rb
|
47
|
+
- spec/liaison_interface_spec.rb
|
48
|
+
- spec/liaison_packet_spec.rb
|
49
|
+
- spec/mock/mock_liaison.rb
|
50
|
+
has_rdoc: true
|
51
|
+
homepage: http://levinalex.net/src/liaison
|
52
|
+
post_install_message:
|
53
|
+
rdoc_options:
|
54
|
+
- --main
|
55
|
+
- README.txt
|
56
|
+
require_paths:
|
57
|
+
- lib
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: "0"
|
63
|
+
version:
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: "0"
|
69
|
+
version:
|
70
|
+
requirements: []
|
71
|
+
|
72
|
+
rubyforge_project: liaison_labor
|
73
|
+
rubygems_version: 1.2.0
|
74
|
+
signing_key:
|
75
|
+
specification_version: 2
|
76
|
+
summary: interfaces Liaison device to worklist_manager
|
77
|
+
test_files: []
|
78
|
+
|