2020/03/29

うまく抽象化できてない

いまgolangで書こうとしているRC-S370のアプリというか何というかだが、どうにも抽象化がうまいことできていない。

全然golangとは関係なく、前からできていないと思っているものがまたやってきただけだ。

 

今日までのところ
https://github.com/hirokuma/go_pasori370/tree/e8289f371d29810a9c371eaa91232882f12d2a03

 

mainはどうでもいいとして、それ以外はdevとpcdに分けている。
devでRC-S370のコマンドを書いて、pcdではそれを組み合わせて呼び出すだけにしようかと考えている。

 

が、pcdは意味があるのだろうか。
RC-S370のコマンドを個別に書いたとして、それを呼び出す順番もまたRC-S370に依存していることがほとんどだ。
だとすると、pcdはもっと抽象化してタグを読み書きするインターフェースだけにして、どう呼び出すのかはdevに任せてしまった方がよいのではないか。

それだったら、devでインターフェースを切って、dev/rcs370、みたいな構成にしてしまえばいいんじゃないか。
pcdのように間に入る人がいらなくなる。

 

でもなぁ・・・タグの読み書きといっても、NFC-A, B, Fのような形だとそこは抽象化できないのだよな。
NDEFでやればいいのだろうけど、そこまで気力が湧かない。
偉大だな、NDEF。

 

というわけで、方向性が自分の中ですらモヤモヤしているというところであった。

[golang]testとerror

golangの埋め込みを使うことで継承っぽいことができるので、それを使ってテストしようと思っていた。
ハードウェアが絡む部分は、mockを作ってやってしまいたいのでね。

が、先にロジックの部分をテストしておいて、その次にmockを作った方が良いことに気付いた。
まあ、いつもそうやって作るので、golangだろうと何だろうと、そうなるだろう。

 

正常系よりも、準正常系や異常系のチェックが大半だろう。
特にハードウェアが絡むと、実機では通常発生しないようなエラーもテストしたいし。

golangでエラーといえば、errors。
errors.New("文字列")でエラーを返していたけど、文字列だけで判定するのは嫌だ。
エラーコードのような一意の値でうまいことやれないだろうか?


何かを参考にしようと検索していたら、こういうのがあった。

https://github.com/ethereum/go-ethereum/blob/ec2131c8d3382db3762909a8e7d225be61bae230/core/error.go

なるほどね、各packageごとにerror.goを用意しておくのね。
Newだからアドレスの比較になるので、時間もかかるまい。

ちょっと嫌なのは、Newするということは動的に作るというところだ。
動的に作るということはRAMに作るということで、RAMを消費してしまうだろう。
まあ、今回は組み込み向けじゃないから、気にしないが。

 

あとは、せっかくなのでGitHub Actionsとかいうものを使ってみたいのだが、まだまだ私には敷居が高いのう。

[golang][windows]vscodeが起動しているとgo getに失敗する

深く調べていないのだが、記録として残しておこう。

 

環境

  • Windows10 + WSL(Ubuntu 18.04)
  • Visual Studio Code(1.43.2) + Remote-WSL(0.42.4) + Go(0.13.1)

 

Windowsのvscodeを起動した状態でgo getすると失敗した。
既にgo getしてあれば大丈夫だったが、新規で取得する場合に失敗するようだ。

$ go get github.com/google/gousb
go get: rename /home/hirokuma/go/pkg/mod/github.com/google/gousb@v0.0.0-20190812193832-18f4c1d8a750.tmp-933615597 /home/hirokuma/go/pkg/mod/github.com/google/gousb@v0.0.0-20190812193832-18f4c1d8a750: permission denied

 

vscodeを終了しておいてgo getすると大丈夫みたい。
問題がvscodeなのかextensionsなのかは知らないが、取りあえず動けばいいのだ。

2020/03/28

[golang]go getの"no Go files"はエラーじゃない

「わかる」「わからない」「わかる」「わからない」・・・と花占いのように悩んでしまうのが、golangのmodule関係。

今までVirtualBox上で動かしていたのだけど、今回はテストを試したいだけなのでWindowsでも問題ない。Windowsというか、WSLですな。

git cloneしようと思ったが、そういえばgo getってのもあったのを思い出した。

 

src$ go get -d github.com/hirokuma/go_pasori370
package github.com/hirokuma/go_pasori370: no Go files in /home/hirokuma/go/src/github.com/hirokuma/go_pasori370

エラーだと思ってあれこれパラメータを変えたけど、メッセージは変わらない。
あきらめげgit cloneしようと思ってディレクトリを見てみると、なんとダウンロードできているじゃないか。。。

 

そう、メッセージは単に「goファイルがないよ」といっているだけなのだ。
そして、確かに私はgo_pasori370の直下にはgoファイルを置いていない。
そういえば、go getはpackageを使おうと思って取ってくるようなものだろうから、goファイルがないというのはおかしいと思っているのかもしれない。

 

みんな、というか、ブログなどに書いている人はあまりその辺で悩んでいるような様子が見られない。
まあ、解決する方法を検索しているせいかもしれないが、何か、何か私が理解できていないものがあるのだろう。
その何かを見つける日まで、私の戦いは続く・・・。

VirtualBoxのGuest OS(Linux)を変更して起きたこと2件

タイトルが長い・・・。

 

私の作業環境はWindows10。
Linuxを使いたいことも多いので、そちらはVirtualBoxで動かしている。

余っていたHDDにVMのデータを置いていたのだが、どうにも起動などが遅いので、ほとんど使っていなかった128GBのSSDにOSを入れて、そっちとmountして使うように変更した。

今まではXubuntuかLubuntuだったのだが、今回はMint Linuxにした。
特に何かあったわけではないのだが、たまには違うのもよかろう。

 

Mint Linuxに問題があったわけでもないのだが、ちょうどそのタイミングでいくつか現象が出てしまったのでメモを残しておこう。


Host OSとGuest OSの間でクリップボードの共有ができなくなった

Host側で調べ物をして、その結果をコピーしてGuest側に持っていく、という使い方をしているので、クリップボード共有ができないのは非常に痛手だ。

これの原因は、VirtualBoxのバージョンだった。
アップデートして6.1.4にしていたところが問題だった。

virtualbox.org • View topic - VirtualBox 6.1.4 Clipboard sharing doesn't work
https://forums.virtualbox.org/viewtopic.php?f=6&t=97156

どうしようもなさそうなので、6.1.2にしたら共有できた。

 

Visual Studio Codeを使っているとVMごと落ちる

左側のEXPLORERでファイルを右クリックしてもメニュー枠しか表示されていないので、もう一度右クリックするとVMごと落ちる、という現象に出会った。
これは必ず発生して、一度右クリックした後にvscodeを終了させたとしても落ちてしまった。

Linux Mint Guest OS crashes while using VSCode · Issue #84745 · microsoft/vscode · GitHub
https://github.com/microsoft/vscode/issues/84745

$HOME/.vscode/argv.jsonに"disable-hardware-acceleration": true、などと書けば良い。
私の場合は既にそのファイルがあって、コメントアウトまでされている状態だったので、コメントを外せば済んだ。

スクロールすると描画が遅いのが気になってしまうのだが、VMが落ちるよりはましだ。
そのうち解決するかもしれんしね。


Mint Linux自体は、設定がしやすくて良いですな。

2020/03/26

[golang]本体無しでテストをしたい (3)

前回、golangで埋め込みというものを使うと継承っぽいことができることが分かった。

では、それをどうテストやmockに応用すると良いのだろうか?

【Go言語】埋め込みでinterfaceを簡単に満たす - Eureka Engineering - Medium
https://medium.com/eureka-engineering/golang-embedded-ac43201cf772

 

テストしたいのは、UserInterfaceを引数に持つIsTestTarget()関数。
何を困っているかというと、この関数内でさらに関数を呼び出しているところだ。
u.Age()の戻り値を26とか25にして、trueだったりfalseだったりになることを確認したいのだが、u.Age()の戻り値をどうやって変更させるか。。。

 

というわけで、ここではUserInterfaceを埋め込んだtestUserという構造体を作り、testUserをreceiverとするAge()関数を作ってしまう。
そうすると、testUserは本体の実装からは切り離されるし、u.Age()は好きな値を返せるようになるし、めでたしめでたし。

「これって、UserInterfaceのテストじゃなくて、testUserのテストになるんじゃないの?」と気がしたが、そもそもIsTestTarget()が実行できれば良いだけなので、その引数にそこまでこだわる必要はあるまい。


では、話の始まりに戻ろう。

testingだけで完結するmockを作る #Golang - Qiita
https://qiita.com/maitaketurn/items/2618e9ecd68852a0a83b

 

テスト対象の構造体TaskControllerはinterfaceのTaskInterfaceを埋め込んでいて、テスト側のmockTaskControllerもTaskInterfaceを埋め込んで、それをreceiverに持つcreateTaskとupdateTaskと同じsignatureの関数がある。

