WSLを試してみる。

今、週末やっているサポートの仕事の対象がクライアント側だけだったのが、来年からサーバー側も面倒を見ることになった。

サーバーはVPS上のLinuxPHP + PostgreSQL の環境で動作している。検証・開発用の環境を新たに作る必要があるのでどうするか考え中。

案としては、

  1. 本番環境とどうようにVPSを借りてLinuxのサーバーを立ち上げる。
  2. クライアントの開発用マシンに Vagrant で仮想サーバーを立ち上げる。
  3. クライアントの開発用マシンのWSL上に PHPPostgreSQL をインストールする。

が浮かんだが、一番手軽な案3で試してみることにする。

とりあえずの下準備として、Microsoft Store で Ubuntu 18.04 LTSをインストールして、apt update & apt upgrade を済ませる。


追記 2019.12.14

WSL 環境に、apt で、PostgreSQL, PHP, Apache2 をインストール。普通に apt install hoge hoge するだけで特に問題なし。
WSL とは関係ないけど、PostgreSQL がローカルホストで接続する場合、Pear 認証が有効になっているため WSLのユーザーアカウント名と PostgreSQLに接続するロールの名称が一致していないとはじかれてしまう。回避方法はあるらしいけど、面倒なのでWSLのアカウントと 同じロールを作成することにした。

M5Stack 加速度センサーを使ってみる。

日曜電子工作の続き。今回は M5Stack Gray に内蔵されている加速度センサーを使ってみる。

入門書「みんなのM5Stack入門」に簡単なサンプルが掲載されているので写経してみるが、ちょっと問題がある。書籍では9軸のセンサーにMPU9250というチップが使用されていることが前提になっているが、最近の出荷分はMPU6886という別のチップが搭載されており、そのままでは動作しない。そこで、サンプルのMPU9250を使用しているコードをMPU6886用のコードに書き換える必要がある。

作業は基本逐次的な置き換えで済むので難しくはない。というか、MPU6886の方がキャリブレーションが必要なかったりX,Y,Z 3軸の値を一度の関数呼び出しで取得できたりとむしろ簡易になっている。

書籍の水準器のサンプル level0.ino をMPU6886用に修正したものが次のソース。

#include <M5Stack.h>
#include "utility/MPU6886.h"

MPU6886 IMU;  // Create MPU6886 Object

void drawGrid() {
  M5.Lcd.drawLine(41, 120, 279, 120, CYAN);
  M5.Lcd.drawLine(160, 1, 160, 239, CYAN);
  M5.Lcd.drawCircle(160, 120, 119, CYAN);
  M5.Lcd.drawCircle(160, 120, 60, CYAN);
}

void drawSpot(int ax, int ay) {
  int x, y;
  x = map(constrain(ax, -300, 300), -300, 300, 40, 280);
  y = map(constrain(ay, -300, 300), -300, 300, 240, 0);
  M5.Lcd.fillScreen(BLACK);
  drawGrid();
  M5.Lcd.fillCircle(x, y, 7, WHITE);
}

void setup() {
  M5.begin();     // INIT M5Stack
  Wire.begin();   // INIT I2C
  IMU.Init();     // Init MPU6886
  
}

void loop() {
  float ax, ay, az;
  IMU.getAccelData(&ax, &ay, &az);
  drawSpot((int)(ax * 1000), (int)(ay * 1000));
  delay(100);
}

修正箇所は、

  1. 9軸センサーのライブラリ用ヘッダファイルを MPU6886.h に変更。
  2. それにともない、ライブラリのクラスもMPU6886に変更。
  3. 初期化は Init() メソッドを呼ぶだけに。キャリブレーションは不要。
  4. 加速度の取得は getAccelData() メソッドを呼ぶだけに。スケールを取得しての補正は不要。

という感じ。

Rust 入門 簡単な関数を書いてみる(5)

Rust 入門続き。

住所を正規化する関数、ひとまず完成。 住所の文字列から、都道府県、市区町村、番地、建物を分割し表記を正規化した構造体を返す。

文字列を文字単位に分割するには、chars() を呼び出せばよいというのはすぐに分かったが、char からユニコードのコードポイントに変換するのにちょっと迷った。
答えは知ってみれば単純で、単に u32にキャストしてやれば良いだけだった。逆にコードポイントから char に変換するには、

