rs_232 2.0.4
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.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +64 -0
- data/Rakefile +47 -0
- data/bin/env.rb +9 -0
- data/cucumber.yml +24 -0
- data/ext/rs_232/constants.c +51 -0
- data/ext/rs_232/constants.h +98 -0
- data/ext/rs_232/extconf.rb +102 -0
- data/ext/rs_232/initializer.c +399 -0
- data/ext/rs_232/initializer.h +54 -0
- data/ext/rs_232/posix/port.c +429 -0
- data/ext/rs_232/posix/port.h +53 -0
- data/ext/rs_232/structs.h +62 -0
- data/ext/rs_232/windows/port.c +358 -0
- data/ext/rs_232/windows/port.h +53 -0
- data/features/config/config.yml +2 -0
- data/features/connection.feature +8 -0
- data/features/step_definitions/connection_steps.rb +33 -0
- data/features/support/adapter/dev.rb +131 -0
- data/features/support/adapter/generic.rb +64 -0
- data/features/support/adapter/rs_logger.rb +36 -0
- data/features/support/adapter.rb +5 -0
- data/features/support/after.rb +5 -0
- data/features/support/env.rb +100 -0
- data/gem_tasks/cov.rake +5 -0
- data/gem_tasks/cucumber.rake +23 -0
- data/gem_tasks/doc.rake +12 -0
- data/gem_tasks/environment.rake +7 -0
- data/gem_tasks/rspec.rake +6 -0
- data/lib/rs_232/version.rb +8 -0
- data/lib/rs_232.rb +145 -0
- data/rs_232.gemspec +40 -0
- data/spec/simplecov_setup.rb +15 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/support/fixtures.rb +23 -0
- metadata +198 -0
@@ -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,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
|
+
|