mdecourse / vecp2018

車輛工程系大一計算機程式 (課程已經結束)
GNU Affero General Public License v3.0
0 stars 0 forks source link

參考用 Lua 程式碼 #8

Closed mdecourse closed 6 years ago

mdecourse commented 6 years ago

Github 倉儲中的 Lua 程式碼

取自 https://gist.github.com/daurnimator/9dd8168ab14846e880e4b50546b39110#file-fengari-vue-lua

-- Load Vue library
package.loadlib("https://cdn.jsdelivr.net/npm/vue@2.5.13/dist/vue.js", "*")
-- Get Object helper
local Object = dofile("https://gist.githubusercontent.com/daurnimator/5a7fa933e96e14333962093322e0ff95/raw/8c6968be0111c7becc485a692162ad100e87d9c7/Object.lua").Object

local myapp = js.new(js.global.Vue, Object{
    el = "#foo";
    template = [[
        <div id="foo">{{message}}</div>
    ]];
    data = Object{
        message = "weeee";
    }
})
myapp.message = "something else"

取自 https://gist.github.com/daurnimator/5a7fa933e96e14333962093322e0ff95#file-object-lua

local js = require "js"

-- Helper to copy lua table to a new JavaScript Object
-- e.g. Object{mykey="myvalue"}
local function Object(t)
    local o = js.new(js.global.Object)
    for k, v in pairs(t) do
        assert(type(k) == "string" or js.typeof(k) == "symbol", "JavaScript only has string and symbol keys")
        o[k] = v
    end
    return o
end

return {
    Object = Object;
}

取自 https://gist.github.com/daurnimator/f5e872f0a988f7bbd025#file-exampleseval-in-lua

local http_request = require "http.request"
local http_util = require "http.util"

local request  = http_request.new_from_uri("https://eval.in/")

request.headers:upsert(":method", "POST")
request.headers:upsert("content-type", "application/x-www-form-urlencoded")
request.follow_redirects = false
request:set_body(http_util.dict_to_query{utf8 = "λ", code = 'print("hello")', execute = "on", lang = "ruby/mri-2.2", input = ""})

local headers, stream = request:go()
stream:shutdown()
print(headers:get"location")

取自 https://gist.github.com/daurnimator/f1c7965b47a5658b88300403645541aa#file-tarantool_cqueues-lua

-- This code monkey patches cqueues primitives to allow for transparent use of cqueues inside of tarantool

local cqueues = require "cqueues"
local socket = require "socket" -- https://tarantool.org/en/doc/1.7/reference/reference_lua/socket.html (not luasocket)

local old_step; old_step = cqueues.interpose("step", function(self, timeout)
    if cqueues.running() then
        return old_step(self, timeout)
    else
        local t = self:timeout() or math.huge
        if timeout then
            t = math.min(t, timeout)
        end
        if t > 0.0 then
            socket.iowait(self:pollfd(), self:events(), t)
        end
        return old_step(self, 0.0)
    end
end)

取自 https://gist.github.com/daurnimator/f1c7965b47a5658b88300403645541aa#file-tarantool_http_server-lua

#!/usr/bin/env tarantool

require "tarantool_cqueues"

local fiber = require "fiber"
local http_headers = require "http.headers"
local http_server = require "http.server"
local http_util = require "http.util"

local function reply(myserver, stream) -- luacheck: ignore 212
    -- Read in headers
    local req_headers = assert(stream:get_headers())
    local req_method = req_headers:get ":method"

    -- Log request to stdout
    assert(io.stdout:write(string.format('[%s] "%s %s HTTP/%g"  "%s" "%s"\n',
        os.date("%d/%b/%Y:%H:%M:%S %z"),
        req_method or "",
        req_headers:get(":path") or "",
        stream.connection.version,
        req_headers:get("referer") or "-",
        req_headers:get("user-agent") or "-"
    )))

    -- Build response headers
    local res_headers = http_headers.new()
    res_headers:append(":status", "200")
    res_headers:append("content-type", "text/plain")
    -- Send headers to client; end the stream immediately if this was a HEAD request
    assert(stream:write_headers(res_headers, req_method == "HEAD"))
    if req_method ~= "HEAD" then
        -- Send body, ending the stream
        assert(stream:write_chunk("Hello world!\n", true))
    end
end

local myserver = assert(http_server.listen {
    host = "127.0.0.1";
    port = 8000;
    onstream = reply;
    onerror = function(self, context, op, err)
        local msg = op .. " on " .. tostring(context) .. " failed"
        if err then
                msg = msg .. ": " .. tostring(err)
        end
        assert(io.stderr:write(msg, "\n"))
    end;
})
-- Override :add_stream to call onstream in a new fiber (instead of new cqueues coroutine)
function myserver:add_stream(stream)
    fiber.create(function()
        fiber.yield() -- want to be called from main loop; not from :add_stream callee
        local ok, err = http_util.yieldable_pcall(self.onstream, self, stream)
        stream:shutdown()
        if not ok then
            self:onerror()(self, stream, "onstream", err)
        end
    end)
end

-- Run server in its own tarantool fibre
fiber.create(function()
    assert(myserver:loop())
end)

-- Start another fibre that just prints+sleeps in a loop to show off non-blocking-ness of http server
fiber.create(function()
    for i=1, 100 do
        print("HELLO ", i)
        fiber.sleep(0.1)
    end
end)

取自 https://gist.github.com/daurnimator/f1c7965b47a5658b88300403645541aa#file-tarantool_websocket-lua

#!/usr/bin/env tarantool

require "tarantool_cqueues"

local fiber = require "fiber"
package.loaded["http.client"] = nil -- tarantool has a namespace clash
local websocket = require "http.websocket"

fiber.create(function()
    local ws = websocket.new_from_uri("wss://ws-feed.gdax.com")
    assert(ws:connect())
    assert(ws:send([[{"type": "subscribe", "product_id": "BTC-USD"}]]))
    for _=1, 5 do
        local data = assert(ws:receive())
        print(data)
    end
    assert(ws:close())
end)

-- Start another fibre that just prints+sleeps in a loop to show off non-blocking-ness
fiber.create(function()
    for i=1, 100 do
        print("HELLO ", i)
        fiber.sleep(0.1)
    end
end)

取自 https://gist.github.com/daurnimator/72ff05933903af612bebc40aa145d519#file-dom-create-lua

local js = require "js"
local window = js.global
local document = window.document

local function setElement(elem, props)
    for k, v in pairs(props) do
        if type(k) == "number" then
            -- skip
        elseif type(v) == "table" then
            local to = elem[k]
            for kk, vv in pairs(v) do
                to[kk] = vv
            end
        else
            elem[k] = v
        end
    end
    for _, v in ipairs(props) do
        if type(v) == "string" then
            v = document:createTextNode(v)
        end
        elem:appendChild(v)
    end
    return elem
end
local function createElement(name, props)
    return setElement(document:createElement(name), props)
end
local dom = setmetatable({}, {
    __index = function(t, k)
        local v = function(props)
            return setElement(document:createElement(k), props)
        end
        t[k] = v
        return v
    end;
})

setElement(document.body, {
    dom.ul {
        dom.li{style={color="red"}, "hi"};
        dom.li{style={color="green"}, "world"};
    };
})

取自 https://gist.github.com/daurnimator/c18b3732f452e58097c68a04f0dc2ac1#file-htmlgen-lua

local document = js.global.document;

local extra = {}
function extra:append(x)
    self.__wrapped__:append(x)
    return self
end

local function wrap(e)
    return setmetatable({
        __wrapped__ = e;
    }, {
        __index = function(e, k)
            return extra[k] or e.__wrapped__[k]
        end;
    })
end

local function create(tag, attr)
    local e = document:createElement(tag)
    if attr then
        for k, v in pairs(attr) do
            e:setAttribute(k, v)
        end
    end
    return wrap(e)
end

local _ENV = setmetatable({}, {
    __index=function(_, k)
        return _G[k] or function(a) return create(k, a) end
    end;
})

print(div{id="foo"}:append("a").outerHTML)

取自 https://gist.github.com/daurnimator/45839b0d6bd4a94c58d2d942c04b6789#file-searchpath-lua

local DIRSEP = package.config:sub(1, 1)

local function is_readable(filename)
        local fd = io.open(filename, "r")
        if fd then
                fd:close()
                return true
        else
                return false
        end
end

function searchpath(name, path, sep, rep)
        assert(type(name) == "string")
        assert(type(path) == "string")
        if sep == nil then
                sep = "%."
        else
                assert(type(sep) == "string")
                sep = sep:gsub("[%%%^%$%(%)%.%[%]%*%+%-%?]", "%%%0") -- need to escape sep so it doesn't get interpreted as a pattern later
        end
        if rep == nil then
                rep = DIRSEP
        else
                assert(type(rep) == "string")
        end
        if #sep > 0 then
                name = name:gsub(sep, rep)
        end
        local msg = ""
        local repl_name = name:gsub("%%", "%%%%")
        for template in path:gmatch("[^;]+") do -- PATH_SEP
                local filename = template:gsub("%?", repl_name) -- PATH_MARK
                if is_readable(filename) then
                        return filename
                end
                msg = msg .. "\n\tno file '" .. filename .. "'"
        end
        return nil, msg
end

取自 https://gist.github.com/daurnimator/c712a77e312b4ad32f01d3cd412e21f9#file-awesome-cq-lua

local cqueues = require "cqueues"
local cq = cqueues.new()
_G.cq = cq

-- Until https://github.com/pavouk/lgi/issues/111 is fixed we can only use a timer
local cqtimer = gears.timer{timeout=0.01}
cqtimer:connect_signal("timeout", function()
        local ok, err, errno, thd = cq:step(0)
        if not ok then
                naughty.notify({ preset = naughty.config.presets.critical,
                                                 title = "Oops, an error happened!",
                                                 text = debug.traceback(thd, err) })
        end
end)
cqtimer:start()

取自 https://gist.github.com/daurnimator/be276c5d32329e2a9250f4aabeea48a8#file-gen_table-lua

