Coder Social home page Coder Social logo

mdecourse / vecp2018 Goto Github PK

View Code? Open in Web Editor NEW
0.0 0.0 0.0 63.98 MB

車輛工程系大一計算機程式 (課程已經結束)

License: GNU Affero General Public License v3.0

HTML 0.14% Python 0.96% Lua 0.50% JavaScript 98.22% CSS 0.01% Makefile 0.01% C 0.16%

vecp2018's People

Contributors

eimada avatar kmolab avatar mdecourse avatar scrum-1 avatar

vecp2018's Issues

全球資訊網上的 Lua 程式應用

使用 Javascript 改寫 Lua 解譯器之後

請將下列六行程式複製到 http://mde.tw/vecp2018/lua/SchoolProject/ 中執行:

-- 利用 dofile() 從網頁中取得 star() 函式的定義, 並對應到 local 變數 star 中
local star = dofile("https://raw.githubusercontent.com/mdecourse/vecp2018_1/gh-pages/w5/star.lua").star
-- 有了 star 的函式定義, 可以直接以內定值呼叫 star() 執行繪圖
star()
-- 也可以修改輸入變數, 執行其他繪圖
star(100, 300, 300, 6, "rgb(200,0,0)", "rgb(0,0,200)", "stroke", 45 )

一旦計算機程式進入全球資訊網, 全球協同成為可行模式之一

上述位於 Github 倉儲的 star.lua 程式碼寫法如下:

-- 導入 "js" 模組
local js = require "js"
-- global 就是 javascript 的 window
local global = js.global
local document = global.document
-- html 檔案中 canvas id 設為 "canvas"
local canvas = document:getElementById("canvas")
-- 將 ctx 設為 canvas 2d 繪圖畫布變數
local ctx = canvas:getContext("2d")

-- 乘上 deg 可轉為徑度單位
deg = math.pi / 180

-- 建立多邊形定點位置畫線函式
local function star(radius, xc, yc, n, fs, ss, fors, theta)
    radius = radius or 50
    xc = xc or 100
    yc = yc or 100
    n = n or 5
    -- 屬性呼叫使用 . 而方法呼叫使用 :
    -- 填色屬性
    fs = fs or "rgb(200,0,0)"
    -- 畫筆顏色屬性
    ss = ss or "rgb(0,0,200)"
    -- 內定為填色
    fors = fors or "fill"
    -- 旋轉角度
    theta = theta or 0
    ctx.fillStyle = fs
    ctx.strokeStyle = ss
    xi = xc + radius*math.cos((360/n)*deg+(90+theta)*deg)
    yi = yc - radius*math.sin((360/n)*deg+(90+theta)*deg)
    ctx:beginPath()
    ctx:moveTo(xi,yi)
    for i = 2, n+1 do
        x = xc + radius*math.cos((360/n)*deg*i+(90+theta)*deg)
        y = yc - radius*math.sin((360/n)*deg*i+(90+theta)*deg)
        ctx:lineTo(x,y)
    end
    ctx:closePath()
    if fors == "fill" then
        ctx:fill()
    else
        ctx:stroke()
    end
end

return {
    star = star;
}

概念與知識設計理論說: 概念與知識都是數學理論中的集合

意即, 個人或團隊都可以擁有無限多的概念 (concept) 與無限多的知識 (knowledge), 學習的目的在確認個人或團隊的口袋中 ,到底有哪些概念? 有哪些知識? 簡單說 ,概念就是想法, 而且是當下還沒有知識可以落實的想法, 至於知識, 則是可以用來解決問題, 得到結果的方法集合.

只是, 概念與知識雖然可以無限多, 但工程師解題的過程卻通常有時效限制, 有可用資源的限制, 而且概念與知識都是時間的函式, 也就是說, 先前所獲得的知識, 在空間與時間的遷流變化下, 有可能無法真正落實解題, 而流為概念, 而先前的概念, 在相關知識與實際解題過程, 予以充實後, 可以累積許多有用的知識, 另外, 概念除了透過解題驗證, 可以成為知識外, 也可以觸發出更多概念, 反之, 知識一旦透過證實, 在當下無法解題, 就成為概念之外, 也可以將知識用於解題的過程中, 產生許多其他有用的知識, 相關轉換表示如下:

概念->概念 - 概念可以激發更多概念
知識->知識 - 知識的運用可以獲得更多知識
概念->知識 - 概念的落實當下 ,可以產生知識
知識->概念 - 知識用於解題當下, 若發現已不適用, 則成為概念

段落結論:

假如知識無窮多, 沒有落實不了的概念
產生概念雖不可受限, 但腳踏實地則需要能夠解決問題的知識

可以天馬行空, 同時也要腳踏實地

期末考驗證

請在各組的雲端系統, 以線條畫上自己的學號最後兩個數字, 然後推到雲端系統後, 將連結回報至下方回應處.

分組期末報告

下載期末報告專用隨身系統 (無需自行升級, 請直接下載升級後的版本)

kmol2_1805 - 7z 檔案 340 MB, 解壓縮後 1.2 GB, 用法與 kmol_level1 相同.

kmol2_1805 可攜系統中 V-rep 3.5 rev4 缺少兩個 dll:
vrep350_rev4_vcdlls.zip

建立各組的 heroku 雲端網站

各組組長必須利用 Gmail 帳號, 至 https://www.heroku.com/ 登記一組帳號, 並建立一個雲端應用程式, 應用程式名稱請選 vecp_組長學號, 之後所完成的雲端網站網址將為: https://vecp_組長學號.herokuapp.com

建立 heroku 帳號前, 必須先登入對應的 Gmail 帳號, heroku 帳號建立後, 必須在 Gmail 帳號中驗證後才可繼續使用 heroku 功能.

以 2015fallhw AT Gmail 郵箱建立的雲端範例網站為: http://a2015fallhw.herokuapp.com

步驟一: 建立 heroku 應用程式

確定各組已經利用組長 Gmail 郵箱登記 https://www.heroku.com/ 帳號後, 隨後登入 heroku 的帳號即為組長的 Gmail 郵箱, 密碼則為登記時所自選包含至少一個特殊符號的密碼.

假設第一組組長學號為 40628423, 且 Gmail 為 [email protected], 則預計建立的雲端應用程式名稱為 vecp-40628423, 之後設定完成的雲端網站將為: https://vecp-40628423.herokuapp.com

步驟二: 啟動 kmol2_1805 隨身系統

在命令列中輸入:

heroku login

系統將會回應 (以 [email protected] 帳號登入為例) :

Enter your Heroku credentials:
Email: [email protected]
Password: ********************
Logged in as [email protected]

