rust websocket

サーバーアプリ

extern crate ws;

use ws::{listen, Sender, Handler, Result, Message, CloseCode, Handshake};
use ws::Message::Text;
use ws::Message::Binary;
use ws::util::Token;

struct Server {
   out: Sender,
}

impl Handler for Server {
  fn on_open(&mut self, _shake: Handshake) -> Result<()> {
	  println!("on_open");
	  Ok(())
  }
  fn on_message(&mut self, msg: Message) -> Result<()> {
	  println!("{:?}", msg);
	  Ok(())
  }
}

fn main() {
  listen("[IP]:[PORT]", |out| Server { out: out }).unwrap();
}
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>hello node.js!</title>
    <script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
    <script>
	$(document).ready(function(){
	console.log("hello");
	var sock = new WebSocket('ws://:[IP]:[PORT]');

	sock.addEventListener('open', function(e){
		console.log('Socket open');
	});

	sock.addEventListener('message', function(e){
		console.log(e.data);
	});

	$(':button[name=buttonMsg]').click(function(){
		var msg = $(':text[name=message]').val();
		console.log(msg);
		sock.send(msg);
	});
	});
    </script>
  </head>
  <body>
    <input type="text" name="message" value="">
    <input type="button" name="buttonMsg" value="send message">
    <input type="button" name="buttonDiscon" value="disconnect">
  </body>
</html>

このクライアントをraspiに置いておいて、apacheとかで見られるようにしておけばOK

f:id:katakanan:20181129000745p:plain
OK

Rust lopdf③

e-tipsmemo.hatenablog.com
文字列を抽出するのは最後

let mut textline : Vec<Object>= vec![];

for l in tmp.split('\n') {
	match op(CompleteStr(l)) {
		Ok((_, operate)) => {
			let operator = operate.operator;
			match operator.as_str() {
				"TJ" => {
					textline.extend(operate.operands);
				}
				"ET" => {
					println!("{:?}", textline);
					textline = vec![];
				}
				_ => (),
			}
		}
		Err(_) => (),
	}
}

ストリームは命令の列なのでそれを一行ずつ分解していって、lopdfのOperation構造体に入れ、
それがうまくパースできてTJなら一行の要素としてextendしていって、ET(End Text)が来たらテキストとして書き出す(つもり)。

行儀のいいPDFならPDFとして見たときに、文脈として関係ないテキストは別々のBT~ETの中に入っているだろうと期待していた、
f:id:katakanan:20181202005941p:plain
例えば上の"PB14"と"PA0-WKUP"


そんなこともなかった

BT
4.5002 0 0 4.5024 401.7752 354.5591 Tm
.0001 Tc
(PB14)Tj
-56.6623 9.25 TD
0 Tc
(PA0-WKUP)Tj
75.7583 64.4482 TD
.0001 Tc
(10)Tj
-8.934 0 TD
0 Tc
(9)Tj
-82.2613 -92.2518 TD
(K)Tj
.0835 9.212 TD
(J)Tj
ET

PDF Referenceによると
http://archimedespalimpsest.net/Documents/External/pdf_reference_1-7.pdf
TDが行を変えている命令だから、これがあれば文字列を分解することにしようかと思ったが、
実際はわかれているべきものなのかどうかがわからない

BT
4.5002 0 0 4.5024 315.6859 478.9902 Tm
0 Tc
(V)Tj
1.417 -.5 TD
.0001 Tc
(SS_2)Tj
ET

VSS_2も行の位置を変える命令を用いられている
f:id:katakanan:20181202013552p:plain

これがひとまとまりである単語であるかわかれているべき単語なのかを
判定するのは骨だとおもわれるのでテキストから抽出する作戦はひとまず終了

次はTableから抽出してみようと思う。

PDF構造解説

PDF構造解説

Adobe Acrobat DC ? PDF Readerそのほか

Adobe Acrobat DC ? PDF Readerそのほか

Ryzen 録画サーバー 電力測定③ (Rust serialport ②)

e-tipsmemo.hatenablog.com
e-tipsmemo.hatenablog.com