local IDNA_map = {}
local highest
local version
for line in io.lines("IdnaMappingTable.txt") do
    local rf, rb, what = line:match("^(%x+)%.%.(%x+) *; *([^#]*)")
    if rf then
        rf = tonumber(rf, 16)
        rb = tonumber(rb, 16)
    else
        local c
        c, what = line:match("^(%x+) *; *([^#]*)")
        if c then
            c = tonumber(c, 16)
            rf = c
            rb = c
        else
            local v = line:match("^# IdnaMappingTable%-(%d+%.%d+%.%d+)")
            if v then
                version = '"' .. v .. '"'
            end
        end
    end

    if what then
        local map
        what, map = what:match("^([%w_]+) *;?([%x ]*)")
        if what == "disallowed" then
        elseif what == "valid" or what == "disallowed_STD3_valid" then
            for i=rf,rb do
                IDNA_map[i] = string.format("%q", utf8.char(i)) -- "true"
                if what == "disallowed_STD3_valid" then
                    IDNA_map[i] = "a and " .. IDNA_map[i] .. " or nil"
                end
                highest = i
            end
        elseif what == "mapped" or what == "disallowed_STD3_mapped" or what == "deviation" then
            local t = {}
            for cp in map:gmatch("%x+") do
                table.insert(t, tonumber(cp, 16))
            end
            local s = utf8.char(table.unpack(t))
            for i=rf,rb do
                IDNA_map[i] = string.format("%q", s)
                if what == "disallowed_STD3_mapped" then
                    IDNA_map[i] = "a and " .. IDNA_map[i] .. " or nil"
                end
                highest = i
            end
        elseif what == "ignored" then
            for i=rf,rb do
                IDNA_map[i] = '""'
                highest = i
            end
        else
            error("Unknown status value: " .. what)
        end
    end
end

print("return {\n\tunicode_version = " .. version .. ";\n\tget_map = function(a)\n\tlocal t = {")
local optimization_point = 0x2CEA1
for i=1, optimization_point, 16 do
    local had_one = false
    for c=i, math.min(optimization_point, i+15) do
        local v = IDNA_map[c]
        if v then
            io.write(v, ";")
            if #v > 10 then
                io.write("\n")
                had_one = false
            else
                had_one = true
            end
        else
            had_one = true
            io.write("nil;")
        end
    end
    if had_one then
        io.write("\n")
    end
end
print("}")
for c=optimization_point+1, highest do
    local v = IDNA_map[c]
    if v then
        print(string.format("t[0x%x]=%s", c, v))
    end
end
print("\treturn t\n\tend;\n}")

取自 https://gist.github.com/daurnimator/192dc5b210718dd129cfc1e5986df97b#file-echo_server-lua

local port = arg[1] or "8000"

local nice_server = require "nice_server"

local timeout = 2

local function reply(resp)
    local req_body = assert(resp.stream:get_body_as_string(timeout))
    local req_body_type = resp.request_headers:get "content-type"
    resp.headers:upsert(":status", "200")
    resp.headers:append("content-type", req_body_type or "text/plain")
    resp:set_body(req_body)
end

assert(nice_server.new {
    host = "localhost";
    port = port;
    reply = reply;
}:loop())

取自 https://gist.github.com/daurnimator/192dc5b210718dd129cfc1e5986df97b#file-nice_server-lua

local cqueues = require "cqueues"
local cc = require "cqueues.condition"
local ce = require "cqueues.errno"
local new_headers = require "http.headers".new
local server = require "http.server"
local version = require "http.version"
local http_util = require "http.util"
local zlib = require "http.zlib"

local default_server = string.format("%s/%s", version.name, version.version)

local error_text = [[
<html>
<head>
<title>503 Internal Server Error</title>
</head>
<body>
An internal server error occured.
</body>
</html>
]]

local response_methods = {}
local response_mt = {
    __index = response_methods;
    __name = nil;
}

local function new_response(request_headers, stream)
    local headers = new_headers();
    -- Give some defaults
    headers:append(":status", "503")
    headers:append("server", default_server)
    headers:append("date", http_util.imf_date())
    return setmetatable({
        request_headers = request_headers;
        stream = stream;
        -- Record peername upfront, as the client might disconnect before request is completed
        peername = select(2, stream:peername());

        headers = headers;
        body = nil;
    }, response_mt)
end

function response_methods:combined_log()
    -- Log in "Combined Log Format"
    -- https://httpd.apache.org/docs/2.2/logs.html#combined
    return string.format('%s - - [%s] "%s %s HTTP/%g" %s %d "%s" "%s"',
        self.peername or "-",
        os.date("%d/%b/%Y:%H:%M:%S %z"),
        self.request_headers:get(":method") or "",
        self.request_headers:get(":path") or "",
        self.stream.connection.version,
        self.headers:get(":status") or "",
        self.stream.stats_sent,
        self.request_headers:get("referer") or "-",
        self.request_headers:get("user-agent") or "-")
end