char::from_u32(0xff10).unwrap()

とすれば良い。

関数はトレイトを利用して構造体の静的メソッド風に呼び出されるようにしてみた。
Rustにはクラスはないが、トレイトを定義することで構造体にメソッドを生やすことができる。ここらへん golang に似てる。

extern crate regex;
use regex::Regex;
use std::collections::HashMap;

const PREFECTURES:[(&str, &str); 47] = [
        ("01","北海道"), ("02","青森県"), ("03","岩手県"), ("04","宮城県"), ("05","秋田県"),
        ("06","山形県"), ("07","福島県"), ("08","茨城県"), ("09","栃木県"), ("10","群馬県"),
        ("11","埼玉県"), ("12","千葉県"), ("13","東京都"), ("14","神奈川県"), ("15","新潟県"),
        ("16","富山県"), ("17","石川県"), ("18","福井県"), ("19","山梨県"), ("20","長野県"),
        ("21","岐阜県"), ("22","静岡県"), ("23","愛知県"), ("24","三重県"), ("25","滋賀県"),
        ("26","京都府"), ("27","大阪府"), ("28","兵庫県"), ("29","奈良県"), ("30","和歌山県"),
        ("31","鳥取県"), ("32","島根県"), ("33","岡山県"), ("34","広島県"), ("35","山口県"),
        ("36","徳島県"), ("37","香川県"), ("38","愛媛県"), ("39","高知県"), ("40","福岡県"),
        ("41","佐賀県"), ("42","長崎県"), ("43","熊本県"), ("44","大分県"), ("45","宮崎県"),
        ("46","鹿児島県"), ("47","沖縄県")    
    ];

const HANKAKU_ZENKAKU_KANA_CHARS:[(&str, &str); 89] = [
    ("ヴ", "ヴ"), 
    ("ガ", "ガ"), ("ギ", "ギ"), ("グ","グ"), ("ゲ", "ゲ"), ("ゴ","ゴ"), 
    ("ザ","ザ"), ("ジ","ジ"), ("ズ","ズ"), ("ゼ","ゼ"), ("ゾ","ゾ"), 
    ("ダ","ダ"), ("ヂ","ヂ"), ("ヅ","ヅ"), ("デ","デ"), ("ド","ド"), 
    ("バ","バ"), ("ビ","ビ"), ("ブ","ブ"), ("ベ","ベ"), ("ボ","ボ"), 
    ("パ","パ"), ("ピ","ピ"), ("プ","プ"), ("ペ","ペ"), ("ポ","ポ"), 
    ("ア","ア"), ("イ","イ"), ("ウ","ウ"), ("エ","エ"), ("オ","オ"),
    ("カ","カ"), ("キ","キ"), ("ク","ク"), ("ケ","ケ"), ("コ","コ"),
    ("サ","サ"), ("シ","シ"), ("ス","ス"), ("セ","セ"), ("ソ","ソ"),
    ("タ","タ"), ("チ","チ"), ("ツ","ツ"), ("テ","テ"), ("ト","ト"),
    ("ナ","ナ"), ("ニ","ニ"), ("ヌ","ヌ"), ("ネ","ネ"), ("ノ","ノ"),
    ("ハ","ハ"), ("ヒ","ヒ"), ("フ","フ"), ("ヘ","ヘ"), ("ホ","ホ"),
    ("マ","マ"), ("ミ","ミ"), ("ム","ム"), ("メ","メ"), ("モ","モ"),
    ("ヤ","ヤ"), ("ユ","ユ"), ("ヨ","ヨ"), 
    ("ラ","ラ"), ("リ","リ"), ("ル","ル"), ("レ","レ"), ("ロ","ロ"),
    ("ワ","ワ"), ("ヲ","ヲ"), ("ン","ン"),
    ("ァ","ァ"), ("ィ","ィ"), ("ゥ","ゥ"), ("ェ","ェ"), ("ォ","ォ"),
    ("ャ","ャ"), ("ュ","ュ"), ("ョ","ョ"), ("ッ","ッ"),
    ("、","、"), ("。","。"), ("ー","ー"), ("「","「"), ("」","」"),
    ("゙","”"), ("゚","'"), ("・","・")    
];

