鳳凰OS(Phoenix OS)をVMWareにインストールしてみよう

鳳凰システム – Phoenix OS
http://www.phoenixos.com/ja_JP/ という Android ベースな中国産の OS があることを知りました。以前、Remix OS があって、うまいこと仮想環境にインストールできなくて断念していたのですが、今回の Phoenix OS は VMWare Player にインストールして程よくことができたので、お試しに。ただし、再起動すると、何故かインストーラから始まる(どうやら、内部 HDD じゃなくて、ネットワーク起動が優先になっているのが問題らしい)ので、あまり実用的ではありませんが。

http://www.phoenixos.com/zh_CN/
image

日本語OSからアクセスすると「鳳凰システム」になっていますが、れっきとした北京超卓科技 https://www.lagou.com/gongsi/62428.html なので中国の株式会社ですね。

Remix OSのインストールと設定 | E.i.Z
http://eizone.info/remix-os/

を見ると、Remix OS のほうは潰れたそうです。

Phoenix OS は、中国語版と英語版しかありません。Nexus 7 のタブレット Android に入れることもできるようですが、ちょっと危ういので、Phoenix OS for x86 のほうを VMware Player 上に入れます。実は、Windows を PC に入れた状態からディアルブートするインストーラーがあるのですが、VMWare 上では起動ができないため、*.iso ファイルをダウンロードして別途仮想環境で動かしています。ISO image (64bit) にある PhoenixOSInstaller_v2.2.0.207_x86_x64.iso   854.5M のやつですね。中国から落とすと 100kb/sec 程度しか出ないのでダウンロードに5時間位かかりますが、ひたすら待ちます。ちなみに、これは中国内の回線が遅いのではなく、中国と日本間の回線が遅いのです。例の検閲絡みですね。中国内だと高速通信がバンバン走っているので、日本国内よりも(最近は光通信があるから同じぐらいか)早いぐらいです。

仮想環境でのメモリは 1GB、ストレージは16GB 程度あれば十分です。スマホで動く程度で鳳凰OSも動かすことができます。

鳳凰OSをインストールする

*.iso をマウントして VMWare を起動すると、OS をどのように起動するのかを聞く画面が出てきます。デフォルトでは、インストールメディア(DVDか?)を使ってインストールする方法になっていますが、下矢印キーを押して HDD にインストールするようにします。DVD メディア起動だと、ストレージが小さくて後から説明する Android アプリをインストールできません。

image

なんと、この間が5秒しかないので、すかさず下矢印キーを押してください!

で、HDD インストール時に ext4 にフォーマットしてインストールを続けていきくと、下の画面になります。PCがそこそこ早ければ5分位で終わります。

image

インストール直後の起動画面。言語は、英語と中国語しか選べません。

image

見慣れた、Windows 10 っぽい画面がでてきます。動きもそれっぽいので、使うのにはそれほど問題ないような気もしますが…アプリをインストールしようにも、基本は Android なアプリしか入れられないで、利用範囲はかなり狭いです。

image

標準で、Phoenix Feature なストアがあるんですが、アプリが非常に少なく利用できる状態にありません(現在 v2.0 なのに…って感じがするけど、まあ、そういうものなのかも)。なので、素直に Google Play を使います。google アカウントは念のために新しく作っています。バックドアがあるかもしれないからね。

image

組み込み系 Android 御用達の「戦艦少女R」をインストールします。何故か、マウスでするする動くし、ほどよくゲームなので、試しにちょうどよいのです。しかも、中華 Android で非常によく動きます(弊社比)。

image

戦艦少女Rが動いたら、ユーザー名を入れて適当にチュートリアルをこなしてみましょう。音も出て、結構きびきび動くことがわかります。画質も結構きれいですね。

鳳凰OSにはファイルマネージャがある

いわゆる、ファイルエクスプローラーがあるので、Android 内部のファイルを見ることができます。どうもルート権限はないみたいなので、Documents 配下ぐらいしか触れない気もしますが。

image

Google Player から色々ダウンロードしてみる

この際なので、色々試してみます。

すべてのゲームが正常に動くわけではなくて、起動ができなかったり、画面が微妙だったりします。鳳凰OSでは、Android アプリをマルチウィンドウで動かせるわけですが、こんな風に、にゃんこ大戦争と戦艦少女Rを並べて起動ができます。ここはマルチで動いていて、両方とも音がなります。にゃんこ大戦争は、ウィンドウを大きくしてもゲーム画面が大きくなりません。戦艦少女Rは全画面表示でも動きます。戦艦少女R、優秀ですね。

image

ちなみに、歌マクロスはインストールできるもののマウスが効かず、マギレコとパズドラは起動ができず。ミリオンダッシュは最初の年齢が入れられず、と動かないものもの結構あります。モノ的には Android 7. 1 なのですが、ウィンドウ化するにあたって無理がきている模様。

image

以下は、いくつか動かしてみたもの。

# Android 版の Office 365 が入っているのだけど、アカウントを入れる勇気がないので試していません。

紅白アプリは、Xamarin で作っているものです。動きますね。

image

カンペアプリは、確か Unity だったはず。マウスでお絵かきができます。たぶんタッチパネルも動くと思う。

image

ネットワークドライブも扱えます。これは別のPCにある mp4 を鳳凰OS標準の Video Player で表示させたところです。有線LANだからか、動画や音の遅延はありません。

image

面白いところでは、Google Play にある Remote Desktop アプリをダウンロードさせて Windows 10 にリモートアクセスができます。リモート先から Tera Term を開いてラズパイに繋げているところです。キーボードとマウスが有効に働きます。

image

Android 自身への接続ができる Terminal Emulator を入れると、自分自身を操作できます。フォントの大きさは自由に変えられます。ユーザー名は u0_a77 という一般ユーザー名(ランダム?)になっています。su は効かないので、ルートになれるかどうかはわかりません。

image

結構色々動きそうなのですが、難点がいくつかあります。

再起動ができない

なぜか、VMWare 上で再起動させると、ひたすらネットワーク起動をしようとして起動できない状態に陥ります。これは、鳳凰OS無いからシャットダウンしたときも同様です。

仕方がないので、DVD を入れたまま(*.isoがアクセスできる状態にしたまま)で、起動。すかさず、下矢印を押して、HDD にインストールする状態にして、

image

下のように HDD をフォーマットしないままで進みます(初回のインストールではフォーマットが必須です)。そうすると、HDD の内容がそのままマウントされるので、以前の状態が復元されます。

image

本体のインストールに30秒位かかりますが、まあ VMWare 上だから仕方がないのかなと。実機 PC にしてディアルブートにした立ち上がるのかもしれません。

鳳凰OSアプリの開発はどうするのか?

どうやってやるんでしょうねぇ?書いてありません。

と思ったら、どうやら TCP/IP 経由の Android デバッグが動きます。

  1. 設定を Native Settings にする。
  2. 設定の About → Build number を連打。
  3. メインの設定には「Developer options」は出ないが、左上のハンバーガーメニューから出るので、ここから USB デバッグを ON にする。
  4. PC のほうから、adb connect <ipアドレス> で接続

↓な感じ、で無事 Visual Studio から Xamarin.Android アプリが起動できます。

image

めでたしめでたし。

カテゴリー: 開発, Xamarin | 鳳凰OS(Phoenix OS)をVMWareにインストールしてみよう はコメントを受け付けていません

ラズパイでUbuntu Core を試してみよう(前編)

ふと、Nano Pi K2 に Ubuntu を入れて .NET Core を使ってあれこれしようと思ったのですが、何やらこの OS がちょっと Ubuntu とは違う。Raspberry Pi に Ubuntu Mate を入れたときと挙動がちがう。そもそも CPU は同じ Cortex-A53 なのだから、GPIO まわりが違うとはいえ、Hello world 位は動くのではないか?

http://wiki.friendlyarm.com/wiki/index.php/NanoPi_K2#UbuntuCore

image

と思って、何の OS だか再確認すると「UbuntuCore」と書いてあります。これは、普通の Ubuntu とはどう違うのか?ってのが発端です。

そもそも Nano Pi K2 って何?

