rs_232 2.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,358 @@
1
+ /*
2
+ * Copyright (c) 2013, Ingenico Inc.
3
+ *
4
+ * Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted,
5
+ * provided that the above copyright notice and this permission notice appear in all copies.
6
+ *
7
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
8
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
9
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
10
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
11
+ * PERFORMANCE OF THIS SOFTWARE.
12
+ *
13
+ **/
14
+
15
+ #include "port.h"
16
+
17
+
18
+ VALUE setDtrIO(VALUE self, VALUE rb_int)
19
+ {
20
+ {
21
+ Check_Type(rb_int, T_FIXNUM);
22
+ }
23
+
24
+ int boolean = FIX2INT(rb_int);
25
+
26
+ PortDescriptor *port = NULL;
27
+
28
+ Data_Get_Struct(self, PortDescriptor, port);
29
+
30
+ return INT2FIX( EscapeCommFunction(port->fd, boolean == 1 ? SETDTR : CLRDTR) );
31
+ }
32
+
33
+
34
+ VALUE setRtsIO(VALUE self, VALUE rb_int)
35
+ {
36
+ {
37
+ Check_Type(rb_int, T_FIXNUM);
38
+ }
39
+
40
+ int boolean = FIX2INT(rb_int);
41
+
42
+ PortDescriptor *port = NULL;
43
+
44
+ Data_Get_Struct(self, PortDescriptor, port);
45
+
46
+ return INT2FIX( EscapeCommFunction(port->fd, boolean == 1 ? SETRTS : CLRRTS) );
47
+ }
48
+
49
+
50
+ VALUE lineStatusIO(VALUE self)
51
+ {
52
+
53
+ PortDescriptor *port = NULL;
54
+
55
+ Data_Get_Struct(self, PortDescriptor, port);
56
+
57
+ unsigned long Status = 0, Temp = 0;
58
+
59
+ GetCommModemStatus(port->fd, &Temp);
60
+
61
+ if (Temp & MS_CTS_ON) Status |= LS_CTS;
62
+ if (Temp & MS_DSR_ON) Status |= LS_DSR;
63
+ if (Temp & MS_RING_ON) Status |= LS_RI;
64
+ if (Temp & MS_RLSD_ON) Status |= LS_DCD;
65
+
66
+ return LONG2FIX(Status);
67
+ }
68
+
69
+
70
+ static void platformInitIO(PortDescriptor *port)
71
+ {
72
+ port->fd = INVALID_HANDLE_VALUE;
73
+ port->status = 1;
74
+ }
75
+
76
+
77
+ void updateSettings(PortDescriptor *port)
78
+ {
79
+
80
+ if (port->status != 0)
81
+ rb_raise(rb_eException, "Can not set due to comport is not open, status: %d\n", port->status);
82
+
83
+
84
+ if (port->toBeUpdated & T_BaudRate)
85
+ port->commConfig.dcb.BaudRate = port->settings.BaudRate;
86
+
87
+
88
+ if (port->toBeUpdated & T_Parity)
89
+ {
90
+ port->commConfig.dcb.Parity = (BYTE) port->settings.Parity;
91
+ port->commConfig.dcb.fParity = (port->settings.Parity == PAR_NONE) ? 0 : 1;
92
+ }
93
+
94
+
95
+ if (port->toBeUpdated & T_DataBits)
96
+ {
97
+ port->commConfig.dcb.ByteSize = (BYTE) port->settings.DataBits;
98
+ }
99
+
100
+
101
+ if (port->toBeUpdated & T_StopBits)
102
+ {
103
+ switch (port->settings.StopBits)
104
+ {
105
+ case STOP_1:
106
+ port->commConfig.dcb.StopBits = ONESTOPBIT;
107
+ break;
108
+ case STOP_2:
109
+ port->commConfig.dcb.StopBits = TWOSTOPBITS;
110
+ break;
111
+ }
112
+ }
113
+
114
+ if (port->toBeUpdated & T_Flow)
115
+ {
116
+ switch (port->settings.FlowControl)
117
+ {
118
+ case FLOW_OFF:
119
+ port->commConfig.dcb.fOutxCtsFlow = 0;
120
+ port->commConfig.dcb.fRtsControl = RTS_CONTROL_DISABLE;
121
+ port->commConfig.dcb.fInX = 0;
122
+ port->commConfig.dcb.fOutX = 0;
123
+ break;
124
+ case FLOW_XONXOFF:
125
+ port->commConfig.dcb.fOutxCtsFlow = 0;
126
+ port->commConfig.dcb.fRtsControl = RTS_CONTROL_DISABLE;
127
+ port->commConfig.dcb.fInX = 1;
128
+ port->commConfig.dcb.fOutX = 1;
129
+ break;
130
+ case FLOW_HARDWARE:
131
+ port->commConfig.dcb.fOutxCtsFlow = 1;
132
+ port->commConfig.dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
133
+ port->commConfig.dcb.fInX = 0;
134
+ port->commConfig.dcb.fOutX = 0;
135
+ break;
136
+ }
137
+ }
138
+
139
+ if (port->toBeUpdated & T_TimeOut)
140
+ {
141
+ int millisec = port->settings.Timeout_Millisec;
142
+
143
+ if (millisec == -1)
144
+ {
145
+ port->commTimeouts.ReadIntervalTimeout = MAXDWORD;
146
+ port->commTimeouts.ReadTotalTimeoutConstant = 0;
147
+ } else
148
+ {
149
+ port->commTimeouts.ReadIntervalTimeout = MAXDWORD;
150
+ port->commTimeouts.ReadTotalTimeoutConstant = millisec;
151
+ }
152
+ port->commTimeouts.ReadTotalTimeoutMultiplier = 0;
153
+ port->commTimeouts.WriteTotalTimeoutMultiplier = millisec;
154
+ port->commTimeouts.WriteTotalTimeoutConstant = 0;
155
+ }
156
+
157
+
158
+ if (port->toBeUpdated & T_SettingsDone)
159
+ SetCommConfig(port->fd, &port->commConfig, sizeof(COMMCONFIG));
160
+
161
+ if ((port->toBeUpdated & T_TimeOut))
162
+ SetCommTimeouts(port->fd, &port->commTimeouts);
163
+
164
+ port->toBeUpdated = 0;
165
+ }
166
+
167
+
168
+ static int queryStatusIO(VALUE self)
169
+ {
170
+ PortDescriptor *port = NULL;
171
+
172
+ Data_Get_Struct(self, PortDescriptor, port);
173
+
174
+ return port->status;
175
+ }
176
+
177
+
178
+ VALUE isClosedIO(VALUE self)
179
+ {
180
+ return queryStatusIO(self) != 0 ? Qtrue : Qfalse;
181
+ }
182
+
183
+
184
+ VALUE flushIO(VALUE self)
185
+ {
186
+ PortDescriptor *port = NULL;
187
+
188
+ Data_Get_Struct(self, PortDescriptor, port);
189
+
190
+ return (INT2FIX(FlushFileBuffers(port->fd)));
191
+ }
192
+
193
+
194
+ VALUE bytesAvailableIO(VALUE self)
195
+ {
196
+ PortDescriptor *port = NULL;
197
+
198
+ Data_Get_Struct(self, PortDescriptor, port);
199
+
200
+ DWORD Errors;
201
+ COMSTAT Status;
202
+ if (ClearCommError(port->fd, &Errors, &Status))
203
+ {
204
+ return INT2FIX(Status.cbInQue);
205
+ }
206
+ return INT2FIX(0);
207
+ }
208
+
209
+
210
+ VALUE openIO(VALUE self)
211
+ {
212
+
213
+ PortDescriptor *port = NULL;
214
+
215
+ Data_Get_Struct(self, PortDescriptor, port);
216
+
217
+ if (port->status == 0)
218
+ return INT2FIX(port->status);
219
+
220
+ DWORD conf_length = sizeof(COMMCONFIG);
221
+ port->commConfig.dwSize = conf_length;
222
+ DWORD threading = 0;
223
+
224
+ port->fd = CreateFileA(port->settings.ComPort,
225
+ GENERIC_READ | GENERIC_WRITE,
226
+ 0,
227
+ NULL,
228
+ OPEN_EXISTING,
229
+ threading,
230
+ NULL);
231
+
232
+ if (port->fd == INVALID_HANDLE_VALUE)
233
+ {
234
+
235
+ port->status = 1;
236
+ rb_raise(rb_eException, "Unable to open comport: %s\n", port->settings.ComPort);
237
+
238
+ } else
239
+ {
240
+
241
+ port->status = 0;
242
+ rb_iv_set(self, "@open", INT2FIX(port->status));
243
+
244
+ GetCommConfig(port->fd, &port->commConfig, &conf_length);
245
+ GetCommState(port->fd, &(port->commConfig.dcb));
246
+
247
+ port->commConfig.dcb.fBinary = 1;
248
+ port->commConfig.dcb.fInX = 0;
249
+ port->commConfig.dcb.fOutX = 0;
250
+ port->commConfig.dcb.fAbortOnError = 0;
251
+ port->commConfig.dcb.fNull = 0;
252
+
253
+ port->commConfig.dcb.fDtrControl = 1;
254
+
255
+ port->toBeUpdated = T_ALL;
256
+
257
+ setSettings(self);
258
+
259
+ }
260
+
261
+ return INT2FIX(port->status);
262
+ }
263
+
264
+
265
+ VALUE writeIO(VALUE self, VALUE message)
266
+ {
267
+
268
+ int recv;
269
+ int len;
270
+ PortDescriptor *port = NULL;
271
+
272
+ Data_Get_Struct(self, PortDescriptor, port);
273
+
274
+ Check_Type(message, T_STRING);
275
+
276
+ len = RSTRING_LEN(message);
277
+ char cStr[len];
278
+ strcpy(cStr, RSTRING_PTR(message));
279
+
280
+ if (!WriteFile(port->fd, cStr, len, (LPDWORD)((void *) &recv), NULL))
281
+ {
282
+ rb_raise(rb_eIOError, "IO: writing of the %d bytes has been failed", len);
283
+ }
284
+
285
+ return (INT2FIX(recv));
286
+
287
+ }
288
+
289
+
290
+ VALUE readIO(VALUE self, VALUE rb_int)
291
+ {
292
+
293
+ {
294
+ Check_Type(rb_int, T_FIXNUM);
295
+ }
296
+
297
+ PortDescriptor *port = NULL;
298
+
299
+ Data_Get_Struct(self, PortDescriptor, port);
300
+
301
+ int n;
302
+ int len = FIX2INT(rb_int);
303
+ char buf[len];
304
+
305
+ ReadFile(port->fd, &buf, len, (LPDWORD)((void *) &n), NULL);
306
+
307
+ if (n > 0)
308
+ return rb_str_new(buf, n);
309
+
310
+ return Qnil;
311
+ }
312
+
313
+
314
+ VALUE closeIO(VALUE self)
315
+ {
316
+
317
+ PortDescriptor *port = NULL;
318
+
319
+ Data_Get_Struct(self, PortDescriptor, port);
320
+
321
+ flushIO(self);
322
+ port->status = CloseHandle(port->fd);
323
+ port->fd = INVALID_HANDLE_VALUE;
324
+
325
+ rb_iv_set(self, "@open", INT2FIX(port->status));
326
+
327
+ return INT2FIX(port->status);
328
+
329
+ }
330
+
331
+
332
+ VALUE initializeStruct(VALUE self, VALUE portName)
333
+ {
334
+
335
+ {
336
+ Check_Type(portName, T_STRING);
337
+ }
338
+
339
+ PortDescriptor *port = NULL;
340
+
341
+ Data_Get_Struct(self, PortDescriptor, port);
342
+
343
+ port->settings.BaudRate = BAUD115200;
344
+ port->settings.Parity = PAR_NONE;
345
+ port->settings.FlowControl = FLOW_OFF;
346
+ port->settings.DataBits = DATA_8;
347
+ port->settings.StopBits = STOP_1;
348
+ port->settings.Timeout_Millisec = 0;
349
+
350
+ snprintf(port->settings.ComPort, sizeof(port->settings.ComPort) - 1, WIN_PATTERN, RSTRING_PTR(portName));
351
+
352
+ platformInitIO(port);
353
+
354
+ rb_iv_set(self, "@port", portName);
355
+
356
+ return self;
357
+ }
358
+
@@ -0,0 +1,53 @@
1
+ /*
2
+ * Copyright (c) 2013, Ingenico Inc.
3
+ *
4
+ * Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted,
5
+ * provided that the above copyright notice and this permission notice appear in all copies.
6
+ *
7
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
8
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
9
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
10
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
11
+ * PERFORMANCE OF THIS SOFTWARE.
12
+ *
13
+ **/
14
+
15
+ #ifndef rs_232_port_h____FILEEXTENSION___
16
+ #define rs_232_port_h____FILEEXTENSION___
17
+
18
+ # include <ruby.h>
19
+ # include <ruby/io.h>
20
+ # include <windows.h>
21
+ # include <fcntl.h>
22
+ # include <io.h>
23
+ # include <stdio.h>
24
+ # include <string.h>
25
+ # include "structs.h"
26
+
27
+ #define WIN_PATTERN "\\\\.\\%s"
28
+
29
+ void setBaudRate(VALUE, VALUE);
30
+
31
+ VALUE getBaudRate(VALUE);
32
+
33
+ void setParity(VALUE, VALUE);
34
+
35
+ VALUE getParity(VALUE);
36
+
37
+ void setDataBits(VALUE, VALUE);
38
+
39
+ VALUE getDataBits(VALUE);
40
+
41
+ void setStopBits(VALUE, VALUE);
42
+
43
+ VALUE getStopBits(VALUE);
44
+
45
+ void setFlowControl(VALUE, VALUE);
46
+
47
+ VALUE getFlowControl(VALUE);
48
+
49
+ void setTimeout(VALUE, VALUE);
50
+
51
+ void setSettings(VALUE);
52
+
53
+ #endif
@@ -0,0 +1,2 @@
1
+ SERIAL_PORT: COM20
2
+ DEBUG: 0
@@ -0,0 +1,8 @@
1
+ Feature: Connection
2
+
3
+ Scenario: Should have an ability to check open or not
4
+
5
+
6
+ * connection instance "should" be available
7
+ * I "close" connection
8
+ * connection instance "should not" be available
@@ -0,0 +1,33 @@
1
+ When /^connection instance "(should|should not)" be available$/ do |matcher|
2
+
3
+ action = matcher == 'should' ? :should : :should_not
4
+
5
+ if respond_to? :should
6
+ adapter.open?.send(action, be_true)
7
+ else
8
+ assert_equal(adapter.open?, action == :should)
9
+ end
10
+
11
+ end
12
+
13
+ Given /^I "(open|close)" connection$/ do |action|
14
+
15
+ adapter.send(action)
16
+
17
+ if action == 'close'
18
+ if respond_to? :should
19
+ adapter.open?.should be_false
20
+ else
21
+ assert_equal(connection.open?, false)
22
+ end
23
+ else
24
+ if respond_to? :should
25
+ adapter.open?.should be_true
26
+ else
27
+ assert_equal(connection.open?, true)
28
+ end
29
+ end
30
+
31
+ end
32
+
33
+
@@ -0,0 +1,131 @@
1
+ class Adapter::Dev < Adapter::Generic
2
+ attr_reader :interface
3
+ include CommPort
4
+
5
+ # == constructor with default params
6
+ # by default port will be configured with:
7
+ #
8
+ # @baud_rate = 115200 # BAUD_115200
9
+ # @data_bits = 8 # DATA_BITS_8
10
+ # @parity = 0 # PAR_NONE
11
+ # @stop_bits = 1 # STOP_1
12
+ # @flow_control = 0 # FLOW_OFF
13
+ #
14
+ #
15
+ def initialize(port, &block)
16
+ @interface ||= Rs232.new(port)
17
+ connect
18
+ $stdout.puts "*** Rs232 instance has been initialized. Build v#{CommPort::VERSION}"
19
+ super(&block)
20
+ end
21
+
22
+ # Open and configure interface
23
+ #
24
+ # @return [Bool]
25
+ #
26
+ def connect
27
+ @interface.open
28
+ # custom configuration should be there if required
29
+ # @interface.baud_rate = BAUD_115200
30
+ # @interface.data_bits = DATA_BITS_8
31
+ # @interface.parity = PAR_NONE
32
+ # @interface.stop_bits = STOP_1
33
+ # @interface.flow_control = FLOW_OFF
34
+ @open = open?
35
+ end
36
+
37
+ # == Write function implementation
38
+ #
39
+ # @param [String] bytes
40
+ # @return [Int]
41
+ #
42
+ def write(bytes)
43
+ @interface.write(bytes)
44
+ end
45
+
46
+ # == Closing interface and freeing structures
47
+ #
48
+ # @return [Bool]
49
+ #
50
+ def close
51
+ @interface.flush
52
+ @interface.close
53
+ @open = open?
54
+ !open?
55
+ end
56
+
57
+ # == Flashing buffer function
58
+ #
59
+ def flush
60
+ @interface.flush
61
+ end
62
+
63
+ # @return [Bool]
64
+ #
65
+ def open?
66
+ @interface && !@interface.closed?
67
+ end
68
+
69
+ # == read() implementation example
70
+ #
71
+ # @param +count+ [Int]
72
+ # @param +blocking+ [Bool]
73
+ #
74
+ # @return [String]
75
+ #
76
+ # === Alternative implementation:
77
+ # @usage:
78
+ #
79
+ # +timeout+ = blocking_value ? 15000 : 0
80
+ # +@interface.timeout+ = +timeout+
81
+ # +@interface.read( +count+ )+
82
+ #
83
+ def read(count, blocking = false)
84
+ array = []
85
+
86
+ bytes_count = (count == -1) ? @interface.available? : count
87
+
88
+ if blocking
89
+ bytes = read_io_until(count, count)
90
+ array.push bytes if bytes
91
+ else
92
+ bytes_count.times do
93
+ byte = @interface.read(1)
94
+ array.push byte if byte
95
+ end
96
+ end
97
+ array.empty? ? nil : array.join
98
+ end
99
+
100
+ private
101
+
102
+ # == simulate blocking function
103
+ #
104
+ # @param +count+ [Int]
105
+ # @param +up_to+ [Int]
106
+ #
107
+ # no direct ruby usage
108
+ #
109
+ def block_io_until(count, up_to)
110
+ while @interface.available? < count && up_to > 0
111
+ up_to -= 1
112
+ end
113
+ up_to > 0
114
+ end
115
+
116
+ # == simulate blocking function
117
+ #
118
+ # @param +count+ [Int]
119
+ # @param +up_to+ [Int]
120
+ #
121
+ # no direct ruby usage
122
+ #
123
+ def read_io_until(count, up_to)
124
+ until block_io_until(count, up_to)
125
+ sleep 0.001
126
+ end
127
+ read(count)
128
+ end
129
+
130
+ end
131
+
@@ -0,0 +1,64 @@
1
+ require 'monitor'
2
+
3
+ module Adapter
4
+
5
+ class Generic
6
+ include MonitorMixin
7
+ include RsLogger
8
+
9
+ attr_accessor :event
10
+
11
+ def initialize(*args, &block)
12
+ Thread.abort_on_exception = true
13
+ unless instance_variables.include?(:@rxd)
14
+ @rxd = true
15
+ end
16
+ @event = block
17
+ super(*args)
18
+ run
19
+ end
20
+
21
+ def reading_allowed?
22
+ @rxd
23
+ end
24
+
25
+ def defer_reading
26
+ @rxd = false
27
+ !reading_allowed?
28
+ end
29
+
30
+ def allow_reading
31
+ @rxd = true
32
+ end
33
+
34
+ def rx(int, blocking = false)
35
+ byte = read(int, blocking)
36
+ if byte
37
+ logger.debug "RX [#{byte.length}]: #{byte.inspect}"
38
+ end
39
+ byte
40
+ end
41
+
42
+ def tx(bytes)
43
+ int = write(bytes)
44
+ logger.debug "TX [#{int}]: #{bytes.inspect}"
45
+ int
46
+ end
47
+
48
+ def restart_rx_thread
49
+ allow_reading
50
+ run
51
+ end
52
+
53
+ def run
54
+ Thread.new do
55
+ loop do
56
+ rx(1, false) if reading_allowed?
57
+ sleep(0.005)
58
+ end
59
+ end
60
+ end
61
+
62
+ end
63
+
64
+ end
@@ -0,0 +1,36 @@
1
+ require "logger"
2
+
3
+ module Adapter::RsLogger
4
+
5
+ class LogFormatter < ::Logger::Formatter
6
+
7
+ def call(severity, time, progname, msg)
8
+ "#{format_datetime(time)}[TID:#{$$}] [Rs232::#{severity}]: #{msg}\n"
9
+ end
10
+
11
+
12
+ private
13
+
14
+ def format_datetime(time)
15
+ if @datetime_format.nil?
16
+ time.strftime("%Y-%m-%d %H:%M:%S.") << "%06d " % time.usec
17
+ else
18
+ time.strftime(@datetime_format)
19
+ end
20
+ end
21
+
22
+ end
23
+
24
+ def logger
25
+ @logger ||= begin
26
+ logger = ::Logger.new( ENV["LOG"]||$stdout )
27
+ logger.level = ENV["DEBUG"].to_i
28
+ logger.formatter= LogFormatter.new
29
+ logger
30
+ end
31
+ end
32
+
33
+ module_function :logger
34
+
35
+ end
36
+
@@ -0,0 +1,5 @@
1
+ module Adapter
2
+ autoload :RsLogger, File.expand_path("../adapter/rs_logger", __FILE__)
3
+ autoload :Generic, File.expand_path("../adapter/generic", __FILE__)
4
+ autoload :Dev, File.expand_path("../adapter/dev", __FILE__)
5
+ end
@@ -0,0 +1,5 @@
1
+ After do
2
+ adapter.close
3
+ logger.debug "Closed..."
4
+ end
5
+