YU2TA7KA's BLOG ~take one step at a time~

派生開発、組み込み開発周りのこと。

RustでJenkinsのXFDを試作してみた on Raspberry Pi3 B+

はじめに

Rustを使って何か組み込みっぽいことをできないかと思っていたときに同僚の方からXFD作ってみるのは?と提案いただき、試作してみました。思った以上にラズパイでRustビルドさせるのがしんどかった(時間がかかる)のが今回の一番の学びでした。

XFDとは?

eXtreme Feedback Deviceの略称で、プロジェクトステータスやメトリクスなどの結果を目に見える装置で表現して、エラーの見落としを防ぐ+楽しさを場に提供するもの、です。
- XFD TOP

作ったもの

構成

f:id:yuji-tanaak:20210503142630p:plain

環境

PC Raspberry Pi3 B+
OS Raspbian 10.9
監視対象 Jenkins
プログラミング言語 Rust 1.51.0

Jenkinsのローカルサーバ、Rustのビルド実行、LED制御すべてをラズパイで行っています。

概要説明

プログラムを実行するとJenkinsの最新のビルド結果を取得します。ビルド結果が正常であればLEDが点灯、異常であれば点滅するというシステムになります。

わかったこと

XFDの試作にRaspberry Piは便利

XFDの構成要素として、監視対象とHW制御の2つが必要ですが、両方をまとめて実装できる環境としてRaspberry Piがぴったりと思います。LinuxOSのためサーバ構築が容易ですし、Lチカはラズパイの本業です。今回はRustを使いたかったのでRustにしましたが、多分Pytyonでやればさらに手軽に色々できるような気がします。

Rustのcrateが優秀なことを実感

プログラミング言語Rustにはcrate(クレート)という仕組みがあります。これは他のプログラミング言語でいう「ライブラリ」や「パッケージ」と同義です。今回はJenkinsビルド結果を取得するAPIのcrateとLED制御のcrateを利用しました。各crateで今回やりたいことは一瞬で実現できました。エラーハンドリング周りも考慮されていて、今後拡張する際に非常に頼もしいです。
使用したcrateは以下です。
jenkins_api = "0.8.0"
rppal = "0.12.0"

Raspberry Pi3でRustの開発はしんどい

Raspberry Pi3だと環境は整っているのですが、各crateを組み込んでビルドすると数分かかります。しかも初回のcrate導入時のコンパイルには数時間かかりました笑。これはしんどいです!なお、ラズパイ4ならスペック上がっているので割と開発いけるかもしれません*1

やってみたいこと

リファクタリング

いまはmain()に全部書いてしまっているので、関数化したり、構造見直したり、まともにしていきたいです。

監視端末とフィードバックデバイスの分離

今回は全てラズパイに収めましたが、監視するPCとフィードバックデバイスを分けたいです。PC側で結果の判定制御までを行って、フィードバックデバイスには結果に応じた動作要求のみを行う感じです。

フィードバック装置の検討

一個のLEDを光らせているだけでは寂しいので、もっと面白いフィードバック装置を考えたいです。もっと光らす、旗を振る、椅子が震えるとか。

おわりに

わりとすんなり動作確認まで試作ができて良かったです。LED光るとやっぱり楽しいですね!




参考

ソースコード

use jenkins_api::build::BuildStatus;
use jenkins_api::JenkinsBuilder;

use std::error::Error;
use std::thread;
use std::time::Duration;

use rppal::gpio::Gpio;

const GPIO_LED: u8 = 25;

fn main() -> Result<(), Box<dyn Error>> {

    // access to Jenkins
    let jenkins = JenkinsBuilder::new("http://localhost:8080")
        .with_user("username", Some("password"))
        .build()
        .unwrap();

    // get job
    let job = jenkins.get_job("job name").unwrap();

    // get last build status
    let to_build = if let Some(short_build) = job.last_build.clone() {
        // print last build status
        let build = short_build.get_full_build(&jenkins).unwrap();
        println!(
            "last build for job {} at {} was {:?}",
            job.name, build.timestamp, build.result
        );

        //LED control
        if let Some(result) = build.result {
            let mut pin = Gpio::new()?.get(GPIO_LED)?.into_output();
            pin.set_reset_on_drop(true);
            if result == BuildStatus::Success {
                println!("build is success");
                //5秒点灯
                pin.set_high();
                thread::sleep(Duration::from_millis(5000));
            } else {
                println!("Error: couldn't build job. ");
                //5回点滅
                for _ in 0..5 {
                    pin.set_high();
                    thread::sleep(Duration::from_millis(1000));
                    pin.set_low();
                    thread::sleep(Duration::from_millis(1000));
                }
            }
            result != BuildStatus::Success
        } else {
            true
        }
    } else {
        println!("job {} was never built", job.name);
        true
    };

    Ok(())
}

[package]
name = "hello"
version = "0.1.0"
authors = ["pi"]
edition = "2018"

[dependencies]
jenkins_api = "0.8.0"
rppal = "0.12.0"

エラー対応

・ラズパイにRustをインストールしようとするとRAM不足が発生、以下で解決。

export RUSTUP_UNPACK_RAM=16777216

・ラズパイにJenkinsをインストールしようとするとGPGエラー発生、以下を参考にして解決。
apt-get update時に「公開鍵を利用できないため、以下の署名は検証できませんでした」と出た場合 - Qiita

*1:買いたいけど買っていない。。。