Friendly ARM で購入できるラズパイ互換機です。Nano Pi にはいくつか種類があって、メモリや USB の数などで選ぶわけですが、Nano Pi K2 は、Android が動作して歌マクロスを動かすために購入しました。Raspberry Pi 3 よりも安く購入ができます。ちなみに、このボードでは歌マクロスは動きませんでした orz どうやら、タッチパネルが必須みたいですね。ただし、マギレコは動きます。これは後日。

image

現在のところ動作できる OS は、Android 5.1 と Ubuntu Core だけです。Raspbian のような Debian 系はありません。購入時には、赤外線のコントローラーとケースが付属しているので、Android を入れると Fire TV のように動作できます。というか、その目的で売っているみたいですね。これも後日。

Ubuntu Core とは何か

そういえば、Docker を勉強した頃に、CentOS の Atomic というのがあって、Docker 専用のコンテナ OS でした、ってのをやっと思い出しました。

コンテナホスト向けOS環境「CentOS Atomic Host」や「Snappy Ubuntu Core」を試す – さくらのナレッジ
http://knowledge.sakura.ad.jp/knowledge/4217/

元の OS に apt-get やらで色々インストールすると環境が汚れるので、Docker を入れて動かします。Docker のコンテナとしての機能しか要らないので、機能が相当削ってあり vi すら動かない状態だった、とかなんとか。そんな訳で、素の状態の OS として使う訳ではありません。

当時、Raspberry Pi でも Docker が動かないかとかいう話があったのですが、当時のラズパイは、まだ 32bit で、Docker を動かすには 64bit CPU が必須だったという状態だった、というのを覚えています。

で、現在のラズパイ3は、64bit なので、Docker が動きます。なので、それのコンテナ用としての Ubuntu Core も動作する、という状況な訳ですね。www.raspberrypi.org でも「Snappy Ubuntu Core」と言う形でダウンロードできます。これをクリックすると、Ubuntu developer の https://developer.ubuntu.com/core/get-started/raspberry-pi-2-3 にジャンプするので、「Ubuntu Core」のことです。

image

さて、Ubuntu Core が動作する環境は、 https://developer.ubuntu.com/core/get-started で見れるわけですが、ここには Intel x86/64 系や Mac はありません。通常の PC で試したい場合は、KVM 版の仮想環境で動くものを使います。

となると、CentOS の Atomic のように複数の Docker を載せて動かすというよりも、Ubuntu mate よりもメモリが少ない環境/ストレージが少ない環境の組み込み用 Linux として動作させる、という形にシフトしたようです。2016年頃の記事を見ると「snappy ubuntu core」としてラズパイ上で docker を動かす記事が多いのですが、先のダウンロード先が Ubuntu core になっていることから内部構成が変わっています。そもそも、snappy と言うコマンドが無くなって、snap というコマンドに変わっています。

image

目的も Docker のコンテナとしてではなく(snap find docker で見つかるので、インストールはできる模様)、小メモリで IoT 機器が扱えるというスタイルみたいですね。

Ubuntu Core をインストール

まずは、Raspberry Pi 3 にインストールします。Raspberry Pi 2 でも CPU が 64ビット版であればインストールできます。

Raspberry Pi 2 or 3 | Developer
https://developer.ubuntu.com/core/get-started/raspberry-pi-2-3

普通の Raspbian と同じように micro SD に焼き込みます。色々な機能は削られているみたいで、8GB 以下でも大丈夫な感じです。限られた処理しか使わないのであれば、安価な 8GB を大量に買って入れ込むということもできそうですね。16GB はまだそこそこ高いので。

ひとつ気を付けなければいけないのは、Ubuntu Core に対して SSH するためには「Ubuntu SSO account」を取得して、そのアカウントでログインする必要があります。どうやら、組み込み機器へのインストールを制御するために Ubuntu SSO を使うらしいのですが、

image

  • Ubuntu Core の初回起動時に Ubuntu SSO のアカウントを入力する必要がある。
    • つまり、モニタとキーボードが必須
    • Ubuntu SSO に繋がるために、外部接続が必須になる。
  • Ubuntu Core に SSH するときにRSA公開鍵/秘密鍵が必要になる。
    • これが結構ハマる。ラズパイの英語フォーラムでもハマったまま解決していなかったり。
    • 公開鍵使って SSH している人には当たり前のことなのか?

という制限があります。

Ubuntu Core を起動して、Ubuntu SSO に登録したメールアドレスを入れると、以下のようなアカウントで Ubuntu Core にアクセスできるようになります。私の場合は moonmile というアカウントを取りました。おそらく、早い者勝ちですね。

image

Ubuntu Core は、この写真で分かるように単純なコンソール環境…と言いますか、コンソール環境ですらないです。外部から SSH で繋げてあれこれややるしかありません。WinSCP は繋げたのでファイルのアップロードなどはできます。

Raspberry Pi からだと。

ssh moonmile@172.16.0.16

のようにして SSH で接続できます。このときに、public/private key が必要になります。

SSH/OpenSSH/Keys – Community Help Wiki
https://help.ubuntu.com/community/SSH/OpenSSH/Keys

を見て、ssh-keygen -t rsa で作ります。ラズパイの場合、デフォルトで .ssh フォルダができているのですが、.ssh/id_rsa のパーミッションを chmod 600 id_ras で変えておく必要があります。ここがデフォルトの 644 のままだと、何故か ssh で繋げるときにエラーになって不明な状態に陥ります。

.ssh/id_rsa.pub の内容を、Ubuntu SSO の SSH Keys に貼り付けて、登録しておきます。初回 SSH するときには、秘密鍵のパスフレーズが必要になるので、忘れないでください。その後は、Ubuntu SSO のパスワードと混乱しそうですが、RSA公開鍵を使ってアクセスをするので、自分の秘密鍵のパスフレーズが必要、という訳です。

そうすると、無事 Raspberry Pi から(通常の Linux でも同じです)Ubuntu Core にログインできます。

image

Tera Term でログインする

では、Windows の環境から Tera Term を使って Ubuntu Core にログインしてみましょう。Raspberry Pi 上の .ssh/id_rsa と .ssh/id_rsa.pub を Windows 上の何処かにコピーします。github などで既に、id_rsa を作っている場合は、それを使ってもよいでしょう。

SSH 認証で、「RSA~鍵を使う」を選択して、id_rsa ファイルを指定します。ユーザー名には、Ubuntu SSO のログイン名を入れ、パスフレーズは、id_rsa のパスフレーズを入れます。ややこしいですね。

image

うまくつながると、↓のようにログインができます。

image

WinSCP でファイル転送する

Windows からファイル転送ができるように WinSCP の設定もしておきます。

転送プロトコルで「SCP」を選択して、ユーザー名に Ubuntu SSO のアカウント名を入れます。

image

設定ボタンをクリックして、SSH → 認証で、秘密鍵を指定します。

image

秘密キーのパスフレーズは、接続時に要求されるので入力します。このパスフレーズは保存できないようです。

image

うまくつながると、下記のようにファイル転送が SSH 経由できるようになります。

image

ホスト名の変更と avahi-daemon のインストール

ubuntu core のホスト名は、初期値が localhost になっているので、これを変更します。ホスト名を「raspicore」にするときは、以下のように実行。

sudo hostnamectl set-hostname raspicore

あと、windows からホスト名 raspicore.local でアクセスできるように avahi-daemon も入れておきます。

snap install avahi

でインストールができます。

インストールしたパッケージは snap list で確認ができます。

image

snap 自体は、Unbutu mata でも使えるので、このあたりは普通に使えるのかなと。snapcraft で自前のパッケージも作れます。

と言う訳で Raspberry Pi 上で ubuntu core が動き出したので、お次は GPIO 廻りをどう弄るのか?を調べていきましょう。

カテゴリー: 開発, RaspberryPi | ラズパイでUbuntu Core を試してみよう(前編) はコメントを受け付けていません

Live2D Viewer でデスクトップマスコットを表示してみよう

スマホでゲームをしていると、Live2D で二次元キャラが動くものがあります。

マギアレコード 魔法少女まどかマギカ外伝
https://play.google.com/store/apps/details?id=com.aniplex.magireco