登入後, heroku 會將登入資料存入 y:\home_netrc 檔案中, 之後對遠端 heroku 的對應倉儲進行操作

git clone 之前所建立的 heroku 應用程式倉儲

當組長在 heroku 建立應用程式後, 進入 settings 中下方複製該應用程式對應的 git 網址, 例如: 與 vecp-40628423 應用程式對應的 git 網址為: https://git.heroku.com/vecp-40628423.git

在近端隨身系統的命令列中, 更換目錄到 y:\tmp 目錄中, 以 git clone 指令, 取下遠端 heroku 應用程式的倉儲內容 (一開始為空倉儲):

git clone https://git.heroku.com/vecp-40628423.git 

所取下的倉儲會以 vecp-40628423 目錄存檔, 空倉儲目錄中, 只會有一個 .git 目錄.

接下來, 以命令列, 在 y:\tmp 目錄中執行下列指令, 取下分組期末專案網站樣本:

git clone https://github.com/mdecourse/a2015fallhw

完成後, y:\tmp 目錄中會增加 vecp-40628423 目錄 (也就是各組所建立的 heroku 應用程式名稱), 以及 a2015fallhw 等兩個目錄, 接下來刪除 a2015fallhw 目錄中的 .git 目錄後, 將其餘的檔案複製到 vecp-40628423 目錄中.

將近端 vecp-40628423 目錄中的倉儲提交推送到雲端

接下來要操作 git 指令, 將各組 vecp-40628423 應用程式的近端內容, 提交推送到雲端, 這時需要先設定 git 使用者的對應資料, 必須在命令列中執行下列指令 (可以在 y:\tmp\vecp-40628423 目錄中執行):

git config --global user.email "[email protected]"
git config --global user.name "40628423"

完成上述指令後, git 會將身分設定資料存入 y:\home.gitconfig 檔案中.

接下來, 在 y:\tmp\vecp-40628423 目錄中執行:

git add .
git commit -m "initial add"
git push

以上指令執行後, 若一切正常, 遠端的 heroku 雲端系統將會啟動對應的應用程式, 完成後, 使用者就可以利用瀏覽器連線到: https://vecp-40628423.herokuapp.com 檢視所建立的雲端系統, 其內容將與 http://a2015fallhw.herokuapp.com 相同.

由於使用的 heroku 雲端帳號屬於免收費類別, 因此每月只提供約 20 天的連線用量, 若一定時間無連線需求, 雲端主機便會自動關機, 等待有連結要求時, 再同步開機, 而且若使用者透過瀏覽器更改網站內容, 每隔幾十分鐘網站內容將會自動回復為原始倉儲中的版本.

vecp-week7

解讀 Lua 程式

以下的 Lua 程式碼, 用來控制 https://github.com/mdecourse/vecp2018/blob/gh-pages/v-rep/YouBot_interactive.ttt, 是 V-rep 平台中的一個場景, 請試著從中找出已經看得懂的程式語法.

function deg2rad(deg)
   return deg*math.pi/180
end

function rad2deg(rad)
   return rad*180/math.pi
end

function nullFunc(arg)
   return arg
end

function toggleRadians(ui,id,newVal)
   if (newVal==2) then
      angleTo = deg2rad
      angleFrom = rad2deg
   else
      angleTo = nullFunc
      angleFrom = nullFunc
   end	  
end

applyJoints=function(jointHandles,joints)
    for i=1,#jointHandles,1 do
        simSetJointPosition(jointHandles[i],joints[i])
    end
end

getJoints=function(jointHandles,angles)
   angles={0,0,0,0,0,0,0,0}
   for i=1,#jointHandles,1 do
      angles[i]=simGetJointPosition(jointHandles[i])
   end
   return angles
end

updateActualText=function(jointHandles, angles)
   local ang_str="( "
   for i=1,#jointHandles,1 do
      -- simExtCustomUI_setLabelText(ui,5000+i,string.format('Actual = %+6.3f', angles[i]))
      ang_str=ang_str..string.format("%6.3f, ", angles[i])
   end
   ang_str=string.sub(ang_str,1,-3)
   ang_str=ang_str.."  )"
   simExtCustomUI_setLabelText(ui,1237,ang_str)
end

function changeAllSliders(ui,q)
   for i=1,#jh,1 do
      simExtCustomUI_setSliderValue(ui,4000+i,q[i]*1000)
      simExtCustomUI_setLabelText(ui,3000+i,string.format('Reference = %+6.3f',q[i]))
   end
end

function sliderChange(ui,id,newVal)
   for i=1,#jh,1 do
      if (id==4000+i) then
         simExtCustomUI_setLabelText(ui,3000+i,string.format('Reference = %+6.3f',newVal/1000))
         ref_ang[i]=newVal/1000
         break
      end
   end
end

function mysplit(inputstr, sep)
   if sep == nil then
      sep = "%s"
   end
   local t={}
   for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
      table.insert(t,str)
   end
   return t
end