とりあえずRustでWattChecker-REXのサンプルコードと同じことができるようになった

fn main() {
    println!("Hello, world!");
    let mut wc = WattChecker::default();
    wc.init();
    wc.start_measure();

    let running = Arc::new(AtomicBool::new(true));
    let r = running.clone();
    ctrlc::set_handler(move || {
        r.store(false, Ordering::SeqCst);
    }).expect("Error setting Ctrl-C handler");

    while running.load(Ordering::SeqCst)
    {
        match wc.request_measure() {
            Ok(cmd) => disp_data(cmd),
            Err(e) => eprintln!("{:?}", e),
        }

        thread::sleep(Duration::from_millis(2000));
    }

    println!("stop measuring");
    wc.stop_measure();
}

本当はportをcloneして別スレッドで動かすことで、
WriteとReadのスレッドを分けることもできるがそれは後で。

f:id:katakanan:20181123180256p:plain
せっかくリアルタイムで測定してくれているので
これをWeb画面から表示できるようにしてみたい。

rust serialport①

e-tipsmemo.hatenablog.com
電力測定のためのアプリを作るために
Rustでserialportを操作する方法を調べた。

非同期だったりとかいろいろあってよくわからなかったが結果的に動いた
serialport-rsを利用することにした。

https://crates.io/crates/serialport

wattchecker-rexが配布しているCのコードで送っているコマンドを表示させた。
f:id:katakanan:20181122221418p:plain

RTC設定→リアルタイム測定2秒ごと繰り返しという処理内容
コマンドをどうやって組み立てているかは見ればわかる

ひとまず
timer : [n]というコマンドを送って
buf[n]という応答が帰ってくればよさそう

fn main() {
    println!("Hello, world!");

    let settings = serialport::SerialPortSettings{
        baud_rate : 115200,
        data_bits : DataBits::Eight,
        flow_control : FlowControl::None,
        parity : Parity::None,
        stop_bits : StopBits::One,
        timeout : Duration::from_millis(1)
    };

    let mut port = match serialport::open_with_settings("/dev/rfcomm0", &settings)
    {
        Ok(p) => p,
        Err(_) => panic!("Error"),
    };

    println!("{:?}", port.settings());
    let cmd = vec![ここにコマンドを入れておく];

    for b in cmd.clone()
    {
        println!("timer : 0x{:02X}", b);
    }

    match port.write(&cmd)
    {
        Ok(_) => println!("OK"),
        Err(e) => eprintln!("{:?}", e),
    }

    let mut buffer : [u8; 1] = unsafe{mem::uninitialized()};
    loop{
        match port.read(&mut buffer)
        {
            Ok(bytes) => if bytes == 1
            {
                println!("0x{:02X}", buffer[0]);
            },
            Err(ref e) if e.kind() == io::ErrorKind::TimedOut => (),
            Err(e) => eprintln!("{:?}", e),
        }
    }
}

serialportのサンプルコードをそのまま利用
Err(ref e)でタイムアウトのエラーを無視している。
これで特定の長さを受信したら外のloopをbreakすればよさそう。

f:id:katakanan:20181122222046p:plain

うまくいった。(この時はすでにRTCコマンドも計算してしまった)
コマンドの計算にはCRC8なども出てくるのでそれはCRC8-crateなどを利用した。

次回は別スレッドでコマンドを送信しながら
方やデータ受信してそれをどうにかするというものを書く予定。(サンプルdeplexを利用すれば簡単そう)

プログラミングRust

プログラミングRust

Raspberry Pi 3 MODEL B

Raspberry Pi 3 MODEL B

Raspberry Pi 3 Model B+ スターターセット BASIC

Raspberry Pi 3 Model B+ スターターセット BASIC

Rust lopdf②

e-tipsmemo.hatenablog.com
の続き。

lopdfであるページのリファレンス番号を列強することはできたので、
その中からstreamを持つものだけを抽出したい。

ページ1から参照されているオブジェクトを一つずつ見ていく。

get_object()で番号が参照するオブジェクトを得られるが
そのオブジェクトはいろいろなものを内包している可能性があるので
streamの時だけVecに入れるようにした。

