Rust websocket/Ryzen サーバー電力測定④

e-tipsmemo.hatenablog.com
の続きで
e-tipsmemo.hatenablog.com
ができるようになったので。

測定された時刻、電圧、電流、ワット数をserde_jsonシリアライズして送信
Cargo.toml

serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
#[derive(Serialize, Deserialize, Debug)]
struct Measdata{
    time: String,
    voltage: f32,
    current: f32,
    wattage: f32,
}

fn parse_data(d: Vec<u8>) -> Measdata
{
    let t = format!("20{:02}/{:02}/{:02} {:02}:{:02}:{:02}", d[19], d[18], d[17], d[16], d[15], d[14]);
    let c = ((d[7] as u32) << 16)|((d[6] as u32) << 8)|((d[5] as u32));
    let v = ((d[10] as u32) << 16)|((d[9] as u32) << 8)|((d[8] as u32));
    let p = ((d[13] as u32) << 16)|((d[12] as u32) << 8)|((d[11] as u32));
    Measdata{time:t,
            voltage:(v as f32)/1000.0,
            current:(c as f32)/128.0,
            wattage:(p as f32)*5.0/1000.0}
}

メインループ

while running.load(Ordering::SeqCst)
{
	match wc.request_measure() {
		Ok(data) => {
			let m = parse_data(data);
			let json_str = serde_json::to_string(&m).unwrap();
			println!("{}", json_str);
			sender.send(json_str);
		},
		Err(e) => eprintln!("{:?}", e),
	}
	thread::sleep(Duration::from_millis(2000));
}

serde_jsonは非常に便利。

f:id:katakanan:20190112003449p:plain

OK
あとはこのjsonからグラフ化してみる予定。

Ryzenは(Ryzen5 2600Xを使用している)エンコ爆速なんだけどアイドル高めだという噂なので気になる。

PLEX 地上デジタル・BS・CS対応TVチューナー PX-W3PE4

PLEX 地上デジタル・BS・CS対応TVチューナー PX-W3PE4

AMD CPU Ryzen 5 2600X with Wraith Spire cooler YD260XBCAFBOX

AMD CPU Ryzen 5 2600X with Wraith Spire cooler YD260XBCAFBOX

rust websocket server sender clone

e-tipsmemo.hatenablog.com

e-tipsmemo.hatenablog.com
を組み合わせて、
Serverがlistenするスレッドとは別のスレッドからも、sendしたかった。

extern crate ws;

use ws::{listen, Sender, Handler, Error, ErrorKind, Result, Message, Handshake};

use std::time::Duration;
use std::thread;
use std::sync::mpsc::Sender as TSender;

enum Event{
    Connect(Sender),
    Disconnect,
}

struct Server {
    ws_out: Sender,
    thread_out: TSender<Event>,
}

impl Handler for Server {
    fn on_open(&mut self, _ : Handshake)->Result<()>{
        println!("on_open");
        self.thread_out.send(Event::Connect(self.ws_out.clone()))
            .map_err(|err| {
                Error::new(
                    ErrorKind::Internal,
                    format!("Unable to communicate between threads: {:?}", err),
                )
            })
    }
    fn on_message(&mut self, msg : Message)->Result<()>{
        println!("{:?}", msg);
        Ok(())
    }
}

fn main() {
    let (tx, rx) = std::sync::mpsc::channel();
    let server = thread::spawn(move || {
        listen("192.168.0.14:3000", |out| {
            Server{
                ws_out: out,
                thread_out: tx.clone(),
            }
        }).unwrap();
    });

    if let Ok(Event::Connect(sender)) = rx.recv()
    {
        for i in 0..5{
        sender.send(format!("{}", i));
        thread::sleep(Duration::from_millis(2000));
        }
    }
}

クライアントは前回のjavascriptのままでServerに別スレッドと、Eventをやり取りするSenderを入れておく。
クライアントからアクセスされたらon_openが呼ばれるので、そこでwsのsenderを送ってやることで、
main関数のほうのスレッドはサーバーが持ってるsenderのcloneをもらうことができる。

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そのほか