diff --git a/.gitignore b/.gitignore index b982d06..46c661e 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,7 @@ lib/*.jar .cproject atlassian-ide-plugin.xml rspec.xml +ruby-rs-232.xcodeproj +script/ruby +/vendor +ext/rs_232/Makefile diff --git a/LICENSE.txt b/LICENSE.txt index 30e6d02..7414396 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2013, Ingenico Inc. +Copyright (c) 2013, Roman Lishtaba. MIT License @@ -16,7 +16,7 @@ included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +NONINFINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md index 2e32050..0f6b102 100644 --- a/README.md +++ b/README.md @@ -20,105 +20,26 @@ Or install it yourself as: ## Usage -See `examples` folder for details. +You may use this gem directly as a low-level transport layer in your communication module. As an alternative you may check another gem called (ruby-digital-transport)[https://github.com/rlishtaba/ruby-digital-transport] which is kind of transport abstraction porviding unified interface to SerialPort (using this gem), TCP and USB-HID. ```ruby - class SerialPortAdapter - include CommPort - - attr_reader :interface - private :interface - - # constructor with default params - # by default port will be configured with: - # - # baud_rate = 115200 # BAUD_115200 - # data_bits = 8 # DATA_BITS_8 - # parity = 0 # PAR_NONE - # stop_bits = 1 # STOP_BITS_1 - # flow_control = 0 # FLOW_OFF - # - # see other public constants on CommPort namespace - # - def initialize(port, options = {}) - @port = port - @options = options - @open = false - @interface = Rs232.new(port) - connect - end - - def connect - return if open? - can_configure_timeout = interface.respond_to?(:connecting_timeout) - interface.connecting_timeout = @options.fetch(:connecting_timeout, 60) if can_configure_timeout - interface.open - configure_interface! - @open = open? - end - - def write(bytes) - interface.write(bytes) - end - - def close - return unless open? - flush - interface.close - @open = open? - !open? - end - - def flush - interface.flush - end - - def open? - interface && !interface.closed? - end - - def recv(count) - array = [] - chunk = read_io_until(count, count) - array.push chunk if chunk - array.empty? ? nil : array.join - end - - def recv_nonblock(count) - array = [] - chunks_count = (count == -1) ? interface.available? : count - chunks_count.times do - chunk = interface.read(1) - array.push chunk if chunk - end - array.empty? ? nil : array.join - end - - def read(count, blocking = false) - blocking ? recv(count) : recv_nonblock(count) - end - - private - - def configure_interface! - interface.baud_rate = @options.fetch(:baud_rate, BAUD_115200).to_i - interface.data_bits = @options.fetch(:data_bits, DATA_BITS_8).to_i - interface.parity = @options.fetch(:parity, PAR_NONE).to_i - interface.stop_bits = @options.fetch(:stop_bits, STOP_BITS_1).to_i - interface.flow_control = @options.fetch(:flow_control, FLOW_OFF).to_i - end - - def block_io_until(count, up_to) - up_to -= 1 while interface.available? < count && up_to > 0 - up_to > 0 - end - - def read_io_until(count, up_to) - sleep 0.001 until block_io_until(count, up_to) - read(count) - end - end - + > include Rs232 + > port = new_serial_port('/dev/tty.ACM0', baud_rate: 9600) + #=> # + > port.open? + #=> false + > port.connect # rasing IOError in case of port couldn't be opened. + #=> # + > port.pending_bytes + #=> 15 + > port.read(15) + #=> 'Hello, World!!!' + > port.write("Hi") + #=> 2 + > port.close + #=> true + > port.open? + #=> false ``` ## Contributing diff --git a/Rakefile b/Rakefile index 2a9fa32..3820a75 100755 --- a/Rakefile +++ b/Rakefile @@ -1,13 +1,35 @@ lib = File.expand_path '../lib', __FILE__ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include? lib + require 'bundler/setup' require 'bundler/gem_tasks' -require 'rake' -load 'tasks/clean.rake' -load 'tasks/cov.rake' -load 'tasks/rspec.rake' -load 'tasks/cucumber.rake' -load 'tasks/compile.rake' +require 'rake/extensiontask' +Rake::ExtensionTask.new('rs_232') do |ext| + ext.name = 'rs_232_native' + ext.source_pattern = '*.{c,cpp}' +end + +require 'rake/clean' +CLEAN.include %w(**/*.{log} doc coverage tmp pkg **/*.{o,so,bundle} Makefile) + +require 'simplecov' +task :cov do + ENV['SIMPLECOV'] = 'features' + Rake::Task['default'].invoke +end + +require 'cucumber/rake/task' +Cucumber::Rake::Task.new(:features) do |t| + t.fork = true + t.profile = :default +end + +require 'rspec/core/rake_task' +RSpec::Core::RakeTask.new do |t| + t.fail_on_error = false + t.verbose = true + t.rspec_opts = '--format RspecJunitFormatter --out rspec.xml --tag ~wip' +end -task default: [:clobber, :compile, :spec, :features] +task default: [:clobber, :compile, :spec] diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index 4f6a141..0000000 --- a/examples/README.md +++ /dev/null @@ -1,3 +0,0 @@ -### Examples - -Current folder contains potential idea how could you implement adapter in order to represent link layer. diff --git a/examples/serial_port_adapter.rb b/examples/serial_port_adapter.rb deleted file mode 100644 index 9f5ed02..0000000 --- a/examples/serial_port_adapter.rb +++ /dev/null @@ -1,97 +0,0 @@ -require 'rs_232' - -class SerialPortAdapter - include CommPort - - attr_reader :interface - private :interface - - # constructor with default params - # by default port will be configured with: - # - # baud_rate = 115200 # BAUD_115200 - # data_bits = 8 # DATA_BITS_8 - # parity = 0 # PAR_NONE - # stop_bits = 1 # STOP_BITS_1 - # flow_control = 0 # FLOW_OFF - # - # see other public constants on CommPort namespace - # - def initialize(port, options = {}) - @port = port - @options = options - @open = false - @interface = Rs232.new(port) - connect - end - - def connect - return if open? - can_configure_timeout = interface.respond_to?(:connecting_timeout) - interface.connecting_timeout = @options.fetch(:connecting_timeout, 60) if can_configure_timeout - interface.open - configure_interface! - @open = open? - end - - def write(bytes) - interface.write(bytes) - end - - def close - return unless open? - flush - interface.close - @open = open? - !open? - end - - def flush - interface.flush - end - - def open? - interface && !interface.closed? - end - - def recv(count) - array = [] - chunk = read_io_until(count, count) - array.push chunk if chunk - array.empty? ? nil : array.join - end - - def recv_nonblock(count) - array = [] - chunks_count = (count == -1) ? interface.available? : count - chunks_count.times do - chunk = interface.read(1) - array.push chunk if chunk - end - array.empty? ? nil : array.join - end - - def read(count, blocking = false) - blocking ? recv(count) : recv_nonblock(count) - end - - private - - def configure_interface! - interface.baud_rate = @options.fetch(:baud_rate, BAUD_115200).to_i - interface.data_bits = @options.fetch(:data_bits, DATA_BITS_8).to_i - interface.parity = @options.fetch(:parity, PAR_NONE).to_i - interface.stop_bits = @options.fetch(:stop_bits, STOP_BITS_1).to_i - interface.flow_control = @options.fetch(:flow_control, FLOW_OFF).to_i - end - - def block_io_until(count, up_to) - up_to -= 1 while interface.available? < count && up_to > 0 - up_to > 0 - end - - def read_io_until(count, up_to) - sleep 0.001 until block_io_until(count, up_to) - read(count) - end -end diff --git a/ext/rs_232/Constants.c b/ext/rs_232/Constants.c new file mode 100644 index 0000000..a8f2f7b --- /dev/null +++ b/ext/rs_232/Constants.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2013, Roman Lishtaba. + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, + * provided that the above copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + **/ + +/* + * @author Roman Lishtaba + */ + +#include "Constants.h" + +void Constants_Init(VALUE namespace) +{ + rb_define_const(namespace, "BAUD_110", INT2FIX(BAUD110)); + rb_define_const(namespace, "BAUD_300", INT2FIX(BAUD300)); + rb_define_const(namespace, "BAUD_600", INT2FIX(BAUD600)); + rb_define_const(namespace, "BAUD_1200", INT2FIX(BAUD1200)); + rb_define_const(namespace, "BAUD_2400", INT2FIX(BAUD2400)); + rb_define_const(namespace, "BAUD_4800", INT2FIX(BAUD4800)); + rb_define_const(namespace, "BAUD_9600", INT2FIX(BAUD9600)); + rb_define_const(namespace, "BAUD_19200", INT2FIX(BAUD19200)); + rb_define_const(namespace, "BAUD_38400", INT2FIX(BAUD38400)); + rb_define_const(namespace, "BAUD_57600", INT2FIX(BAUD57600)); + rb_define_const(namespace, "BAUD_115200", INT2FIX(BAUD115200)); + + rb_define_const(namespace, "DATA_BITS_5", INT2FIX(DATA_5)); + rb_define_const(namespace, "DATA_BITS_6", INT2FIX(DATA_6)); + rb_define_const(namespace, "DATA_BITS_7", INT2FIX(DATA_7)); + rb_define_const(namespace, "DATA_BITS_8", INT2FIX(DATA_8)); + + rb_define_const(namespace, "PAR_NONE", INT2FIX(PAR_NONE)); + rb_define_const(namespace, "PAR_ODD", INT2FIX(PAR_ODD)); + rb_define_const(namespace, "PAR_EVEN", INT2FIX(PAR_EVEN)); + + rb_define_const(namespace, "STOP_BITS_1", INT2FIX(STOP_1)); + rb_define_const(namespace, "STOP_BITS_3", INT2FIX(STOP_2)); + + rb_define_const(namespace, "FLOW_OFF", INT2FIX(FLOW_OFF)); + rb_define_const(namespace, "FLOW_HARDWARE", INT2FIX(FLOW_HARDWARE)); + rb_define_const(namespace, "FLOW_XONXOFF", INT2FIX(FLOW_XONXOFF)); +} diff --git a/ext/rs_232/Constants.h b/ext/rs_232/Constants.h new file mode 100644 index 0000000..1eb4018 --- /dev/null +++ b/ext/rs_232/Constants.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2013, Roman Lishtaba. + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, + * provided that the above copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + **/ + +/* + * @author Roman Lishtaba + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define LS_CTS 0x01 +#define LS_DSR 0x02 +#define LS_DCD 0x04 +#define LS_RI 0x08 +#define LS_RTS 0x10 +#define LS_DTR 0x20 +#define LS_ST 0x40 +#define LS_SR 0x80 + + enum { PORT_OPEN, PORT_CLOSED }; + + enum BaudRateType + { + BAUD110 = 110, + BAUD300 = 300, + BAUD600 = 600, + BAUD1200 = 1200, + BAUD2400 = 2400, + BAUD4800 = 4800, + BAUD9600 = 9600, + BAUD19200 = 19200, + BAUD38400 = 38400, + BAUD57600 = 57600, + BAUD115200 = 115200 + }; + + enum DataBitsType + { + DATA_5 = 5, + DATA_6, + DATA_7, + DATA_8 + }; + + enum ParityType + { + PAR_NONE, + PAR_ODD, + PAR_EVEN, + }; + + enum StopBitsType + { + STOP_1 = 1, + STOP_2 + }; + + enum FlowType + { + FLOW_OFF, + FLOW_HARDWARE, + FLOW_XONXOFF + }; + + enum SettingsFlags + { + T_BaudRate = 0x0001, + T_Parity = 0x0002, + T_StopBits = 0x0004, + T_DataBits = 0x0008, + T_Flow = 0x0010, + T_TimeOut = 0x0100, + T_ALL = 0x0fff, + T_SettingsDone = 0x00ff, + }; + + void Constants_Init(VALUE); + +#ifdef __cplusplus +} +#endif diff --git a/ext/rs_232/initializer.c b/ext/rs_232/Rs232.c similarity index 68% rename from ext/rs_232/initializer.c rename to ext/rs_232/Rs232.c index 157ee22..a9114da 100644 --- a/ext/rs_232/initializer.c +++ b/ext/rs_232/Rs232.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Ingenico Inc. + * Copyright (c) 2013, Roman Lishtaba. * * Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, * provided that the above copyright notice and this permission notice appear in all copies. @@ -10,45 +10,44 @@ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. * - **/ + */ -#include "initializer.h" +/* + * @author Roman Lishtaba + */ + +#include "Rs232.h" static void destructor(PortDescriptor *port) { xfree(port); } - static void markDescriptor(PortDescriptor *port) { + //noop } static VALUE allocateStruct(VALUE self) { - PortDescriptor *port; - + PortDescriptor *port = NULL; rb_iv_set(self, "@port", Qnil); - + return Data_Make_Struct(self, PortDescriptor, markDescriptor, destructor, port); } void setBaudRate(VALUE self, VALUE int_baudRate) { + Check_Type(int_baudRate, T_FIXNUM); + PortDescriptor *port; - - { - Check_Type(int_baudRate, T_FIXNUM); - } - int baudRate = FIX2INT(int_baudRate); - - Data_Get_Struct(self, PortDescriptor, port); - int baud = 110; - + + Data_Get_Struct(self, PortDescriptor, port); + switch (baudRate) { case BAUD110: @@ -85,10 +84,10 @@ void setBaudRate(VALUE self, VALUE int_baudRate) rb_raise(rb_eException, "BaudRate value does not match specification, current: %d", baudRate); break; } - + port->settings.BaudRate = (enum BaudRateType) baud; port->toBeUpdated |= T_BaudRate; - + updateSettings(port); } @@ -96,28 +95,21 @@ void setBaudRate(VALUE self, VALUE int_baudRate) VALUE getBaudRate(VALUE self) { PortDescriptor *port; - Data_Get_Struct(self, PortDescriptor, port); - + return INT2FIX(port->settings.BaudRate); } void setParity(VALUE self, VALUE int_parity) { - - { - Check_Type(int_parity, T_FIXNUM); - } - - int parity = FIX2INT(int_parity); - + Check_Type(int_parity, T_FIXNUM); + PortDescriptor *port; - Data_Get_Struct(self, PortDescriptor, port); - + int parity = FIX2INT(int_parity); enum ParityType parity_type = PAR_NONE; - + switch (parity) { case PAR_NONE: @@ -133,40 +125,31 @@ void setParity(VALUE self, VALUE int_parity) rb_raise(rb_eException, "Parity value does not match specification, current: %d", parity); break; } - + port->settings.Parity = parity_type; port->toBeUpdated |= T_Parity; - + updateSettings(port); } VALUE getParity(VALUE self) { - PortDescriptor *port; - Data_Get_Struct(self, PortDescriptor, port); - + return INT2FIX(port->settings.Parity); } void setDataBits(VALUE self, VALUE intDataBits) { - - { - Check_Type(intDataBits, T_FIXNUM); - } - - int dataBits = FIX2INT(intDataBits); - + Check_Type(intDataBits, T_FIXNUM); PortDescriptor *port; - Data_Get_Struct(self, PortDescriptor, port); - + int dataBits = FIX2INT(intDataBits); enum DataBitsType dataBitsType = DATA_8; - + switch (dataBits) { case DATA_5: @@ -185,40 +168,32 @@ void setDataBits(VALUE self, VALUE intDataBits) rb_raise(rb_eException, "DataBits value does not match specification, current: %d", dataBits); break; } - + port->settings.DataBits = dataBitsType; port->toBeUpdated |= T_DataBits; - + updateSettings(port); } VALUE getDataBits(VALUE self) { - PortDescriptor *port; - Data_Get_Struct(self, PortDescriptor, port); - + return INT2FIX(port->settings.DataBits); } void setStopBits(VALUE self, VALUE intStopBits) { - - { - Check_Type(intStopBits, T_FIXNUM); - } - - int stopBits = FIX2INT(intStopBits); - + Check_Type(intStopBits, T_FIXNUM); + PortDescriptor *port; - Data_Get_Struct(self, PortDescriptor, port); - + int stopBits = FIX2INT(intStopBits); enum StopBitsType stopBitsType = STOP_1; - + switch (stopBits) { case STOP_1: @@ -231,40 +206,31 @@ void setStopBits(VALUE self, VALUE intStopBits) rb_raise(rb_eException, "StopBits value does not match specification, current: %d", stopBits); break; } - + port->settings.StopBits = stopBitsType; port->toBeUpdated |= T_StopBits; - + updateSettings(port); } VALUE getStopBits(VALUE self) { - PortDescriptor *port; - Data_Get_Struct(self, PortDescriptor, port); - + return INT2FIX(port->settings.StopBits); } void setFlowControl(VALUE self, VALUE intFlow) { - - { - Check_Type(intFlow, T_FIXNUM); - } - - int flow = FIX2INT(intFlow); - + Check_Type(intFlow, T_FIXNUM); PortDescriptor *port; - Data_Get_Struct(self, PortDescriptor, port); - + int flow = FIX2INT(intFlow); enum FlowType flowType = FLOW_OFF; - + switch (flow) { case FLOW_OFF: @@ -280,119 +246,120 @@ void setFlowControl(VALUE self, VALUE intFlow) rb_raise(rb_eException, "FlowControl value does not match specification, current: %d", flow); break; } - + port->settings.FlowControl = flowType; port->toBeUpdated |= T_StopBits; - + updateSettings(port); } VALUE getFlowControl(VALUE self) { - PortDescriptor *port; - Data_Get_Struct(self, PortDescriptor, port); - + return INT2FIX(port->settings.FlowControl); } void setTimeout(VALUE self, VALUE rb_timeout) { - - { - Check_Type(rb_timeout, T_FIXNUM); - } - + Check_Type(rb_timeout, T_FIXNUM); PortDescriptor *port; - Data_Get_Struct(self, PortDescriptor, port); - long timeout = FIX2LONG(rb_timeout); - + port->settings.Timeout_Millisec = timeout; port->toBeUpdated |= T_TimeOut; - + updateSettings(port); } - +/* + * Get connection timeout + * + * @return [Fixnum] the timeout value + * @see Impl#timeout + */ VALUE getTimeout(VALUE self) { - PortDescriptor *port; - Data_Get_Struct(self, PortDescriptor, port); - + return INT2FIX(port->settings.Timeout_Millisec); } - void setSettings(VALUE self) { - PortDescriptor *port; - Data_Get_Struct(self, PortDescriptor, port); - + setBaudRate(self, INT2FIX(port->settings.BaudRate)); setParity(self, INT2FIX(port->settings.Parity)); setDataBits(self, INT2FIX(port->settings.DataBits)); setStopBits(self, INT2FIX(port->settings.StopBits)); setFlowControl(self, INT2FIX(port->settings.FlowControl)); setTimeout(self, LONG2FIX(port->settings.Timeout_Millisec)); - port->toBeUpdated = T_ALL; - + updateSettings(port); } - -void Init_rs_232(void) +void Init_rs_232_native(void) { - - VALUE root, rb_cPort; - - root = rb_define_module("CommPort"); - - Constants_Init(root); - - rb_cPort = rb_define_class_under(root, "Rs232", rb_cObject); - rb_define_alloc_func(rb_cPort, allocateStruct); - rb_define_method(rb_cPort, "initialize", (VALUE ( *)()) initializeStruct, 1); - - rb_define_method(rb_cPort, "baud_rate=", (VALUE ( *)()) setBaudRate, 1); - rb_define_method(rb_cPort, "baud_rate", (VALUE ( *)()) getBaudRate, 0); - - rb_define_method(rb_cPort, "parity=", (VALUE ( *)()) setParity, 1); - rb_define_method(rb_cPort, "parity", (VALUE ( *)()) getParity, 0); - - rb_define_method(rb_cPort, "data_bits=", (VALUE ( *)()) setDataBits, 1); - rb_define_method(rb_cPort, "data_bits", (VALUE ( *)()) getDataBits, 0); - - rb_define_method(rb_cPort, "stop_bits=", (VALUE ( *)()) setStopBits, 1); - rb_define_method(rb_cPort, "stop_bits", (VALUE ( *)()) getStopBits, 0); - - rb_define_method(rb_cPort, "flow_control=", (VALUE ( *)()) setFlowControl, 1); - rb_define_method(rb_cPort, "flow_control", (VALUE ( *)()) getFlowControl, 0); - - rb_define_method(rb_cPort, "timeout=", (VALUE ( *)()) setTimeout, 1); - rb_define_method(rb_cPort, "timeout", (VALUE ( *)()) getTimeout, 0); - - rb_define_method(rb_cPort, "open", (VALUE ( *)()) openIO, 0); - rb_define_method(rb_cPort, "close", (VALUE ( *)()) closeIO, 0); - rb_define_method(rb_cPort, "flush", (VALUE ( *)()) flushIO, 0); - rb_define_method(rb_cPort, "closed?", (VALUE ( *)()) isClosedIO, 0); - - - rb_define_method(rb_cPort, "write", (VALUE ( *)()) writeIO, 1); - rb_define_method(rb_cPort, "read", (VALUE ( *)()) readIO, 1); - rb_define_method(rb_cPort, "available?", (VALUE ( *)()) bytesAvailableIO, 0); - - rb_define_method(rb_cPort, "line_status", (VALUE ( *)()) lineStatusIO, 0); - rb_define_method(rb_cPort, "set_rts", (VALUE ( *)()) setRtsIO, 1); - rb_define_method(rb_cPort, "set_dtr", (VALUE ( *)()) setDtrIO, 1); - + const char rootModName[] = "Rs232"; + const char implClassName[] = "Native"; + const char constantsModName[] = "Constants"; + const char VERSION[] = "2.0.0"; + + // define root Rs232 module + const VALUE root = rb_define_module(rootModName); + + // define impl ruby class + const VALUE impl = rb_define_class_under(root, implClassName, rb_cObject); + + // define module Constants under Native + const VALUE constants = rb_define_module_under(root, constantsModName); + + // initialize constants in to module Native::Constants + Constants_Init(constants); + + // define native version constant + rb_define_const(impl, "NATIVE_VERSION", rb_str_new2(VERSION)); + + rb_define_alloc_func(impl, allocateStruct); + + rb_define_method(impl, "initialize", RUBY_METHOD_FUNC(initializeStruct), 1); + + rb_define_method(impl, "baud_rate=", RUBY_METHOD_FUNC(setBaudRate), 1); + rb_define_method(impl, "baud_rate", RUBY_METHOD_FUNC(getBaudRate), 0); + + rb_define_method(impl, "parity=", RUBY_METHOD_FUNC(setParity), 1); + rb_define_method(impl, "parity", RUBY_METHOD_FUNC(getParity), 0); + + rb_define_method(impl, "data_bits=", RUBY_METHOD_FUNC(setDataBits), 1); + rb_define_method(impl, "data_bits", RUBY_METHOD_FUNC(getDataBits), 0); + + rb_define_method(impl, "stop_bits=", RUBY_METHOD_FUNC(setStopBits), 1); + rb_define_method(impl, "stop_bits", RUBY_METHOD_FUNC(getStopBits), 0); + + rb_define_method(impl, "flow_control=", RUBY_METHOD_FUNC(setFlowControl), 1); + rb_define_method(impl, "flow_control", RUBY_METHOD_FUNC(getFlowControl), 0); + + rb_define_method(impl, "timeout=", RUBY_METHOD_FUNC(setTimeout), 1); + rb_define_method(impl, "timeout", RUBY_METHOD_FUNC(getTimeout), 0); + + rb_define_method(impl, "open", RUBY_METHOD_FUNC(openIO), 0); + rb_define_method(impl, "close", RUBY_METHOD_FUNC(closeIO), 0); + rb_define_method(impl, "flush", RUBY_METHOD_FUNC(flushIO), 0); + rb_define_method(impl, "open?", RUBY_METHOD_FUNC(isOpenIO), 0); + + rb_define_method(impl, "write", RUBY_METHOD_FUNC(writeIO), 1); + rb_define_method(impl, "read", RUBY_METHOD_FUNC(readIO), 1); + rb_define_method(impl, "available?", RUBY_METHOD_FUNC(bytesAvailableIO), 0); + + rb_define_method(impl, "line_status", RUBY_METHOD_FUNC(lineStatusIO), 0); + rb_define_method(impl, "set_rts", RUBY_METHOD_FUNC(setRtsIO), 1); + rb_define_method(impl, "set_dtr", RUBY_METHOD_FUNC(setDtrIO), 1); } diff --git a/ext/rs_232/initializer.h b/ext/rs_232/Rs232.h similarity index 54% rename from ext/rs_232/initializer.h rename to ext/rs_232/Rs232.h index 3d033f8..bb8e056 100644 --- a/ext/rs_232/initializer.h +++ b/ext/rs_232/Rs232.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Ingenico Inc. + * Copyright (c) 2013, Roman Lishtaba. * * Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, * provided that the above copyright notice and this permission notice appear in all copies. @@ -12,43 +12,52 @@ * **/ -#ifndef rs_232_initializer____FILEEXTENSION___ -#define rs_232_initializer____FILEEXTENSION___ - -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__) +/* + * @author Roman Lishtaba + */ -# include "windows/port.h" +#pragma once +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__) + +# include "windows/Port.h" + #else - -# include "posix/port.h" - + +# include "posix/Port.h" + #endif - -#include "structs.h" - -VALUE initializeStruct(VALUE, VALUE); - -void updateSettings(PortDescriptor *port); - -VALUE isClosedIO(VALUE); - -VALUE openIO(VALUE); - -VALUE closeIO(VALUE); - -VALUE bytesAvailableIO(VALUE); - -VALUE flushIO(VALUE); - -VALUE writeIO(VALUE, VALUE); - -VALUE readIO(VALUE, VALUE); - -VALUE lineStatusIO(VALUE); - -VALUE setRtsIO(VALUE, VALUE); - -VALUE setDtrIO(VALUE, VALUE); - + +#include "Structs.h" + + VALUE initializeStruct(VALUE, VALUE); + + void updateSettings(PortDescriptor *port); + + VALUE isOpenIO(VALUE); + + VALUE openIO(VALUE); + + VALUE closeIO(VALUE); + + VALUE bytesAvailableIO(VALUE); + + VALUE flushIO(VALUE); + + VALUE writeIO(VALUE, VALUE); + + VALUE readIO(VALUE, VALUE); + + VALUE lineStatusIO(VALUE); + + VALUE setRtsIO(VALUE, VALUE); + + VALUE setDtrIO(VALUE, VALUE); + +#ifdef __cplusplus +} #endif \ No newline at end of file diff --git a/ext/rs_232/Structs.h b/ext/rs_232/Structs.h new file mode 100644 index 0000000..28c3761 --- /dev/null +++ b/ext/rs_232/Structs.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2013, Roman Lishtaba. + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, + * provided that the above copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + **/ + +/* + * @author Roman Lishtaba + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "Constants.h" + + /* + * structure to contain port settings + */ + typedef struct PortSettings_T + { + char ComPort[40]; + enum BaudRateType BaudRate; + enum DataBitsType DataBits; + enum ParityType Parity; + enum StopBitsType StopBits; + enum FlowType FlowControl; + long Timeout_Millisec; + } PortSettings; + + + /* + * port descriptor structure + */ + typedef struct portDescriptor_T + { +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__) + HANDLE fd; + COMMCONFIG commConfig; + COMMTIMEOUTS commTimeouts; +#else + int fd; + struct termios posixConfig; + struct termios previousPosixConfig; +#endif + int status; + PortSettings settings; + int toBeUpdated; + } PortDescriptor; + +#ifdef __cplusplus +} +#endif diff --git a/ext/rs_232/constants.c b/ext/rs_232/constants.c deleted file mode 100644 index d727b4a..0000000 --- a/ext/rs_232/constants.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2013, Ingenico Inc. - * - * Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, - * provided that the above copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER - * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - * - **/ - - -#include "constants.h" - -void Constants_Init(VALUE root) -{ - rb_define_const(root, "BAUD_110", INT2FIX(BAUD110)); - rb_define_const(root, "BAUD_300", INT2FIX(BAUD300)); - rb_define_const(root, "BAUD_600", INT2FIX(BAUD600)); - rb_define_const(root, "BAUD_1200", INT2FIX(BAUD1200)); - rb_define_const(root, "BAUD_2400", INT2FIX(BAUD2400)); - rb_define_const(root, "BAUD_4800", INT2FIX(BAUD4800)); - rb_define_const(root, "BAUD_9600", INT2FIX(BAUD9600)); - rb_define_const(root, "BAUD_19200", INT2FIX(BAUD19200)); - rb_define_const(root, "BAUD_38400", INT2FIX(BAUD38400)); - rb_define_const(root, "BAUD_57600", INT2FIX(BAUD57600)); - rb_define_const(root, "BAUD_115200", INT2FIX(BAUD115200)); - - rb_define_const(root, "DATA_BITS_5", INT2FIX(DATA_5)); - rb_define_const(root, "DATA_BITS_6", INT2FIX(DATA_6)); - rb_define_const(root, "DATA_BITS_7", INT2FIX(DATA_7)); - rb_define_const(root, "DATA_BITS_8", INT2FIX(DATA_8)); - - rb_define_const(root, "PAR_NONE", INT2FIX(PAR_NONE)); - rb_define_const(root, "PAR_ODD", INT2FIX(PAR_ODD)); - rb_define_const(root, "PAR_EVEN", INT2FIX(PAR_EVEN)); - - rb_define_const(root, "STOP_BITS_1", INT2FIX(STOP_1)); - rb_define_const(root, "STOP_BITS_3", INT2FIX(STOP_2)); - - - rb_define_const(root, "FLOW_OFF", INT2FIX(FLOW_OFF)); - rb_define_const(root, "FLOW_HARDWARE", INT2FIX(FLOW_HARDWARE)); - rb_define_const(root, "FLOW_XONXOFF", INT2FIX(FLOW_XONXOFF)); - -} \ No newline at end of file diff --git a/ext/rs_232/constants.h b/ext/rs_232/constants.h deleted file mode 100644 index d872783..0000000 --- a/ext/rs_232/constants.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2013, Ingenico Inc. - * - * Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, - * provided that the above copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER - * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - * - **/ - -#ifndef constants____FILEEXTENSION___ -#define constants____FILEEXTENSION___ - -#include - - -#define LS_CTS 0x01 -#define LS_DSR 0x02 -#define LS_DCD 0x04 -#define LS_RI 0x08 -#define LS_RTS 0x10 -#define LS_DTR 0x20 -#define LS_ST 0x40 -#define LS_SR 0x80 - - -enum BaudRateType -{ - BAUD110 = 110, - BAUD300 = 300, - BAUD600 = 600, - BAUD1200 = 1200, - BAUD2400 = 2400, - BAUD4800 = 4800, - BAUD9600 = 9600, - BAUD19200 = 19200, - BAUD38400 = 38400, - BAUD57600 = 57600, - BAUD115200 = 115200 - -}; - - -enum DataBitsType -{ - DATA_5 = 5, - DATA_6, - DATA_7, - DATA_8 -}; - - -enum ParityType -{ - PAR_NONE, - PAR_ODD, - PAR_EVEN, -}; - - -enum StopBitsType -{ - STOP_1 = 1, - STOP_2 -}; - - -enum FlowType -{ - FLOW_OFF, - FLOW_HARDWARE, - FLOW_XONXOFF -}; - - -enum SettingsFlags -{ - T_BaudRate = 0x0001, - T_Parity = 0x0002, - T_StopBits = 0x0004, - T_DataBits = 0x0008, - T_Flow = 0x0010, - T_TimeOut = 0x0100, - T_ALL = 0x0fff, - T_SettingsDone = 0x00ff, -}; - - -void Constants_Init(VALUE); - -#endif diff --git a/ext/rs_232/extconf.rb b/ext/rs_232/extconf.rb index 3d13545..2acf52d 100644 --- a/ext/rs_232/extconf.rb +++ b/ext/rs_232/extconf.rb @@ -1,31 +1,34 @@ require 'mkmf' require 'rbconfig' -if ENV['DEBUG_C'] +EXTENSION_NAME = 'rs_232_native'.freeze +dir_config(EXTENSION_NAME) + +if ENV['DEBUG'] $CFLAGS << ' -DDEBUG' - $CFLAGS << ' -g -O' + $CFLAGS << ' -g' $stdout.puts "compiling in debug mode... flags: #{$CFLAGS}" end -dir_config('rs_232') +$CFLAGS << ' -Wall -g' $warnflags = '-Wall' OS = case RbConfig::CONFIG['host_os'].downcase - when /linux/ - 'linux' - when /darwin/ - 'darwin' - when /freebsd/ - 'freebsd' - when /openbsd/ - 'openbsd' - when /sunos|solaris/ - 'solaris' - when /mswin|mingw/ - 'windows' - else - RbConfig::CONFIG['host_os'].downcase + when /linux/ + 'linux' + when /darwin/ + 'darwin' + when /freebsd/ + 'freebsd' + when /openbsd/ + 'openbsd' + when /sunos|solaris/ + 'solaris' + when /mswin|mingw/ + 'windows' + else + RbConfig::CONFIG['host_os'].downcase end have_header('ruby.h') @@ -49,10 +52,10 @@ have_header('errno.h') have_header('sys/ioctl.h') else - fail "RS-233 implementation is not tested for this #{OS} platform." + fail "RS-233 implementation wasn't been tested for #{OS} platform." end -$objs = %w(constants.o port.o initializer.o) +$objs = %w(Constants.o Port.o Rs232.o).freeze $CFLAGS += " -DOS_#{OS.upcase}" @@ -62,4 +65,4 @@ MSG -create_makefile('rs_232') +create_makefile(EXTENSION_NAME) diff --git a/ext/rs_232/posix/port.c b/ext/rs_232/posix/Port.c similarity index 82% rename from ext/rs_232/posix/port.c rename to ext/rs_232/posix/Port.c index a3f82c9..70318f7 100644 --- a/ext/rs_232/posix/port.c +++ b/ext/rs_232/posix/Port.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Ingenico Inc. + * Copyright (c) 2013, Roman Lishtaba. * * Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, * provided that the above copyright notice and this permission notice appear in all copies. @@ -12,66 +12,57 @@ * **/ -#include "port.h" +/* + * @author Roman Lishtaba + */ -VALUE setDtrIO(VALUE self, VALUE rb_int) -{ - { - Check_Type(rb_int, T_FIXNUM); - } +#include "Port.h" - int boolean = FIX2INT(rb_int); +VALUE setDtrIO(VALUE self, VALUE rb_int) +{ + Check_Type(rb_int, T_FIXNUM); + const int boolean = FIX2INT(rb_int); + int status; PortDescriptor *port = NULL; - Data_Get_Struct(self, PortDescriptor, port); - - int status; + ioctl(port->fd, TIOCMGET, &status); if (boolean == 1) status |= TIOCM_DTR; else status &= ~TIOCM_DTR; ioctl(port->fd, TIOCMSET, &status); - + return INT2FIX(status); - } VALUE setRtsIO(VALUE self, VALUE rb_int) { - { - Check_Type(rb_int, T_FIXNUM); - } - - int boolean = FIX2INT(rb_int); - + Check_Type(rb_int, T_FIXNUM); + const int boolean = FIX2INT(rb_int); PortDescriptor *port = NULL; - Data_Get_Struct(self, PortDescriptor, port); - int status; + ioctl(port->fd, TIOCMGET, &status); if (boolean == 1) status |= TIOCM_RTS; else - status &= - ~TIOCM_RTS; + status &= ~TIOCM_RTS; ioctl(port->fd, TIOCMSET, &status); - + return INT2FIX(status); } VALUE lineStatusIO(VALUE self) { - PortDescriptor *port = NULL; - Data_Get_Struct(self, PortDescriptor, port); - unsigned long Status = 0, Temp = 0; + ioctl(port->fd, TIOCMGET, &Temp); if (Temp & TIOCM_CTS) Status |= LS_CTS; if (Temp & TIOCM_DSR) Status |= LS_DSR; @@ -81,7 +72,7 @@ VALUE lineStatusIO(VALUE self) if (Temp & TIOCM_RTS) Status |= LS_RTS; if (Temp & TIOCM_ST ) Status |= LS_ST; if (Temp & TIOCM_SR ) Status |= LS_SR; - + return LONG2FIX(Status); } @@ -89,7 +80,7 @@ VALUE lineStatusIO(VALUE self) static void platformInitIO(PortDescriptor *port) { port->fd = -1; - port->status = 1; + port->status = PORT_CLOSED; } @@ -107,19 +98,18 @@ static void setPosixBaudRate(PortDescriptor *port) void updateSettings(PortDescriptor *port) { - - if (port->status != 0) + if (PORT_OPEN != port->status) rb_raise(rb_eException, "Can not set due to comport is not open, status: %d\n", port->status); - + /* - * BaudRate clause ************************************************ - */ + * BaudRate clause + */ if (port->toBeUpdated & T_BaudRate) setPosixBaudRate(port); - + /* - * Parity clause ************************************************ - */ + * Parity clause + */ if (port->toBeUpdated & T_Parity) { switch (port->settings.Parity) @@ -136,11 +126,10 @@ void updateSettings(PortDescriptor *port) break; } } - + /* - * Data Bits clause ************************************************ - */ - + * Data Bits clause + */ if (port->toBeUpdated & T_DataBits) { port->posixConfig.c_cflag &= (~CSIZE); @@ -160,11 +149,11 @@ void updateSettings(PortDescriptor *port) break; } } - + /* - * StopBits clause ************************************************ - */ - + * StopBits clause + */ + if (port->toBeUpdated & T_StopBits) { switch (port->settings.StopBits) @@ -177,11 +166,11 @@ void updateSettings(PortDescriptor *port) break; } } - + /* - * FlowControl clause ************************************************ - */ - + * FlowControl clause + */ + if (port->toBeUpdated & T_Flow) { switch (port->settings.FlowControl) @@ -201,25 +190,25 @@ void updateSettings(PortDescriptor *port) break; } } - - + + /* - * In case of update flush IO - */ - + * In case of update flush IO + */ + if (port->toBeUpdated & T_SettingsDone) { tcsetattr(port->fd, TCSAFLUSH, &port->posixConfig); } - + /* - * Timeout clause ************************************************ - */ - + * Timeout clause + */ + if (port->toBeUpdated & T_TimeOut) { int timeout = port->settings.Timeout_Millisec; - + if (timeout == -1) { fcntl(port->fd, F_SETFL, O_NDELAY); /* O_NDELAY should release if no any data here */ @@ -230,148 +219,116 @@ void updateSettings(PortDescriptor *port) } tcgetattr(port->fd, &port->posixConfig); port->posixConfig.c_cc[VTIME] = (timeout / 100); - + tcsetattr(port->fd, TCSAFLUSH, &port->posixConfig); } - + port->toBeUpdated = 0; - } int queryStatusIO(VALUE self) { PortDescriptor *port = NULL; - Data_Get_Struct(self, PortDescriptor, port); - + return port->status; } -VALUE isClosedIO(VALUE self) +VALUE isOpenIO(VALUE self) { - return queryStatusIO(self) == 1 ? Qtrue : Qfalse; + return queryStatusIO(self) == PORT_OPEN ? Qtrue : Qfalse; } VALUE flushIO(VALUE self) { PortDescriptor *port = NULL; - Data_Get_Struct(self, PortDescriptor, port); - + return INT2FIX( tcdrain(port->fd) ); } VALUE bytesAvailableIO(VALUE self) { - PortDescriptor *port = NULL; - Data_Get_Struct(self, PortDescriptor, port); - int bytesQueued; - + if (ioctl(port->fd, FIONREAD, &bytesQueued) == -1) - { return INT2FIX(0); - } + return INT2FIX(bytesQueued); } VALUE openIO(VALUE self) { - PortDescriptor *port = NULL; - Data_Get_Struct(self, PortDescriptor, port); - - if ((port->fd = open(port->settings.ComPort, O_RDWR | O_NOCTTY | O_SYNC)) != -1) + + if ((port->fd = open(port->settings.ComPort, O_RDWR | O_NOCTTY | O_NDELAY)) != -1) { - - port->status = 0; - - { - rb_iv_set(self, "@open", INT2FIX(port->status)); - } - + port->status = PORT_OPEN; + rb_iv_set(self, "@open", INT2FIX(port->status)); + tcgetattr(port->fd, &port->previousPosixConfig); port->posixConfig = port->previousPosixConfig; cfmakeraw(&port->posixConfig); - - + port->posixConfig.c_cflag |= CREAD | CLOCAL; port->posixConfig.c_lflag &= (~(ICANON| ECHO| ECHOE| ECHOK| ECHONL| ISIG)); port->posixConfig.c_iflag &= (~(INPCK| IGNPAR| PARMRK| ISTRIP| ICRNL| IXANY)); port->posixConfig.c_oflag &= (~OPOST); port->posixConfig.c_cc[VMIN] = 0; port->posixConfig.c_cc[VTIME] = 0; - + port->toBeUpdated = T_ALL; - + setSettings(self); - } else { - port->status = 1; - rb_raise(rb_eException, "Unable to open comport: %s\n", port->settings.ComPort); + port->status = PORT_CLOSED; + rb_raise(rb_eIOError, "Unable to open comport: `%s`", port->settings.ComPort); } - - return INT2FIX(port->status); + + return self; } VALUE writeIO(VALUE self, VALUE message) { - - { - Check_Type(message, T_STRING); - } - - int recv; - int len; - + Check_Type(message, T_STRING); PortDescriptor *port = NULL; - Data_Get_Struct(self, PortDescriptor, port); - - len = (int) RSTRING_LEN(message); + + const int len = (int) RSTRING_LEN(message); char cStr[len]; strcpy(cStr, RSTRING_PTR(message)); - - recv = (int) write(port->fd, cStr, (size_t) sizeof(cStr)); - - if (recv < 0){ + + const int recv = (int) write(port->fd, cStr, (size_t) sizeof(cStr)); + + if (recv < 0) rb_raise(rb_eEOFError, "TX: writing of the %d bytes has failed", len); - } - + return INT2FIX(recv); } VALUE readIO(VALUE self, VALUE rb_int) { - - { - Check_Type(rb_int, T_FIXNUM); - } - + Check_Type(rb_int, T_FIXNUM); int recv; - PortDescriptor *port = NULL; - Data_Get_Struct(self, PortDescriptor, port); - - int len = FIX2INT(rb_int); - + const int len = FIX2INT(rb_int); char in[len]; - + recv = (int) read(port->fd, in, sizeof(in)); if (recv > 0) - return rb_str_new(in, recv); + return rb_str_new(in, recv); return Qnil; } @@ -379,51 +336,38 @@ VALUE readIO(VALUE self, VALUE rb_int) VALUE closeIO(VALUE self) { - - int closed; - PortDescriptor *port = NULL; - Data_Get_Struct(self, PortDescriptor, port); - flushIO(self); - tcsetattr(port->fd, TCSAFLUSH | TCSANOW, &port->previousPosixConfig); - closed = close(port->fd); - - port->status = closed == 0 ? 1 : 0; - + + const int state = close(port->fd); + + port->status = (state == 0 ? PORT_CLOSED : PORT_OPEN); + rb_iv_set(self, "@open", INT2FIX(port->status)); rb_iv_set(self, "@fd", INT2FIX(port->fd)); - - return INT2FIX(closed); - + + return INT2FIX(state); } VALUE initializeStruct(VALUE self, VALUE portName) { - - { - Check_Type(portName, T_STRING); - } - + Check_Type(portName, T_STRING); PortDescriptor *port = NULL; - Data_Get_Struct(self, PortDescriptor, port); - + port->settings.BaudRate = BAUD115200; port->settings.Parity = PAR_NONE; port->settings.FlowControl = FLOW_OFF; port->settings.DataBits = DATA_8; port->settings.StopBits = STOP_1; port->settings.Timeout_Millisec = 0; - + strcpy(port->settings.ComPort, RSTRING_PTR(portName)); - platformInitIO(port); - rb_iv_set(self, "@port", portName); - + return self; -} \ No newline at end of file +} diff --git a/ext/rs_232/posix/port.h b/ext/rs_232/posix/Port.h similarity index 59% rename from ext/rs_232/posix/port.h rename to ext/rs_232/posix/Port.h index 9478612..e7cc0a9 100644 --- a/ext/rs_232/posix/port.h +++ b/ext/rs_232/posix/Port.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Ingenico Inc. + * Copyright (c) 2013, Roman Lishtaba. * * Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, * provided that the above copyright notice and this permission notice appear in all copies. @@ -12,9 +12,16 @@ * **/ -#ifndef rs_232_port_h____FILEEXTENSION___ -#define rs_232_port_h____FILEEXTENSION___ +/* + * @author Roman Lishtaba + */ + +#pragma once +#ifdef __cplusplus +extern "C" { +#endif + # include # include # include @@ -23,31 +30,33 @@ # include # include # include -# include "structs.h" - - -void setBaudRate(VALUE, VALUE); - -VALUE getBaudRate(VALUE); - -void setParity(VALUE, VALUE); - -VALUE getParity(VALUE); - -void setDataBits(VALUE, VALUE); - -VALUE getDataBits(VALUE); - -void setStopBits(VALUE, VALUE); - -VALUE getStopBits(VALUE); - -void setFlowControl(VALUE, VALUE); - -VALUE getFlowControl(VALUE); - -void setTimeout(VALUE, VALUE); - -void setSettings(VALUE); - +# include "Structs.h" + + + void setBaudRate(VALUE, VALUE); + + VALUE getBaudRate(VALUE); + + void setParity(VALUE, VALUE); + + VALUE getParity(VALUE); + + void setDataBits(VALUE, VALUE); + + VALUE getDataBits(VALUE); + + void setStopBits(VALUE, VALUE); + + VALUE getStopBits(VALUE); + + void setFlowControl(VALUE, VALUE); + + VALUE getFlowControl(VALUE); + + void setTimeout(VALUE, VALUE); + + void setSettings(VALUE); + +#ifdef __cplusplus +} #endif diff --git a/ext/rs_232/structs.h b/ext/rs_232/structs.h deleted file mode 100644 index 5919b63..0000000 --- a/ext/rs_232/structs.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2013, Ingenico Inc. - * - * Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, - * provided that the above copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER - * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - * - **/ - -#ifndef rs_232_structs_h -#define rs_232_structs_h - -#include "constants.h" - -/* - * structure to contain port settings - */ - -typedef struct PortSettings_T -{ - char ComPort[40]; - enum BaudRateType BaudRate; - enum DataBitsType DataBits; - enum ParityType Parity; - enum StopBitsType StopBits; - enum FlowType FlowControl; - long Timeout_Millisec; -} PortSettings; - - -/* - * port descriptor structure - */ - -typedef struct portDescriptor_T -{ -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__) - - HANDLE fd; - COMMCONFIG commConfig; - COMMTIMEOUTS commTimeouts; - -#else - - int fd; - struct termios posixConfig; - struct termios previousPosixConfig; - -#endif - - int status; - PortSettings settings; - int toBeUpdated; - -} PortDescriptor; - -#endif diff --git a/ext/rs_232/windows/port.c b/ext/rs_232/windows/Port.c similarity index 80% rename from ext/rs_232/windows/port.c rename to ext/rs_232/windows/Port.c index 4fd2560..2347302 100644 --- a/ext/rs_232/windows/port.c +++ b/ext/rs_232/windows/Port.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Ingenico Inc. + * Copyright (c) 2013, Roman Lishtaba. * * Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, * provided that the above copyright notice and this permission notice appear in all copies. @@ -12,7 +12,11 @@ * **/ -#include "port.h" +/* + * @author Roman Lishtaba + */ + +#include "Port.h" VALUE setDtrIO(VALUE self, VALUE rb_int) @@ -20,49 +24,44 @@ VALUE setDtrIO(VALUE self, VALUE rb_int) { Check_Type(rb_int, T_FIXNUM); } - int boolean = FIX2INT(rb_int); - PortDescriptor *port = NULL; - Data_Get_Struct(self, PortDescriptor, port); - + return INT2FIX( EscapeCommFunction(port->fd, boolean == 1 ? SETDTR : CLRDTR) ); } VALUE setRtsIO(VALUE self, VALUE rb_int) { - { - Check_Type(rb_int, T_FIXNUM); - } - + Check_Type(rb_int, T_FIXNUM); + int boolean = FIX2INT(rb_int); - + PortDescriptor *port = NULL; - + Data_Get_Struct(self, PortDescriptor, port); - + return INT2FIX( EscapeCommFunction(port->fd, boolean == 1 ? SETRTS : CLRRTS) ); } VALUE lineStatusIO(VALUE self) { - + PortDescriptor *port = NULL; - + Data_Get_Struct(self, PortDescriptor, port); - + unsigned long Status = 0, Temp = 0; - + GetCommModemStatus(port->fd, &Temp); - + if (Temp & MS_CTS_ON) Status |= LS_CTS; if (Temp & MS_DSR_ON) Status |= LS_DSR; if (Temp & MS_RING_ON) Status |= LS_RI; if (Temp & MS_RLSD_ON) Status |= LS_DCD; - + return LONG2FIX(Status); } @@ -76,28 +75,28 @@ static void platformInitIO(PortDescriptor *port) void updateSettings(PortDescriptor *port) { - - if (port->status != 0) - rb_raise(rb_eException, "Can not set due to comport is not open, status: %d\n", port->status); - - + + if (PORT_OPEN != port->status) + rb_raise(rb_eIOError, "Can not set due to comport is not open, status: %d\n", port->status); + + if (port->toBeUpdated & T_BaudRate) port->commConfig.dcb.BaudRate = port->settings.BaudRate; - - + + if (port->toBeUpdated & T_Parity) { port->commConfig.dcb.Parity = (BYTE) port->settings.Parity; port->commConfig.dcb.fParity = (port->settings.Parity == PAR_NONE) ? 0 : 1; } - - + + if (port->toBeUpdated & T_DataBits) { port->commConfig.dcb.ByteSize = (BYTE) port->settings.DataBits; } - - + + if (port->toBeUpdated & T_StopBits) { switch (port->settings.StopBits) @@ -110,7 +109,7 @@ void updateSettings(PortDescriptor *port) break; } } - + if (port->toBeUpdated & T_Flow) { switch (port->settings.FlowControl) @@ -135,11 +134,11 @@ void updateSettings(PortDescriptor *port) break; } } - + if (port->toBeUpdated & T_TimeOut) { int millisec = port->settings.Timeout_Millisec; - + if (millisec == -1) { port->commTimeouts.ReadIntervalTimeout = MAXDWORD; @@ -153,14 +152,14 @@ void updateSettings(PortDescriptor *port) port->commTimeouts.WriteTotalTimeoutMultiplier = millisec; port->commTimeouts.WriteTotalTimeoutConstant = 0; } - - + + if (port->toBeUpdated & T_SettingsDone) SetCommConfig(port->fd, &port->commConfig, sizeof(COMMCONFIG)); - + if ((port->toBeUpdated & T_TimeOut)) SetCommTimeouts(port->fd, &port->commTimeouts); - + port->toBeUpdated = 0; } @@ -168,25 +167,24 @@ void updateSettings(PortDescriptor *port) static int queryStatusIO(VALUE self) { PortDescriptor *port = NULL; - Data_Get_Struct(self, PortDescriptor, port); - + return port->status; } -VALUE isClosedIO(VALUE self) +VALUE isOpenIO(VALUE self) { - return queryStatusIO(self) != 0 ? Qtrue : Qfalse; + return queryStatusIO(self) != PORT_OPEN ? Qfalse : Qtrue; } VALUE flushIO(VALUE self) { PortDescriptor *port = NULL; - + Data_Get_Struct(self, PortDescriptor, port); - + return (INT2FIX(FlushFileBuffers(port->fd))); } @@ -194,9 +192,9 @@ VALUE flushIO(VALUE self) VALUE bytesAvailableIO(VALUE self) { PortDescriptor *port = NULL; - + Data_Get_Struct(self, PortDescriptor, port); - + DWORD Errors; COMSTAT Status; if (ClearCommError(port->fd, &Errors, &Status)) @@ -209,150 +207,134 @@ VALUE bytesAvailableIO(VALUE self) VALUE openIO(VALUE self) { - + PortDescriptor *port = NULL; - Data_Get_Struct(self, PortDescriptor, port); - - if (port->status == 0) - return INT2FIX(port->status); - + if (port->status == PORT_OPEN) + return self; + DWORD conf_length = sizeof(COMMCONFIG); port->commConfig.dwSize = conf_length; DWORD threading = 0; - + port->fd = CreateFileA(port->settings.ComPort, - GENERIC_READ | GENERIC_WRITE, - 0, - NULL, - OPEN_EXISTING, - threading, - NULL); - + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + threading, + NULL); + if (port->fd == INVALID_HANDLE_VALUE) { - - port->status = 1; - rb_raise(rb_eException, "Unable to open comport: %s\n", port->settings.ComPort); - - } else - { - - port->status = 0; - rb_iv_set(self, "@open", INT2FIX(port->status)); - + port->status = PORT_CLOSED; + rb_raise(rb_eIOError, "Unable to open comport: `%s`", port->settings.ComPort); + + } else { + port->status = PORT_OPEN; + rb_iv_set(self, "@open", isOpenIO(self)); + GetCommConfig(port->fd, &port->commConfig, &conf_length); GetCommState(port->fd, &(port->commConfig.dcb)); - + port->commConfig.dcb.fBinary = 1; port->commConfig.dcb.fInX = 0; port->commConfig.dcb.fOutX = 0; port->commConfig.dcb.fAbortOnError = 0; port->commConfig.dcb.fNull = 0; - + port->commConfig.dcb.fDtrControl = 1; - + port->toBeUpdated = T_ALL; - + setSettings(self); - + } - - return INT2FIX(port->status); + + return self; } VALUE writeIO(VALUE self, VALUE message) { - int recv; int len; PortDescriptor *port = NULL; - + Data_Get_Struct(self, PortDescriptor, port); - + Check_Type(message, T_STRING); - + len = RSTRING_LEN(message); char cStr[len]; strcpy(cStr, RSTRING_PTR(message)); - + if (!WriteFile(port->fd, cStr, len, (LPDWORD)((void *) &recv), NULL)) - { - rb_raise(rb_eIOError, "IO: writing of the %d bytes has been failed", len); - } - - return (INT2FIX(recv)); - + rb_raise(rb_eIOError, "IO: writing of the %d bytes has been failed. Error #%d", len, (int) GetLastError()); + + return INT2FIX(recv); } - VALUE readIO(VALUE self, VALUE rb_int) { - - { - Check_Type(rb_int, T_FIXNUM); - } - + Check_Type(rb_int, T_FIXNUM); PortDescriptor *port = NULL; - Data_Get_Struct(self, PortDescriptor, port); - + int n; int len = FIX2INT(rb_int); char buf[len]; - - ReadFile(port->fd, &buf, len, (LPDWORD)((void *) &n), NULL); - + + if(!ReadFile(port->fd, &buf, len, (LPDWORD)((void *) &n), NULL)) + rb_raise(rb_eIOError, "IO: reading of %d bytes has been failed. Error #%d", len, (int) GetLastError()); + if (n > 0) - return rb_str_new(buf, n); - + return rb_str_new(buf, n); + return Qnil; } VALUE closeIO(VALUE self) { - PortDescriptor *port = NULL; - Data_Get_Struct(self, PortDescriptor, port); - - flushIO(self); - port->status = CloseHandle(port->fd); - port->fd = INVALID_HANDLE_VALUE; - - rb_iv_set(self, "@open", INT2FIX(port->status)); - - return INT2FIX(port->status); - + + if (port->fd != INVALID_HANDLE_VALUE) { + flushIO(self); + port->status = CloseHandle(port->fd); + port->fd = INVALID_HANDLE_VALUE; + rb_iv_set(self, "@open", isOpenIO(self)); + } + + return !isOpenIO(self); } VALUE initializeStruct(VALUE self, VALUE portName) { - + { Check_Type(portName, T_STRING); } - + PortDescriptor *port = NULL; - + Data_Get_Struct(self, PortDescriptor, port); - + port->settings.BaudRate = BAUD115200; port->settings.Parity = PAR_NONE; port->settings.FlowControl = FLOW_OFF; port->settings.DataBits = DATA_8; port->settings.StopBits = STOP_1; port->settings.Timeout_Millisec = 0; - + snprintf(port->settings.ComPort, sizeof(port->settings.ComPort) - 1, WIN_PATTERN, RSTRING_PTR(portName)); - + platformInitIO(port); - + rb_iv_set(self, "@port", portName); - + return self; } diff --git a/ext/rs_232/windows/port.h b/ext/rs_232/windows/Port.h similarity index 59% rename from ext/rs_232/windows/port.h rename to ext/rs_232/windows/Port.h index b9514a1..2932ca0 100644 --- a/ext/rs_232/windows/port.h +++ b/ext/rs_232/windows/Port.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Ingenico Inc. + * Copyright (c) 2013, Roman Lishtaba. * * Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, * provided that the above copyright notice and this permission notice appear in all copies. @@ -12,9 +12,16 @@ * **/ -#ifndef rs_232_port_h____FILEEXTENSION___ -#define rs_232_port_h____FILEEXTENSION___ +/* + * @author Roman Lishtaba + */ + +#pragma once +#ifdef __cplusplus +extern "C" { +#endif + # include # include # include @@ -22,32 +29,34 @@ # include # include # include -# include "structs.h" - +# include "Structs.h" + #define WIN_PATTERN "\\\\.\\%s" - -void setBaudRate(VALUE, VALUE); - -VALUE getBaudRate(VALUE); - -void setParity(VALUE, VALUE); - -VALUE getParity(VALUE); - -void setDataBits(VALUE, VALUE); - -VALUE getDataBits(VALUE); - -void setStopBits(VALUE, VALUE); - -VALUE getStopBits(VALUE); - -void setFlowControl(VALUE, VALUE); - -VALUE getFlowControl(VALUE); - -void setTimeout(VALUE, VALUE); - -void setSettings(VALUE); - + + void setBaudRate(VALUE, VALUE); + + VALUE getBaudRate(VALUE); + + void setParity(VALUE, VALUE); + + VALUE getParity(VALUE); + + void setDataBits(VALUE, VALUE); + + VALUE getDataBits(VALUE); + + void setStopBits(VALUE, VALUE); + + VALUE getStopBits(VALUE); + + void setFlowControl(VALUE, VALUE); + + VALUE getFlowControl(VALUE); + + void setTimeout(VALUE, VALUE); + + void setSettings(VALUE); + +#ifdef __cplusplus +} #endif diff --git a/lib/rs_232.rb b/lib/rs_232.rb index dafaf69..ecee5b4 100644 --- a/lib/rs_232.rb +++ b/lib/rs_232.rb @@ -1,6 +1,111 @@ require 'rs_232/version' -require 'rs_232.so' +require 'rs_232_native.so' +require 'forwardable' module Rs232 - # see 'examples' folder in order to use existent or decorate your own adapter. + # public accessible factory method + # + # @example + # + # > include Rs232 + # > port = new_serial_port('/dev/tty.ACM0', baud_rate: 9600) + # #=> # + # > port.open? + # #=> false + # > port.connect # rasing IOError in case of port couldn't be opened. + # #=> # + # > port.pending_bytes + # #=> 15 + # > port.read(15) + # #=> 'Hello, World!!!' + # > port.write("Hi") + # #=> 2 + # > port.close + # #=> true + # > port.open? + # #=> false + # + # @param [String] port name + # @param [Hash] opts with such keys as: baud_rate, parity, data_bits, stop_bits, flow_control, timeout + # @param [Proc] block is a function to apply on constructor finish + def new_serial_port(port, opts = {}, &block) + Impl.new(port, opts, &block) + end + + # the following class represents ruby public interface + # to native Rs232 implementation + # + # There no public instantiation, can be instantiate through + # the factory method @see Rs232.new + # + class Impl + include Rs232::Constants + + STD_OPTS = { + baud_rate: BAUD_115200, + parity: PAR_NONE, + data_bits: DATA_BITS_8, + stop_bits: STOP_BITS_1, + flow_control: FLOW_OFF, + timeout: 10 + }.freeze + + extend Forwardable + + def_delegators :@impl, + :read, + :write, + :flush, + :open?, + :set_rts, + :set_dtr + + def initialize(name, opts = {}) + @impl = Rs232::Native.new(name) + @opts = STD_OPTS.merge(opts).freeze + freeze + yield self if block_given? + end + + # @raise IOError in case of port was not open. + # @return [Impl] instance + def connect + return self if open? + @impl.open.tap do |io| + io.baud_rate = @opts[:baud_rate] + io.parity = @opts[:parity] + io.data_bits = @opts[:data_bits] + io.stop_bits = @opts[:stop_bits] + io.flow_control = @opts[:flow_control] + io.timeout = @opts[:timeout] + end + self + end + + # @return [Boolean] + def close + return !open? unless open? + 0 == @impl.close + end + + # @return [Fixnum] representing count of bytes in the buffer + def pending_bytes + @impl.available? + end + + # @return [Hash] of configurations on the OS side. + def settings + {}.tap do |o| + o[:baud_rate] = @impl.baud_rate + o[:parity] = @impl.parity + o[:data_bits] = @impl.data_bits + o[:stop_bits] = @impl.stop_bits + o[:flow_control] = @impl.flow_control + o[:timeout] = @impl.timeout + o[:line_status] = @impl.line_status + end.freeze + end + end + + private_constant :Impl end diff --git a/lib/rs_232/version.rb b/lib/rs_232/version.rb index f1c6fe0..6b03648 100644 --- a/lib/rs_232/version.rb +++ b/lib/rs_232/version.rb @@ -1,3 +1,3 @@ module Rs232 - VERSION = '2.3.1' + VERSION = '3.0.0.pre5'.freeze end diff --git a/lib/tasks/clean.rake b/lib/tasks/clean.rake deleted file mode 100644 index efebb63..0000000 --- a/lib/tasks/clean.rake +++ /dev/null @@ -1,3 +0,0 @@ -require 'rake/clean' - -CLEAN.include %w(**/*.{log} doc coverage tmp pkg **/*.{o,so,bundle} Makefile) diff --git a/lib/tasks/compile.rake b/lib/tasks/compile.rake deleted file mode 100644 index 73ccdca..0000000 --- a/lib/tasks/compile.rake +++ /dev/null @@ -1,6 +0,0 @@ -require 'rake/extensiontask' - -Rake::ExtensionTask.new('rs_232') do |ext| - ext.tmp_dir = 'tmp' - ext.source_pattern = '*.{c,cpp}' -end diff --git a/lib/tasks/cov.rake b/lib/tasks/cov.rake deleted file mode 100644 index af4694f..0000000 --- a/lib/tasks/cov.rake +++ /dev/null @@ -1,5 +0,0 @@ -require 'simplecov' -task :cov do - ENV['SIMPLECOV'] = 'features' - Rake::Task['default'].invoke -end diff --git a/lib/tasks/cucumber.rake b/lib/tasks/cucumber.rake deleted file mode 100644 index 24ebae5..0000000 --- a/lib/tasks/cucumber.rake +++ /dev/null @@ -1,5 +0,0 @@ -require 'cucumber/rake/task' -Cucumber::Rake::Task.new(:features) do |t| - t.fork = true - t.profile = :default -end diff --git a/lib/tasks/rspec.rake b/lib/tasks/rspec.rake deleted file mode 100644 index f35bd25..0000000 --- a/lib/tasks/rspec.rake +++ /dev/null @@ -1,7 +0,0 @@ -require 'rspec/core/rake_task' -desc 'Run RSpec' -RSpec::Core::RakeTask.new do |t| - t.fail_on_error = false - t.verbose = true - t.rspec_opts = '--format RspecJunitFormatter --out rspec.xml --tag ~wip' -end diff --git a/rs_232.gemspec b/rs_232.gemspec index 02473a9..a9fc1a1 100644 --- a/rs_232.gemspec +++ b/rs_232.gemspec @@ -8,25 +8,32 @@ Gem::Specification.new do |spec| spec.version = Rs232::VERSION spec.authors = ['Roman Lishtaba'] spec.email = ['roman@lishtaba.com'] + spec.description = 'RS-232 cross-platform implementation as Ruby C native extension.' spec.summary = <<-SUMMARY RS-232 cross-platform implementation as Ruby C native extension. Rubygem offering simple API in order to start using Serial Port communication in your project running on Linux, Mac OS X and Windows. SUMMARY + spec.homepage = 'http://www.lishtaba.com' spec.license = 'MIT' + spec.files = [ Dir.glob('ext/**/*'), Dir.glob('lib/**/*.rb'), - 'Rakefile', 'rs_232.gemspec', + 'Rakefile', + 'rs_232.gemspec', 'Gemfile', 'LICENSE.txt' ].flatten + spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ['lib'] - spec.extensions = Dir.glob('ext/**/extconf.rb') - spec.add_development_dependency 'bundler', '~> 1.10' + + spec.extensions = 'ext/rs_232/extconf.rb' + + spec.add_development_dependency 'bundler' spec.add_development_dependency 'rake', '~> 10.0' spec.add_development_dependency 'rubocop', '~> 0' spec.add_development_dependency 'simplecov', '~> 0.9' @@ -35,6 +42,8 @@ Rubygem offering simple API in order to start using Serial Port communication in spec.add_development_dependency 'rspec_junit_formatter', '~> 0' spec.add_development_dependency 'yard', '~> 0.8' spec.add_development_dependency 'rake-compiler', '~> 0' + spec.add_development_dependency 'pry' + spec.post_install_message = <<-MSG (::) You've installed binary version of the Rs-232 gem! (::) MSG diff --git a/script/console b/script/console index 8272ed8..da15006 100755 --- a/script/console +++ b/script/console @@ -1,8 +1,7 @@ #!/usr/bin/env ruby -%w(lib examples).each do |path| - expanded_path = File.expand_path("../#{path}", __FILE__) - $LOAD_PATH.unshift(expanded_path) unless $LOAD_PATH.include? expanded_path -end + +$LOAD_PATH << File.join(File.dirname(__FILE__), '../ext/rs_232') +$LOAD_PATH << File.join(File.dirname(__FILE__), '../lib') require 'irb' require 'irb/completion' diff --git a/spec/fixtures/test.txt b/spec/fixtures/test.txt new file mode 100644 index 0000000..5ac001b --- /dev/null +++ b/spec/fixtures/test.txt @@ -0,0 +1 @@ +Hello, World!!! diff --git a/spec/lib/constants_spec.rb b/spec/lib/constants_spec.rb new file mode 100644 index 0000000..42ea21d --- /dev/null +++ b/spec/lib/constants_spec.rb @@ -0,0 +1,56 @@ +require 'spec_helper' + +describe Rs232 do + EXPECTED_CONSTANTS = [ + :BAUD_110, :BAUD_300, :BAUD_600, :BAUD_1200, + :BAUD_2400, :BAUD_4800, :BAUD_9600, :BAUD_19200, + :BAUD_38400, :BAUD_57600, :BAUD_115200, :DATA_BITS_5, + :DATA_BITS_6, :DATA_BITS_7, :DATA_BITS_8, :PAR_NONE, + :PAR_ODD, :PAR_EVEN, :STOP_BITS_1, :STOP_BITS_3, + :FLOW_OFF, :FLOW_HARDWARE, :FLOW_XONXOFF + ].sort.freeze + + TOP_LEVEL_CONSTANTS = [ + :Native, + :Constants, + :VERSION + ].sort.freeze + + INTERFACE_METHODS = [ + :baud_rate=, + :baud_rate, + :parity=, + :parity, + :data_bits=, + :data_bits, + :stop_bits=, + :stop_bits, + :flow_control=, + :flow_control, + :timeout=, + :timeout, + :open, + :close, + :flush, + :open?, + :write, + :read, + :available?, + :line_status, + :set_rts, + :set_dtr + ].sort.freeze + + it 'should have an NATIVE constant' do + expect(Rs232.constants.sort).to eq(TOP_LEVEL_CONSTANTS) + end + + it 'should have an VERSION constant' do + current_constants = Rs232::Constants.constants.sort + expect(current_constants).to eq(EXPECTED_CONSTANTS) + end + + it 'should have respond to new' do + expect((Rs232::Native.instance_methods - methods).sort).to eq(INTERFACE_METHODS) + end +end diff --git a/spec/support/fixtures.rb b/spec/support/fixtures.rb index 8533e39..7111926 100644 --- a/spec/support/fixtures.rb +++ b/spec/support/fixtures.rb @@ -18,6 +18,6 @@ def binary_fixture(name) end def fixtures_path - Pathname(absolute_fixture_path '') + Pathname(absolute_fixture_path('')) end end