それとは別に、mockCreateTaskとmockUpdateTaskというメンバがあるが、これは関数ポインタみたいなものだろう。
継承したような形で実装したcreateTask()とupdateTask()は、このmockCreateTaskとmockUpdateTaskを呼び出すだけだ。
だから、実際に呼ぶ前にmockCreateTaskとmockUpdateTaskには本体のアドレスを教えてやらねばならぬ。

 

あれに近いか。
fffでfakeを設定するやつだ。例えが分かりづらくて済まんね。
ともかく、テストごとに返したい値が変わるから可変値というやり方もあるけど、ここでは関数ごと差し替えられるようにした、と。


こういうのは、実装し続けていたらわかるようになるだろう。
少なくとも、go.modよりも理解はしやすいだろう。

次回は、自分のテストに埋め込みを使ってやってみる。

2020/03/25

[golang]本体無しでテストをしたい (2)

テストでmockを使う方法を調べているつもりだったが、いつの間にか埋め込みというものを調べていた。
まあよくあることだ。

 

そもそも、埋め込みのパターンの記事から読み始めてはいかんな。
埋め込みとは何か、から見なくては。

【Go言語】埋め込みでinterfaceを簡単に満たす - Eureka Engineering - Medium
https://medium.com/eureka-engineering/golang-embedded-ac43201cf772

大ざっぱな理解だろうが、継承っぽいことができる、ということでいいのかな。
vtblをコピーしてアドレスが同じところを指しているだけなので、定義すればオーバーライドしてそのメソッドだけは別のものを指定できる、と。
interfaceを埋め込んだ場合はどこも指していないので、定義すれば呼び出せるし、定義しなければnilか何かになっていて呼べない。

 

body.go

01: package body
02: 
03: type BodyInterface interface {
04:     Go() string
05:     GoGo() string
06: }
07: 
08: type BodyStruct struct {
09:     BodyInterface
10: }
11: 
12: func (body *BodyStruct) Go() string {
13:     return "Go"
14: }
15: 
16: func (body *BodyStruct) GoGo() string {
17:     return "GoGo"
18: }

 

mummy.go

01: package body
02: 
03: type MummyStruct struct {
04:     BodyStruct
05: }
06: 
07: func (body *MummyStruct) GoGo() string {
08:     return "MuMu"
09: }

 

main.go

01: package main
02: 
03: import (
04:     "ex6/body"
05:     "fmt"
06: )
07: 
08: func main() {
09:     var body0 body.BodyStruct
10:     fmt.Printf("%s\n", body0.Go())
11:     fmt.Printf("%s\n", body0.GoGo())
12: 
13:     var body body.MummyStruct
14:     fmt.Printf("%s\n", body.Go())
15:     fmt.Printf("%s\n", body.GoGo())
16: }

$ go run ex6/main
Go
GoGo
Go
MuMu

 

MummyStructをreceiverに持つGo()は実装していないのだが、BodyStructのGo()が呼び出せている。
予想通りだ。

 

では、こうしたらどうなる?

 

mummy.go

01: package body
02: 
03: type MummyStruct struct {
04:     BodyInterface
05: }
06: 
07: func (body *MummyStruct) GoGo() string {
08:     return "MuMu"
09: }

 

main.go

01: package main
02: 
03: import (
04:     "ex6/body"
05:     "fmt"
06: )
07: 
08: func main() {
09:     var body0 body.BodyStruct
10:     fmt.Printf("%s\n", body0.Go())
11:     fmt.Printf("%s\n", body0.GoGo())
12: 
13:     var body body.MummyStruct
14:     fmt.Printf("%s\n", body.GoGo())
15:     fmt.Printf("%s\n", body.Go())
16: }

継承っぽいことをしている箇所を、BodyStructではなくBodyInterfaceにしたのだ。
まあ、結果の予想が付くので、Printfするところもbody.GoGo()を先に呼び出すようにした。

$ go run ex6/main
Go
GoGo
MuMu
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x18 pc=0x48ea85]

goroutine 1 [running]:
main.main()
        /home/xxx/go/src/ex6/main/main.go:15 +0x1e5
exit status 2

 

定義したMummyStructのGoGo()は呼び出しできているが、定義していないGo()を呼び出すとpanicが発生している。
予想通りですな。

 

では、次回はこれをどうテストに応用できるのか、だ。

2020/03/24

[golang]本体無しでテストをしたい (1)

golangでは、それなりにテストを行う環境が用意されている。
vscodeなんか使うと、テスト関数のファイル先頭や関数先頭にテスト実行のリンクが出てくるくらいには環境が用意されている。

 

しかし、ハードウェアが絡む場合、実機を使わないようにテストしたい場合がほとんどだろう。
今回のようにPCでそのまま実行できるならよいのかもしれないが、クロスコンパイルで環境がそもそも無かったり、ハードウェアの挙動を指定できなかったりすると、その部分だけ差し替えて実行したいものだ。

スタブとかモックとか言うやつですな。
今まではずっと「スタブ」と呼んでいたのだけど、どうやらモック(mock)という方が正しいらしい。

 

どれが筋のいいやり方なのか分からんので、最初に出てきたものを見てみる。

testingだけで完結するmockを作る #Golang - Qiita
https://qiita.com/maitaketurn/items/2618e9ecd68852a0a83b#%E3%81%BE%E3%81%A8%E3%82%81

んんー、よくわからんぞ?

 

まず、テスト対象の実装
これはわかる。わかるというか、テスト対象というだけだ。
あるとすれば、interfaceを持っていることと、宣言されたTaskInterfaceをメンバに持つ構造体TaskControllerがあるということか。
TaskInterfaceをメンバに持って、どういう効果があるのだろうか。

わからんのは、test側
構造体mockTaskControllerがあるのはいいとして、その先頭がTaskInterfaceだけ書いてある。
これが「ダックタイピング」というやつなのか・・・?

 

そうではないらしい。
ダックタイピングというのは、たぶんC言語でC++のclassっぽい、あるいはclassの継承っぽいことをさせたいときに、とあるアドレスからの相対アドレスにあるデータが期待したデータ配列にさえなっていれば実行できるよ、というようなやつと同じようなやつだろう。
言語としてサポートしているなら、メンバの順番が変わっていてもうまいことやってくれるのかもしれん。

 

どうも、埋め込み(embedding)とかいうらしい。

Go言語の埋め込みについて4つのポイントでまとめた - Qiita
https://qiita.com/momotaro98/items/4f6e2facc40a3f37c3c3

似ているのはここか。
HighJumpRunnerインターフェースがあって、それと関係なくgrasshopper構造体があって、でも(*grasshopper)をreceiverに持つHighJump()関数がある。
そして、*grasshopperを埋め込んだShinJinrui2構造体がある。
ShinJinrui2のインスタンスaShinJinrui2の埋め込みメンバ?にgrasshopperのポインタを設定すると、aShinJinrui2.HighJump()が実行できるようになっている。

 

なんか利点があるのだろうが、今日は頭が回らんのでここまで。

2020/03/22

受信とデコード

前回の続きではあるが、golangと関係ない話だ。

 

RC-S956は、PasoriのRC-S370に搭載されているチップで、RC-S620/Sも同じチップが載っている。
RC-S370とRC-S620/Sの違いは、インターフェースがUSBなのかUARTなのかだ(他にもあるが、使うだけならそれでいいだろう)。

 

RC-S956はNFC R/Wとして動作するチップだ。
詳しいことは知らんのだが、

  1. インターフェースを通してコマンドをユーザが送信
  2. コマンドのフォーマットが正しければチップがACKを送信
  3. レスポンスがあればチップがレスポンスデータを送信

ということになっている。

ただ、ACKやレスポンスを送信するといっても、受信してほしいデータがありますよフラグがあるわけでもなく、そういう取り決めになっているだけだ。
ACKを返すのも設定次第だし(今回は必ずある)、レスポンスデータも固定長では無いので、なんか嫌な感じだ。


では、どう実装するのがよいだろうか。

まず、受信サイズはInEndpointのパケットサイズの逓倍にするのがよかろう。
gousbのサンプルも10パケット分ということで10倍した領域を用意していたし。

幸いなことに、RC-S956からの1回分のレスポンスはサイズの上限が決まっている。
そして、コマンドに対してレスポンスを返すだけで、勝手にレスポンスだけを返すことはなかったはず。
Target系のメッセージがちょっと自信ないのだが、今回は忘れよう。
1コマンド送信に対して、ACK受信(6byte)とレスポンス受信(最大256byte=4パケット)。

ACKは、いい。
受信して最初のデータなので、受信データの先頭6byteをチェックすれば良い。

問題はレスポンスだ。
ACKと別USBパケットになると思うのだけど、100%の確信があるわけではない。
設定でACKを返さないようにできた気はするのだが、その設定をするまではACKが返ってくるので、あまりそこでがんばりたくない。


となると・・・USBの1パケット中にACKとレスポンスが混ざっていたとしてもデコードできるようにするし、ACKとレスポンスが別パケットになったとしても受信をそれぞれ行うようにする、というやり方しかないか。

文字にすると「当たり前やん」と言われそうなのだけど、そういうのが苦手なのだ。

 