// 漢数字をアラビア数字に変換する
fn kanji_numeral_to_arabic_numerals(s: &str) -> String {

    // 漢数字を半角数字に置換する
    fn z2h(s: &str) -> String {
        s.replace("一", "1")
        .replace("壱", "1")
        .replace("1", "1")
        .replace("二", "2")
        .replace("弐", "2")
        .replace("2", "2")
        .replace("三", "3")
        .replace("参", "3")
        .replace("3", "3")
        .replace("四", "4")
        .replace("4", "4")
        .replace("五", "5")
        .replace("5", "5")
        .replace("六", "6")
        .replace("6", "6")
        .replace("七", "7")
        .replace("7", "7")
        .replace("八", "8")
        .replace("8", "8")
        .replace("九","9")
        .replace("9", "9")
        .replace("〇", "0")
        .replace("0", "0")
    };

    // 変換処理
    fn convert(s: &str, re: &Regex) -> i64 {

        let mut digits: HashMap<&str, i64> = HashMap::new();
        digits.insert("十", 10);
        digits.insert("拾", 10);
        digits.insert("百", 100);
        digits.insert("千", 1000);
        digits.insert("万", 10000);
        digits.insert("億", 100000000);
        digits.insert("兆", 1000000000000);

        let reg_divide_digit_unit:Regex = Regex::new("[十拾百千]|\\d+").unwrap();
        let reg_arabic_numerals:Regex = Regex::new("^[0-9]+$").unwrap();

        let mut unit:i64 = 1;
        let mut result:i64 = 0;
        for piece in re.find_iter(s).map(|m| m.as_str()).collect::<Vec<&str>>().iter().rev() {
            if digits.contains_key(piece) {
                if unit > 1 {
                    result += unit;
                }
                unit = digits[piece];
            } else {
                if reg_arabic_numerals.is_match(piece) {
                    result += piece.parse::<i64>().unwrap() * unit;
                } else {
                    result += convert(piece, &reg_divide_digit_unit) * unit;
                };
                unit = 1;            
            }
        }
        if unit > 1 {
            result += unit;
        }

        result
    };
    
    convert(&z2h(s), &Regex::new("[万億兆]|[^万億兆]+").unwrap()).to_string()
}

// 住所から都道府県とそれ以降を分割する
fn get_prefecture(address: &str) -> (String, String) {
    let prefectures = PREFECTURES.iter().map(|x| x.1).collect::<Vec<&str>>().join("|");
    let pattern = format!("^({})(.+*)$", prefectures);
    let re = Regex::new(&pattern).unwrap();

    match re.captures(address) {
        Some(m) => ((&m[1]).to_string(), (&m[2]).to_string()),
        None => ("".to_string(), address.to_string())
    }
}

// 住所から市区町村とそれ以降を分割する
fn get_city(address: &str) -> (String, String) {
    let re1 = Regex::new(concat!(
        "^(余市郡(仁木町|赤井川村|余市町)|余市町|柴田郡村田町|(武蔵|東)村山市|",
        "[東西北]村山郡...?町|田村(市|郡..町)芳賀郡市貝町|(佐波郡)?玉村町|[羽大]村市|",
        "(十日|大)町市|(中新川郡)?上市町|(野々|[四廿]日)市市|西八代郡市川三郷町|",
        "神崎郡市川町|高市郡(高取町|明日香村)|(吉野郡)?下市町|(杵島郡)?大町町)(.+)"
    )).unwrap();
    let re2 = Regex::new("^(.+[市区町村])(.+)").unwrap();

    match re1.captures(address) {
        Some(m) => {
            match re2.captures(&m[12]) {
                Some(m2) => (format!("{}{}",(&m[1]),(&m2[1])), (&m2[2]).to_string()),
                None => ((&m[1]).to_string(), (&m[12]).to_string())
            }
        },
        None => {
            match re2.captures(address) {
                Some(m2) => ((&m2[1]).to_string(), (&m2[2]).to_string()),
                None => ("".to_string(), address.to_string())
            }
        }
    }
}

// 半角カナを全角カナに、全角英数を半角英数に変換する
fn pre_convert(s: &str) -> (String) {
    let mut tmp = s.replace(" ", " ");
    for (hankaku, zenkaku) in HANKAKU_ZENKAKU_KANA_CHARS.iter() {
        tmp = tmp.replace(hankaku, zenkaku);
    }

    tmp.chars().map(|ch| {
        let code_point = ch as u32;
        if code_point >= 0xff10 && code_point <= 0xff51 {
            std::char::from_u32(code_point - 0xfee0).unwrap()
        } else {
            ch
        }
    }).collect()
}