function response_methods:set_body(body)
    self.body = body
    local length
    if type(self.body) == "string" then
        length = #body
    end
    if length then
        self.headers:upsert("content-length", string.format("%d", #body))
    end
end

function response_methods:set_503()
    local headers = new_headers()
    headers:append(":status", "503")
    headers:append("server", default_server)
    headers:append("date", http_util.imf_date())
    self.headers = headers
    headers:append("content-type", "text/html")
    self:set_body(error_text)
end

function response_methods:enable_compression()
    if self.headers:has("content-encoding") then
        return false
    end
    local deflater = zlib.deflate()
    local new_body = deflater(self.body, true)
    self.headers:append("content-encoding", "gzip")
    self.body = new_body
    return true
end

local function default_onerror(...)
    io.stderr:write(string.format(...), "\n\n")
end
local function default_log(response)
    io.stderr:write(response:combined_log(), "\n")
end

local function new(options)
    local reply = assert(options.reply)
    local onerror = options.onerror or default_onerror
    local log = options.log or default_log
    local myserver = server.listen(options)

    local main_cq = cqueues.new()
    main_cq:wrap(function()
        local clients_cq = cqueues.new()
        -- create a thread that waits forever so :loop doesn't return
        local cond = cc.new()
        clients_cq:wrap(function()
            cond:wait()
        end)
        cqueues.running():wrap(function()
            while true do
                local _, err, _, thd = clients_cq:loop()
                if thd == nil then -- non-thread specific error; something is terribly wrong
                    error(err)
                end
                onerror("client thread error: %s", debug.traceback(thd, err))
            end
        end)
        assert(myserver:run(function(stream)
            local req_headers, err, errno = stream:get_headers()
            if req_headers == nil then
                -- connection hit EOF before headers arrived
                stream:shutdown()
                if err ~= ce.EPIPE and errno ~= ce.ECONNRESET then
                    onerror("header error: %s", tostring(err))
                end
                return
            end

            local resp = new_response(req_headers, stream)

            local ok, err2 = pcall(reply, resp) -- only works in 5.2+
            if stream.state ~= "closed" and stream.state ~= "half closed (local)" then
                if not ok then
                    resp:set_503()
                end
                local send_body = resp.body and req_headers:get ":method" ~= "HEAD"
                stream:write_headers(resp.headers, not send_body)
                if send_body then
                    stream:write_chunk(resp.body, true)
                end
            end
            stream:shutdown()
            if not ok then
                onerror("stream error: %s", tostring(err2))
            end
            log(resp)
        end, clients_cq))
    end)
    return main_cq
end

return {
    new = new;
}

取自 https://gist.github.com/daurnimator/95e9137d617689444f396b6518fbfb7c#file-brightness-lua

local awful = require "awful"
local naughty = require "naughty"

local function get()
    local fd = assert(io.popen("xbacklight -get"))
    local n = assert(fd:read("*n"))
    fd:close()
    return n
end
local last
local function set(n)
    n = math.floor(math.min(100, math.max(0, n)))
    if os.execute(string.format("xbacklight -time %d -set %d", 0, n)) then
        last = naughty.notify({
            text = string.format("brightness is now %d%%", n);
            replaces_id = last and last.id or nil;
        })
    end
end
root.keys(awful.util.table.join(root.keys(),
    awful.key({}, "XF86MonBrightnessDown", function() set(get()-6) end),
    awful.key({}, "XF86MonBrightnessUp",   function() set(get()+6) end)
))

取自 https://gist.github.com/daurnimator/95e9137d617689444f396b6518fbfb7c#file-multimedia-lua

local awful = require "awful"

local function pianobar(cmd)
    local pianobar_fifo = assert(io.open(os.getenv"HOME" .. "/.config/pianobar/ctl", "w"))
    pianobar_fifo:setvbuf("no")
    assert(pianobar_fifo:write(cmd))
    pianobar_fifo:close()
end

root.keys(awful.util.table.join(root.keys(),
    awful.key({}, "XF86AudioMute",        function() os.execute("ponymix toggle") end),
    awful.key({}, "XF86AudioLowerVolume", function() os.execute("ponymix decrease 5") end),
    awful.key({}, "XF86AudioRaiseVolume", function() os.execute("ponymix increase 5") end),
    --awful.key({}, "XF86AudioPrev",        function() pianobar("p") end),
    awful.key({}, "XF86AudioPlay",        function()
        if os.execute("pgrep pianobar") then
            pianobar("p")
        else
            awful.spawn("urxvt -e pianobar")
        end
    end),
    awful.key({}, "XF86AudioNext",        function() pianobar("n") end)
))

取自 https://gist.github.com/daurnimator/95e9137d617689444f396b6518fbfb7c#file-rc-lua

-- Standard awesome library
local gears = require("gears")
local awful = require("awful")
require("awful.autofocus")
-- Widget and layout library
local wibox = require("wibox")
-- Theme handling library
local beautiful = require("beautiful")
-- Notification library
local naughty = require("naughty")
local menubar = require("menubar")
local hotkeys_popup = require("awful.hotkeys_popup").widget

local cqueues = require "cqueues"
local cq = cqueues.new()
_G.cq = cq
local cqtimer = gears.timer{timeout=0.01}
cqtimer:connect_signal("timeout", function()
    local ok, err, errno, thd = cq:step(0)
    if not ok then
        naughty.notify({ preset = naughty.config.presets.critical,
                         title = "Oops, an error happened!",
                         text = debug.traceback(thd, err) })
    end
end)
cqtimer:start()

-- {{{ Error handling
-- Check if awesome encountered an error during startup and fell back to
-- another config (This code will only ever execute for the fallback config)
if awesome.startup_errors then
    naughty.notify {
        preset = naughty.config.presets.critical;
        title = "Oops, there were errors during startup!";
        text = awesome.startup_errors;
    }
end

-- Handle runtime errors after startup
do
    local in_error = false
    awesome.connect_signal("debug::error", function (err)
        -- Make sure we don't go into an endless error loop
        if in_error then return end
        in_error = true

        naughty.notify({ preset = naughty.config.presets.critical,
                         title = "Oops, an error happened!",
                         text = debug.traceback(err, 2) })
        in_error = false
    end)
end
-- }}}

function awful.util.get_themes_dir()
    return awful.util.get_configuration_dir() .. "themes/"
end

-- {{{ Variable definitions
-- Themes define colours, icons, font and wallpapers.
beautiful.init(awful.util.get_themes_dir().."default/theme.lua")

-- This is used later as the default terminal and editor to run.
local terminal = "urxvt"
local editor = os.getenv("EDITOR") or "nano"
local editor_cmd = terminal .. " -e " .. editor

-- Default modkey.
-- Usually, Mod4 is the key with a logo between Control and Alt.
-- If you do not like this or do not have such a key,
-- I suggest you to remap Mod4 to another key using xmodmap or other tools.
-- However, you can use another modifier like Mod1, but it may interact with others.
local modkey = "Mod4"
local alt = "Mod1"

-- Table of layouts to cover with awful.layout.inc, order matters.
awful.layout.layouts = {
    awful.layout.suit.tile.bottom,
    awful.layout.suit.tile.top,
    awful.layout.suit.tile,
    awful.layout.suit.tile.left,
    awful.layout.suit.fair,
    awful.layout.suit.fair.horizontal,
    awful.layout.suit.spiral,
    awful.layout.suit.spiral.dwindle,
    awful.layout.suit.max,
    awful.layout.suit.max.fullscreen,
    awful.layout.suit.magnifier,
    awful.layout.suit.corner.nw,
    awful.layout.suit.floating,
}
-- }}}

-- {{{ Helper functions
local function client_menu_toggle_fn()
    local instance = nil

    return function ()
        if instance and instance.wibox.visible then
            instance:hide()
            instance = nil
        else
            instance = awful.menu.clients({ theme = { width = 250 } })
        end
    end
end

-- {{{ Menu
-- Create a laucher widget and a main menu
local myawesomemenu = {
    { "hotkeys", function() return false, hotkeys_popup.show_help end},
    { "manual", terminal .. " -e man awesome" },
    { "edit config", editor_cmd .. " " .. awesome.conffile },
    { "restart", awesome.restart },
    { "quit", function() awesome.quit() end}
}

local mymainmenu = awful.menu {
    theme = {
        width = 200;
    };
    items = {
        { "awesome",       myawesomemenu, beautiful.awesome_icon };
        { "open terminal", terminal,  "/usr/share/icons/Adwaita/256x256/apps/utilities-terminal.png" };
        { "File Browser",  "thunar",  "/usr/share/icons/hicolor/128x128/apps/Thunar.png" };
        { "Firefox",       "firefox", "/usr/share/icons/hicolor/256x256/apps/firefox.png" };
        { "Chromium",      "chromium","/usr/share/icons/hicolor/256x256/apps/chromium.png" };
        { "Gajim",         "gajim",   "/usr/share/icons/hicolor/scalable/apps/gajim.svg" };
        { "Liferea",       "liferea", "/usr/share/icons/hicolor/scalable/apps/liferea.svg" };
        { "Sublime Text",  "subl3",   "/usr/share/icons/hicolor/256x256/apps/sublime-text.png" };
        { "Suspend",       "systemctl suspend" };
        { "Reboot",        "systemctl reboot" };
        { "Poweroff",      "systemctl poweroff" };
    };
}

local mylauncher = awful.widget.launcher({ image = beautiful.awesome_icon,
                                     menu = mymainmenu })

-- Menubar configuration
menubar.utils.terminal = terminal -- Set the terminal for applications that require it
-- }}}

-- Keyboard map indicator and switcher
local mykeyboardlayout = awful.widget.keyboardlayout()

-- {{{ Wibar
-- Create a textclock widget
local mytextclock = wibox.widget.textclock()

-- Create a wibox for each screen and add it
local taglist_buttons = awful.util.table.join(
    awful.button({ }, 1, function(t) t:view_only() end),
    awful.button({ modkey }, 1, function(t)
        if client.focus then
            client.focus:move_to_tag(t)
        end
    end),
    awful.button({ }, 3, awful.tag.viewtoggle),
    awful.button({ modkey }, 3, function(t)
        if client.focus then
            client.focus:toggle_tag(t)
        end
    end),
    awful.button({ }, 4, function(t) awful.tag.viewnext(t.screen) end),
    awful.button({ }, 5, function(t) awful.tag.viewprev(t.screen) end)
)

local tasklist_buttons = awful.util.table.join(
    awful.button({ }, 1, function (c)
        if c == client.focus then
            c.minimized = true
        else
            -- Without this, the following
            -- :isvisible() makes no sense
            c.minimized = false
            if not c:isvisible() and c.first_tag then
                c.first_tag:view_only()
            end
            -- This will also un-minimize
            -- the client, if needed
            client.focus = c
            c:raise()
        end
    end),
    awful.button({ }, 3, client_menu_toggle_fn()),
    awful.button({ }, 4, function ()
        awful.client.focus.byidx(1)
    end),
    awful.button({ }, 5, function ()
        awful.client.focus.byidx(-1)
    end)
)

local function set_wallpaper(s)
    -- Wallpaper
    if beautiful.wallpaper then
        local wallpaper = beautiful.wallpaper
        -- If wallpaper is a function, call it with the screen
        if type(wallpaper) == "function" then
            wallpaper = wallpaper(s)
        end
        gears.wallpaper.maximized(wallpaper, s, true)
    end
end

-- Re-set wallpaper when a screen's geometry changes (e.g. different resolution)
screen.connect_signal("property::geometry", set_wallpaper)

awful.screen.connect_for_each_screen(function(s)
    -- Wallpaper
    set_wallpaper(s)

    -- Each screen has its own tag table.
    awful.tag({ "1", "2", "3", "4", "5", "6", "7", "8", "9" }, s, awful.layout.layouts[1])

    -- Create a promptbox for each screen
    s.mypromptbox = awful.widget.prompt()
    -- Create an imagebox widget which will contains an icon indicating which layout we're using.
    -- We need one layoutbox per screen.
    s.mylayoutbox = awful.widget.layoutbox(s)
    s.mylayoutbox:buttons(awful.util.table.join(
        awful.button({ }, 1, function () awful.layout.inc(1) end),
        awful.button({ }, 3, function () awful.layout.inc(-1) end),
        awful.button({ }, 4, function () awful.layout.inc(1) end),
        awful.button({ }, 5, function () awful.layout.inc(-1) end)))
    -- Create a taglist widget
    s.mytaglist = awful.widget.taglist(s, awful.widget.taglist.filter.all, taglist_buttons)

    -- Create a tasklist widget
    s.mytasklist = awful.widget.tasklist(s, awful.widget.tasklist.filter.currenttags, tasklist_buttons)

    -- Create the wibox
    s.mywibox = awful.wibar({ position = "top", screen = s })

    -- Add widgets to the wibox
    s.mywibox:setup {
        layout = wibox.layout.align.horizontal,
        { -- Left widgets
            layout = wibox.layout.fixed.horizontal,
            mylauncher,
            s.mytaglist,
            s.mypromptbox,
        },
        s.mytasklist, -- Middle widget
        { -- Right widgets
            layout = wibox.layout.fixed.horizontal,
            mykeyboardlayout,
            wibox.widget.systray(),
            mytextclock,
            s.mylayoutbox,
        },
    }
end)
-- }}}

-- {{{ Mouse bindings
root.buttons(awful.util.table.join(
    awful.button({ }, 3, function () mymainmenu:toggle() end),
    awful.button({ }, 4, awful.tag.viewnext),
    awful.button({ }, 5, awful.tag.viewprev)
))
-- }}}

-- {{{ Key bindings
local globalkeys = awful.util.table.join(
    awful.key({ modkey,           }, "s",      hotkeys_popup.show_help,
        {description="show help", group="awesome"}),
    awful.key({ modkey,           }, "Left",   awful.tag.viewprev,
        {description = "view previous", group = "tag"}),
    awful.key({ modkey,           }, "Right",  awful.tag.viewnext,
        {description = "view next", group = "tag"}),
    awful.key({ modkey,           }, "Escape", awful.tag.history.restore,
        {description = "go back", group = "tag"}),

    awful.key({ modkey,           }, "j",
        function ()
            awful.client.focus.byidx( 1)
        end,
        {description = "focus next by index", group = "client"}
    ),
    awful.key({ modkey,           }, "k",
        function ()
            awful.client.focus.byidx(-1)
        end,
        {description = "focus previous by index", group = "client"}
    ),
    awful.key({ modkey,           }, "w", function () mymainmenu:show() end,
        {description = "show main menu", group = "awesome"}),

    -- Layout manipulation
    awful.key({ modkey, "Shift"   }, "j", function () awful.client.swap.byidx(  1)    end,
        {description = "swap with next client by index", group = "client"}),
    awful.key({ modkey, "Shift"   }, "k", function () awful.client.swap.byidx( -1)    end,
        {description = "swap with previous client by index", group = "client"}),
    awful.key({ modkey, "Control" }, "j", function () awful.screen.focus_relative( 1) end,
        {description = "focus the next screen", group = "screen"}),
    awful.key({ modkey, "Control" }, "k", function () awful.screen.focus_relative(-1) end,
        {description = "focus the previous screen", group = "screen"}),
    awful.key({ modkey,           }, "u", awful.client.urgent.jumpto,
        {description = "jump to urgent client", group = "client"}),
    awful.key({ modkey,           }, "Tab",
        function ()
            awful.client.focus.history.previous()
            if client.focus then
                client.focus:raise()
            end
        end,
        {description = "go back", group = "client"}),

    -- Standard program
    awful.key({ modkey,           }, "Return", function () awful.spawn(terminal) end,
        {description = "open a terminal", group = "launcher"}),
    awful.key({ modkey, "Control" }, "r", awesome.restart,
        {description = "reload awesome", group = "awesome"}),
    awful.key({ modkey, "Shift"   }, "q", awesome.quit,
        {description = "quit awesome", group = "awesome"}),

    awful.key({ modkey,           }, "l",     function () awful.tag.incmwfact( 0.05)          end,
        {description = "increase master width factor", group = "layout"}),
    awful.key({ modkey,           }, "h",     function () awful.tag.incmwfact(-0.05)          end,
        {description = "decrease master width factor", group = "layout"}),
    awful.key({ modkey, "Shift"   }, "h",     function () awful.tag.incnmaster( 1, nil, true) end,
        {description = "increase the number of master clients", group = "layout"}),
    awful.key({ modkey, "Shift"   }, "l",     function () awful.tag.incnmaster(-1, nil, true) end,
        {description = "decrease the number of master clients", group = "layout"}),
    awful.key({ modkey, "Control" }, "h",     function () awful.tag.incncol( 1, nil, true)    end,
        {description = "increase the number of columns", group = "layout"}),
    awful.key({ modkey, "Control" }, "l",     function () awful.tag.incncol(-1, nil, true)    end,
        {description = "decrease the number of columns", group = "layout"}),
    awful.key({ modkey,           }, "space", function () awful.layout.inc( 1)                end,
        {description = "select next", group = "layout"}),
    awful.key({ modkey, "Shift"   }, "space", function () awful.layout.inc(-1)                end,
        {description = "select previous", group = "layout"}),

    awful.key({ modkey, "Control" }, "n",
        function ()
            local c = awful.client.restore()
            -- Focus restored client
            if c then
                client.focus = c
                c:raise()
            end
        end,
        {description = "restore minimized", group = "client"}),

    -- Prompt
    awful.key({ modkey },            "r",     function () awful.screen.focused().mypromptbox:run() end,
        {description = "run prompt", group = "launcher"}),

    awful.key({ modkey }, "x",
        function ()
            awful.prompt.run {
                prompt       = "Run Lua code: ",
                textbox      = awful.screen.focused().mypromptbox.widget,
                exe_callback = awful.util.eval,
                history_path = awful.util.get_cache_dir() .. "/history_eval"
            }
        end,
        {description = "lua execute prompt", group = "awesome"}),

    -- Screensaver
    awful.key({ modkey, alt}, "l", function() os.execute("slock") end,
        {description = "lock the screen"}),
    -- Screenshot
    awful.key({ }, "Print", function() awful.spawn("xfce4-screenshooter") end,
        {description = "take a screenshot"}),
    -- Menubar
    awful.key({ modkey, "Shift" }, "p", function() menubar.show() end,
        {description = "show the menubar", group = "launcher"})
)

local clientkeys = awful.util.table.join(
    awful.key({ modkey,           }, "f",
        function (c)
            c.fullscreen = not c.fullscreen
            c:raise()
        end,
        {description = "toggle fullscreen", group = "client"}),
    awful.key({ modkey, "Shift"   }, "c",      function (c) c:kill()                         end,
        {description = "close", group = "client"}),
    awful.key({ modkey, "Control" }, "space",  awful.client.floating.toggle                     ,
        {description = "toggle floating", group = "client"}),
    awful.key({ modkey, "Control" }, "Return", function (c) c:swap(awful.client.getmaster()) end,
        {description = "move to master", group = "client"}),
    awful.key({ modkey,           }, "o",      function (c) c:move_to_screen()               end,
        {description = "move to screen", group = "client"}),
    awful.key({ modkey,           }, "t",      function (c) c.ontop = not c.ontop            end,
        {description = "toggle keep on top", group = "client"}),
    awful.key({ modkey,           }, "n",
        function (c)
            -- The client currently has the input focus, so it cannot be
            -- minimized, since minimized clients can't have the focus.
            c.minimized = true
        end,
        {description = "minimize", group = "client"}),
    awful.key({ modkey,           }, "m",
        function (c)
            c.maximized = not c.maximized
            c:raise()
        end,
        {description = "maximize", group = "client"})
)

-- Bind all key numbers to tags.
-- Be careful: we use keycodes to make it works on any keyboard layout.
-- This should map on the top row of your keyboard, usually 1 to 9.
for i = 1, 9 do
    globalkeys = awful.util.table.join(globalkeys,
        -- View tag only.
        awful.key({ modkey }, "#" .. i + 9,
            function ()
                local screen = awful.screen.focused()
                local tag = screen.tags[i]
                if tag then
                    tag:view_only()
                end
            end,
            {description = "view tag #"..i, group = "tag"}),
        -- Toggle tag display.
        awful.key({ modkey, "Control" }, "#" .. i + 9,
            function ()
                local screen = awful.screen.focused()
                local tag = screen.tags[i]
                if tag then
                    awful.tag.viewtoggle(tag)
                end
            end,
            {description = "toggle tag #" .. i, group = "tag"}),
        -- Move client to tag.
        awful.key({ modkey, "Shift" }, "#" .. i + 9,
            function ()
                if client.focus then
                    local tag = client.focus.screen.tags[i]
                    if tag then
                        client.focus:move_to_tag(tag)
                    end
                end
            end,
            {description = "move focused client to tag #"..i, group = "tag"}),
        -- Toggle tag on focused client.
        awful.key({ modkey, "Control", "Shift" }, "#" .. i + 9,
            function ()
                if client.focus then
                    local tag = client.focus.screen.tags[i]
                    if tag then
                        client.focus:toggle_tag(tag)
                    end
                end
            end,
            {description = "toggle focused client on tag #" .. i, group = "tag"})
    )
end

local clientbuttons = awful.util.table.join(
    awful.button({ }, 1, function (c) client.focus = c; c:raise() end),
    awful.button({ modkey }, 1, awful.mouse.client.move),
    awful.button({ modkey }, 3, awful.mouse.client.resize))

-- Set keys
root.keys(globalkeys)
-- }}}

-- {{{ Rules
-- Rules to apply to new clients (through the "manage" signal).
awful.rules.rules = {
    -- All clients will match this rule.
    {
        rule = { },
        properties = {
            border_width = beautiful.border_width,
            border_color = beautiful.border_normal,
            focus = awful.client.focus.filter,
            raise = true,
            keys = clientkeys,
            buttons = clientbuttons,
            screen = awful.screen.preferred,
            placement = awful.placement.no_overlap+awful.placement.no_offscreen
        }
    },

    -- Floating clients.
    {
        rule_any = {
            instance = {
                "DTA",  -- Firefox addon DownThemAll.
                "copyq",  -- Includes session name in class.
            },
            class = {
                "Arandr",
                "Gpick",
                "Kruler",
                "MessageWin",  -- kalarm.
                "Sxiv",
                "Wpa_gui",
                "pinentry",
                "veromix",
                "xtightvncviewer",
            },
            name = {
                "Event Tester",  -- xev.
            },
            role = {
                "AlarmWindow",  -- Thunderbird's calendar.
                "pop-up",       -- e.g. Google Chrome's (detached) Developer Tools.
            }
        },
        properties = { floating = true }
    },

    -- Add titlebars to normal clients and dialogs
    {
        rule_any = {type = { "normal", "dialog" } },
        properties = { titlebars_enabled = true }
    },

    -- Set Firefox to always map on the tag named "2" on screen 1.
    -- { rule = { class = "Firefox" },
    --   properties = { screen = 1, tag = "2" } },
}
-- }}}

-- {{{ Signals
-- Signal function to execute when a new client appears.
client.connect_signal("manage", function (c)
    -- Set the windows at the slave,
    -- i.e. put it at the end of others instead of setting it master.
    -- if not awesome.startup then awful.client.setslave(c) end

    if awesome.startup and
        not c.size_hints.user_position
        and not c.size_hints.program_position then
        -- Prevent clients from being unreachable after screen count changes.
        awful.placement.no_offscreen(c)
    end
end)

-- Add a titlebar if titlebars_enabled is set to true in the rules.
client.connect_signal("request::titlebars", function(c)
    -- buttons for the titlebar
    local buttons = awful.util.table.join(
        awful.button({ }, 1, function()
            client.focus = c
            c:raise()
            awful.mouse.client.move(c)
        end),
        awful.button({ }, 3, function()
            client.focus = c
            c:raise()
            awful.mouse.client.resize(c)
        end)
    )

    awful.titlebar(c, {size=30;}):setup {
        { -- Left
            awful.titlebar.widget.iconwidget(c),
            buttons = buttons,
            layout  = wibox.layout.fixed.horizontal
        },
        { -- Middle
            { -- Title
                align  = "center",
                widget = awful.titlebar.widget.titlewidget(c)
            },
                buttons = buttons,
                layout  = wibox.layout.flex.horizontal
            },
        { -- Right
            awful.titlebar.widget.floatingbutton (c),
            awful.titlebar.widget.maximizedbutton(c),
            awful.titlebar.widget.stickybutton   (c),
            awful.titlebar.widget.ontopbutton    (c),
            awful.titlebar.widget.closebutton    (c),
            layout = wibox.layout.fixed.horizontal()
        },
        layout = wibox.layout.align.horizontal
    }
end)

-- Enable sloppy focus, so that focus follows mouse.
client.connect_signal("mouse::enter", function(c)
    if awful.layout.get(c.screen) ~= awful.layout.suit.magnifier
        and awful.client.focus.filter(c) then
        client.focus = c
    end
end)

client.connect_signal("focus", function(c) c.border_color = beautiful.border_focus end)
client.connect_signal("unfocus", function(c) c.border_color = beautiful.border_normal end)
-- }}}

local function running(prog)
    return not not os.execute("pgrep -u " .. os.getenv "USER" .. " " .. prog)
end

if not running("thunar") then
    awful.spawn("thunar --daemon")
end
if not running("pasystray") then
    awful.spawn("pasystray")
end
if not running("nm-applet") then
    awful.spawn("nm-applet --sm-disable")
end
if not running("system-config-printer-applet") then
    awful.spawn("system-config-printer-applet")
end
dofile(awful.util.getdir("config") .. "/multimedia.lua")
dofile(awful.util.getdir("config") .. "/brightness.lua")

取自 https://gist.github.com/daurnimator/a19e633032c2dc5086ea63323d6a24b6#file-cleverbot-lua

-- API figured out from https://github.com/pierredavidbelanger/chatter-bot-api/blob/master/python/chatterbotapi.py

local request = require "http.request"
local http_util = require "http.util"
local digest = require "openssl.digest"

local function hex(str)
    return (str:gsub(".", function(c)
        return string.format("%0x", string.byte(c))
    end))
end
local function md5hex(str)
    return hex(digest.new("md5"):final(str))
end
local function uri_encode_with_plus(str)
    return http_util.encodeURIComponent(str):gsub("%%20", "+")
end

local methods = {}
local mt = {
    __name = "cleverbot";
    __index = methods;
}
local function new()
    return setmetatable({
        base_uri = "http://www.cleverbot.com/";
        service_uri = "http://www.cleverbot.com/webservicemin?uc=165&";
        hash_pos = 35;
        cookie = {};
        current_state = "";
    }, mt)
end

function methods:get_cookie()
    local req = request.new_from_uri(self.base_uri)
    local headers, stream = assert(req:go())
    stream:shutdown()
    assert(headers:get(":status") == "200")
    local set_cookie = headers:get("set-cookie")
    local key, value = set_cookie:match("([^%s;=]+)=?([^%s;]*)")
    self.cookie[key] = value
end

function methods:think(thought)
    if next(self.cookie) == nil then
        self:get_cookie()
    end
    local req = request.new_from_uri(self.service_uri)
    req.headers:upsert(":method", "POST")
    do
        local cookie = {}
        for k, v in pairs(self.cookie) do
            cookie[#cookie+1] = k .. "=" .. v
        end
        cookie = table.concat(cookie, "; ")
        req.headers:upsert("cookie", cookie)
    end
    req.headers:upsert("content-type", "application/x-www-form-urlencoded")
    local current_query = "stimulus="..uri_encode_with_plus(thought)
        .. self.current_state
        .. "&islearning=1&icognoid=wsf&"
    local icognocheck = md5hex(current_query:sub(10, self.hash_pos))
    req:set_body(current_query .. "icognocheck="..icognocheck)
    local headers, stream = assert(req:go())
    local response_body = stream:get_body_as_string()
    if headers:get(":status") ~= "200" then
        error(response_body)
    end
    stream:shutdown()
    local set_cookie = headers:get("set-cookie")
    if set_cookie then
        local key, value = set_cookie:match("([^%s;=]+)=?([^%s;]*)")
        self.cookie[key] = value
    end
    local iter = response_body:gmatch("([^\r]*)\r")
    local text = iter()
    self.current_state = "&sessionid=" .. uri_encode_with_plus(iter())
        .. "&logurl=" .. uri_encode_with_plus(iter())
        .. "&vText8=" .. uri_encode_with_plus(iter())
        .. "&vText7=" .. uri_encode_with_plus(iter())
        .. "&vText6=" .. uri_encode_with_plus(iter())
        .. "&vText5=" .. uri_encode_with_plus(iter())
        .. "&vText4=" .. uri_encode_with_plus(iter())
        .. "&vText3=" .. uri_encode_with_plus(iter())
        .. "&vText2=" .. uri_encode_with_plus(iter())
        .. "&prevref=" .. uri_encode_with_plus(iter())
    return text
end

return {
  new = new;
}

取自 https://gist.github.com/daurnimator/23e36762dc62198da8804350df654ecf#file-init-lua

local cqueues = require "cqueues"

local unpack = table.unpack or unpack
local pack = table.pack or function(...) return {n=select("#", ...), ...} end

local POLLIN, POLLOUT, POLLPRI = 1, 2, 4

-- a tostring() that can't throw
local function safe_tostring(t)
    local ok, s = pcall(tostring, t)
    if not ok then
        return "(null)"
    else
        return s
    end
end

local function each_arg(...)
    return function(args, last)
        if last == args.n then return nil end
        local i = last + 1
        return i, args[i]
    end, pack(...), 0
end

local methods = {}
local mt = {
    __name = "lua cqueue";
    __index = methods;
}

local function thread_add_head(thread, head)
    thread.prev = head
    thread.next = head.next
    if head.next then
        head.next.prev = thread
    end
    head.next = thread
end
local function thread_del(thread)
    local prev, next = thread.prev, thread.next
    prev.next = next
    if next then
        next.prev = prev
    end
    -- thread.prev, thread.next = nil, nil -- not needed but catches programming errors
end
local function thread_move(thread, head)
    -- Remove from old list
    thread_del(thread)
    -- Add to new list
    thread_add_head(thread, head)
end

local _POLL = {} -- something unique

-- Table of scheduler objects
local cstack = setmetatable({}, {__mode="kv"})
-- The currently running scheduler
local cstack_running = nil
local function cstack_push(new)
    new.running = cstack_running
    cstack_running = new
end
local function cstack_pop()
    cstack_running = cstack_running.running
end

local timer_methods = {}
local timer_mt = {
    __name = "timer collection";
    __index = timer_methods;
}
local function new_timers()
    return setmetatable({}, timer_mt)
end
function timer_methods:add(ob, deadline)
    self[ob] = deadline
end
function timer_methods:remove(ob)
    self[ob] = nil
end
function timer_methods:min()
    local min = math.huge
    for _, deadline in pairs(self) do
        if deadline < min then
            min = deadline
        end
    end
    if min == math.huge then
        return nil
    else
        return min
    end
end
timer_methods.each = pairs

local condition_methods = {}
local condition_mt = {
    __name = "condition";
    __index = condition_methods;
}
local function new_condition(lifo)
    return setmetatable({
        lifo = lifo;
        head = 1;
        tail = 0;
    }, condition_mt)
end
do
    local function check(self, why, ...)
        if self == why then
            return true, ...
        else
            return false, why, ...
        end
    end
    function condition_methods:wait(...)
        return check(self, coroutine.yield(_POLL, self, ...))
    end
end
local function condition_add(self, fn)
    if self.lifo then
        local i = self.head - 1
        self[i] = fn
        self.head = i
    else
        local i = self.tail + 1
        self[i] = fn
        self.tail = i
    end
end
function condition_methods:signal(max)
    if max then
        max = math.min(self.tail, self.head + max)
    else
        max = self.tail
    end
    for i=self.head, max do
        local event = self[i]
        event.pending = true
        thread_move(event.thread, event.thread.scheduler.pending)
        -- TODO: wakeup
    end
end
function condition_methods:pollfd()
    return self
end
function condition_methods:events()
    return nil
end
function condition_methods:timeout()
    return nil
end

local function new()
    local self = setmetatable({
        thread_count = 0; -- count of items in polling+pending
        polling = {next=nil}; -- linked list of threads
        pending = {next=nil}; -- linked list of threads
        current = nil; -- current coroutine
        running = nil; -- linked list of cqueues
        timers = new_timers();
    }, mt)
    cstack[self] = true
    return self
end

function methods.interpose(key, func)
    local old = methods[key]
    methods[key] = func
    return old
end

-- local monotonic_clock = 0
-- local function monotime()
--  monotonic_clock = monotonic_clock + 1
--  return monotonic_clock
-- end
local monotime = cqueues.monotime

local function running()
    local is_caller
    if cstack_running then
        is_caller = cstack_running.current == coroutine.running()
    else
        is_caller = false
    end
    return cstack_running, is_caller
end

local function cancel(...)
    for cq in pairs(cstack) do
        cq:cancel(...)
    end
end

local function reset()
    for cq in pairs(cstack) do
        cq:reset()
    end
end

local poller = new()
local function poll(...)
    if running() then
        return coroutine.yield(_POLL, ...)
    else
        local tuple

        poller:wrap(function (...)
            tuple = { poll(...) }
        end, ...)

        -- NOTE: must step twice, once to call poll and
        -- again to wake up
        assert(poller:step())
        assert(poller:step())

        return unpack(tuple or {})
    end
end

local function sleep(timeout)
    poll(timeout)
end

do
    local handle_resume
    local function do_resume(self, thread, ...)
        cstack_push(self)
        self.current = thread.co
        return handle_resume(self, thread, coroutine.resume(thread.co, ...))
    end
    local function cleanup(self, thread, ...)
        thread_del(thread)
        self.thread_count = self.thread_count - 1
        return ...
    end
    function handle_resume(self, thread, ok, first, ...)
        self.current = nil
        cstack_pop()
        if not ok then
            return cleanup(self, thread, nil, first, nil, thread.co)
        elseif coroutine.status(thread.co) == "dead" then
            return cleanup(self, thread, true)
        else
            if first == _POLL then
                local now = monotime()
                local thread_timeout = math.huge
                for _, v in each_arg(...) do
                    local pollfd, events, timeout, cond = nil, 0, nil, nil
                    if type(v) == "number" then
                        timeout = v
                    elseif getmetatable(v) == condition_mt then
                        cond = v
                    elseif v ~= nil then
                        if v.pollfd ~= nil then
                            pollfd = v.pollfd
                            if type(pollfd) == "function" then
                                local pcall_ok, err = pcall(pollfd, v)
                                if not pcall_ok then
                                    return cleanup(self, thread, nil, "error calling method pollfd: " .. safe_tostring(err))
                                end
                                pollfd = err
                            end
                            if type(pollfd) == "number" then
                                if pollfd == -1 then
                                    pollfd = nil
                                end
                            elseif getmetatable(pollfd) == condition_mt then
                                pollfd, cond = nil, pollfd
                            elseif pollfd ~= nil then
                                return cleanup(self, thread, nil, "invalid pollfd (expected nil, number or condition)", nil, thread.co, v)
                            end
                        end
                        if v.events ~= nil then
                            events = v.events
                            if type(events) == "function" then
                                local pcall_ok, err = pcall(events, v)
                                if not pcall_ok then
                                    return cleanup(self, thread, nil, "error calling method events: " .. safe_tostring(err))
                                end
                                events = err
                            end
                            if events == nil then
                                events = 0
                            elseif type(events) == "string" then
                                local e = 0
                                if events:match "r" then
                                    e = e + POLLIN
                                end
                                if events:match "w" then
                                    e = e + POLLOUT
                                end
                                if events:match "p" then
                                    e = e + POLLPRI
                                end
                                events = e
                            elseif type(events) ~= "number" then
                                return cleanup(self, thread, nil, "invalid events (expected nil, number or string)", nil, thread.co, v, pollfd)
                            end
                        end
                        if v.timeout ~= nil then
                            timeout = v.timeout
                            if type(timeout) == "function" then
                                local pcall_ok, err = pcall(timeout, v)
                                if not pcall_ok then
                                    return cleanup(self, thread, nil, "error calling method timeout: " .. safe_tostring(err))
                                end
                                timeout = err
                            end
                        end
                    end
                    if timeout == nil then
                        timeout = math.huge
                    elseif type(timeout) ~= "number" then
                        return cleanup(self, thread, nil, "invalid timeout (expected nil or number)", nil, thread.co, v, pollfd)
                    end

                    if timeout < math.huge or cond or (pollfd and events ~= 0) then
                        local event = {
                            thread = thread;
                            value = v;
                            deadline = now + timeout;
                            pending = false;
                        }
                        table.insert(thread.events, event)

                        if cond then
                            condition_add(cond, event)
                        end

                        if pollfd then
                            error("NYI pollfd")
                        end

                        if timeout then
                            thread_timeout = math.min(timeout, thread_timeout)
                        end
                    end
                end
                if thread.events[1] ~= nil or thread_timeout ~= math.huge then
                    if thread_timeout ~= math.huge then
                        -- add thread to timers
                        self.timers:add(thread, now+thread_timeout)
                    end
                    thread_move(thread, self.polling)
                end
                return true
            else
                return do_resume(self, thread, coroutine.yield(first, ...))
            end
        end
    end
    function methods:step(timeout)
        if running() then
            poll(self, timeout)
            timeout = 0.0
        end
        assert(self.current == nil, "cannot step live cqueue")

        if self.pending.next then
            timeout = 0
        else
            timeout = timeout or math.huge
            local t = self.timers:min()
            if t then
                timeout = math.min(timeout, t-monotime())
            end
        end

        -- Find out what in self.polling is ready; take up to 'timeout'
        if timeout > 0 then
            print("WAITING FOR", timeout)
            cqueues.sleep(timeout)
            print("WAITED")
        end

        -- Move them to .pending list
        local now = monotime()
        for thread, deadline in self.timers:each() do
            if deadline <= now then
                for _, event in ipairs(thread.events) do
                    if event.deadline <= now then
                        event.pending = true
                    end
                end
                thread_move(thread, self.pending)
            end
        end

        -- Run pending threads
        local thread = self.pending.next
        while thread do
            local next_thread = thread.next -- Save next one incase current gets moved
            local polled_ready, i = {}, 0
            for _, v in ipairs(thread.events) do
                if v.pending then
                    i = i + 1
                    polled_ready[i] = v.value
                end
            end
            self.timers:remove(thread)
            local ok, err, errno, thd, ob, fd = do_resume(self, thread, unpack(polled_ready, 1, i))
            if not ok then
                return nil, err, errno, thd, ob, fd
            end
            thread = next_thread
        end
        return true
    end
end

function methods:attach(co)
    assert(type(co) == "thread")
    local thread = {co = co; events = {}; scheduler = self; next = nil; prev = nil}
    thread_add_head(thread, self.pending)
    self.thread_count = self.thread_count + 1
    -- tryalert
    return self
end

function methods:wrap(func, ...)
    local co = coroutine.create(function(...)
        coroutine.yield()
        return func(...)
    end)
    coroutine.resume(co, ...)
    local thread = {co = co; events = {}; scheduler = self; next = nil; prev = nil}
    thread_add_head(thread, self.pending)
    self.thread_count = self.thread_count + 1
    -- tryalert
    return self
end

function methods:empty()
    return self.thread_count == 0
end

function methods:count()
    return self.thread_count
end

function methods:cancel(...)
    for _, v in each_arg(...) do
        if type(v) ~= "number" then
            v = v.pollfd
            if type(v.pollfd) == "function" then
                local ok, err = pcall(v.pollfd, v)
                if not ok then
                    error("error calling method pollfd: "..safe_tostring(err))
                end
                v = err
            end
            if type(v) ~= "number" then
                error("error loading field pollfd")
            end
        end
        --
    end
end

-- function methods:reset()
--  -- Move polling list to pending

--  local n = #self.pending
--  local e = #self.polling
--  for i=1, e do
--      self.pending[n+i] = self.polling[i]
--      self.polling[i] = nil
--  end
-- end

-- function methods:pollfd()
--  return nil
-- end

function methods:events()
    return "r"
end

function methods:timeout()
    if self.pending.next then
        return 0
    else
        local t = self.timers:min()
        if t then
            return t - monotime()
        else
            return nil
        end
    end
end

local function todeadline(timeout)
    -- special case 0 timeout to avoid monotime call in totimeout
    return timeout and (timeout > 0 and monotime() + timeout or 0) or nil
end -- todeadline

local function totimeout(deadline)
    if not deadline then
        return nil, false
    elseif deadline == 0 then
        return 0, true
    else
        local curtime = monotime()

        if curtime < deadline then
            return deadline - curtime, false
        else
            return  0, true
        end
    end
end

function methods:loop(timeout)
    local function checkstep(self, deadline, ok, ...)
        local timeout, expired = totimeout(deadline)

        if not ok then
            return false, ...
        elseif expired or self:empty() then
            return true
        else
            return checkstep(self, deadline, self:step(timeout))
        end
    end

    local deadline = todeadline(timeout)
    return checkstep(self, deadline, self:step(timeout))
end

function methods:errors(timeout)
    local deadline = todeadline(timeout)

    return function ()
        local timeout = totimeout(deadline)
        return select(2, self:loop(timeout))
    end
end

return {
    _POLL = _POLL;
    new = new;
    running = running;
    cancel = cancel;
    reset = reset;
    monotime = monotime;
    poll = poll;
    sleep = sleep;
}
mdecourse commented 6 years ago
if (sim_call_type==sim_childscriptcall_initialization) then 
    steer_handle= simGetObjectHandle('steer_joint')
    motor_handle= simGetObjectHandle('motor_joint')
    fl_brake_handle= simGetObjectHandle('fl_brake_joint')
    fr_brake_handle= simGetObjectHandle('fr_brake_joint')
    bl_brake_handle= simGetObjectHandle('bl_brake_joint')
    br_brake_handle= simGetObjectHandle('br_brake_joint')

    --wheel radius:         0.09
    --wheel base:             0.6
    --wheel track:             0.35
    --maximum steering rate:     70 deg/sec

        --the maximum steer angle 30 degree
    max_steer_angle=0.5235987
        --the maximum torque of the motor
    motor_torque=60

    dVel=1
    dSteer=0.1

        --input steer
    steer_angle=0
        --input velocity
    motor_velocity=dVel*10
        --input brake
    brake_force=0
end 

if (sim_call_type==sim_childscriptcall_cleanup) then 

end 

if (sim_call_type==sim_childscriptcall_actuation) then 
    --current steer pos
    steer_pos=simGetJointPosition(steer_handle);
    --current angular velocity of back left wheel
    bl_wheel_velocity=simGetObjectFloatParameter(bl_brake_handle,sim_jointfloatparam_velocity)
    --current angular velocity of back right wheel
    br_wheel_velocity=simGetObjectFloatParameter(br_brake_handle,sim_jointfloatparam_velocity)
    --average angular velocity of the back wheels
    rear_wheel_velocity=(bl_wheel_velocity+br_wheel_velocity)/2
    --linear velocity
    linear_velocity=rear_wheel_velocity*0.09 

    -- Read the keyboard messages (make sure the focus is on the main window, scene view):
    message,auxiliaryData=simGetSimulatorMessage()
    while message~=-1 do
        if (message==sim_message_keypress) then
            if (auxiliaryData[1]==2007) then
                -- up key
                if (motor_velocity<dVel*9.99) then
                    motor_velocity=motor_velocity+dVel
                end
            end
            if (auxiliaryData[1]==2008) then
                -- down key
                if (motor_velocity>-dVel*1.99) then
                    motor_velocity=motor_velocity-dVel
                else
                --    brake_force=100
                end
            end
            if (auxiliaryData[1]==string.byte('a')) then
                --  a char key
            --if (auxiliaryData[1]==string.byte('a')) then
                -- left key
                if (steer_angle<dSteer*4.99) then
                    steer_angle=steer_angle+dSteer
                end
            end
            if (auxiliaryData[1]==2010) then
                -- right key
                if (steer_angle>-dSteer*4.99) then
                    steer_angle=steer_angle-dSteer
                end
            end
        end
        message,auxiliaryData=simGetSimulatorMessage()
    end

    if (math.abs(motor_velocity)<dVel*0.1) then
        brake_force=100
    else
        brake_force=0
    end

        --set maximum steer angle
    if (steer_angle> max_steer_angle) then
    steer_angle=max_steer_angle
    end
    if (steer_angle< -max_steer_angle) then
    steer_angle= -max_steer_angle
    end
    simSetJointTargetPosition(steer_handle, steer_angle)

        --brake and motor can not be applied at the same time
    if(brake_force>0) then
    simSetJointForce(motor_handle, 0)
    else
    simSetJointForce(motor_handle, motor_torque)
    simSetJointTargetVelocity(motor_handle, motor_velocity)
    end

    simSetJointForce(fr_brake_handle, brake_force)
    simSetJointForce(fl_brake_handle, brake_force)
    simSetJointForce(bl_brake_handle, brake_force)
    simSetJointForce(br_brake_handle, brake_force)
end 
mdecourse commented 6 years ago

E-puck lua scripts:

-- This is the Epuck principal control script. It is threaded

-- FUNCTION MOVE
-- Moving the robot
-- distance : distance (meters)
-- velLeft : angular speed left wheel
-- velRight : angular speed rigth wheel
move = function(distance, velLeft, velRight)
    simSetJointTargetVelocity(leftMotor, velLeft)
    simSetJointTargetVelocity(rightMotor, velRight)

    local i = 0
    local distanceParcourue = 0
    local p = simGetObjectPosition(ePuckBase, -1)
    local p1 = simGetObjectPosition(ePuckBase, -1)

    -- time-out : variable i
    while (i < 1000 and distanceParcourue < distance) do
        p1 = simGetObjectPosition(ePuckBase, -1)
        distanceParcourue = distanceMes(p[1], p[2], p1[1], p1[2])
        --simAddStatusbarMessage('parcourue :' .. distanceParcourue .. ' - Attendue :' .. distance)
        i = i + 1
        simSwitchThread() 
    end

    simSetJointTargetVelocity(leftMotor,0)
    simSetJointTargetVelocity(rightMotor,0)
end

-- FUNCTION TURNLEFT
-- Make a left turn (90°)
-- vel : angular speed of wheels
turnLeft=function(vel)
   local rotAmount=-math.pi/2
   local sign=rotAmount/math.abs(rotAmount)
   simSetJointTargetVelocity(leftMotor,vel*sign*0.5)
   simSetJointTargetVelocity(rightMotor,-vel*sign*0.5)
   local previousAngle=simGetObjectOrientation(ePuckBase,-1)[3]
   local rot=0
   local i = 0

   while (i < 1000) do --and (math.abs(rot) < math.pi/2)
      local angle=simGetObjectOrientation(ePuckBase,-1)[3]
      local da=angle-previousAngle
      if da>=0 then
         da=math.mod(da+math.pi,2*math.pi)-math.pi
      else
         da=math.mod(da-math.pi,2*math.pi)+math.pi
      end
      rot=rot+da
      previousAngle=angle
      if math.abs(rot)>math.pi/2 then
         break
      end
      simSwitchThread()
   end
   simSetJointTargetVelocity(leftMotor,0)
   simSetJointTargetVelocity(rightMotor,0)
end

-- FUNCTION TURNRIGHT
-- Make a right turn (90°)
-- vel : angular speed of wheels
turnRight=function(vel)
   local rotAmount=math.pi/2
   local sign=rotAmount/math.abs(rotAmount)
   simSetJointTargetVelocity(leftMotor,vel*sign*0.5)
   simSetJointTargetVelocity(rightMotor,-vel*sign*0.5)
   local previousAngle=simGetObjectOrientation(ePuckBase,-1)[3]
   local rot=0
   local i = 0

   while (i < 1000) do --and (math.abs(rot) < math.pi/2) 
      local angle=simGetObjectOrientation(ePuckBase,-1)[3]
      local da=angle-previousAngle
      if da>=0 then
         da=math.mod(da+math.pi,2*math.pi)-math.pi
      else
         da=math.mod(da-math.pi,2*math.pi)+math.pi
      end
      rot=rot+da
      previousAngle=angle
      if math.abs(rot)>math.pi/2 then
         break
      end
      simSwitchThread()
   end
   simSetJointTargetVelocity(leftMotor,0)
   simSetJointTargetVelocity(rightMotor,0)
end

-- FONCTION UTURN
-- Make a half turn (180°)
-- vel : angular speed of wheels
uTurn=function(vel)
   local rotAmount=math.pi/2
   local sign=rotAmount/math.abs(rotAmount)
   simSetJointTargetVelocity(leftMotor,vel*sign*0.5)
   simSetJointTargetVelocity(rightMotor,-vel*sign*0.5)
   local previousAngle=simGetObjectOrientation(ePuckBase,-1)[3]
   local rot=0
   local i = 0

   while (i < 1000) do --and (math.abs(rot) < math.pi/2) 
      local angle=simGetObjectOrientation(ePuckBase,-1)[3]
      local da=angle-previousAngle
      if da>=0 then
         da=math.mod(da+math.pi,2*math.pi)-math.pi
      else
         da=math.mod(da-math.pi,2*math.pi)+math.pi
      end
      rot=rot+da
      previousAngle=angle
      if math.abs(rot)>math.pi then
         break
      end
      simSwitchThread()
   end
   simSetJointTargetVelocity(leftMotor,0)
   simSetJointTargetVelocity(rightMotor,0)
end

-- FUNCTION DISTANCE
-- Measure the absolute distance between point A(xa, yx) and B(xb, yb)
-- xa : coordinates x of first point (A)
-- ya : coordinates y of first point (A)
-- xb : coordinates x of second point (B)
-- yb : coordinates y of second point (B)
-- return : absolute distance between points
distanceMes = function(xa, ya, xb, yb)
    return math.sqrt( math.pow((xb - xa), 2) + math.pow((yb - ya), 2) )
end

-- FUNCTION SENSORS
-- Save actual state (true/false) of sensors
sensors = function()
    local boolSensors = 0x0
    local sc=simGetObjectSizeFactor(bodyElements)
    local r = 0
    local d = 0
    local i=0

    for i=1,8,1 do
        --r,d = simCheckProximitySensor(proxSens[i], sim_handle_all)
        r,d = simReadProximitySensor(proxSens[i])

        mask = math.pow(2, (i-1))
        notMask = simBoolXor32(mask, 0xFFFFFFFF)

--        if res > 0 and dist < 0.05*s then
        if (r > 0) then  
          boolSensors = simBoolOr32(boolSensors, mask)
            --simAddStatusbarMessage('sens[' .. i .. '] - ' .. r .. ' - ' .. d)            
        else
            boolSensors = simBoolAnd32(boolSensors, notMask)
            --simAddStatusbarMessage('sens[' .. i .. ']') 
        end

    end

    return boolSensors
end

-- NOT USED
moveDistance = function(distance, vel, vel2)
    local i = 0
    local distanceParcourue = 0
    local p = simGetObjectPosition(ePuckBase, -1)
    local p1 = simGetObjectPosition(ePuckBase, -1)
    bodyElements=simGetObjectHandle('ePuck_bodyElements')
    s=simGetObjectSizeFactor(bodyElements)

    simSetJointTargetVelocity(leftMotor,vel)
    simSetJointTargetVelocity(rightMotor,vel)

    velLeft=vel
    velRight=vel
    maxVel = vel

    while (i < 1000 and distanceParcourue < distance) do

        noDetectionDistance=0.05*s --0.05
        proxSensDist={noDetectionDistance,noDetectionDistance,noDetectionDistance,noDetectionDistance,noDetectionDistance,noDetectionDistance,noDetectionDistance,noDetectionDistance}
        for i=1,8,1 do
            res,dist=simReadProximitySensor(proxSens[i])
            if (res>0) and (dist<noDetectionDistance) then
                proxSensDist[i]=dist
            end
        end

        if (proxSensDist[1] < 0.2*noDetectionDistance) then
            velLeft=velLeft + maxVel * 0
            velRight=velRight + maxVel * -0.05 * (1-(proxSensDist[2]/noDetectionDistance))
            --velRight=velRight + maxVel * -0.05
        elseif (proxSensDist[6] < 0.2*noDetectionDistance) then
            velLeft=velLeft + maxVel * -0.05 * (1-(proxSensDist[4]/noDetectionDistance))
            --velLeft=velLeft + maxVel * -0.05
            velRight=velRight + maxVel * 0
        else
            velLeft=vel
            velRight=vel
        end

        p1 = simGetObjectPosition(ePuckBase, -1)
        distanceParcourue = distanceMes(p[1], p[2], p1[1], p1[2])
        i = i + 1
        simSetJointTargetVelocity(leftMotor,velLeft)
        simSetJointTargetVelocity(rightMotor,velRight)
        simSwitchThread() 
    end --while

        simSetJointTargetVelocity(leftMotor,0)
        simSetJointTargetVelocity(rightMotor,0)

end --function

actualizeLEDs=function()
    if (relLedPositions==nil) then
        relLedPositions={{-0.0343,0,0.0394},{-0.0297,0.0171,0.0394},{0,0.0343,0.0394},
                    {0.0297,0.0171,0.0394},{0.0343,0,0.0394},{0.0243,-0.0243,0.0394},
                    {0.006,-0.0338,0.0394},{-0.006,-0.0338,0.0394},{-0.0243, -0.0243,0.0394}}
    end
    if (drawingObject) then
        simRemoveDrawingObject(drawingObject)
    end
    type=sim_drawing_painttag+sim_drawing_followparentvisibility+sim_drawing_spherepoints+
        sim_drawing_50percenttransparency+sim_drawing_itemcolors+sim_drawing_itemsizes+
        sim_drawing_backfaceculling+sim_drawing_emissioncolor
    drawingObject=simAddDrawingObject(type,0,0,bodyElements,27)
    m=simGetObjectMatrix(ePuckBase,-1)
    itemData={0,0,0,0,0,0,0}
    simSetLightParameters(ledLight,0)
    for i=1,9,1 do
        if (ledColors[i][1]+ledColors[i][2]+ledColors[i][3]~=0) then
            p=simMultiplyVector(m,relLedPositions[i])
            itemData[1]=p[1]
            itemData[2]=p[2]
            itemData[3]=p[3]
            itemData[4]=ledColors[i][1]
            itemData[5]=ledColors[i][2]
            itemData[6]=ledColors[i][3]
            simSetLightParameters(ledLight,1,{ledColors[i][1],ledColors[i][2],ledColors[i][3]})
            for j=1,3,1 do
                itemData[7]=j*0.003
                simAddDrawingObjectItem(drawingObject,itemData)
            end
        end
    end
end

getLightSensors=function()
    data=simReceiveData(0,'EPUCK_lightSens')
    if (data) then
        lightSens=simUnpackFloatTable(data)
    end
    return lightSens
end

threadFunction=function()
    while simGetSimulationState()~=sim_simulation_advancing_abouttostop do
        st=simGetSimulationTime()
        velLeft=0
        velRight=0
move(1, 10, 10)
turnLeft(1)
move(1, 10, 10)
turnLeft(1)
move(1, 10, 10)

        actualizeLEDs()
        simSwitchThread() -- Don't waste too much time in here (simulation time will anyway only change in next thread switch)
    end

end

-- Put some initialization code here:
simSetThreadSwitchTiming(200) -- We will manually switch in the main loop
bodyElements=simGetObjectHandle('ePuck_bodyElements')
leftMotor=simGetObjectHandle('ePuck_leftJoint')
rightMotor=simGetObjectHandle('ePuck_rightJoint')
ePuck=simGetObjectHandle('ePuck')
ePuckBase=simGetObjectHandle('ePuck_base')
ledLight=simGetObjectHandle('ePuck_ledLight')
proxSens={-1,-1,-1,-1,-1,-1,-1,-1}
for i=1,8,1 do
    proxSens[i]=simGetObjectHandle('ePuck_proxSensor'..i)
end
maxVel=120*math.pi/180
ledColors={{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0}}

-- Braitenberg weights for the 4 front prox sensors (avoidance):
braitFrontSens_leftMotor={1,2,-2,-1}
-- Braitenberg weights for the 2 side prox sensors (following):
braitSideSens_leftMotor={-1,0}
-- Braitenberg weights for the 8 sensors (following):
braitAllSensFollow_leftMotor={-3,-1.5,-0.5,0.8,1,0,0,-4}
braitAllSensFollow_rightMotor={0,1,0.8,-0.5,-1.5,-3,-4,0}
braitAllSensAvoid_leftMotor={0,0.5,1,-1,-0.5,-0.5,0,0}
braitAllSensAvoid_rightMotor={-0.5,-0.5,-1,1,0.5,0,0,0}

-- Here we execute the regular thread code:
res,err=xpcall(threadFunction,function(err) return debug.traceback(err) end)
if not res then
    simAddStatusbarMessage('Lua runtime error: '..err)
end

-- Put some clean-up code here:

for i=1,9,1 do
    ledColors[i]={0,0,0} -- no light
end
actualizeLEDs()

reference

A Mobile Robot Solving a Virtual Maze Environment.pdf GENETIC-ALGORITHM SEEDING OF IDIOTYPIC NETWORKS FOR MOBILE-ROBOT NAVIGATION.pdf

e-puck-python.zip

mdecourse commented 6 years ago
Track = {}
Track.__index = Track

function Track.new(parent, name, roller_diam, depth, length, num_rollers)
    local self = setmetatable({}, Track)
    self.name = name
    self.roller_diam = roller_diam
    self.num_rollers = num_rollers

    self.body = nil
    self.roller_j = {}
    self.roller = {}

    self.roller_d = {}

    local color_mask = false
    local smooth = false

    if type(self.roller_diam) ~= 'table' then
        self.roller_diam = {self.roller_diam, self.roller_diam}
    end

    local min_d = math.min(self.roller_diam[1], self.roller_diam[2])
    self.body = simCreatePureShape(0, 2, {length, min_d, 0.999*depth}, 0.06)
    simSetObjectName(self.body, name)
    simSetObjectParent(self.body, parent, false)

    for i=1,num_rollers do
        self.roller_j[i] = simCreateJoint(sim_joint_revolute_subtype, sim_jointmode_force, 0, {depth, 0.02})
        simSetObjectName(self.roller_j[i], self.name .. '_r' .. i .. '_j')
        simSetObjectParent(self.roller_j[i], self.body, true)
        simSetObjectIntParameter(self.roller_j[i], 2000, 1)
        simSetObjectPosition(self.roller_j[i], self.body, {length * (-0.5 + (i - 1) / (self.num_rollers - 1)), 0, 0})
        simSetObjectOrientation(self.roller_j[i], self.body, {math.pi*0, 0, 0})

        self.roller_d[i] = ((i - 1) / (self.num_rollers - 1)) * (self.roller_diam[1] - self.roller_diam[2]) + self.roller_diam[2]
        self.roller[i] = simCreatePureShape(2, 2+(smooth and 4 or 0)+8, {self.roller_d[i], self.roller_d[i], depth}, 0.06)
        simSetObjectName(self.roller[i], self.name .. '_r' .. i)
        simSetObjectParent(self.roller[i], self.roller_j[i], false)
        local k = (i - 1) % 8
        simSetObjectIntParameter(self.roller[i], 3019, 2^k+(2^8-1)*2^8)
        simResetDynamicObject(self.roller[i])
        if color_mask then simSetShapeColor(self.roller[i], nil, sim_colorcomponent_ambient_diffuse, ({{1,0,0}, {0,1,0}, {0,0,1}, {1,1,0}, {1,0,1}, {0,0,0}, {0,1,1}, {1,1,1}})[1+k]) end
    end

    return self
end

function Track.setVelocity(self, vel)
    for i=1,self.num_rollers do
        simSetJointTargetVelocity(self.roller_j[i], 2 * vel / self.roller_d[i])
    end
end

TrackedRobot = {}
TrackedRobot.__index = TrackedRobot

function TrackedRobot.new(parent, name, body_size, hoff, joffx, joffz, track_length, flipper_length, track_diam, flipper_diam, track_depth, flipper_depth, num_rollers, body_track_spacing, track_flipper_spacing)
    local self = setmetatable({}, TrackedRobot)
    self.name = name
    self.track_diam = track_diam
    self.flipper_diam = flipper_diam
    self.num_rollers = num_rollers

    self.lr = {'l', 'r'}
    self.fr = {'f', 'r'}

    self.body = nil
    self.track_j = {}
    self.track = {}
    self.flipper_j = {{},{}}
    self.flipper = {{},{}}

    local static_root = false
    local flippers = true

    self.body = simCreatePureShape(0, 2+8+(static_root and 16 or 0), body_size, 0.006)
    simSetObjectName(self.body, self.name)

    local joffy = {0.5*(body_size[2]+track_depth)+body_track_spacing, -0.5*(body_size[2]+track_depth)-body_track_spacing}
    local toffy = {0.5*track_depth, -0.5*track_depth}
    local jrot = {math.pi, 0}
    for t=1,2 do
        self.track_j[t] = simCreateJoint(sim_joint_revolute_subtype, sim_jointmode_force, 0, {track_depth, 0.02})
        simSetObjectName(self.track_j[t], self.name .. '_t' .. self.lr[t] .. '_j')
        simSetObjectParent(self.track_j[t], self.body, false)
        simSetObjectPosition(self.track_j[t], self.body, {joffx,joffy[t],-0.5*body_size[3]-hoff+joffz})
        simSetObjectOrientation(self.track_j[t], self.body, {math.pi*0.5,jrot[t],0})
        simSetObjectIntParameter(self.track_j[t], 2000, 1)
        simSetJointForce(self.track_j[t], 20)

        self.track[t] = Track.new(self.track_j[t], self.name .. '_t' .. self.lr[t], track_diam, track_depth, track_length, num_rollers)

        local xoff = {-0.5*track_length, 0.5*track_length}
        local aoff = {0, math.pi}
        for f=1,(flippers and 2 or 0) do
            self.flipper_j[t][f] = simCreateJoint(sim_joint_revolute_subtype, sim_jointmode_force, 0, {track_depth+flipper_depth, 0.02})
            simSetObjectName(self.flipper_j[t][f], self.name .. '_t' .. self.lr[t] .. '_f' .. self.fr[f] .. '_j')
            simSetObjectParent(self.flipper_j[t][f], self.track[t].body, false)
            simSetObjectPosition(self.flipper_j[t][f], self.track[t].body, {xoff[f],0,track_flipper_spacing+0.5*track_depth+0.5*flipper_depth})
            simSetObjectOrientation(self.flipper_j[t][f], self.track[t].body, {0,0,0})
            simSetObjectIntParameter(self.flipper_j[t][f], 2000, 1)

            self.flipper[t][f] = Track.new(self.flipper_j[t][f], self.name .. '_t' .. self.lr[t] .. '_f' .. self.fr[f], {track_diam, flipper_diam}, flipper_depth, flipper_length, num_rollers)
            simSetObjectPosition(self.flipper[t][f].body, self.flipper_j[t][f], {-0.5*flipper_length, 0, 0})

            simSetJointPosition(self.flipper_j[t][f], aoff[f])
        end
    end

    simSetObjectParent(self.body, parent, true)

    local script = simAddScript(sim_scripttype_childscript)
    simAssociateScriptWithObject(script, self.body)
    simSetScriptText(script, [[
if (sim_call_type==sim_childscriptcall_initialization) then 
    left_track_j = simGetObjectHandle(']]..name..[[_tl_j')
    right_track_j = simGetObjectHandle(']]..name..[[_tr_j')
end
if (sim_call_type==sim_childscriptcall_actuation) then 
    local l = simGetJointPosition(left_track_j)
    local r = simGetJointPosition(right_track_j)
    local m = (l+r)/2
    simSetJointTargetVelocity(left_track_j, m-l)
    simSetJointTargetVelocity(right_track_j, m-r)
end
]])

    simSetModelProperty(self.body, 0)

    return self
end

function TrackedRobot.setVelocity(self, vel)
    local vs = {-1, 1}
    for t=1,2 do
        self.track[t]:setVelocity(vel[t]*vs[t])
        self.flipper[t][1]:setVelocity(vel[t]*vs[t])
        self.flipper[t][2]:setVelocity(vel[t]*vs[t])
    end
end
mdecourse commented 6 years ago
self=simGetObjectAssociatedWithScript(sim_handle_self)
--[[
    simInt simCreatePureShape(simInt primitiveType,simInt options,
const simFloat* sizes,simFloat mass,const simInt* precision)

primitiveType: 0 for a cuboid, 1 for a sphere, 2 for a cylinder and 3 for a cone
options: Bit-coded: if bit0 is set (1), backfaces are culled. If bit1 is set (2), 
edges are visible. If bit2 is set (4), the shape appears smooth. 
If bit3 is set (8), the shape is respondable. 
If bit4 is set (16), the shape is static. If bit5 is set (32), the cylinder has open ends
sizes: 3 values indicating the size of the shape
mass: the mass of the shape
precision: 2 values that allow specifying the number of sides and faces of a cylinder or sphere. 
Can be NULL for default values

]]
local ball1 = simCreatePureShape(1, 8, {0.2, 0, 0}, 0, nil)
simSetObjectPosition(ball1, self, {0.25 , 1 , 1})
simSetObjectName(ball1, "ball1")

local ball2 = simCreatePureShape(1, 8, {0.2, 0, 0}, 0, nil)
simSetObjectPosition(ball2, self, {-0.25 , 1 , 2})
simSetObjectName(ball2, "ball2")

另一範例:

-- Save current selection state:
currentSelection=simGetObjectSelection()
-- Copy an object:
simRemoveObjectFromSelection(sim_handle_all,-1)
simAddObjectToSelection(sim_handle_single,shapeHandle)
simCopyPasteSelectedObjects()
copy=simGetObjectSelection()[1] -- this is the copy
-- now change some parameters of the copy (e.g. its color):
simSetShapeColor(copy,nil,0,{0.67,0.16,0.16})
-- Restore the initial selection state:
simRemoveObjectFromSelection(sim_handle_all,-1)
simAddObjectToSelection(currentSelection)
Above code is taken from the demo scene blobDetectionWithPickAndPlace.ttt, from the child script attached to object partsProducer.

https://hp.kmol.info:8443/get_page/Lua%20Examples

mdecourse commented 6 years ago

list all the globals

local seen={}

function dump(t,i)
    seen[t]=true
    local s={}
    local n=0
    for k in pairs(t) do
        n=n+1 s[n]=k
    end
    table.sort(s)
    for k,v in ipairs(s) do
        print(i,v)
        v=t[v]
        if type(v)=="table" and not seen[v] then
            dump(v,i.."\t")
        end
    end
end

dump(_G,"")