なんてのは、各キャラが動いていますね。ってことで、この Live2D のデータはゲームに使われるだけじゃなくて、そのまま PC のデスクトップマスコットにできます。

Live2D Viewer
http://www.live2d.com/ja/download

image

元々、ビューアがあるのは知っていたのだけど、そのまま背景を透明にすれば、デスクトップマスコットになるんですね。こんな風に Amazon Prime を見ながら、マスコットが楽しめます…というか、子供には邪魔じゃないかと言われたのですが、まあ、これはこれでいいのです。

image

Liver2D Viewer は Adobe Air で動きます。インストールした後に、モデルデータ(*.moc)をドロップして、「キャンパスウィンドウの表示」のチェックを外します。余分な腕のデータとかは「パーツの表示」で適宜外します。

image

そのままだと、マウスを追うだけで詰まらないので「プロジェクト」→「サンプル」→「モーション:アイドリング」をいれると、少し揺れるような感じのモーションが付きます。たぶん、特定のキーか何らかのイベントでモーションを動かすこともできるでしょう。そのあたりは、少し探してみようかなと。FaceRig で連携するのもそれかと。

Live2D は当然 Android でも動作するので、真面目にポーティングすると Xamarin.Android で使えるようになるかなと。たぶん *.jar のバインディングあたりで。WebView ではなんとか動く模様 https://docs.com/user469647/7372/xamarin-de-live2d

カテゴリー: 開発 | Live2D Viewer でデスクトップマスコットを表示してみよう はコメントを受け付けていません

続 .NET Standard とは何か?

.NET Standard とは何か? | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/8218

の続編です。1年前位に、.NET Standard が発表されて、その後どうなるのかと思いつつ、.NET Core 2.0 のプレビュー版が出てきて、最近 Rasbian 上で .NET Core 2.0 を動かしてみて分かったことをいくつかまとめます。

PLCは最小公倍数で、.NET Standardは最大公約数だ

せまっ苦しいPCL(Potable Class Library)では、なかなか大変なことになっているので、いっそのこと、.NET Framework、.NET Core、UWP な .NET Runtime を大まかに含めてしまったのが「.NET Standard」であるというのは、概ね合っています。完全に包括しているわけではない(たまに、.NET Frameworkで含まれているクラスが .NET Standard に含まれていないので)のですが、概ね含まれています。

大まかに.NETの動作環境を考えると、

  • .NET Framework は、Windows の Desktop で動く
  • .NET Core は、Linux で動く
  • .NET の Runtime は、UWP(Windowsストア)な環境で動く
  • .NET の Runtime は、Windows Mobile で動く

更に、

  • Mono が Linux 上で動く。
  • Mono が、macOS 上で動く。
  • Xamarin.MAc が macOS 上で動く。
  • Xamarin.Android(MonoDroid)が、Android 上で動く。
  • Xamarin.iOS(MonoTouch)が、iOS 上で動く。

という現状があります。Xmarinな環境はベースがMonoなので、Android/iOSに限らずLinuxやmacOSでも動作する点で範囲が広いわけですが、いざ、Winodwsも含めて共通ライブラリを作るとPLC Hellに陥ります。結局のところ、最小公倍数なクラス群をまとめる形になるので、せまっ苦しくなります。

実は、.NET Standardが最大公約数的とはいえ、Windowsのデスクトップ環境(WindowsフォームやWPFとか)は入っていないし、逆にXamarin.Macで使われるようなCocoaな機能は入っていません。なので、主に非グラフィックな機能を集めることになります。一方で、Xamarin.Formsが、iOS/Android/UWPをカバーし始め、Windows(.NET Framework)のWPF/UWPと括弧付きですがSilverlightを含めて、「XAML Standard」という発想が出てくるのは無理からぬことです。が、まあそれはさておき。

じゃあ、なんとなくですが、PCLよりも広い感じで使える.NET Standardということで、どんな風に使うのか?というのを具体的に考えます。

Rasbianで.NET Core 2.0を動かしてみる

.NET Core 2.0 から実行環境にRaspberry PiのARMv7/8な環境がサポートされるようなりました。もともと、RasbianではMonoが動くのですから、.NET Coreが動かないというのも変な形なのですが、まあ動かなかった訳です。で、現時点ではビルドはできないけれども(つまりコンパイラが動かないとうこと…なのか)、実行だけはできる環境が整いました。

