Category Archives: Programming

从零开始的 Rust 学习笔记(11) —— 让 Breezin 用上 RESTful API 和 Access Token

於是接著上一篇 Rust 學習筆記,讓上次寫的 Breezin 用上 RESTful API 和 Access Token~之前的 HTTP API 的話就是特別樸素的那種,比如

http://10.0.1.2:2275/get?name=fan1
http://10.0.1.2:2275/set?name=fan1&value=2000
http://10.0.1.2:2275/set?name=fan1&value=auto

並且上面的都是 GET 請求,好處就是在瀏覽器裡手動輸入相應的 API 和引數就能呼叫;壞處就是非常不 RESTful,表示是否成功的狀態碼只在返回的 JSON 中,而 HTTP 的狀態碼都是 HTTP 200 OK;其次,動詞 setget 都在 URL 中出現,而不是像 RESTful API 規範的那樣,體現在 HTTP Method 上。

使用 RESTful API 的話,我們的請求就是如下樣子的了~

请求数据

HTTP MethodAPI EndpointDescription
GEThttp://10.0.1.2:2275/api/v1/fansGet all fans status
GEThttp://10.0.1.2:2275/api/v1/fans/:idGet fan status of given :id
GEThttp://10.0.1.2:2275/api/v1/tempsGet all smc temperature sensors' status
PUThttp://10.0.1.2:2275/api/v1/fans/:idUpdate specified property of fan with :id

當然,更新風扇的屬性的話,實際上可寫入的就只有 3 個 —— min, manualoutput。那麼要傳值的話,肯定就是放在 PUT 方法的 body 裡面了~

例如需要設定 fan1 的最低 RPM 為 2000 的話,那麼就使用 PUT 方法訪問的 API Endpoint 是 http://10.0.1.2:2275/api/v1/fans/1,其 body 為

{
  "property": "min",
  "value": 2000
}

同時,因為選擇哪一個風扇是在 URI 上確定的,因此也需要用一下正則表達式去匹配。這裡我們用到的正則表達式如下~

Continue reading 从零开始的 Rust 学习笔记(11) —— 让 Breezin 用上 RESTful API 和 Access Token

从零开始的 Rust 学习笔记(10) —— Breezin

家裡有一臺半閒置的 12 年的 Mac Mini,之前偶爾跑點 Docker 的東西,順便還有把一臺超舊的印表機共享到局域網裡。不過想想應該拿它做點別的事,比如裝個 Linux 然後搭上 v2ray 做軟路由實現透明代理,然後再裝個 Docker 偶爾測試自己寫的 Linux 的東西~

不過這一篇 post 並不是寫如何用 v2ray 在 Linux 上搭軟路由,而是想起現在的軟路由用的是 Raspberry Pi 4。Raspberry Pi 4 在實際使用的時候還是不錯的,但是發熱量比較大,以及 softirq 看起來略有點爆炸 233333

因為打算用 Raspberry Pi 4 做點別的專案,那麼軟路由透明代理的 workload 就交給 Mac Mini 好啦,再寫個程式手動控制一下 Mac Mini 的風扇轉速~這樣就不會因為 workload 比較大,然後晚上風扇轉速太高影響睡眠? 白天的時候倒是基本無所謂。測試的時候發現 3000-3300 RPM 幾乎聽不到聲音,同時也比最低速 1800 RPM 高出一截,不太會因為過熱而出現問題~(╹ڡ╹)

