Closed yamori813 closed 11 months ago
Arrayクラスの [] メソッドが参考になるのではないでしょうか。
https://github.com/mruby/mruby/blob/298155d59ed4ed952ce00a8beb0438ab4692fac4/src/array.c#L964
このメソッドでは可変長引数で、ary[n] と ary[n,m] の2パターンあります。
実装では、if (mrb_get_argc(mrb) == 1)
で、引数の個数を判定して動作を分けています。
この修正をしたところ引数3の場合正しく動かなくなりました。:(
こちらのミスでした。お騒がせしてすみません。
別の話になりますが、センサーなどはバイナリ値を返しますがread()の返り値が文字列というのはどのような形式になりますか?
また
s = i2c.read( 0x45, 3, 0xf3, 0x2d )
writeの方がarrayなので、
s = i2c.read( 0x45, 3, [0xf3, 0x2d] )
の方が自然ではないですか?
I2C.newでキーワード引数を使用していますが、mrbgemsのCコードでキーワード引数はどのようにして拾えるのでしょうか?
戻り値の件
Rubyは、バイトアレイ型は String と共用する方針なので、read の戻り値もそのように設計してあります。
例)センサーから 0x21, 0x00, 0x3a の3バイトが返る場合、readの戻り値は、"\x21\x00\x3a"
が返ることを想定
戻り値を使う方法として、以下の2種類の方法が代表的な使い方だと考えています。
getbyte メソッドを使う
s = i2c.read( 0x45, 3, 0xf3, 0x2d )
b0 = s.getbyte(0)
b1 = s.getbyte(1)
b2 = s.getbyte(2)
bytes メソッドを使う
a = i2c.read( 0x45, 3, 0xf3, 0x2d ).bytes
b0 = a[0]
b1 = a[1]
b2 = a[2]
センサーのように数バイトしか送受信がないデバイスならば、戻り値が String でなく Array である方が使いやすいと思います。これは別の方からも指摘をうけました。けれども I2C接続の FlashROM を扱うユースケースがありまして、その場合 Read/Write 単位が数KBになります。数KB 長の Array はマイコンにとってはすごく厳しいので String を規定しました。
readメソッドの第3引数の件
write
メソッドのデータは、 Integer, Array
s = i2c.read( 0x45, 3, 0xf3, 0x2d )
s = i2c.read( 0x45, 3, [0xf3, 0x2d] )
使う側にとってより自由度が高く、ライブラリを実装する側は苦労する という方針ですね。
readの返り値の理解しました。
こちらもmruby-cairoといmrgbemsでメモリバッファに描画して、他のmrbgemsでデバイスに描画するためにデータ渡しするのをArrayでは効率が悪いのでポインター渡しにしていました。
私の理解ではキーワード引数はrubyコードでは使えますが、mrbgemsのCコードでは直接処理する事は出来ないと思います。rubyコードでクラスを作って、そこからCコードを呼び出すみたいな事をすればできるのかもしれませんが、あまり現実的ではありません。
Cコードでキーワード引数を取得する事自体は可能のはずですが、mrubyでのオーソライズされた方法を私は知らないです。 だれか詳しい人はいませんかね。
参考情報として、mruby/c の方は、MRBCKW*** マクロを用意して対応できるようにしています。 サンプルは、PIC32の mruby/c 実装だと以下。 https://github.com/YoshihiroOgura/pic32mx170_mrubyc/blob/758b664af4045ae51a52b03e53ba8e73030accae/pwm.c#L180
@yamori813 mrbgemのCコードでキーワード引数を扱うには、mrb_get_args()のフォーマット文字に “:” を指定して、mrb_kwargs構造体で受け取ります。
使用方法は mruby.h に記載がありますので、こちらを参照ください。 mrb_get_args()を呼び出す前に、mrb_kwargsの変数を初期化する必要があるところが注意点です。
実装例は mrbgem/mruby-io/src/io.c が参考になると思います。 ご参考まで。
@mimaki ありがとうございます。
処理が複雑ですね。デバイス周りは制約があったりするので、あまり複雑にならないのが嬉しいです。
@yamori813 リファレンス実装があると良いですね。それがあれば、皆がそれを元にデバイス依存の部分のみ書けば良いですし。 全体の設計思想として、以下の事を考えて設計しています。
その結果として
といった構成になっているはずです。キーワード引数の対応は、ライブラリ作成者にとっては面倒ですけどね。
i2cのwriteのエラー時には0が返り、readのエラー時にはmrb_nil_value()が返るという認識でよかったですか?
あと
s = i2c.read( 0x45, 3, 0xf3, 0x2d )
は2バイト送信後受信になり
s = i2c.read( 0x45, 3, 0xf3,)
は1バイト送信後受信で、intの引数は2バイトまで直接指定が可能という仕様ですか?
エラー時の挙動や戻り値は、今のところ規格では明確に定めていないものが多いです。 とりあえず正常系をきちんと定めてレビュー・試験実装してもらい、異常系(エラー時)の挙動はむしろ提案して欲しいと思っています。どうしたらよいと思いますか? Ruby的には例外を発生させるのが本筋かと思い、Rboard+mruby/c では例外を発生させていますが、ちょっと大げさと思う面もあり悩みます。
read の引数の件
intの引数は2バイトまで直接指定が可能という仕様ですか?
readの引数の数、具体的には送信できるバイト数ですが、制限はありません。
s = i2c.read( 0x45, 3, 0xf3, 0x2d, 0x03, 0x04, 0x05 ) # 5bytes送信後 3bytes 受信
この仕様は、rubygem の i2c とコンパチブルになっています。 https://www.rubydoc.info/gems/i2c/0.4.2/I2C/Dev#read-instance_method
readの引数ですが、mrubyのmrbgemsで制限無しの引数は実装できるのでしょうか?
返り値も引数も複数要素がある場合はArrayを基本として、最適化が必要な場合はStringsでの実装も可能とするのが、現実的な気がします。
FreeBSD上のmruby用のi2c mrbgemsをちょっと修正してみました。
https://github.com/yamori813/mruby-bsdiic
i2cはバイトオペレーションで操作できるslave(device)が多いので、バイトオペレーションとarrayでのオペレーションにしてみました。
頑張らないと実装できないのはmrubyらしくないと思うんですが。
ここを見るとESP32のmruby/cでもreadの返り値はarrayみたいですね。 https://www.gfd-dennou.org/arch/sugiyama/IoTeX_2018/es/mrubyc_2020/lecture_mrubyc_04-I2C.htm.ja
そうですね。この記事ではまだ規格策定前なので、自分なりの方法で実装されたのですね。今回こういったケースを統一させて、可能な限り規格に沿って実装してもらえば、利用者にとって嬉しいことが多いだろうという目論見です。 ですので実装を頑張るのは、ライブラリ作成者です。その分利用者は楽をするというバランスです。もちろんライブラリ作成が難しすぎてもうまく回らないので、現実的なところを考えたつもりです。
可変引数は、mrubyのソースを見ると、MRB_ARGS_ANY()を指定してArrayで受け取ることが出来るようです。
しかし返り値はコストを削減のためにStringsで、引数はいろいろなタイプを可能にしてコストを増やすのはちぐはぐな感じがします。
直接お話しする機会を得たので、そのサマリーを追記して、この件はクローズしたいと思います。
Sorry use Japanese.
i2cのwriteで
i2c.write( 0x45, 0x30, 0xa2 ) i2c.write( 0x11, [0x30, 0xa2] )
と可変の引数を受け取れるようにしていますが、これはmrbgemsのCではどのように実装するのでしょうか?
mruby-bsdiicというmrbgemsでwriteとwritesに分けているので、この仕様に従いまとめてみようか思い疑問になりました。