コマンド送信後、まずUSB5パケット以上の受信を行う。
先頭の6byteをACKと比較する。
それ以上のデータがまだあればレスポンスとして比較し、残っていなければUSB4パケット以上を受信する。

おそらく、レスポンスが複数パケットになったとしても、1回の受信で全部取得できるだろう。
ACKと連続したパケットであろうとなかろうと、最初のパケットを読めばレスポンス長はわかるので、足りているかどうかはチェックできるのだけど・・・大丈夫じゃないの、と思ってしまうのよね・・・それがダメなのか・・・。

2020/03/21

[golang][gousb]Write()やRead()から戻らない (2)

golangなのだが、どちらかといえばlibusbの方かもしれない。いや、USBか。

 

Pasoriからちまちまと受信とデコードを繰り返そうと考えていたので、まず3バイトreadして、次は2バイト受信して・・・というコードを書いていた。

が、USBデバイスはパケット単位でのアクセスだったと思う。
Endpointの値を見ても、

2020/03/21 22:04:51 InEndpoint= ep #4 IN (address 0x84) bulk [64 bytes]
2020/03/21 22:04:51 OutEndpoint= ep #4 OUT (address 0x04) bulk [64 bytes]

今回は64バイトなのだろう。

 

送信する方は一度に送っているのでよいのだが、受信の方は考えないといけない。
ACKとレスポンスを返してくるので、おそらくはACKで1パケット、あとはレスポンスで必要な分だけのパケット数というところか。

ACKの返信は脊髄反射的なものだったはずだ。レスポンスを返すよりもACKの方を先に返すはずだ。
だから、パケットとしてもきっとレスポンスと混ざることはない・・・と信じたい。
いや、そんなことを考えるよりは、ACKとレスポンスをデコードできるまで受信し続ける、の方が正しいのか。

 

ともかく、数バイトではなく、EndpointDescのMaxPacketSize単位にすると受信できそうだった。
そうなると、デコードの仕方も考え直さないとなぁ。。。

[golang][gousb]Write()やRead()から戻らない (1)

golangのgousbを使おうとしている。

EndpointはINもOUTも取れたので、実際にデバイスと送受信してみる。
それよりも前にinterfaceの使い方がよくわからずに悩んだのだが、別の機会に。

 

https://github.com/hirokuma/go_pasori370/blob/754bb09df7e35e67a678979ac8240bf10d08eb46/dev/pasori370.go#L97

01: length, err := dev.outEndpoint.Write(data)
  

image

wiresharkで期待したデータをUSBで送信しているのは分かった。

分かったのだが、このWrite()から制御が戻ってこないのだ。
レスポンスが受信できないのであれば送信データが間違っているのかもしれないし、Write()がエラーでも返してくれればヒントになるかもしれない。
だが、制御自体戻ってこないのだ。
なぜだ・・・。

 

と思ったら、open時にgousb.Contextをdeferでcloseするようにしていたためだった。
失敗失敗。
gousb.Deviceのcloseと同じタイミングでcloseすればよいだろう。


Write()の結果は戻るようになったが、次のRead()が戻らない。
wiresharkを見ても受信データが流れているようには見えない。ただ、URB_BULK inにはなっているので受信しようとはしているのだろう。

念のため送信しているデータを以前作ったパケットデータと比較したが、間違えてはいない。
うまくいっていればACKが受信できると思うのだが、そうなっていない。

 

wiresharkで受信したままRC-S370を外して付けなおすと、たぶんkernelがアクセスしているログが取れた。
Resetコマンドもやりとりしているようで、こういう感じになっていた。

image

待ち時間が足りないのか、などとも思っていたのだが、これを見るとそうでもないようだ。
うーむ。

うちのやつと比較してみよう。
こちらがうちのログだ。

image