あとはstreamからBT ~ ETを抜き出す?

PDF構造解説

PDF構造解説

Adobe Acrobat DC ? PDF Readerそのほか

Adobe Acrobat DC ? PDF Readerそのほか

Rust lopdf

RustでPDFを弄くろうと思ったのでlopdfというライブラリを利用した。
まず前提として、PDFのフォーマットをちょっとしらないと意味ないので。

PDF 構文 -ファイル 解析手順-
とか
詳細PDF入門 ー 実装して学ぼう!PDFファイルの構造とその書き方読み方 - プログラムモグモグ
を斜め読

PDFのstreamの内容がFlateDecodeされていると面倒なので

pdftk test.pdf output test2.pdf uncompress

を行った

最終的にはテキストを抜き出したい
(pdftotextを試したがうまく行かなかった。)

[dependencies]
lopdf = "0.19.0"
extern crate lopdf;

use lopdf::content::{Content, Operation};
use lopdf::Object::*;
use lopdf::{Document, Object, Stream};
use std::str::from_utf8;

fn main() {
    let doc = Document::load("test2.pdf");
    let test = match doc {
        Ok(a) => a,
        Err(_) => panic!("load error"),
    };

    let pages = test.get_pages();
    let id = pages.get(&1).unwrap();
    let contents = test.get_page_contents(*id);
    println!("Refered from Page 1\n{:?}", contents);
}

ページ1から参照されているオブジェクトの番号がVecで得られるが
そのオブジェクトもまたどこかのオブジェクトを参照している可能性がある

pub enum Object {
    Null,
    Boolean(bool),
    Integer(i64),
    Real(f64),
    Name(Vec<u8>),
    String(Vec<u8>, StringFormat),
    Array(Vec<Object>),
    Dictionary(Dictionary),
    Stream(Stream),
    Reference(ObjectId),
}

とのことなので
ObjectのArrayなら順番にその先を見に行って
streamならそれを取り出すように、たぶん再帰的な実装が必要?

Ryzen 録画サーバー 電力測定②

e-tipsmemo.hatenablog.com
の続き・・・・・



だが、Raspberry piで何度bluetoothctlを利用してWattcheckerに接続し直しても
一向にserialportサービスが見えない・・


結局OSをRaspberry pi Stretchに変えたらうまくいってしまった・・・

www.raspberrypi.org
まえはJessieだったかをインストールしていたはず。
前のOSでは

sdptool browse YY:YY:YY:YY:YY:YY

としても。

sdptool なんとかかんとか host is down

とエラーがでて相手のデバイスが公開しているサービスが確認できなかった。

OSを変えたところ

sdptool browse YY:YY:YY:YY:YY:YY
Browsing YY:YY:YY:YY:YY:YY ...
Service RecHandle: 0x10001
Service Class ID List:
"PnP Information" (0x1200)
Protocol Desc....

とすんなり見ることができた。

結局

sdptool browse YY:YY:YY:YY:YY:YY | grep -i serial

sudo sdptool browse local | grep -i serial

の両方を試して、両方にserial portサービスが出てくればOK
そのためにはbluetooth.serviceをcompatibleオプションで起動するとか
sdptool add SPするとかが必要になってくる
エアコンの消費電力をRaspberry Pi 3B+とBluetoothワットチェッカー REX-BTWATTCH1で常時モニタリング。電圧・電流・電力もグラフ化! | IoT PLUS
ここに書いてあるとおり。

f:id:katakanan:20181031023251p:plain

いとも簡単にできてしまった。。。

つぎは、データをうまいこと自作ソフトウェアで取得できるようにしたい。

注意

Raspberry pi zero Wだとbluetooth LE (BLE)が乗っていて普通のBluetooth 4.0とかではないらしいので
Serial Port プロファイルはないらしい。
そのかわりGATTというBLE用のプロファイルは普通に使える。
また、このWattcheckerも一応BLEに対応しているらしいので
GATTを介した定期的なデータ取得は可能かもしれない。

が普通にBluetooth4.0などを使ったほうがはるかに楽かもしれない。