那麼名字就叫 Breezin 好啦(一邊聽彩彩的 Breezin' 一邊寫~

當然,僅僅說完成功能的話,Shell Script 都完全足夠,但是既然是正好在玩 Rust 的話,那就用 Rust 寫來玩玩吧(*^3^)

在 Mac Mini 上安裝了 Ubuntu 18.04 LTS 之後,SMC 報告的風扇和溫度等資訊都被對映在了 /sys/devices/platform/applesmc.768

➜ ~ ls /sys/devices/platform/applesmc.768/fan*
-r--r--r-- 1 root root 4096 12  3 14:52 /sys/devices/platform/applesmc.768/fan1_input
-r--r--r-- 1 root root 4096 12  3 14:52 /sys/devices/platform/applesmc.768/fan1_label
-rw-r--r-- 1 root root 4096 12  3 14:53 /sys/devices/platform/applesmc.768/fan1_manual
-r--r--r-- 1 root root 4096 12  3 14:52 /sys/devices/platform/applesmc.768/fan1_max
-rw-r--r-- 1 root root 4096 12  3 14:54 /sys/devices/platform/applesmc.768/fan1_min
-rw-r--r-- 1 root root 4096 12  3 14:53 /sys/devices/platform/applesmc.768/fan1_output
-r--r--r-- 1 root root 4096 12  3 14:52 /sys/devices/platform/applesmc.768/fan1_safe

這裡可以看到實際可寫入的只有 fan1_manual, fan1_minfan1_output

fan1_label 裡面儲存了風扇的名字。fan1_manual 實際上會被解釋成一個 Boolean 值,0 代表系統控制,1 代表手動設定。

fan1_min 是對應風扇的最低的 RPM,這個是我們可以控制的。相應的 fan1_max 則是最大轉速,但是不能限制風扇的最大轉速。

fan1_input 是表示當前風扇報告的 RPM,但是是一個只讀的量。fan1_output 表示需要對應風扇達到的 RPM,當 fan1_manual 的值為 1 時有效,否則寫入之後也會被系統覆蓋(即自動控制轉速)。

最後的 fan1_safe 看起來大概是該風扇的最低?安全轉速,然而雖然寫了可讀,實際測試的時候並不可讀(也許是 Ubuntu 下 SMC 驅動的問題?)。

➜ ~ cat /sys/devices/platform/applesmc.768/fan1_safe
cat: /sys/devices/platform/applesmc.768/fan1_safe: Invalid argument

在知道了這些對應的對映之後,想法就是在使用者請求資訊的時候,去 glob /sys/devices/platform/applesmc.768/fan*,然後做成 JSON 資料返回。在使用者設定的風扇轉速的時候,就寫入到對應的 output 裡面,並將 manual 設定為 1

於是設想就是做一個 HTTP API,假如 IP 是 10.0.1.2,服務執行在 2275 埠的話,那麼要做的就是如下 2 個 API

Continue reading 从零开始的 Rust 学习笔记(10) —— Breezin

站名牌產生器(((o(*゚▽゚*)o)))

這個站名牌產生器算是超久之前寫的程式了,站名牌的樣式仿自 http://data.but.tw/eki/。在那個網站上還有超多別的城市的地鐵站名牌,然而這個網站並不能生成 SVG 格式的向量圖,其生成的圖片在清晰度上也有所欠缺,於是我就仿照著其中 6 個站名牌做了一點微小的工作~

雖然並不是鐵道廚,但是有些站名牌的確還蠻好看的,於是就做了一個 macOS 下可以生成站名牌 SVG 向量圖,以及可以儲存成縮放任意倍數 PNG 圖片的程式~要是有誰想改成網頁版的話,那就更好了233333

程式碼的話就在這裡啦 ➜ #/eki

下面再放幾張生成好的圖片吧~

Continue reading 站名牌產生器(((o(*゚▽゚*)o)))

Toy BlockChain with GoLang

啊,這個只是一個玩具類型的區塊鏈而已~主要關注在區塊鏈的一部分實現上,暫時沒有涉及到分布式相關的問題。這個項目的文件組織如下~

.
 ├── block.go       // Block struct 的聲明以及相關函數
 ├── blockchain.go  // BlockChain struct 的聲明以及相關函數
 ├── cli.go         // 與 command line 相關的代碼
 ├── pow.go         // 一個簡單的 Proof of Work 實現
 └── utils.go       // 輔助函數

首先是 utils.go,這裏聲明了兩個輔助函數,一個是將 int64 類型的數字轉為其對應的字節表示,另一個是檢查 err 的函數

package main

import (
    "bytes"
    "encoding/binary"
    "fmt"
    "os"
)

//  int64 類型轉為其 Byte Respresentation
//
// Parameter
// ---------
//   num: 要轉換的 int64 數字
//
// Return
// ------
//   該數字對應的 Byte Respresentation
func IntToByte(num int64) []byte {
    var buffer bytes.Buffer
    err := binary.Write(&buffer, binary.BigEndian, num)
    CheckErr("IntToByte(num int64) []byte", err)
    return buffer.Bytes()
}

// 檢查是否出錯
// 
// Parameter
// ---------
//   info: 用戶定義的信息
//   err: error 類的實例
func CheckErr(info string, err error) {
    if err != nil {
        fmt.Printf("[ERROR] %s:%s\n", info, err)
        os.Exit(1)
    }
}

接下來是一個簡單的 Proof of Work 的實現,pow.go,這裡我們假定要求的是 SHA256 的最高 16 個 bit 都為 0 才行

Continue reading Toy BlockChain with GoLang

A Simple Approach to Add Invisible Watermark with OpenCV

摸鱼摸鱼,今天试试用简单的频域隐写水印~

其实隐写水印的方法也有不少了,这里是其中一个简单的方法,使用的是离散傅立叶变换,相比起小波变换的版本,这里的鲁棒性没有那么强,但也还是挺好玩的,下次有时间可以试试看小波变换的方法233333

在频域增加水印的好处是肉眼不易看见,而且对于一般的裁剪、拉伸、涂抹有较强的抵抗性~比如下面的图像就加上隐写的水印,但是与左侧的原图几乎没有视觉上的差异。

在将两张图转换到频域上之后,则一眼能看到右侧图上的水印~

Continue reading A Simple Approach to Add Invisible Watermark with OpenCV

C/C++ 中非法的 void main() 与 main() 函数返回值的作用

这篇 post 是主要是写给 Association of Robots and Artificial Intelligence 的小伙伴的~主要说说为什么 void main() 是非法的,以及 main() 函数的返回值到底有什么用~

先从 void main() 讲起吧~这里就先引用 C++ 之父,Bjarne Stroustrup,在他的博客中写过的一篇问答:Can I write "void main()"?

Can I write "void main()"?

The definition
                                      void main() { /* ... */ }
is not and never has been C++, nor has it even been C. See the ISO C++ standard 3.6.1[2] or the ISO C standard 5.1.2.2.1. A conforming implementation accepts
                                      int main() { /* ... */ }
and
                                      int main(int argc, char* argv[]) { /* ... */ }
A conforming implementation may provide more versions of main(), but they must all have return type int. The int returned by main() is a way for a program to return a value to "the system" that invokes it. On systems that doesn't provide such a facility the return value is ignored, but that doesn't make "void main()" legal C++ or legal C. Even if your compiler accepts "void main()" avoid it, or risk being considered ignorant by C and C++ programmers.

翻译:这种写法 void main() { /* ... */ },从来都没有在 C/C++ 中存在过,根据 ISO C++ 标准 3.6.1 或者 ISO C 标准 5.1.2.2.1,只有 int main() {/* ... */} 或者是 int main(int argc, char* argv[]) {/* ... */} 才是可接受的。

A conforming implementation may provide more versions of main(), but they must all have return type int. The int returned by main() is a way for a program to return a value to “the system” that invokes it. On systems that doesn’t provide such a facility the return value is ignored, but that doesn’t make “void main()” legal C++ or legal C. Even if your compiler accepts “void main()” avoid it, or risk being considered ignorant by C and C++ programmers. —— Bjarne Stroustrup

翻译:符合规范的写法可能会有一些别的版本,但是他们都必须返回 int。这个main()返回的 int是用来会返回给调用这个程序的“系统”的。在没有提供这样的功能的系统中,这个返回值会被忽略,但是那也绝不使得void main在 C/C++ 中是合法的。即便你的编译器让你编译过了,或者就是仔细考虑过这么写的后果/风险

比如,我们来编译一下如下的 C 代码

void main() {
    
}

那么编译时就会报一个警告

编译器告诉我们 main() 函数应该有的返回类型是 int 而不是 void

此外,void main() 也不在 C89 或者 ANSI C 规范中受支持,要么会报错,要么会产生警告。事实上,没有任何一个 C/C++ 标准支持这种形式的 main() 函数。以下是依次以 C89 标准和 ANSI C 标准编译时会有的输出。

Continue reading C/C++ 中非法的 void main() 与 main() 函数返回值的作用

Python 2.7 + Scripting Bridge 导出 iTunes Library 里音乐的 MetaInfo 与封面到 MongoDB

作为某个 Project 的一部分~(暂时不透露是什么,嘻嘻(⁎⁍̴̛ᴗ⁍̴̛⁎) ) 需要把 iTunes 里面的所有音乐的 MetaInfo 和封面导出到 MongoDB 中

MongoDB 上次已经已经在 Raspberry Pi 4 上编译部署好了~在 Raspberry Pi 4 上安装 64-bit MongoDB Server 服务

然后再配合很久以前玩过的 在 Python 里使用 Scripting Bridge 与 iTunes 交互,就可以达到目标了233333

当然需要注意的是,这里要使用的是 macOS 自带的 Python 2.7,因为 ScriptingBridge 只安装在了自带的 Python 2.7 里

真正代码的话,其实整体来说很简单,需要考虑的点是如何做到不重复写封面,因为——

  1. 目前 Scripting Bridge 与 iTunes 交互时,只能一首音乐一首音乐的依次遍历,不能直接按照专辑遍历
  2. 同一张专辑里,有的音乐可能包含多张封面
  3. 不同的专辑可能被我 assgin 过相同的封面

综合这几点考虑的话,那就只能每次拿到有封面的音乐之后,对它的每一张封面都计算 SHA256 摘要(这里暂且认为 SHA256 的空间足够大,不会产生碰撞),并在放进 global_sha256 前,检查是否已经有相同的 SHA256 存在其中。如果没有的话,才保存图片到磁盘中,并放到那首歌的 MetaInfo 中;如果在 global_sha256 中有的话,那么就再看那首歌的 MetaInfo 中有没有这个 SHA256(因为也许有人不小心添加了两张一样的封面到音乐里)。

在遍历完所有音乐之后,把这些 MetaInfo 写入到 JSON 文件中~(就像下面这样

{
  "album": "Cutie Panther", 
  "name": "夏、終わらないで。", 
  "artist": "BiBi (南條愛乃, Pile, 徳井青空)", 
  "cover": [
    "44f9b56091c7ca5b011cd9cb306eab21d4f854300c96347a0a7f3538cbeb9dcd-1"
  ], 
  "composer": "渡辺和紀", 
  "year": 0, 
  "sha256": [
    "44f9b56091c7ca5b011cd9cb306eab21d4f854300c96347a0a7f3538cbeb9dcd"
  ]
}

最后再导进 MongoDB 数据库就可以啦(当然需要安装一下 pymongo 库)~主要的就分为 2 个 stage ♪(´ε` )

python2.7 -m pip install --user pymongo
python2.7 iTunes.py -s 1
python2.7 iTunes.py --host raspberrypi.local -s 2
Continue reading Python 2.7 + Scripting Bridge 导出 iTunes Library 里音乐的 MetaInfo 与封面到 MongoDB

Write a Digital Clock on macOS

昨天打着打着电话,突然玲就问我,macOS 能不能同时显示两个数字时钟,于是我就找了一圈,DashBoard 上虽然可以显示两个,但是都是模拟时钟,而且玲也没买触摸板,在桌面和 DashBoard 之间切换不那么方便。

就计划帮她写一个,于是“产品经理玲”就发给我了一个 prototype (゚o゚;;

写,都可以写(x

Continue reading Write a Digital Clock on macOS

Play the game that sets you free!

As many of macOS users have already enabled TouchID for sudo by adding the line below to /etc/pam.d/sudo.

auth       sufficient     pam_tid.so

That's convenient, however, not interesting at all. Let's do something amusing! What about granting root privilege by winning the floppy bird game! Play the game that sets you free \(≧▽≦)/ Σ(・□・;)

This post will roughly be divided into 3 parts,

  1. Get the original sudo project compiled successfully
  2. Add the floppy bird game to sudo
  3. Test and replace the sudo which ships with macOS to sudo-floppy

1. Get the original sudo project compiled successfully

The very first thing is to fetch the latest source code of sudo on https://opensource.apple.com/tarballs/sudo/. At the time of writing, the latest release is https://opensource.apple.com/tarballs/sudo/sudo-86.50.1.tar.gz. Open the sudo.xcodeproj in Xcode after downloading and unzipping the tarball and we can start!

Continue reading Play the game that sets you free!