kernelの方は、out(CMD), in, in(ACK, in, out, in(RES)。
うちの方は、out(CMD), out, in, in。

関係あるかどうか分からないが、outが2回続いているな。
OutEndpoint.Write()を1回だけ呼んでみても、それだけでoutが2回発生してしまう。

 

・・・いや、これは関係なかった。
過去の自作ライブラリでもこうなった。

image

out(CMD), out, in, in(ACK), in, in(RESP)。
readをリトライしていけばなんとかなるかも、と思ったが、ダメだね。

 

わからぬ・・・。

[golang]srcにある方のソースファイルを使いたい

gousbを使ってNFCデバイスを動かそうとしている。
で、動くところまで作ってgithubにpushして、また書いてpushして、ということを繰り返している。
ちゃんと動くまでmaster以外でやった方がいいのかもしれんが、まあ、他の人に向けてるわけでもないのでよかろう。

 

今日はさっきブログで書いていたEndpointの取得をやろうとしていた。
コンパイルは通るがpanicが起きていたので、とりあえずpanicが起きているところをコメントアウトして動かそうとしていた。

が、まだpanicが起きる。
ログを見ても、行番号がコメントアウトした行になっている。
何が起きているのかと思ったら、どうも$GOPATH/src以下にあるソースファイルを使っておらず、$GOPATH/pkgにgithubからcloneした方のソースファイルを使っているようなのだ。

go.modを見ると、moduleが自分のgithubのリポジトリになっているのだが、requireにも自分のgithubリポジトリが書いてあって、しかもpushしてgo get -uするとgo.modのrequireに書いてあるバージョンが更新されているようなのだ。

 

どうやるのがよいのが正解かよくわからなかったのだけど、go.modにreplaceを書いた。

replace github.com/hirokuma/go_pasori370 => ../go_pasori370

replace github.com/hirokuma/go_pasori370/dev => ../go_pasori370/dev

一度、ディレクトリを上に戻っているのは、こうするとエラーになったからだ。

replace github.com/hirokuma/go_pasori370 =>

replace github.com/hirokuma/go_pasori370/dev => ./dev

あ、このgo.modなら通るやん。

replace github.com/hirokuma/go_pasori370 => ./

replace github.com/hirokuma/go_pasori370/dev => ./dev

 

なんかねぇ、言語そのものじゃないところが分かりづらいのよねぇ。

2020/03/20

[golang]gousbのSetAutoDetach()

gousbに戻ろう。

 

デバイスのオープンをして、Endpointを探そうとしている。
lsusbサンプルを見て、forでぐるぐる回して探そうとしていた。

が、この方式だと取得できるのはConfigではなくConfigDesc、InterfaceではなくInterfaceDesc、そしてEndpointではなくEndpointDescなのだ。
そして、DescからDescではない値がどうしても取得できそうにない。

forでrangeでぐるぐる回しているので、Descからではなくループしたindexからであれば(*Device)Config(cfgNum)(*Config)Interface(num, alt)などを使えば取得できそうな気がする。
気がするのだが、なんかInterfaceがうまく取得できないのだ。
なんだろうねぇ。。。

 

別サンプルのrawreadは、ぐるぐる回さずに値を直接指定してEndpointなどを取得している。
これを使うと、確かにちゃんと取得ができていた。
同じように自分のところを書いてみるけど、やはりうまくいかない。

ということは、何か私にできていないことがあるはず。。。

 

見比べると、rawreadサンプルではSetAutoDetach(true)を呼んでいた。
私も呼んでみると、うまくいくではないか。

(*Device) SetAutoDetach(autodetach)はInterfaceでattachしたい場合にkernelから自動的にdetachし、Interfaceが終わったら自動的にkernelにattachし直す、というものらしい。
Deviceのオープン直後はデフォルトではfalseになっているそうだ。

 

確かに、busyっぽいエラーになっていた気がする。
であれば、kernelがNFCデバイスを認識するようになっているようだし、trueにしておくのが無難だろう。
kernelが使っていなければ、元に戻すものもないだろうし。

昔つくっていたlibusbを使ったサンプルも、もしかしたら今だと動かないのかもなぁ。

2020/03/17

[github]固定URLがいるときはyを押す

GitHubでソースコードを見ながらお話しすることがある。
リアルタイムの場合は良いのだが、メモ書きなどで残しておくときには特定のcommit idが載っている方のURLじゃないと、数ヶ月後にアクセスしたときに行番号がずれていたりする。

GitHubの固定URLを取得するときは、いつも「commit idをクリック→commit履歴画面に遷移するのでFilesをクリック→commit id指定のトップ画面に戻るのでまたクリックして所定の位置までたどる」というめんどくさいことをしていた。

 

なんかブラウザのプラグインとかないかなー、と検索してみると、あるじゃないか。
しかも、普通のGitHubのショートカットとして。

Getting permanent links to files - GitHub Help
https://help.github.com/en/github/managing-files-in-a-repository/getting-permanent-links-to-files#further-reading

yキーを押すだけだと・・・。

押すと、確かにできた。

 

これで楽になる、という喜びと、今までなんて時間を無駄にしてきたんだ、というがっくり感が両方襲ってきますな。

2020/03/15

Docker再び (2)

時代はdockerだ!
などと私が言い出したときは、仕事として必然的に覚えないといけなくなったと思ってもらって構わない。

hiro99ma blog: Docker再び (1)
https://hiro99ma.blogspot.com/2019/05/docker-1.html

当然ながら、前回の記憶は無い。。。


今回は、dockerだけではなく、docker-composeも覚えたい。

どうやら、dockerの1 imageの中であれこれ立ち上げるのではなく、1 image - 1 appli、みたいな使い方をするそうだ。
それが主流なのかは知らんが、モジュールは疎結合が望ましいのと同じようなものだろうから、理解はできる。
複数のimageを立ち上げたりするのに、docker-composeがよいそうだ。

 

前回の復習。

imageはdocker imageのことで、classとinstanceの関係でいえばclassに当たるもの。
containerは、instanceに当たるもの。
volumeは、ファイルを残しておく領域のようなもの。

"Dockerfile"というものを書いて`docker build`するとimageを作ることができる。

こんなところか。

 

では、今日はこちらを見ながら学習する。

docker と docker-compose の初歩 - Qiita
https://qiita.com/hiyuzawa/items/81490020568417d85e86

うちはメイン環境がWindowsなのだが、dockerはVirtualBox上のUbuntu18.04で動かす。
画面がほしいからVirtualBoxでやってるけど、WSL2でdocker対応するような記事もあるので、しばしこれでやろう。

$ docker version
Client:
  Version:           19.03.6
  API version:       1.40
  Go version:        go1.12.17
  Git commit:        369ce74a3c
  Built:             Fri Feb 28 23:45:43 2020
  OS/Arch:           linux/amd64
  Experimental:      false

Server:
  Engine:
  Version:          19.03.6
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.12.17
  Git commit:       369ce74a3c
  Built:            Wed Feb 19 01:06:16 2020
  OS/Arch:          linux/amd64
  Experimental:     false
  containerd:
  Version:          1.3.3-0ubuntu1~18.04.1
  GitCommit:       
  runc:
  Version:          spec: 1.0.1-dev
  GitCommit:       
  docker-init:
  Version:          0.18.0
  GitCommit:      


Dockerfileを書くのも良いが、Docker Hubというところにあるimage集(repository、と呼ぶのかな)を使うという手段もあるそうだ。
そして、Docker Hubから持ってきたimageをカスタマイズして使うこともできるそうだ。

記事では、CentOSのimageを使っている。
私であればUbuntuなどのdebian系がよいのかもしれない。
そういえば、前回はDockerfileを書いたが、あれの"FROM"がDocker Hubのrepository名なのだろう。

例えば、こちらはbusyboxだ。

https://hub.docker.com/_/busybox

bashが必須とかでなければ、サイズが小さくてよいかもしれん。

まあ、今回は無難にUbuntu18.04を使おう。

https://hub.docker.com/_/ubuntu

tagsを見ると、バージョンを指定した取得の仕方が出てくるので、それをまねすれば良かろう。

 

記事では、Dockerfileは書かず、`docker run`でattachしたあと、attachしてコンソールからインストールや自動起動の設定を行い、`docker commit`してカスタマイズimageを残している。
Dockerfileを使えば自動化できるのだろうか?

イメージの構築 — Docker-docs-ja 1.9.0b ドキュメント
http://docs.docker.jp/engine/userguide/dockerimages.html

なるほど、`docker commit`は手動で変更する場合、`docker build`はDockerfileで変更する場合ということか。

うーん、dockerのドキュメントは丁寧だな。
これを読んでいく方が正しい気がするのだけど、あとで考えよう。

 

題材が思いつかないので、Ubuntu18.04をベースに、python3をインストールして、このサンプルが動くようにしてみよう。

Compose を始めましょう — Docker-docs-ja 17.06.Beta ドキュメント
http://docs.docker.jp/compose/gettingstarted.html

このサンプルがpython3で動くのかどうかわからんのだが、まあいい。
まずは、手動で確認。

$ docker run -it ubuntu:18.04
root@55463b0cbeb1:/# python3
bash: python3: command not found
root@55463b0cbeb1:/# apt update && apt install python3 -y
...
root@55463b0cbeb1:/# python3-pip
bash: python3-pip: command not found
root@55463b0cbeb1:/# apt install python3-pip -y
...

python3はそうでもないが、python3-pipはいろいろダウンロードしているな。
ともかく、python3とpython3-pipをインストールすればよさそうだ。

Dockerfile

01: FROM ubuntu:18.04
02: ADD . /code
03: WORKDIR /code
04: RUN apt update && apt install python3 python3-pip -y
05: RUN pip3 install -r requirements.txt
06: RUN python3 app.py
  

requirements.txt、app.py、docker-compose.ymlはサンプルと同じ。

では、docker rmでコンテナを削除して、やり直し。

$ docker build -t web3 .

ビルドすると、ずらずらメッセージが表示され、最後は

Step 6/6 : RUN python3 app.py
  ---> Running in 725c62862d51
  * Serving Flask app "app" (lazy loading)
  * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
  * Debug mode: on
  * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
  * Restarting with stat
  * Debugger is active!
  * Debugger PIN: 297-571-982

になった。
あれ、実際に立ち上がってしまったのか?
docker psで見ると、立ち上がっているようだ。

 

とりあえずCtrl+Cで止めて、docker composeの方でやってみよう。

$ docker-compose up

aptで取得するところから始まった。
さっき動いていたようだから、ここは省略してくれるとうれしいのだがなぁ。
もしかしたら、docker-composeでの初回だからやっているのかもしれん。

・・・ダメだ。
ダメというか、ホスト側で5000番ポートが使われていない。

$ docker exec -it 1ce5c59baf33 /bin/bash
root@1ce5c59baf33:/code# ps
  PID TTY          TIME CMD
   11 pts/0    00:00:00 bash
   21 pts/0    00:00:00 ps
root@1ce5c59baf33:/code# netstat
bash: netstat: command not found
root@1ce5c59baf33:/code# ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0   4628   764 ?        Ss   06:03   0:00 /bin/sh -c python3 app.py
root         6  0.0  0.8  79316 26484 ?        S    06:03   0:00 python3 app.py
root         8  1.3  0.8 161344 26716 ?        Sl   06:03   1:42 /usr/bin/python3 /code/app.py
root        11  5.0  0.1  18508  3404 pts/0    Ss   08:12   0:00 /bin/bash
root        23  0.0  0.0  34404  2816 pts/0    R+   08:12   0:00 ps aux

最近はnetstatは使わないという記事を見かけたが、まあ許しておくれ。
他のコマンドを知らんのだ。使えなかったけど。

root@1ce5c59baf33:/code# netstat -na
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State     
tcp        0      0 0.0.0.0:5000            0.0.0.0:*               LISTEN    
tcp        0      0 172.17.0.2:50742        91.189.88.31:80         TIME_WAIT 
Active UNIX domain sockets (servers and established)
Proto RefCnt Flags       Type       State         I-Node   Path

net-toolsをインストールしてnetstatしてみたが、こっちは動いている。

 

ああ! サンプルDockerfileの最後の行はCMDじゃないか!!
最後はbuild successみたいなログで終わるはずなのだよ。

01: FROM ubuntu:18.04
02: ADD . /code
03: WORKDIR /code
04: RUN apt update && apt install python3 python3-pip -y
05: RUN pip3 install -r requirements.txt
06: CMD python3 app.py
  

$ docker rmi `docker images -qa`
$ docker build -t web3
......

Successfully built acb54c64c632
Successfully tagged web3:latest

ほらね。

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED              SIZE
web3                latest              acb54c64c632        About a minute ago   481MB
ubuntu              18.04               72300a873c2c        3 weeks ago          64.2MB

docker-compose.ymlの"web"を"web3"に変更した。

$ docker-compose up
....

redis_1  | 1:M 15 Mar 2020 08:57:43.217 * DB loaded from disk: 0.010 seconds
redis_1  | 1:M 15 Mar 2020 08:57:43.217 * Ready to accept connections
web3_1   |  * Serving Flask app "app" (lazy loading)
web3_1   |  * Environment: production
web3_1   |    WARNING: This is a development server. Do not use it in a production deployment.
web3_1   |    Use a production WSGI server instead.
web3_1   |  * Debug mode: on
web3_1   |  * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
web3_1   |  * Restarting with stat
web3_1   |  * Debugger is active!
web3_1   |  * Debugger PIN: 622-200-617

前回は赤色のメッセージだったのだが、今回は何色っていえばいいのか分からんが、ブラウンっぽい色で終わった。
ホスト側でnetstatすると5000番が見えているし、ブラウザでたたいてもサンプル通りになった。

 

よかった・・・。
しかし、中途半端な状態であってもdocker側(クライアント側?)は動いているのだな。

2020/03/12

[golang]kではじめてはダメなの?

golangは大文字だとpublicみたいな扱いだ。
だから、privateなものは小文字で始めなくてはならない。
ただ・・・C言語やってる人としては、大文字=const的なもの、というイメージがある。

そういえば、C言語でもconst変数なんかはkで始めることがあるな。
あれが何のルールなのかよく知らないのだけど、それでいいや。

 

image

 

なんで?
aからkまで試したが、警告が出たのはkだけだ。

Question: don't use leading k in Go names. Why? · Issue #258 · golang/lint
https://github.com/golang/lint/issues/258

ハンガリアン記法だから、か。
だいたい、ハンガリアンって名前も相当なものよね。。。

 

そして図らずもkがハンガリアン記法に由来したものということがわかってしまった。

警告を無視しても良いのだが、残っているのも気になる。
だが、vIDはまあいいとしても、pIDは抵抗があるな。

今回は、vendorID, productIDでよかろう。

[linux]存在しないディレクトリから相対移動する

長いタイトルを付けたくなかったので省略してしまった。
どういう状況かを説明せねばなるまい。

  • よく使うディレクトリの階層が深いので、シンボリックリンクを作った
  • cdでシンボリックリンク先に移動して作業していた。そのときにディレクトリを作った。一時的なデータを入れているなどと思ってもらえば良かろう。
  • 作ったディレクトリの中で作業中、一時的なディレクトリを全部消すスクリプトを走らせた
  • また一時的なディレクトリを作るところからやり直したいので、1つ上のディレクトリに移動しよう

状況を省略すると「シンボリックリンク先でmkdirしたディレクトリの中にいる状態でそのディレクトリが削除されたので、cd ..で上に戻りたい」だ。

 

~$ mkdir -p a/b/c/d/e/f
~$ ln -s a/b/c/d/e qqq
~$ cd qqq
~/qqq$ ls
f
~/qqq$ cd f
~/qqq/f$ rm -rf ~/a/b/c/d/e/f
~/qqq/f$ cd ..
~/a/b/c/d/e$

 

そう、私はqqqに戻ってほしかったのだが、オリジナルの方に飛んでしまったのだ。
相対ディレクトリの元が消えてるのだから、相対と言われても困るのだろう。

`pwd`はまだ使えるのだが、そこから最後のディレクトリ名だけを取り除く、なんてことをやるくらいだったら、普通にcdで移動し直した方が早い気がする。

cd `dirname $PWD`

こんな感じでできそうではあるのだが、aliasに入れておいて使うのかどうか。。。

2020/03/11

[slack]もしかしてRTMはもう使えなくなったのか(2020/03/11)?

私が以前、Pythonで使ったslackbotはこちらだ。

https://github.com/lins05/slackbot

RTMを使っている、と。
まあ、そうなるよねぇ。

ただ、New Slack appsはRTMは使わんだろう、と書いてある。

https://api.slack.com/rtm

では、Newだとどんな感じでイベントを受け取るのだろうか。

https://api.slack.com/authentication/basics#listening

うーん・・・Request URLは必須なのか?

 

おお、ツール一覧があるのか。

https://api.slack.com/tools

しかし、PythonとかJavaScriptとかしかない。。。


うん、RTMを使わないでやる方法はあきらめたよ、私は。
RTMを使おう。

まずは、classic Slack appを選択。

https://api.slack.com/apps?new_classic_app=1

そして、適当にOAuth & Permissionsの設定をして、Bot User OAuth Access Tokenを取得。
どのパーミッションがいるのか分からないので、やりながら調べるのだ。
とりあえず、channels:readを追加しないとchannelsのサンプルが動作しないことは分かった。
まずは、これだけ付けておこう。

  • app_menthins:read
  • channels:read
  • im:read
  • im:write
  • reactions:write

 

まずは、websocketサンプルが良かろう。
というのも、これは主にイベントログを出すためのサンプルのようだったからだ。

slack-bot: 2020/03/11 21:45:18 websocket_managed_conn.go:177: Connecting to RTM
Event Received: slack-bot: 2020/03/11 21:45:19 rtm.go:71: Using URL:
slack-bot: 2020/03/11 21:45:19 websocket_managed_conn.go:181: Failed to start or connect to RTM: not_allowed_token_type
slack-bot: 2020/03/11 21:45:19 websocket_managed_conn.go:147: reconnection 1 failed: not_allowed_token_type reconnecting in 100ms

not_allowed_token_typeね。うんうん、いい調子だ。

https://api.slack.com/methods/rtm.connect

user tokenにはscopeはいらないが、botの方は"bot"というscopeがいるらしい。
が、botというscopeはdeprecatedっていわれたんだよなぁ。
そして、Scopes Updateとかいう画面に遷移してみると、やたらとチェックが入っていたので、適当に外したのが今の状態なのだ。

もしや・・・あのチェックが入った状態が"bot"を選択したのと同じだったのか・・・?
何も読まずに進めたのがまずかったか。。。

 

再確認だ。

まず、別のclassic appを作って"bot" scopeを追加する。

image

update scopesをクリック。

Step1は、bot token scopeの選択だ。
・・・うん、旧来のscopeには全部チェックが入っているな。
気にせず、一番下にあるcontinueをクリック。

Step2は、user token scopeの選択。
rtm.connectには不要らしいので、これはそのまま。

Step3は確認で、Step4はなんかURLとか出てきた。
疲れてるので、今回はスルー。

 

では、Bot Token Scopesを見ておこう。

  • calls:read
  • calls:write
  • channels:read
  • chat:write
  • dnd:read
  • files:read
  • groups:read
  • im:history
  • im:read
  • im:write
  • mpim:history
  • mpim:read
  • mpim:write
  • pins:write
  • reactions:read
  • reactions:write
  • remote_files:read
  • remote_files:share
  • remote_files:write
  • team:read
  • users:read
  • users:read.email
  • users:write

いやあ、こんなにいらんじゃろう。
とは思うものの、さっき失敗したので、まずはこれらのscopesを追加して試そう。

・・・ダメじゃないか。変わらないじゃないか。。。
もしかして、update scopesしたときに変更したのがまずかったのか?
いや、そうでもないようだ。

 

はっ!
もしかして、Update Scopesしたこと自体がまずかったのか??

image

むう、やはりUpdate Scopesしないとダメなのか・・・。

 

と、Update Scopesに飛んだのだが、よく見ると一番上にメッセージが出ていた。

image

いやいや、それができないから困ってるのだよ。

 

もしかして、ドキュメントの更新が遅れているだけで、今からアプリを追加した人はRTMが使えなくなっているんじゃなかろうか。
「今から」と書いたのは、私が以前作ったPythonのアプリは動いているからだ。

 

動くとか動かないとかいう以前に、設定ができないので利用できなくなってるという気がしている。。。
落ち着くために、しばしSlackは忘れよう。

2020/03/09

[golang]slackのClassic app

golangでslackのbotを作りたいので検索すると、出てきた。

Go で slack bot - Kohei Yoshida - Medium
https://medium.com/@yukihira/go-%E3%81%A7-slack-bot-b3711d8ae7bd

 

重要な情報は多いが、Event Subscriptionsの設定が必要というのは大きかった。
ソースコードもあるし、動かせばわかるは・・・

 

あれ、Event Subscriptionsの"Save Changes"がクリックできない。。
ここで入力が必要そうなものはRequest URLか。
イベントが起きたらHTTP POSTで通知するよ、といっているように見えるのだが、気のせいだろうか。

 

もしそうなら、グローバルでアクセスできるサーバを持たない状態では使えないはずだ。
じゃあ、やっぱりWebSocketでやるしかないの?
RTMになるの?
でも、最近はRTMは使わないらしいやん。。。

あれ、でも「Create a Slack App (Classic) 」を選択して作っても、普通に作ったのと同じような設定画面にしか見えない。

Slack has improved how we handle permissions for apps, so you can now request only the specific scopes your app needs. We recommend updating your app’s scopes. Beginning February 21, all new app submissions to the App Directory must use granular permissions. Read our blog post.

これがClassicの方は出てくるのだ。
2月21日を過ぎたから、もう見分けが付かなくなったのだろうか?

2020/03/08

slackのtokenを取得する(2020/03)

せっかくgolangの勉強をしているのだから、他で作っているものもときどきgolangで書いてみよう。

というわけで、slack botだ。


検索したら出てきた。

https://github.com/slack-go/slack

 

コードはサンプルをまねするとして、token idをどうやって取得するのか忘れた。。。

ここでいいと思う。

https://api.slack.com/authentication/oauth-v2

が、たぶんclassic という方を使うんじゃなかろうか。

https://api.slack.com/apps?new_classic_app=1

ここでCreateするといろいろ出てくるのだが、この画面に出てくるのはVerification Tokenだから別物だ。
左側の"OAuth & Permissions"をクリックすると、たぶん今までのtoken idが取得できる。

 

しかし、これもすんなり取得できるわけではなく、まず"Scopes"の追加をする。
これが、たくさん項目があるのだ・・・。
コンボボックスで1つずつ追加することもできるし、ボタンの"Update Scopes"をクリックすると権限一覧画面に遷移するので、そこでチェックボックスによって同時に設定できるのだろう。
だろうが、設定すると"Update Scopes"のボタンが出てこないのが残念だ。

 

channel一覧を取得するサンプルは動いたのだが、私としてはapp宛のメンションを受け取ったら返事をする、みたいなシンプルなものを試したい。
試したいのだが、permissionがわからん。
eventsapiサンプルが期待するやつかと思うのだが、動かしてもnot_allowed_token_typeになるのだ。

うーむ。。。

[golang]エラーの返し方

C言語の時はあまり悩まなかった・・・いや、どうするかは悩むけど言語的には悩まなかったエラーの返し方。
golangの場合は、だいたいerrorsというものを使っているようだ。

 

Pythonもそうだけど、戻り値を簡単に複数返すことができるというのは楽ですな。
私の場合、C言語ではboolを戻り値にして「成功かエラー」だけにすることが多い。
多いのだが、あまり考えたくないというのと、組み込みだと「エラーが起きる=リセットするしかない or バグ」ということがほとんどだったので、戻り値を用意してスタックを使ってしまうくらいだったらvoidでいいや、みたいな気持ちだったからだ。
ほら、エラーが起きたことを知らせるにしても、LEDの点滅とかブザー鳴動だし。

しかし、最近は組み込みじゃないプログラムを書くことが増え、「エラーをユーザに見せる」という技がいるようになってきた。そうするとboolではとても足りず、「intにするか・・・でも戻り値の変更はつらいので引数増やすか・・・でもそれも・・・」とさらに悩むということになってしまっている。

 

その点、複数の戻り値を同時に返すことができるし、戻り値の2番目にerrorsを返しがち、というルールもあるようなので、それに載っかるのが無難だろう。

ただ・・・個人的には暗黙のルールってのがどうも苦手で。
プロジェクトごととか個人のルールはあってしかるべきだが、言語でのルールは仕様に含めてしまってもいいのでは・・・。

いや、そうしたらしたで「なんでここまで縛られるんじゃー」って思うのは間違いないな。


それはともかく、errorsの使い方も調べる。

errors - GoDoc
https://godoc.org/github.com/pkg/errors

発生時はNew()かErrorf()で新規に作るか、伝えられたerrorsに情報を付加して返すかだろう。
WithMessage()とWrap()は同じもののような気がするが、実装はどうなんだろう?

01: func Wrap(err error, message string) error {
02:     if err == nil {
03:         return nil
04:     }
05:     err = &withMessage{
06:         cause: err,
07:         msg:   message,
08:     }
09:     return &withStack{
10:         err,
11:         callers(),
12:     }
13: }
  
01: func WithMessage(err error, message string) error {
02:     if err == nil {
03:         return nil
04:     }
05:     return &withMessage{
06:         cause: err,
07:         msg:   message,
08:     }
09: }
  

Wrap()は、WithMessage()+コールスタック、というところだろうか。

 

普段ならコードを書いて検証するところなのだが、今日は気分が乗らない・・・。

[golang] testを書こう

単体テストというかユニットテストというか、とにかくそういうのを序盤から書いていかないと、後からまとめて書くのはつらい。
テストを作っておくと、書き換えたいときも精神的負担が少ないしね。

 

testing - The Go Programming Language
https://golang.org/pkg/testing/

テストって言うと、どうしても夢のような機構が存在することを期待してしまう。
テストコードを書かなくてもそれっぽいテストコードを自動で書いてくれないだろうか、とか。
とかというか、それしかないが。

もちろん、そういうものはgolangにもない。
あるのは、テストコードがしくみとして考慮されている、ということか。

  • テストしたいファイルの名前は、後ろに"_test.go"にする
  • テストのsignatureは、TestXxx(t *testing.Test)
  • NGルートはt.Errorf()みたいな感じで終わらせる
    • Skip()などもあるらしい

 

raw_test.go

01: package pcd_test
02: 
03: import (
04:     "bytes"
05:     "testing"
06: 
07:     "github.com/hirokuma/go_pasori370/pcd"
08: )
09: 
10: func TestRawEncode(t *testing.T) {
11:     var msg pcd.RawMsg
12: 
13:     msg.Cmd = 0x18
14:     msg.Data = []uint8{0x01}
15:     data := pcd.RawEncode(&msg)
16: 
17:     model := []uint8{
18:         0x00, 0x00, 0xFF, 0x03, 0xFD, 0xD4, 0x18, 0x01, 0x13, 0x00,
19:     }
20:     if bytes.Compare(model, data) != 0 {
21:         t.Fatalf("not same")
22:     }
23: }
  

src/github.com/hirokuma/go_pasori370/pcd$ go test .
ok      github.com/hirokuma/go_pasori370/pcd    0.002s
src/github.com/hirokuma/go_pasori370/pcd$ go test .
ok      github.com/hirokuma/go_pasori370/pcd    (cached)
src/github.com/hirokuma/go_pasori370/pcd$ go clean -testcache
src/github.com/hirokuma/go_pasori370/pcd$ go test .
ok      github.com/hirokuma/go_pasori370/pcd    0.003s

1つ上のディレクトリからだと、こう。

src/github.com/hirokuma/go_pasori370$ go test ./...
?       github.com/hirokuma/go_pasori370/cmd    [no test files]
?       github.com/hirokuma/go_pasori370/dev    [no test files]
ok      github.com/hirokuma/go_pasori370/pcd    0.003s

ちなみに、"./..."じゃなくて"..."とすると、golangの大元からテストするようだ。

 

いくつかわかったこと。

  • _test.goのパッケージ名は、オリジナルと同じか、後ろに_testをつけるのが一般的か
    • オリジナルと同じだとimportがいらん
    • 外部から使ったのと同じようにしたいなら、_testを付けるのがいいのかな
  • _test.goのオリジナルにある関数を全部テスト関数にしなくても怒られない
    • -vつけてリストを出すしかないか
    • -coverで漏れたところを感じとるのがよいのかなぁ
  • テストの結果はキャッシュされる
    • go clean -testcache
  • テストとは関係ないが、配列やsliceの固定値は定義できない
    • 全部RAMに展開するということか

2020/03/07

[golang]RC-S956 encode部分だけ

golangで、とりあえずRC-S956に送信するデータだけ作ることにした。

01: package pcd
02: 
03: // RawMsg raw data
04: type RawMsg struct {
05:     Cmd  uint8
06:     Data []uint8
07: }
08: 
09: // RawEncode encode RC-S956 format
10: func RawEncode(msg *RawMsg) []uint8 {
11:     data := make([]uint8, 3+2+1+1+len(msg.Data)+2)
12:     data[0] = 0x00
13:     data[1] = 0x00
14:     data[2] = 0xff
15:     data[3] = (uint8)(1 + 1 + len(msg.Data))
16:     data[4] = (uint8)(-data[3])
17:     data[5] = 0xd4
18:     data[6] = msg.Cmd
19:     copy(data[7:], msg.Data[:])
20: 
21:     sum := uint(0xd4 + msg.Cmd)
22:     for _, v := range msg.Data {
23:         sum += uint(v)
24:     }
25:     data[7+len(msg.Data)] = uint8(-sum)
26:     data[7+len(msg.Data)+1] = 0x00
27: 
28:     return data
29: }
30: 
31: // RawDecode decode RC-S956 format
32: func RawDecode(data []uint8) *RawMsg {
33:     return new(RawMsg)
34: }
  

たったこれだけ(しかもDecodeはやってない)のだが、けっこう時間がかかった。

 

structの形を決める

私はどうも、汎用的に作るのが苦手だし、拡張とか考えるのも苦手なようだ。

今回はgolangなので、メモリの確保については悩まずに済むし、ステップ数を減らす努力もしなくてよいだろう。
0x00の代入は省略しようかどうかと多少悩んだのだが、そこはコンパイラに任せよう。
データ構造としては、書いていた方がわかりやすいから、今回は書いた。

 

0xD4がメインコマンドなのだが、それはもう取り込んだ形にした。
わざわざメインコマンドとサブコマンドをユーザに指定させる必要はあるまい。
そもそも、0xD4以外で始まるパターンってよく知らんし。

 

というわけで、structはサブコマンドとそのパラメータのみになった。
パラメータはmakeして渡すイメージだ。

 

packageパス

またしてもpackageパスに悩まされた。

今回はmainパッケージは呼ぶだけにするつもりなので、github.com/hirokuma/go_pasori370/cmdの下にmain.goを置くことにした。
それ以外はgithub.com/hirokuma/go_pasori370/pcdに。

main.goでimportするのは"github.com/hirokuma/go_pasori370/pcd"でよかったのだが、go runするパスがよくわからなくなった。

# これはOK
src/github.com/hirokuma/go_pasori370/cmd $ go run .
src/github.com/hirokuma/go_pasori370/cmd $ go run github.com/hirokuma/go_pasori370/cmd

# これはNG
src/github.com/hirokuma/go_pasori370 $ go run cmd
src/github.com/hirokuma/go_pasori370 $ go run cmd/.

カレントディレクトリはいいけど、パス名が入った途端にsrcからの絶対パスと判定されるのか。

 

 

rangeの戻り値

作ったのは良かったのだが、DCSの値があわなかった。
forでぐるぐる回して足しているところだ。

for v := range msg.Data {
    sum += uint(v)
}

これは、ダメなのね。
正しくは、こう。

for _, v := range msg.Data {
    sum += uint(v)
}

あー、あるある~、って言ってほしいところであった。

 

01: package main
02: 
03: import (
04:     "encoding/hex"
05:     "log"
06: 
07:     "github.com/hirokuma/go_pasori370/pcd"
08: )
09: 
10: func main() {
11:     var msg pcd.RawMsg
12: 
13:     msg.Cmd = 0x18
14:     msg.Data = []uint8{0x01}
15:     data := pcd.RawEncode(&msg)
16:     log.Printf("data: %s\n", hex.Dump(data))
17: }
  

$ go run github.com/hirokuma/go_pasori370/cmd
2020/03/07 20:52:12 data: 00000000  00 00 ff 03 fd d4 18 01  13 00                    |..........|

久々すぎて値に自信がないのだが、まあ実際に流して見れば分かるだろう。

[golang][gousb]デバイスの認識

では、gousbを使ってみよう。

 

exampleを使う。

https://github.com/google/gousb/blob/master/example_test.go

まあ、まずはデバイスを認識できるかどうかからだ。
libusb-1.0がいるから、事前にaptなりなんなりでインストールしておく。

01: // sudo apt install libusb-1.0
02: 
03: package main
04: 
05: import (
06:     "fmt"
07:     "log"
08: 
09:     "github.com/google/gousb"
10: )
11: 
12: const (
13:     VID = 0x054c
14:     PID = 0x02e1
15: )
16: 
17: // https://github.com/google/gousb/blob/master/example_test.go
18: func exampleSimple() {
19:     // Initialize a new Context.
20:     ctx := gousb.NewContext()
21:     defer ctx.Close()
22: 
23:     // Open any device with a given VID/PID using a convenience function.
24:     dev, err := ctx.OpenDeviceWithVIDPID(VID, PID)
25:     if err != nil {
26:         log.Fatalf("Could not open a device: %v", err)
27:     }
28:     defer dev.Close()
29: 
30:     // Claim the default interface using a convenience function.
31:     // The default interface is always #0 alt #0 in the currently active
32:     // config.
33:     intf, done, err := dev.DefaultInterface()
34:     if err != nil {
35:         log.Fatalf("%s.DefaultInterface(): %v", dev, err)
36:     }
37:     defer done()
38: 
39:     // Open an OUT endpoint.
40:     ep, err := intf.OutEndpoint(7)
41:     if err != nil {
42:         log.Fatalf("%s.OutEndpoint(7): %v", intf, err)
43:     }
44: 
45:     // Generate some data to write.
46:     data := make([]byte, 5)
47:     for i := range data {
48:         data[i] = byte(i)
49:     }
50: 
51:     // Write data to the USB device.
52:     numBytes, err := ep.Write(data)
53:     if numBytes != 5 {
54:         log.Fatalf("%s.Write([5]): only %d bytes written, returned error is %v", ep, numBytes, err)
55:     }
56:     fmt.Println("5 bytes successfully sent to the endpoint")
57: }
58: 
59: func main() {
60:     exampleSimple()
61: }
  

$ go run .
2020/03/07 11:04:01 Could not open a device: libusb: bad access [code -3]
exit status 1

まあ、いきなりは動かんか。

 

デバイスを使う場合、sudoとかしないといけなさそうな気がするのだが、lsusbなんかはsudoしなくても使える。
wiresharkなんかはグループを作っておけばsudoせずにいけたのだが、libusbはどうだったか。

L.24のctx.OpenDeviceWithVIDPID(VID, PID) で起きている。
C言語で書くとこんな感じだった。

https://github.com/hirokuma/libhknfcrw_c/blob/master/examples/cyg_devaccess_pasori.c#L109

sudoしてみる。

$ sudo ./go_pasori1
2020/03/07 11:11:44 vid=054c,pid=02e1,bus=2,addr=5.DefaultInterface(): failed to select interface #0 alternate setting 0 of config 1 of device vid=054c,pid=02e1,bus=2,addr=5: failed to claim interface 0 on vid=054c,pid=02e1,bus=2,addr=5,config=1: libusb: device or resource busy [code -6]

これはL.35のメッセージだから、権限の問題ということか。

udevで設定を書くことで回避できるようだが、しばらくはsudoでよかろう。


では、次はL.35のエラーについてだ。
Busyっぽいのだが、私はRC-S370をVirtualBoxのデバイス指定で使えるようにしただけだ。

dmesgを見てみる。

[15145.225082] usb 2-2: new full-speed USB device number 6 using ohci-pci
[15146.112505] usb 2-2: New USB device found, idVendor=054c, idProduct=02e1
[15146.112507] usb 2-2: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[15146.112509] usb 2-2: Product: RC-S370/P
[15146.112511] usb 2-2: Manufacturer: Sony
[15146.124738] usb 2-2: NFC: NXP PN533 firmware ver 1.48 now attached

OSがRC-S370をデバイスとして認めてしまっている・・・?
そういえば、いつからかLinuxはNFCデバイス(たぶんReader/Writerだろう)を識別するようになったという記事を見かけた気がする。

RC-S380(Pasori)とLinux NFC Subsystem | 何かできる気がする
https://dekirukigasuru.com/blog/2019/01/05/linux-nfc-subsystem/

Linux NFC Subsystem、ね。
できれば、今回は新しいことは試さず、USBとして動かしたいのだ。

 

そういえばC言語で書いていたときも、default interfaceみたいなものは使えなかった気がする。

https://github.com/hirokuma/libhknfcrw_c/blob/master/examples/cyg_devaccess_pasori.c#L133-L156

おそらく、この辺りはend pointを探しているところだろう。

なので、便利関数を使うサンプルではなく、その下にある方のサンプルを使おう。

https://github.com/google/gousb/blob/master/example_test.go#L76

ここから下を貼り付けただけなので、コードは省略だ。

2020/03/07 11:47:56 vid=054c,pid=02e1,bus=2,addr=6.Config(2): device vid=054c,pid=02e1,bus=2,addr=6: configuration id 2 not found in the descriptor of the device. Available config ids: [1]

"Config(2)"なので、ここのエラーか。

https://github.com/google/gousb/blob/master/example_test.go#L105

そういえば、なんとなくコードを貼り付けて動かしているのだが・・・この2とか3とかってなんなのだ??
エラーログの一番最後に"[1]"となっているから、2じゃなくて1ならconfigがあるということか。

・・・1にすると進んだ。。。
まあいい、最後まで通してから考えよう。

 

次のエラーはこれ。

2020/03/07 11:53:06 vid=054c,pid=02e1,bus=2,addr=6,config=1.Interface(3, 0): descriptor of interface (3, 0) in vid=054c,pid=02e1,bus=2,addr=6,config=1: interface 3 not found, available interfaces 0..0

3じゃなくて0ってことか。
が、0にしたらしたで別のエラーになった。

2020/03/07 11:54:51 vid=054c,pid=02e1,bus=2,addr=6,config=1.Interface(3, 0): failed to claim interface 0 on vid=054c,pid=02e1,bus=2,addr=6,config=1: libusb: device or resource busy [code -6]

ダメだ、このやり方じゃダメだ。。。


C言語で書いていたときのやり方がいいだろう。
調べられるリストを作って、先頭から順に調べて使えるものを探す、と。

改めて、openする流れをおさらいしよう。

https://github.com/hirokuma/libhknfcrw_c/blob/master/examples/cyg_devaccess_pasori.c#L47

  1. libusb_open_device_with_vid_pid()でハンドル取得
  2. libusb_get_device()でデバイス取得
  3. libusb_get_config_descriptor()でコンフィグ取得
  4. コンフィグからinterfaceの数だけEnd Pointを探す?
  5. libusb_claim_interface()でclaim

自分で書いておきながら理解していないが、当時もlibusbのサンプルから持ってきたやり方を使っただけだろう。
コンフィグ中をぐるぐる探すところも、本当はbreakとかして抜けるべき何じゃなかろうかね。。。

01: // sudo apt install libusb-1.0
02: 
03: package main
04: 
05: import (
06:     "fmt"
07:     "log"
08: 
09:     "github.com/google/gousb"
10: )
11: 
12: const (
13:     VID = 0x054c
14:     PID = 0x02e1
15: )
16: 
17: // https://github.com/google/gousb/blob/master/example_test.go
18: func openUsb() {
19:     // Initialize a new Context.
20:     ctx := gousb.NewContext()
21:     defer ctx.Close()
22: 
23:     // Open any device with a given VID/PID using a convenience function.
24:     dev, err := ctx.OpenDeviceWithVIDPID(VID, PID)
25:     if err != nil {
26:         log.Fatalf("Could not open a device: %v", err)
27:     }
28:     defer dev.Close()
29: 
30:     for _, cfg := range dev.Desc.Configs {
31:         for _, inf := range cfg.Interfaces {
32:             for _, alt := range inf.AltSettings {
33:                 for _, endPnt := range alt.Endpoints {
34:                     fmt.Printf("endpoint=%v\n", endPnt)
35:                 }
36:             }
37:         }
38:     }
39: }
40: 
41: func main() {
42:     openUsb()
43: }
  

$ sudo ./go_pasori1
endpoint=ep #4 OUT (address 0x04) bulk [64 bytes]
endpoint=ep #4 IN (address 0x84) bulk [64 bytes]

 

最初、for文でinterface数なんかをがんばって取得してぐるぐる回そうとしていたのだが、lsusbサンプルを見てrangeというものがあったことを思い出す。


おそらくこれでUSBデバイスはオープンできたと思う。

だが、C言語でlibusbを使ったときは最後にlibusb_claim_interface()を呼んでいた。
これは何をする関数なのだろうか?

http://libusb.sourceforge.net/api-1.0/group__dev.html#ga32fabedf5f13fdecf1cb33acdb19b57a

制御するデバイスを主張する、なのか?

C言語版では固定で0を使っていたのだが、果たしてうまくいっていたのだろうか・・・。

 

それはともかく、gousbだとConfig()がそれなのか。
しかし、libusb_bulk_transfer()は無く、代わりにReadStream, WriteStreamを取得してアクセスするようだ。
その辺はサンプルを見た方が早いだろう。

https://github.com/google/gousb/blob/master/example_test.go#L52

 

Closeするのはdevだけでよさそうな気配がする。
これはまだ確信を得られないので、後で考えよう。

2020/03/06

[golang]USBデバイスを操作できるのか

なんとなくgolangで書いていけるんじゃないかというところまでやってきたと思うので、何か作ることにする。
何かしら1つでも動くものを作れば、見えてくるものがあるんじゃなかろうか。

 

というわけで、今回はUSBデバイスが操作できるのか調べよう。


出てきたのは、これ。

google/gousb: gousb provides low-level interface for accessing USB devices
https://github.com/google/gousb

googleの下にあるってことは、golangの標準ライブラリかと思ったのだが、そうではないようだ。
まあ、普通はUSBデバイスを操作しようとは思わんよな。

 

libusb-1.0を使うらしく、先にインストールがいるそうだ。
ということは、かなりローレベルなところを扱うようにできているのか。

そのおかげか、Windowsでも使えるらしい。
まあ、Windowsでlibusbということは、zadigなんかも使うことになるだろう。

だが、今はgolangをVirtualBox上のLubuntu18.04で動かしているので、USBデバイスもVirtualBox経由になる。
WSLではまだUSBデバイスが扱えなかったと思うし、しばらくはそれでよかろう。


うまくいくかわからないが、昔書いたUSBを制御するアプリをgolangに移植していこう。

C言語でつくり、C++、C#、Javaに持っていき、Androidでも確か動かした気がする。
最初に書いてから何年も経つので、今から書き直すともうちょっとましになるかもしれん(ならない気もするが・・・)。

ともあれ、課題にはほどよいんじゃなかろうか。

2020/03/04

[golang]defer

関数を抜けるときに実行してくれる処理を指定することができるそうだ。

それがdefer。

Defer, Panic, and Recover - The Go Blog
https://blog.golang.org/defer-panic-and-recover

なぜか知らんが、deferとpanicとrecoverは並べて比べるもののようだ。
まあ、後者2つは知らんのだがね。


deferキーワードを付けた関数はリストに載せられ、リストに載ったものは関数がreturnする際に呼び出されるそうだ。リストは上に積んでいくタイプで、呼ばれるときも上から順番だ。

 

src/ex7/main.go

01: package main
02: 
03: import "fmt"
04: 
05: func main() {
06: 	defer fmt.Printf("Yoshio.\n")
07: 	defer fmt.Printf(" is ")
08: 	defer fmt.Printf("My name")
09: }
  

src$ go run ex7
My name is Yoshio.

ふむ。
では、こうしたらどうなるだろう?

src/ex7/main.go

01: package main
02: 
03: import "fmt"
04: 
05: func main() {
06: 	{
07: 		defer fmt.Printf("Yoshio.\n")
08: 		defer fmt.Printf(" is ")
09: 		defer fmt.Printf("My name")
10: 	}
11: 	defer fmt.Printf("Mao.\n")
12: 	defer fmt.Printf(" are ")
13: 	defer fmt.Printf("You")
14: }
  

src$ go run ex7
You are Mao.
My name is Yoshio.

もしかしたらスコープを抜けるタイミングで実行されるんじゃないかと思ったのだが、returnするところまでリストに積んでから実行しているな。


deferは関数をリストに載せるものなら、こんなのでもよいのだろうか。

01: package main
02: 
03: import "fmt"
04: 
05: func main() {
06:     defer func() {
07:         fmt.Printf("My name")
08:         fmt.Printf(" is ")
09:         fmt.Printf("Yoshio.\n")
10:     }
11: }
  

無名関数、というやつかね(anonymous function、らしい)。

function must be invoked in defer statement

うーん、無名関数は関数ではないのだろうか?

01: package main
02: 
03: import "fmt"
04: 
05: func main() {
06:     defer func() {
07:         fmt.Printf("My name")
08:         fmt.Printf(" is ")
09:         fmt.Printf("Yoshio.\n")
10:     }()
11: }
  

ああ、括弧がこんなところに必要なのね!

src$ go run ex7
My name is Yoshio.

 

括弧に気付いたのはこちらのおかげだ。

go - Multiple defers vs deferred anonymous function - Stack Overflow
https://stackoverflow.com/posts/32541870/edit

そう、私もどっちがいいのか気になって、そもそも書けるのかどうかを試していたのだった。
順番が大切なら、anonymous functionにした方が意図が分かりやすいだろう。

2020/03/03

[golang]class関数っぽいやつはメソッド

気を抜くと・・・実装をまったくやっていない・・・。
意味が無くても良いので、何か手を動かさねば。

 

というわけで、今回はgolangのclass関数っぽいやつをやってみる。


classっぽい関数というのは、あれだ。

  • 実装としてはstruct
  • 関数名の前に括弧してstructの変数を書いておくと、その変数があたかもグローバル変数であるかのように使用できる

 

名前が分からんのだ・・・。
正式名が分かれば検索できるのだが、どうにも覚える気力が湧かない。

https://golang.org/ref/spec#Function_declarations

あれ・・・funcの直後に関数名が来ているぞ?

https://golang.org/ref/spec#Method_declarations

こっちだ。
そうか、functionとmethodは別物だったのか。
class変数っぽいやつは、Receiverという名前ね。うん、どこかで聞いた覚えはある。

 

というわけで、今回はメソッドをやってみるのだ。


src/ex6/cmd/main.go

01: package main
02: 
03: import "ex6/gogo6"
04: 
05: func main() {
06: 	var da gogo6.Data6
07: 
08: 	da.PrintGogo()
09: }
  

 

src/ex6/gogo6/print_gogo.go

01: package gogo6
02: 
03: import "fmt"
04: 
05: type Data6 struct {
06: 	name string
07: 	age  int
08: }
09: 
10: func (data *Data6) PrintGogo() {
11: 	fmt.Printf("name: %s\n", data.name)
12: 	fmt.Printf("age: %d\n", data.age)
13: }
  

src/ex6$ go run ex6/cmd
name:
age: 0

初期化していないので、デフォルト値が出力されている。
C言語の人としては未初期化の変数を使うのは心が痛むのだが、まあ動くからよかろう。

 

未初期化が嫌なら初期値を設定すれば良いのだが、Data6のメンバは小文字で始まっているので外部からはアクセスできない(undefinedになる)。

この手の構造体は、Newするメソッドも用意するのが一般的だったと思う。
なので、print_gogo.goにこういうメソッドを追加しようとした。

01: func (data *Data6) NewData6() {
02: 	data.name = ""
03: 	data.age = 0
04: }
05: 
06: func (data *Data6) NewData6(name string, age int) {
07: 	data.name = name
08: 	data.age = age
09: }
  

はい、ダメでした。
オーバーロードはないのね。

https://golang.org/doc/effective_go.html#composite_literals

うーん、Newは値の初期化というよりは、普通にメモリを確保する方がよさそうだ。
初期化だけだったらInitがいいのかな?

メモリ確保も、golangだとnewとmakeがあるようだ。
眺めた感じでは、newは1つ、makeはmapとかsliceとかかな。

 

src/ex6/cmd/main.go

01: package main
02: 
03: import "ex6/gogo6"
04: 
05: func main() {
06: 	da := gogo6.NewData6("yoshio", 95)
07: 
08: 	da.PrintGogo()
09: }
  

src/ex6/gogo6/print_gogo.go

01: package gogo6
02: 
03: import "fmt"
04: 
05: type Data6 struct {
06: 	name string
07: 	age  int
08: }
09: 
10: func NewData6(name string, age int) *Data6 {
11: 	data := new(Data6)
12: 	data.name = name
13: 	data.age = age
14: 	return data
15: }
16: 
17: func (data *Data6) PrintGogo() {
18: 	fmt.Printf("name: %s\n", data.name)
19: 	fmt.Printf("age: %d\n", data.age)
20: }
  

src/ex6$ go run ex6/cmd
name: yoshio
age: 95

 

newは解放もあるかもしれんが、今日はここまで。