diff options
author | Alan Aguiar <alanjas@hotmail.com> | 2012-10-18 01:09:19 (GMT) |
---|---|---|
committer | Alan Aguiar <alanjas@hotmail.com> | 2012-10-18 01:09:19 (GMT) |
commit | bf90188aa6b9d309d40cde736db2f81df68856e6 (patch) | |
tree | c57cf8c31f1f039f22c9500015583d78ea3ca1eb | |
parent | e1e2489a88e977c77d7ba47ef1c2c5763f5cd012 (diff) |
replace butia_support with support from TurtleBots 17
89 files changed, 5283 insertions, 0 deletions
diff --git a/lib/support/Makefile b/lib/support/Makefile new file mode 100644 index 0000000..56a5568 --- /dev/null +++ b/lib/support/Makefile @@ -0,0 +1,17 @@ +# Makefile para instalar todas las dependencias de lua y bobot + +all: dependency + @echo bobot listo para usar en XO ! + +dependency: + $(MAKE) -C ../serialcomm/lua_bindings + $(MAKE) -C ../lualibusb + $(MAKE) -C ../libs/lua-5.1.4 linux + $(MAKE) -C ../libs/luasocket + +clean: + @rm -f *~ *.o drivers/*~ + $(MAKE) -C ../serialcomm/lua_bindings clean + $(MAKE) -C ../lualibusb clean + $(MAKE) -C ../libs/lua-5.1.4 clean + $(MAKE) -C ../libs/luasocket clean diff --git a/lib/support/bobot-server.lua b/lib/support/bobot-server.lua new file mode 100644 index 0000000..fcaa55b --- /dev/null +++ b/lib/support/bobot-server.lua @@ -0,0 +1,227 @@ +#!/usr/bin/lua + +--[[ + +Syntax + + # lua bobot-server.lua [DEBUG] [connection]* + +Parameters: + DEBUG enables debug printing in bobot + connection a list of connection services to attempt. Supported + values in bobot (for now) are usb, serial and chotox + +If no connection services are provided, defaults to usb and serial. + +Examples: + Start with debug disabled and the dummy chotox service, only: + # lua bobot-server.lua chotox + + Start with debug enabled and the serial services only: + # lua bobot-server.lua DEBUG serial + + Start with debug disabled and the usb and serial services (same as default): + # lua bobot-server.lua usb serial + +--]] + +--package.path=package.path..";./bobot_server/?.lua" +local my_path = debug.getinfo(1, "S").source:match[[^@?(.*[\/])[^\/]-$]] or "./" +package.path=package.path..";"..my_path.."bobot_server/?.lua;" + ..my_path.."lib/?.lua;"..my_path.."?.lua" + +--tcp listening address +local N_PROTOCOLS = 2 +local ADDRESS = "*" +local PORT_B = 2009 --B is for bobot +local PORT_H = 2010 --H is for http + +local TIMEOUT_REFRESH = 3 + +local socket = require("socket") +local process = require("bobot-server-process").process +local http_serve = require("bobot-server-http").serve + +local bobot = require("bobot") + +local set_debug +for i, v in ipairs(arg) do + if v=="DEBUG" then + set_debug=true + table.remove(arg, i) + break + end +end +if set_debug then + bobot.debugprint = print + bobot.debugprint("Debugging messages enabled") +else + bobot.debugprint = function() end +end + + +local server_b = assert(socket.bind(ADDRESS, PORT_B)) +local server_h = assert(socket.bind(ADDRESS, PORT_H)) + +local recvt={[1]=server_b, [2]=server_h} + +devices = {} + +local function get_device_name(d) + +--print("DEVICENAME", d.module, d.hotplug, d.handler) + local board_id, port_id = '', '' + if #bobot.baseboards>1 then + board_id='@'..d.baseboard.idBoard + end + if d.hotplug then + port_id = ':'..d.handler + end + + local n=d.module..board_id..port_id + + if not devices[n] then + return n + end + + local i=2 + local nn=n.."#"..i + while devices[nn] do + i=i+1 + nn=n.."#"..i + end + + return nn +end + +local function read_devices_list() + bobot.debugprint("=Listing Devices") + local bfound + devices={} + for _, bb in ipairs(bobot.baseboards) do + bobot.debugprint("===board ", bb.idBoard) + for _,d in ipairs(bb.devices) do + local regname = get_device_name(d) + devices[regname]=d + devices[#devices+1]=d + d.name=regname + bobot.debugprint("=====module ",d.module," name",regname) + end + bfound = true + end + if not bfound then bobot.debugprint("ls:WARN: No Baseboard found.") end +end + +local function split_words(s) + local words={} + + for p in string.gmatch(s, "%S+") do + words[#words+1]=p + end + + return words +end + +local socket_handlers = {} +setmetatable(socket_handlers, { __mode = 'k' }) +socket_handlers[server_b]=function() + local client, err=server_b:accept() + if not client then return end + bobot.debugprint ("bs:New bobot client", client, client:getpeername()) + table.insert(recvt,client) + socket_handlers[client] = function () + local line,err = client:receive() + if err=='closed' then + bobot.debugprint ("bs:Closing bobot client", client) + for k, v in ipairs(recvt) do + if client==v then + table.remove(recvt,k) + return + end + end + end + if line then + local words=split_words(line) + local command=words[1] + if not command then + bobot.debugprint("bs:Error parsing line:", line, command) + else + if not process[command] then + bobot.debugprint("bs:Command not supported:", command) + else + if command=="QUIT" and #recvt>N_PROTOCOLS+1 then + client:send("server in use\n") + return + end + local ret = process[command](words) or "" + client:send(ret .. "\n") + end + end + end + end +end + +socket_handlers[server_h]=function() + local client, err=server_h:accept() + if not client then return end + bobot.debugprint ("bs:New http client", client, client:getpeername()) + client:setoption ("tcp-nodelay", true) + --client:settimeout(5) + table.insert(recvt,client) + socket_handlers[client] = function () + local ret,err=http_serve(client) + if err=='closed' then + bobot.debugprint ("bs:Closing http client", client) + for k, v in ipairs(recvt) do + if client==v then + table.remove(recvt,k) + return + end + end + end + if ret then + client:send(ret) + end + end +end + +function server_refresh () + local refreshed + for i, bb in ipairs(bobot.baseboards) do + --if bb.refresh and not (bb.comms.type=='serial' and bb.devices) then + if bb.refresh and bb.hotplug then + if not bb:refresh() then + bobot.baseboards[i]=nil + end + refreshed=true + end + end + if refreshed then read_devices_list() end +end + +function server_init () + bobot.init(arg) + read_devices_list() +end + + +server_init() +bobot.debugprint("Listening...") +-- loop forever waiting for clients + +while 1 do + local recvt_ready, _, err=socket.select(recvt, nil, TIMEOUT_REFRESH) + if err=='timeout' then + if #bobot.baseboards==0 then + server_init () + else + server_refresh () + end + else + local skt=recvt_ready[1] + socket_handlers[skt]() + end +end + + + diff --git a/lib/support/bobot.lua b/lib/support/bobot.lua new file mode 100644 index 0000000..c7dd062 --- /dev/null +++ b/lib/support/bobot.lua @@ -0,0 +1,55 @@ +--[[ +bobot library + +Example usage: + bobot=require("bobot") + bobot.init() + +init() can receive a list of connectors to use. Supported values are "usb", "serial" and "chotox". +For example, to only use de dummy chotox driver, use init({"chotox"}) +If no parameter is provided, behaves like init({"usb","serial"}). + +--]] + +local my_path = debug.getinfo(1, "S").source:match[[^@?(.*[\/])[^\/]-$]] or "./" +package.path=package.path..";"..my_path.."lib/?.lua" + +local socket=require('socket') + +local B = {} + +B.debugprint = print --function() end --do not print anything by default + +--baseboards[iSerial] = BaseBoard +--B.baseboards = {} + +--Returns number of baseboards detected. +B.init = function ( comms ) + if not comms or #comms==0 then comms = {"usb","serial"} end + + B.baseboards={} --flush the baseboard because this function could be call after hardware remove or adition + + local n_boards, n_boards_total = {}, 0 + local start_time = os.time() + + repeat + for _, comm in ipairs(comms) do + B.debugprint ("Querying for baseboards:", comm) + local comm_lib = require('comms_'..comm) + if not comm_lib then + B.debugprint("Could not open library:", comm) + else + comm_lib.type=comm + n_boards[comm] = comm_lib.init(B.baseboards) + n_boards_total = n_boards_total + n_boards[comm] + end + end + if n_boards_total == 0 then socket.sleep(1) end + until n_boards_total > 0 or os.time()-start_time > 3 + + return n_boards_total +end + +--B.init() + +return B diff --git a/lib/support/bobot_server/bobot-server-http.lua b/lib/support/bobot_server/bobot-server-http.lua new file mode 100644 index 0000000..efa239d --- /dev/null +++ b/lib/support/bobot_server/bobot-server-http.lua @@ -0,0 +1,211 @@ +module(..., package.seeall); + +local bobot = require("bobot") + +--local devices=devices +local process = require("bobot-server-process").process +local butia = require("butia/butia_http") +local util = require("http-util") + +local parse_params = util.parse_params +local load_template = util.load_file +local find_page = util.find_page + +--enable to cache in RAM +--[[ +local index_template=load_template('indextemplate.txt') +local dump_template=load_template('dumptemplate.txt') +local dump_template_descr=load_template('dumptemplate_descr.txt') +local dump_template_descr_row=load_template('dumptemplate_descr_row.txt') +--]] + +local function check_open_device(d, ep1, ep2) + if not d then return end + if d.handler then return true end + -- if the device is not open, then open the device + bobot.debugprint ("Opening", d.name, d.handler) + return d:open(ep1 or 1, ep2 or 1) --TODO asignacion de ep? +end + +local html_list_devices = function (params) + local ret,comma = "", "" + local dsel=params['dsel'] + for d_name, d in pairs(devices) do + local broken="" + if not check_open_device(d, ep1, ep2) then + broken=" (failed to open)" + bobot.debugprint ("bs: WARN! Failure opening", d_name) + end + if dsel==d_name then + ret = ret .. comma .. '<strong>' .. d_name .. broken .. '</strong>' + else + ret = ret .. comma .. '<a href="/dump.htm?dsel='..d_name..'">'..d_name..broken..'</a>' + end + comma=", " + end + return ret +end + +local html_describe_device = function (params) + --borrar + dump_template_descr_row=load_template('dumptemplate_descr_row.txt') + + local dsel=params['dsel'] + local command=params['command'] + if not dsel then return "" end + + local device=devices[dsel] + + if not device then return "<TR><TD>Missing Device!</TD></TR>" end + if not device.api then return "<TR><TD>Missing Driver for Device!</TD></TR>" end + + local ret = "" + for fname, fdef in pairs(device.api) do + local row=dump_template_descr_row + + local result,ok + --print ("::",command,fname) + if command==fname then + --preparar parametros + local fparams={} + for i,param in ipairs(fdef.parameters) do + local rname=param['rname'] + local rtype=param['rtype'] + if rtype=="int" or rtype=="number" or rtype=="numeric" then + fparams[i]=tonumber(params[rname]) + else + fparams[i]=params[rname] + end + end + + --ejecutar + ok, result= pcall( fdef.call, unpack(fparams) ) + if not ok then bobot.debugprint ("Error calling", ret) end + --imprimir + bobot.debugprint ("::::",result) + end + + local returns='' + local comma='' + for i,rets in ipairs(fdef.returns) do + returns = returns..comma..rets['rtype']..' '..rets['rname'] + comma=',' + end + local formfields='' + local parameters='' + local comma='' + for i,rets in ipairs(fdef.parameters) do + local rname=rets['rname'] + formfields=formfields..rname..': <INPUT TYPE="text" NAME="'..rname..'" /><br>' + parameters = parameters..comma..rets['rtype']..' '..rname + comma=',' + end + + local rep = { + ['COMMAND'] = fname, + ['RETURNS'] = returns, + ['PARAMETERS'] = parameters, + ['RESULT'] = result, + ['MODULENAME'] = dsel, + ['FORMFIELDS'] = formfields, + } + local generated_row=string.gsub(row, '<!%-%-(%w+)%-%->', rep) + + ret=ret..generated_row + + end + + return ret +end + +local get_page={} +setmetatable(get_page, {__index = function(_,page) bobot.debugprint ("======", page);return find_page(page) end}) +get_page["/index.htm"] = function (p) + local index_template=load_template('indextemplate.txt') + + local params=parse_params(p) + local rep = { + ['DATA1'] = params['campo'], + ['DATA2'] = tostring(params['unboton'])..', '..tostring(params['otroboton']), + } + local page=string.gsub(index_template, '<!%-%-(%w+)%-%->', rep) + return "HTTP/1.1 200/OK\r\nContent-Type:text/html\r\nContent-Length: "..#page.."\r\n\r\n"..page +end +get_page["/"]=get_page["/index.htm"] +get_page["/dump.htm"] = function (p) + --remove this + dump_template=load_template('dumptemplate.txt') + dump_template_descr=load_template('dumptemplate_descr.txt') + + local params=parse_params(p) + local dsel=params['dsel'] + + local usetemplate + if dsel then + usetemplate=dump_template_descr + else + usetemplate=dump_template + end + + local rep = { + ['LIST'] = html_list_devices(params), + ['MODULENAME'] = dsel or "(empty dsel)", + ['ROWS'] = html_describe_device(params), + } + local page=string.gsub(usetemplate, '<!%-%-(%w+)%-%->', rep) + return "HTTP/1.1 200/OK\r\nContent-Type:text/html\r\nContent-Length: "..#page.."\r\n\r\n"..page +end +get_page["/favicon.ico"] = function () + local served, err = io.open('bobot_server/favicon.ico', "rb") + if served ~= nil then + local content = served:read("*all") + return "HTTP/1.1 200/OK\r\nContent-Type:image/x-icon\r\nContent-Length: " + ..#content.."\r\n\r\n" .. content + else + bobot.debugprint("Error opening favicon:", err) + return default_page() + end +end +get_page["/bobot.png"] = function () + local served, err = io.open('bobot_server/bobot.png', "rb") + if served ~= nil then + local content = served:read("*all") + return "HTTP/1.1 200/OK\r\nContent-Type:image/png\r\nContent-Length: " + ..#content.."\r\n\r\n" .. content + else + bobot.debugprint("Error opening logo:", err) + return default_page() + end +end +butia.init(get_page) + +function serve(skt) + local line,err = skt:receive('*l') --read first line, must be GET or POST + if err then return nil, err end + + local f,p=string.match(line, '^GET ([%/%.%d%w%-_]+)[%?]?(.-) HTTP/1.1$') + if f then + repeat + --skip headers we don't care + line,err=skt:receive() + until line=='' or line==nil + if err then return end + local s=get_page[f](p) + return(s..'\r\n') + end + + local f,p=string.match(line, '^POST ([%/%.%d%w%-_]+) HTTP/1.1$') + if f then + local length + repeat + --skip headers we don't care + line,err=skt:receive() + length=length or string.match(line, '^Content%-Length%: (%d+)$') + until line=='' or line==nil + if err then return end + local p=skt:receive(tonumber(length)) + local s=get_page[f](p) + return(s..'\r\n') + end +end + diff --git a/lib/support/bobot_server/bobot-server-process.lua b/lib/support/bobot_server/bobot-server-process.lua new file mode 100644 index 0000000..18231cb --- /dev/null +++ b/lib/support/bobot_server/bobot-server-process.lua @@ -0,0 +1,176 @@ +#!/usr/bin/lua + +module(..., package.seeall); + +local bobot = require("bobot") + +--local devices=devices +--local DEBUG = false + +local function check_open_device(d, ep1, ep2) + if not d then return end + if d.handler or d.name=='pnp' then return true end + + -- if the device is not open, then open the device + bobot.debugprint ("Opening", d.name, d.handler) + return d:open(ep1 or 1, ep2 or 1) --TODO asignacion de ep? +end + +process = {} + +process["INIT"] = function () --to check the new state of hardware on the fly + server_init() +end +process["REFRESH"] = function () --to check the new state of hardware on the fly + --for _, bb in ipairs(bobot.baseboards) do + -- bb:refresh() + --end + server_refresh() + return 'ok' +end + + +process["LIST"] = function () + local ret,comma = "", "" + for _, d in ipairs(devices) do + ret = ret .. comma .. d.name + comma="," + end + return ret +end + + +process["LISTI"] = function () + if bobot.baseboards then + bobot.debugprint("listing instanced modules...") + for _, bb in ipairs(bobot.baseboards) do + local handler_size=bb:get_handler_size() + for i=1, handler_size do + t_handler = bb:get_handler_type(i) + bobot.debugprint("handler=", i-1 ," type=" ,t_handler) + end + end + end +end + + +process["OPEN"] = function (parameters) + local d = parameters[2] + local ep1= tonumber(parameters[3]) + local ep2= tonumber(parameters[4]) + + if not d then + bobot.debugprint("ls:Missing 'device' parameter") + return + end + + local device = devices[d] + if check_open_device(device, ep1, ep2) then + return "ok" + else + return "fail" + end +end +process["DESCRIBE"] = function (parameters) + local d = parameters[2] + local ep1= tonumber(parameters[3]) + local ep2= tonumber(parameters[4]) + + if not d then + bobot.debugprint("ls:Missing \"device\" parameter") + return + end + + local device = devices[d] + if not check_open_device(device, ep1, ep2) then + return "fail" + end + if not device.api then + return "missing driver" + end + + local ret = "{" + for fname, fdef in pairs(device.api) do + ret = ret .. fname .. "={" + ret = ret .. " parameters={" + for i,pars in ipairs(fdef.parameters) do + ret = ret .. "[" ..i.."]={" + for k, v in pairs(pars) do + ret = ret .."['".. k .."']='"..tostring(v).."'," + end + ret = ret .. "}," + end + ret = ret .. "}, returns={" + for i,rets in ipairs(fdef.returns) do + ret = ret .. "[" ..i.."]={" + for k, v in pairs(rets) do + ret = ret .."['".. k .."']='"..tostring(v).."'," + end + ret = ret .. "}," + end + ret = ret .. "}}," + end + ret=ret.."}" + + return ret +end +process["CALL"] = function (parameters) + local d = parameters[2] + local call = parameters[3] + + if not (d and call) then + bobot.debugprint("ls:Missing parameters", d, call) + return + end + + local device = devices[d] + if not check_open_device(device, nil, nil) then + return "fail" + end + + if not device.api then return "missing driver" end + local api_call=device.api[call]; + if not api_call then return "missing call" end + + if api_call.call then + --local tini=socket.gettime() + local ok, ret = pcall (api_call.call, unpack(parameters,4)) + if not ok then bobot.debugprint ("Error calling", ret) end + --print ('%%%%%%%%%%%%%%%% bobot-server',socket.gettime()-tini) + return ret + end +end +process["CLOSEALL"] = function () + if bobot.baseboards then + for _, bb in ipairs(bobot.baseboards) do + --this command closes all the open user modules + --it does not have sense with plug and play + bb:force_close_all() --modif andrew + end + end + return "ok" +end +process["BOOTLOADER"] = function () + if bobot.baseboards then + for _, bb in ipairs(bobot.baseboards) do + bb:switch_to_bootloader() + end + end + return "ok" +end +process["DEBUG"] = function (parameters) --disable debug mode Andrew code! + local debug = parameters[2] + if not debug then return "missing parameter" end + if debug=="ON" then + bobot.debugprint = print --function(...) print (...) end --enable printing + elseif debug=="OFF" then + bobot.debugprint = function() end --do not print anything + end + return "ok" +end +process["QUIT"] = function () + bobot.debugprint("Requested EXIT...") + os.exit() + return "ok" +end + diff --git a/lib/support/bobot_server/bobot.png b/lib/support/bobot_server/bobot.png Binary files differnew file mode 100644 index 0000000..2de1124 --- /dev/null +++ b/lib/support/bobot_server/bobot.png diff --git a/lib/support/bobot_server/butia/butia.htm b/lib/support/bobot_server/butia/butia.htm new file mode 100644 index 0000000..ca86673 --- /dev/null +++ b/lib/support/bobot_server/butia/butia.htm @@ -0,0 +1,17 @@ +<html>
+<HEAD>
+ <TITLE>Robot Butia</TITLE>
+</HEAD>
+<frameset rows="25%,50%,25%">
+
+ <frame src="header.htm" />
+ <frameset cols="25%,75%">
+ <frame src="command.htm" />
+ <frame src="view.htm" />
+ </frameset>
+<frame src="sensors.htm" />
+
+</frameset>
+
+</html>
+
diff --git a/lib/support/bobot_server/butia/butia_http.lua b/lib/support/bobot_server/butia/butia_http.lua new file mode 100644 index 0000000..6a0b1ff --- /dev/null +++ b/lib/support/bobot_server/butia/butia_http.lua @@ -0,0 +1,32 @@ +module(..., package.seeall); + +--local devices=devices +local process = require("bobot-server-process").process +local util = require("http-util") + +local parse_params = util.parse_params +local load_template = util.load_file + +local butia_template=load_template('butia/butia.htm') or "Error loading template butia.htm" +local header_template=load_template('butia/header.htm') or "Error loading template header.htm" + +function init (get_page) + get_page["/butia.htm"] = function (p) + local params=parse_params(p) + local rep = { + ['DATA1'] = params['campo'], + ['DATA2'] = tostring(params['unboton'])..', '..tostring(params['otroboton']), + } + local page=string.gsub(butia_template, '<!%-%-(%w+)%-%->', rep) + return "HTTP/1.1 200/OK\r\nContent-Type:text/html\r\nContent-Length: "..#page.."\r\n\r\n"..page + end + + get_page["/header.htm"] = function (p) + local params=parse_params(p) + local rep = { + } + local page=string.gsub(header_template, '<!%-%-(%w+)%-%->', rep) + return "HTTP/1.1 200/OK\r\nContent-Type:text/html\r\nContent-Length: "..#page.."\r\n\r\n"..page + end + +end diff --git a/lib/support/bobot_server/butia/command.htm b/lib/support/bobot_server/butia/command.htm new file mode 100644 index 0000000..9cec722 --- /dev/null +++ b/lib/support/bobot_server/butia/command.htm @@ -0,0 +1,36 @@ +<HTML>
+
+<BODY BGCOLOR="#bfbfbf" TEXT="#000000" VLINK="#000099" LINK="#9900ff" ALINK="#FFFF00">
+<CENTER>
+<h1 style="color: rgb(0, 153, 0);">Haz click en los botones para dirigir el robot</h1>
+<hr>
+<BR>
+<TABLE BORDER=1 WIDTH="95%" CELLSPACING=3 CELLPADDING=3>
+<TR>
+ <TD ALIGN="center" VALIGN="top">
+ <TABLE BORDER=1 WIDTH="95%" CELLSPACING=3 CELLPADDING=3>
+ <TR>
+ <TD ALIGN="center"></TD>
+ <TD ALIGN="center"><A HREF="up.htm"><IMG SRC="images/btar_up.gif" BORDER=0 WIDTH=20 HEIGHT=20></A></TD>
+ <TD ALIGN="center"></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="center"><A HREF="left.htm"><IMG SRC="images/btar_lft.gif" BORDER=0 WIDTH=20 HEIGHT=20></A></TD>
+ <TD ALIGN="center"></TD>
+ <TD ALIGN="center"><A HREF="right.htm"><IMG SRC="images/btar_rgt.gif" BORDER=0 WIDTH=20 HEIGHT=20></A></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="center"></TD>
+ <TD ALIGN="center"><A HREF="down.htm"><IMG SRC="images/btar_dn.gif" BORDER=0 WIDTH=20 HEIGHT=20></A></TD>
+ <TD ALIGN="center"></TD>
+ </TR>
+ </TABLE>
+ </TD>
+</TR>
+</TABLE>
+<P>
+
+</CENTER>
+</BODY>
+</HTML>
+
diff --git a/lib/support/bobot_server/butia/describeButia.htm b/lib/support/bobot_server/butia/describeButia.htm new file mode 100644 index 0000000..b147468 --- /dev/null +++ b/lib/support/bobot_server/butia/describeButia.htm @@ -0,0 +1,88 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<HTML>
+<HEAD>
+<TITLE>
+butia (Describe)
+</TITLE>
+</HEAD>
+
+<BODY BGCOLOR="white" >
+<HR>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0" SUMMARY="">
+<TR>
+<TD COLSPAN=2 BGCOLOR="#EEEEFF" >
+<A NAME="navbar_top_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3" SUMMARY="">
+ <TR ALIGN="center" VALIGN="top">
+ <TD BGCOLOR="#EEEEFF" > <FONT ><B>DESCRIBE butia</B></FONT> </TD>
+ <TD BGCOLOR="#EEEEFF" > <A HREF="lback.htm"><FONT><B>DESCRIBE lback</B></FONT></A> </TD>
+ <TD BGCOLOR="#FFFFFF" > <A HREF="dist.htm"><FONT ><B>DESCRIBE dist</B></FONT></A> </TD>
+
+ </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+<b>Butia<br>Robot Educativo</b></EM>
+</TD>
+</TR>
+</TABLE>
+<HR>
+<H2>
+<FONT SIZE="-1">
+Funciones disponibles en </FONT>
+<BR>
+butia</H2>
+
+<HR>
+
+<P>
+A continuación se lista las funciones disponibles en este módulo.
+<br>
+Puedes invocar la función ingresando los datos necesarios y presionando el botón Submit.
+<p>
+
+<P>
+
+<A NAME="method_summary"><!-- --></A>
+<TABLE BORDER="1" WIDTH="100%" CELLPADDING="3" CELLSPACING="0" SUMMARY="">
+<TR BGCOLOR="#CCCCFF" >
+<TH ALIGN="left" COLSPAN="2"><FONT SIZE="+2">
+<B>Resumen de Funciones</B></FONT></TH>
+</TR>
+
+
+<TR BGCOLOR="white" >
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE> data (String)</CODE></FONT></TD>
+<TD><CODE><B>read_ver</B>(data number)</CODE>
+
+<BR>
+
+<FORM METHOD="POST" ACTION="evaluate_function.do">
+<HIDDEN VALUE="read_ver"/>
+data: <INPUT TYPE="text" NAME="data" />
+<INPUT TYPE="submit" VALUE="Submit" />
+</FORM>
+</TD>
+</TR>
+
+<TR BGCOLOR="white" >
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE> data (String)</CODE></FONT></TD>
+<TD><CODE><B>get_volt</B>()</CODE>
+
+<BR>
+
+<FORM METHOD="POST" ACTION="evaluate_function.do">
+<HIDDEN VALUE="get_volt"/>
+<INPUT TYPE="submit" VALUE="Submit" />
+</FORM>
+</TD>
+</TR>
+
+</TABLE>
+
+<P>
+
+</BODY>
+</HTML>
\ No newline at end of file diff --git a/lib/support/bobot_server/butia/header.htm b/lib/support/bobot_server/butia/header.htm new file mode 100644 index 0000000..7d64136 --- /dev/null +++ b/lib/support/bobot_server/butia/header.htm @@ -0,0 +1,10 @@ +<HTML>
+
+<BODY BGCOLOR="#bfbfbf" TEXT="#000000" VLINK="#000099" LINK="#9900ff" ALINK="#FFFF00">
+<CENTER>
+<H1>ROBOT BUTIA</H1>
+<IMG SRC="butia/images/butiaRobot3.png" style="width: 152px; height: 113px;" ALT="Butia" align="middle"><P>
+<HR SIZE=1 NOSHADE>
+</CENTER>
+</BODY>
+</HTML>
diff --git a/lib/support/bobot_server/butia/images/btar_dn.gif b/lib/support/bobot_server/butia/images/btar_dn.gif Binary files differnew file mode 100644 index 0000000..7f6b4f3 --- /dev/null +++ b/lib/support/bobot_server/butia/images/btar_dn.gif diff --git a/lib/support/bobot_server/butia/images/btar_lft.gif b/lib/support/bobot_server/butia/images/btar_lft.gif Binary files differnew file mode 100644 index 0000000..e104fe4 --- /dev/null +++ b/lib/support/bobot_server/butia/images/btar_lft.gif diff --git a/lib/support/bobot_server/butia/images/btar_rgt.gif b/lib/support/bobot_server/butia/images/btar_rgt.gif Binary files differnew file mode 100644 index 0000000..4cc5ae0 --- /dev/null +++ b/lib/support/bobot_server/butia/images/btar_rgt.gif diff --git a/lib/support/bobot_server/butia/images/btar_up.gif b/lib/support/bobot_server/butia/images/btar_up.gif Binary files differnew file mode 100644 index 0000000..e3180df --- /dev/null +++ b/lib/support/bobot_server/butia/images/btar_up.gif diff --git a/lib/support/bobot_server/butia/images/butiaRobot3.png b/lib/support/bobot_server/butia/images/butiaRobot3.png Binary files differnew file mode 100644 index 0000000..f7e74cf --- /dev/null +++ b/lib/support/bobot_server/butia/images/butiaRobot3.png diff --git a/lib/support/bobot_server/butia/images/clase07.jpg b/lib/support/bobot_server/butia/images/clase07.jpg Binary files differnew file mode 100644 index 0000000..acc498f --- /dev/null +++ b/lib/support/bobot_server/butia/images/clase07.jpg diff --git a/lib/support/bobot_server/butia/sensors.htm b/lib/support/bobot_server/butia/sensors.htm new file mode 100644 index 0000000..dca9244 --- /dev/null +++ b/lib/support/bobot_server/butia/sensors.htm @@ -0,0 +1,40 @@ +<HTML>
+
+<BODY >
+<CENTER>
+
+<table cellspacing="0" cellpadding="5" border="1" align="top">
+<tr>
+<th> Sensor Name
+</th><th> Type (A/D)
+</th><th> Value
+</th><th> Other
+</th></tr>
+<tr>
+<td> Temperature
+</td><td> D
+</td><td> 23
+</td><td> C
+</td></tr>
+<tr>
+<td> Pote
+</td><td> A
+</td><td> 100
+</td><td>
+</td></tr>
+<tr>
+<td> Gas
+</td><td> A
+</td><td> 15
+</td><td>
+</td></tr>
+<tr>
+<td> Button
+</td><td> D
+</td><td> 1
+</td><td> Pressed
+</td></tr>
+
+</CENTER>
+</BODY>
+</HTML>
\ No newline at end of file diff --git a/lib/support/bobot_server/butia/view.htm b/lib/support/bobot_server/butia/view.htm new file mode 100644 index 0000000..0fda3dc --- /dev/null +++ b/lib/support/bobot_server/butia/view.htm @@ -0,0 +1,10 @@ +<HTML>
+
+<BODY BGCOLOR="#bfbfbf" TEXT="#000000" VLINK="#000099" LINK="#9900ff" ALINK="#FFFF00">
+<CENTER>
+
+<IMG SRC="images/clase07.jpg" WIDTH=100% HEIGHT=100% ALT="Butia"><P>
+
+</CENTER>
+</BODY>
+</HTML>
\ No newline at end of file diff --git a/lib/support/bobot_server/dumptemplate.txt b/lib/support/bobot_server/dumptemplate.txt new file mode 100644 index 0000000..b1c1dfa --- /dev/null +++ b/lib/support/bobot_server/dumptemplate.txt @@ -0,0 +1,11 @@ +<html> +<head><title>Bobot dump</title></head> +<body> +<h2>Bobot</h2> +<a href="/index.htm">home</a> | modules +<hr> +Modules: <!--LIST--><br> + +<hr> +</body> +</html> diff --git a/lib/support/bobot_server/dumptemplate_descr.txt b/lib/support/bobot_server/dumptemplate_descr.txt new file mode 100644 index 0000000..e34df38 --- /dev/null +++ b/lib/support/bobot_server/dumptemplate_descr.txt @@ -0,0 +1,37 @@ +<html> +<head><title>Bobot dump</title></head> +<body> +<h2>Bobot dump</h2> +<a href="/index.htm">home</a> | dump +<hr> +Modules: <!--LIST--><br> + +<HR> +<H2> +<FONT SIZE="-1"> +Funciones disponibles en </FONT> +<BR> +<!--MODULENAME--></H2> + +<HR> + +<P> +A continuación se lista las funciones disponibles en este módulo. +<br> +Puedes invocar la función ingresando los datos necesarios y presionando el botón Submit. +<p> + +<P> + +<A NAME="method_summary"><!-- --></A> +<TABLE BORDER="1" WIDTH="100%" CELLPADDING="3" CELLSPACING="0" SUMMARY=""> +<TR BGCOLOR="#CCCCFF" > +<TH ALIGN="left" COLSPAN="2"><FONT SIZE="+2"> +<B>Resumen de Funciones</B></FONT></TH> +</TR> + + +<!--ROWS--><br> +<hr> +</body> +</html> diff --git a/lib/support/bobot_server/dumptemplate_descr_row.txt b/lib/support/bobot_server/dumptemplate_descr_row.txt new file mode 100644 index 0000000..ade4736 --- /dev/null +++ b/lib/support/bobot_server/dumptemplate_descr_row.txt @@ -0,0 +1,20 @@ +<TR BGCOLOR="white" > +<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1"> +<CODE>(<!--RETURNS-->)</CODE></FONT></TD> +<TD><CODE><B><!--COMMAND--></B>(<!--PARAMETERS-->)</CODE> + +<BR> + +<FORM METHOD="POST" ACTION="/dump.htm"> +<INPUT TYPE=HIDDEN NAME="command" VALUE="<!--COMMAND-->"/> +<INPUT TYPE=HIDDEN NAME="dsel" VALUE="<!--MODULENAME-->"/> +<!--FORMFIELDS--> +<INPUT TYPE="submit" VALUE="Submit" /> +</FORM> + + +<!--RESULT--> + +</TD> +</TR> + diff --git a/lib/support/bobot_server/favicon.ico b/lib/support/bobot_server/favicon.ico Binary files differnew file mode 100644 index 0000000..3834805 --- /dev/null +++ b/lib/support/bobot_server/favicon.ico diff --git a/lib/support/bobot_server/http-util.lua b/lib/support/bobot_server/http-util.lua new file mode 100644 index 0000000..c8b9a6c --- /dev/null +++ b/lib/support/bobot_server/http-util.lua @@ -0,0 +1,41 @@ +module(..., package.seeall); + +local bobot = require("bobot") + +local my_path = debug.getinfo(1, "S").source:match[[^@?(.*[\/])[^\/]-$]] + +--local devices=devices +function load_file (filename) + local served, err = io.open(my_path..filename, "r") + if served then + return served:read("*all") + else + bobot.debugprint("Error opening", my_path..filename,":", err) + end +end + +function parse_params(s) + local params={} + for k,v in string.gmatch(s, '([%w%%%_%-]+)=([%w%%%_%-]+)') do + bobot.debugprint('param', k, v) + params[k]=v + end + return params +end + +local page404="<html><head><title>404 Not Found</title></head><body><h3>404 Not Found!</h3><hr><small>bobot</small></body></html>" +local http404="HTTP/1.1 404 Not Found\r\nContent-Type:text/html\r\nContent-Length: "..#page404.."\r\n\r\n" .. page404 +page404=nil +local function error_page() + return http404 +end + +function find_page (page) + local page=string.sub(page,2) + local file=load_file(page) + if file then + return function() return file end + else + return error_page + end +end diff --git a/lib/support/bobot_server/indextemplate.txt b/lib/support/bobot_server/indextemplate.txt new file mode 100644 index 0000000..24b2245 --- /dev/null +++ b/lib/support/bobot_server/indextemplate.txt @@ -0,0 +1,16 @@ +<html> +<head><title>Bobot</title></head> +<body> +<h2>Bobot</h2> +home | <a href="/dump.htm">modules</a> +<hr> + +<img src="/bobot.png" alt="bobot logo" width="200" height="200" /> +<p> +<a href="http://www.fing.edu.uy/inco/grupos/mina/">Grupo MINA</a><br> +In.Co., Facultad de Ingeniería<br> +Universidad de la República<br> +Uruguay</p> +<hr> +</body> +</html> diff --git a/lib/support/drivers/admin.lua b/lib/support/drivers/admin.lua new file mode 100644 index 0000000..c3e3da9 --- /dev/null +++ b/lib/support/drivers/admin.lua @@ -0,0 +1,11 @@ +local device = _G +local RESET = string.char(0xFF) + +api={} +api.reset = {} +api.reset.parameters = {} +api.reset.returns = {} +api.reset.call = function (data) + device:send(RESET) +end + diff --git a/lib/support/drivers/ax.lua b/lib/support/drivers/ax.lua new file mode 100644 index 0000000..d505511 --- /dev/null +++ b/lib/support/drivers/ax.lua @@ -0,0 +1,118 @@ +local device = _G +local WRITE_INFO = 0x01 +local READ_INFO = 0x02 +local GET_RAW_POS = 0x03 +local char000 = string.char(0,0,0) +local mode='wheel' + +--byte id,byte regstart, int value +api={} +api.write_info = {} +api.write_info.parameters = {[1]={rname="id", rtype="number", min=0, max=255},[2]={rname="regstart", rtype="number", min=0, max=255},[3]={rname="value", rtype="number", min=0, max=65536}} ----byte id,byte regstart, int value +api.write_info.returns = {[1]={rname="write_info_return", rtype="number"}} --one return +api.write_info.call = function (id, regstart, value) + id, regstart, value = tonumber(id), tonumber(regstart), tonumber(value) + local write_info_payload = string.char(WRITE_INFO, id, regstart, math.floor(value / 256),value % 256) + device:send(write_info_payload) + local write_info_response = device:read(2) or char000 + local raw_val = (string.byte(write_info_response, 2) or 0) + return raw_val +end + +--- Set wheel mode. +--Set the motor to continuous rotation mode. +api.wheel_mode = {} +api.wheel_mode.parameters = {[1]={rname="id", rtype="number", min=0, max=255}} +api.wheel_mode.returns = {} +api.wheel_mode.call = function (id ) + id = tonumber(id) + local ret = device:send(string.char(WRITE_INFO,id,0x06,0x00,0x00)) + local write_info_response = device:read(1) or string.char(0,0) + local ret = device:send(string.char(WRITE_INFO,id,0x08,0x00,0x00)) + local write_info_response = device:read(1) or string.char(0,0) + mode='wheel' + end + +--- Set joint mode. +-- Set the motor to joint mode. Angles are provided in degrees, +-- in the full servo coverage (0 - 300 degrees arc) +-- @param min the minimum joint angle (defaults to 0) +-- @param max the maximum joint angle (defaults to 300) +api.joint_mode = {} +api.joint_mode.parameters = {[1]={rname="id", rtype="number", min=0, max=255},[2]={rname="minimo", rtype="number", min=0, max=1023},[3]={rname="maximo", rtype="number", min=0, max=1023}} +api.joint_mode.returns = {} +api.joint_mode.call = function (id ,minimo, maximo) + id = tonumber(id) + minimo=tonumber(minimo) + maximo=tonumber(maximo) + local ret = device:send(string.char(WRITE_INFO,id,0x06,math.floor(minimo / 256),minimo % 256)) + local write_info_response = device:read(1) or string.char(0,0) + local ret = device:send(string.char(WRITE_INFO,id,0x08,math.floor(maximo / 256),maximo % 256)) + local write_info_response = device:read(1) or string.char(0,0) + mode='joint' + end + + +--- Set motor position. +-- Set the target position for the motor's axle. Only works in +-- joint mode. +-- @param value Angle in degrees, in the 0 .. 300deg range. +api.set_position = {} +api.set_position.parameters = {[1]={rname="id", rtype="number", min=0, max=255},[2]={rname="pos", rtype="number", min=0, max=1023}} +api.set_position.returns = {} +api.set_position.call = function (id, pos ) + id = tonumber(id) + pos = tonumber(pos) + local ret = device:send(string.char(WRITE_INFO,id,0x1E,math.floor(pos / 256),pos % 256)) + local write_info_response = device:read(1) or string.char(0,0) + end + + +--- Get motor position. +-- Read the axle position from the motor. +-- @return The angle in deg. The reading is only valid in the +-- 0 .. 300deg range +api.get_position = {} +api.get_position.parameters = {[1]={rname="id", rtype="number", min=0, max=255},[2]={rname="pos", rtype="number", min=0, max=1023}} +api.get_position.returns = {[1]={rname="motor_position", rtype="number"}} --one return +api.get_position.call = function(id) + local send_response = device:send(string.char(GET_RAW_POS,id)) + local value = device:read(3) or string.char(0,0,0) + local h_value = string.byte(value, 2) + local l_value = string.byte(value, 3) + local raw_angle = 256 * h_value + l_value + local ang=0.29*(raw_angle) -- deg ? + return raw_angle + + end + + +--- Set motor speed. +-- @param value If motor in joint mode, speed in deg/sec in the 1 .. 684 range +-- (0 means max available speed). +-- If in wheel mode, as a fraction of max torque (in the -1 .. 1 range). +api.set_speed = {} +api.set_speed.parameters = {[1]={rname="id", rtype="number", min=0, max=255},[2]={rname="speed", rtype="number", min=-1, max=684}} +api.set_speed.returns = {} --no return +api.set_speed.call = function(id, speed) + --if mode=='joint' then + -- 0 .. 684 deg/sec + local vel=math.floor(speed * 1.496) + local lowb = math.floor(vel/ 256) + local highb = vel % 256 + + + --local lowb, highb = get2bytes_unsigned(vel) + local ret = device:send(id,0x20,string.char(lowb,highb)) + print ("ret= ",ret, " lowb= ", lowb, " highb= ", highb) + if ret then return ret:byte() end + --else --mode=='wheel' + -- -1 .. +1 max torque + --local vel=math.floor(speed * 1023) + --local lowb, highb = get2bytes_signed(vel) + --local ret = device:send(id,0x20,string.char(lowb,highb)) + --if ret then return ret:byte() end + --end +end + + diff --git a/lib/support/drivers/boot.lua b/lib/support/drivers/boot.lua new file mode 100644 index 0000000..2471ab9 --- /dev/null +++ b/lib/support/drivers/boot.lua @@ -0,0 +1,11 @@ +local device = _G +local RESET = string.char(0xFF) + +api={} +api.reset = {} +api.reset.parameters = {} --no parameters +api.reset.returns = {} --no returns +api.reset.call = function () + device:send(RESET) +end + diff --git a/lib/support/drivers/boton.lua b/lib/support/drivers/boton.lua new file mode 100644 index 0000000..4f846c9 --- /dev/null +++ b/lib/support/drivers/boton.lua @@ -0,0 +1,19 @@ +local device = _G + +local GET_VALUE=string.char(0x00) +local string_byte=string.byte + +-- description: lets us know button's current status +-- input: empty +-- output: button's current status. Possible status: 1 pressed - 0 not pressed +api={} +api.getValue = {} +api.getValue.parameters = {} -- no input parameters +api.getValue.returns = {[1]={rname="state", rtype="int"}} +api.getValue.call = function () + device:send(GET_VALUE) -- operation code 1 = get button's status + local sen_dig_response = device:read(2) -- 2 bytes to read (opcode, data) + if not sen_dig_response or #sen_dig_response~=2 then return -1 end + local raw_val = string_byte(sen_dig_response, 2) or 0 -- keep data + return raw_val +end diff --git a/lib/support/drivers/butia.lua b/lib/support/drivers/butia.lua new file mode 100644 index 0000000..b29c9f1 --- /dev/null +++ b/lib/support/drivers/butia.lua @@ -0,0 +1,39 @@ +local device = _G +local RD_VERSION = string.char(0x02) -- lee la versión del firmware de la placa (Igual para 1.0 y 2.0) +local GET_VOLT = string.char(0x03) -- obtiene el voltage de la baterÃa (Igual para 1.0 y 2.0) + +api={} +api.read_ver = {} +api.read_ver.parameters = {} +api.read_ver.returns = {[1]={rname="data", rtype="string"}} +api.read_ver.call = function (data) + device:send(RD_VERSION) + local devolver = -1 + local ret = device:read(2) + if ret then + devolver = string.byte(ret , 2) --leo el segundo byte obtenido que tiene la versión (el primero tiene el opcode) + end + --local devolver = (string.byte(version_response,2) or 0) + (string.byte(version_response,3) or 0)* 256 + return devolver +end + +api.get_volt = {} +api.get_volt.parameters = {} -- no se envian parámetros +api.get_volt.returns = {[1]={rname="volts", rtype="string"}} --nos devuelve el voltaje de las baterÃas +api.get_volt.call = function () + device:send(GET_VOLT) --envÃo el código de operación + local data_in = device:read(2) --leo 2 bytes, primero el código de operación y segundo el voltaje + local voltaje = string.byte(data_in or "00000000" , 2) --leo el segundo byte obtenido que es el que tiene el voltaje + --local resultado = string.char(math.floor(voltNum / 10),".",tonumber(volNum) % 256, " volts") + return voltaje +end + +--[[ +function bytesToString(data) + local data_hex = "" + for i=1, string.len(data) do + data_hex = data_hex .. string.format('%02X', (string.byte(data, i))) + end + return data_hex +end +--]] diff --git a/lib/support/drivers/buzzer.lua b/lib/support/drivers/buzzer.lua new file mode 100644 index 0000000..a2ede05 --- /dev/null +++ b/lib/support/drivers/buzzer.lua @@ -0,0 +1,51 @@ +local device = _G +local RD_VERSION = string.char(0x00) +local PRENDER = string.char(0x01) +local APAGAR = string.char(0x02) +local BUZZER_CORTO = string.char(0x03) +local BUZZER_TRIPLE = string.char(0x04) + +api={} +api.read_version = {} +api.read_version.parameters = {} --no parameters +api.read_version.returns = {[1]={rname="version", rtype="number"}} --one return +api.read_version.call = function () + local get_read_version = RD_VERSION + device:send(get_read_version) + local version_response = device:read(2) + local raw_val = string.byte(version_response, 2) + --print("rawval, deg_temp: ", raw_val, deg_temp) + return raw_val +end + +api.prender = {} +api.prender.parameters = {} --no parameters +api.prender.returns = {} --no return +api.prender.call = function () + local write_res, err = device:send(PRENDER) + if write_res then return 1 else return 0 end +end + +api.apagar = {} +api.apagar.parameters = {} --no parameters +api.apagar.returns = {} --no return +api.apagar.call = function () + local write_res, err = device:send(APAGAR) + if write_res then return 1 else return 0 end +end + +api.buzzer_corto = {} +api.buzzer_corto.parameters = {[1]={rname="num", rtype="number"}} +api.buzzer_corto.returns = {} --no return +api.buzzer_corto.call = function (num) + local write_res, err = device:send(BUZZER_CORTO .. string.char(num)) + if write_res then return 1 else return 0 end +end + +api.buzzer_triple = {} +api.buzzer_triple.parameters = {[1]={rname="tiempo1", rtype="number"}, [2]={rname="tiempo2", rtype="number"}, [3]={rname="tiempo3", rtype="number"}} +api.buzzer_triple.returns = {} --no return +api.buzzer_triple.call = function (tiempo1, tiempo2, tiempo3) + local write_res, err = device:send(BUZZER_TRIPLE .. string.char(tiempo1) .. string.char(tiempo2) .. string.char(tiempo3)) + if write_res then return 1 else return 0 end +end diff --git a/lib/support/drivers/debug.lua b/lib/support/drivers/debug.lua new file mode 100644 index 0000000..d8191ec --- /dev/null +++ b/lib/support/drivers/debug.lua @@ -0,0 +1,39 @@ +local device = _G +local RD_VERSION = string.char(0x00) +local RD_DEBUG = string.char(0x01) +local MESSAGE = string.char(0x02) + + +api={} +api.read_version = {} +api.read_version.parameters = {} --no parameters +api.read_version.returns = {[1]={rname="version", rtype="number"}} --one return +api.read_version.call = function () + local get_read_version = RD_VERSION + device:send(get_read_version) + local version_response = device:read(2) + local raw_val = string.byte(version_response, 2) + --print("rawval, deg_temp: ", raw_val, deg_temp) + --return raw_val + return version_response +end + +api.rd_debug = {} +api.rd_debug.parameters = {} --no parameters +api.rd_debug.returns = {[1]={rname="data", rtype="string"}} --debug message +api.rd_debug.call = function () + local write_res, err = device:send(RD_DEBUG) + local len = 4 + local ret = device:read(len) or "" + print("====",ret, string.len(ret)) + return ret +end + +api.message = {} +api.message.parameters = {} --no parameters +api.message.returns = {} +api.message.call = function () + local write_res, err = device:send(MESSAGE) + return write_res +end + diff --git a/lib/support/drivers/display.lua b/lib/support/drivers/display.lua new file mode 100644 index 0000000..3629cb9 --- /dev/null +++ b/lib/support/drivers/display.lua @@ -0,0 +1,99 @@ +local device = _G + +local string_char=string.char + +local RD_VERSION = string_char(0x00) +local ESCRIBIR = string_char(0x01) +local PRUEBA = string_char(0x02) +local BORRAR = string_char(0x03) +local INICIAR = string_char(0x04) +local PRENDER_BKL = string_char(0x08) +local APAGAR_BKL = string_char(0x09) +local AGENDAR_MSG = string_char(0x0A) +local SET_TICKS = string_char(0x0B) +local ESPACIO = string_char(0x20) + + +api={} +api.read_version = {} +api.read_version.parameters = {} --no parameters +api.read_version.returns = {[1]={rname="version", rtype="number"}} --one return +api.read_version.call = function () + device:send(RD_VERSION) + local version_response = device:read(2) + local raw_val = string.byte(temperature_response, 2) + --print("rawval, deg_temp: ", raw_val, deg_temp) + return raw_val +end + +api.escribir = {} +api.escribir.parameters = {[1]={rname="message", rtype="string"}} +api.escribir.returns = {} +api.escribir.call = function (str) + local msg = ESCRIBIR .. str + device:send(msg) + --device:read() +end + +api.prueba = {} +api.prueba.parameters = {} --no parameters +api.prueba.returns = {} --no return +api.prueba.call = function () + device:send(PRUEBA) +end + +api.borrar = {} +api.borrar.parameters = {} --no parameters +api.borrar.returns = {} --no return +api.borrar.call = function () + device:send(BORRAR) +end + +api.iniciar = {} +api.iniciar.parameters = {} --no parameters +api.iniciar.returns = {} --no return +api.iniciar.call = function () + device:send(INICIAR) +end + +api.prender_bkl = {} +api.prender_bkl.parameters = {} --no parameters +api.prender_bkl.returns = {} --no return +api.prender_bkl.call = function () + device:send(PRENDER_BKL) +end + +api.apagar_bkl = {} +api.apagar_bkl.parameters = {} --no parameters +api.apagar_bkl.returns = {} --no return +api.apagar_bkl.call = function () + device:send(APAGAR_BKL) +end + +api.set_ticks = {} +api.set_ticks.parameters = {[1]={rname="cantTicks", rtype="numeric"}} --how many timmers ticks have to pass before display agendar_msg screen +api.set_ticks.returns = {} --no return +api.set_ticks.call = function (ticks) + device:send(SET_TICKS .. string_char(ticks)) +end + +api.agendar_msg = {} +api.agendar_msg.parameters = { + [1]={rname="message", rtype="string"}, -- first parameter is the message + [2]={rname="isCiclic", rtype="numeric"}, -- 1 in case of a ciclic message 0 elsewere + [3]={rname="isEnd", rtype="numeric"}} -- 1 in case of end of message, so previously calls to agendar_msg could be concatenated +api.agendar_msg.returns = {[1]={rname="isFull", rtype="boolean"}} --If it is no more space in the event buffer returns true, false elsewere +api.agendar_msg.call = function (str, isCiclic, isEnd) + device:send(AGENDAR_MSG .. string_char(32) .. string_char(isEnd) .. string_char(isCiclic) .. str) + print("despues del send") + local ret = device:read(3) or char0000 --el tercer byte recibido indica con un 1 si el buffer circuilar que se utiliza para mantener los mensajes esta lleno, 0 en caso controario + print("despues del read") + local error = (string.byte(ret,3) or 0) + print("despues de capturar el 3er byte") + if(error == 1) then + print "error, buffer lleno" + else + print "ok" + end + return error +end diff --git a/lib/support/drivers/dist.lua b/lib/support/drivers/dist.lua new file mode 100644 index 0000000..5b8a247 --- /dev/null +++ b/lib/support/drivers/dist.lua @@ -0,0 +1,25 @@ +local device = _G + +local GET_VALUE=string.char(0x00) +local string_byte=string.byte + +-- description: lets us know dist sensor's current value +-- input: empty +-- output: dist sensor's current value. +api={} +api.getValue = {} +api.getValue.parameters = {} -- no input parameters +api.getValue.returns = {[1]={rname="par1", rtype="int"}} +api.getValue.call = function () + device:send(GET_VALUE) -- operation code 1 = get distance of object + local sen_anl_response = device:read(3) -- operation code and data + if not sen_anl_response or #sen_anl_response~=3 then return -1 end + --local raw_val = (string_byte(sen_anl_response, 2) or 0) + (string_byte(sen_anl_response, 3) or 0)* 256 + raw_val = string.byte(sen_anl_response, 2)* 256 + string.byte(sen_anl_response, 3) + raw_val = 1024 - raw_val + return raw_val +end + + + + diff --git a/lib/support/drivers/dynamix.lua b/lib/support/drivers/dynamix.lua new file mode 100644 index 0000000..52ea4ba --- /dev/null +++ b/lib/support/drivers/dynamix.lua @@ -0,0 +1,34 @@ +local device = _G +local WRITE_INFO = 0x01 +local SEND_BUS = 0x01 +local READ_INFO = 0x02 +local GET_RAW_POS = 0x03 +local char000 = string.char(0,0,0) + +--byte id,byte regstart, int value +api={} +api.write_info = {} +api.write_info.parameters = {[1]={rname="id", rtype="number", min=0, max=255},[2]={rname="regstart", rtype="number", min=0, max=255},[3]={rname="value", rtype="number", min=0, max=65536}} ----byte id,byte regstart, int value +api.write_info.returns = {[1]={rname="write_info_return", rtype="number"}} --one return +api.write_info.call = function (id, regstart, value) + id, regstart, value = tonumber(id), tonumber(regstart), tonumber(value) + local write_info_payload = string.char(WRITE_INFO, id, regstart, math.floor(value / 256),value % 256) + device:send(write_info_payload) + local write_info_response = device:read(2) or char000 + local raw_val = (string.byte(write_info_response, 2) or 0) + return raw_val +end + +api.pasarela = {} +api.pasarela.parameters = {[1]={rname="packet", rtype="string", min=0, max=255}} +api.pasarela.returns = {} +api.pasarela.call = function (value) + local valueN = tonumber(value) + local checksum = 255 - ((0xfe + 0x04 + 0x03 + 0x19 +valueN)%256) + print (checksum) + local paquete = string.char(SEND_BUS, 0xff, 0xff,0xfe, 0x04, 0x03, 0x19, valueN, checksum) + device:send(paquete) +end + + + diff --git a/lib/support/drivers/gas.lua b/lib/support/drivers/gas.lua new file mode 100644 index 0000000..a056759 --- /dev/null +++ b/lib/support/drivers/gas.lua @@ -0,0 +1,20 @@ +local device = _G +local RD_GAS = string.char(0x01) +local char000 = string.char(0,0,0) + +api={} +api.get_gas = {} +api.get_gas.parameters = {} --no parameter +api.get_gas.returns = {[1]={rname="gas level", rtype="number"}} --one return +api.get_gas.call = function () + local get_payload = RD_GAS + device:send(get_payload) + local response = device:read(3) + if not response then + print ('WARN: api.get_gas.call failure on device:read(3)') + response=char000 + end + local raw_val = string.byte(response, 2) + 255*string.byte(response, 3) + return raw_val +end + diff --git a/lib/support/drivers/grises.lua b/lib/support/drivers/grises.lua new file mode 100644 index 0000000..a680266 --- /dev/null +++ b/lib/support/drivers/grises.lua @@ -0,0 +1,19 @@ +local device = _G + +local GET_VALUE=string.char(0x00) +local string_byte=string.byte + +-- description: lets us know grey sensor's current value +-- input: empty +-- output: grey sensor's current value. +api={} +api.getValue = {} +api.getValue.parameters = {} -- no input parameters +api.getValue.returns = {[1]={rname="par1", rtype="int"}} +api.getValue.call = function () + device:send(GET_VALUE) -- operation code 1 = get grey level + local sen_anl_response = device:read(3) -- operation code and data + if not sen_anl_response or #sen_anl_response~=3 then return -1 end + local raw_val = math.floor(((string_byte(sen_anl_response, 2) or 0) + (string_byte(sen_anl_response, 3) or 0)* 256)/ 100) + return raw_val +end diff --git a/lib/support/drivers/hotplug/button.lua b/lib/support/drivers/hotplug/button.lua new file mode 100644 index 0000000..9ddba48 --- /dev/null +++ b/lib/support/drivers/hotplug/button.lua @@ -0,0 +1,32 @@ +local device = _G + +local RD_VERSION=string.char(0x00) +local GET_VALUE=string.char(0x01) +local string_byte=string.byte + +-- description: lets us know button module's version +api={} +api.getVersion = {} +api.getVersion.parameters = {} -- no input parameters +api.getVersion.returns = {[1]={rname="version", rtype="int"}} +api.getVersion.call = function () + device:send(RD_VERSION) -- operation code 0 = get version + local version_response = device:read(3) -- 3 bytes to read (opcode, data) + if not version_response or #version_response~=3 then return -1 end + local raw_val = (string_byte(version_response,2) or 0) + (string_byte(version_response,3) or 0)* 256 + return raw_val +end + +-- description: lets us know button's current status +-- input: empty +-- output: button's current status. Possible status: 1 pressed - 0 not pressed +api.getValue = {} +api.getValue.parameters = {} -- no input parameters +api.getValue.returns = {[1]={rname="state", rtype="int"}} +api.getValue.call = function () + device:send(GET_VALUE) -- operation code 1 = get button's status + local sen_dig_response = device:read(2) -- 2 bytes to read (opcode, data) + if not sen_dig_response or #sen_dig_response~=2 then return -1 end + local raw_val = string_byte(sen_dig_response, 2) or 0 -- keep data + return raw_val +end diff --git a/lib/support/drivers/hotplug/distanc.lua b/lib/support/drivers/hotplug/distanc.lua new file mode 100644 index 0000000..2340796 --- /dev/null +++ b/lib/support/drivers/hotplug/distanc.lua @@ -0,0 +1,32 @@ +local device = _G + +local RD_VERSION=string.char(0x00) +local GET_VALUE=string.char(0x01) +local string_byte=string.byte + +-- description: lets us know dist module's version +api={} +api.getVersion = {} +api.getVersion.parameters = {} -- no input parameters +api.getVersion.returns = {[1]={rname="version", rtype="int"}} +api.getVersion.call = function () + device:send(RD_VERSION) -- operation code 0 = get version + local version_response = device:read(3) -- 3 bytes to read (opcode, data) + if not version_response or #version_response~=3 then return -1 end + local raw_val = (string_byte(version_response,2) or 0) + (string_byte(version_response,3) or 0)* 256 + return raw_val +end + +-- description: lets us know dist sensor's current value +-- input: empty +-- output: dist sensor's current value. +api.getValue = {} +api.getValue.parameters = {} -- no input parameters +api.getValue.returns = {[1]={rname="par1", rtype="int"}} +api.getValue.call = function () + device:send(GET_VALUE) -- operation code 1 = get distance of object + local sen_anl_response = device:read(3) -- operation code and data + if not sen_anl_response or #sen_anl_response~=3 then return -1 end + local raw_val = (string_byte(sen_anl_response, 2) or 0) + (string_byte(sen_anl_response, 3) or 0)* 256 + return raw_val +end diff --git a/lib/support/drivers/hotplug/gpio.lua b/lib/support/drivers/hotplug/gpio.lua new file mode 100644 index 0000000..331efb3 --- /dev/null +++ b/lib/support/drivers/hotplug/gpio.lua @@ -0,0 +1,19 @@ +local device = _G + +local RD_VERSION=string.char(0x00) +local string_byte=string.byte + +-- description: lets us know button module's version +api={} +api.getVersion = {} +api.getVersion.parameters = {} -- no input parameters +api.getVersion.returns = {[1]={rname="version", rtype="int"}} +api.getVersion.call = function () + device:send(RD_VERSION) -- operation code 0 = get version + local version_response = device:read(3) -- 3 bytes to read (opcode, data) + if not version_response or #version_response~=3 then return -1 end + local raw_val = (string_byte(version_response,2) or 0) + (string_byte(version_response,3) or 0)* 256 + return raw_val +end + + diff --git a/lib/support/drivers/hotplug/grey.lua b/lib/support/drivers/hotplug/grey.lua new file mode 100644 index 0000000..eb24f36 --- /dev/null +++ b/lib/support/drivers/hotplug/grey.lua @@ -0,0 +1,32 @@ +local device = _G + +local RD_VERSION=string.char(0x00) +local GET_VALUE=string.char(0x01) +local string_byte=string.byte + +-- description: lets us know grey module's version +api={} +api.getVersion = {} +api.getVersion.parameters = {} -- no input parameters +api.getVersion.returns = {[1]={rname="version", rtype="int"}} +api.getVersion.call = function () + device:send(RD_VERSION) -- operation code 0 = get version + local version_response = device:read(3) -- 3 bytes to read (opcode, data) + if not version_response or #version_response~=3 then return -1 end + local raw_val = (string_byte(version_response,2) or 0) + (string_byte(version_response,3) or 0)* 256 + return raw_val +end + +-- description: lets us know grey sensor's current value +-- input: empty +-- output: grey sensor's current value. +api.getValue = {} +api.getValue.parameters = {} -- no input parameters +api.getValue.returns = {[1]={rname="par1", rtype="int"}} +api.getValue.call = function () + device:send(GET_VALUE) -- operation code 1 = get grey level + local sen_anl_response = device:read(3) -- operation code and data + if not sen_anl_response or #sen_anl_response~=3 then return -1 end + local raw_val = (string_byte(sen_anl_response, 2) or 0) + (string_byte(sen_anl_response, 3) or 0)* 256 + return raw_val +end diff --git a/lib/support/drivers/hotplug/light.lua b/lib/support/drivers/hotplug/light.lua new file mode 100644 index 0000000..ce953b2 --- /dev/null +++ b/lib/support/drivers/hotplug/light.lua @@ -0,0 +1,32 @@ +local device = _G + +local RD_VERSION=string.char(0x00) +local GET_VALUE=string.char(0x01) +local string_byte=string.byte + +-- description: lets us know light module's version +api={} +api.getVersion = {} +api.getVersion.parameters = {} -- no input parameters +api.getVersion.returns = {[1]={rname="version", rtype="int"}} +api.getVersion.call = function () + device:send(RD_VERSION) -- operation code 0 = get version + local version_response = device:read(3) -- 3 bytes to read (opcode, data) + if not version_response or #version_response~=3 then return -1 end + local raw_val = (string_byte(version_response,2) or 0) + (string_byte(version_response,3) or 0)* 256 + return raw_val +end + +-- description: lets us know light sensor's current value +-- input: empty +-- output: light sensor's current value. +api.getValue = {} +api.getValue.parameters = {} -- no input parameters +api.getValue.returns = {[1]={rname="par1", rtype="int"}} +api.getValue.call = function () + device:send(GET_VALUE) -- operation code 1 = get light level + local sen_anl_response = device:read(3) -- operation code and data + if not sen_anl_response or #sen_anl_response~=3 then return -1 end + local raw_val = (string_byte(sen_anl_response, 2) or 0) + (string_byte(sen_anl_response, 3) or 0)* 256 + return raw_val +end diff --git a/lib/support/drivers/hotplug/port.lua b/lib/support/drivers/hotplug/port.lua new file mode 100644 index 0000000..331efb3 --- /dev/null +++ b/lib/support/drivers/hotplug/port.lua @@ -0,0 +1,19 @@ +local device = _G + +local RD_VERSION=string.char(0x00) +local string_byte=string.byte + +-- description: lets us know button module's version +api={} +api.getVersion = {} +api.getVersion.parameters = {} -- no input parameters +api.getVersion.returns = {[1]={rname="version", rtype="int"}} +api.getVersion.call = function () + device:send(RD_VERSION) -- operation code 0 = get version + local version_response = device:read(3) -- 3 bytes to read (opcode, data) + if not version_response or #version_response~=3 then return -1 end + local raw_val = (string_byte(version_response,2) or 0) + (string_byte(version_response,3) or 0)* 256 + return raw_val +end + + diff --git a/lib/support/drivers/hotplug/tilt.lua b/lib/support/drivers/hotplug/tilt.lua new file mode 100644 index 0000000..1076479 --- /dev/null +++ b/lib/support/drivers/hotplug/tilt.lua @@ -0,0 +1,19 @@ +local device = _G + +api={} +api.getTilt = {} +api.getTilt.parameters = {} -- -- no input parameters +api.getTilt.returns = {[1]={rname="par1", rtype="int"}} +api.getTilt.call = function () + device:send(string.char(0x00)) -- codigo de operacion 0 + local sen_dig_response = device:read(3) + local raw_val + if not sen_dig_response or string.byte(sen_dig_response or "00000000", 2) == nil or string.byte(sen_dig_response or "00000000", 3) == nil + then + raw_val = "nil value" + else + raw_val = string.byte(sen_dig_response, 3) % 2 + end + return raw_val + +end diff --git a/lib/support/drivers/hotplug/vibra.lua b/lib/support/drivers/hotplug/vibra.lua new file mode 100644 index 0000000..0c3d985 --- /dev/null +++ b/lib/support/drivers/hotplug/vibra.lua @@ -0,0 +1,19 @@ +local device = _G + +api={} +api.getVibra = {} +api.getVibra.parameters = {} -- -- no input parameters +api.getVibra.returns = {[1]={rname="par1", rtype="int"}} +api.getVibra.call = function () + device:send(string.char(0x00)) -- codigo de operacion 0 + local sen_dig_response = device:read(3) + local raw_val + if not sen_dig_response or string.byte(sen_dig_response or "00000000", 2) == nil or string.byte(sen_dig_response or "00000000", 3) == nil + then + raw_val = "nil value" + else + raw_val = string.byte(sen_dig_response, 3) % 2 + end + return raw_val + +end diff --git a/lib/support/drivers/lback.lua b/lib/support/drivers/lback.lua new file mode 100644 index 0000000..9f903b1 --- /dev/null +++ b/lib/support/drivers/lback.lua @@ -0,0 +1,21 @@ +local device = _G + +api={} +api.send = {} +api.send.parameters = {[1]={rname="data", rtype="string"}} +api.send.returns = {} +api.send.call = function (data) + --print("####", data, string.len(data)) + device:send(data) +end + +api.read = {} +api.read.parameters = {[1]={rname="len", rtype="int", default=64, min=0, max=255}} +api.read.returns = {[1]={rname="data", rtype="string"}} +api.read.call = function (len) + local len = len or 64 + local ret = device:read(len) +-- print("====",ret, string.len(ret)) + return ret +end + diff --git a/lib/support/drivers/led.lua b/lib/support/drivers/led.lua new file mode 100644 index 0000000..1095d93 --- /dev/null +++ b/lib/support/drivers/led.lua @@ -0,0 +1,17 @@ +local device = _G + +local string_char=string.char + +api={} +api.setLight = {} +api.setLight.parameters = {[1]={rname="message", rtype="string"}} +api.setLight.returns = {} +api.setLight.call = function (intensidad) + intensidad=tonumber(intensidad) + if (not intensidad) or intensidad<0 then intensidad=0 + elseif intensidad>255 then intensidad=255 end + + local msg = string_char(0x00) .. string_char(math.floor(intensidad)) -- entre 0 y 255 + device:send(msg) + local version_response = device:read() +end diff --git a/lib/support/drivers/ledA.lua b/lib/support/drivers/ledA.lua new file mode 100644 index 0000000..f76b501 --- /dev/null +++ b/lib/support/drivers/ledA.lua @@ -0,0 +1,46 @@ +local device = _G +local RD_VERSION = string.char(0x00) +local PRENDER = string.char(0x01) +local APAGAR = string.char(0x02) +local BLINK = string.char(0x03) + +api={} +api.read_version = {} +api.read_version.parameters = {} --no parameters +api.read_version.returns = {[1]={rname="version", rtype="number"}} --one return +api.read_version.call = function () + local get_read_version = RD_VERSION + device:send(get_read_version) + local version_response = device:read(2) + local raw_val = string.byte(version_response, 2) + --print("rawval, deg_temp: ", raw_val, deg_temp) + return raw_val +end + +api.prender = {} +api.prender.parameters = {} --no parameters +api.prender.returns = {} --no return +api.prender.call = function () + device:send(PRENDER) +-- local response = device:read(2) +-- print("[",string.byte(response, 1),",", string.byte(response, 2),"]") +-- print("-----------------------------------------------") +-- if (string.byte(response, 1) ~= nil or string.byte(response, 2) ~= nil) then +-- print("vino data") +-- end +end + +api.apagar = {} +api.apagar.parameters = {} --no parameters +api.apagar.returns = {} --no return +api.apagar.call = function () + device:send(APAGAR) +end + +api.blink = {} +api.blink.parameters = {[1]={rname="time", rtype="number"}, [2]={rname="blinks", rtype="number"}} +api.blink.returns = {} --no return +api.blink.call = function (time, blinks) + local msg = BLINK .. string.char(time) .. string.char(blinks) + device:send(msg) +end diff --git a/lib/support/drivers/ledR.lua b/lib/support/drivers/ledR.lua new file mode 100644 index 0000000..1d3e726 --- /dev/null +++ b/lib/support/drivers/ledR.lua @@ -0,0 +1,31 @@ +local device = _G +local RD_VERSION = string.char(0x00) +local PRENDER = string.char(0x01) +local APAGAR = string.char(0x02) + +api={} +api.read_version = {} +api.read_version.parameters = {} --no parameters +api.read_version.returns = {[1]={rname="version", rtype="number"}} --one return +api.read_version.call = function () + local get_read_version = RD_VERSION + device:send(get_read_version) + local version_response = device:read(2) + local raw_val = string.byte(version_response, 2) + --print("rawval, deg_temp: ", raw_val, deg_temp) + return raw_val +end + +api.prender = {} +api.prender.parameters = {} --no parameters +api.prender.returns = {} --no return +api.prender.call = function () + device:send(PRENDER) +end + +api.apagar = {} +api.apagar.parameters = {} --no parameters +api.apagar.returns = {} --no return +api.apagar.call = function () + device:send(APAGAR) +end diff --git a/lib/support/drivers/ledV.lua b/lib/support/drivers/ledV.lua new file mode 100644 index 0000000..1d3e726 --- /dev/null +++ b/lib/support/drivers/ledV.lua @@ -0,0 +1,31 @@ +local device = _G +local RD_VERSION = string.char(0x00) +local PRENDER = string.char(0x01) +local APAGAR = string.char(0x02) + +api={} +api.read_version = {} +api.read_version.parameters = {} --no parameters +api.read_version.returns = {[1]={rname="version", rtype="number"}} --one return +api.read_version.call = function () + local get_read_version = RD_VERSION + device:send(get_read_version) + local version_response = device:read(2) + local raw_val = string.byte(version_response, 2) + --print("rawval, deg_temp: ", raw_val, deg_temp) + return raw_val +end + +api.prender = {} +api.prender.parameters = {} --no parameters +api.prender.returns = {} --no return +api.prender.call = function () + device:send(PRENDER) +end + +api.apagar = {} +api.apagar.parameters = {} --no parameters +api.apagar.returns = {} --no return +api.apagar.call = function () + device:send(APAGAR) +end diff --git a/lib/support/drivers/leds.lua b/lib/support/drivers/leds.lua new file mode 100644 index 0000000..7e55ce6 --- /dev/null +++ b/lib/support/drivers/leds.lua @@ -0,0 +1,43 @@ +local device = _G +local RD_VERSION = string.char(0x00) +local PRENDER = string.char(0x01) +local APAGAR = string.char(0x02) +local BLINK = string.char(0x03) + +api={} +api.read_version = {} +api.read_version.parameters = {} --no parameters +api.read_version.returns = {[1]={rname="version", rtype="number"}} --one return +api.read_version.call = function () + local get_read_version = RD_VERSION + device:send(get_read_version) + local version_response = device:read(2) + local raw_val = string.byte(version_response, 2) + --print("rawval, deg_temp: ", raw_val, deg_temp) + return raw_val +end + +api.prender = {} +api.prender.parameters = {[1]={rname="numLed", rtype="number"}} --recibe the led number +api.prender.returns = {} --no return +api.prender.call = function (ledNumber) + local write_res, err = device:send(PRENDER .. string.char(ledNumber)) + if write_res then return 1 else return 0 end +end + +api.apagar = {} +api.apagar.parameters = {[1]={rname="numLed", rtype="number"}} --recibe the led number +api.apagar.returns = {} --no return +api.apagar.call = function (ledNumber) + local write_res, err = device:send(APAGAR .. string.char(ledNumber)) + if write_res then return 1 else return 0 end +end + +api.blink = {} +api.blink.parameters = {[1]={rname="time", rtype="number"}, [2]={rname="blinks", rtype="number"}, [3]={rname="numLed", rtype="number"}} +api.blink.returns = {} --no return +api.blink.call = function (time, blinks, ledNumber) + local msg = BLINK .. string.char(time) .. string.char(blinks) .. string.char(ledNumber) + local write_res, err = device:send(msg) + if write_res then return 1 else return 0 end +end diff --git a/lib/support/drivers/luz.lua b/lib/support/drivers/luz.lua new file mode 100644 index 0000000..ed14e26 --- /dev/null +++ b/lib/support/drivers/luz.lua @@ -0,0 +1,21 @@ +local device = _G + +api={} +api.getLuz = {} +api.getLuz.parameters = {} -- -- no input parameters +api.getLuz.returns = {[1]={rname="par1", rtype="int"}} +api.getLuz.call = function () + device:send(string.char(0x00)) -- codigo de operacion 0 + local sen_anl_response = device:read(3) + local raw_val + if not sen_anl_response or string.byte(sen_anl_response or "00000000", 2) == nil or string.byte(sen_anl_response or "00000000", 3) == nil + then + raw_val = "nil value" + else + raw_val = string.byte(sen_anl_response, 2)* 256 + string.byte(sen_anl_response, 3) + raw_val = 1024 - raw_val + + end + return raw_val + +end diff --git a/lib/support/drivers/magnet.lua b/lib/support/drivers/magnet.lua new file mode 100644 index 0000000..e70b69c --- /dev/null +++ b/lib/support/drivers/magnet.lua @@ -0,0 +1,21 @@ +local device = _G + +-- descripción: permite conocer el estado el botón en un momento dado. +-- entrada: no tiene. +-- salida: estado del botón. Posibles estados: 1 presionado, 0 libre. +api={} +api.getCampo = {} +api.getCampo.parameters = {} -- no tiene parámetros de entrada +api.getCampo.returns = {[1]={rname="par1", rtype="int"}} -- 1 = presionado, 0 = libre +api.getCampo.call = function () + device:send(string.char(0x00)) -- codigo de operacion = 0 + local sen_dig_response = device:read(3) -- leo 2 bytes (opcode, data) + local raw_val + if not sen_dig_response or string.byte(sen_dig_response or "00000000", 2) == nil or string.byte(sen_dig_response or "00000000", 3) == nil + then + raw_val = "nil value" + else + raw_val = 1 - (string.byte(sen_dig_response, 3) % 2) + end + return raw_val +end diff --git a/lib/support/drivers/motor.lua b/lib/support/drivers/motor.lua new file mode 100644 index 0000000..c1a86d6 --- /dev/null +++ b/lib/support/drivers/motor.lua @@ -0,0 +1,42 @@ +local device = _G +local SET_VEL_ADL = 0x00 -- código de op para mover el motor hacia adelante +local SET_VEL_ATR = 0x01 -- código de op para mover el motor hacia atrás + + +api={} +api.setveladl = {} +api.setveladl.parameters = {[1]={rname="id", rtype="int"}, [2]={rname="vel", rtype="int"}} --primer parametro id motor, segundo velocidad +api.setveladl.returns = {[1]={rname="dato", rtype="int"}} --codigo de operación +api.setveladl.call = function (id, vel) + local msg = string.char(SET_VEL_ADL,id, math.floor(vel / 256),vel % 256) + device:send(msg) + local ret = device:read(1) + local raw_val = string.byte(ret or " ", 1) + return raw_val +end + +api.setvelatr = {} +api.setvelatr.parameters = {[1]={rname="id", rtype="int"}, [2]={rname="vel", rtype="int"}} --primer parametro id motor, segundo velocidad +api.setvelatr.returns = {[1]={rname="dato", rtype="int"}} --codigo de operación +api.setvelatr.call = function (id, vel) + local msg = string.char(SET_VEL_ATR,id, math.floor(vel / 256),vel % 256) + device:send(msg) + local ret = device:read(1) + local raw_val = string.byte(ret or " ", 1) + return raw_val +end + + +api.setvelatr2 = {} +api.setvelatr2.parameters = {[1]={rname="id", rtype="int"}, [2]={rname="vel", rtype="int"}} --primer parametro id motor, segundo velocidad +api.setvelatr2.returns = {[1]={rname="dato", rtype="int"}} --codigo de operación +api.setvelatr2.call = function (vel) + local msg = string.char(SET_VEL_ATR,0, math.floor(vel / 256),vel % 256) + device:send(msg) + local msg = string.char(SET_VEL_ATR,1, math.floor(vel / 256),vel % 256) + device:send(msg) + local ret = device:read(1) + local ret = device:read(1) + local raw_val = string.byte(ret or " ", 1) + return raw_val +end diff --git a/lib/support/drivers/motorTm.lua b/lib/support/drivers/motorTm.lua new file mode 100644 index 0000000..9343855 --- /dev/null +++ b/lib/support/drivers/motorTm.lua @@ -0,0 +1,47 @@ +local device = _G + +api={} +api.step = {} +api.step.parameters = {[1]={rname="step", rtype="int", min=0, max=65536}} +api.step.returns = {} +api.step.call = function (freq) + local msg = string.char(0x02) .. string.char(math.floor(freq / 256)) .. string.char(freq % 256) + device:send(msg) + device:read(1) +end + +api.steps = {} +api.steps.parameters = {[1]={rname="number", rtype="int", min=0, max=65536}} +api.steps.returns = {} +api.steps.call = function (number) + local msg = string.char(0x03) .. string.char(math.floor(number / 256)) .. string.char(number % 256) + device:send(msg) + device:read(1) +end + +api.phasetype = {} +api.phasetype.parameters = {[1]={rname="phasetype", rtype="int", min=0, max=2}} +api.phasetype.returns = {} +api.phasetype.call = function (phasetype) + local msg = string.char(0x04) .. string.char(phasetype) + device:send(msg) +end + +api.power_on = {} +api.power_on.parameters = {[1]={rname="power_on", rtype="int", min=0, max=1}} +api.power_on.returns = {} +api.power_on.call = function (on) + local msg = string.char(0x06) .. string.char(on) + device:send(msg) +end + +api.direction = {} +api.direction.parameters = {[1]={rname="direction", rtype="int", min=-1, max=1}} +api.direction.returns = {} +api.direction.call = function (dir) + if dir==-1 then dir=2 end + local msg = string.char(0x07) .. string.char(dir+1) + device:send(msg) +end + + diff --git a/lib/support/drivers/motores.lua b/lib/support/drivers/motores.lua new file mode 100644 index 0000000..45d2967 --- /dev/null +++ b/lib/support/drivers/motores.lua @@ -0,0 +1,48 @@ +local device = _G +local SET_VEL_MTR = 0x00 -- código de op para mover un motor con vel y sentido +local SET_VEL_2MTR = 0x01 -- código de op para mover dos motores con vel y sentido + + +api={} +api.setvelmtr = {} +api.setvelmtr.parameters = {[1]={rname="id", rtype="int"},[2]={rname="sentido", rtype="int"},[3]={rname="vel", rtype="int"}} --parametros, id sentido vel +api.setvelmtr.returns = {[1]={rname="dato", rtype="int"}} --codigo de operación +api.setvelmtr.call = function (id, sentido, vel) + vel=tonumber(vel) + if vel>1023 then vel=1023 end + local msg = string.char(SET_VEL_MTR,id, sentido, math.floor(vel / 256),vel % 256) + device:send(msg) + local ret = device:read(1) + local raw_val = string.byte(ret or " ", 1) + return raw_val +end + +api.setvel2mtr = {} +api.setvel2mtr.parameters = {[1]={rname="sentido", rtype="int"},[2]={rname="vel", rtype="int"},[3]={rname="sentido", rtype="int"},[4]={rname="vel", rtype="int"}} +api.setvel2mtr.returns = {[1]={rname="dato", rtype="int"}} --codigo de operación +api.setvel2mtr.call = function (sentido1, vel1, sentido2, vel2) + vel1, vel2 = tonumber(vel1), tonumber(vel2) + if vel1>1023 then vel1=1023 end + if vel2>1023 then vel2=1023 end + local msg = string.char(SET_VEL_2MTR,sentido1, math.floor(vel1 / 256),vel1 % 256, sentido2, math.floor(vel2 / 256),vel2 % 256) + device:send(msg) + local ret = device:read(1) + local raw_val = string.byte(ret or " ", 1) + return raw_val +end + + +api.setvelatr2 = {} +api.setvelatr2.parameters = {[1]={rname="id", rtype="int"}, [2]={rname="vel", rtype="int"}} --primer parametro id motor, segundo velocidad +api.setvelatr2.returns = {[1]={rname="dato", rtype="int"}} --codigo de operación +api.setvelatr2.call = function (vel) + local vdiv, vmod = math.floor(vel / 256),vel % 256 + local msg = string.char(SET_VEL_ATR, 0, vdiv, vmod) + device:send(msg) + msg = string.char(SET_VEL_ATR, 1, vdiv, vmod) + device:send(msg) + local ret = device:read(1) + ret = device:read(1) + local raw_val = string.byte(ret or " ", 1) + return raw_val +end diff --git a/lib/support/drivers/motorin.lua b/lib/support/drivers/motorin.lua new file mode 100644 index 0000000..042f3cc --- /dev/null +++ b/lib/support/drivers/motorin.lua @@ -0,0 +1,28 @@ +local device = _G + +api={} +api.speed = {} +api.speed.parameters = {[1]={rname="freq", rtype="int", min=0, max=65536}} +api.speed.returns = {} +api.speed.call = function (freq) + local msg = string.char(0x02) .. string.char(math.floor(freq / 256)) .. string.char(freq % 256) + device:send(msg) + device:read(1) +end + +api.steps = {} +api.steps.parameters = {[1]={rname="number", rtype="int", min=0, max=65536}} +api.steps.returns = {} +api.steps.call = function (number) + local msg = string.char(0x03) .. string.char(math.floor(number / 256)) .. string.char(number % 256) + device:send(msg) + device:read(1) +end + +api.on = {} +api.on.parameters = {[1]={rname="direction", rtype="int", min=-1, max=1}} +api.on.returns = {} +api.on.call = function (dir) + local msg = string.char(0x01) .. string.char(dir+1) + device:send(msg) +end diff --git a/lib/support/drivers/motors.lua b/lib/support/drivers/motors.lua new file mode 100644 index 0000000..a7c9a05 --- /dev/null +++ b/lib/support/drivers/motors.lua @@ -0,0 +1,70 @@ +local device = _G +local RD_VERSION=string.char(0x00) +local SET_VEL_2MTR=0x01 -- código de op para mover dos motores con vel y sentido +local TEST_MOTORS=string.char(0x02) +local SET_VEL_MTR = 0x01 -- código de op para mover un motor con vel y sentido + +api={} +api.getVersion = {} +api.getVersion.parameters = {} -- no input parameters +api.getVersion.returns = {[1]={rname="version", rtype="int"}} +api.getVersion.call = function () + device:send(RD_VERSION) -- operation code 0 = get version + local version_response = device:read(3) -- 3 bytes to read (opcode, data) + if not version_response or #version_response~=3 then return -1 end + local raw_val = (string.byte(version_response,2) or 0) + (string.byte(version_response,3) or 0)* 256 + return raw_val +end + +api.setvelmtr = {} -- no impl en firmware +api.setvelmtr.parameters = {[1]={rname="id", rtype="int"},[2]={rname="sentido", rtype="int"},[3]={rname="vel", rtype="int"}} --parametros, id sentido vel +api.setvelmtr.returns = {[1]={rname="dato", rtype="int"}} --codigo de operación +api.setvelmtr.call = function (id, sentido, vel) + vel=tonumber(vel) + if vel>1023 then vel=1023 end + local msg = string.char(SET_VEL_MTR,id, sentido, math.floor(vel / 256),vel % 256) + device:send(msg) + local ret = device:read(1) + local raw_val = string.byte(ret or " ", 1) + return raw_val +end + +api.setvel2mtr = {} +api.setvel2mtr.parameters = {[1]={rname="sentido", rtype="int"},[2]={rname="vel", rtype="int"},[3]={rname="sentido", rtype="int"},[4]={rname="vel", rtype="int"}} +api.setvel2mtr.returns = {[1]={rname="dato", rtype="int"}} --codigo de operación +api.setvel2mtr.call = function (sentido1, vel1, sentido2, vel2) + vel1, vel2 = tonumber(vel1), tonumber(vel2) + if vel1>1023 then vel1=1023 end + if vel2>1023 then vel2=1023 end + local msg = string.char(SET_VEL_2MTR,sentido1, math.floor(vel1 / 256),vel1 % 256, sentido2, math.floor(vel2 / 256),vel2 % 256) + device:send(msg) + local ret = device:read(1) + local raw_val = string.byte(ret or " ", 1) + return raw_val +end + +api.setvelatr2 = {} -- no impl en firmware +api.setvelatr2.parameters = {[1]={rname="id", rtype="int"}, [2]={rname="vel", rtype="int"}} --primer parametro id motor, segundo velocidad +api.setvelatr2.returns = {[1]={rname="dato", rtype="int"}} --codigo de operación +api.setvelatr2.call = function (vel) + local vdiv, vmod = math.floor(vel / 256),vel % 256 + local msg = string.char(SET_VEL_ATR, 0, vdiv, vmod) + device:send(msg) + msg = string.char(SET_VEL_ATR, 1, vdiv, vmod) + device:send(msg) + local ret = device:read(1) + ret = device:read(1) + local raw_val = string.byte(ret or " ", 1) + return raw_val +end + +api.testMotors = {} +api.testMotors.parameters = {} -- no input parameters +api.testMotors.returns = {[1]={rname="dato", rtype="int"}} +api.testMotors.call = function () + device:send(TEST_MOTORS) -- operation code 2 = test motors + local ret = device:read(1) -- 1 byte to read (opcode) + if not ret or #ret~=1 then return -1 end + local ret = string.byte(ret, 1) + return ret +end diff --git a/lib/support/drivers/move.lua b/lib/support/drivers/move.lua new file mode 100644 index 0000000..7c361e3 --- /dev/null +++ b/lib/support/drivers/move.lua @@ -0,0 +1,16 @@ +local device = _G +local GET_MOVE = string.char(0x01) +local char000 = string.char(0,0,0) + +api={} +api.get_move = {} +api.get_move.parameters = {} --no parameter +api.get_move.returns = {[1]={rname="digital_value", rtype="number"}} --one return +api.get_move.call = function () + local get_move_payload = GET_MOVE + device:send(get_move_payload) + local move_response = device:read(2) or char000 + local raw_val = (string.byte(move_response, 2) or 0) + return raw_val +end + diff --git a/lib/support/drivers/pnp.lua b/lib/support/drivers/pnp.lua new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/lib/support/drivers/pnp.lua diff --git a/lib/support/drivers/pote.lua b/lib/support/drivers/pote.lua new file mode 100644 index 0000000..fac23cf --- /dev/null +++ b/lib/support/drivers/pote.lua @@ -0,0 +1,16 @@ +local device = _G +local RD_POTE = string.char(0x01) +local char000 = string.char(0,0,0) + +api={} +api.get_pote = {} +api.get_pote.parameters = {} --no parameter +api.get_pote.returns = {[1]={rname="analog_value", rtype="number"}} --one return +api.get_pote.call = function () + local get_pote_payload = RD_POTE + device:send(get_pote_payload) + local pote_response = device:read(3) or char000 + local raw_val = (string.byte(pote_response, 2) or 0) + 255*(string.byte(pote_response, 3) or 0) + return raw_val +end + diff --git a/lib/support/drivers/puerta.lua b/lib/support/drivers/puerta.lua new file mode 100644 index 0000000..1d3e726 --- /dev/null +++ b/lib/support/drivers/puerta.lua @@ -0,0 +1,31 @@ +local device = _G +local RD_VERSION = string.char(0x00) +local PRENDER = string.char(0x01) +local APAGAR = string.char(0x02) + +api={} +api.read_version = {} +api.read_version.parameters = {} --no parameters +api.read_version.returns = {[1]={rname="version", rtype="number"}} --one return +api.read_version.call = function () + local get_read_version = RD_VERSION + device:send(get_read_version) + local version_response = device:read(2) + local raw_val = string.byte(version_response, 2) + --print("rawval, deg_temp: ", raw_val, deg_temp) + return raw_val +end + +api.prender = {} +api.prender.parameters = {} --no parameters +api.prender.returns = {} --no return +api.prender.call = function () + device:send(PRENDER) +end + +api.apagar = {} +api.apagar.parameters = {} --no parameters +api.apagar.returns = {} --no return +api.apagar.call = function () + device:send(APAGAR) +end diff --git a/lib/support/drivers/sec.lua b/lib/support/drivers/sec.lua new file mode 100644 index 0000000..e89646b --- /dev/null +++ b/lib/support/drivers/sec.lua @@ -0,0 +1,87 @@ +local device = _G +local RD_VERSION = string.char(0x00) +local GET_SEC = string.char(0X01) +local INTRUSION = string.char(0x02) +local RESET_FLAG = string.char(0x03) +local CONT_INT = string.char(0x04) +local MESS = string.char(0x05) +local RESET = string.char(0xFF) + +api={} +api.read_version = {} +api.read_version.parameters = {} --no parameters +api.read_version.returns = {[1]={rname="version", rtype="number"}} --one return +api.read_version.call = function () + local get_read_version = RD_VERSION + device:send(get_read_version) + local version_response = device:read(4) + local raw_val = string.byte(version_response, 3) + --print("rawval, deg_temp: ", raw_val, deg_temp) + return raw_val +end + +api.get_sec = {} +api.get_sec.parameters = {} --no parameters +--api.get_sec.returns = {[1]={rname="error", rtype="string"},[2]={rname="SecSensorValue_1", rtype="number"},[3]={rname="SecSensorValue_2", rtype="number"}} +api.get_sec.returns = {[1]={rname="SecSensorValue_1", rtype="number"},[2]={rname="SecSensorValue_2", rtype="number"}} +api.get_sec.call = function () + local send_res, err + send_res, err = device:send(GET_SEC) + local ret = device:read(3) + local SecSensorValue_1 = (string.byte(ret,2) or 0) + local SecSensorValue_2 = (string.byte(ret,3) or 0) +-- local SecSensorValue_1 = (string.byte(ret,2)) +-- local SecSensorValue_2 = (string.byte(ret,3)) + if ((SecSensorValue_1 == 0) and (SecSensorValue_2 == 0)) then + alarma = "00" + elseif ((SecSensorValue_1 == 0) and (SecSensorValue_2 == 1)) then + alarma = "01" + elseif ((SecSensorValue_1 == 1) and (SecSensorValue_2 == 0)) then + alarma = "10" + else + alarma = "11" + end + return alarma + --if ret then return true, SecSensorValue_1, SecSensorValue_2 else return false end +end + +api.intrusion = {} +api.intrusion.parameters = {} --no parameters +api.intrusion.returns = {[1]={rname="alarma", rtype="number"}} --one return +api.intrusion.call = function () + local intrusion = INTRUSION + device:send(intrusion) + local alarma = device:read(1) + if (alarma == string.char(0x00)) then + alarma = 0 + else + alarma = 1 + end + return alarma +end + +api.reset_flag = {} +api.reset_flag.parameters = {} --no parameters +api.reset_flag.returns = {[1]={rname="borrado", rtype="number"}} --one return +api.reset_flag.call = function () + local reset_flag = RESET_FLAG + device:send(reset_flag) + local borrado = device:read(1) + if (borrado == string.char(0x00)) then + borrado = 0 + else + borrado = 1 + end + + return borrado +end + +api.cont_int = {} +api.cont_int.parameters = {} --no parameters +api.cont_int.returns = {[1]={rname="cantidad", rtype="number"}} --one return +api.cont_int.call = function () + local cont_int = CONT_INT + device:send(cont_int) + local cantidad = device:read(1) + return string.byte(cantidad) +end diff --git a/lib/support/drivers/sensor.lua b/lib/support/drivers/sensor.lua new file mode 100644 index 0000000..d4df8d4 --- /dev/null +++ b/lib/support/drivers/sensor.lua @@ -0,0 +1,52 @@ +local device = _G +local SEN_DIG = string.char(0x03) -- consulta un sensor digital +local SEN_ANL = string.char(0x00) -- consulta un sensor analogico +local SEN_ I2C = string.char(0x02) -- consulta un sensor i2c + +api={} +api.senanl = {} +api.senanl.parameters = {[1]={rname="idPin", rtype="int"}} -- -- el parámetro indica el pin que queremos leer de la arduino +api.senanl.returns = {[1]={rname="par1", rtype="int"}} --{[1]={rname="par1", rtype="int"}, [2]={rname="par2", rtype="int"}} --two return +api.senanl.call = function (idPin) + device:send(string.char(0x00, idPin)) -- codigo de operacion 0 + local sen_anl_response = device:read(3) + local raw_val + if not sen_anl_response or string.byte(sen_anl_response or "00000000", 2) == nil or string.byte(sen_anl_response or "00000000", 3) == nil + then + raw_val = "nil value" + else + raw_val = string.byte(sen_anl_response or "00000000", 2)* 256 + string.byte(sen_anl_response or "00000000", 3) + end + --local raw_val2 = string.byte(set_anl_response or " ", 3) + return raw_val --string.char(raw_val ,raw_val2) +end + +api.sendig = {} +api.sendig.parameters = {[1]={rname="idPin", rtype="int"}} -- el parámetro indica el pin que queremos leer de la arduino +api.sendig.returns = {[1]={rname="lecturaSensor", rtype="int"}} +api.sendig.call = function (idPin) + device:send(string.char(0x01, idPin)) -- codigo de operacion 1 + local sen_dig_response = device:read(3) + local raw_val + --if not sen_dig_response + --then + -- raw_val = "nil value" + --else + raw_val = string.byte(sen_dig_response or "0000", 2)* 256 + string.byte(sen_dig_response or "0000", 3) + 0 + --end + return raw_val + +end + + +--api.seni2c = {} +--api.seni2c.parameters = {} -- no params +--api.seni2c.returns = {[1]={rname="par1", rtype="int"}, [2]={rname="par2", rtype="int"}} --two return +--api.seni2c.call = function () +-- local get_sen_i2c = SET_I2C +-- device:send(get_sen_i2c) +-- local sen_i2c_response = device:read(2) +-- local raw_val = string.byte(set_i2c_response, 2) +-- return raw_val +--end + diff --git a/lib/support/drivers/stmtr.lua b/lib/support/drivers/stmtr.lua new file mode 100644 index 0000000..9bd9b40 --- /dev/null +++ b/lib/support/drivers/stmtr.lua @@ -0,0 +1,46 @@ +local device = _G + +api={} +api.rawspeed = {} +api.rawspeed.parameters = {[1]={rname="speed1", rtype="int", min=-5, max=5}, [2]={rname="speed2", rtype="int", min=-5, max=5}} +api.rawspeed.returns = {} +api.rawspeed.call = function (speed1, speed2) + local msg = string.char(0x02,speed1,speed2) + device:send(msg) + device:read() +end + +api.t0cfg = {} +api.t0cfg.parameters = {[1]={rname="t0_low", rtype="int", min=0, max=255}, [2]={rname="t0_high", rtype="int", min=0, max=255}} +api.t0cfg.returns = {} +api.t0cfg.call = function (t0_low, t0_high) + local msg = string.char(0x01,t0_low,t0_high) + device:send(msg) + device:read() +end + +api={} +api.speed = {} +api.speed.parameters = {[1]={rname="vel1", rtype="float", min=-127, max=127}, + [2]={rname="vel2", rtype="float", min=-127, max=127}} +api.speed.returns = {} +api.speed.call = function (vel1, vel2) + vel1=tonumber(vel1) + vel2=tonumber(vel2) + + local speed1, speed2 + if vel1>=0 then + speed1=vel1 + else + speed1=0xFF + vel1 + 1 + end + if vel2>=0 then + speed2=vel2 + else + speed2=0xFF + vel2 + 1 + end + + local msg = string.char(0x02,speed1,speed2) + device:send(msg) + device:read() +end diff --git a/lib/support/drivers/temp.lua b/lib/support/drivers/temp.lua new file mode 100644 index 0000000..09ffe9f --- /dev/null +++ b/lib/support/drivers/temp.lua @@ -0,0 +1,28 @@ +local device = _G + +api={} +api.getTemp = {} +api.getTemp.parameters = {} -- -- no input parameters +api.getTemp.returns = {[1]={rname="par1", rtype="int"}} +api.getTemp.call = function () + device:send(string.char(0x00)) -- codigo de operacion 0 + local sen_anl_response = device:read(3) + local raw_val + if not sen_anl_response or string.byte(sen_anl_response or "00000000", 2) == nil or string.byte(sen_anl_response or "00000000", 3) == nil + then + raw_val = "nil value" + else + raw_val = string.byte(sen_anl_response, 2)* 256 + string.byte(sen_anl_response, 3) + + local aux = (raw_val * 500) / 1024 --- obtengo la temperatura en grados + + if aux > 180 then + raw_val = (raw_val * 45) / 1024 + else + raw_val = aux + end + + end + return raw_val + +end diff --git a/lib/support/drivers/temp_lubot.lua b/lib/support/drivers/temp_lubot.lua new file mode 100644 index 0000000..dd22fc4 --- /dev/null +++ b/lib/support/drivers/temp_lubot.lua @@ -0,0 +1,20 @@ +local device = _G +local RD_TEMP = string.char(0x34, 0x02) +local char000 = string.char(0,0,0) + + +api={} +api.get_temperature = {} +api.get_temperature.parameters = {} --no parameters +api.get_temperature.returns = {[1]={rname="temperature", rtype="number"}} --one return +api.get_temperature.call = function () + local get_temp_payload = RD_TEMP + device:send(get_temp_payload) + local temperature_response = device:read(3) or char000 + local raw_val = string.byte(temperature_response, 2) + (string.byte(temperature_response, 3) * 256) + local raw_temp = raw_val / 8 + local deg_temp = raw_temp * 0.0625 + --print("rawval, deg_temp: ", raw_val, deg_temp) + return deg_temp +end + diff --git a/lib/support/lib/bobot_baseboard.lua b/lib/support/lib/bobot_baseboard.lua new file mode 100644 index 0000000..757731a --- /dev/null +++ b/lib/support/lib/bobot_baseboard.lua @@ -0,0 +1,393 @@ +#!/usr/bin/lua + +--module(..., package.seeall); + +local my_path = debug.getinfo(1, "S").source:match[[^@?(.*[\/])[^\/]-$]] + +local bobot_device = require("bobot_device") +local bobot = require("bobot") + +local NULL_BYTE = string.char(0x00) +local DEFAULT_PACKET_SIZE = 0x04 +local GET_USER_MODULES_SIZE_COMMAND = string.char(0x05) +local GET_USER_MODULE_LINE_COMMAND = string.char(0x06) +local GET_HANDLER_SIZE_COMMAND = string.char(0x0A) +local GET_HANDLER_TYPE_COMMAND = string.char(0x0B) +local GET_LINES_RESPONSE_PACKET_SIZE = 5 +local GET_LINE_RESPONSE_PACKET_SIZE = 12 +local GET_HANDLER_TYPE_PACKET_SIZE = 5 +local GET_HANDLER_RESPONSE_PACKET_SIZE = 5 -- +local ADMIN_HANDLER_SEND_COMMAND = string.char(0x00) +local ADMIN_MODULE_IN_ENDPOINT = 0x01 +local ADMIN_MODULE_OUT_ENDPOINT = 0x81 +local GET_USER_MODULE_LINE_PACKET_SIZE = 0x05 +local CLOSEALL_BASE_BOARD_COMMAND = string.char(0x07) +local CLOSEALL_BASE_BOARD_RESPONSE_PACKET_SIZE = 5 +local TIMEOUT = 250 --ms +local MAX_RETRY = 5 + +local BaseBoard = {} + + +--executes s on the console and returns the output +local function run_shell (s) + local f = io.popen(s) -- runs command + local l = f:read("*a") -- read output of command + f:close() + return l +end + +openable = {} +hotplug = {} + +local function parse_drivers() + local driver_files=run_shell("sh -c 'ls "..my_path.."../drivers/*.lua 2> /dev/null'") + for filename in driver_files:gmatch('drivers%/(%S+)%.lua') do + --print ("Driver openable", filename) + openable[filename] = true + end + driver_files=run_shell("sh -c 'ls "..my_path.."../drivers/hotplug/*.lua 2> /dev/null'") + for filename in driver_files:gmatch('drivers%/hotplug%/(%S+)%.lua') do + --print ("Driver hotplug", filename) + hotplug[filename] = true + end +end +parse_drivers() + +local function load_modules(bb) + local retry = 0 + bb.modules = {} + + local n_modules=bb:get_user_modules_size() + while n_modules == nil and retry < MAX_RETRY do + n_modules=bb:get_user_modules_size() + bobot.debugprint("u4b:the module list size returned a nil value, trying to recover...") + retry = retry+1 + end + if not n_modules then return nil end + retry=0 + bobot.debugprint ("Reading modules:", n_modules) + for i = 1, n_modules do + local modulename=bb:get_user_module_line(i) + while modulename == nil and retry < MAX_RETRY do + bobot.debugprint("u4b:the module returned a nil value, trying to recover...") + modulename=bb:get_handler_type(i) + retry = retry+1 + end + if not modulename then return nil end + if openable[modulename] then + bb.modules[i]=modulename + local d = bobot_device:new({ + module=modulename, + --name=module, + baseboard=bb, + hotplug=false, + in_endpoint=0x01, out_endpoint=0x01, + }) -- in_endpoint=0x01, out_endpoint=0x01}) + bb.devices[d]=true + bb.devices[#bb.devices+1]=d + + bb.modules[modulename]=d + elseif hotplug[modulename] then + bb.modules[i]=modulename + bb.modules[modulename]=true + bb.hotplug = true -- bb has a hotplug module + else + bobot.debugprint("Loading modules: missing driver for",modulename) + end + end + return true +end + +local function load_module_handlers(bb) + local retry = 0 + + local n_module_handlers=bb:get_handler_size() + while n_module_handlers == nil and retry < MAX_RETRY do + n_module_handlers=bb:get_handler_size() + bobot.debugprint("u4b:the module handler list size returned a nil value, trying to recover...") + retry = retry+1 + end + if (not n_module_handlers) or (n_module_handlers > 32) then return nil end + retry=0 + bobot.debugprint ("Reading moduleshandlers:", n_module_handlers) + for i=1, n_module_handlers do + local t_handler = bb:get_handler_type(i) + while(t_handler == nil and retry < MAX_RETRY) do + bobot.debugprint("u4b:the module handler returned a nil value, trying to recover...") + t_handler = bb:get_handler_type(i) + retry = retry+1 + end + if not t_handler then return nil end + if t_handler<255 then + local modulename=bb.modules[t_handler+1] + local moduledev=bb.modules[modulename] + if type(moduledev)=='table' + and not moduledev.handler then + moduledev.handler=i-1 + elseif moduledev==true then + --name=name.."@"..(i-1) + local d = bobot_device:new({ + handler=i-1, + module=modulename, + --name=name, + baseboard=bb, + hotplug=(hotplug[modulename]), + in_endpoint=0x01, out_endpoint=0x01, + }) -- in_endpoint=0x01, out_endpoint=0x01}) + if d then + bb.devices[d]=true + bb.devices[#bb.devices+1]=d + end + else + bobot.debugprint ("No opened device!") + end + end + end + return true +end + + +function BaseBoard:refresh() + self.devices = {} + + if not load_modules(self) then + return nil,"failure reading modules" + end + + if not load_module_handlers(self) then + return nil,"failure reading module handlers" + end + return true +end + +--Instantiates BaseBoard object. +--Loads list of modules installed on baseboard +function BaseBoard:new(bb) + --parameters sanity check + assert(type(bb)=="table") + assert(type(bb.comms)=="table") + + --OO boilerplate + setmetatable(bb, self) + self.__index = self + + bb:refresh() + +--bobot.debugprint ('----------------') + --bb:force_close_all() +--bobot.debugprint ('================') + return bb +end + +--Closes all modules opened on baseboard +function BaseBoard:close() + --state sanity check + assert(type(self.devices)=="table") + + for _,d in ipairs(self.devices) do + if type(d.handler)=="number" then + bobot.debugprint ("closing", d.name, d.handler) + d:close() + end + end + + --TODO actually close the baseboard +end + +--returns number of modules present on baseboard +function BaseBoard:get_user_modules_size() + --state sanity check + assert(type(self.comms)=="table") + + local comms=self.comms + + -- In case of get_user_modules_size command is atended by admin module in handler 0 and send operation is 000 + + local handler_packet = ADMIN_HANDLER_SEND_COMMAND .. string.char(DEFAULT_PACKET_SIZE) .. NULL_BYTE + local admin_packet = GET_USER_MODULES_SIZE_COMMAND + local get_user_modules_size_packet = handler_packet .. admin_packet + + local write_res = comms.send(ADMIN_MODULE_IN_ENDPOINT, get_user_modules_size_packet, TIMEOUT) + if write_res then + local data, err = comms.read(ADMIN_MODULE_OUT_ENDPOINT, GET_LINES_RESPONSE_PACKET_SIZE, TIMEOUT) + if not data then + bobot.debugprint("u4b:get_user_modules_size:comunication with I/O board read error", err) + return 0 + else + local user_modules_size = string.byte(data, 5) + return user_modules_size + end + else + bobot.debugprint("u4b:get_user_modules_size:comunication with I/O board write error", write_res) + return 0 + end +end + +--returns thename of a given (by a 1-based index)module +function BaseBoard:get_user_module_line(index) + --state & parameter sanity check + assert(type(index)=="number") + assert(index>0) + assert(type(self.comms)=="table") + + + local comms=self.comms + + -- In case of get_user_module_line command is atended by admin module in handler 0 and send operation is 000 + local get_user_module_line_packet_length = string.char(GET_USER_MODULE_LINE_PACKET_SIZE) + local handler_packet = ADMIN_HANDLER_SEND_COMMAND .. get_user_module_line_packet_length .. NULL_BYTE + local admin_packet = GET_USER_MODULE_LINE_COMMAND .. string.char(index-1) + local get_user_module_line_packet = handler_packet .. admin_packet + + local write_res = comms.send(ADMIN_MODULE_IN_ENDPOINT, get_user_module_line_packet, TIMEOUT) + if write_res then + local data, err = comms.read(ADMIN_MODULE_OUT_ENDPOINT, GET_LINE_RESPONSE_PACKET_SIZE, TIMEOUT) + if not data then + bobot.debugprint("u4b:get_user_modules_line:comunication with I/O board read error", err) + return + end + --the name is between a header and a null + local end_mark = string.find(data, "\000", GET_USER_MODULE_LINE_PACKET_SIZE, true) + if not end_mark then + bobot.debugprint ("u4b:get_user_module_line:Error parsing module name") + return + end + local module_name = string.sub(data, GET_USER_MODULE_LINE_PACKET_SIZE, end_mark-1) + return module_name + else + bobot.debugprint("u4b:get_user_module_line:comunication with I/O board write error", write_res) + end +end + +function BaseBoard:get_handler_size() ------ NEW LISTI ------ + --state sanity check + assert(type(self.comms)=="table") + + local comms=self.comms + + local handler_packet = ADMIN_HANDLER_SEND_COMMAND .. string.char(DEFAULT_PACKET_SIZE) .. NULL_BYTE + local admin_packet = GET_HANDLER_SIZE_COMMAND + local get_handler_size_packet = handler_packet .. admin_packet + + local write_res = comms.send(ADMIN_MODULE_IN_ENDPOINT, get_handler_size_packet, TIMEOUT) + if write_res then + local data, err = comms.read(ADMIN_MODULE_OUT_ENDPOINT, GET_HANDLER_RESPONSE_PACKET_SIZE, TIMEOUT) + if not data then + bobot.debugprint("u4b:get_handler_size:comunication with I/O board read error", err) + return 0 + else + local handler_size = string.byte(data, 5) + return handler_size + end + else + bobot.debugprint("u4b:get_handler_type:comunication with I/O board write error", write_res) + end +end +function BaseBoard:get_handler_type(index) ------ NEW LISTI ------ + --state & parameter sanity check + assert(type(index)=="number") + assert(index>0) + assert(type(self.comms)=="table") + + local comms=self.comms + + -- In case of get_handler_type command is atended by admin module in handler 0 and send operation is 000 + local get_handler_type_packet_length = string.char(GET_HANDLER_TYPE_PACKET_SIZE) --GET_USER_MODULE_LINE_PACKET_SIZE + local handler_packet = ADMIN_HANDLER_SEND_COMMAND .. get_handler_type_packet_length .. NULL_BYTE + local admin_packet = GET_HANDLER_TYPE_COMMAND .. string.char(index-1) + local get_handler_type_packet = handler_packet .. admin_packet + + local write_res = comms.send(ADMIN_MODULE_IN_ENDPOINT, get_handler_type_packet, TIMEOUT) + if write_res then + local data, err = comms.read(ADMIN_MODULE_OUT_ENDPOINT, GET_HANDLER_RESPONSE_PACKET_SIZE, TIMEOUT) + if not data then + bobot.debugprint("u4b:get_handler_type:comunication with I/O board read error", err) + return 0 + else + local handler_type = string.byte(data, 5) + return handler_type + end + else + bobot.debugprint("u4b:get_handler_type:comunication with I/O board write error", write_res) + end +end + +-- resets the baseboard, after this operation the baseboard will claim reenumeration to the operative system +-- this function is deprecated by force_close_all +function BaseBoard:close_all() + for _,d in ipairs(self.devices) do + --bobot.debugprint ("===", d.name,d.handler) + if d.handler then d:close() end + end +end + +-- switch the baseboard to the bootloader program implementend as a usb4all command to the admin module +function BaseBoard:switch_to_bootloader() + --state & parameter sanity check + assert(type(self.comms)=="table") + + local comms=self.comms + -- In case of reset_base_board command is atended by admin module in handler 0 and send operation is 000 + local handler_packet = ADMIN_HANDLER_SEND_COMMAND .. string.char(DEFAULT_PACKET_SIZE) .. NULL_BYTE + local admin_packet = string.char(0x09) --SWITCH_TO_BOOT_BASE_BOARD_COMMAND + local boot_base_board_packet = handler_packet .. admin_packet + + local write_res = comms.send(ADMIN_MODULE_IN_ENDPOINT, boot_base_board_packet, TIMEOUT) + --from this moment the board is reseted, so there is nothing more to do +end + +function BaseBoard:reset() + --state & parameter sanity check + assert(type(self.comms)=="table") + + local comms=self.comms + -- In case of reset_base_board command is atended by admin module in handler 0 and send operation is 000 + local handler_packet = ADMIN_HANDLER_SEND_COMMAND .. string.char(DEFAULT_PACKET_SIZE) .. NULL_BYTE + local admin_packet = string.char(0xFF) --RESET_BASE_BOARD_COMMAND + local reset_base_board_packet = handler_packet .. admin_packet + + local write_res = comms.send(ADMIN_MODULE_IN_ENDPOINT, reset_base_board_packet, TIMEOUT) + if write_res then + -- no tego que leer respuesta porque se reseteo + --libusb.close(libusb_handler) + --self.libusb_handler=nil + --for d_name,d in pairs(self.devices) do + --bobot.debugprint ("===", d.name,d.handler) + -- d.handler=nil + --end + else + bobot.debugprint("u4b:reset:comunication with I/O board write error", write_res) + end +end + +function BaseBoard:force_close_all() + --state & parameter sanity check + assert(type(self.comms)=="table") + + local comms=self.comms + -- In case of reset_base_board command is atended by admin module in handler 0 and send operation is 000 + local handler_packet = ADMIN_HANDLER_SEND_COMMAND .. string.char(DEFAULT_PACKET_SIZE) .. NULL_BYTE + local admin_packet = CLOSEALL_BASE_BOARD_COMMAND + local reset_base_board_packet = handler_packet .. admin_packet + +--bobot.debugprint ('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%') + local write_res = comms.send(ADMIN_MODULE_IN_ENDPOINT, reset_base_board_packet, TIMEOUT) +--bobot.debugprint ('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%') + if write_res then + local data, err = comms.read(ADMIN_MODULE_OUT_ENDPOINT, CLOSEALL_BASE_BOARD_RESPONSE_PACKET_SIZE, TIMEOUT) + if err then + bobot.debugprint("u4b:force_close_all:comunication with I/O board read error",err) + else + --bobot.debugprint("u4b:force_close_all:libusb read",string.byte(data,1,string.len(data))) + end + for _,d in ipairs(self.devices) do + --bobot.debugprint ("===", d.name,d.handler) + d.handler=nil + end + else + bobot.debugprint("u4b:force_close_all:comunication with I/O board write error", write_res) + end +end + +return BaseBoard diff --git a/lib/support/lib/bobot_device.lua b/lib/support/lib/bobot_device.lua new file mode 100644 index 0000000..b560255 --- /dev/null +++ b/lib/support/lib/bobot_device.lua @@ -0,0 +1,216 @@ +--module(..., package.seeall); + +local bobot = require("bobot") + +local string_char = string.char +local string_len = string.len +local string_byte = string.byte + +local OPEN_COMMAND = string_char(0x00) +local CLOSE_COMMAND = string_char(0x01) +local HEADER_PACKET_SIZE = 6 +local NULL_BYTE = string_char(0x00) +local ADMIN_MODULE_IN_ENDPOINT = 0x01 +local ADMIN_MODULE_OUT_ENDPOINT = 0x81 +local ADMIN_HANDLER_SEND_COMMAND = string_char(0x00) +local OPEN_RESPONSE_PACKET_SIZE = 5 +local CLOSE_RESPONSE_PACKET_SIZE = 2 +local TIMEOUT = 200 --ms + +local READ_HEADER_SIZE = 3 + +local my_path = debug.getinfo(1, "S").source:match[[^@?(.*[\/])[^\/]-$]] + +local Device = { + --some usefull stuff for the drivers to use + string=string, + print=bobot.debugprint, + math=math, + tonumber=tonumber, + tostring=tostring +} + + +local drivers_cache = setmetatable({}, {__mode='kv'}) +local function load_driver(d) + local modulename=d.module + if drivers_cache[modulename] then return drivers_cache[modulename] end + + local drivername=string.match(modulename, '^(.-)%d*$') + local f, err + if d.hotplug then + f, err = loadfile(my_path.."../drivers/hotplug/"..drivername..".lua") + else + f, err = loadfile(my_path.."../drivers/"..drivername..".lua") + end + drivers_cache[modulename] = f + return f, err +end + +--Instantiates Device object. +--Attempts to load api from driver +function Device:new(d) + --parameters sanity check + assert(type(d)=="table") + --assert(type(d.name)=="string") + assert(type(d.module)=="string") + assert(type(d.baseboard)=="table") + assert(type(d.baseboard.comms)=="table") + assert(type(d.baseboard.comms.send)=="function") + assert(type(d.baseboard.comms.read)=="function") + + --OO boilerplate + setmetatable(d, self) + self.__index = self + + --store locally, save 2 indirections + d.comms_send = d.baseboard.comms.send + d.comms_read = d.baseboard.comms.read + + --attempt to load api from driver + local f, err = load_driver(d) + if f then + d._G=d + + setfenv(f, d) --the driver's environment is the device + local status, err=pcall(f) + if status then + bobot.debugprint("u4d:new:Success loading driver:", d.module) + else + bobot.debugprint("u4d:new:Error initializing driver:", tostring(err)) + end + else + bobot.debugprint("u4d:new:Error loading driver:", err) + return nil + end + + return d +end + +--opens the device. must be done before sending / reading / etc. +--receives endpoints, which can be ommited if they were provided at Device creation +function Device:open(in_endpoint, out_endpoint) + --state & parameter sanity check + assert(self.handler==nil) + --assert(type(self.name)=="string") + assert(type(in_endpoint)=="number" or type(self.in_endpoint)=="number") + assert(type(self.comms_send)=="function") + assert(type(self.comms_read)=="function") + assert(type(out_endpoint)=="number" or type(self.out_endpoint)=="number") + + --save for later use + if in_endpoint then self.in_endpoint = in_endpoint end + if out_endpoint then self.out_endpoint = out_endpoint end + + local module_name=self.module .."\000" -- usb4all expect null terminated names + + local open_packet_length = string_char(HEADER_PACKET_SIZE + string_len(module_name)) + + local module_in_endpoint = string_char(self.in_endpoint) + local module_out_endpoint = string_char(self.out_endpoint) + + local handler_packet = ADMIN_HANDLER_SEND_COMMAND .. open_packet_length .. NULL_BYTE + local admin_packet = OPEN_COMMAND .. module_in_endpoint .. module_out_endpoint .. module_name + local open_packet = handler_packet .. admin_packet + local write_res = self.comms_send(ADMIN_MODULE_IN_ENDPOINT, open_packet, TIMEOUT) + + if not write_res then + bobot.debugprint("u4d:open:comunication with I/O board write error", write_res) + return false + end + + local data, err = self.comms_read(ADMIN_MODULE_OUT_ENDPOINT, OPEN_RESPONSE_PACKET_SIZE, TIMEOUT) + if not data then + bobot.debugprint ("u4d:open:comunication with I/O boardread error", err) + return false + end + + local handler = string_byte(data, 5) + --hander -1 meand error + if handler==255 then + bobot.debugprint ("u4d:open:Already open!",self.module,self.handler) + return + else + bobot.debugprint ("u4d:open:Success!",self.module,handler) + self.handler = handler --self.handler set means device is open + return true + end + +end + +--closes the device +function Device:close() + if not self.handler then return end --already closed + + --state sanity check + assert(type(self.handler)=="number") + assert(type(self.comms_send)=="function") + assert(type(self.comms_read)=="function") + + local close_packet_length = string_char(0x04) --string.char(HEADER_PACKET_SIZE + string.len(module_name)) + local handler_packet = ADMIN_HANDLER_SEND_COMMAND .. close_packet_length .. NULL_BYTE + local admin_packet = CLOSE_COMMAND .. string_char(self.handler) + local close_packet = handler_packet .. admin_packet + + local write_res = self.comms_send(ADMIN_MODULE_IN_ENDPOINT, close_packet, TIMEOUT) + if not write_res then + bobot.debugprint("u4d:close:comunication with I/O board write error", write_res) + return + end + local data, err = self.comms_read(ADMIN_MODULE_OUT_ENDPOINT, CLOSE_RESPONSE_PACKET_SIZE, TIMEOUT) + + self.handler = nil +end + +--sends data (a string) to device +function Device:send(data) + --state & parameter sanity check + data=tostring(data) + assert(type(data)=="string") + assert(type(self.handler)=="number") + assert(type(self.in_endpoint)=="number") + assert(type(self.comms_send)=="function") + assert(type(self.comms_read)=="function") + + local len=string_len(data) + + local shifted_handler = self.handler * 8 + assert(shifted_handler<256, "u4d:send:shifted_handler vale " .. shifted_handler .. " excede el tamaño maximo representable en un byte (255)") + local user_module_handler_send_command = string_char(shifted_handler) + local send_packet_length = string_char(0x03 + len) + local send_packet = user_module_handler_send_command .. send_packet_length .. NULL_BYTE .. data + + --local tini=socket.gettime() + local write_res, err = self.comms_send(self.in_endpoint, send_packet, TIMEOUT) + --bobot.debugprint ('%%%%%%%%%%%%%%%% device send',socket.gettime()-tini) + + if not write_res then + bobot.debugprint("u4d:send:comunication with I/O board write error", err) + end + + return write_res, err +end + +--read data (len bytes max) from device +function Device:read(len) + len = len or 255 + --state & parameter sanity check + assert(type(len)=="number") + assert(type(self.handler)=="number") + assert(type(self.out_endpoint)=="number") + assert(type(self.comms_send)=="function") + assert(type(self.comms_read)=="function") + + local data, err = self.comms_read(self.out_endpoint, len+READ_HEADER_SIZE, TIMEOUT) + if not data then + bobot.debugprint("u4d:read:comunication with I/O board read error", err) + return nil, err + end + + local data_h = string.sub(data, READ_HEADER_SIZE+1, -1) --discard header + + return data_h, err +end + +return Device + diff --git a/lib/support/lib/comms_chotox.lua b/lib/support/lib/comms_chotox.lua new file mode 100644 index 0000000..18e02bf --- /dev/null +++ b/lib/support/lib/comms_chotox.lua @@ -0,0 +1,39 @@ +local bobot_device = require("bobot_device") + +local comms_chotox = {} + +function comms_chotox.send(endpoint, data, timeout) +end + +function comms_chotox.read(endpoint, len, timeout) +end + + +function comms_chotox.init(baseboards) + --parameters sanity check + assert(type(baseboards)=="table") + + local n_boards=1 + --local bb = bobot_baseboard.BaseBoard:new({idBoard=iSerial, comms=comms_usb}) + local bb = {idBoard=1, comms=comms_chotox} + local devices={} + local is_hotplug= {button=true, led=true, grey=true, distanc=true} + for i, name in ipairs({"button", "grey", "distanc","butia"}) do + local dd={name=name, module=name, baseboard=bb, handler=i} + dd.open = function() return true end + dd.close = function() end + dd.read = function() return "" end + dd.send = function() return true end + if is_hotplug[name] then dd.hotplug=true end + + local d = bobot_device:new(dd) -- in_endpoint=0x01, out_endpoint=0x01}) + + devices[name]=true + devices[#devices+1]=d + end + bb.devices=devices + baseboards[1]=bb + return n_boards +end + +return comms_chotox diff --git a/lib/support/lib/comms_serial.lua b/lib/support/lib/comms_serial.lua new file mode 100644 index 0000000..4bd3bb0 --- /dev/null +++ b/lib/support/lib/comms_serial.lua @@ -0,0 +1,94 @@ +--module(..., package.seeall); + +--local socket=require("socket") + +local bobot_baseboard = require("bobot_baseboard") +local bobot = require("bobot") + +local my_path = debug.getinfo(1, "S").source:match[[^@?(.*[\/])[^\/]-$]] +assert(package.loadlib(my_path .. "lua_serialcomm.so","luaopen_serialcomm"))() +local serialcomm=_G.serialcomm; _G.serialcomm=nil + +local serial_handler + +--executes s on the console and returns the output +local function run_shell (s) + local f = io.popen(s) -- runs command + local l = f:read("*a") -- read output of command + f:close() + return l +end + +local function split_words(s) + local words={} + for p in string.gmatch(s, "%S+") do + words[#words+1]=p + end + return words +end + +local comms_serial = {} + +function comms_serial.send(endpoint, data, timeout) + --parameters sanity check + assert(type(serial_handler)=="number") + --assert(type(endpoint)=="number") + assert(type(data)=="string") + assert(type(timeout)=="number") + + --local tini=socket.gettime() + local ret = serialcomm.send_msg(serial_handler, data) + --bobot.debugprint ('%%%%%%%%%%%%%%%% comms serial send',socket.gettime()-tini) + return ret +end + +function comms_serial.read(endpoint, len, timeout) + --parameters sanity check + assert(type(serial_handler)=="number") + --assert(type(endpoint)=="number") + --assert(type(len)=="number") + --assert(type(timeout)=="number") + + return serialcomm.read_msg(serial_handler, len, timeout) +end + + +function comms_serial.init(baseboards) + --parameters sanity check + assert(type(baseboards)=="table") + + --FIXME leer ttyusbs... + --local tty_s=run_shell("ls /dev/ttyUSB* ") + local tty_s=run_shell("sh -c 'ls /dev/ttyUSB* 2> /dev/null'") --supress errors + local tty_t=split_words(tty_s) + local tty + local err + if (#tty_t == 0) then + return 0,"no ttyUSB found" + end + + -- tty="/dev/ttyUSB0" + --for i=1, #tty_t do + for _, ttyI in ipairs(tty_t) do + bobot.debugprint ("Trying to connect to", ttyI) + serial_handler, err = serialcomm.init(ttyI, 115200) + if serial_handler then + tty=ttyI + break + else + bobot.debugprint("Error connecting:", err) + end + end + if not serial_handler then + bobot.debugprint("cs:", "no ttyUSB could be open") + return 0, err + end + bobot.debugprint ("cs:", tty) + local bb = bobot_baseboard:new({idBoard=tty, comms=comms_serial}) + + baseboards[#baseboards+1]=bb + + return 1 +end + +return comms_serial diff --git a/lib/support/lib/comms_usb.lua b/lib/support/lib/comms_usb.lua new file mode 100644 index 0000000..91d64b5 --- /dev/null +++ b/lib/support/lib/comms_usb.lua @@ -0,0 +1,99 @@ +--module(..., package.seeall); + +local bobot_baseboard = require("bobot_baseboard") +local bobot = require("bobot") + +local my_path = debug.getinfo(1, "S").source:match[[^@?(.*[\/])[^\/]-$]] +assert(package.loadlib(my_path .. "libluausb.so","luaopen_libusb"))() +local libusb=_G.libusb; _G.libusb=nil + +local usb_bulk_write = libusb.bulk_write +local usb_bulk_read = libusb.bulk_read + +local USB4ALL_VENDOR = 0x04d8 +local USB4ALL_PRODUCT = 0x000c +local USB4ALL_CONFIGURATION = 1 +local USB4ALL_INTERFACE = 0 + +local READ_HEADER_SIZE = 3 + +local libusb_handler + + +local comms_usb = {} + +function comms_usb.send(endpoint, data, timeout) + --parameters sanity check + assert(type(libusb_handler)=="userdata") + assert(type(endpoint)=="number") + assert(type(data)=="string") + assert(type(timeout)=="number") + + return usb_bulk_write(libusb_handler, endpoint, data, timeout) +end + +function comms_usb.read(endpoint, len, timeout) + --parameters sanity check + assert(type(libusb_handler)=="userdata") + assert(type(endpoint)=="number") + assert(type(len)=="number") + assert(type(timeout)=="number") + + return usb_bulk_read(libusb_handler, endpoint, len+READ_HEADER_SIZE, timeout) +end + + +function comms_usb.init(baseboards) + --parameters sanity check + assert(type(baseboards)=="table") + + + --refresh devices and buses + libusb.find_busses() + libusb.find_devices() + local n_boards = 0 + + local buses=libusb.get_busses() + for dirname, bus in pairs(buses) do --iterate buses + local devices=libusb.get_devices(bus) + for filename, device in pairs(devices) do --iterate devices + local descriptor = libusb.device_descriptor(device) + + --if device is baseboard... + if ((descriptor.idVendor == USB4ALL_VENDOR) and (descriptor.idProduct == USB4ALL_PRODUCT)) then + --try to intialize baseboard + bobot.debugprint("Initializing Baseboard:", descriptor.idVendor, descriptor.idProduct) + libusb_handler = libusb.open(device) + + if not libusb_handler then + bobot.debugprint("Error opening device") + break + end + if not libusb.set_configuration(libusb_handler, USB4ALL_CONFIGURATION) then + bobot.debugprint("Error configuring device, retrying after a reset") + libusb.reset(libusb_handler) + if not libusb.set_configuration(libusb_handler, USB4ALL_CONFIGURATION) then + bobot.debugprint("Error configuring device.") + break + end + end + if not libusb.claim_interface(libusb_handler, USB4ALL_INTERFACE) then + bobot.debugprint("Error seting device interface") + break + end + + --success initializing, instantiate BaseBoard object and register + local iSerial=descriptor.iSerialNumber + local bb = bobot_baseboard:new({idBoard=iSerial, comms=comms_usb}) + --bb:force_close_all() + --bobot.debugprint("Baseboard:", iSerial) + + baseboards[#baseboards+1]=bb + n_boards = n_boards + 1 + end + end + end + return n_boards +end + +return comms_usb diff --git a/lib/support/lib/libluausb.so b/lib/support/lib/libluausb.so Binary files differnew file mode 100755 index 0000000..a7c9406 --- /dev/null +++ b/lib/support/lib/libluausb.so diff --git a/lib/support/lib/lua_serialcomm.so b/lib/support/lib/lua_serialcomm.so Binary files differnew file mode 100755 index 0000000..cb79295 --- /dev/null +++ b/lib/support/lib/lua_serialcomm.so diff --git a/lib/support/lib/mime/core.so b/lib/support/lib/mime/core.so Binary files differnew file mode 100755 index 0000000..696b7d9 --- /dev/null +++ b/lib/support/lib/mime/core.so diff --git a/lib/support/lib/socket/core.so b/lib/support/lib/socket/core.so Binary files differnew file mode 100755 index 0000000..ddfc8c3 --- /dev/null +++ b/lib/support/lib/socket/core.so diff --git a/lib/support/libluausb.so b/lib/support/libluausb.so Binary files differnew file mode 100755 index 0000000..a7c9406 --- /dev/null +++ b/lib/support/libluausb.so diff --git a/lib/support/lua b/lib/support/lua Binary files differnew file mode 100755 index 0000000..a36f550 --- /dev/null +++ b/lib/support/lua diff --git a/lib/support/lua_serialcomm.so b/lib/support/lua_serialcomm.so Binary files differnew file mode 100755 index 0000000..cb79295 --- /dev/null +++ b/lib/support/lua_serialcomm.so diff --git a/lib/support/share/ltn12.lua b/lib/support/share/ltn12.lua new file mode 100644 index 0000000..b42689a --- /dev/null +++ b/lib/support/share/ltn12.lua @@ -0,0 +1,292 @@ +----------------------------------------------------------------------------- +-- LTN12 - Filters, sources, sinks and pumps. +-- LuaSocket toolkit. +-- Author: Diego Nehab +-- RCS ID: $Id: ltn12.lua,v 1.31 2006/04/03 04:45:42 diego Exp $ +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module +----------------------------------------------------------------------------- +local string = require("string") +local table = require("table") +local base = _G +module("ltn12") + +filter = {} +source = {} +sink = {} +pump = {} + +-- 2048 seems to be better in windows... +BLOCKSIZE = 2048 +_VERSION = "LTN12 1.0.1" + +----------------------------------------------------------------------------- +-- Filter stuff +----------------------------------------------------------------------------- +-- returns a high level filter that cycles a low-level filter +function filter.cycle(low, ctx, extra) + base.assert(low) + return function(chunk) + local ret + ret, ctx = low(ctx, chunk, extra) + return ret + end +end + +-- chains a bunch of filters together +-- (thanks to Wim Couwenberg) +function filter.chain(...) + local n = table.getn(arg) + local top, index = 1, 1 + local retry = "" + return function(chunk) + retry = chunk and retry + while true do + if index == top then + chunk = arg[index](chunk) + if chunk == "" or top == n then return chunk + elseif chunk then index = index + 1 + else + top = top+1 + index = top + end + else + chunk = arg[index](chunk or "") + if chunk == "" then + index = index - 1 + chunk = retry + elseif chunk then + if index == n then return chunk + else index = index + 1 end + else base.error("filter returned inappropriate nil") end + end + end + end +end + +----------------------------------------------------------------------------- +-- Source stuff +----------------------------------------------------------------------------- +-- create an empty source +local function empty() + return nil +end + +function source.empty() + return empty +end + +-- returns a source that just outputs an error +function source.error(err) + return function() + return nil, err + end +end + +-- creates a file source +function source.file(handle, io_err) + if handle then + return function() + local chunk = handle:read(BLOCKSIZE) + if not chunk then handle:close() end + return chunk + end + else return source.error(io_err or "unable to open file") end +end + +-- turns a fancy source into a simple source +function source.simplify(src) + base.assert(src) + return function() + local chunk, err_or_new = src() + src = err_or_new or src + if not chunk then return nil, err_or_new + else return chunk end + end +end + +-- creates string source +function source.string(s) + if s then + local i = 1 + return function() + local chunk = string.sub(s, i, i+BLOCKSIZE-1) + i = i + BLOCKSIZE + if chunk ~= "" then return chunk + else return nil end + end + else return source.empty() end +end + +-- creates rewindable source +function source.rewind(src) + base.assert(src) + local t = {} + return function(chunk) + if not chunk then + chunk = table.remove(t) + if not chunk then return src() + else return chunk end + else + table.insert(t, chunk) + end + end +end + +function source.chain(src, f) + base.assert(src and f) + local last_in, last_out = "", "" + local state = "feeding" + local err + return function() + if not last_out then + base.error('source is empty!', 2) + end + while true do + if state == "feeding" then + last_in, err = src() + if err then return nil, err end + last_out = f(last_in) + if not last_out then + if last_in then + base.error('filter returned inappropriate nil') + else + return nil + end + elseif last_out ~= "" then + state = "eating" + if last_in then last_in = "" end + return last_out + end + else + last_out = f(last_in) + if last_out == "" then + if last_in == "" then + state = "feeding" + else + base.error('filter returned ""') + end + elseif not last_out then + if last_in then + base.error('filter returned inappropriate nil') + else + return nil + end + else + return last_out + end + end + end + end +end + +-- creates a source that produces contents of several sources, one after the +-- other, as if they were concatenated +-- (thanks to Wim Couwenberg) +function source.cat(...) + local src = table.remove(arg, 1) + return function() + while src do + local chunk, err = src() + if chunk then return chunk end + if err then return nil, err end + src = table.remove(arg, 1) + end + end +end + +----------------------------------------------------------------------------- +-- Sink stuff +----------------------------------------------------------------------------- +-- creates a sink that stores into a table +function sink.table(t) + t = t or {} + local f = function(chunk, err) + if chunk then table.insert(t, chunk) end + return 1 + end + return f, t +end + +-- turns a fancy sink into a simple sink +function sink.simplify(snk) + base.assert(snk) + return function(chunk, err) + local ret, err_or_new = snk(chunk, err) + if not ret then return nil, err_or_new end + snk = err_or_new or snk + return 1 + end +end + +-- creates a file sink +function sink.file(handle, io_err) + if handle then + return function(chunk, err) + if not chunk then + handle:close() + return 1 + else return handle:write(chunk) end + end + else return sink.error(io_err or "unable to open file") end +end + +-- creates a sink that discards data +local function null() + return 1 +end + +function sink.null() + return null +end + +-- creates a sink that just returns an error +function sink.error(err) + return function() + return nil, err + end +end + +-- chains a sink with a filter +function sink.chain(f, snk) + base.assert(f and snk) + return function(chunk, err) + if chunk ~= "" then + local filtered = f(chunk) + local done = chunk and "" + while true do + local ret, snkerr = snk(filtered, err) + if not ret then return nil, snkerr end + if filtered == done then return 1 end + filtered = f(done) + end + else return 1 end + end +end + +----------------------------------------------------------------------------- +-- Pump stuff +----------------------------------------------------------------------------- +-- pumps one chunk from the source to the sink +function pump.step(src, snk) + local chunk, src_err = src() + local ret, snk_err = snk(chunk, src_err) + if chunk and ret then return 1 + else return nil, src_err or snk_err end +end + +-- pumps all data from a source to a sink, using a step function +function pump.all(src, snk, step) + base.assert(src and snk) + step = step or pump.step + while true do + local ret, err = step(src, snk) + if not ret then + if err then return nil, err + else return 1 end + end + end +end + diff --git a/lib/support/share/mime.lua b/lib/support/share/mime.lua new file mode 100644 index 0000000..169eda2 --- /dev/null +++ b/lib/support/share/mime.lua @@ -0,0 +1,87 @@ +----------------------------------------------------------------------------- +-- MIME support for the Lua language. +-- Author: Diego Nehab +-- Conforming to RFCs 2045-2049 +-- RCS ID: $Id: mime.lua,v 1.29 2007/06/11 23:44:54 diego Exp $ +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +----------------------------------------------------------------------------- +local base = _G +local ltn12 = require("ltn12") +local mime = require("mime.core") +local io = require("io") +local string = require("string") +module("mime") + +-- encode, decode and wrap algorithm tables +encodet = {} +decodet = {} +wrapt = {} + +-- creates a function that chooses a filter by name from a given table +local function choose(table) + return function(name, opt1, opt2) + if base.type(name) ~= "string" then + name, opt1, opt2 = "default", name, opt1 + end + local f = table[name or "nil"] + if not f then + base.error("unknown key (" .. base.tostring(name) .. ")", 3) + else return f(opt1, opt2) end + end +end + +-- define the encoding filters +encodet['base64'] = function() + return ltn12.filter.cycle(b64, "") +end + +encodet['quoted-printable'] = function(mode) + return ltn12.filter.cycle(qp, "", + (mode == "binary") and "=0D=0A" or "\r\n") +end + +-- define the decoding filters +decodet['base64'] = function() + return ltn12.filter.cycle(unb64, "") +end + +decodet['quoted-printable'] = function() + return ltn12.filter.cycle(unqp, "") +end + +local function format(chunk) + if chunk then + if chunk == "" then return "''" + else return string.len(chunk) end + else return "nil" end +end + +-- define the line-wrap filters +wrapt['text'] = function(length) + length = length or 76 + return ltn12.filter.cycle(wrp, length, length) +end +wrapt['base64'] = wrapt['text'] +wrapt['default'] = wrapt['text'] + +wrapt['quoted-printable'] = function() + return ltn12.filter.cycle(qpwrp, 76, 76) +end + +-- function that choose the encoding, decoding or wrap algorithm +encode = choose(encodet) +decode = choose(decodet) +wrap = choose(wrapt) + +-- define the end-of-line normalization filter +function normalize(marker) + return ltn12.filter.cycle(eol, 0, marker) +end + +-- high level stuffing filter +function stuff() + return ltn12.filter.cycle(dot, 2) +end diff --git a/lib/support/share/socket.lua b/lib/support/share/socket.lua new file mode 100644 index 0000000..211adcd --- /dev/null +++ b/lib/support/share/socket.lua @@ -0,0 +1,133 @@ +----------------------------------------------------------------------------- +-- LuaSocket helper module +-- Author: Diego Nehab +-- RCS ID: $Id: socket.lua,v 1.22 2005/11/22 08:33:29 diego Exp $ +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +----------------------------------------------------------------------------- +local base = _G +local string = require("string") +local math = require("math") +local socket = require("socket.core") +module("socket") + +----------------------------------------------------------------------------- +-- Exported auxiliar functions +----------------------------------------------------------------------------- +function connect(address, port, laddress, lport) + local sock, err = socket.tcp() + if not sock then return nil, err end + if laddress then + local res, err = sock:bind(laddress, lport, -1) + if not res then return nil, err end + end + local res, err = sock:connect(address, port) + if not res then return nil, err end + return sock +end + +function bind(host, port, backlog) + local sock, err = socket.tcp() + if not sock then return nil, err end + sock:setoption("reuseaddr", true) + local res, err = sock:bind(host, port) + if not res then return nil, err end + res, err = sock:listen(backlog) + if not res then return nil, err end + return sock +end + +try = newtry() + +function choose(table) + return function(name, opt1, opt2) + if base.type(name) ~= "string" then + name, opt1, opt2 = "default", name, opt1 + end + local f = table[name or "nil"] + if not f then base.error("unknown key (".. base.tostring(name) ..")", 3) + else return f(opt1, opt2) end + end +end + +----------------------------------------------------------------------------- +-- Socket sources and sinks, conforming to LTN12 +----------------------------------------------------------------------------- +-- create namespaces inside LuaSocket namespace +sourcet = {} +sinkt = {} + +BLOCKSIZE = 2048 + +sinkt["close-when-done"] = function(sock) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function(self, chunk, err) + if not chunk then + sock:close() + return 1 + else return sock:send(chunk) end + end + }) +end + +sinkt["keep-open"] = function(sock) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function(self, chunk, err) + if chunk then return sock:send(chunk) + else return 1 end + end + }) +end + +sinkt["default"] = sinkt["keep-open"] + +sink = choose(sinkt) + +sourcet["by-length"] = function(sock, length) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function() + if length <= 0 then return nil end + local size = math.min(socket.BLOCKSIZE, length) + local chunk, err = sock:receive(size) + if err then return nil, err end + length = length - string.len(chunk) + return chunk + end + }) +end + +sourcet["until-closed"] = function(sock) + local done + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function() + if done then return nil end + local chunk, err, partial = sock:receive(socket.BLOCKSIZE) + if not err then return chunk + elseif err == "closed" then + sock:close() + done = 1 + return partial + else return nil, err end + end + }) +end + + +sourcet["default"] = sourcet["until-closed"] + +source = choose(sourcet) + diff --git a/lib/support/share/socket/ftp.lua b/lib/support/share/socket/ftp.lua new file mode 100644 index 0000000..598f65d --- /dev/null +++ b/lib/support/share/socket/ftp.lua @@ -0,0 +1,281 @@ +----------------------------------------------------------------------------- +-- FTP support for the Lua language +-- LuaSocket toolkit. +-- Author: Diego Nehab +-- RCS ID: $Id: ftp.lua,v 1.45 2007/07/11 19:25:47 diego Exp $ +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +----------------------------------------------------------------------------- +local base = _G +local table = require("table") +local string = require("string") +local math = require("math") +local socket = require("socket") +local url = require("socket.url") +local tp = require("socket.tp") +local ltn12 = require("ltn12") +module("socket.ftp") + +----------------------------------------------------------------------------- +-- Program constants +----------------------------------------------------------------------------- +-- timeout in seconds before the program gives up on a connection +TIMEOUT = 60 +-- default port for ftp service +PORT = 21 +-- this is the default anonymous password. used when no password is +-- provided in url. should be changed to your e-mail. +USER = "ftp" +PASSWORD = "anonymous@anonymous.org" + +----------------------------------------------------------------------------- +-- Low level FTP API +----------------------------------------------------------------------------- +local metat = { __index = {} } + +function open(server, port, create) + local tp = socket.try(tp.connect(server, port or PORT, TIMEOUT, create)) + local f = base.setmetatable({ tp = tp }, metat) + -- make sure everything gets closed in an exception + f.try = socket.newtry(function() f:close() end) + return f +end + +function metat.__index:portconnect() + self.try(self.server:settimeout(TIMEOUT)) + self.data = self.try(self.server:accept()) + self.try(self.data:settimeout(TIMEOUT)) +end + +function metat.__index:pasvconnect() + self.data = self.try(socket.tcp()) + self.try(self.data:settimeout(TIMEOUT)) + self.try(self.data:connect(self.pasvt.ip, self.pasvt.port)) +end + +function metat.__index:login(user, password) + self.try(self.tp:command("user", user or USER)) + local code, reply = self.try(self.tp:check{"2..", 331}) + if code == 331 then + self.try(self.tp:command("pass", password or PASSWORD)) + self.try(self.tp:check("2..")) + end + return 1 +end + +function metat.__index:pasv() + self.try(self.tp:command("pasv")) + local code, reply = self.try(self.tp:check("2..")) + local pattern = "(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)" + local a, b, c, d, p1, p2 = socket.skip(2, string.find(reply, pattern)) + self.try(a and b and c and d and p1 and p2, reply) + self.pasvt = { + ip = string.format("%d.%d.%d.%d", a, b, c, d), + port = p1*256 + p2 + } + if self.server then + self.server:close() + self.server = nil + end + return self.pasvt.ip, self.pasvt.port +end + +function metat.__index:port(ip, port) + self.pasvt = nil + if not ip then + ip, port = self.try(self.tp:getcontrol():getsockname()) + self.server = self.try(socket.bind(ip, 0)) + ip, port = self.try(self.server:getsockname()) + self.try(self.server:settimeout(TIMEOUT)) + end + local pl = math.mod(port, 256) + local ph = (port - pl)/256 + local arg = string.gsub(string.format("%s,%d,%d", ip, ph, pl), "%.", ",") + self.try(self.tp:command("port", arg)) + self.try(self.tp:check("2..")) + return 1 +end + +function metat.__index:send(sendt) + self.try(self.pasvt or self.server, "need port or pasv first") + -- if there is a pasvt table, we already sent a PASV command + -- we just get the data connection into self.data + if self.pasvt then self:pasvconnect() end + -- get the transfer argument and command + local argument = sendt.argument or + url.unescape(string.gsub(sendt.path or "", "^[/\\]", "")) + if argument == "" then argument = nil end + local command = sendt.command or "stor" + -- send the transfer command and check the reply + self.try(self.tp:command(command, argument)) + local code, reply = self.try(self.tp:check{"2..", "1.."}) + -- if there is not a a pasvt table, then there is a server + -- and we already sent a PORT command + if not self.pasvt then self:portconnect() end + -- get the sink, source and step for the transfer + local step = sendt.step or ltn12.pump.step + local readt = {self.tp.c} + local checkstep = function(src, snk) + -- check status in control connection while downloading + local readyt = socket.select(readt, nil, 0) + if readyt[tp] then code = self.try(self.tp:check("2..")) end + return step(src, snk) + end + local sink = socket.sink("close-when-done", self.data) + -- transfer all data and check error + self.try(ltn12.pump.all(sendt.source, sink, checkstep)) + if string.find(code, "1..") then self.try(self.tp:check("2..")) end + -- done with data connection + self.data:close() + -- find out how many bytes were sent + local sent = socket.skip(1, self.data:getstats()) + self.data = nil + return sent +end + +function metat.__index:receive(recvt) + self.try(self.pasvt or self.server, "need port or pasv first") + if self.pasvt then self:pasvconnect() end + local argument = recvt.argument or + url.unescape(string.gsub(recvt.path or "", "^[/\\]", "")) + if argument == "" then argument = nil end + local command = recvt.command or "retr" + self.try(self.tp:command(command, argument)) + local code = self.try(self.tp:check{"1..", "2.."}) + if not self.pasvt then self:portconnect() end + local source = socket.source("until-closed", self.data) + local step = recvt.step or ltn12.pump.step + self.try(ltn12.pump.all(source, recvt.sink, step)) + if string.find(code, "1..") then self.try(self.tp:check("2..")) end + self.data:close() + self.data = nil + return 1 +end + +function metat.__index:cwd(dir) + self.try(self.tp:command("cwd", dir)) + self.try(self.tp:check(250)) + return 1 +end + +function metat.__index:type(type) + self.try(self.tp:command("type", type)) + self.try(self.tp:check(200)) + return 1 +end + +function metat.__index:greet() + local code = self.try(self.tp:check{"1..", "2.."}) + if string.find(code, "1..") then self.try(self.tp:check("2..")) end + return 1 +end + +function metat.__index:quit() + self.try(self.tp:command("quit")) + self.try(self.tp:check("2..")) + return 1 +end + +function metat.__index:close() + if self.data then self.data:close() end + if self.server then self.server:close() end + return self.tp:close() +end + +----------------------------------------------------------------------------- +-- High level FTP API +----------------------------------------------------------------------------- +local function override(t) + if t.url then + local u = url.parse(t.url) + for i,v in base.pairs(t) do + u[i] = v + end + return u + else return t end +end + +local function tput(putt) + putt = override(putt) + socket.try(putt.host, "missing hostname") + local f = open(putt.host, putt.port, putt.create) + f:greet() + f:login(putt.user, putt.password) + if putt.type then f:type(putt.type) end + f:pasv() + local sent = f:send(putt) + f:quit() + f:close() + return sent +end + +local default = { + path = "/", + scheme = "ftp" +} + +local function parse(u) + local t = socket.try(url.parse(u, default)) + socket.try(t.scheme == "ftp", "wrong scheme '" .. t.scheme .. "'") + socket.try(t.host, "missing hostname") + local pat = "^type=(.)$" + if t.params then + t.type = socket.skip(2, string.find(t.params, pat)) + socket.try(t.type == "a" or t.type == "i", + "invalid type '" .. t.type .. "'") + end + return t +end + +local function sput(u, body) + local putt = parse(u) + putt.source = ltn12.source.string(body) + return tput(putt) +end + +put = socket.protect(function(putt, body) + if base.type(putt) == "string" then return sput(putt, body) + else return tput(putt) end +end) + +local function tget(gett) + gett = override(gett) + socket.try(gett.host, "missing hostname") + local f = open(gett.host, gett.port, gett.create) + f:greet() + f:login(gett.user, gett.password) + if gett.type then f:type(gett.type) end + f:pasv() + f:receive(gett) + f:quit() + return f:close() +end + +local function sget(u) + local gett = parse(u) + local t = {} + gett.sink = ltn12.sink.table(t) + tget(gett) + return table.concat(t) +end + +command = socket.protect(function(cmdt) + cmdt = override(cmdt) + socket.try(cmdt.host, "missing hostname") + socket.try(cmdt.command, "missing command") + local f = open(cmdt.host, cmdt.port, cmdt.create) + f:greet() + f:login(cmdt.user, cmdt.password) + f.try(f.tp:command(cmdt.command, cmdt.argument)) + if cmdt.check then f.try(f.tp:check(cmdt.check)) end + f:quit() + return f:close() +end) + +get = socket.protect(function(gett) + if base.type(gett) == "string" then return sget(gett) + else return tget(gett) end +end) + diff --git a/lib/support/share/socket/http.lua b/lib/support/share/socket/http.lua new file mode 100644 index 0000000..ad8db1e --- /dev/null +++ b/lib/support/share/socket/http.lua @@ -0,0 +1,350 @@ +----------------------------------------------------------------------------- +-- HTTP/1.1 client support for the Lua language. +-- LuaSocket toolkit. +-- Author: Diego Nehab +-- RCS ID: $Id: http.lua,v 1.71 2007/10/13 23:55:20 diego Exp $ +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +------------------------------------------------------------------------------- +local socket = require("socket") +local url = require("socket.url") +local ltn12 = require("ltn12") +local mime = require("mime") +local string = require("string") +local base = _G +local table = require("table") +module("socket.http") + +----------------------------------------------------------------------------- +-- Program constants +----------------------------------------------------------------------------- +-- connection timeout in seconds +TIMEOUT = 60 +-- default port for document retrieval +PORT = 80 +-- user agent field sent in request +USERAGENT = socket._VERSION + +----------------------------------------------------------------------------- +-- Reads MIME headers from a connection, unfolding where needed +----------------------------------------------------------------------------- +local function receiveheaders(sock, headers) + local line, name, value, err + headers = headers or {} + -- get first line + line, err = sock:receive() + if err then return nil, err end + -- headers go until a blank line is found + while line ~= "" do + -- get field-name and value + name, value = socket.skip(2, string.find(line, "^(.-):%s*(.*)")) + if not (name and value) then return nil, "malformed reponse headers" end + name = string.lower(name) + -- get next line (value might be folded) + line, err = sock:receive() + if err then return nil, err end + -- unfold any folded values + while string.find(line, "^%s") do + value = value .. line + line = sock:receive() + if err then return nil, err end + end + -- save pair in table + if headers[name] then headers[name] = headers[name] .. ", " .. value + else headers[name] = value end + end + return headers +end + +----------------------------------------------------------------------------- +-- Extra sources and sinks +----------------------------------------------------------------------------- +socket.sourcet["http-chunked"] = function(sock, headers) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function() + -- get chunk size, skip extention + local line, err = sock:receive() + if err then return nil, err end + local size = base.tonumber(string.gsub(line, ";.*", ""), 16) + if not size then return nil, "invalid chunk size" end + -- was it the last chunk? + if size > 0 then + -- if not, get chunk and skip terminating CRLF + local chunk, err, part = sock:receive(size) + if chunk then sock:receive() end + return chunk, err + else + -- if it was, read trailers into headers table + headers, err = receiveheaders(sock, headers) + if not headers then return nil, err end + end + end + }) +end + +socket.sinkt["http-chunked"] = function(sock) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function(self, chunk, err) + if not chunk then return sock:send("0\r\n\r\n") end + local size = string.format("%X\r\n", string.len(chunk)) + return sock:send(size .. chunk .. "\r\n") + end + }) +end + +----------------------------------------------------------------------------- +-- Low level HTTP API +----------------------------------------------------------------------------- +local metat = { __index = {} } + +function open(host, port, create) + -- create socket with user connect function, or with default + local c = socket.try((create or socket.tcp)()) + local h = base.setmetatable({ c = c }, metat) + -- create finalized try + h.try = socket.newtry(function() h:close() end) + -- set timeout before connecting + h.try(c:settimeout(TIMEOUT)) + h.try(c:connect(host, port or PORT)) + -- here everything worked + return h +end + +function metat.__index:sendrequestline(method, uri) + local reqline = string.format("%s %s HTTP/1.1\r\n", method or "GET", uri) + return self.try(self.c:send(reqline)) +end + +function metat.__index:sendheaders(headers) + local h = "\r\n" + for i, v in base.pairs(headers) do + h = i .. ": " .. v .. "\r\n" .. h + end + self.try(self.c:send(h)) + return 1 +end + +function metat.__index:sendbody(headers, source, step) + source = source or ltn12.source.empty() + step = step or ltn12.pump.step + -- if we don't know the size in advance, send chunked and hope for the best + local mode = "http-chunked" + if headers["content-length"] then mode = "keep-open" end + return self.try(ltn12.pump.all(source, socket.sink(mode, self.c), step)) +end + +function metat.__index:receivestatusline() + local status = self.try(self.c:receive(5)) + -- identify HTTP/0.9 responses, which do not contain a status line + -- this is just a heuristic, but is what the RFC recommends + if status ~= "HTTP/" then return nil, status end + -- otherwise proceed reading a status line + status = self.try(self.c:receive("*l", status)) + local code = socket.skip(2, string.find(status, "HTTP/%d*%.%d* (%d%d%d)")) + return self.try(base.tonumber(code), status) +end + +function metat.__index:receiveheaders() + return self.try(receiveheaders(self.c)) +end + +function metat.__index:receivebody(headers, sink, step) + sink = sink or ltn12.sink.null() + step = step or ltn12.pump.step + local length = base.tonumber(headers["content-length"]) + local t = headers["transfer-encoding"] -- shortcut + local mode = "default" -- connection close + if t and t ~= "identity" then mode = "http-chunked" + elseif base.tonumber(headers["content-length"]) then mode = "by-length" end + return self.try(ltn12.pump.all(socket.source(mode, self.c, length), + sink, step)) +end + +function metat.__index:receive09body(status, sink, step) + local source = ltn12.source.rewind(socket.source("until-closed", self.c)) + source(status) + return self.try(ltn12.pump.all(source, sink, step)) +end + +function metat.__index:close() + return self.c:close() +end + +----------------------------------------------------------------------------- +-- High level HTTP API +----------------------------------------------------------------------------- +local function adjusturi(reqt) + local u = reqt + -- if there is a proxy, we need the full url. otherwise, just a part. + if not reqt.proxy and not PROXY then + u = { + path = socket.try(reqt.path, "invalid path 'nil'"), + params = reqt.params, + query = reqt.query, + fragment = reqt.fragment + } + end + return url.build(u) +end + +local function adjustproxy(reqt) + local proxy = reqt.proxy or PROXY + if proxy then + proxy = url.parse(proxy) + return proxy.host, proxy.port or 3128 + else + return reqt.host, reqt.port + end +end + +local function adjustheaders(reqt) + -- default headers + local lower = { + ["user-agent"] = USERAGENT, + ["host"] = reqt.host, + ["connection"] = "close, TE", + ["te"] = "trailers" + } + -- if we have authentication information, pass it along + if reqt.user and reqt.password then + lower["authorization"] = + "Basic " .. (mime.b64(reqt.user .. ":" .. reqt.password)) + end + -- override with user headers + for i,v in base.pairs(reqt.headers or lower) do + lower[string.lower(i)] = v + end + return lower +end + +-- default url parts +local default = { + host = "", + port = PORT, + path ="/", + scheme = "http" +} + +local function adjustrequest(reqt) + -- parse url if provided + local nreqt = reqt.url and url.parse(reqt.url, default) or {} + -- explicit components override url + for i,v in base.pairs(reqt) do nreqt[i] = v end + if nreqt.port == "" then nreqt.port = 80 end + socket.try(nreqt.host and nreqt.host ~= "", + "invalid host '" .. base.tostring(nreqt.host) .. "'") + -- compute uri if user hasn't overriden + nreqt.uri = reqt.uri or adjusturi(nreqt) + -- ajust host and port if there is a proxy + nreqt.host, nreqt.port = adjustproxy(nreqt) + -- adjust headers in request + nreqt.headers = adjustheaders(nreqt) + return nreqt +end + +local function shouldredirect(reqt, code, headers) + return headers.location and + string.gsub(headers.location, "%s", "") ~= "" and + (reqt.redirect ~= false) and + (code == 301 or code == 302) and + (not reqt.method or reqt.method == "GET" or reqt.method == "HEAD") + and (not reqt.nredirects or reqt.nredirects < 5) +end + +local function shouldreceivebody(reqt, code) + if reqt.method == "HEAD" then return nil end + if code == 204 or code == 304 then return nil end + if code >= 100 and code < 200 then return nil end + return 1 +end + +-- forward declarations +local trequest, tredirect + +function tredirect(reqt, location) + local result, code, headers, status = trequest { + -- the RFC says the redirect URL has to be absolute, but some + -- servers do not respect that + url = url.absolute(reqt.url, location), + source = reqt.source, + sink = reqt.sink, + headers = reqt.headers, + proxy = reqt.proxy, + nredirects = (reqt.nredirects or 0) + 1, + create = reqt.create + } + -- pass location header back as a hint we redirected + headers = headers or {} + headers.location = headers.location or location + return result, code, headers, status +end + +function trequest(reqt) + -- we loop until we get what we want, or + -- until we are sure there is no way to get it + local nreqt = adjustrequest(reqt) + local h = open(nreqt.host, nreqt.port, nreqt.create) + -- send request line and headers + h:sendrequestline(nreqt.method, nreqt.uri) + h:sendheaders(nreqt.headers) + -- if there is a body, send it + if nreqt.source then + h:sendbody(nreqt.headers, nreqt.source, nreqt.step) + end + local code, status = h:receivestatusline() + -- if it is an HTTP/0.9 server, simply get the body and we are done + if not code then + h:receive09body(status, nreqt.sink, nreqt.step) + return 1, 200 + end + local headers + -- ignore any 100-continue messages + while code == 100 do + headers = h:receiveheaders() + code, status = h:receivestatusline() + end + headers = h:receiveheaders() + -- at this point we should have a honest reply from the server + -- we can't redirect if we already used the source, so we report the error + if shouldredirect(nreqt, code, headers) and not nreqt.source then + h:close() + return tredirect(reqt, headers.location) + end + -- here we are finally done + if shouldreceivebody(nreqt, code) then + h:receivebody(headers, nreqt.sink, nreqt.step) + end + h:close() + return 1, code, headers, status +end + +local function srequest(u, b) + local t = {} + local reqt = { + url = u, + sink = ltn12.sink.table(t) + } + if b then + reqt.source = ltn12.source.string(b) + reqt.headers = { + ["content-length"] = string.len(b), + ["content-type"] = "application/x-www-form-urlencoded" + } + reqt.method = "POST" + end + local code, headers, status = socket.skip(1, trequest(reqt)) + return table.concat(t), code, headers, status +end + +request = socket.protect(function(reqt, body) + if base.type(reqt) == "string" then return srequest(reqt, body) + else return trequest(reqt) end +end) diff --git a/lib/support/share/socket/smtp.lua b/lib/support/share/socket/smtp.lua new file mode 100644 index 0000000..8f3cfcf --- /dev/null +++ b/lib/support/share/socket/smtp.lua @@ -0,0 +1,251 @@ +----------------------------------------------------------------------------- +-- SMTP client support for the Lua language. +-- LuaSocket toolkit. +-- Author: Diego Nehab +-- RCS ID: $Id: smtp.lua,v 1.46 2007/03/12 04:08:40 diego Exp $ +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +----------------------------------------------------------------------------- +local base = _G +local coroutine = require("coroutine") +local string = require("string") +local math = require("math") +local os = require("os") +local socket = require("socket") +local tp = require("socket.tp") +local ltn12 = require("ltn12") +local mime = require("mime") +module("socket.smtp") + +----------------------------------------------------------------------------- +-- Program constants +----------------------------------------------------------------------------- +-- timeout for connection +TIMEOUT = 60 +-- default server used to send e-mails +SERVER = "localhost" +-- default port +PORT = 25 +-- domain used in HELO command and default sendmail +-- If we are under a CGI, try to get from environment +DOMAIN = os.getenv("SERVER_NAME") or "localhost" +-- default time zone (means we don't know) +ZONE = "-0000" + +--------------------------------------------------------------------------- +-- Low level SMTP API +----------------------------------------------------------------------------- +local metat = { __index = {} } + +function metat.__index:greet(domain) + self.try(self.tp:check("2..")) + self.try(self.tp:command("EHLO", domain or DOMAIN)) + return socket.skip(1, self.try(self.tp:check("2.."))) +end + +function metat.__index:mail(from) + self.try(self.tp:command("MAIL", "FROM:" .. from)) + return self.try(self.tp:check("2..")) +end + +function metat.__index:rcpt(to) + self.try(self.tp:command("RCPT", "TO:" .. to)) + return self.try(self.tp:check("2..")) +end + +function metat.__index:data(src, step) + self.try(self.tp:command("DATA")) + self.try(self.tp:check("3..")) + self.try(self.tp:source(src, step)) + self.try(self.tp:send("\r\n.\r\n")) + return self.try(self.tp:check("2..")) +end + +function metat.__index:quit() + self.try(self.tp:command("QUIT")) + return self.try(self.tp:check("2..")) +end + +function metat.__index:close() + return self.tp:close() +end + +function metat.__index:login(user, password) + self.try(self.tp:command("AUTH", "LOGIN")) + self.try(self.tp:check("3..")) + self.try(self.tp:command(mime.b64(user))) + self.try(self.tp:check("3..")) + self.try(self.tp:command(mime.b64(password))) + return self.try(self.tp:check("2..")) +end + +function metat.__index:plain(user, password) + local auth = "PLAIN " .. mime.b64("\0" .. user .. "\0" .. password) + self.try(self.tp:command("AUTH", auth)) + return self.try(self.tp:check("2..")) +end + +function metat.__index:auth(user, password, ext) + if not user or not password then return 1 end + if string.find(ext, "AUTH[^\n]+LOGIN") then + return self:login(user, password) + elseif string.find(ext, "AUTH[^\n]+PLAIN") then + return self:plain(user, password) + else + self.try(nil, "authentication not supported") + end +end + +-- send message or throw an exception +function metat.__index:send(mailt) + self:mail(mailt.from) + if base.type(mailt.rcpt) == "table" then + for i,v in base.ipairs(mailt.rcpt) do + self:rcpt(v) + end + else + self:rcpt(mailt.rcpt) + end + self:data(ltn12.source.chain(mailt.source, mime.stuff()), mailt.step) +end + +function open(server, port, create) + local tp = socket.try(tp.connect(server or SERVER, port or PORT, + TIMEOUT, create)) + local s = base.setmetatable({tp = tp}, metat) + -- make sure tp is closed if we get an exception + s.try = socket.newtry(function() + s:close() + end) + return s +end + +-- convert headers to lowercase +local function lower_headers(headers) + local lower = {} + for i,v in base.pairs(headers or lower) do + lower[string.lower(i)] = v + end + return lower +end + +--------------------------------------------------------------------------- +-- Multipart message source +----------------------------------------------------------------------------- +-- returns a hopefully unique mime boundary +local seqno = 0 +local function newboundary() + seqno = seqno + 1 + return string.format('%s%05d==%05u', os.date('%d%m%Y%H%M%S'), + math.random(0, 99999), seqno) +end + +-- send_message forward declaration +local send_message + +-- yield the headers all at once, it's faster +local function send_headers(headers) + local h = "\r\n" + for i,v in base.pairs(headers) do + h = i .. ': ' .. v .. "\r\n" .. h + end + coroutine.yield(h) +end + +-- yield multipart message body from a multipart message table +local function send_multipart(mesgt) + -- make sure we have our boundary and send headers + local bd = newboundary() + local headers = lower_headers(mesgt.headers or {}) + headers['content-type'] = headers['content-type'] or 'multipart/mixed' + headers['content-type'] = headers['content-type'] .. + '; boundary="' .. bd .. '"' + send_headers(headers) + -- send preamble + if mesgt.body.preamble then + coroutine.yield(mesgt.body.preamble) + coroutine.yield("\r\n") + end + -- send each part separated by a boundary + for i, m in base.ipairs(mesgt.body) do + coroutine.yield("\r\n--" .. bd .. "\r\n") + send_message(m) + end + -- send last boundary + coroutine.yield("\r\n--" .. bd .. "--\r\n\r\n") + -- send epilogue + if mesgt.body.epilogue then + coroutine.yield(mesgt.body.epilogue) + coroutine.yield("\r\n") + end +end + +-- yield message body from a source +local function send_source(mesgt) + -- make sure we have a content-type + local headers = lower_headers(mesgt.headers or {}) + headers['content-type'] = headers['content-type'] or + 'text/plain; charset="iso-8859-1"' + send_headers(headers) + -- send body from source + while true do + local chunk, err = mesgt.body() + if err then coroutine.yield(nil, err) + elseif chunk then coroutine.yield(chunk) + else break end + end +end + +-- yield message body from a string +local function send_string(mesgt) + -- make sure we have a content-type + local headers = lower_headers(mesgt.headers or {}) + headers['content-type'] = headers['content-type'] or + 'text/plain; charset="iso-8859-1"' + send_headers(headers) + -- send body from string + coroutine.yield(mesgt.body) +end + +-- message source +function send_message(mesgt) + if base.type(mesgt.body) == "table" then send_multipart(mesgt) + elseif base.type(mesgt.body) == "function" then send_source(mesgt) + else send_string(mesgt) end +end + +-- set defaul headers +local function adjust_headers(mesgt) + local lower = lower_headers(mesgt.headers) + lower["date"] = lower["date"] or + os.date("!%a, %d %b %Y %H:%M:%S ") .. (mesgt.zone or ZONE) + lower["x-mailer"] = lower["x-mailer"] or socket._VERSION + -- this can't be overriden + lower["mime-version"] = "1.0" + return lower +end + +function message(mesgt) + mesgt.headers = adjust_headers(mesgt) + -- create and return message source + local co = coroutine.create(function() send_message(mesgt) end) + return function() + local ret, a, b = coroutine.resume(co) + if ret then return a, b + else return nil, a end + end +end + +--------------------------------------------------------------------------- +-- High level SMTP API +----------------------------------------------------------------------------- +send = socket.protect(function(mailt) + local s = open(mailt.server, mailt.port, mailt.create) + local ext = s:greet(mailt.domain) + s:auth(mailt.user, mailt.password, ext) + s:send(mailt) + s:quit() + return s:close() +end) diff --git a/lib/support/share/socket/tp.lua b/lib/support/share/socket/tp.lua new file mode 100644 index 0000000..0683869 --- /dev/null +++ b/lib/support/share/socket/tp.lua @@ -0,0 +1,123 @@ +----------------------------------------------------------------------------- +-- Unified SMTP/FTP subsystem +-- LuaSocket toolkit. +-- Author: Diego Nehab +-- RCS ID: $Id: tp.lua,v 1.22 2006/03/14 09:04:15 diego Exp $ +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +----------------------------------------------------------------------------- +local base = _G +local string = require("string") +local socket = require("socket") +local ltn12 = require("ltn12") +module("socket.tp") + +----------------------------------------------------------------------------- +-- Program constants +----------------------------------------------------------------------------- +TIMEOUT = 60 + +----------------------------------------------------------------------------- +-- Implementation +----------------------------------------------------------------------------- +-- gets server reply (works for SMTP and FTP) +local function get_reply(c) + local code, current, sep + local line, err = c:receive() + local reply = line + if err then return nil, err end + code, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)")) + if not code then return nil, "invalid server reply" end + if sep == "-" then -- reply is multiline + repeat + line, err = c:receive() + if err then return nil, err end + current, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)")) + reply = reply .. "\n" .. line + -- reply ends with same code + until code == current and sep == " " + end + return code, reply +end + +-- metatable for sock object +local metat = { __index = {} } + +function metat.__index:check(ok) + local code, reply = get_reply(self.c) + if not code then return nil, reply end + if base.type(ok) ~= "function" then + if base.type(ok) == "table" then + for i, v in base.ipairs(ok) do + if string.find(code, v) then + return base.tonumber(code), reply + end + end + return nil, reply + else + if string.find(code, ok) then return base.tonumber(code), reply + else return nil, reply end + end + else return ok(base.tonumber(code), reply) end +end + +function metat.__index:command(cmd, arg) + if arg then + return self.c:send(cmd .. " " .. arg.. "\r\n") + else + return self.c:send(cmd .. "\r\n") + end +end + +function metat.__index:sink(snk, pat) + local chunk, err = c:receive(pat) + return snk(chunk, err) +end + +function metat.__index:send(data) + return self.c:send(data) +end + +function metat.__index:receive(pat) + return self.c:receive(pat) +end + +function metat.__index:getfd() + return self.c:getfd() +end + +function metat.__index:dirty() + return self.c:dirty() +end + +function metat.__index:getcontrol() + return self.c +end + +function metat.__index:source(source, step) + local sink = socket.sink("keep-open", self.c) + local ret, err = ltn12.pump.all(source, sink, step or ltn12.pump.step) + return ret, err +end + +-- closes the underlying c +function metat.__index:close() + self.c:close() + return 1 +end + +-- connect with server and return c object +function connect(host, port, timeout, create) + local c, e = (create or socket.tcp)() + if not c then return nil, e end + c:settimeout(timeout or TIMEOUT) + local r, e = c:connect(host, port) + if not r then + c:close() + return nil, e + end + return base.setmetatable({c = c}, metat) +end + diff --git a/lib/support/share/socket/url.lua b/lib/support/share/socket/url.lua new file mode 100644 index 0000000..0e31d8a --- /dev/null +++ b/lib/support/share/socket/url.lua @@ -0,0 +1,297 @@ +----------------------------------------------------------------------------- +-- URI parsing, composition and relative URL resolution +-- LuaSocket toolkit. +-- Author: Diego Nehab +-- RCS ID: $Id: url.lua,v 1.38 2006/04/03 04:45:42 diego Exp $ +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module +----------------------------------------------------------------------------- +local string = require("string") +local base = _G +local table = require("table") +module("socket.url") + +----------------------------------------------------------------------------- +-- Module version +----------------------------------------------------------------------------- +_VERSION = "URL 1.0.1" + +----------------------------------------------------------------------------- +-- Encodes a string into its escaped hexadecimal representation +-- Input +-- s: binary string to be encoded +-- Returns +-- escaped representation of string binary +----------------------------------------------------------------------------- +function escape(s) + return string.gsub(s, "([^A-Za-z0-9_])", function(c) + return string.format("%%%02x", string.byte(c)) + end) +end + +----------------------------------------------------------------------------- +-- Protects a path segment, to prevent it from interfering with the +-- url parsing. +-- Input +-- s: binary string to be encoded +-- Returns +-- escaped representation of string binary +----------------------------------------------------------------------------- +local function make_set(t) + local s = {} + for i,v in base.ipairs(t) do + s[t[i]] = 1 + end + return s +end + +-- these are allowed withing a path segment, along with alphanum +-- other characters must be escaped +local segment_set = make_set { + "-", "_", ".", "!", "~", "*", "'", "(", + ")", ":", "@", "&", "=", "+", "$", ",", +} + +local function protect_segment(s) + return string.gsub(s, "([^A-Za-z0-9_])", function (c) + if segment_set[c] then return c + else return string.format("%%%02x", string.byte(c)) end + end) +end + +----------------------------------------------------------------------------- +-- Encodes a string into its escaped hexadecimal representation +-- Input +-- s: binary string to be encoded +-- Returns +-- escaped representation of string binary +----------------------------------------------------------------------------- +function unescape(s) + return string.gsub(s, "%%(%x%x)", function(hex) + return string.char(base.tonumber(hex, 16)) + end) +end + +----------------------------------------------------------------------------- +-- Builds a path from a base path and a relative path +-- Input +-- base_path +-- relative_path +-- Returns +-- corresponding absolute path +----------------------------------------------------------------------------- +local function absolute_path(base_path, relative_path) + if string.sub(relative_path, 1, 1) == "/" then return relative_path end + local path = string.gsub(base_path, "[^/]*$", "") + path = path .. relative_path + path = string.gsub(path, "([^/]*%./)", function (s) + if s ~= "./" then return s else return "" end + end) + path = string.gsub(path, "/%.$", "/") + local reduced + while reduced ~= path do + reduced = path + path = string.gsub(reduced, "([^/]*/%.%./)", function (s) + if s ~= "../../" then return "" else return s end + end) + end + path = string.gsub(reduced, "([^/]*/%.%.)$", function (s) + if s ~= "../.." then return "" else return s end + end) + return path +end + +----------------------------------------------------------------------------- +-- Parses a url and returns a table with all its parts according to RFC 2396 +-- The following grammar describes the names given to the URL parts +-- <url> ::= <scheme>://<authority>/<path>;<params>?<query>#<fragment> +-- <authority> ::= <userinfo>@<host>:<port> +-- <userinfo> ::= <user>[:<password>] +-- <path> :: = {<segment>/}<segment> +-- Input +-- url: uniform resource locator of request +-- default: table with default values for each field +-- Returns +-- table with the following fields, where RFC naming conventions have +-- been preserved: +-- scheme, authority, userinfo, user, password, host, port, +-- path, params, query, fragment +-- Obs: +-- the leading '/' in {/<path>} is considered part of <path> +----------------------------------------------------------------------------- +function parse(url, default) + -- initialize default parameters + local parsed = {} + for i,v in base.pairs(default or parsed) do parsed[i] = v end + -- empty url is parsed to nil + if not url or url == "" then return nil, "invalid url" end + -- remove whitespace + -- url = string.gsub(url, "%s", "") + -- get fragment + url = string.gsub(url, "#(.*)$", function(f) + parsed.fragment = f + return "" + end) + -- get scheme + url = string.gsub(url, "^([%w][%w%+%-%.]*)%:", + function(s) parsed.scheme = s; return "" end) + -- get authority + url = string.gsub(url, "^//([^/]*)", function(n) + parsed.authority = n + return "" + end) + -- get query stringing + url = string.gsub(url, "%?(.*)", function(q) + parsed.query = q + return "" + end) + -- get params + url = string.gsub(url, "%;(.*)", function(p) + parsed.params = p + return "" + end) + -- path is whatever was left + if url ~= "" then parsed.path = url end + local authority = parsed.authority + if not authority then return parsed end + authority = string.gsub(authority,"^([^@]*)@", + function(u) parsed.userinfo = u; return "" end) + authority = string.gsub(authority, ":([^:]*)$", + function(p) parsed.port = p; return "" end) + if authority ~= "" then parsed.host = authority end + local userinfo = parsed.userinfo + if not userinfo then return parsed end + userinfo = string.gsub(userinfo, ":([^:]*)$", + function(p) parsed.password = p; return "" end) + parsed.user = userinfo + return parsed +end + +----------------------------------------------------------------------------- +-- Rebuilds a parsed URL from its components. +-- Components are protected if any reserved or unallowed characters are found +-- Input +-- parsed: parsed URL, as returned by parse +-- Returns +-- a stringing with the corresponding URL +----------------------------------------------------------------------------- +function build(parsed) + local ppath = parse_path(parsed.path or "") + local url = build_path(ppath) + if parsed.params then url = url .. ";" .. parsed.params end + if parsed.query then url = url .. "?" .. parsed.query end + local authority = parsed.authority + if parsed.host then + authority = parsed.host + if parsed.port then authority = authority .. ":" .. parsed.port end + local userinfo = parsed.userinfo + if parsed.user then + userinfo = parsed.user + if parsed.password then + userinfo = userinfo .. ":" .. parsed.password + end + end + if userinfo then authority = userinfo .. "@" .. authority end + end + if authority then url = "//" .. authority .. url end + if parsed.scheme then url = parsed.scheme .. ":" .. url end + if parsed.fragment then url = url .. "#" .. parsed.fragment end + -- url = string.gsub(url, "%s", "") + return url +end + +----------------------------------------------------------------------------- +-- Builds a absolute URL from a base and a relative URL according to RFC 2396 +-- Input +-- base_url +-- relative_url +-- Returns +-- corresponding absolute url +----------------------------------------------------------------------------- +function absolute(base_url, relative_url) + if base.type(base_url) == "table" then + base_parsed = base_url + base_url = build(base_parsed) + else + base_parsed = parse(base_url) + end + local relative_parsed = parse(relative_url) + if not base_parsed then return relative_url + elseif not relative_parsed then return base_url + elseif relative_parsed.scheme then return relative_url + else + relative_parsed.scheme = base_parsed.scheme + if not relative_parsed.authority then + relative_parsed.authority = base_parsed.authority + if not relative_parsed.path then + relative_parsed.path = base_parsed.path + if not relative_parsed.params then + relative_parsed.params = base_parsed.params + if not relative_parsed.query then + relative_parsed.query = base_parsed.query + end + end + else + relative_parsed.path = absolute_path(base_parsed.path or "", + relative_parsed.path) + end + end + return build(relative_parsed) + end +end + +----------------------------------------------------------------------------- +-- Breaks a path into its segments, unescaping the segments +-- Input +-- path +-- Returns +-- segment: a table with one entry per segment +----------------------------------------------------------------------------- +function parse_path(path) + local parsed = {} + path = path or "" + --path = string.gsub(path, "%s", "") + string.gsub(path, "([^/]+)", function (s) table.insert(parsed, s) end) + for i = 1, table.getn(parsed) do + parsed[i] = unescape(parsed[i]) + end + if string.sub(path, 1, 1) == "/" then parsed.is_absolute = 1 end + if string.sub(path, -1, -1) == "/" then parsed.is_directory = 1 end + return parsed +end + +----------------------------------------------------------------------------- +-- Builds a path component from its segments, escaping protected characters. +-- Input +-- parsed: path segments +-- unsafe: if true, segments are not protected before path is built +-- Returns +-- path: corresponding path stringing +----------------------------------------------------------------------------- +function build_path(parsed, unsafe) + local path = "" + local n = table.getn(parsed) + if unsafe then + for i = 1, n-1 do + path = path .. parsed[i] + path = path .. "/" + end + if n > 0 then + path = path .. parsed[n] + if parsed.is_directory then path = path .. "/" end + end + else + for i = 1, n-1 do + path = path .. protect_segment(parsed[i]) + path = path .. "/" + end + if n > 0 then + path = path .. protect_segment(parsed[n]) + if parsed.is_directory then path = path .. "/" end + end + end + if parsed.is_absolute then path = "/" .. path end + return path +end diff --git a/lib/support/socket.lua b/lib/support/socket.lua new file mode 100644 index 0000000..211adcd --- /dev/null +++ b/lib/support/socket.lua @@ -0,0 +1,133 @@ +----------------------------------------------------------------------------- +-- LuaSocket helper module +-- Author: Diego Nehab +-- RCS ID: $Id: socket.lua,v 1.22 2005/11/22 08:33:29 diego Exp $ +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +----------------------------------------------------------------------------- +local base = _G +local string = require("string") +local math = require("math") +local socket = require("socket.core") +module("socket") + +----------------------------------------------------------------------------- +-- Exported auxiliar functions +----------------------------------------------------------------------------- +function connect(address, port, laddress, lport) + local sock, err = socket.tcp() + if not sock then return nil, err end + if laddress then + local res, err = sock:bind(laddress, lport, -1) + if not res then return nil, err end + end + local res, err = sock:connect(address, port) + if not res then return nil, err end + return sock +end + +function bind(host, port, backlog) + local sock, err = socket.tcp() + if not sock then return nil, err end + sock:setoption("reuseaddr", true) + local res, err = sock:bind(host, port) + if not res then return nil, err end + res, err = sock:listen(backlog) + if not res then return nil, err end + return sock +end + +try = newtry() + +function choose(table) + return function(name, opt1, opt2) + if base.type(name) ~= "string" then + name, opt1, opt2 = "default", name, opt1 + end + local f = table[name or "nil"] + if not f then base.error("unknown key (".. base.tostring(name) ..")", 3) + else return f(opt1, opt2) end + end +end + +----------------------------------------------------------------------------- +-- Socket sources and sinks, conforming to LTN12 +----------------------------------------------------------------------------- +-- create namespaces inside LuaSocket namespace +sourcet = {} +sinkt = {} + +BLOCKSIZE = 2048 + +sinkt["close-when-done"] = function(sock) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function(self, chunk, err) + if not chunk then + sock:close() + return 1 + else return sock:send(chunk) end + end + }) +end + +sinkt["keep-open"] = function(sock) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function(self, chunk, err) + if chunk then return sock:send(chunk) + else return 1 end + end + }) +end + +sinkt["default"] = sinkt["keep-open"] + +sink = choose(sinkt) + +sourcet["by-length"] = function(sock, length) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function() + if length <= 0 then return nil end + local size = math.min(socket.BLOCKSIZE, length) + local chunk, err = sock:receive(size) + if err then return nil, err end + length = length - string.len(chunk) + return chunk + end + }) +end + +sourcet["until-closed"] = function(sock) + local done + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function() + if done then return nil end + local chunk, err, partial = sock:receive(socket.BLOCKSIZE) + if not err then return chunk + elseif err == "closed" then + sock:close() + done = 1 + return partial + else return nil, err end + end + }) +end + + +sourcet["default"] = sourcet["until-closed"] + +source = choose(sourcet) + diff --git a/lib/support/socket/core.so b/lib/support/socket/core.so Binary files differnew file mode 100755 index 0000000..ddfc8c3 --- /dev/null +++ b/lib/support/socket/core.so |