uncompress the img.gz to img , dd it to TF card
boot it , OpenWRT default IP is 192.168.1.1 from eth port , connect it , if no DHCP to get IP , set NIC to use like static ip 192.168.1.123/24 , and connect to 192.168.1.1
add a wan interface "wan" select PPPoE and device select "br-lan" , "Obtain IPv6 address" set Disable
set "LAN" IPv4 address to like "192.168.200.1" , you may want use different subnet
LED Configuration set led0 "kernel: heartbeat" and led1 "kernel: netdev" + "br-lan" + "Transmit + Receive" , the Pi led will be one heartbeat and one network status
then use this doc , this doc will keep continue upgrade to last version ... ssh to Pi and all copy paste to work haha
https://openwrt.org/docs/guide-user/services/vpn/openvpn/server
and if use Tunnelblick at macOS , import the .ovpn key and connect it , will get like "Unrecognized option or missing parameter(s)" , you need goto Tunnelblick settings , select the imported config , and set the "OpenVPN Version" to lastest version
have fun : )
dev之路
技術文章的放置地
2021-11-18
OpenWRT + Pi + PPPoE + VPN (static IP)
2021-11-10
How to make FileZilla over 10 Simultaneous Transfers on FTP protocol
short answer : you can open multiple FileZilla ... haha ......
long answer : you may be BAN from ISP , because FileZilla will send 10+ connection per time , it will be very like DoS attack , so you need test how many connections is allow , and wait like 60 sec to open next FileZilla process , and set the retry connections after 60 sec (like "Delay between failed login attempts") , finally enable the "Send FTP keep-alive commands" , you don't want reconnect when it has limits
have fun : )
2021-11-05
event sourcing / MQ(websocket) 配 rest API 難題
這篇單純寫給有緣人,其實是自己的 memo 就是了
client => ws(pub) => MQ => process(sub => pub) => MQ => ws(sub) => client
這是很簡單的異步處理結構,但如果配上 rest 會 block 的特性,則會很尷尬,類似
client => rest(pub) => MQ => process(sub => pub) ??
如果 rest 端得不到回傳到底是有沒有完成了?且照這結構 rest 端要弄個 block timeout 才行,MQ server 如果不穩時或掉封包,會得不到 response 還後 timeout 到底,最終交易是否有完成其實也是未知
所以這邊有兩種解法才是,或是 1 + 1 解法
client => rest(blocked) => process(sub + rest)
也就是 API 需求直接戳到 process 去,該 process 要開 rest + MQ sub,然後在記憶體內 merge jobs,當然需要過濾只有 CRUD 中的 CUD 且過了所有檢查才能戳著,這樣 process 噴了至少還是會 return result
然後 process 可以不回正確的 response package,可以再由另外一個回覆,類似
client => rest(blocked) => proxy process(job[s]) => process(return uuid[s]) => proxy process(package result[s]) => rest => client
這樣子就算漂亮了,然後還可以繼續擴增處理,類似 ... client 可能只需要知道 uuid 則 process 可以做隔離進行,成為兩條完整的平行線
client => ... => insert process(return uuid , pub uuid need trade) => MQ
MQ => trade process(got inserted jobs , then process)
這樣做隔離的話,就能回 client 完整的 pk 且不會掉才是,當然凡事是有缺點的,類似 insert 的為空的 job 可能會有 balance 的問題,能夠不真實扣帳,而 trade 可以取消 job 的狀況下 ... 應該都還能接受就是,單純要進行取捨而已
2021-07-28
bcache
這邊單純的列出所有 bcache 指令
假設 /dev/sda3 為 ssd,/dev/sdb1 為 hdd
重建:
sudo apt-get install bcache-tools
wipefs -a /dev/sda3
wipefs -a /dev/sdb1
make-bcache -C /dev/sda3 -B /dev/sdb1
sudo mkdir /media/bcache
sudo mount /dev/bcache0 /media/bcache
觀察狀況:
sudo bcache-super-show -f /dev/sda3
sudo bcache-super-show -f /dev/sdb1
取得 ssd 的 uuid 塞到 sdb1 內去做 cache,最終兩個 cset.uuid 必須相同
echo 86cd72eb-3b75-4a67-866a-cdfc48e498ac > /sys/block/bcache0/bcache/attach
# 86cd72eb-3b75-4a67-866a-cdfc48e498ac 為 /dev/sda3 的 cset.uuid
取得目前是何種模式:
cat /sys/block/bcache0/bcache/cache_mode
模式有三種:
writeback:先寫入 ssd 再寫入 hdd,適用於 hdd 過慢狀況
writethrough:兩者 ssd 和 hdd 同時寫入
writearound:直接寫入 hdd
修改模式:
echo "writeback" > /sys/block/bcache0/bcache/cache_mode
cache 狀況:
cat /sys/block/bcache0/bcache/state
如果回 dirty 反而正常,有 cache 尚未回寫的意思
強制回寫內容:
echo 0 > /sys/block/bcache0/bcache/writeback_percent
回復強制回寫(預設為 10):
echo 10 > /sys/block/bcache0/bcache/writeback_percent
最終 /dev/fstab 增加
/dev/bcache0 /media/bcache ext4 rw 0 0
請使用 mount -a 做測試
2020-11-22
M5Stack Core2 build from PlatformIO with VScode (tried official default code)
https://docs.m5stack.com/#/en/arduino/arduino_core2_development
create {new PlatformIO project} , choice board "M5Stack-Core"(no Core2) , then fix the [[ platformio.ini ]] like this
[env:m5stack-core-esp32]
platform = espressif32
board = m5stack-core-esp32
framework = arduino
; [[[for macos]]]
;upload_port = /dev/cu.SLAB_USBtoUART
; [[[fix PSRAM size and you won't need have this file]]]
board_build.partitions = default_16MB.csv
build_flags =
-DBOARD_HAS_PSRAM
-mfix-esp32-psram-cache-issue
{M5Stack Arduino libs}
https://github.com/m5stack/M5Core2
{M5Stack Core2 default factory code}
https://github.com/m5stack/M5-ProductExampleCodes/blob/master/Core/M5Core2/Arduino/Core2_Factory_test/Core2_Factory_test.ino
to build this , you need clone it and put [[ Core2_Factory_test/* ]] to [[{new PlatformIO project}/src/ ]] , rename(replace) [[ Core2_Factory_test.ino ]] to [[ main.cpp ]] and add
#include <Arduino.h>
at first line , then copy [[ {M5Stack Arduino libs}/src/* ]] to same folder (or some way to include it)
and some ref here :
{some examples}
https://github.com/m5stack/M5-ProductExampleCodes
{PlatformIO custom settings doc for ESP32 , include custom partition way}
https://docs.platformio.org/en/latest/platforms/espressif32.html
{ESP32 for Arduino partitions settings}
https://github.com/espressif/arduino-esp32/blob/master/tools/partitions/default_16MB.csv
have fun :)
2020-10-28
金流與帳務系統開發心法
首先,這篇一定是對的,最多有缺漏些東西而已,掌握此心法來做系統開發,不管程式設計師,系統安全,會計等等,基本上都會滿足需求
以下名詞盡量通用,所以使用"資料集",你開心可以替換成 Table / Collection 之類的單位
心法零:四個人打麻將,打了三天三夜,總額不變
任何交易系統都是,從最小的購物車到交易所的撮合系統,全部都是
心法一:使用"搬移"的方式來做帳,所有金流都有來源與對象,如果沒有,就虛擬出來
假設一個情境:『ATM 入帳給使用者,則建立一筆單,然後使用者的餘額增加』
所以通常用很簡單的直覺來規劃一個系統,通常會有兩個資料集,"入帳紀錄"與"使用者餘額",但這邊就缺了一個對象,"銀行"
銀行>入帳紀錄>使用者餘額
如果當下完成並直接入帳 100 元,則銀行餘額變動為 -100 使用者餘額變動為 +100,這邊有個好處,就是你不用去統整所有入帳紀錄,就能得知全站所有使用者餘額絕對不會超過銀行總扣款,所以你可以從使用者餘額總數出發,從入帳紀錄出發,從銀行餘額出發,三方驗證來檢查這件事情是對的,要簡單就用頭尾即可,那麼繼續往下推
假設『銀行入帳需要人工審核,才能進行使用者加值』的狀況,然後發生了些 ... race condtiion 的問題,幫使用者重複入帳
銀行>入帳紀錄>入帳紀錄(帳務變更狀態)>使用者餘額
所以銀行匯入後,該把 100 元金額從"銀行"移至"銀行_{UserID}_未實現"(或是把UserID換成InvoiceID),等帳務驗證後,再從"銀行_{UserID}_未實現"移至"使用者餘額"內,最後正常結果會與上個 case 相同,但這樣有什麼好處?
如果後台寫爛了,連點兩次幫使用者加了兩次款,則其實是『"銀行_{UserID}_未實現"移至"使用者餘額"』這個過程重複執行兩次,則"銀行_UserID_未實現"這個項目的數值會是負的 ... 這樣應該瞬間能稽核出異常的部分才是,因為假設因為系統問題,使用者重複儲值成了 200 元,全以搬移的方式進行,一定會在某個中介狀態或來源為負值,所以"中介狀態"或是"未實現"或是會計原則中的"在途"的數值就變得異常重要,而非只有單純的搬移概念而已
以這心法應該能推到非常多的實作,類似把任何的餘額變動全部存起來,有來源有對象,則整理出任何一個報表,任何時間點切入應該都變得沒問題才是,且多個面向可以彼此稽核,也會滿足會計與安全的角度才是,稽核的速度也會變得超級快
心法二:自我稽核,串鏈與自省
餘額修正時可以留下一個簡單的 crc32 做檢核碼(請加上固定或變動的 private / salt 字串),類似 "twd" 與 "twd_sum",當然如果多幣別則可以把 checksum 統整為同一個欄位,這樣的好處能防止任何的 injection 攻擊,下個要變更餘額的動作時,必須要先驗證 checksum 後才能進行交易,而歷史資料內上下筆內文,可以如同區塊鏈般取出特徵值後,把目前的結果放在下一筆資料內,保持串鏈連結不被打破,類似 checksum("{ID}_{UserID}_{Amount}_{上一筆的checksum}") 放在某個欄位,這樣就不怕某些資料順序被互換,且順序可由 checksum 得知,以上兩者都完成時,你就不用怕有任何的 injection 和做帳狀態被修正才是
心法三:用新的資料(insert / new item)來解決所有問題
在人工審核的 case 內,假設一個訂單 id 1234 修正狀態從"未處理"改成"完成",但其實你無法保證"完成"前面是否曾經是另外一個"未處理"和另外一個"完成"(race condition>重複入帳),所以這個的重點應該會類似於,新增完成後的任何資料應該都必須是鎖定的狀態,中間如果有變更的行為,會有個虛無飄渺的感覺存在那邊,所以建議使用子資料集來做變更,類似 id 1234 變動時,都必須在另外一個子資料集內有另外一個紀錄,則該子資料集就有所有的變動順序等等,或是全部使用新增另一筆資料來覆蓋原本的資料,類似 parent_id (原始資料是誰) , is_masked (是否已遭到覆蓋) 的方式來進行完成
心法四:給自己留後路,每個值變動前存入上次的結果
在某個使用者一千萬筆資料集面前,已知其中有一筆資料的誤差值為 1,請找出該資料,基本上以上面所有實作還不夠,你還缺了一個帳本的概念,就像銀行的存款簿,都會寫上次餘額(上一行的結餘)、此次修正(存提)、此次結餘,所以如果要完成這個條件,你勢必在任何一張單,有做任何餘額變動時,都必須存下它的上次或此次的最終結餘為多少(這邊包含任何的"在途",因為它們也是餘額的一種),這樣用頭尾兩張單還有自我比對就能比對出那筆誤差的單才是
心法五:分散資料集,排序很重要(使用時間,或是序列完成)
系統一定會擴張到某個很恐怖的程度,系統會分散,而會變動使用者餘額的帳也會到處跑到處長,而這如何收斂?甚至還要歸出整個順序才行?重複怎麼辦?
"時間"很重要,分散式系統中每台主機都不準,所以這邊一定要留存中心伺服器給的時間,通常是 DB 時間,類似 MySQL 可以用 "CURTIME(4)" 取到小數點下四位的時間,其他 DB 甚至可以留存下到 nanosecond 等級,任何交易變動的時間都必須要留存下來(留存不下來的代表你違反"心法三"的規則,請用別的 column 或新的資料來完成)這樣就能很方便的以時序拼回來才是,當然假設你們家系統很小,或是這類交易事務不夠頻繁,則集中用同一個資料集,應該就能完成整個排序的維持這樣就最好了
目前的心得大概就是這些唄,有額外的心得會在進行補充,你不用完成所有的項目,可以抽出認為 ok 的來完成即可,或是使用此心法來完成一道道的內外部防火牆來提前告警哩
可能比內文還重要的補充:(有任何問題都能提出,我會用補充的方式繼續補下去哈哈)
1. 心法一的例子中 "移至"銀行_{UserID}_未實現" 這個欄位勢必會一直修正,因為訂單會 merge 此在途餘額,且以本文規則而言,每次修正都必須存下上次修正的過程與結果,所以可以改為 "銀行_{InvoiceID}_未實現",甚至是
"銀行_{InvoiceType}_{InvoiceKind}_{InvoiceID}_{OwnerType}_{OwnerID}_未實現"
就能大大的減少這事情,這問題的根本原因其實是在途金額的唯一性對唄?因為一張訂單的完成,通常只有成功和失敗,至於需要補單或是部分退款,則可以是先成功後,再打退款紀錄,老實說應該沒有所謂入帳一半的甚至需要修正之類的(同一訂單部分商品取消,其實也能使用先退原單後,再新增部分單完成,然後註記 ID 為原始 ID 就能完成需求),單純在途餘額欄位會變成 N 個,但我認為這對 DB schema 設計來說並不會是困難點才是
2. 心法五的例子中,時間其實只是排序方式,假設有其他欄位,類似 pkey / uuid / serial ... 能完成的話,就拿那個就好了,然而如果沒這前提,繼續假設 DB 為分散式系統,勢必每一台 DB 時間都不準,然而這話題需要分割兩個部分,首先要從 transaction 角度為起點,如果該 DB 設計是所有 transaction 都從同一台來執行的話 ( RDBMS 系列,還有部分 noSQL 實作) 則你應該不用擔心這問題才是,然而如果還是會發生的話,就要自己寫 sort 演算法了,所有的帳應該都可以 merge 在一起在任何一個時間點,則可以找個資料集用 merge 的方式一直算下去,如果發覺結果完全正確單純順序錯帳的話(配合心法四能自省找到錯帳),應該只有 sort 有問題,通常 sort 有問題的深度應該不會超過 10 筆才是,10 筆的前提會是同一使用者在同一幣別同時操作超過了 10 次,然而這問題真正的主因應該會類似:大量批次處理新增資料時順序塞錯了,或是某台主機時間根本就沒有 sync 到 ... 而用遞迴或是一些演算法應該就能重新 sort 完成才是
以上
2020-06-10
來源不明的 image 的 dd 後處理
tune2fs -r $((10*1024*1024/4096)) /dev/sda1
2020-02-24
dd image 重建
sudo -sH
losetup -Pf disk.img
fdisk -l #可以看到硬碟了,目標在 loop4p
mkdir /mnt/loop4p2
mount /dev/loop4p2 loop4p2
# 先清一些垃圾
cd /mnt/loop4p2
rm var/log/*.gz var/log/*.1 var/cache/apt/archives/*.deb
dd if=/dev/zero of=/mnt/loop4p2/empty
# df 可以看到硬碟慢慢減少
rm /mnt/loop4p2/empty
# 記下 df 有多少,類似
# /dev/loop4p2 122851588 113626940 6090084 95% /mnt/loop4p2
# 則目標為 113626940,這次應該希望 120000000 就好,丟過過去再擴容
umount /mnt/loop4p2/empty
e2fsck -f /dev/loop4p2
resize2fs /dev/loop4p2 120000000s
dd if=/loop4 of=/dev/sdc
# fdisk 刪除後新增 = 擴容,可以從 table 層 (DOS / GPT) 開始重建
# e2fsck -f /dev/sdc1
# resize2fs /dev/sdc1
losetup -vd /dev/loop4p2
2019-05-17
BTC node 的 block 倒退手法 ...
invalidateblock
reconsiderblock
一個後退一個前進,應該可以解決大部分末尾 block 錯誤的問題,然後重新 sync 即可
2018-05-21
Ethereum smart contract / ERC20 補遺
首先 ... 如果全世界有個程式只能寫一次,之後無法再修改,只能發佈新的程式時,會引發怎樣的災難和困擾? ... 單純這邊列下買保險的做法
A.所有 transaction 都必須確認 eip658Transition 上的 status (使用 eth_getTransactionReceipt => status 確認 0x1(完成) or 0x0(失敗) [下簡寫為 status])做為是否有完成 contract 的首要條件,否則沒有任何地方可供判斷,因為 transaction 入 chain 後就不會刪除,不同於 bitcoin 會刪除或是隱藏有問題的交易,且即使使用了 EVM 也無法追蹤實際運作細項(類似誰多了錢誰少了錢),多重交易時無法判斷哪個 transaction 是否完成,因此只能使用 status 來做判斷(案例 ... 對手商 ...)
B.不管如何上 SafeMath 就對了,不用判斷大小,而是直接上 SafeMath 後直接運算,簡單來說實際運作的 contract code 內不應該有比較運算(大於小於之類的運算),而是直接用 SafeMath 中的類似 .add / .sub / .div 之類的運算 function 做包裹,而比較運算只能在 SafeMath 中出現,因為當出事時,SafeMath 內直接會 require(false) 而直接噴走,並自動把 status 改成 0x0(案例 ... BatchOverFlow ...)
C.contract code 內的 return false 並不會把 status 改成 0x0,事實上只有 require(false) 或是 gas 不夠之類的才會改,其餘全部都是 0x1,所以請勿使用 return false (舊的寫法是 throw,新的寫法是 require(false),status 真實代表只是程式是否有"正確完成"而已,但並不代表交易是有效的,因此把交易是否有效反向綁定在程式是否由正確完成上,即可互相觀察)
大概是這些,有想到再繼續補下去 ......
2018-04-30
RDBMS 的 lock 更新前置計算或然率
SELECT ... FROM items WHERE id = 123
#前置檢查 status 或其他欄位,可免除 lock 成本,甚至可以快取還有額外運算
SELECT ... FROM items WHERE id = 123 FOR UPDATE
# row base lock 開始
# a = a + 1
# 很慢的 function 要處理,處理完後會塞回原值或其他欄位
UPDATE items SET ... WHERE id = 123
但其實可以改成
SELECT ... FROM items WHERE id = 123
# 前置檢查 status 或其他欄位,可免除 lock 成本,甚至可以快取還有額外運算
# ori_a = a
# new_a = ori_a + 1
# 很慢的 function 要處理,並暫存於其他欄位
SELECT ... FROM items WHERE id = 123 FOR UPDATE
# row base lock 開始
# if ori_a == a #額外判斷成本
# a = new_a
# 直接把暫存欄位也直接塞入,免除計算
# #賺到的部分
# else
# 重新處理:很慢的 function 要處理,處理完後會塞回原值或其他欄位
# #虧的部分
# end
UPDATE items SET ... WHERE id = 123
2017-11-12
convert image to Arduino OLED ( Adafruit GFX Library & black white )
#define filename_width 24
#define filename_height 24
static char filename_bits[] = {
0xFF, 0x3F, 0xFF, 0xFF, 0xDF, 0xFE, 0xFF, 0xEF, 0xFD, 0xFF, 0xF7, 0xFB,
0xFF, 0xF7, 0xFB, 0xFF, 0xFB, 0xFD, 0xFF, 0xFD, 0xFE, 0xFF, 0xFE, 0xFE,
0xFF, 0xFD, 0xFD, 0xFF, 0xF3, 0xFD, 0xFF, 0xFB, 0xFE, 0xFF, 0x7B, 0xFF,
0xFF, 0xBD, 0xFF, 0xFF, 0xDE, 0xFF, 0x7F, 0xDF, 0xFF, 0xFF, 0xBE, 0xFF,
0xFF, 0xDE, 0xFF, 0xFF, 0xEE, 0xFF, 0x7F, 0xF7, 0xFF, 0x7F, 0xFB, 0xFF,
0xBF, 0xFD, 0xFF, 0xBF, 0xFE, 0xFF, 0x5F, 0xFF, 0xFF, 0x9F, 0xFF, 0xFF,
};
因為是記憶體直接對照,所以就是 memory buffer 的格式了,其中的 filename 處為檔案名稱,會直接換成變數名稱,但如果直接出 stdout 所以該處會變成 -這 code 其實 Arduino 中的 Adafruit GFX Library 可以直接吃了,不過需要加工一下
其中 char 要轉成 uint8_t 型態,而黑白顛倒(對 OLED 而言亮的是黑色),所以要 xor 255 也就是把整個 binary 整個反轉過,而 char 轉 uint8_t 時每個 set 都是顛倒的,要換回去才行
完成的程式長這樣,有需要的人可以拿去用哩
# [[Ruby code]]
# need imagemagick , convert all images(gif) & merge to output.txt
target = File.open('output.txt' , 'w')
filenames = Dir["*.gif"]
filenames.each do |filename|
var_name = filename.split(/\./)[0..-2].join('_')
content = `convert #{__dir__}/#{filename} xbm:-`
#re-define var name & change type to uint8_t
content.gsub!(/define \-/ , "define #{var_name}")
content.gsub!(/static char -_bits/ , "static uint8_t #{var_name}_bits")
#inverse color && swap bitmap buffer
content.gsub!(/0x[\dA-F]{2}/) do |source|
source = (source.to_i(16).to_s(2).rjust(8 , '0').reverse.to_i(2) ^ 255).to_s(16).upcase
"0x#{"0" if source.length < 2}#{source}"
end
target.puts(content)
end
target.close
puts "finished , total #{filenames.length} files"
之後把 output.txt 的內容複製貼上到 IDE 內就可以直接使用了呦,超方便的哈哈
2017-06-13
童話鎮的勇者
童話鎮長期以來被魔王城跑出來的魔物侵擾,破壞莊稼,調戲民女,但缺乏領導人物,村民也不想抵抗,想辦法討好魔物們,雖然有損失但彼此相安無事
有一天童話鎮來了一個外人,穿著非常破爛,看似經歷過非常多的戰鬥,身上充滿著殺意與鬥氣,來到鎮上後看到鎮上的狀況,把所有的魔物都趕跑了,並佇立在鎮門口,魔物看到都嚇跑了
鎮長知道這一切後就邀請那個外人,住進了鎮內,去除破爛的外表,穿著光鮮亮麗有威嚴的服裝,並稱他為勇者,而鎮內因為沒有魔物的侵擾,從童話鎮變成了童話市,最後發展變成了童話城,鎮長也變成了城主
童話城其實不時的還是有魔物的侵擾,所以城主命令勇者建立了護衛軍,希望勇者把技術都傳給手下們,然而城主也希望勇者能褪去殺意與鬥氣,和善的對待村民,城主也希望能有定期報告,得知近況
勇者盡量的符合城主的期待,護衛軍雖然人數少,但盡忠職守不眠不休地防止侵擾,護衛軍內人人都想變成勇者,完成相同偉大的任務,勇者抑制了自己的殺意和鬥氣,學會如何談笑風聲,變得健談,定期彙報狀況給城主,安定了好長一段時間
但勇者漸漸感覺自己已經不再是當初的那個自己,沒有了鬥氣和殺意,長期過著和平的日子,好像少了些什麼 ......
城內過慣了和平,當民眾遠遠的看到了魔物,責怪護衛軍為何不去處理,護衛軍去追魔物時,魔物一溜煙就跑了,平常的訓練常常就這樣空缺了荒廢了,民眾看到多次魔物後,回報給城主,城主問勇者說:『護衛軍不是你在帶的嗎?為何有這種事情發生呢?』
勇者為了滿足城主的需求與旁邊大臣的建議,建立了多個哨點,把所有護衛軍都派到哨點去,連勇者都去哨點了,而城內一個護衛軍也沒有,當飛行系的魔物,如獨眼蝙蝠、獨角翼龍等等的魔物飛到城內作亂時,勇者與護衛軍會接到飛鴿傳書,指示要回城內平亂,但哨點與童話城相隔半天的路程,所以來回一次一天就什麼事情都沒做到了,更別說疏於平常的練習等等
有一天,前線的哨兵帶來一個非常不好的消息,魔王城的等級提升了
城主知道後找來勇者問說該怎麼辦,勇者說:『我們可以把護衛軍全部從哨點叫回來,組成勇者騎士團,去攻打魔王城』,城主說不行,因為週邊還是要顧,勇者說:『那我自己去吧,不過我需要練習和準備的時間』,城主勉強的點頭同意
勇者在找回過去的自我的期間,升級過的魔王城果然不是蓋的,一次就是十多隻的飛行魔物,護衛軍因為都還很遠在當哨兵,所以城主直接找人去勇者家中找勇者來,勇者大劍一輝,沒事了,回家繼續修煉,民眾看到了很崇拜勇者,但卻也養成習慣,看到大隻一點的鳥就會跑到勇者家嚷嚷,勇者到現場定眼一看,品種不對,還要放下剛剛在家的殺意和鬥氣,拿出過往的笑容對民眾解釋該品種不是魔物,不會有任何干擾民眾的行為
城很大,民眾很多,所以勇者多少次忘記自己是正在醞釀屠城的氣魄,反而都在服務著民眾,逐漸地喪失自我 ... 護衛軍每一個人也都想成為另一個勇者,但卻也都在哨塔站哨 ... 不時的還會被怪罪:為何你不會打魔物和攻打魔王城?
有一天,勇者出發了,前往魔王城,出發前要去拿武器,發覺兵器庫沒兵器了,原來是長久以來城內維運用光了,勇者心裡想:『罷了,這次前去,完成這一切,武器應該都用不到了』,一路走經過哨點,哨兵給予著勇者莫大的祝福,勇者信心滿滿,當攻破了第三個魔物的要塞後,準備觸碰到魔王城門,勇者回頭 ... 回到了城內 ......
原來是剛剛勇者收到了飛鴿傳書,上面用紅色的紙,上面寫著三個"急"字,勇者怕出事所以回城一趟,結果是旁邊的大臣說想知道最近的抗魔物花費多少,想知道最近的資料,不管怎樣都要第一個回報,因為要抓到最近的量來做準確評估,另外一方面魔王城再遠一點的地方還有另外兩個魔王城,請『順便打一打』,對你來說很容易的不是嗎?城主聽了很有道理,點頭同意
勇者知道後又花費了一段時間,將裝備和心態升級,重新打了第一個魔王城的三個魔物的要塞,摸到魔王城門時又收到飛鴿傳書,又是紅色的紙,又是三個"急"字,勇者擔心又回頭了,結果是旁邊的大臣提議要增加錄影機來增加城內的安全,防止財庫被偷,要勇者同意才能進行,城主聽了很有道理,點頭同意,並質疑勇者如果沒有辦法攻下魔王城時該怎麼辦的問題
此時,勇者站在一個人生的十字路口,分歧世界分歧劇情
A:勇者選擇了離開這一切,離開童話城,離開前城主說:『你忍心拋下這一切嗎?當初為了你建立這整套制度,如果你離開我會解散護衛軍!!!』,勇者最終離開了,童話城內聚力不足,崩解四散
B:勇者裝傻,拋棄掉殺意與鬥氣,單純的服務民眾,結果幾年後三個魔王城合併成一個超大的魔王國,以武力把童話城納為己有
C1:勇者就此消失匿跡,來到鄉間山上的一間房,不理會任何飛鴿傳書與凡間的侵擾,拋下所有過去,但尚未沉著心就出發了,只消滅兩個魔王城,回到城內,獲得城主和大臣的賞識,不過一旁的大臣小聲碎碎念:『不是說好消滅三個嗎?為何只有兩個?』
C2:勇者就此消失匿跡,來到鄉間山上的一間房,不理會任何飛鴿傳書與凡間的侵擾,拋下所有過去,修得最終無劍的技能,一舉成功消滅三個魔王城,回到城內,獲得城主和大臣的賞識,但城主和大臣心中感覺:『這不是應該的嗎?這就是勇者該做的事情啊,花了那麼多時間不是理所當然的嗎?』
D:勇者就此消失匿跡,另外一個外人來到童話城內,在勇者修煉期間滅了三個魔王城,勇者發覺沒城可滅,遠走他鄉
==========
我不是勇者,但我正做著勇者的事情,沒人可以幫助到勇者,而人人卻都可以是勇者 ... 或是幫助勇者的人 ...... 如果你懂 MIS 和 RD 的分別,請你拆開而非混用,不然再強的勇者都會離開 ...
然後,順便聽個音樂唄:
https://www.youtube.com/watch?v=ijRH_84lIQQ
https://www.youtube.com/watch?v=GlFIfw7kadY
2017-02-10
phantomjs , web crawler 的最終解
最完美的方式莫過於類似 wkhtmltopdf / wkhtmltoimage,開一個 webkit 的 browser 將網頁完整下載,並執行 CSS / JS / AJAX 後所剩下的 dom tree 轉為 source code,該 HTML 就是真正顯示到使用者螢幕上的資訊,也才是最終想要的成果
之後才看到 phantomjs,稍微練了一下,效果不錯,缺點後述
/*
指令使用
./phantomjs --web-security=false --ignore-ssl-errors=true --load-images=false hello.js
你可以增加 --debug=true --max-disk-cache-size=1000 來做 debug
*/
console.log('[[initialize]]');
//lib,等待 ajax call,判定 dom 的產生時機
function waitFor(testFx, onReady, timeOutMillis){
var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 10000, //最長 timeout 時間
start = new Date().getTime(),
condition = false,
interval = setInterval(function(){
if((new Date().getTime() - start < maxtimeOutMillis) && !condition){
condition = testFx();
}else if(!condition){
console.log("...下載太久timeout或判定來源失誤");
phantom.exit(1);
}else{
//console.log("'waitFor()' finished in " + (new Date().getTime() - start) + "ms.");
onReady(condition);
clearInterval(interval);
}
}, 250); //0.25一次巡迴
};
//這個來源其實可以從別頁來才是
var source = [
'http://24h.pchome.com.tw/store/DBAC9R','http://24h.pchome.com.tw/store/DBAC9S',
'http://24h.pchome.com.tw/store/DBAC9T','http://24h.pchome.com.tw/store/DBAC9U',
'http://24h.pchome.com.tw/store/DBACA0','http://24h.pchome.com.tw/store/DBACA1',
'http://24h.pchome.com.tw/store/DBACA2','http://24h.pchome.com.tw/store/DBACG7',
'http://24h.pchome.com.tw/store/DBACEA','http://24h.pchome.com.tw/store/DBACFP'
];
var page = require('webpage').create();
var start_at = (new Date()).getTime() / 1000;
console.log(start_at);
var process_me = function(url){
console.log(url);
page.open(url , function(status){
//下面這行顯示整頁的HTML
//console.log(page.content);
if(status !== "success"){
console.log("斷線了??或是該網頁連線錯誤");
}else{
waitFor(function(){
return page.evaluate(function(){
var temp = []
jQuery('#ProdGridContainer dd').each(function(index,dom){var t = jQuery(dom) ; temp.push([t.find('.prod_img img').attr('alt') , t.find('.price .value').html()])});
return temp.length > 0 ? temp : false;
});
},function(ans){
for(var i = 0 ; i < ans.length ; i++){
console.log(ans[i][0] + " : " + ans[i][1]);
}
console.log('總數 : ' + ans.length + ', cost(sec) : ' + ((new Date()).getTime() / 1000 - start_at))
start_at = (new Date()).getTime() / 1000;
if(source.length == 0){
console.log('結束惹')'
phantom.exit();
}else{
process_me(source.pop());
}
});
}
});
}
process_me(source.pop());
裡面有執行方式,phantomjs 本身包含了 webkit 瀏覽器,所以要去官方網站下載各平台的執行檔才行,好處是相依性都包在裡面了不用另外處理,其餘請看註解
phantomjs 因為是 JS 控制瀏覽器,所以外面大部分的 code 都是針對 webkit 瀏覽器的操作,而 page.evaluate 這個 function 會在 webkit 的網頁內執行,很像是開了 chrome dev tool 然後把 code 貼上的感覺就是,所以腦袋裡面要分開操作瀏覽器的 JS 和在網頁裡面執行的 JS 兩個角色,而對方網頁有載入 jQuery,當然在 page.evaluate 內就可以用 jQuery,但其他地方無法就是(也不需要),然而記得從裡面傳出資訊到外面的 JS 時,一定只能用基本型別(上面的 return temp),否則 phantomjs 會把 prototype 之類的打光光包光光,會非常非常緩慢且肥大,這邊要注意就是
裡面建了一個 waitFor 的 listener,功用是等待 AJAX 後 dom 的生成,所以輸入兩個值,第一個 func 會 return temp || false,當不為 false 時則判定 dom 生成,該 temp 會傳到第二個 func 變成 ans,也就是取得後要做啥事情的意思而已,概念很簡單就是
所以以上,完美的機器人最終解法整套超漂亮的,然而缺點是所有的 JS / CSS 應該都會被下載回來,所以和傳統爬蟲會多 x N 隻的 JS 下載,和執行的等待時間,這花費的基數其實有點高,所以...看似完美解,但其實能不用就不用唄
事實上還有高級做法,類似把對方網址所有的需求先過一層 proxy,把對方的 JS / CSS 先 cache 起來,則可以大幅減少下載和等待的時間才是,不過這邊等待 dom 生成的時間還是省不了,所以保守估計還是會慢一般傳統做法的 20 倍左右唄
okay anyway 這邊大概就這樣而已,也當作自己的 memo 就是了 ...... 然而實際上不用 phantomjs 的話,還有非常多種"邪惡"的爬法,不過我怕我公開後被抓去關,就先略過唄 X"D
2016-09-12
GnuPG 簽署
看了很多演算法,類似 AES ( 對稱加密 ) / RSA ( 非對稱加密 ) 都無法滿足實作需求,最後轉到了 OpenPGP 身上,而 Linux 已經有實作叫做 GnuPG,或稱為 GPG(PGP vs GPG ?? 這一定是某種玩笑 X"D), GPG 本身是非對稱式加密
以下貼一些之前整理的使用方式
#JokerCatz = key_name
#產生公+私金鑰
gpg --expert --gen-key
#匯出公私鑰
gpg --armor --export JokerCatz > public.asc
gpg --armor --export-secret-keys JokerCatz > secret.asc
#匯入公私鑰
gpg --import secret.asc
#編輯#裡面很囉唆
gpg --edit-key JokerCatz
#公私鑰列表
gpg --list-keys
gpg --list-secret-key
#移除公私鑰(自產金鑰 = 公 + 私,要移除的話要分別移除)
gpg --delete-key JokerCatz
gpg --delete-secret-key JokerCatz
#加密
#--trust-model : 信任非第三方公正者發的key,自產大概都要加這個
#--yes : 同意覆蓋檔案
#多個 --recipient KEYNAME 則用多把 public key 來 sign,且任何一把 private 都能解)
gpg --trust-model always --yes --encrypt --recipient MEOWs --output temp.txt.gpg temp.txt
#解密,使用private key(不用指定key)
gpg --decrypt --output temp.txt temp.txt.gpg
實際就大概這樣用而已唄,而不像 AES / RSA 本身是 key string or key file,GPG弄了之後很類似 global key pool 所以使用時都直接指定名稱即可,而 key 轉移到其他主機,則用匯入匯出即可
再來,其實 gpg 支援 pipeline,所以可以用在備份即時壓縮上,類似這邊所說的
https://lists.gnupg.org/pipermail/gnupg-users/2008-December/035168.html
my-pipeline-that-streams-data | gpg -o output-file.gpg --encrypt
嗯,超帥氣,這樣連中介檔案都不用留的 所以 mysql 備份也可以這樣來玩,類似
mysqldump --opt --all-databases | gpg --trust-model always --yes --encrypt --recipient JokerCatz --output mysqldump.sql.gpg
而其實 GPG 在簽章的同時應該有同時使用 gz 壓縮就是,所以不用二次壓縮,而這邊和 HSM 相比只差沒辦法把 private key 丟在破壞即失效的硬體內唄,以上
2016-08-29
Mi Note (Android 6.x) Root from OSX
類似用 brew 安裝
http://stackoverflow.com/questions/31374085/installing-adb-on-mac-os-x
或是用指令安裝
http://wccftech.com/set-android-adb-fastboot-mac-os/
再來把手機切到 fastboot 模式,然後
$ fastboot devices
會列出連結的手機
之後就可以使用這篇的教學
https://xiaomi.eu/community/threads/how-to-root-the-mi-note-virgo-on-marshmallow-6-2-18-6-2-4.29952/
用 TWRP 開機後使用 TWRP 刷入 SuperSU(建議先下載到手機內)
============
如果是要重刷整隻手機,一樣在 fastboot 內看到該手機後,把線刷包解壓縮,直接執行裡面有的 sh 檔即可
flash_all.sh 全清空,包含所有的內容
flash_all_except_storage.bat 清空 User 但不會清空虛擬 SD 卡的樣子
flash_all_except_data_storage.sh 保留 User 和虛擬 SD 卡
這邊選擇使用,就這樣而已,這邊單純自己用的 memo,以上
2016-08-05
Raspberry Pi as OpenVPN 暫存
新版使用
https://github.com/StarshipEngineer/OpenVPN-Setup
Pi3
#/etc/modprobe.d/raspi-blacklist.conf
#wifi
blacklist brcmfmac
blacklist brcmutil
#bt
blacklist btbcm
blacklist hci_uart
Pi3 remove wlan
sudo apt-get purge wireless* wpasupplicant bluez*
sudo apt-get autoremove
Pi3 rotate screen and remove colorful square
#/boot/config.txt
avoid_warnings=1
display_rotate=2
Pi3 disable screen blank
#/etc/kbd/config
POWERDOWN_TIME=0
BLANK_TIME=0
addon up / down / auth script
2016-06-20
sleep sort...
[[sleep_sort.rb]]
ARGV.each{|e|fork{sleep(e.to_f/10);puts e}}
use
ruby sleep_sort.rb 1 9 2 8 3 7 4 6 5 0
A...... 這不會出事咪X"DDD來源:http://m.blog.csdn.net/article/details?id=8514088
2016-05-27
Ruby : 有趣的 卍用 trace obj
module W卍
def self.method_missing(method_name , *argv , &block)
puts "======vvvvvv======"
puts "name : #{method_name}"
puts "argv : #{argv}"
puts "block : #{block}"
puts "======^^^^^^======"
return W卍
end
end
# W卍[123].new.map{|i|i}.uniq.join(W卍).each do |i| puts i ; end
#=> ======vvvvvv======
#=> name : []
#=> argv : [123]
#=> block :
#=> ======^^^^^^======
#=> ======vvvvvv======
#=> name : new
#=> argv : []
#=> block :
#=> ======^^^^^^======
#=> ======vvvvvv======
#=> name : map
#=> argv : []
#=> block : #
#=> ======^^^^^^======
#=> ======vvvvvv======
#=> name : uniq
#=> argv : []
#=> block :
#=> ======^^^^^^======
#=> ======vvvvvv======
#=> name : join
#=> argv : [W卍]
#=> block :
#=> ======^^^^^^======
#=> ======vvvvvv======
#=> name : each
#=> argv : []
#=> block : #
#=> ======^^^^^^======
#=> W卍
2016-05-11
captcha 之必要性與修正
在開發時遇到的需求,類似太簡單的 captcha 會被破解( ... 自己破解自己成功 ... ),太困難的 captcha 很好用但其實很擾人,然而這就是辯思之旅,captcha 存在必要的與否
captcha 的存在必要性其實是針對機器人的防止,防止的是對『下個動作的操作』,類似登入或發文系統通常有 captcha,原因是防止登入測試與機器人發廣告信
而目前系統的問題發生在登入時,所以是怕被機器人 try 帳號密碼
分析『 try 帳號密碼 』這件事情,所得到的影響的其實是『頻率』與『對象』這兩個因素,所以只需針對這兩個因素來完成即可
中間過程略過,其實將 IP 與帳號來視為危險來源與保護對象即可,目前所想得到的最完美解為類似
- 該 IP 期間內是否登入錯誤太多次,否則直接顯示
- 使用者輸入帳號後,Ajax 到後端檢查該帳號期間內是否登入錯誤太多次,而需要顯示 captcha,如果需要則回傳 captcha 資訊並顯示
- 設定時間區間為 segment counter,每次登入錯誤時就把目前的 counter + 1,目前這個 segment 的初始值是上個 segment x 0.8 來做退火,判斷使用目前和上個 segment 的錯誤總數是否到達峰值
2016-05-04
產生有向性圖狀關連( MySQL + Graphviz )
user_id , from_user_id , kind
過程:先定義 user_id < from_user_id + way ( 1 = 正向 , 2 = 反向 ),進行 GROUP 來做 clear 重複關連的動作,外面的 SELECT 再 sum 一次,如果 1 & 2 都有時變成 3 雙向,即為所求
SELECT kind , user_id , from_user_id , SUM(way) AS way FROM (
SELECT
kind ,
IF(user_id < from_user_id , user_id , from_user_id) AS user_id ,
IF(user_id < from_user_id , from_user_id , user_id) AS from_user_id ,
IF(user_id < from_user_id , 1 , 2) AS way
FROM tracks
WHERE user_id IS NOT NULL AND from_user_id IS NOT NULL
GROUP BY
kind ,
IF(user_id < from_user_id , user_id , from_user_id) ,
IF(user_id < from_user_id , from_user_id , user_id) ,
IF(user_id < from_user_id , 1 , 2)
) AS t GROUP BY kind , user_id , from_user_id
之後 Graphviz 就很簡單,圖要用 digraph(有箭頭)
way(1) : A -> B
way(2) : B -> A
way(3) : A -> B [dir="both"]
最後,類似這種解法一定要 1+ 開頭,因為 0 + 1 = 1 ([0,1] = 2 status) , 1 + 2 = 3 ([1,2,3] = 3 status)
2016-03-28
Ruby method object...
class A
def say(name = "yoo")
puts name
end
end
a = A.new
temp = a.method(:say) #get Method obj
class B
def do_something(me)
me.call('WTF')
end
end
B.new.do_something(temp)
#=> WTF
2016-03-16
log trace ...
gunzip *.gz #decompress log files
grep -B 10 '\(aaa\|bbb\|ccc\)' * > target.txt #keywords and include 10 back lines and save to target.txt
#vim's work...
2016-03-13
Google AdWords API + test account 申請流程......
Google AdWords 的帳號分成下面幾種:
- 請先去申請 AdWords Manager 帳號,登入後在灰色齒輪項目內可以找到『AdWords API 中心』,然後拿到格式很亂的亂碼『開發人員權杖(developer_token)』(這個項目在其他類型的 AdWords 帳號內"一定不會有")
- 申請 AdWords Manager test 帳號,登入後在灰色齒輪左邊可以找到數字中間用 "-" 連接的『客戶編號(client_customer_id)』
- 用 Adword Manager test 的帳號開啟 Google Developers Console 開一個新的專案,專案名稱隨便,新增後憑證那邊要先建立,開『OAuth 用戶端 ID』,名稱隨便,Logo 隱私權之類的都可略過,之後是建立用戶端 ID,選其他就好,名稱隨便,之後取得『用戶端 ID(oauth2_client_id)』,還有『用戶端密鑰(oauth2_client_secret)』,之後在總覽內啟用 API,廣告類項目都可以開
繼續後記:
client_customer_id 分成主帳號 test 和次要帳號 sub account,類似 create_account 必須用 test 的 client_customer_id,而類似 add_campaigns 因為有內容,則需要用 sub account 的 client_customer_id
test 主帳號的 ID 在頁面的右上方,而次要帳號 sub account 在帳號列表,每個帳號的下方的數字的部分
用錯主帳號的話會得到類似『OperationAccessDenied.ADD_OPERATION_NOT_PERMITTED』
用錯子帳號的話會得到類似『NOT_AUTHORIZED』
anyway 換一下 client_customer_id 切換一下說不定就可以解掉這些問題
繼續後記:
AdWords Manager 可以樹狀,可以再連結另外一個 Manager 帳號,而 API 打出來做列表時,可列出所有 Manager 帳號和一般帳號(所有子代都一次列出)並且做操作之類的
2016-03-09
mysqld_multi + master / slave replication
#增加設定值(/etc/mysql/my.cnf)
[mysqld]
server_id = 1
log_bin = /var/log/mysql/mysql-bin.log # if replication
[mysqld2]
server_id = 1002
pid-file = /var/run/mysqld/mysqld2.pid
socket = /var/run/mysqld/mysqld2.sock
port = 3307
datadir = /var/lib/mysql2
log_bin = /var/log/mysql/mysql-bin2.log # if replication
#要建立額外的檔案,包含pid和sock(這邊沒做會開不起來...)
sudo -u mysql touch /var/run/mysqld/mysqld2.pid
sudo -u mysql touch /var/run/mysqld/mysqld2.sock
#建立空白資料集
sudo mysql_install_db --user=mysql --datadir=/var/lib/mysql2
#之後就可以測試mysqld_multi
mysqld_multi report #check status
mysqld_multi start 2 #open 2
mysqld_multi report #recheck
#mysqld_multi stop 2 #if need stop
#建立連線2改用
mysql -u root -p -S /var/run/mysqld/mysqld2.sock
#==========[[master]]
sudo /etc/init.d/mysql restart #重開 master server (因為有設定 log_bin)
#next need 2 session , mysql client + bash
#設定帳號密碼
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'localhost' IDENTIFIED BY 'PASSWORD_HERE';
#鎖定整個資料庫
FLUSH TABLES WITH READ LOCK;
# remember 顯示資訊要記好,類似以下要記下來
#+------------------+----------+--------------+------------------+
#| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
#+------------------+----------+--------------+------------------+
#| mysql-bin.000002 | 267 | | |
#+------------------+----------+--------------+------------------+
SHOW MASTER STATUS;
#dump所有的資料庫,需加master-data(這邊要另外的session來做,原來的client不能中斷)
mysqldump -u root -p --master-data --all-databases > all_mysql_db.sql
#解除寫入
UNLOCK TABLES;
#==========[[slave]]
#dump回去
mysql -u root -p -S /var/run/mysqld/mysqld2.sock < all_mysql_db.sql
#設定繫結,這邊要填入 File => MASTER_LOG_FILE & Position => MASTER_LOG_POS
CHANGE MASTER TO MASTER_HOST='localhost',MASTER_USER='repl',MASTER_PASSWORD='PASSWORD_HERE',MASTER_LOG_FILE='mysql-bin.000002',MASTER_LOG_POS=267;
#開始
START SLAVE;
#check master & slave
SHOW MASTER STATUS;
SHOW SLAVE STATUS;
#如果是 "Waiting for master to send event" 就ok
打完收工 (rock)
2016-01-16
Rails memcache lock (dalli)
我可以用DB,但怕有效能影響,所以只能選擇 memory 系列,而我有 Redis & memcache(dalli) 可以選
用 Redis 的話缺點很大,因為 hash 沒有 expire 可選,只有一般的 key 有,所以會多一票垃圾 key 在列表內,非常 dirty ...,所以只剩下 memcache
而 memcache 其實無 lock 選項,而...只有一種作法之類的,叫做 increment,也就是某個變數 + 1 的意思,當 return > 1 時,就代表這個 lock 已經失效,這就非常方便了
最後實作會類似這樣的 code :
def action_lock(kind , timer: 600 , token: nil , user: current_user)
return false if !timer || timer < 1
#key的格式請自己編
return Rails.cache.increment("ACTION_LOCK_#{user.id}_#{kind}_#{token}" , 1 , :expires_in => timer) == 1
end
有這個基本後,就可以再製作很多的東西,也就是專案的需求
- 封包重送問題
- 同BTC wallet和金額的重複交易問題
- 發送頻率問題
(1):封包重送問題,可以使用 random post token 來完成,類似每次 post from 都產生一個 random key 來當 token,然後 post 出去後就記錄已使用,下次再接收時則阻擋,這樣可以防止類似手機或筆電的連線中斷而封包重送且重複執行的問題,類似 "#{kind}_#{user_id}_#{random_token}"
(2):重複交易其實就很簡單,想辦法把KEY編寫一樣即可,類似 "#{kind}_#{user_id}_#{wallet}_#{amount}" 的方式,並設定過期時間,就可以輕易完成
(3):發送頻率則更簡單,因為是限制單一使用者,則直接把 user_id 當作 key即可,類似 "#{kind}_#{user_id}"
okay打完收工(rock)
2015-10-19
Ruby : Exception bug??
require 'thread'
$temp = Thread.new do
loop do
puts 'loop me'
begin
puts "try thread"
raise Exception.new('QwQ') if rand > 0.5
puts "skip try"
rescue
puts "QwQ"
end
sleep(0.5)
end
puts '...WTF'
end
loop do
puts "runner #{Thread.list.length} #{$temp.status}"
sleep(2)
end
loop 會 fail loop ... 然後把所有的 rescue 改成 rescue Exception => e 就會成功,like
require 'thread'
$temp = Thread.new do
loop do
puts 'loop me'
begin
puts "try thread"
raise Exception.new('QwQ') if rand > 0.5
puts "skip try"
rescue Exception => e
puts "QwQ"
end
sleep(0.5)
end
puts '...WTF'
end
loop do
puts "runner #{Thread.list.length} #{$temp.status}"
sleep(2)
end
......兩個檔案diff只有一行......Orz"......成功...
原因: rescue 預設是 StandardError 而非 Exception ...... 來源:
https://robots.thoughtbot.com/rescue-standarderror-not-exception
2015-09-15
Pebble time CJK language pack
just link: http://blog.kuro.ro/pebble-time-chinese-japanese-language-pack/
2015-09-14
2015-08-26
Raspberry Pi Kiosk on Raspbian Debian Wheezy
@chromium --kiosk --ignore-certificate-errors --incognito --noerrdialogs --disable-restore-session-state "http://127.0.0.1"
& PS : Pi no WebGL support......
& addon : http://www.tweaking4all.com/hardware/raspberry-pi/install-lazarus-pascal-on-raspberry-pi-2/