singe/thirdparty/librs232/bindings/lua/rs232.lua
2023-10-23 19:38:18 -05:00

426 lines
9.2 KiB
Lua

------------------------------------------------------------------
--
-- Author: Alexey Melnichuk <alexeymelnichuck@gmail.com>
--
-- Copyright (C) 2015-2016 Alexey Melnichuk <alexeymelnichuck@gmail.com>
--
-- This file is part of lua-rs232 library.
--
-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be 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 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.
------------------------------------------------------------------
local rs232 = require "rs232.core"
local function class(base)
local t = base and setmetatable({}, base) or {}
t.__index = t
t.__class = t
t.__base = base
function t.new(...)
local o = setmetatable({}, t)
if o.__init then
if t == ... then -- we call as Class:new()
return o:__init(select(2, ...))
else -- we call as Class.new()
return o:__init(...)
end
end
return o
end
return t
end
local function select_by_prefix(pfx, p)
local names = {}
local values = {}
p = p or ''
for k, v in pairs(rs232) do
if type(k) == 'string' and type(v) == 'number' then
if k:sub(1, #pfx) == pfx then
local name = p .. k:sub(#pfx+1)
assert(not names[v])
names[v] = name
values[name] = v
end
end
end
return values, names
end
local ERROR, ERROR_NAMES = select_by_prefix'RS232_ERR_'
local BAUD_NAMES, BAUD do
local tmp = select_by_prefix'RS232_BAUD_'
BAUD_NAMES, BAUD = {}, {}
for k, v in pairs(tmp) do
BAUD_NAMES[ '_' .. k ] = v
BAUD_NAMES['BAUD_' .. k ] = v
BAUD[v] = 'BAUD_' .. k
end
end
local DATA_NAMES, DATA do
local tmp = select_by_prefix'RS232_DATA_'
DATA_NAMES, DATA = {}, {}
for k, v in pairs(tmp) do
DATA_NAMES[ '_' .. k ] = v
DATA_NAMES['DATA_' .. k ] = v
DATA[v] = 'DATA_' .. k
end
end
local DTR_NAMES, DTR = select_by_prefix'RS232_DTR_'
local RTS_NAMES, RTS = select_by_prefix'RS232_RTS_'
local FLOW_NAMES, FLOW = select_by_prefix'RS232_FLOW_'
local PARITY_NAMES, PARITY = select_by_prefix'RS232_PARITY_'
local STOP_NAMES, STOP do
local tmp = select_by_prefix'RS232_STOP_'
STOP_NAMES, STOP = {}, {}
for k, v in pairs(tmp) do
STOP_NAMES[ '_' .. k ] = v
STOP_NAMES['STOP_' .. k ] = v
STOP[v] = 'STOP_' .. k
end
end
local RS232Error = class() do
function RS232Error:__init(no, ext)
self._no = no
self._name = assert(ERROR_NAMES[no])
self._ext = ext
return self
end
function RS232Error:cat() return 'RS232' end
function RS232Error:no() return self._no end
function RS232Error:name() return self._name end
function RS232Error:msg() return rs232.error_tostring(self._no) end
function RS232Error:ext() return self._ext end
function RS232Error:__eq(rhs)
return self._no == rhs._no
end
function RS232Error:__tostring()
local err = string.format("[%s][%s] %s (%d)",
self:cat(), self:name(), self:msg(), self:no()
)
if self:ext() then
err = string.format("%s - %s", err, self:ext())
end
return err
end
end
local ERRORS = {
[rs232.RS232_ERR_TIMEOUT] = RS232Error.new(rs232.RS232_ERR_TIMEOUT);
}
local function Error(e)
return ERRORS[e] or RS232Error.new(e)
end
local function F(e, ...)
if e ~= rs232.RS232_ERR_NOERROR then
return nil, Error(e)
end
return true, ...
end
local Port = class() do
function Port:__init(name, opt)
self._name = assert(name)
self._opt = {}
for k,v in pairs(opt or {})do
self._opt[k] = v
end
return self
end
function Port:open()
local ok, ret = F(rs232.open(self._name))
if not ok then return nil, ret end
self._p = ret
ok, ret = self:set(self._opt)
if not ok then
self._p:close()
self._p = nil
return nil, err
end
return self, tostring(self._p)
end
function Port:close(...)
if not self._p then return nil, "Already closed" end
local p = self._p
self._p = nil
return p:close()
end
function Port:set(port_opt)
local p = self._p
local ok, err
if port_opt.baud ~= nil then
ok, err = self:set_baud_rate(port_opt.baud)
if not ok then return nil, err end
end
if port_opt.baud_rate ~= nil then
ok, err = self:set_baud_rate(port_opt.baud_rate)
if not ok then return nil, err end
end
if port_opt.data_bits ~= nil then
ok, err = self:set_data_bits(port_opt.data_bits)
if not ok then return nil, err end
end
if port_opt.parity ~= nil then
ok, err = self:set_parity(port_opt.parity)
if not ok then return nil, err end
end
if port_opt.stop_bits ~= nil then
ok, err = self:set_stop_bits(port_opt.stop_bits)
if not ok then return nil, err end
end
if port_opt.flow_control ~= nil then
ok, err = self:set_flow_control(port_opt.flow_control)
if not ok then return nil, err end
end
if port_opt.rts ~= nil then
ok, err = self:set_rts(port_opt.rts)
if not ok then return nil, err end
end
if port_opt.dtr ~= nil then
ok, err = self:set_dtr(port_opt.dtr)
if not ok then return nil, err end
end
return self
end
function Port:read(...)
local e, data, len = self._p:read(...)
if e ~= rs232.RS232_ERR_NOERROR then
if e == rs232.RS232_ERR_TIMEOUT then
return data or ''
end
return data, Error(e)
end
return data
end
function Port:write(...)
local ok, len = F(self._p:write(...))
if not ok then return nil, len end
return len
end
function Port:flush(...)
local ok, err = F(self._p:flush(...))
if not ok then return nil, err end
return self
end
function Port:in_queue_clear(...)
local ok, err = F(self._p:in_queue_clear(...))
if not ok then return nil, err end
return self
end
function Port:in_queue(...)
local ok, len = F(self._p:in_queue(...))
if not ok then return nil, len end
return len
end
function Port:device(...)
local ok, name = F(self._p:in_queue(...))
if not ok then return nil, name end
return name
end
function Port:fd(...)
local ok, fd = F(self._p:fd(...))
if not ok then return nil, fd end
return fd
end
local function check_val(value, NAMES, VALUES)
local v
if type(value) == 'string' then
v = assert( NAMES[value:upper()], 'Unsupported value:' .. tostring(value) )
else
v = value
assert( VALUES[value], 'Unsupported value:' .. tostring(value) )
end
return v
end
function Port:set_baud_rate(value)
local v = check_val(value, BAUD_NAMES, BAUD)
local ok, err = F(self._p:set_baud_rate(v))
if not ok then return nil, err end
return self
end
function Port:baud_rate()
local val = self._p:baud_rate()
return val, BAUD[val]
end
function Port:set_data_bits(value)
local v = check_val(value, DATA_NAMES, DATA)
local ok, err = F(self._p:set_data_bits(v))
if not ok then return nil, err end
return self
end
function Port:data_bits()
local val = self._p:data_bits()
return val, DATA[val]
end
function Port:set_stop_bits(value)
local v = check_val(value, STOP_NAMES, STOP)
local ok, err = F(self._p:set_stop_bits(v))
if not ok then return nil, err end
return self
end
function Port:stop_bits()
local val = self._p:stop_bits()
return val, STOP[val]
end
function Port:set_parity(value)
local v = check_val(value, PARITY_NAMES, PARITY)
local ok, err = F(self._p:set_parity(v))
if not ok then return nil, err end
return self
end
function Port:parity()
local val = self._p:parity()
return val, PARITY[val]
end
function Port:set_flow_control(value)
local v = check_val(value, FLOW_NAMES, FLOW)
local ok, err = F(self._p:set_flow_control(v))
if not ok then return nil, err end
return self
end
function Port:flow_control()
local val = self._p:flow_control()
return val, FLOW[val]
end
function Port:set_dtr(value)
local v = check_val(value, DTR_NAMES, DTR)
local ok, err = F(self._p:set_dtr(v))
if not ok then return nil, err end
return self
end
function Port:dtr()
local val = self._p:dtr()
return val, DTR[val]
end
function Port:set_rts(value)
local v = check_val(value, RTS_NAMES, RTS)
local ok, err = F(self._p:set_rts(v))
if not ok then return nil, err end
return self
end
function Port:rts()
local val = self._p:rts()
return val, RTS[val]
end
function Port:__tostring()
return "RS232 Port " .. tostring(self._p)
end
end
return setmetatable({
_NAME = "rs232";
_VERSION = "0.1.1-dev";
_COPYRIGHT = "Copyright (C) 2015-2016 Alexey Melnichuk";
_LICENSE = "MIT";
port = Port.new;
error = RS232Error.new;
ERROR = ERROR;
},{__index = rs232})