// 建物名の正規化
fn normalize_building_name(name: &str) -> String {
    let re = Regex::new("(.+?)(([\\d〇一二三四五六七八九十百千万]+)(階|F|F|号|号室))$").unwrap();
    match re.captures(name) {
        Some(m) => {
            let floor = kanji_numeral_to_arabic_numerals(&m[3]);
            let mut suffix = &m[4];
            if suffix == "F" {
                suffix = "階"
            }
            format!("{}{}{}", &m[1], floor, suffix)
        },
        None => {
            name.to_string()
        }
    }
}

// 住所から番地と建物名に分割する
fn get_address(address: &str) -> (String, String) {
    let all_num = "[\\d一二三四五六七八九十百千万]+";
    let pattern = format!("(.*?)({}({}|丁目|丁|番地|番|号|-|‐|ー|−|の|東|西|南|北)*({}|丁目|丁|番地|番|号))(.*)",
                    all_num, all_num, all_num);
    let re = Regex::new(&pattern).unwrap();
    match re.captures(address) {
        Some(m) => {
            let re2 = Regex::new(all_num).unwrap();
            if re2.is_match(&m[2]) {
                let address_number = re2.captures_iter(&m[2]).map(|x| (&x[0]).to_string()).collect::<Vec<String>>().join("-");
                (format!("{}{}", &m[1], address_number), normalize_building_name(&m[5]))
            } else {
                (format!("{}{}", &m[1], &m[2]), normalize_building_name(&m[5]))
            }
        },
        None => {
            (address.to_string(), "".to_string())
        }
    }
  }

// 住所構造体 
#[derive(Debug)]
pub struct Address {
    prefecture: String,
    city: String,
    address: String,
    building_name: String
}

impl Address {
    // 文字列から住所を正規化して構造体として返す
    pub fn from(addr: &str) -> Address {
        let (prefecture, tmp1) = get_prefecture(&pre_convert(addr));
        let (city, tmp2) = get_city(&tmp1);
        let (address, building_name) = get_address(&tmp2);

        Address {
            prefecture,
            city,
            address,
            building_name
        }
    }
}

詳解Swift 第5版

「詳解Swift」の第5版が出たので購入。 今回から判型が小型になって驚いた。

初版から出るたびに買い直しているが、第5版の目玉はカスタム属性とDSLに関する解説だろうか? SwiftUI に関係する章だが普通にSwiftUIで画面をポチポチ作る分には直接出てくる部分ではなさそうなので、そこら変は必要が出てきたらちゃんと読もうという感じでななめ読みで済ましてしまった。

基本的な文法の解説などに関しては、第4版から大きく変わったという印象はなし。

詳解 Swift 第5版

M5Stack ではじめてのLチカ

f:id:tricogimmick:20191117095929j:plain

先日購入したM5Stakは書籍に連動したスターターキットなので、LEDやセンサ、ブレッドボードなどがおまけについてくる。日曜工作ではじめてのLチカに挑戦してみることに。

といっても、やることはブレッドボードにLEDと抵抗をぶっ刺し、M5スタックのデジタル出力とグランドにジャンパワイヤで繋ぐだけ。あとは数行のスケッチを写経してM5Stackに転送するだけ。いとも簡単にLEDが点滅した。電気工作なんて小学生以来だが当時はブレッドボードなんて便利なものはなく全部半田ごてだった。それを思うと隔世の感がある。

ついでに、温度センサも試してみる。こちらは抵抗も不要なので直接センサをジャンパワイヤでM5Stackに繋げることができる。こちらも書籍のサンプルコードを写経して転送するとあっさり動く。ただ、アナログデータの読み込み時にM5Stackの内蔵スピーカーからプッという音が出る。なんかノイズを拾ってるっぽい。

検索してみると、「M5Stack analogRead() ノイズ問題(20180407解決) - t-yosh’s blog」という記事を見つけた。記事によると、スピーカーが接続されているGPIOの25番のピンに対して0を書き込んでやると良いとのこと。写経したスケッチの setup() 関数内に

dacWrite(25, 0);

を追記したところノイズは無事消えた。先人の知恵に感謝。