function fulljointEntry(ui,id,newVal)
   if (newVal==enteredString) then
      enteredString=newVal
      out=string.format("Already processed and sent this string... try editing")
      simExtCustomUI_setLabelText(ui, 1236, out)
      return
   end
   enteredString=newVal
   local q=mysplit(newVal,",")
   local out=""
   if (#q==0) then
      out=string.format("No conversions completed <br> Are you separating with commas?")
      simExtCustomUI_setLabelText(ui, 1236, out)
      return
   elseif (#q<#jh) then
      out=string.format("Not enough configuration variables specified")
      simExtCustomUI_setLabelText(ui, 1236, out)
      return
   elseif (#q>#jh) then
      out=string.format("Too many configuration variables specified")
      simExtCustomUI_setLabelText(ui, 1236, out)
      return
   end
   for i=1,#q,1 do
      qtest=tonumber(q[i])
      if (qtest==nil) then
         out=string.format("Could not convert entry number %d:<br> Entered='",i,q[i])..q[i].."'"
         simExtCustomUI_setLabelText(ui, 1236, out)
         return
      else
         q[i]=qtest
      end
   end
   simExtCustomUI_setLabelText(ui, 1236, "Successful conversion:<br>"..newVal)
   changeAllSliders(ui, q)
   ref_ang = q
end


function jointEntry(ui,id,newVal)
   angle = tonumber(newVal)
   if (angle==nil) then
      print("Could not convert number..."..newVal)
      return
   end
   -- if (angle >= 2*math.pi) then
   -- 	  print("Clipping angle to 2pi")
   -- 	  angle = 2*math.pi
   -- end
   -- if (angle <= -2*math.pi) then
   -- 	  print("Clipping angle to -2pi")
   -- 	  angle = -2*math.pi
   -- end
   for i=1,#jh,1 do
      if (id==7000+i) then
         simExtCustomUI_setLabelText(ui,3000+i,string.format('Reference = %+6.3f',angle))
         ref_ang[i]=angle
         changeAllSliders(ui, ref_ang)
         break
      end
   end
end


function eul2so3_xyzr(a,b,c)
   -- Build empty array
   R = {}
   for i=1,3,1 do
      R[i] = {}
      for j=1,3,1 do
         R[i][j] = 0
      end
   end

   -- calculate constants
   local c1,s1 = math.cos(a),math.sin(a)
   local c2,s2 = math.cos(b),math.sin(b)
   local c3,s3 = math.cos(c),math.sin(c)
   
   -- fill out values:
   -- https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
   R[1][1] = c2*c3
   R[1][2] = -c2*s3
   R[1][3] = s2
   R[2][1] = c1*s3+c3*s1*s2
   R[2][2] = c1*c3-s1*s2*s3
   R[2][3] = -c2*s1
   R[3][1] = s1*s3-c1*c3*s2
   R[3][2] = c3*s1+c1*s2*s3
   R[3][3] = c1*c2
   return R
end

function so3andp2se3(R,p)
   local g = {}
   for i=1,4,1 do
      g[i] = {}
      for j=1,4,1 do
         if (i<=3) then
            if (j<=3) then
               g[i][j] = R[i][j]
            else
               g[i][j] = p[i]
            end
         else
            if (j<=3) then
               g[i][j] = 0
            else
               g[i][j] = 1
            end
         end
      end
   end
   return g
end

function createSE3string(pos,ori)
   -- first let's convert the orientation into an SE(3) matrix:
   local R=eul2so3_xyzr(ori[1], ori[2], ori[3])
   local g=so3andp2se3(R,pos)
   local out="<b><big><tt>"
   for i=1,4,1 do
      out=out.."| "
      for j=1,4,1 do
         out=out..string.format(" %+6.3f",g[i][j])
      end
      out=out.." | <br>"
   end
   out=out.."</tt></big></b>"
   return out
end

function calcSE3(ui, id, target, reference)
   pos=simGetObjectPosition(target,reference)
   ori=simGetObjectOrientation(target,reference)
   if pos and ori then
      out=createSE3string(pos,ori)
      simExtCustomUI_setLabelText(ui,id,out)
   end
end

function closeEventHandler(h)
    simAddStatusbarMessage('Window '..h..' is closing...')
    simExtCustomUI_hide(h)
end

if (sim_call_type==sim_childscriptcall_initialization) then
   -- joint limits:
   --    X: (-2, 2)
   --    Y: (-2, 2)
   --   Th: (-360, 360)
   --    1: (-168, 168)
   --    2: (-64, 89)
   --    3: (-150, 145)
   --    4: (-102, 102)
   --    5: (-160, 160)
   
   xml = [[
   <ui closeable="false" onclose="closeEventHandler" resizable="true">
     <tabs>

       <tab title="Enter Config and SE(3) Value">
         <group>
           <group layout="vbox">
             <label text="<big> Configuration Entry:</big>" wordwrap="false" style="font-weight: bold;"/>
             <label text="Enter 8 comma-separated angles" />
             <edit value="" oneditingfinished="fulljointEntry" />
             <label value="" wordwrap="false" />
             <label text="<big> Current configuration:</big>" wordwrap="false" style="font-weight: bold;"/>
             <group layout="vbox">
               <label value="" id="1237" wordwrap="true" />
             </group>
             <label text="<big> Messages:</big>" wordwrap="false" style="font-weight: bold;"/>
             <group layout="vbox">
               <label value="" id="1236" wordwrap="true" />
             </group>
           </group>
           <group>
             <!-- <group> -->
               <label text="<big> Current SE(3) (world to end-effector):</big>" wordwrap="false" style="font-weight: bold;"/>
               <!-- <button text="Calculate SE(3) transform:" onclick="calcSE3" id="1235"/> -->
               <label text="T(θ) = " wordwrap="false" />
               <label text="" id="1234" wordwrap="false" />
             <!-- </group> -->
              <!-- <group> -->
              <!--   <label text="<big> Settings:</big>" wordwrap="false" style="font-weight: bold;"/> -->
              <!--   <checkbox text="Use degrees instead of radians?" checked="false" onchange="toggleRadians" /> -->
              <!-- </group> -->
           </group>
           <group>
             <!-- <group> -->
               <label text="<big> Current SE(3) (world to youBot base):</big>" wordwrap="false" style="font-weight: bold;"/>
               <!-- <button text="Calculate SE(3) transform:" onclick="calcSE3" id="1235"/> -->
               <label text="T(θ) = " wordwrap="false" />
               <label text="" id="1235" wordwrap="false" />
             <!-- </group> -->
              <!-- <group> -->
              <!--   <label text="<big> Settings:</big>" wordwrap="false" style="font-weight: bold;"/> -->
              <!--   <checkbox text="Use degrees instead of radians?" checked="false" onchange="toggleRadians" /> -->
              <!-- </group> -->
           </group>
           <stretch />
         </group>
       </tab>

       
       <tab title="Joint Angle Sliders">
         <group layout="grid" >
           <group>
             <group layout="grid">
               <label text="<big> Chassis X:</big>" id="6001" wordwrap="false" style="font-weight: bold;"/>
               <label text="Reference =  0.000" id="3001" wordwrap="false" />
               <!-- <label text="Actual =  0.000" id="5001" wordwrap="false" /> -->
             </group>
             <hslider id="4001" tick-position="above" tick-interval="333" minimum="-2000" maximum="2000" onchange="sliderChange" />
             <group layout="grid">
               <label text="Enter position (m):" />
               <edit value="" id="7001" oneditingfinished="jointEntry" />
             </group>
           </group>
           
           <group>
             <group layout="grid">
               <label text="<big> Arm 2:</big>" id="6005" wordwrap="false" style="font-weight: bold;"/>
               <label text="Reference =  0.000" id="3005" wordwrap="false" />
               <!-- <label text="Actual =  0.000" id="5005" wordwrap="false" /> -->
             </group>
             <hslider id="4005" tick-position="above" tick-interval="222" minimum="-1117" maximum="1553" onchange="sliderChange" />
             <group layout="grid">
               <label text="Enter joint angle (rad):" />
               <edit value="" id="7005" oneditingfinished="jointEntry" />
             </group>
           </group>
           
           <br/>
           
           <group>
             <group layout="grid">
               <label text="<big> Chassis Y:</big>" id="6002" wordwrap="false" style="font-weight: bold;"/>
               <label text="Reference =  0.000" id="3002" wordwrap="false" />
               <!-- <label text="Actual =  0.000" id="5002" wordwrap="false" /> -->
             </group>
             <hslider id="4002" tick-position="above" tick-interval="333" minimum="-2000" maximum="2000" onchange="sliderChange" />
             <group layout="grid">
               <label text="Enter position (m):" />
               <edit value="" id="7002" oneditingfinished="jointEntry" />
             </group>
           </group>

           
           <group>
             <group layout="grid">
               <label text="<big> Arm 3:</big>" id="6006" wordwrap="false" style="font-weight: bold;"/>
               <label text="Reference =  0.000" id="3006" wordwrap="false" />
               <!-- <label text="Actual =  0.000" id="5006" wordwrap="false" /> -->
             </group>
             <hslider id="4006" tick-position="above" tick-interval="429" minimum="-2620" maximum="2530" onchange="sliderChange" />
             <group layout="grid">
               <label text="Enter joint angle (rad):" />
               <edit value="" id="7006" oneditingfinished="jointEntry" />
             </group>
           </group>

           <br/>
           
           <group>
             <group layout="grid">
               <label text="<big> Chassis �:</big>" id="6003" wordwrap="false" style="font-weight: bold;"/>
               <label text="Reference =  0.000" id="3003" wordwrap="false" />
               <!-- <label text="Actual =  0.000" id="5003" wordwrap="false" /> -->
             </group>
             <hslider id="4003" tick-position="above" tick-interval="523" minimum="-3140" maximum="3140" onchange="sliderChange" />
             <group layout="grid">
               <label text="Enter joint angle (rad):" />
               <edit value="" id="7003" oneditingfinished="jointEntry" />
             </group>
           </group>

           <group>
             <group layout="grid">
               <label text="<big> Arm 4:</big>" id="6007" wordwrap="false" style="font-weight: bold;"/>
               <label text="Reference =  0.000" id="3007" wordwrap="false" />
               <!-- <label text="Actual =  0.000" id="5007" wordwrap="false" /> -->
             </group>
             <hslider id="4007" tick-position="above" tick-interval="296" minimum="-1780" maximum="1780" onchange="sliderChange" />
             <group layout="grid">
               <label text="Enter joint angle (rad):" />
               <edit value="" id="7007" oneditingfinished="jointEntry" />
             </group>
           </group>

           <br/>
           
           <group>
             <group layout="grid">
               <label text="<big> Arm 1:</big>" id="6004" wordwrap="false" style="font-weight: bold;"/>
               <label text="Reference =  0.000" id="3004" wordwrap="false" />
               <!-- <label text="Actual =  0.000" id="5004" wordwrap="false" /> -->
             </group>
             <hslider id="4004" tick-position="above" tick-interval="488" minimum="-2932" maximum="2932" onchange="sliderChange" />
             <group layout="grid">
               <label text="Enter joint angle (rad):" />
               <edit value="" id="7004" oneditingfinished="jointEntry" />
             </group>
           </group>

           <group>
             <group layout="grid">
               <label text="<big> Arm 5:</big>" id="6008" wordwrap="false" style="font-weight: bold;"/>
               <label text="Reference =  0.000" id="3008" wordwrap="false" />
               <!-- <label text="Actual =  0.000" id="5008" wordwrap="false" /> -->
             </group>
             <hslider id="4008" tick-position="above" tick-interval="481" minimum="-2890" maximum="2890" onchange="sliderChange" />
             <group layout="grid">
               <label text="Enter joint angle (rad):" />
               <edit value="" id="7008" oneditingfinished="jointEntry" />
             </group>
           </group>
           
         </group>
         
       </tab>

    </tabs>
</ui>
]]
    ui=simExtCustomUI_create(xml)
    -- get joints:
    jh={-1,-1,-1,-1,-1,-1,-1,-1}
    jh[1]=simGetObjectHandle('World_X_Joint')
    jh[2]=simGetObjectHandle('World_Y_Joint')
    jh[3]=simGetObjectHandle('World_Th_Joint')
    for i=4,#jh,1 do
       jh[i]=simGetObjectHandle('Joint'..(i-3))
    end
    -- for i=1,#jh,1 do
    --    print(string.format("jh[%d] = %d",i,jh[i]))
    -- end
    -- base=simGetObjectHandle('ArmBase_Frame')
    world=simGetObjectHandle('WorldFrame')
    base=simGetObjectHandle('BaseVisual')
    ee=simGetObjectHandle('EE_Frame')
    -- fill out initial string:
    calcSE3(ui, 1234, ee, world)
    calcSE3(ui, 1235, base, world)
    -- array for reference angle
    ref_ang={0,0,0,0,0,0,0,0}
    -- array for storing when angles where manually entered
    enteredString=""
    -- angle parsing
    toggleRadians(ui,0,0)
end


if (sim_call_type==sim_childscriptcall_actuation) then
   applyJoints(jh, ref_ang)
   -- array for actual angle
   act_ang = getJoints(jh)
   updateActualText(jh, act_ang)
   calcSE3(ui, 1234, ee, world)
   calcSE3(ui, 1235, base, world)
end

if (sim_call_type==sim_childscriptcall_sensing) then
   
end

if (sim_call_type==sim_childscriptcall_cleanup) then
    simExtCustomUI_destroy(ui)
end

Lua 5.0 程式語言簡介

參考資料

https://www.lua.org/pil/1.html

從 print() 開始

print("Hello World")

大家可以利用 http://mde.tw/vecp2018/lua/SchoolProject/ 練習簡單的 Lua 程式, 接下來練習函式的定義與在瀏覽器中取使用者輸入的用法:

-- 導入 js 模組
js = require("js")
-- 取得 window
window = js.global

-- defines a factorial function
function fact (n)
  if n == 0 then
    return 1
  else
    return n * fact(n-1)
  end
end

-- 利用 window:prompt 方法回應取得使用者的輸入
a = window:prompt("enter a number:")
print(fact(a))

Lua lesson1

Lua 程式: https://github.com/mdecourse/vecp2018/blob/gh-pages/lua/lesson1_traffic_light.lua

V-rep 配合場景: https://github.com/mdecourse/vecp2018/blob/gh-pages/lua/lesson1_workingTrafficLight.ttt

學習目標:

  1. 下載簡易版隨身程式系統, 其中包含 SciTE 程式編輯與執行器, Lua 解譯器, Python 解譯器, 以及 V-rep 教育版程式套件, Fossil SCM, 以及 Solvespace 參數式繪圖程式, Zoomit, ShareX, ProcessExplorer, 並且學習如何使用 USB 上的隨身程式系統.

  2. 學習簡單的 Lua 程式語法, 並利用 V-rep 模擬系統練習各種語法的應用.

  3. 學習如何利用 Fossil SCM 在隨身碟中建立倉儲, 如何管理自己的計算機程式倉儲.

  4. 學習如何利用 Solvespace 建立簡單的 3D 幾何物件後, 輸入 V-rep 中, 並以 Lua 程式控制.

  5. 學習如何利用 ShareX 錄製 Windows 環境下的練習操作影片, 並如何將這些影片上傳到 Youtube.

W16

缺席

40628448
40628438
40628450
40628422
40628456

更新 vecp2018

https://github.com/mdecourse/wcms-scrum3http://wcms-scrum3.herokuapp.com/ 雲端網站的同步倉儲, http://wcms-scrum3.herokuapp.com/lua2 中所採行的 Lua 程式碼呼叫方式與原先 vecp2018 SchoolProject 中的方法不同, 是將所有的程式放在同一個檔案中, 而最新的做法則是分開各程式檔案, 並且可以直接以 URL 連結, 到特定目錄中擷取.

這裡要將最新作法從雲端移植到 Github Pages 中. 完成後:

http://mde.tw/vecp2018/lua/SchoolProject

可以透過 http://mde.tw/vecp2018/lua/SchoolProject/?filename=gear1 將 ext/gear1.lua 的內容執行後 (採用同名 function return 程式內容).

vecp-week1

上課時段:

ven1a - 2018/03/02 起每週 (五) 晚上 10-11 節 (18:30-20:00)

課程介紹: 本課程名稱為計算機程式, 將分別利用 Lua, Python 與 C 說明計算機程式的基本功能.

第一週準備工作: 請各學員利用學校配發的 Gmail 帳號, 申請 Github 帳號, 登入 Github 後, 連結至 https://github.com/mdecourse/vecp2018/issues 後, 了解如何在各週上課內容 issues 中提出問題、心得與相關討論議題.

第一週課程內容

初步了解 Lua 程式語言

http://lua-users.org/wiki/ScopeTutorial

參考資料:

Python 機器人控制:
. https://github.com/clamesc/Training-Neural-Networks-for-Event-Based-End-to-End-Robot-Control
Lua 程式設計教學:
. Lua 簡介
. https://cwchen.tw/lua-prog/
. http://www.runoob.com/lua/lua-tutorial.html
. 從 Javascript 學 Lua
. http://lua-users.org/wiki/ObjectOrientationTutorial
Lua 應用:
. 基因演算與類神經網路應用
. 靜態網頁轉檔
V-rep 建模:
. https://www.jianshu.com/p/eb3f38c0c5fa
. https://github.com/Johan944/Need4Stek
. https://github.com/eligoldweber/DriverlessCarSimulation
. https://github.com/jokla/vrep_car_simulation
. https://github.com/Eric-Gonzalez/car-simulator
. https://github.com/AgrawalAmey/rl-car

碰撞檢測:
. https://github.com/vrld/HC

計算機程式相關工具

編譯 Lua

利用 https://www.msys2.org/ 編譯從 https://www.lua.org/download.html 下載的原始碼

mingw32-make mingw

就可以編譯取得 lua.exe, luac.exe 與 liblua.a, 接下來利用 SciTE 的 lua.properties 設定, 可以解譯 .lua 程式:

lua.properties 檔案:

# Define SciTE settings for Lua files.
# Lua 5.x highlighting is enabled by default. For other choices,
# including Lua 4 highlighting, see below.

file.patterns.lua=*.lua

*source.patterns.lua=$(file.patterns.lua);

shbang.lua=lua

filter.lua=Lua (lua)|$(file.patterns.lua)|

*filter.lua=$(filter.lua)

lexer.$(file.patterns.lua)=lua

*language.lua=Lu&a|lua||

word.chars.lua=$(chars.alpha)$(chars.numeric)$(chars.accented)_%
word.characters.$(file.patterns.lua)=$(word.chars.lua)

#### Lua 4.0.1

# Keywords
keywordclass.lua4=\
and break do else elseif \
end for function if in \
local nil not or repeat \
return then until while

# Basic Functions
keywordclass2.lua4=\
_ALERT assert call collectgarbage copytagmethods \
dofile dostring error foreach foreachi \
gcinfo getglobal getn gettagmethod globals \
newtag next print rawget rawset \
setglobal settag settagmethod sort tag \
tonumber tostring tinsert tremove type \
_VERSION _ERRORMESSAGE

# String Manipulation & Mathematical Functions
keywordclass3.lua4=\
strbyte strchar strfind strlen strlower \
strrep strsub strupper format gsub \
abs acos asin atan atan2 \
ceil cos deg exp floor \
log log10 max min mod \
rad sin sqrt tan frexp \
ldexp random randomseed PI

# Input and Output Facilities & System Facilities
keywordclass4.lua4=\
openfile closefile readfrom writeto appendto \
remove rename flush seek tmpname read write \
clock date execute exit getenv setlocale \
_INPUT _OUTPUT _STDIN _STDOUT _STDERR -- file descriptors

# Debug (not enabled by default)
keywordclass5.lua4=\
getinfo getlocal setlocal setcallhook setlinehook

#### Lua 5.0.3

# Keywords
keywordclass.lua50=$(keywordclass.lua4) false true

# Basic Functions
keywordclass2.lua5=\
assert collectgarbage dofile error _G \
getmetatable ipairs loadfile next pairs \
pcall print rawequal rawget rawset \
setmetatable tonumber tostring type _VERSION \
xpcall string table math coroutine io os debug

keywordclass2.lua50=$(keywordclass2.lua5) \
getfenv gcinfo loadlib loadstring require \
setfenv unpack _LOADED LUA_PATH _REQUIREDNAME

# String, Table , Mathematical, Bitwise
keywordclass3.lua5=\
string.byte string.char string.dump string.find string.format \
string.gsub string.len string.lower string.rep string.sub string.upper \
table.concat table.insert table.remove table.sort \
math.abs math.acos math.asin math.atan math.atan2 \
math.ceil math.cos math.deg math.exp math.floor \
math.frexp math.ldexp math.log math.max math.min \
math.pi math.pow math.rad math.random math.randomseed \
math.sin math.sqrt math.tan

keywordclass3.lua50=$(keywordclass3.lua5) \
string.gfind \
table.foreach table.foreachi table.getn table.setn \
math.mod math.log10

# Coroutine, Input/Output, System, Package
keywordclass4.lua50=\
coroutine.create coroutine.resume coroutine.status coroutine.wrap coroutine.yield \
io.close io.flush io.input io.lines io.open \
io.output io.read io.tmpfile io.type io.write \
io.stdin io.stdout io.stderr \
os.clock os.date os.difftime os.execute os.exit \
os.getenv os.remove os.rename os.setlocale os.time \
os.tmpname

# Debug (not enabled by default)
keywordclass5.lua50=\
debug.debug debug.gethook debug.getinfo debug.getlocal debug.getupvalue \
debug.setlocal debug.setupvalue debug.sethook debug.traceback

#### Lua 5.1.4

# Keywords
keywordclass.lua51=$(keywordclass.lua50)

# Basic Functions
keywordclass2.lua51=$(keywordclass2.lua5) \
getfenv load loadstring select \
setfenv unpack package

# String, Table , Mathematical, Bitwise
keywordclass3.lua51=$(keywordclass3.lua5) \
string.gmatch string.match string.reverse \
table.maxn \
math.cosh math.fmod math.huge math.log10 math.modf \
math.sinh math.tanh

# Coroutine, Input/Output, System, Package
keywordclass4.package=\
require package.cpath package.loaded \
package.loadlib package.path package.preload

keywordclass4.lua51=$(keywordclass4.lua50) \
coroutine.running io.popen \
module package.loaders package.seeall \
$(keywordclass4.package)

# Debug (not enabled by default)
keywordclass5.lua51=$(keywordclass5.lua50) \
debug.getfenv debug.getmetatable debug.getregistry debug.setfenv debug.setmetatable

#### Lua 5.2.0

# Keywords
keywordclass.lua52=$(keywordclass.lua50) goto

# Basic Functions
keywordclass2.lua52=$(keywordclass2.lua5) \
load rawlen select package bit32 _ENV

# String, Table , Mathematical, Bitwise
keywordclass3.bit32=\
bit32.arshift bit32.band bit32.bnot bit32.bor bit32.btest \
bit32.bxor bit32.extract bit32.replace bit32.lrotate bit32.lshift \
bit32.rrotate bit32.rshift

keywordclass3.lua52=$(keywordclass3.lua5) \
string.gmatch string.match string.reverse \
table.pack table.unpack \
math.cosh math.fmod math.huge math.modf math.sinh math.tanh \
$(keywordclass3.bit32)

# Coroutine, Input/Output, System, Package
keywordclass4.lua52=$(keywordclass4.lua50) \
coroutine.running io.popen \
package.config package.searchers package.searchpath \
$(keywordclass4.package)

# Debug (not enabled by default)
keywordclass5.lua52=$(keywordclass5.lua50) \
debug.getmetatable debug.getregistry debug.setmetatable \
debug.getuservalue debug.setuservalue debug.upvalueid debug.upvaluejoin

#### Lua 5.3.0

# Keywords
keywordclass.lua53=$(keywordclass.lua50) goto

# Basic Functions
keywordclass2.lua53=$(keywordclass2.lua5) \
load rawlen select package utf8 _ENV

# String, UTF8, Table , Mathematical
keywordclass3.utf8=\
utf8.char utf8.charpattern utf8.codes \
utf8.codepoint utf8.len utf8.offset

keywordclass3.lua53=\
string.byte string.char string.dump string.find string.format \
string.gmatch string.gsub string.len string.lower string.match \
string.pack string.packsize string.rep \
string.reverse string.sub string.unpack string.upper \
table.concat table.insert table.move table.pack \
table.remove table.sort table.unpack \
math.abs math.acos math.asin math.atan \
math.ceil math.cos math.deg math.exp \
math.floor math.fmod math.huge \
math.log math.max math.maxinteger math.min math.mininteger \
math.modf math.pi math.rad \
math.random math.randomseed math.sin \
math.sqrt math.tan \
math.tointeger math.type math.ult \
$(keywordclass3.utf8)

# Coroutine, Input/Output, System, Package
keywordclass4.lua53=$(keywordclass4.lua50) \
coroutine.isyieldable coroutine.running io.popen \
package.config package.searchers package.searchpath \
$(keywordclass4.package)

# Debug (not enabled by default)
keywordclass5.lua53=$(keywordclass5.lua52)

#### Lua 5.x

# Keywords
keywordclass.lua5x=$(keywordclass.lua53)

# Basic Functions
keywordclass2.lua5x=$(keywordclass2.lua5) \
getfenv gcinfo load loadlib loadstring \
require select setfenv unpack \
_LOADED LUA_PATH _REQUIREDNAME \
package rawlen package bit32 utf8 _ENV

# String, Table , Mathematical, Bitwise
keywordclass3.lua5x=$(keywordclass3.lua5) \
string.gfind string.gmatch string.match string.reverse \
string.pack string.packsize string.unpack \
table.foreach table.foreachi table.getn table.setn \
table.maxn table.pack table.unpack table.move \
math.cosh math.fmod math.huge math.log10 math.modf \
math.mod math.sinh math.tanh math.maxinteger math.mininteger \
math.tointeger math.type math.ult \
$(keywordclass3.bit32) \
$(keywordclass3.utf8)

# Coroutine, Input/Output, System, Package
keywordclass4.lua5x=$(keywordclass4.lua50) \
coroutine.isyieldable coroutine.running io.popen \
module package.loaders package.seeall \
package.config package.searchers package.searchpath \
$(keywordclass4.package)

# Debug (not enabled by default)
keywordclass5.lua5x=$(keywordclass5.lua50) \
debug.getfenv debug.getmetatable debug.getregistry debug.setfenv debug.setmetatable \
debug.getuservalue debug.setuservalue debug.upvalueid debug.upvaluejoin

# Keyword highlighting selection
# Options: lua4, lua50, lua51, lua52, lua53, lua5x (default)
# or enable everything like this: $(keywordclass.lua4)$(keywordclass.lua5x)
keywords.$(file.patterns.lua)=$(keywordclass.lua5x)
keywords2.$(file.patterns.lua)=$(keywordclass2.lua5x)
keywords3.$(file.patterns.lua)=$(keywordclass3.lua5x)
keywords4.$(file.patterns.lua)=$(keywordclass4.lua5x)

# Add keywords5, 6, 7 & 8 for user-defined libraries
#keywords5.$(file.patterns.lua)=
#keywords6.$(file.patterns.lua)=
#keywords7.$(file.patterns.lua)=
#keywords8.$(file.patterns.lua)=

#~ statement.indent.$(file.patterns.lua)=5 do else function then
#~ statement.lookback.$(file.patterns.lua)=20
indent.maintain.$(file.patterns.lua)=1

comment.block.lua=--~
comment.block.at.line.start.lua=1

# Lua styles

#Default
style.lua.32=$(font.code.base),fore:#000000
# White space: Visible only in View Whitespace mode (or if it has a back colour)
style.lua.0=fore:#FF0000
# Block comment (Lua 5.0)
style.lua.1=$(colour.code.comment.box),$(font.code.comment.box),back:#D0F0F0,eolfilled
# Line comment
style.lua.2=$(colour.code.comment.line),$(font.code.comment.line)
# Doc comment -- Not used in Lua (yet?)
style.lua.3=$(colour.notused),$(font.notused)
# Number
style.lua.4=$(colour.number)
# Keyword
style.lua.5=$(colour.keyword)
# (Double quoted) String
style.lua.6=$(colour.string)
# Character (Single quoted string)
style.lua.7=$(colour.char)
# Literal string
style.lua.8=$(colour.string),$(font.code.comment.box),back:#E0FFFF
# Preprocessor (obsolete in Lua 4.0 and up)
style.lua.9=$(colour.preproc)
# Operators
style.lua.10=$(colour.operator)
# Identifier (everything else...)
style.lua.11=
# End of line where string is not closed
style.lua.12=back:#E0C0E0,eolfilled
# Other keywords (bozo test colors, but toned down ;)
style.lua.13=$(style.lua.5),back:#F5FFF5
style.lua.14=$(style.lua.5),back:#F5F5FF
style.lua.15=$(style.lua.5),back:#FFF5F5
style.lua.16=$(style.lua.5),back:#FFF5FF
style.lua.17=$(style.lua.5),back:#FFFFF5
style.lua.18=$(style.lua.5),back:#FFA0A0
style.lua.19=$(style.lua.5),back:#FFF5F5
# Labels
style.lua.20=fore:#7F7F00
# Braces are only matched in operator style
braces.lua.style=10

# compatible with LuaBinaries for Lua 5.1; will work on both platforms.
command.compile.*.lua=y:\lua-5.3.4\bin\luac.exe -o "$(FileName).luc" "$(FileNameExt)"
# Lua 5.1
command.go.*.lua=y:\lua-5.3.4\bin\lua.exe "$(FileNameExt)"
# Lua 4.0
#command.go.*.lua=Lua-4.0.exe -c -f "$(FileNameExt)"

需要 Build 時

https://github.com/SCons/scons

全球資訊網 Lua 解譯器

https://github.com/fengari-lua/fengari-web 取得 fengari-web.js, 利用 https://ace.c9.io/ 當作編輯器, 可以組合成 https://github.com/davchoo/SchoolProject, 加以修改除錯後成為 http://mde.tw/vecp2018/lua/SchoolProject/

期末查驗

G19

https://vecp-40628435.herokuapp.com/lua40628435
https://vecp-40628435.herokuapp.com/lua40628414
https://vecp-40628435.herokuapp.com/lua40628417

G1

https://vecp-40628408.herokuapp.com/lua40628408
https://vecp-40628408.herokuapp.com/lua40628428
https://vecp-40628408.herokuapp.com/lua40628436

G11

https://vecp-40628427.herokuapp.com/lua40628427
https://vecp-40628427.herokuapp.com/lua40628437
https://vecp-40628427.herokuapp.com/lua40628412

G13

https://vecp-40328410.herokuapp.com/lua40328410
https://vecp-40328410.herokuapp.com/lua40628448
https://vecp-40328410.herokuapp.com/lua40328421

G9

https://vecp-40628451.herokuapp.com/lua40628451
https://vecp-40628451.herokuapp.com/lua40628453
https://vecp-40628451.herokuapp.com/lua40628438

G17

https://vecp-40628407.herokuapp.com/lua40628407
https://vecp-40628407.herokuapp.com/lua40628401
https://vecp-40628407.herokuapp.com/lua40628445

G18

https://vecp-40628442.herokuapp.com/lua40628442
https://vecp-40628442.herokuapp.com/lua40628452
https://vecp-40628442.herokuapp.com/lua40628454

G15

https://vecp-a40628440.herokuapp.com/lua40628440
https://vecp-a40628440.herokuapp.com/lua40628413
https://vecp-a40628440.herokuapp.com/lua40628450

G12

https://vecp-40628418.herokuapp.com/lua40628418
https://vecp-40628418.herokuapp.com/lua40628411
https://vecp-40628418.herokuapp.com/lua40628421

G10

https://vecp-40628431.herokuapp.com/lua40628431
https://vecp-40628431.herokuapp.com/lua40628424
https://vecp-40628431.herokuapp.com/lua40628425

G14

https://vecp-40628405.herokuapp.com/lua40628405
https://vecp-40628405.herokuapp.com/lua40628410
https://vecp-40628405.herokuapp.com/lua40628416

G7

https://vecp-40628404.herokuapp.com/lua40628404
https://vecp-40628404.herokuapp.com/lua40628422
https://vecp-40628404.herokuapp.com/lua40628456

vecp-week4

window.localStorage 的應用

http://mde.tw/vecp2018/lua/SchoolProject 之所以能讓使用者能夠直接在瀏覽器中執行簡單的 Lua 程式範例,
是因為利用 Javascript 重寫 Lua 解譯器, , 因此具有 Javascript 程式語言的特性, 其中包括 https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage

所謂的 localStorage, 是能讓使用者透過 Javascript 程式, 將資料存在近端的瀏覽器中, 也能取出利用.

localStorage 典型的應用之一, 就是當使用者在 http://mde.tw/vecp2018/lua/SchoolProject 的編輯器中輸入 Lua 程式碼後, 按下執行時, editor.lua 就會將編輯器中的 Lua 程式碼放入與 "lua_src_id" 字串對應的 localStorage 儲存空間中, 且在載入頁面時就啟動執行的 ace/FileSaver.min.js 與 ace/filereader.js 及設定將程式碼存在近端的 Javascript, 就可儲存所執行的 Lua 程式內容.

index.html 內容如下:

<!DOCTYPE html>
<html>
    <head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<link rel="stylesheet" href="style.css">
<title>How to program in Lua</title>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<!-- 導入 FileSaver 與 filereader -->
<script type="text/javascript" src="ace/FileSaver.min.js"></script>

<script type="text/javascript" src="ace/filereader.js"></script>
<script type="text/javascript">
function doSave(storage_id, filename){
    var blob = new Blob([localStorage[storage_id]], {type: "text/plain;charset=utf-8"});
    filename = document.getElementById(filename).value
    saveAs(blob, filename+".lua");
}
</script>
    </head>
    <body>
<header>
        <h1 class="center">Hello Lua!</h1>
        
        <div class="repl-container">
            <div class="row editor-container">
                <div class="col-sm editor">print("Lua is fun!")</div>
                <div class="col-sm output"></div>
            </div>
            <div class="row">
                <button type="button" class="btn btn-primary btn-execute">Execute</button>
            </div>
    <form>
    <label>Filename: <input type="text" id="lua_filename" placeholder="input file name"/>.lua</label>
    <input type="submit" value="Save" onclick="doSave('lua_src_id', 'lua_filename');"/>
</form>
            <br />
        </div>
<script src="ace/ace.js"></script>
<script src="ace/mode-lua.js"></script>
<script src="fengari-web.js"></script>
<script src="editor.lua" type="application/lua" async></script>
<script src="editor.js"></script>
<br /><br />
<canvas id="canvas" width="600" height="600"></canvas>
    </body>
</html>

editor.lua 內容如下:

local js = require "js"
local __G = _G
local load = load
local pack, unpack = table.pack, table.unpack
local tostring = tostring
local traceback = debug.traceback
local insert = table.insert
local document = js.global.document
local xpcall = xpcall
storage = js.global.localStorage

M = {} --All of the user globals

local newenv = setmetatable({}, {
        __index = function(t, k)
            local v = M[k]
            if v == nil then return __G[k] end
            return v;
        end,
        __newindex = M
    })

function js.global.runLua(window, code, outputElm) 
    --Reset user global table to a clean state
    --M = {}
    M["_G"] = M
    --Redirect print and error to output box
    M.print = function(text)
        local msgElm = document:createTextNode(text)
        msgElm.className = "console-msg"
        outputElm:appendChild(msgElm)
        outputElm:appendChild(document:createElement("br"))
    end
    
    M.error = function(text)
        local msgElm = document:createTextNode(text)
        msgElm.className = "console-error"
        outputElm:appendChild(msgElm)
        outputElm:appendChild(document:createElement("br"))
    end
    
    --Clean output window
    while (outputElm:hasChildNodes()) do
        outputElm:removeChild(outputElm.firstChild);
    end 
    
    --Finally load and run the code
    storage:setItem("lua_src_id", tostring(code))
    local fn, err = load(tostring(code), "sandbox", "bt", newenv)
    
    if fn then
        local results = pack(xpcall(fn, traceback))
        if results[1] then
            if results.n > 1 then
                M.print(unpack(results, 2, results.n))
            end
        else
            M.print(results[2])
        end
    else
       M.error(err)
    end
end

瀏覽器端設定

在 Firefox 中, 以 Preferences - General - Downloads 選擇"Always ask me where to save files"

在 Chrome 中, 以 Settings - Advanced - Downloads 選擇 Ask where to save each file before downloading

參考用 Lua 程式碼

Github 倉儲中的 Lua 程式碼

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

-- Load Vue library
package.loadlib("https://cdn.jsdelivr.net/npm/[email protected]/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;
}

vecp-week3

學哪一種計算機程式語言都好

在學習一套程式語言的過程, 了解各種程式語言各有其適用情境, 應該是個不錯的開始, 無論您從大一就開始學習 C 或 C++ 或 C# 或 Python 或 Java 或 Javascript 或 Ruby 或 swift 或 Lua 或其他計算機程式語言, 其終極目的應該只有一個, 就是逐步認清, 未來將會有更多的事物是必須仰賴計算機程式完成, 單單一種程式語言無法滿足所有工作 (當然 http://www.red-lang.org/ (源自 rebol快速瀏覽 red 程式語言) 的開發群可能不太同意這種論調), 但是就在 2018 年第一季, 「單單一種程式語言無法滿足所有工作」這句話, 應該還算適用的.

因此, 假如您得知其他同年的計算機程式課程, 正在教導上述其他各種程式語言, 要記住, 只要能夠用來解題的程式語言, 都值得學習, 在本課程, 我們先設法學會使用 Lua 程式語言.

有興趣的話, 也可以參考一下: https://haxe.org/ (還有 https://en.wikipedia.org/wiki/Haxe)

有人用 Javascript 重寫 Lua

http://lua.space/webdev/why-we-rewrote-lua-in-js, 我們也知道, https://fengari.io/ 利用 Javascript 重寫了 Lua 程式語言, 讓我們可賴以在瀏覽器中直接執行 Lua 程式.
意思是說 ,假如我們會配置 https://fengari.io/, 就可以直接在 web based 環境 ,直接練習 Lua 程式語法.

安裝的方法: https://github.com/fengari-lua/fengari-web

參考資料: https://youtu.be/xrLIgmd8xik

真的要好好感謝 David Choo

因為 David Choo 已經在一個月前完成 https://github.com/davchoo/SchoolProject, 我們只要將這個專案配置到課程倉儲中, 大家就可以直接在 http://mde.tw/vecp2018/lua/SchoolProject/ 中練習簡單的 Lua 程式語法了.

第三週的分組練習題目

第二週的課程內容中, 我們已經知道 Lua 基本重複迴圈用法, 如下:

-- lua test
--[[ 這是 Lua 程式的多行註解
Sc1.exe 是以副檔名 .lua 辨識程式語法, 將本程式放入編輯器後, 以 Tools->Go 執行
]]
for i = 1,10 do
  print(i .. "沒有問題")
end

那麼, 各組應該可以利用 http://mde.tw/vecp2018/lua/SchoolProject/ 寫一個能夠從 1 累加到 10, 並將結果印出的程式吧!

vecp-week2

計算機程式第二週課程

  1. 各學員是否已經根據說明, 以學號申請 Github 帳號, 並且了解如何建立倉儲, 如何在 Issues 發佈資料.
  2. 各學員是否已經將 kmol_level1 放入 USB 隨身碟, 並且了解如何利用 Sc1.exe 執行 C, Python 與 Lua 程式.

ANSI C 測試程式

/* Hello World program */
// Sc1.exe 是以副檔名 .c 辨識程式語法, 將本程式放入編輯器後, 以 Tools->Go 執行

#include<stdio.h>

main()
{
    int i;
    
    for (i=0; i<10; i++){
        printf("Hello World\n");
    }

}

Python 測試程式

# 單行註解
''' 多行註解,
Sc1.exe 是以副檔名 .py 辨識程式語法, 將本程式放入編輯器後, 以 Tools->Go 執行
'''
for 索引 in range(10):
    print("可以")

Lua 測試程式

-- lua test
--[[ 這是 Lua 程式的多行註解
Sc1.exe 是以副檔名 .lua 辨識程式語法, 將本程式放入編輯器後, 以 Tools->Go 執行
]]
for i = 1,10 do
  print(i, "沒有問題")
end

接下來將利用初步了解 Lua 程式語言, 以及 Lua 變數 scope 用法 進入 Lua 的程式世界

利用 Solvespace 與 V-rep 練習 Lua 與 Python 程式

本課程將利用 Solvespace 建立汽車相關零組件, 導入 V-rep 後, 以 Lua 及 Python 程式進行控制.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.