さて、ラズパイ上でGPIOを制御する(Lチカする)ことなるので、ハードウェアを触るとここは RaspberryPi.Net を使いました(Raspberry#IOのほうが有名なのですが、これがどうも動かないもので)。さらに、ハードウェアそのものを動かすために bcm2835 といチップを操作する C言語のライブラリを扱います。つまり、次の3つの部分に分かれるわけです。

  • メインのプログラム(C#)
  • RaspberryPi.Netクラスライブラリ(C#)
  • bcm2835ライブラリ(C言語)

bcm2835ライブラリは、C言語で書かれているのでRaspberry Piべったりの制御ということが分かるので、当然ながら他のマシンとの互換性はありません。
メインのプログラムとRaspberryPi.NetクラスライブラリはC#で書かれているので、先に書いたような色々な.NETな実行環境で動かすことが可能です(実際のところ、GPIO制御が入るので、ラズパイ上でしか動かないのですが)。

最初に考えるのは、ラズパイ上なので、この2つをMonoでビルドすることです。Monoはラズパイ上で動いているので、そのまま動きますよね。

次に考えたのは、せっかく .NET Core 2.0 がラズパイ上で動くようになったので、メインのプログラムとRaspberryPi.Netクラスライブラリの両方を .NET Core 2.0 でビルドし直すということです。
この環境で動かすこともできるようになりました。

ふと、じゃあ .NET Standard 2.0(.NET Core 2.0をサポートしているので)を含めるとどうなるのだろうか?ということを考えてみると、

  • メインのプログラム(C#) → .NET Core 2.0
  • RaspberryPi.Netクラスライブラリ(C#)→ .NET Standard 2.0
  • bcm2835ライブラリ(C言語)

という組み合わせが考えられます。
RaspberryPi.Netクラスライブラリは汎用的に(その他のAndroidやiOSなど…GPIOを使えないけど)使うことを考えて .NET Standard でビルドをしておき、そのライブラリをメインの .NET Core で作った実行ファイル(Rasbian上なので拡張子はexeではありませんが)で利用します。

実際、この組み合わせで動作を確認しています。
これで、なんとなくわかると思いますが、

  • 最終的な実行部分(メインプログラム)は、各OSに即した.NET環境を使う
  • 中間のライブラリは一括して.NET Standardで作る。
  • 個別の制御はC言語や即した.NET環境を使う

という作り分けになるでしょう。

中間のライブラリを片っ端から.NET Standardにする

発想的には、こんな風に.NET Standardがサンドイッチ状態になります。

実際にはフロントが要らない場合もあるし、その環境でしか動かさないのであれば無理に.NET Standarでビルドをする必要はないのですが、中間の部分を.NET Standardでビルドをしておいて、NuGetのようにダウンロードできるようにしておけば、全ての.NET実行環境で使えることになるでしょう。

.NET Standardなライブラリの作り方

Visual Studio 2017 Update3プレビュー版では、.NET Standard 2.0 が使えます。プロジェクトのテンプレートを見ると非常に簡単で「クラスライブラリ」しかありません。

理由は簡単です。先に書いた通り、フロントUIでもないし、個別のハードウェア寄りでもない中間層を受け持つので「クラスライブラリ」だけで十分なのです。

既存のプロジェクトを.NET Standardに変えたい場合は、*.csporj を開いて、以下のように変更するだけです。

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>
</Project>

コンパイル対象のファイルは、フォルダー内のC#の全てのファイルが自動的に対象になります。少々乱暴な気がしますが、プロジェクトの設定が非常に簡単になっています。

そんな訳で、これを踏まえて Rasbian + .NET Core/Standard の組み合わせを後日考察。

カテゴリー: 開発 | 続 .NET Standard とは何か? はコメントを受け付けていません

俺のラズパイ2で.NET Core 2.0なF#が動いたよ

俺のラズパイ2で.NET Core 2.0が動くまで | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/8732

では C# が動いたので、今度は F# で試してみましょう。
手順は、C# の時と一緒で、

  1. Raspberry Pi2/3でRasbianを用意する。
  2. PC上で、.NET Coreのプログラムを作る。
  3. PCからRaspberry Piにデプロイする(アセンブリをコピーする)

すれば ok です。コードが F# なので、デプロイするアセンブリに「FSharp.Core.dll」が含まれていれば、ああ F# で動いているね、ということになります。

F#なコンソールアプリを作る

プログラム言語を -lang で指定して、コンソールアプリを作ります。

dotnet new console -n hellofs -lang F#

できあがったコードは、こんな感じ。皆さん、見慣れた F# なコードですよね :)

// Learn more about F# at http://fsharp.org
open System
[<EntryPoint>]
let main argv =
    printfn "Hello World from F#!"
    0 // return an integer exit code

[/code]

ビルドする

ビルドは、C# と同じです。ARM 用に -r linux-arm のスイッチを付けて publish します。

dotnet build
dotnet publish -r linux-arm

転送する

ビルドができたら、WinSCP を使って publish フォルダ―を転送します。

この部分は、適当なバッチを作っておきたいところですね。

実行フラグを付ける

Tera term で Raspbian に接続して、chmod +x して実行権限を付けます。

chmod +x hellofs
./hellfs

いよいよ asp.net mvc code + F# ができるのか?

と思いきや、ビルド時にエラーがでています。

どうも Razor のコード出力あたりでエラーになっていますね。この部分、通常の ubuntu でやってもエラーになるので、普通に不具合じゃないかなと。プレビュー版だし。
確か、1.1 の時は mvc にしても linux 上(Raspberry Pi上じゃなくて、x64のUnbuntu上)
で動いたはずなので、いずれ直ると思います。

dotnet new webapi のほうは、linux-arm でも動くので、これは後で試します。次は、せっかくなので GPIO を使って .net core 2.0 + Lチカの巻へ。

 

カテゴリー: 開発, F#, RaspberryPi | 俺のラズパイ2で.NET Core 2.0なF#が動いたよ はコメントを受け付けていません

俺のラズパイ2で.NET Core 2.0が動くまで

俺のラズパイ3で.NET CoreなF#が動かないわけがない | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/8028

の記事が去年の8月末で、あれからほぼ1年が経ちました。経っているうちに、.NET Coreが2.0のプレビュー版になって、.NET Coreが動くようになりましたよ、という話。

core/roadmap.md at master ・ dotnet/core
https://github.com/dotnet/core/blob/master/roadmap.md#technology-roadmaps
Running a .NET Core 2 app on Raspbian Jessie, and deploying to the Pi with Cake | Jeremy Lindsay
https://jeremylindsayni.wordpress.com/2017/07/23/running-a-net-core-2-app-on-raspbian-jessie-and-deploying-to-the-pi-with-cake/
core/RaspberryPiInstructions.md at master ・ dotnet/core
https://github.com/dotnet/core/blob/master/samples/RaspberryPiInstructions.md
<b>C#</b>RaspberryPi 3に.NET Coreを入れてHello World! – Qiita
http://qiita.com/logikuma/items/de8c987dc2308a96256d

.NET Core 1.0 ではいつの間にか消えてしまった Raspberry Pi のサポートですが、.NET 2.0 になって復活していました。

動作環境

  • Raspberry Pi2と3の Rasbian/Ubuntu の環境で動く。
  • Raspberry Pi Zero では動かない。
  • Raspberry Pi 上ではビルドができない。
  • なので、WindowsやLinux上でビルドして、Raspberry Pi へデプロイする。

つまり、実行環境としての.NET Core関連のアセンブリをラズパイへ入れ込めば動作する。ビルドする場合は、別のPC/Linux/Macが必要、という訳。Zeroで動かないのは、.NET Coreの動作対象が armv7とarmv8 となっているためで、zero が使っている arm32 は対象になっていない、ということです。
となると、armv7/8 の cpu を使っている arm 関係の組み込みボードでもいけそうでは?という予想が立つので、おそらく nano pi あたりでも動くはずです。

Rasbian Pi上で動かす

現状では、.NET Coreのランタイムが

  • Rasbian(Raspberry Pi標準のOS)
  • Ubuntu
  • Windows IoT Core

上で動きます。Rasbianで動かすときは、Scratchとの連携とかnode.jsの代わりとか色々できそうです。Ubuntuは低電力の常時Webサーバーというのもありで、ちょっとしたサーバー機として使えます。Windows IoT Coreの場合は、モニタ絡みで先行きはUWP on .NET Standardな環境かなと思ったり。
自前ビルド環境がないのが辛いところですが(Rasbian/Ubuntuという開発環境なのに)、別途、.NET Core対応なコンパイラとかスクリプトを作れば、それはRaspberry Pi上でもするっと動くようになるのかなと思ったり、まあ、そういうときはmonoを使えばいい訳ですが。

さて、.NET Coreのランタイムかつアセンブリ(各種のDLLファイル)は、実行環境に何かのインストールをする必要はありません。すべて、ビルドするときのアセンブリで完結しています。ということは、Raspberry Pi 上でセルフビルドができないのですから、そもそも、何らかの.NET関係のアセンブリをインストールしておく必要はありません。

ということで、手順は簡単で、

  1. Raspberry Pi2/3でRasbianを用意する。
  2. PC上で、.NET Coreのプログラムを作る。
  3. PCからRaspberry Piにデプロイする(アセンブリをコピーする)

だけで十分です。

PC上で hello フォルダーを作成して dotnet new console します。

mkdir hello
cd hello
dotnet new console

あるいは

dotnet new console -n hello

Raspberry Pi のデプロイするために dotnet publish -r linux-arm します。

dotent build
dotnet publish -r linx-arm

ここで一度 build していますが、Raspberry Pi に転送するだけならば、publish だけで十分かもしれません。

hello/bin/Debug/netcoreapp2.0/linux-arm/publish の中身に hello を含む .NET Core 2.0 用のアセンブリがごっそり作られます。

publish フォルダの中身を、WinSCP などで Raspberry Pi に転送します。

Tera Term などで Raspberry Pi に繋ぎ、転送した publish フォルダにある hello だけに実行権限を付けます。

mv publish hello
chmod +x hello/hello

アセンブリに実行権限を付ける訳です。

後は、普通に hello を動かせば動作できます。

./hello

※ラズパイ上の dotnet コマンドはバージョンしか出さないので、おそらく dotnet-runtime-latest-linux-arm.tar.gz をダウンロード&展開する必要はないハズです。
※実行権限を chmode 775 -R すると全てのアセンブリに設定してしまうので、ピンポイントで chmod +x しています。

追記 2017/07/31

arm 上の dotnet コマンドですが、pc/linux にあるような dotnet run コマンドがありません…が、アセンブリを実行するために、dotnet ./hello.dll ってのができますね。なるほど。以下な感じで最新版を取ってきて、展開して使えます。

wget https://dotnetcli.blob.core.windows.net/dotnet/Runtime/release/2.0.0/dotnet-runtime-latest-linux-arm.tar.gz
mkdir /opt/dotnet
tar -xvf dotnet-runtime-latest-linux-arm.tar.gz -C /opt/dotnet
ln -s /opt/dotnet/dotnet /usr/local/bin/dotnet

のように /usr/local/bin/dotnet で動作できるようにしておいて

dotnet ./hello.dll

で実行します。PC/linux のほうにもこの機能はある。

このあたり、英語のブログ記事も錯綜している感じがするので、元ネタの

core/RaspberryPiInstructions.md at master ・ dotnet/core
https://github.com/dotnet/core/blob/master/samples/RaspberryPiInstructions.md

を参照するとよいでしょう。

Ubuntu上で動かす

Ubuntu 上で動かすときも基本は Rasbian と変わりません。
ただし、素の Ubuntu の場合、開発的なライブラリが少し足りないので apt-get で追加しておきます。

core/prereqs.md at master ・ dotnet/core
https://github.com/dotnet/core/blob/master/Documentation/prereqs.md

にあるライブラリを以下で追加します。

sudo apt-get install libunwind8 libunwind8-dev gettext libicu-dev liblttng-ust-dev libcurl4-openssl-dev libssl-dev uuid-dev unzip

これで、さっき dotnet publish -r linux-arm で作ったアセンブリを ubuntu のほうにコピーすると同じように動作します。

IoTのテンプレートを使う。

ここでは console のテンプレートを使った訳ですが、実は dotnet new –list すると、console 以外のテンプレートも使えることが分かります。

aps.net mvc の場合は dotnet new mvc とかのアレのやつですね。
ここに coreiot というテンプレートがあって、

dotnet new -i RaspberryPi.Template::*

とするとインストールができます(既に .net core 2.0 の dotnet コマンドに付いるかもしれません)。
このテンプレートを使って、

dotnet new coreiot -n helloiot

とした後で、vscode を開くと build.cake にビルドからデプロイまでを含めたCake(C# の makefile)を作ってくれます。

ビルド用の ps スクリプトを,

Invoke-WebRequest http://cakebuild.net/download/bootstrapper/windows -OutFile build.ps1

でダウンロードして動かすと、pscp まで使ったデプロイまで一気にやってくれるはずなんですけどね…どうも PscpSettings の設定がうまくできていないらしく、うまく動いていません。

Cake – Home
http://cakebuild.net/

後でここは調べておきましょう。

そんな訳で

セルフビルドはできませんが、これで Rasberry Pi 2/3 上で .NET Core 2.0 が動作することが確認できました。これで何がやりたいかというと、F#でWeb APIが作りたいわけで、mono で作ってもよいのですが、どうせならば .net core + asp.net mvc core の組み合わせでやりたいのです。なんせ、PCよりも断然低電力なので常時立ち上げておいてもよいし、AzureやAWSよりもネットワーク的に近い位置に作れるから、これはこれで使い勝手がいいかなと。
と言う訳で、F#でのお試しは引き続き。

カテゴリー: 開発, RaspberryPi | 俺のラズパイ2で.NET Core 2.0が動くまで はコメントを受け付けていません

iOSから自己署名証明書で接続することができないので、Let’s Encryptの証明書を使う

iPhone(iOS 10以降)でUIWebViewを使って https 通信をするときに自己署名証明書を使っている場合、どうやっても接続ができなかった。

現状

色々調べたのだが、

ServicePointManager.ServerCertificateValidationCallback を使って、SSL証明書のエラーを回避する → そもそもここが呼ばれない。
Info.plist に NSExceptionDomainsとNSExceptionAllowsInsecureHTTPLoadsを追加して例外扱いにする → NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813) のエラーになる。
非推奨の allowsAnyHTTPSCertificateForHost をオーバーライドして、ture を返す → そもそも、ここが呼ばれない。

自己証明書を使用しているサーバにHttpClientで接続 言語: C#
https://code.msdn.microsoft.com/windowsdesktop/HttpClient-fc321142
xcodeにてApp Transport Security(ATS)除外のドメインを複数設定する – Qiita
http://qiita.com/BurialMound/items/87b8c33e2c7ad767c9dc
SwiftでHTTPS通信時に自己認証証明書の警告によるエラーを無視させる – Steel Dragon 14106
http://raimon49.github.io/2015/05/27/ignore-verify-self-signed-certificate.html

ちなみに、WebView じゃなくて HttpClient とか NSURLSesstion の場合はうまく動いたりするので、WebView 固有の問題かもしれない。

最終的には、Xamarin.Forms を使うわけだが、その前段階として Xamarin.iOS と Swift で調査をしている。
「動いた」というページもあるのだが、

  • 「動いた」という記事自体が少し古い
  • 実機で動いても iOS シミュレータはだめっぽい、という記事がある。
  • Info.plist に IP を書いた場合は動かない。ドメイン名が必要。

とあるので、ここは最初に戻って、自己署名証明書じゃない正式な証明書を使うことを検討する。

ちなみに自己署名証明書は、↓を参考にすればok。

自己署名証明書の作成方法 – Qiita
http://qiita.com/akito1986/items/8eb41f5a43bb9421ae79

検証環境自体が、

  • HTTPS のサーバーは、外から見えない(検証機なので)
  • HTTPS のサーバーはドメインを持っていない(内部IPだけなので)
  • iOSシミュレーターでも確認したい。

という所謂、検証環境を想定しいている。

証明書を作る

証明書を作るのには、Let’s Encrypt が無償で使える。

Let’s Encrypt 総合ポータル
https://letsencrypt.jp/

適当なサーバーやクライアントの環境(Linuxかmacに限る)があれば、certbot コマンドで正式な証明書を作ることができる。この証明書は、そのまま外部のWebサーバーに載せることもできる、今回のような検証機に載せることもできる。

Let’s Encryptを使うときの制限は、

  • 証明書を発行するときに、ドメインを持ち外部公開されているサーバーが必要
  • 証明書の期限は3か月に制限されている

となる。外部に公開されているサーバーに証明書をおき、certbot を定期的に動作させて3か月の証明書期間を更新する仕組みなっている。
ただし、手動(manual)で証明書を取得する仕組みも用意されているので、必ずしも自動更新しなければいけない訳ではない。

問題は、「外部公開されているドメイン」が必要なところで、今回のように社内に検証機があって外部から見えないようになっている、グローバルIPを持っていない、ような場合には使えるのか?という疑問がある。なので、いきおい自己署名書になるわけだが、大丈夫、少しトリックを使うと使えるようになる。

実は、

  • Let’s Encrypt から見えるサーバー
  • 実際に証明書を使うサーバー

が同一である必要はない。Let’s Encrypt は、証明書を発行するときにレスポンスファイルを外部サーバーに置くことになるが、これは実際に証明書を使うサーバーとは異なっていても良いのだ。つまり証明書の servername だけ合わせておけばよいということになる。

証明書を作成する手順

Let’s EncryptでManual発行してみました | ぽちゃ猫.com
https://www.pochaneko.com/ietsencrypt-man/

を参考にしながら、certbot をインストールする。今回は mac 上でやっている。

1.仮アクセスできる外部サーバーを用意する。捨てドメイン名になるので ssl.moonmile.net のようにサブドメインを使うのが吉
2.sudo certbot certonly –manual で起動する
3.証明書に書き込むドメイン ssl.moonmile.net など を入れる

4.アクセスチェックのファイルが表示されるので、このファイルを先の外部サーバー ssl.moonmile.net に置く。
5.無事、証明書ができると /etc/letsencrypt/live に置かれる。

-rw-r--r--  1 root  wheel  543  7 23 18:38 README
lrwxr-xr-x  1 root  wheel   36  7 23 18:38 cert.pem -&amp;amp;gt; ../../archive/moonmile.net/cert1.pem
lrwxr-xr-x  1 root  wheel   37  7 23 18:38 chain.pem -&amp;amp;gt; ../../archive/moonmile.net/chain1.pem
lrwxr-xr-x  1 root  wheel   41  7 23 18:38 fullchain.pem -&amp;amp;gt; ../../archive/moonmile.net/fullchain1.pem
lrwxr-xr-x  1 root  wheel   39  7 23 18:38 privkey.pem -&amp;amp;gt; ../../archive/moonmile.net/privkey1.pem

6.シンボリックリンクされているので、本体をコピーする。

privkey.pem が秘密キー
cert.pem が証明書

7.検証機の WEB サーバー(今回は xamp)の C:/xampp/apache/conf/ に証明書を配置する

ssl.key に privkey.pem
ssl.crt に cert.pem

8.config/extra/httpd-ssl.conf に以下を追加する。ssl を有効にすることを忘れずに。

<VirtualHost *:443>
    DocumentRoot "C:/xampp/htdocs"
    ServerName ssl.moonmile.net
    SSLEngine on
    SSLCertificateFile "conf/ssl.crt/cert.pem"
    SSLCertificateKeyFile "conf/ssl.key/privkey.pem"
</VirtualHost>

これで HTTPS サーバーの準備が完了

9.iOSシミュレーターを動かす mac の /etc/hosts に ssl.moonmile.net を追加する。

172.16.0.12 ssl.moonmile.net

な風に追加する。「172.16.0.12」は検証サーバー機のIPアドレス。
iOSシミュレーターは、ホストの mac の /etc/hosts を共有しているようで、これで度命名「ssl.moonmile.net」でアクセスできます。

実行してみる

xcode で UIWebView を使って表示させてみると、

@IBAction func clickConnet(_ sender: Any) {
    let url = URL(string: "https://ssl.moonmile.net/")
    let req = URLRequest(url: url!)
    web.loadRequest(req)
}

こんな風に、https 接続して xamp トップページが表示される。当然、「nscurl –ats-diagnostics 」もさっくりと PASS する。

これは、info.plist の変更なしでいける…のがちょっと不思議なんだけど、何も設定しないから、そのままで審査が通るってことでしょう。

おまけ

ちなみに、UIWebView 限定なのであれこれやったが、SFSafariViewController を使うと自己署名証明書でもスルーしてくれる。通常のブラウザと同じように、自己署名だからという警告が1回だけ出るが、その後はキャッシュされるらしく警告がでなくなる。

ATS対策 外部ページの表示にはSFSafariViewController – Qiita
http://qiita.com/bird_tomita/items/2139d2b95c35a4e7d20f

なんか、適当な https を表示するだけだったら、こっちのほうが手軽でよいかも、ということで。

カテゴリー: 開発, Xamarin, iOS | iOSから自己署名証明書で接続することができないので、Let’s Encryptの証明書を使う はコメントを受け付けていません

Scratch から Arduino を操作しよう、というわけで NetScrattino を作る

Scratch を使って Arduino を動かそうとすると http://scratchx.org/ を使うのがいいのだろうけど、どうも自分の環境ではうまく動かない。オフラインの Scratch 2.0 の場合、ファイルメニューをシフトを押しながら開くと「実験的なHTTP拡張を取り込み」というのが出て、適当なHTTPサーバーを作ると繋がるらしいことが分かった。

サーバーを作るのが手間といえば手間なんだけど(ScratchXの場合は、Chrome拡張をインストールすると、Chrome側にHTTPサーバーを立てる仕組みになっている)、一度作っておけば、Arduino 以外に接続するのも楽ではないかなと思って、作ってみることにする。.NET で作れば HttpListener があるので、何とかなるのではないかな、と。

Scrattino 2 | Yengawa Systems
http://www.yengawa.com/scrattino2
Let’s Make With Arduino!
https://lets.makewitharduino.com/sample/scratch/

Scrattino2 のほうは、ArduinoにFirmataを入れるんだけど、HTTPサーバーはMac版しかない。Scratio のほうは、独自プロトコルにしてあって中身は Python で書かれている。
ざっと、Scratio で Scratch の拡張ブロックの操作を確認したところで、まあいけそうなことが分かったので Firmata への接続を作ることにした。

シリアル通信で Firmata に接続する

まずは、NetScrattino から Arduino にシリアル通信する。

シリアル通信は双方向に通信ができるので、NetScrattinoからコマンドを送信すると同時に、定期的に Arduino のほうからアナログピンの状態を送信してくれる。これを保持しておく。

protocol/protocol.md at master ・ firmata/protocol
https://github.com/firmata/protocol/blob/master/protocol.md
firmataプロトコル覚え書き
https://gist.github.com/hiroeorz/7868628

あたりを見ながら、ひとまずデジタルピンとアナログピンの読み書き、モードの設定、レポートの設定だけを送れるようにしておく。

// two byte digital data format, second nibble of byte 0 gives the port number (e.g. 0x92 is the third port, port 2)
// 0  digital data, 0x90-0x9F, (MIDI NoteOn, but different data format)
// 1  digital pins 0-6 bitmask
// 2  digital pin 7 bitmask 
member this.digitalWrite(pin,value) =
    let portNumber = (pin >>> 3) &&& 0xFF
    digitalInputData.[portNumber] <-
        if value = 0 then
            digitalInputData.[portNumber] &&& ~~~(1 <<< (pin &&& 0x07))
        else
            digitalInputData.[portNumber] ||| (1 <<< (pin &&& 0x07)) 
    let message = [|
        DIGITAL_MESSAGE ||| byte(portNumber) 
        byte(digitalInputData.[portNumber] &&& 0x7F)
        byte(digitalInputData.[portNumber] >>> 7)
    |]
    _socket.Write(message, 0, message.Length);

あれこれ面倒なので、F# で書いたのであった。
Arduino から非同期で送ってくるデータは、DataReceived で受け取る。

_socket.DataReceived.Add( fun (e) -> 
    while _socket.BytesToRead > 0 do
        let head = _socket.ReadByte() |> byte
        match head with
        // analog 14-bit data format
        // 0  analog pin, 0xE0-0xEF, (MIDI Pitch Wheel)
        // 1  analog least significant 7 bits
        // 2  analog most significant 7 bits
        | h when ANALOG_MESSAGE <= h && h <= ANALOG_MESSAGE + 15uy -> 
            let pin = int(h - ANALOG_MESSAGE)
            let lsb = _socket.ReadByte()
            let msb = _socket.ReadByte()
            let data = (msb <<< 7) ||| lsb
            analogInputData.[pin] <- data
        // two byte digital data format, second nibble of byte 0 gives the port number (e.g. 0x92 is the third port, port 2)
        // 0  digital data, 0x90-0x9F, (MIDI NoteOn, but different data format)
        // 1  digital pins 0-6 bitmask
        // 2  digital pin 7 bitmask 
        | h when DIGITAL_MESSAGE <= h && h <= DIGITAL_MESSAGE + 15uy -> 
            let pin = int(h - DIGITAL_MESSAGE)
            let lsb = _socket.ReadByte()
            let msb = _socket.ReadByte()
            let data = (msb <<< 7) ||| lsb
            digitalInputData.[pin] <- data
        | _ -> 
            // read off
            let d = _socket.ReadExisting()
            ()
)

HTTPサーバーを作って Scratch に応答する

Scratch の拡張ブロックは、JSON形式で書くことができて、こんな風になっている。

{
  "extensionName": "Net Scrattino",
  "extensionPort": 5410,
  "url": "https://github.com/yokobond/scrattino2",
  "blockSpecs": [
    [" ", "INPUT %m.digitalPinNames mode %m.inputPinModes", "setMode", "D2", "PULLUP"],
    [" ", "OUTPUT %m.digitalPinNames value %m.digitalValues", "digitalWrite", "D2", 0],
    [" ", "PWM %m.digitalPinNames value %d.pwmValues", "analogWrite", "D2", 0],
    [" ", "SERVO %m.digitalPinNames degree %d.servoValues", "servoWrite", "D2", 0],
    [" ", "Set Pin %m.digitalPinNames to %d.digitalPinModes mode", "setPinMode", "D2", "OUTPUT"],
    [" ", "LED %m.digitalPinNames is %m.OnOffValues", "digitalWrite", "D2", "ON"],
    ["-"],
    ["r", "A0", "a0"],
    ["r", "A1", "a1"],
    ["r", "A2", "a2"],
    ["r", "A3", "a3"],
    ["r", "A4", "a4"],
    ["r", "A5", "a5"],
    ["-"],
//    ["R", "value of %m.digitalPinNames", "pinValue", "D2"],
    ["r", "D2", "d2"],
    ["r", "D3", "d3"],
    ["r", "D4", "d4"],
    ["r", "D5", "d5"],
    ["r", "D6", "d6"],
    ["r", "D7", "d7"],
    ["r", "D8", "d8"],
    ["r", "D9", "d9"],
    ["r", "D10", "d10"],
    ["r", "D11", "d11"],
    ["r", "D12", "d12"],
    ["r", "D13", "d13"]
  ],
  "menus": {
    "digitalPinNames": ["D2", "D3", "D4", "D5", "D6", "D7", "D8", "D9", "D10", "D11", "D12", "D13"],
    "analogPinNames": ["A0", "A1", "A2", "A3", "A4", "A5"],
    "digitalPinModes": ["INPUT", "INPUT_PULLUP", "OUTPUT", "PWM", "SERVO"],
    "inputPinModes": ["PULLUP", "PULLDOWN"],
    "digitalValues": [0, 1],
    "OnOffValues": ["ON", "OFF"],
    "pwmValues": [0, 64, 128, 192, 255],
    "servoValues": [0, 45, 90, 135, 180],
    "analogValues": [0, 256, 512, 768, 1023]
  }
}

blockSpecs にあるのがブロックの定義で、これを web api な形で呼び出す。
仕様は、https://wiki.scratch.mit.edu/w/images/ExtensionsDoc.HTTP-9-11.pdf に書かれている。ID を使って非同期にデータを送る方法は面倒なんだが、通常はポーリング(/poll)を送って、データを返すパターンが多いので、それだけならばそんなに難しくはない。
で、これも F# で実装してみる。

let mutable arduino = new FirmataNET.Arduino()

// Scratchから受信するためのHTTPサーバー
let Server( port ) =
    let listener = new System.Net.HttpListener()
    listener.Prefixes.Add("http://127.0.0.1:"+(port |> string)+"/" )
    listener.Start()
    while true do
        let context = listener.GetContext()
        let res = context.Response
        let mutable data = ""
        let path = context.Request.Url.PathAndQuery
        match path with 
            | "/poll" -> 
                for i=0 to 5 do 
                    data <- data + String.Format("a{0} {1}\n", i, arduino.analogRead(i))
                for i=2 to 13 do 
                    data <- data + String.Format("d{0} {1}\n", i, arduino.digitalRead(i))
                // デバッグ出力
                let mutable debug = ""
                for i=0 to 5 do 
                    debug <- debug + String.Format("a{0} {1} ", i, arduino.analogRead(i))
                debug <- debug + "\n"
                for i=2 to 13 do 
                    debug <- debug + String.Format("d{0} {1} ", i, arduino.digitalRead(i))
                debug <- debug + "\n"
                // printfn "%s" path
                // printfn "%s" debug
            | "/reset_all" ->
                printfn "/reset_all"
                arduino.Reset()
                data <- "ok"
            | _ ->
                let pa = path.Split([|'/'|])
                match pa.[1] with   
                | "digitalWrite" ->
                    let pin = pa.[2].Substring(1) |> int
                    let value = 
                        match pa.[3].ToUpper() with
                        | "ON" -> 1
                        | "OFF" -> 0
                        | _ -> pa.[3] |> int
                    arduino.digitalWrite( pin, value )
                | "analogWrite" ->
                    let pin = pa.[2].Substring(1) |> int
                    let value = pa.[3] |> int
                    arduino.pinMode( pin, 0x03 )    // PWM
                    arduino.analogWrite( pin, value )
                | "servoWrite" ->
                    let pin = pa.[2].Substring(1) |> int
                    let value = pa.[3] |> int
                    arduino.pinMode( pin, 0x04 )    // SERVO
                    arduino.analogWrite( pin, value )
                | "setMode" ->
                    let pin = pa.[2].Substring(1) |> int
                    let value = if pa.[3] = "PULLUP" then 0x0B else 0x00
                    arduino.pinMode( pin, value )
                | "setPinMode" ->
                    let pin = pa.[2].Substring(1) |> int
                    let value = 
                        match pa.[3] with
                        | "INPUT" -> 0
                        | "OUTPUT" -> 1
                        | "PWM" -> 3
                        | "SERVO" -> 4
                        | "INPUT_PULLUP" -> 11
                        | _ -> 1
                    arduino.pinMode( pin, value )
                | _ ->
                    data <- ""
                printfn "%s" path
        res.StatusCode <- 200
        let sw = new System.IO.StreamWriter( res.OutputStream )
        sw.Write( data )
        sw.Close()
    ()

Scratch で拡張ブロックを作ってみる

先に作った JSON を Scratch 2.0 に読み込ませると、自前で作ったブロックが使えるようになる。

旗をクリックしたときとか、スペースキーを押されたとき、などのイベントのブロックがあるが、テストをするときはブロック自体をダブルクリックすれば実行されるので、プロトタイプを作るときには結構便利。mBlock の場合だと、あらかじめ Arduino にデプロイしてしまうので、変更するに書き込まないといけないし。まあ、Firmata 自体がプロトタイプを作るためのものでもあるので、用途的にはちょうどよいかと思う。

簡易プロキシにUIを付ける

最初は、コマンドラインだけでやっていたのだが、Firmata を直接扱えたほうが便利なので、簡易プロキシ(NetScrattino)にUIを付けてみる。

これは WPF で作って、内部的に MVVM パターンになっているので、この解説はまた後で。

Scratchと連携させる

せっかくの Scratch なので、Arduino を操作するだけじゃなくて猫のほうも操作できるようにしておく。

これは、Lチカをしながら猫が走るパターン。LEDをマウスでクリックすると、Arduino上のLEDが光ると同時に絵のLEDも光る。

ざっと、簡単なものとして、

  • LEDの点滅
  • PWMでLEDの点灯
  • サーボを動かす
  • ポテンショメーター(回転とかスライダーとか)でアナログピンで読み取る

なところまでできた。後は、順次

スクラッチーノでScratchとArduinoをつなぐ – MeiDe Digital Craft 2016
https://sites.google.com/site/meidedigitalcraft2016/knowhow/scrattino-usage

にある実習を動かすようなプログラムが組めればよいかな。

コード

NetScrattino のコードはこちら

moonmile/NetScrattino: Simple Server to connect from Scratch to Arduino
https://github.com/moonmile/NetScrattino

これから

ScratchX
http://scratchx.org/#extensions

の拡張を見ていくと Kinect とか Leapmotion とかもある。環境が悪いのかよくわからにけど、うちの PC では ScratchX が動かないので何とも言えないのだけど、どうやら、COM 制限のような気がする。このあたりは、別の PC や Mac で試してみよう。

ローカルで実験する場合は、HTTP プロキシを作ったほうが応用範囲が広そうなので(.NETで作れるし)、カメラでの撮影を Scratch 側で制御するとか、物体認識を Scratch に持って来るというのもできそうな感じはする。

カテゴリー: 開発, F#, Scratch | Scratch から Arduino を操作しよう、というわけで NetScrattino を作る はコメントを受け付けていません

ちょっと雑だが、C# で JsonProvider もどきを作る

F# には TypeProvider というのがあって、動的にクラスを作り、それを F# のコードで扱えるものです。

F# Data: JSON 型プロバイダー
https://fsharp.github.io/FSharp.Data/ja/library/JsonProvider.html

でもって、C# には TypeProvider がないんので F# が羨ましかったり、いやそう言うなら F# で組めばいい訳ですが。以前、F# の XAML の TypeProvider を作ったときに、そのまま Xamarin の PCL には持って行けなくて諦めた覚えがあるんですが。今だともうちょっと工夫できるかも、ってことで、C# で TypeBuilder を使ってみます。

雑に JsonProvider を作る

JSON の文字列を渡して、それをプロパティに持つクラスを作ります。実は、Newtonsoft.Json は dynamic を持っているので、あまり意味はない…というか、結論から言えば T4 とか CodeDOM を使ったほうが早いのでは?という感じはします。

class Program
{
    static void Main(string[] args)
    {
        var json = @"{ name: 'tomoaki', num: 101 }";
        var jp = new JsonProvider(json);
        var t = jp.Make("SampleJson");

        var o = new SampleJson();
        o.name = "aaaa";
        o.num = "100";
    }
}
public class JsonProvider
{
    JObject root;
    Dictionary<string, string> dic = new Dictionary<string, string>();

    public JsonProvider( string json )
    {
        root = JObject.Parse(json);
        var cur = root.GetEnumerator();
        while ( cur.MoveNext() )
        {
            var it = cur.Current;
            Debug.WriteLine("{0} {1}", it.Key, it.Value);
            dic.Add(it.Key.ToString(), it.Value.ToString());
        }
    }

    public Type Make( string className )
    {
        var assemblyName = new AssemblyName("JsonProviderAssembly");
        var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
        var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name, assemblyName.Name + ".dll");
        var mb = moduleBuilder.DefineType(className, TypeAttributes.Class | TypeAttributes.Public, typeof(object));

        foreach (var it in dic)
        {
            var propName = it.Key;

            FieldBuilder customerNameBldr = mb.DefineField("_" + propName, typeof(string), FieldAttributes.Private);
            PropertyBuilder custNamePropBldr = mb.DefineProperty(propName, PropertyAttributes.HasDefault, typeof(string), null);
            MethodBuilder custNameGetPropMthdBldr =
                        mb.DefineMethod("get_" + propName,
                            MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
                            typeof(string),
                            Type.EmptyTypes);
            ILGenerator custNameGetIL = custNameGetPropMthdBldr.GetILGenerator();
            custNameGetIL.Emit(OpCodes.Ldarg_0);
            custNameGetIL.Emit(OpCodes.Ldfld, customerNameBldr);
            custNameGetIL.Emit(OpCodes.Ret);
            MethodBuilder custNameSetPropMthdBldr =
                        mb.DefineMethod("set_" + propName,
                            MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
                            null,
                            new Type[] { typeof(string) });
            ILGenerator custNameSetIL = custNameSetPropMthdBldr.GetILGenerator();
            custNameSetIL.Emit(OpCodes.Ldarg_0);
            custNameSetIL.Emit(OpCodes.Ldarg_1);
            custNameSetIL.Emit(OpCodes.Stfld, customerNameBldr);
            custNameSetIL.Emit(OpCodes.Ret);
            custNamePropBldr.SetGetMethod(custNameGetPropMthdBldr);
            custNamePropBldr.SetSetMethod(custNameSetPropMthdBldr);
        }
        Type t = mb.CreateType();
        assemblyBuilder.Save(assemblyName.Name + ".dll");
        return t;
    }
}

ModuleBuilder.CreateType でクラスを作った後で、AssemblyBuilder.Save で保存します。F# の TypeProvider の場合はこれがビルド時に行われるので、ビルド時のアセンブリと実行時のアセンブリが異なるので不整合が起ります。じゃあ、どちらも .NET Frameworkの環境であったり、ビルド時に敢えて Xamarin.Forms の PCL に合うようなアセンブリを衝くてやれば良いのだろう、と考えているのですが、これはまた後で実験します。

さて、AssemblyBuilder.Save で保存した DLL を、プロジェクトから参照設定すると作成した SampleJson クラスが使えます。一度、実行して DLL を作らないと駄目ってところが、結局のところ T4 と同じで、あまり意味がない。F# の TypeBuilder のように自動でインテリセンスが効けばいいんだけど。
ちなみに、作成した JsonProviderAssembly.dll を参照設定して、SampleJson のインスタンスを作ろうとすると DLL がロックされて書き込めないというデッドロックな状態になります。ビルド時にコピーする処理が必要ですね。
あと、実行しないとアセンブリが作られないので、T4 にして、ビルド時にアセンブリを作って後から参照するとかにしないと。

クラスを定義するためのいくつかの方法: C# プログラミング 再入門
http://dotnetcsharptips.seesaa.net/article/416983160.html

これを見る限り、CSharpCodeProvider を使って文字列から生成するほうが楽そうですね。が、Xamarin.Android からは Microsoft.CSharp.CSharpCodeProvider が見当たらないので、これはこれで。

カテゴリー: 開発 | ちょっと雑だが、C# で JsonProvider もどきを作る はコメントを受け付けていません

TypeBuilderを使って、既存のクラスにメソッドを生やす

継承可能なDynamicObjectを作ろうとしたが挫折中 | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/8672

なところで、Xamarin.Forms からイベントを探索しているのはリフレクションを使っているなあ、ということが分かったので、じゃあ元のクラスに仮のメソッドをつけて無視すればいいのでは?と考えました。

XAML で、Clicked イベントがついていた時に

<Button Text="Click me" Clicked="Button_Clicked" />

↓なように、動的に Button_Click を生やしたいわけです。

public class MainPage : Xamarin.Forms.ContentPage
{
    private void Button_Click(object sender, EventArgs e)	
    {
    }
}

本来ならば、コンパイル時にメソッド名が決まっていればよいので、普通に ContentPage を継承してあらかじめコードで Button_Click を付けておけばよいのですが、XamlPreview のように XAML だけを送る場合は動的に Button_Click に作りたいのですよね。

実行時に Button_Click メソッドを作る

TypeBuilder.CreateType メソッド (System.Reflection.Emit)
https://msdn.microsoft.com/ja-jp/library/system.reflection.emit.typebuilder.createtype(v=vs.110).aspx

というのがあって実行時にクラスが作れます。
最初に断っておきますが、これは Xamarin.iOS では動きません。動かないので、XamlPreview の目的に達しないのですが、まあ、忘備録的に記録しておくというとで。Xamarin.Android 上では動くし、どうやら .NET Core 上でも動くので他にも応用が利きそうかなと。

public class PageGenerator : IPageGenerator
{
    public Type Create()
    {
        var assemblyName = new AssemblyName("dynamicassembly");
        var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
        var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name);
        var tb = moduleBuilder.DefineType("DynamicContentPage", TypeAttributes.Class, typeof(Xamarin.Forms.ContentPage));
            
        MethodBuilder meth = tb.DefineMethod(
            "Button_Clicked",
            MethodAttributes.Public,
            typeof(void),
            new Type[] { typeof(object), typeof(EventArgs) });
        ILGenerator methIL = meth.GetILGenerator();
        methIL.Emit(OpCodes.Ret);

        Type t = tb.CreateType();
        return t;
    }
}

IPageGenerator を定義しているのは、PCL プロジェクト内では動かないので DependencyService.Get するためです。
動的にアセンブリを作って、クラスを作成しています。AssemblyBuilderAccess.Run を指定するとメモリ上で動きますね。DefineType メソッドで継承先に Xamarin.Forms.ContentPage を指定します。
メソッド名は、DefineMethod で指定して、中身は GetILGenerator で作るという感じ。

こうすると、ContentPage を継承した DynamicContentPage というクラスが動的にできます。
これをメインのほうで、

var tg = DependencyService.Get<IPageGenerator>();
Type t = tg.Create();
ContentPage page = Activator.CreateInstance(t) as ContentPage;

とすれば、無事 ContentPage オブジェクトとして使えます。DynamicContentPage クラス自体は動的に作ったものなので、プログラムを書いているときには存在しません。なので、当然インテリセンスとかは効きません。

これ、ビルド時にアセンブリに落として参照設定すれば、F# の TypeProvider と同じ動きになるんじゃないかなと思うんですが、どうなんでしょう?

メソッドの中身を Expression.CompileToMethod で書ける?

動的に作成した Button_Clicked ですが、IL なので、ちょっと面倒くさい。

ILGenerator methIL = meth.GetILGenerator();
methIL.Emit(OpCodes.Ret);

じゃあ、Expression を使って、Expression.Lambda で既存のメソッドを呼び出せば楽じゃないか?と思って作ったのがこれ。CompileToMethod を使うと IL を吐き出してくれます。

var mi = typeof(PageGenerator).GetMethod("Button_Clicked", new Type[] { typeof(object), typeof(EventArgs) });
var arg1 = Expression.Parameter(typeof(object));
var arg2 = Expression.Parameter(typeof(EventArgs));
var lambda = Expression.Lambda(Expression.Call(mi, arg1, arg2), arg1, arg2);
lambda.CompileToMethod(meth);

でも、なぜか Xamarin.Android 上では実行時に CompileToMethod でダンマリになるという感じでうまくいかない。
何かしたいというと、動的に作った Button_Clicked から、既存のコードを呼び出して、それをさらに Desktop のクライアントに送れたら便利かなと思った次第なのですが、CompileToMethod がうまくいかない。

Xamarin.iOS では動かない

iOS では動的コードが動かないので、Xamarin.iOS では動きません。ビルドは通るけど、実行時に AssemblyBuilder.DefineDynamicAssembly で落ちます。

iOS で Emit 絡みがダメなのが分かったので、XamlPreview では使えないのだけど、デスクトップ側でアセンブリができるのだから、F# の TypeProvider っぽいのができないかなぁと思案中…なので続く。

カテゴリー: 開発, Xamarin | TypeBuilderを使って、既存のクラスにメソッドを生やす はコメントを受け付けていません