とりあえず、GPIOを使ったデジタルやアナログの入出力は簡単にできることは判ったんで、これを使ってなにか遊びたい。何か良いネタはないものか。

t-yosh.hatenablog.com

Rust 入門 簡単な関数を書いてみる(4)

Rust入門の続き

住所から都道府県と市区町村を抜き出す関数はできたので、次は番地と建物を抜き出す必要があるが、その前に番地等の表記ゆれを正規化するために、漢数字をアラビア数字に変換する関数を書く。

Rust でも、PascalC#みたいに関数内関数を書けるのだな。 三項演算子と同様、経験的に使う人と使わない人がはっきり分かれる機能だと思うけど、僕は結構好んで使う方だ。

// 漢数字をアラビア数字に変換する
fn kanji_numeral_to_arabic_numerals(s: &str) -> String {

    // 漢数字を半角数字に置換する
    fn z2h(s: &str) -> String {
        s.replace("一", "1")
        .replace("壱", "1")
        .replace("1", "1")
        .replace("二", "2")
        .replace("弐", "2")
        .replace("2", "2")
        .replace("三", "3")
        .replace("参", "3")
        .replace("3", "3")
        .replace("四", "4")
        .replace("4", "4")
        .replace("五", "5")
        .replace("5", "5")
        .replace("六", "6")
        .replace("6", "6")
        .replace("七", "7")
        .replace("7", "7")
        .replace("八", "8")
        .replace("8", "8")
        .replace("九","9")
        .replace("9", "9")
        .replace("〇", "0")
        .replace("0", "0")
    };

    // 変換処理
    fn convert(s: &str, re: Regex) -> i64 {

        let mut digits: HashMap<&str, i64> = HashMap::new();
        digits.insert("十", 10);
        digits.insert("拾", 10);
        digits.insert("百", 100);
        digits.insert("千", 1000);
        digits.insert("万", 10000);
        digits.insert("億", 100000000);
        digits.insert("兆", 1000000000000);

        let reg_arabic_numerals:Regex = Regex::new("^[0-9]+$").unwrap();

        let mut unit:i64 = 1;
        let mut result:i64 = 0;
        for piece in re.find_iter(s).map(|m| m.as_str()).collect::<Vec<&str>>().iter().rev() {
            if digits.contains_key(piece) {
                if unit > 1 {
                    result += unit;
                }
                unit = digits[piece];
            } else {
                if reg_arabic_numerals.is_match(piece) {
                    result += piece.parse::<i64>().unwrap() * unit;
                } else {
                    let reg_divide_digit_unit:Regex = Regex::new("[十拾百千]|\\d+").unwrap();
                    result += convert(piece, reg_divide_digit_unit) * unit;
                };
                unit = 1;            
            }
        }
        result
    };
    
    convert(&z2h(s), Regex::new("[万億兆]|[^万億兆]+").unwrap()).to_string()
}

Rust 入門 簡単な関数を書いてみる(3)

Rust 入門つづき。

引き続き住所を正規化する関数を書きながらRustの学習を進めているのだが、なんだかまだ理解できない状況にぶつかった。

正規表現でマッチした箇所を逆順に処理したく、試行錯誤の結果とりあえず次のような記述でいけそうなトコまでは辿り着いた。

let v: Vec<&str> = re.find_iter(s).map(|m| m.as_str()).collect();
for piece in v.iter().rev() {
    // 何かしたい処理
}

しかし、こう書くと「cannot infer type for B」というコンパイルエラーになる。

for piece in re.find_iter(s).map(|m| m.as_str()).collect().iter().rev() {
  // 何かしたい処理
}

これは、

let v: = re.find_iter(s).map(|m| m.as_str()).collect();

こう書くと、結果の型推論が利かずコンパイルエラーになるのと同じ原因だと思うのだが、何故そうなるのかがまだ理解できていない。

Rust なかなか手ごわい。


追記

collect() は型ヒントで型を明示しないといけないというのを完全に忘れていた。 という訳で型を指定することで1行で書けるようになった。

for piece in re.find_iter(s).map(|m| m.as_str()).collect::<Vec<&str>>().iter().rev() {
  // 何かしたい処理
}

前に書いた、都道府県を抜き出す処理ではキチンと collect() を呼ぶときに型指定してんのに、何で忘れるかなぁ。