継承可能なDynamicObjectを作ろうとしたが挫折中

Xamarin.Forms 用の超軽量プレビューアを作る | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/8669

で、XAMLにクリックイベントが入っていると XamlLoader がパースエラーになるので、そのイベントをうまい具合に無視しなければいけないのですが、じゃあ、もともとある ContentPage クラスに後からイベントを追加できたらうまくスルーできるのではないか?と思って、継承可能な DynamicObject を探していました。

正確に言えば、DynamicObject は継承可能なので、

public class DynamicViewModel : DynamicObject
{
    Dictionary<string, object> dic = new Dictionary<string, object>();

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        return dic.TryGetValue(binder.Name, out result);
    }
    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        dic[binder.Name] = value;
        return true;
    }
}

な感じで DynamicObject を継承した ViewModel を作っておいて、後追いで次のようにプロパティを増やすことが可能です。

dynamic vm = new DynamicViewModel();
vm.Title = "Hello";
var title = vm.Title;

dynamic なので、インテリセンスは効かないけど、うまくくるめば XML や JSON をマッピングすることができます。ちなみに、Newtonsoft.Json.Linq.JObject を使うと、WPF の ViewModel としてそのまま使えます。何故か、Xamarin.Forms では使えないので、呼び出し方が微妙に違うのかなと。呼び出せるほうが不思議な感じがするのですが。.NET Framework と Profile259 の Runtime の違いかもしれません。

DynamicViewModel な方法は、ASP.NET の ViewBag にも使われているので割とポピュラーな手段です。詳細は、

メタプログラミング.NET | Kevin Hazzard, Jason Bock
https://www.amazon.co.jp/dp/4048867741

な本にも書いてあります。随分前だけど、

MVVMパターンでViewModelを楽に作る方法 – かずきのBlog@hatena
http://blog.okazuki.jp/entry/20100702/1278056325

なところで、MSDN マガジンへのリンクもあります。と言う訳で、じゃあ、DynamicObject を継承して ContentPage に後付けで Clicked なメソッドを生やすことができるんじゃないだろうか、と考えて、

public class SubPage : ContentPage, DynamicObject
{
    public SubPage() { }

    /*
    private void Button_Clicked(object sender, EventArgs e)
    {

    }
    */
    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    {
        // 読み捨て
        System.Diagnostics.Debug.WriteLine("called: " + binder.Name);
        result = null;
        return true;
    }
}

なことを考えたのですが、ダメです。C# は多重継承ができないから、ContentPage と DynamicObject の両方を基底に持つことはできないんですね。じゃあ、どっちかをインターフェースにして、内部で再実装させればいいと思ったわけで、となると DynamicObject のほうをインターフェースにしたいですよね。ってことであれこれ探すとそれっぽいものがありました。

remi/MetaObject: Simple dynamic method invocation for your .NET objects
https://github.com/remi/MetaObject

IDynamicMetaObjectProvider インターフェースを付けて、内部的に再実装しようという試みです。

public class DynamicContnetPage : ContentPage, IDynamicMetaObjectProvider
{
    #region MetaObject
    public DynamicMetaObject GetMetaObject(System.Linq.Expressions.Expression e)
    {
        return new MetaObject(e, this);
    }
    #endregion

    Dictionary<string, object>; dic = new Dictionary<string, object>();

    public virtual System.Collections.Generic.IEnumerable<string> GetDynamicMemberNames()
    {
        // return Value.GetDynamicMemberNames();
        return new string[] { };
    }

    public virtual bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    {
        result = null;
        return true;
    }
    public virtual bool TryGetMember(GetMemberBinder binder, out object result)
    {
        return dic.TryGetValue(binder.Name, out result);
    }
    public virtual bool TrySetMember(SetMemberBinder binder, object value)
    {
        dic[binder.Name] = value;
        return true;
    }
}

こんな風に MetaObject を使っておくと、

public class SubPage : DynamicContnetPage
{
    public SubPage() { }

    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    {
        // 読み捨て
        System.Diagnostics.Debug.WriteLine("called: " + binder.Name);
        result = null;
        return true;
    }
}

こんな風に、Clicked イベントやらを読み捨ててくれるはずです。中身で何か実装すれば、デバッグログとか通信っぽいものもできますね。このあたりは、実 DynamicObject のコードを見るといいのですが、中身的に IDynamicMetaObjectProvider インターフェースが DynamicMetaObject GetMetaObject(Expression parameter) を要求するのでメタデータを用意しておかないという仕組み&制限なのです。でもって、これが「式 Expression」を要求するというメタ構造になっていて、えらい大変なことになってます。

さて、これで万事解決と思いきや、いざコンパイルしてみると、MetaObject のコードがビルドできません。なんと、MetaObject は .NET Framework 専用なんですね。ああ、Xamarin.Forms の PCL は Profile259 なので .NET Runtime を使う訳なので、微妙に異なる訳です。仕方がないので、Runtime のほうに書き直そうかとしたら案の定 System.Reflection の中身が違うので、GetMethod を GetRuntimeMethod に直したりしながら、ええ、GetConstructor がないので、GetTypeInfo().DeclaredConstructors に変えてみたりと、あれこれとビルドが通るように修正。

で、なんとかビルドが通ったものを Xamarin.Forms の PCL に配置していざ、XamlLoader を動かすと、嗚呼、TryInvokeMember が呼び出される前に例外をはいて落ちてしまいます。どうやら、XAML に Clicked イベントを書くと XAML をパースするときに対応するメソッドを探してしまうらしいんですね。

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---&gt; Xamarin.Forms.Xaml.XamlParseException: Position 13:37. No method Button_Clicked found on type XamlPreview.SubPage
  at Xamarin.Forms.Xaml.ApplyPropertiesVisitor.SetPropertyValue (System.Object xamlelement, Xamarin.Forms.Xaml.XmlName propertyName, System.Object value, System.Object rootElement, Xamarin.Forms.Xaml.INode node, Xamarin.Forms.Xaml.HydratationContext context, System.Xml.IXmlLineInfo lineInfo) [0x000de] in C:\BuildAgent3\work\ca3766cfc22354a1\Xamarin.Forms.Xaml\ApplyPropertiesVisitor.cs:310 
  at Xamarin.Forms.Xaml.ApplyPropertiesVisitor.Visit (Xamarin.Forms.Xaml.ValueNode node, Xamarin.Forms.Xaml.INode parentNode) [0x00070] in C:\BuildAgent3\work\ca3766cfc22354a1\Xamarin.Forms.Xaml\ApplyPropertiesVisitor.cs:63 
...

ここで、どんな方法で探しているのかが不明(たぶんリフレクション?)なので、ちょっとこの先は解らず。仕方がないから Xamarin.Forms のコードを読むか、別な対策を立てるか思案中。

 

あった、

in ApplyPropertiesVisitor.cs で XamlParseException 例外を発生させている。

		static bool TryConnectEvent(object element, string localName, object value, object rootElement, IXmlLineInfo lineInfo, out Exception exception)
		{
			exception = null;

			var elementType = element.GetType();
			var eventInfo = elementType.GetRuntimeEvent(localName);
			var stringValue = value as string;

			if (eventInfo == null || IsNullOrEmpty(stringValue))
				return false;

			var methodInfo = rootElement.GetType().GetRuntimeMethods().FirstOrDefault(mi => mi.Name == (string)value);
			if (methodInfo == null) {
				exception = new XamlParseException($"No method {value} found on type {rootElement.GetType()}", lineInfo);
				return false;
			}

			try {
				eventInfo.AddEventHandler(element, methodInfo.CreateDelegate(eventInfo.EventHandlerType, rootElement));
				return true;
			} catch (ArgumentException ae) {
				exception = new XamlParseException($"Method {stringValue} does not have the correct signature", lineInfo, ae);
			}
			return false;
		}
カテゴリー: 開発, XAML, Xamarin | 継承可能なDynamicObjectを作ろうとしたが挫折中 はコメントを受け付けていません

Xamarin.Forms 用の超軽量プレビューアを作る

自前の LoadFromXaml ができたので、実機に XAML を送り込んで表示するプレビューアを作ってみます。
その昔、

moonmile/XFormsPreviewer: Dynamic loading XAML file of Xamarin.Forms
https://github.com/moonmile/XFormsPreviewer

なるものを作り始めたのだが、自前で XAML をパースしているので、Forms のバージョンアップに追随できないし、そのうちに本家から Xamarin.Forms Previewer が出たり、Xamarin Live Player が出たりして、頓挫&忘れておりました。
が、どうも、俺の思っているプレビューアと違う、と思っていた次第で、やっぱり軽量なものを作ってみようかと。

コード

moonmile/XFormsXamlDynamicLoad:
https://github.com/moonmile/XFormsXamlDynamicLoad

– src/XamlPreview 実機やエミュレータに仕込むプレビューアプリ
– src/XamlPreviewUp 実機へXAMLをPOSTするWPFアプリ

使い方

  1. XamlPreview をビルドして、エミュレーターや実機にインストール(デバッグ実行)します。
  2. XamlPreview は http://172.16.0.16:8080/ な感じで簡易HTTPサーバーしてます。

  1. XAML を送り込む XamlPreviewUP を起動します。
  2. IP を設定して、プレビューしたい XAML ファイルをドロップします。

すると、エミュレーター/実機のXamlPreviewの表示が切り替わります。

XAML を送り込むタイミングは、ファイルをドロップしたときと XamlPreviewUP のボタンを押したときなので、Visual Studio で XAML を修正した後にボタンを押して画面を更新すればよいでしょう。これは、そのうち更新状態をチェックして自動のアップロードするようにする予定。

制限

内部的に Xamarin.Forms.Xaml.XamlLoader クラスの Load メソッドを使っているので、XAML ファイルだけだとパースエラーになる場合があります。
例えば、Button のクリックイベント(Clicked)が書いてあるとエラーになるという状態。これ、わざわざイベント先のメソッドの存在をチェックしているらしく、所謂イベント系を書いてしまうとエラーになります。
これは、XAML を送るときにイベント系のものを削ってしまおうかなと。

Binding の記述はそのままでも大丈夫なので、デザイン時のデータバインディングクラスを作るとかして、対応したいかなと。特に ListView のデモとかにはよいだろうと。

利点

Xamarin.Forms Previewer や Xamarin Live Player よりも圧倒的に軽いです。これは PCL ライブラリとかビルドせずに、XAML だけ送って表示しているので、最初のデザインとかGridの調節とかにや良いかなと。あと、XAML の練習用ですね。

ちょっと面白いところでは、SliderBindingsPage.xaml が動きます。

XAML 内だけでバインディングする方法でスライダーを動かすと文字が回転します。このあたりの XAML は https://github.com/xamarin/xamarin-forms-samples からピックアップしています。

コードの解説は後日

カテゴリー: 開発, Xamarin | Xamarin.Forms 用の超軽量プレビューアを作る はコメントを受け付けていません

リフレクションを使ってXamarin.FormsにXAMLを動的ロードする

ふと、LoadFromXaml が public になれば、と思って探して、Xamarin.Forms のコードを見ていったら、

Load Xaml Dynamically At Runtime ? Xamarin Forums
https://forums.xamarin.com/discussion/87727/load-xaml-dynamically-at-runtime
Please make XamlLoader Public ? Xamarin Forums
https://forums.xamarin.com/discussion/87810/please-make-xamlloader-public

需要はあるようなないような。以前、動的にロードしたかったのは Xamarin Live Player 以前の頃だったので、既にあるからまあ特にいらんだろうという感じもするし、XAML をサーバーからダウンロードして切り替えることができたら、少しは違うかもしれないと思ったり。

サンプル

moonmile/XFormsXamlDynamicLoad: Xamarin.FormsでXAMLを動的にロードするサンプル
https://github.com/moonmile/XFormsXamlDynamicLoad

拡張メソッドを定義

ContentPageのLoadFromXamlメソッドが private ならば、自前で作ってしまえ、ってことで自前で作ってしまう。

static class ContentPageExtensions
{
    public static TXaml LoadFromXaml<TXaml>(this TXaml view, string xaml)
    {
        Load(view, xaml);
        return view;
    }
    private static void Load(object view, string xaml)
    {
        var t = Type.GetType("Xamarin.Forms.Xaml.XamlLoader, Xamarin.Forms.Xaml");
        var mi = t.GetRuntimeMethod("Load", new Type[] { typeof(object), typeof(string) });
        var obj = mi.Invoke(null, new object[] { view, xaml });
        return;
    }
}

自前のLoadFromXamlから、本物のXamarin.Forms.Xaml.XamlLoader.Loadを呼び出す。実は、XamlLoaderクラスはinternalなので呼び出せないのだが、Type.GetTypeでロードして、GetRuntimeMethodを使うと呼び出せるんですね。

PrivateObject クラス?(Microsoft.VisualStudio.TestTools.UnitTesting)
https://msdn.microsoft.com/ja-jp/library/microsoft.visualstudio.testtools.unittesting.privateobject.aspx

と同じことをやります。

メインページから遷移するときに、自前で XAML をロードさせます。コード内に書いちゃうとリソースで XAML にしたほうが楽だろう(実際そうだし)と思うのですが、似たような感じでネット上から XAML をダウンロードすることも可能ということで。

private void Button_Clicked(object sender, EventArgs e)
{
    var xaml = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
<ContentPage xmlns=""http://xamarin.com/schemas/2014/forms""
        xmlns:x=""http://schemas.microsoft.com/winfx/2009/xaml"">
<StackLayout>
<Label Text=""Hello Sub Page"" />
<Button Text=""click me!"" Clicked=""Button_Clicked"" />
<Label Text=""{Binding Count, StringFormat='{0} clicked'}"" />
</StackLayout>
</ContentPage>
";
    /// XAML を動的にロードする
    this.Navigation.PushAsync(new SubPage().LoadFromXaml(xaml));
}

SubPage クラスのほうは MVVM も効くので、ViewModel クラスだけで取り廻して XAML のほうは動的に切り替えることもできるかな、と。

public class SubPageViewModel : ObservableObject
{
    private int count = 0;
    public int Count
    {
        get { return count; }
        set { SetProperty(ref count, value, nameof(Count)); }
    }
}
public class SubPage : ContentPage
{
    public SubPage()
    {
        vm = new SubPageViewModel();
        this.BindingContext = vm;
    }
    SubPageViewModel vm;
    private void Button_Clicked(object sender, EventArgs e)
    {
        vm.Count++;
    }
}

実行

カテゴリー: 開発, XAML, Xamarin | リフレクションを使ってXamarin.FormsにXAMLを動的ロードする はコメントを受け付けていません

F#でXamarin.Formsを使う

いつからできるようになったんだっけ?

Petzold Book Blog – Writing Xamarin.Forms Apps in F#
http://www.charlespetzold.com/blog/2015/10/Writing-Xamarin-Forms-Apps-in-FSharp.html

なところなので、実は随分前からある。

で、Visual Studio for Mac の「Blank Forms App」で F# が選べるようになっていたので試してみる。ちなみに、Windows のほうの Visual Studio 2017 には F# のテンプレートがないので、Mac で作ったものを Windows 側にコピーしている。

テンプレートが少しおかしいらしく、Android のほうの Xamarin.Forms が入らない(入れようとしてエラーになっている)ので手動で入れる。

Mac のほうで作るので UWP のプロジェクトはない。
きちんと *.xaml があるので、XAML がロードできる。

type SampleXFormsFPage() = 
    inherit ContentPage()
    let xaml = base.LoadFromXaml(typeof<SampleXFormsFPage>)

C# だと、自動生成された *.g.cs ファイルにLoadFromXamlがあって、内部的にクラスとXAMLをマッチングさせる。で、F# の場合は *.g.fs を生成しないので、これを直接使うという訳。

いわゆる x:Name がプロパティにマッピングされないので、自前で FindByName を呼び出す。OnAddメソ\ッドは、動的にリフレクションを使っているのか、AddHandler を使わずにマッピングされる。UWP の場合は、Click に対応するイベントコードが生成されるが、Xamarin.Forms の場合は、イベントが生成されないのでビルド時にはチェックされず、実行時に対応するイベントがないと例外が発生する。これは C# も F# も同じ。

namespace SampleXFormsF

open Xamarin.Forms
open Xamarin.Forms.Xaml
open System

type TodoItem() =
    member val Id = "" with get, set 
    member val Name = "" with get, set
    member val Done = false with get, set

type SampleXFormsFPage() = 
    inherit ContentPage()
    let _ = base.LoadFromXaml(typeof<SampleXFormsFPage>)
    let todoList = base.FindByName<ListView>("todoList")
    let newItemName = base.FindByName<Entry>("newItemName")
    let items = new System.Collections.ObjectModel.ObservableCollection<TodoItem>()

    do
        todoList.ItemsSource <- items

    override this.OnAppearing() = 
        base.OnAppearing()

    member this.OnAdd( sender : obj, e : EventArgs ) =
            let item = new TodoItem()
            item.Id <- Guid.NewGuid().ToString()
            item.Name <- newItemName.Text
            items.Add( item )

    member this.OnSelected( sender : obj, e : SelectedItemChangedEventArgs ) = ()
    member this.OnRefresh( sender : obj, e : EventArgs ) =  ()
    member this.OnComplete( sender : obj, e : EventArgs ) =  ()

XAML はこんな感じ。もともと、Mobile App のクイックスタートのコードを持ってきているので、後から Azure に対応させる。

<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:SampleXFormsF" x:Class="SampleXFormsF.SampleXFormsFPage">
    <Grid RowSpacing="0">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <ActivityIndicator Grid.RowSpan="2" HorizontalOptions="Center" VerticalOptions="Center" IsVisible="False" IsEnabled="True" x:Name="syncIndicator"/>
        <StackLayout Grid.Row="0" BackgroundColor="#5ABAFF" Padding="10,30,10,5">
            <Label TextColor="#555555" Text="Azure App Service" />
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition Width="Auto"/>
                </Grid.ColumnDefinitions>
                <Entry x:Name="newItemName" Placeholder="Item name" />
                <StackLayout x:Name="buttonsPanel" Grid.Column="1" Orientation="Horizontal" HorizontalOptions="StartAndExpand">
                    <Button Text="+" MinimumHeightRequest="30" Clicked="OnAdd" />
                </StackLayout>
            </Grid>
        </StackLayout>
        <ListView x:Name="todoList" ItemSelected="OnSelected" IsPullToRefreshEnabled="true" Refreshing="OnRefresh" Grid.Row="1">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <ViewCell.ContextActions>
                            <MenuItem Clicked="OnComplete" Text="Complete" CommandParameter="{Binding .}"/>
                        </ViewCell.ContextActions>
                        <StackLayout HorizontalOptions="StartAndExpand" Orientation="Horizontal" Padding="15,5,0,0">
                            <StackLayout Padding="5,0,0,0" VerticalOptions="StartAndExpand" Orientation="Vertical">
                                <Label Text="{Binding Name}" />
                            </StackLayout>
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>
</ContentPage>

実行してみる


ちなみに、フロントが C# でも良いならば、適当な UWP プロジェクトを取ってきて、F# の PCL を参照設定することで動作できる。

カテゴリー: 開発, F#, Xamarin | F#でXamarin.Formsを使う はコメントを受け付けていません

Xamarin.Forms で .NET Standard を利用してファイルアクセスするまで

Xamarin.Forms プロジェクトのPCLを .NET Standard 化する方法を調べていくと、Visual Studio 2017 のリリース前だったりするので、project.json の変更があって「あれ?」となってしまうので、ちょっと記録的に。
ちなみに、この記事も半年ぐらい経つと意味がなくなるような気がするので、まあ、そのときはそのときで。

Xamarin.Formsでプロジェクトを作る

普通にXamarin.FormsでPCLを使ったプロジェクトを作ります。

Visual Studio 2017 の場合は、コード共有で「ポータブルクラスライブラリ」のほうを選択。

.NET Standard のクラスライブラリを追加する

[Visual C#]→[.NET Standard]で、「クラスライブラリ(.NET Stanrdard)」を追加

.NET Standardのプロジェクトを編集して、PackageTargetFallbackの部分を追加。これがないと、NuGetからXamarin.Formsを取り込めません。

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard1.6</TargetFramework>
    <PackageTargetFallback>$(PackageTargetFallback);portable-net45+win8+wp8+wpa81;</PackageTargetFallback>
  </PropertyGroup>

その後で、NuGetからXamarin.Formsをインストールします。

PCLプロジェクトからファイルをコピー

ソリューションエクスプローラで、「App.xaml」と「MainPage.xaml」をマウスでドラッグして、.NET Standardのプロジェクトにコピーします。
csprojファイルを開いて、ItemGroupのところをコピーしてもよいです。

Properties/AssemblyInfo.cs ファイルだけはコピーしません。内部的に、AssemblyTitle やらが作らている模様。

ちなみに、.NET Standardプロジェクトを追加したあとで、PCLプロジェクトをアンロードすると二度とロードできないという現象が発生します。なんだかなー。そういうときは、一度、.NET Standardプロジェクトを外して、PCLプロジェクトをロードします。

この手順を逆にして、.NET Standardプロジェクトのcsprojの内容を、PCLプロジェクトのほうにコピーしてもOKです。

Xamarin.iOS/Adnroidプロジェクトの参照を変更する

Xamarin.iOS/AdnroidプロジェクトからPCLプロジェクトが参照されているところを、.NET Standardプロジェクトのほうに変更します。
と、同時に Xamarin.Forms のバージョンも合わせておきます。

これでやっとこさ、Xamarin.Formsで.NET Standardが使える状態が完了です。まあ、ここまで作れたものをXamarin.Formsのプロジェクトテンプレートとして置いておけばよいので、一度作れると後は楽かも。

試しにファイルアクセスさせる

MainPage.xaml をこんな感じで修正しておいて、

<StackLayout>
    <Label Text="Welcome to Xamarin Forms!" />
    <Button Text="save" Clicked="Save_Clicked" ></Button>
    <Button Text="load" Clicked="Load_Clicked" ></Button>
    <Label x:Name="text1"  Text="output" />
</StackLayout>

Save/Loadボタンをクリックしたときのイベントがこんな感じ。

private async void Save_Clicked(object sender, EventArgs e)
{
    var path = "/data/user/0/App18.Android/files/";
    var st = System.IO.File.Open(path + "sample.txt", FileMode.OpenOrCreate);
    using (var sw = new StreamWriter(st))
    {
        await sw.WriteLineAsync("sample data");
    }
}

private async void Load_Clicked(object sender, EventArgs e)
{
    var path = "/data/user/0/App18.Android/files/";
    var st = System.IO.File.Open(path + "sample.txt", FileMode.Open);
    using (var sr = new StreamReader(st))
    {
        var text = await sr.ReadToEndAsync();
        text1.Text = text;
    }
}

Xamarim.iOS/Androidであれば、「System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments)」と書くところが、パスを直書きになっているのがアレですね。iOS/Androidの場合分けが必要でしょう。どうやって書くのか判りませんが。
Xamarin.Forms の場合は、System.IO.Fileがないので、これが使えるということは無事 .NET Standard 化しているということです。

Androidエミュレータで実行すると、こんな感じになります。

じゃあ、PCLは.NET Standardに完全に置き換わるのか?

standard/docs/netstandard-20 at master ・ dotnet/standard
https://github.com/dotnet/standard/tree/master/docs/netstandard-20

を見る限り、「No PCL left behind. Dakota can consume a PCL based assembly from a .NET Standard-based library without having to jump through hoops.」とあるので、生のままでPCLを使うことは少なくなるんでしょう。少なくともProfileの順列組み合わせ状態は激減するかなと。現状、あまり意味がないし。
その後は、.NET Standard が肥大化しちゃうか、機種依存な機能が漏れてしまうかのところなんだけど、いまのところXamarin.Formsに関しては下回りのPCLは必須な感じがします。

カテゴリー: 開発, Xamarin | Xamarin.Forms で .NET Standard を利用してファイルアクセスするまで はコメントを受け付けていません

Azure 上の .NET Core では DbSet の Join ができない

ひどくピンポイントかつ3時間ほどハマってしまったので、メモ書き的に残しておく。

環境は、

  • ASP.NET Core
  • .NET Core 1.1
  • EF Core を利用

ASP.NET Core で Web API を作っていて、ローカルの .NET Core で LINQ の Join が動いていたのだが、いざ Azure にアップロードして動作させると、Join 部分がうまくいかなくて意味不明なエラーを返した。

var cnt1 = (
        from ti in _context.TicketView
        join st in _context.Status on ti.Status_Id equals st.Id
        where ti.ProjectId == project.Id &&
                ti.Tracker_Id == tr.Id &&
                st.IsClosed == false
        select ti.Id
    ).Count();

とあるところで、こんな風に TicketView と Statues テーブルを JOIN させた。何をやりたかったかというと、Statues テーブルの IsClosed フラグを見て、その数をカウントしたかったのだ。これを、ローカル PC の ASP.NET Core で動作させると、するすると動いたので、そのまま Azure に乗せてみると…

テーブル構造が違っているからマイグレーションしろ、

というお達しが来る。最初は、エラーの意味が解らなくて四苦八苦したのだが、少しずつ障害を絞り込んでいくとここの JOIN 部分に至った。この JOIN を消してしまって、var cnt1 = 0 とすると動作することが分かったので、あれこれやって、

var status = _context.Status.OrderBy(x =&amp;gt; x.Position).ToList();
var ticketviews = _context.TicketView.Where(x =&amp;gt; x.ProjectId == project.Id).ToList();

var cnt1 = (from ti in ticketviews
            join st in status on ti.Status_Id equals st.Id 
            where ti.ProjectId == project.Id &&
                    ti.Tracker_Id == tr.Id &&
                    st.IsClosed == false
            select ti.Id).Count();

な風に、一度 List に取り込んでから JOIN するとうまく動くようになった。

LINQ を使ってジェネリックの List 同士を Join させる分には大丈夫と解っているのだが、_context.Status のほう(DbSet<Status> などで DI されている)の Join の動作が不明である。もともと、ローカル PC で DbSet 同士の Join ができちゃうのがおかしいのか、それとも Azure 上の .NET Core で DbSet 同士の Join ができないのがおかしいのか。

どちらにせよ、同じコードをローカル PC と Azure 上で動かしたときに、挙動が違うのは「おかしい」ので、ここは調べないといけない。これ、Linux 上の .NET Core だとどうなのだろうか? 後で調べるか思案中。

追記 2017/05/11

Ubuntu に donet 1.0.3 をインストールして構築してみると、上の Join が動くようになったので、Azure 上だけがダメな模様。

Azure の App Service のコンソールでバージョンを調べると doetnet コマンド自体が 1.0.1 、.net core のバージョンが Version  : 1.1.0-preview1-001100-00 になっているので、この違いと思われる。ちなみに、ローカルPC の場合は、1.1.0 になっている。この微妙な差(Azure のほうが previewのままなのが)原因らしい。

Build 2017 の微妙な時期なので、これが終わると 2.0 にアップデートされるかもしれん。

追記 2017/05/19

現時点で、.NET Command Line Tools (2.0.0-preview1-005977)  になっているので、join が正常に動いている。やったー。

カテゴリー: Azure, OpenCCPM | Azure 上の .NET Core では DbSet の Join ができない はコメントを受け付けていません

オーケストラに学ぶプロジェクトマネジメント

さて、やっとこさ書き上げて発売されたのでブログも再開。

[amazonjs asin=”4798050024″ locale=”JP” title=”成功するチームの作り方 オーケストラに学ぶプロジェクトマネジメント”]

目次

第1章 コンダクターとプロジェクトマネジメント
1 チームにおけるコンダクター(指揮者)の役目
2 プロフェッショナルな奏者≒プロフェッショナルなエンジニア
3 ハーモニー(調和)とは何か
4 チームが成し遂げる作品(製品)は何か

第2章 プロジェクト計画、プランニングの伝達
1 計画駆動とアジャイル開発とオーケストレーションと
2 形作る作品をイメージで共有する
3 どうやって形作っていくのか
4 三面図(WBS、PERT、ガントチャート)
5 CCPMと焦らない心

第3章 プロジェクトの構成員
1 新人が持っていない能力は何か
2 中途採用が持っている能力は何か
3 プロフェッショナルなエンジニア/プログラマとは
4 コンダクターとプロジェクトメンバーの関係
5 客演という外注

第4章 メンバーの技量
1 メンバーの能力を相互に知る
2 メンバーの能力を相互に尊重する
3 見極めに利用できるもの(ブログ、GitHub、資格)
4 「一緒に仕事ができる」条件を並べる

第5章 継続的な努力
1 チームに参加する時点が最低の実力
2 プロジェクトが終わった時点で最高点に達する
3 「Google 20%ルール」の本来の意味
4 「40時間勤務」の本来の意味
5 ツールを残して次回に備える

第6章 先人の知恵を活用する
1 新しいことを安易に取り入れない理由
2 暗黙知と形式知
3 UMLとパターンランゲージ
4 ISO9001と品質工学
5 温故知新の上にある新しい技術

第7章 現実を計測する
1 難破船のように漂わないために羅針盤を使う
2 現実に適応するためにハンドルを使う
3 希望が挫けないために航海日誌を書く
4 進捗率の自動計測とTDD
5 EVの本来の使い方
6 下降する進捗グラフ(バーンダウンチャート)

第8章 変化は好きですか?

1 世の中は思い通りにはいかない
2 世の中は変化ばかりではない
3 効果的な変化とは≒レバレッジ
4 計画駆動と要件定義
5 アジャイル開発とTOC
[コラム]すべてに目を光らせるために

第9章 仕事とバランス
1 チームでは何が有利か
2 チームでは何が不利か
3 「刺身システム」と「のりしろ」
4 マリッジリスクに対応する「ゆとり」
5 「ゆとり」の時間を何に活用するのか

第10章 ドロップアウトの条件
1 チームが悲鳴を上げる時
2 「排除すべき人」を排除する
3 ゼロ生産者とマイナス生産者に対応する
4 決断する「閾値」を決めておく
[コラム]戦略と戦術の違い

第11章 インセンティブと報酬
1 鞭のない「飴」
2 プロを雇うための費用を算出する
3 インセンティブを提案する
4 成果にのみ「報酬」を与える
[コラム]努力をムダにしない

第12章 差異と反復
1 ジル・ドゥルーズの『差異と反復』
2 本能で違いを読み取れ—差異
3 繰り返しで拡張しろ—反復
4 リスク管理とフェールセーフ
5 失敗学と危険な匂い

カテゴリー: 開発, プロジェクト管理 | オーケストラに学ぶプロジェクトマネジメント はコメントを受け付けていません

Microsoft OCR をデスクトップのWFPアプリで動かす方法

巷ではGoogleのリアルタイム翻訳アプリが流行っているのだが、あいにく手元にある Android が古いので、それほど面白い画像が取れない。ので、Google Cloud Vision で OCR 機能を試してみたのだが、実は Microsoft OCR もあるよ、ってことで。

こんなものがある。

Windows.Media.Ocr namespace – Windows app development
https://msdn.microsoft.com/library/windows/apps/windows.media.ocr.aspx?f=255&MSPPError=-2147217396&cs-save-lang=1&cs-lang=csharp#code-snippet-4

試してみると、Microsoft OCR も Google のそれに対してさほど悪い結果でないことが分かった。発表の頃はもうちょっとダメな感じがあったのだが、単純に文字列を読み取る OCR という点では Google のとどっこいどっこいという感じである。Google Cloud Vision の場合は無料枠だと 1,000回/月 という制限があるので、無料の範囲であって Windows 上であれば Microsoft OCR でも十分かもしれない。まあ、とはいえ Google のほうも 100万回/月にしたって $2.5 で使えるので実験レベルであれば十分だろう。

Microsoft OCR のサンプルがストアアプリになっている

Windows-universal-samples/Samples/OCR at master ・ Microsoft/Windows-universal-samples
https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/OCR

サンプルコードをダウンロードして実行してみると、UWPアプリ(ストアアプリ)で動く。

確かに、UWP アプリにするとストアにアップできたり、Windows IoT Core で動いたりと便利ではあるのだが、画像とファイルの扱いが面倒になっているので、できることならば従来通りのデスクトップアプリで試しておきたい…と思いつつ調べたのだが、サンプルが見当たらない。

実は OCR を提供している Windows.Media.Ocr が「Windows Runtime API」に入っていて UWP アプリで使うことを想定している状態なのであった。じゃあ仕方がない、以前の WinRT をライブラリを WPF アプリから使えるようにしてみよう、と思って作ったサンプルがこれです。

https://github.com/moonmile/google-cloud-vision-sample/tree/master/src/OCR/MsOcrWPF

WinRT のライブラリを使う準備

Windows 8.1 の頃にやった技は Windows 10 でも有効なので、*.csproj に TargetPlatformVersion タグを追加する。このバージョンは、適当に UWP アプリから取ってくる。

<TargetPlatformVersion>10.0.14393.0</TargetPlatformVersion>

すると参照設定で「Windows」→「コア」というのが使えるようになって、WinRT のアセンブリを指定できるようになる。

OCR は「Windows.Media」のほうに入っている。

OCR を使う場合は、結果的に

– Windows.Foundation
– Windows.Graphics
– Windows.Media
– Windows.Storage

の4つのWinRTアセンブリを追加することになる。

実は、サジェストを使ってアセンブリを自動的にロードすることもできるのだが、Windows.Foundation.UniversalApiContract が他のものと競合してしまってうまくいかないので、自前でやる。もし Windows.Foundation.UniversalApiContract が参照設定に入ってしまったときは、参照から外してしまう。

ビルドをするときに、WinRT のランタイムが足りないと怒られるので、System.Runtime.WindowsRuntime.dll も追加しておく。

C:/Program Files (x86)/Reference Assemblies/Microsoft/Framework/.NETCore/v4.5.1/System.Runtime.WindowsRuntime.dll

OCR を使うコード

実は Microsoft OCR を使うときのコードは非常に単純で、次のように SoftwareBitmap を渡して、OCR結果である OcrResult オブジェクトを受け取ることができる。

public async Task<OcrResult> detect( SoftwareBitmap bitmap)
{
    var ocrEngine = OcrEngine.TryCreateFromUserProfileLanguages();
    var ocrResult = await ocrEngine.RecognizeAsync(bitmap);
    return ocrResult;
}

画像を渡して結果を受け取るだけなので、使い方は単純なのだが、

  • SoftwareBitmap クラスを扱わないといけない
  • WinRT の Async はデスクトップの async/await とは違った形になっている

の2点が難関になっている。

GetAwaiter メソッドを自作で拡張する

http://blog.xin9le.net/entry/2012/11/12/123231

を参考にして…というかそのままコードを持ってきて、以下のように GetAwaiter メソッドを拡張する。

public static class TaskEx
{
    public static Task<T> AsTask<T>(this IAsyncOperation<T> operation)
    {
        var tcs = new TaskCompletionSource<T>();
        operation.Completed = delegate  //--- コールバックを設定
        {
            switch (operation.Status)   //--- 状態に合わせて完了通知
            {
                case AsyncStatus.Completed: tcs.SetResult(operation.GetResults()); break;
                case AsyncStatus.Error: tcs.SetException(operation.ErrorCode); break;
                case AsyncStatus.Canceled: tcs.SetCanceled(); break;
            }
        };
        return tcs.Task;  //--- 完了が通知されるTaskを返す
    }
    public static TaskAwaiter<T> GetAwaiter<T>(this IAsyncOperation<T> operation)
    {
        return operation.AsTask().GetAwaiter();
    }
}

実はアセンブリ System.Runtime.WindowsRuntime.dll を追加すれば拡張クラスがあるはずなのだが、うまくいかないので自前で作る。クラスを作らない場合は、こんなビルドエラーがでてくるはず。

エラー	CS0012	型 'IAsyncAction' は、参照されていないアセンブリに定義されています。アセンブリ 'Windows, Version=255.255.255.255, Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime' に参照を追加する必要があります。

SoftwareBitmap クラスを扱えるようにする

SoftwareBitmap ってのも、WinRT の世界のものなので、デスクトップアプリからは直接扱えない。これでは面倒なので、デスクトップアプリで使えるようにファイルパスを渡して SoftwareBitmap オブジェクトを生成する関数を作っておく。

/// <summary>
/// ファイルパスを指定して SoftwareBitmap を取得
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
private async Task<SoftwareBitmap> LoadImage(string path)
{
    var fs = System.IO.File.OpenRead(path);
    var buf = new byte[fs.Length];
    fs.Read(buf, 0, (int)fs.Length);
    var mem = new MemoryStream(buf);
    mem.Position = 0;

    var stream = await ConvertToRandomAccessStream(mem);
    var bitmap = await LoadImage(stream);
    return bitmap;
}
/// <summary>
/// IRandomAccessStream から SoftwareBitmap を取得
/// </summary>
/// <param name="stream"></param>
/// <returns></returns>
private async Task<SoftwareBitmap> LoadImage(IRandomAccessStream stream)
{
    var decoder = await Windows.Graphics.Imaging.BitmapDecoder.CreateAsync(stream);
    var bitmap = await decoder.GetSoftwareBitmapAsync(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
    return bitmap;
}
/// <summary>
/// MemoryStream から IRandomAccessStream へ変換
/// </summary>
/// <param name="memoryStream"></param>
/// <returns></returns>
public async Task<IRandomAccessStream> ConvertToRandomAccessStream(MemoryStream memoryStream)
{
    var randomAccessStream = new InMemoryRandomAccessStream();
    var outputStream = randomAccessStream.GetOutputStreamAt(0);
    var dw = new DataWriter(outputStream);
    var task = new Task(() => dw.WriteBytes(memoryStream.ToArray()));
    task.Start();
    await task;
    await dw.StoreAsync();
    await outputStream.FlushAsync();
    return randomAccessStream;
}
  1. System.IO.File でファイルオープンして MemoryStream へ読み込み
  2. MemoryStream から IRandomAccessStream に変換
  3. IRandomAccessStream から SoftwareBitmap に変換
  4. SoftwareBitmap オブジェクトを OCRエンジンに渡す

というややこしい手順になっている。

画面にマークを付ける

UWP アプリのサンプルでは、マークを XAML の Rectangle を使っているが、デスクトップアプリの場合は Bitmap に直接書き込んだほうが手軽だろう。そのために WPF アプリでやりたいわけだし。

private async void clickDetect(object sender, RoutedEventArgs e)
{
    var bitmap = await LoadImage(screenFile);
    var result = await detect(bitmap);
    text1.Text = result.Text;

    // 認識した個所をマークする
    var bmp = Bitmap.FromFile(screenFile) as Bitmap;
    var g = Graphics.FromImage(bmp);
    var br = new SolidBrush(System.Drawing.Color.FromArgb(0x80, System.Drawing.Color.Blue));
    var text = "";
    foreach (var line in result.Lines)
    {
        text += line.Text + " ";
        foreach (var it in line.Words )
        {
            var rc = new System.Drawing.Rectangle(
                (int)it.BoundingRect.X, (int)it.BoundingRect.Y,
                (int)it.BoundingRect.Width, (int)it.BoundingRect.Height); 
            g.FillRectangle(br, rc);
            g.DrawRectangle(Pens.Red, rc);
            text += it.Text + " ";
        }
    }
    image.Source = bmp.ToImageSource();
}

public static class BitmapEx
{
    /// <summary>
    /// Bitmap から BitmapSource へ変換
    /// </summary>
    /// <param name="bmp"></param>
    /// <returns></returns>
    public static System.Windows.Media.ImageSource ToImageSource(this Bitmap bmp)
    {
        BitmapSource bitmapSource = Imaging.CreateBitmapSourceFromHBitmap
            (
                bmp.GetHbitmap(),
                IntPtr.Zero,
                Int32Rect.Empty,
                BitmapSizeOptions.FromEmptyOptions()
            );
        return bitmapSource;
    }
}

そんな訳で

C# – C#で画像ファイルからOCRしたい(14229)|teratail
https://teratail.com/questions/14229

な回答が分かった訳だが(この質問は、OCR のサンプルを作った後に見つけた)、これ、ちょっと大変過ぎるよ。どうしたものか。

サンプルコード

https://github.com/moonmile/google-cloud-vision-sample

参考先

非同期メソッド入門 (10) – WinRTとの相互運用 – xin9le.net
http://blog.xin9le.net/entry/2012/11/12/123231
WinRTのAPIをデスクトップアプリから使う 8.1版 – かずきのBlog@hatena
http://blog.okazuki.jp/entry/2014/06/26/201327
c# – Is there a way to convert a System.IO.Stream to a Windows.Storage.Streams.IRandomAccessStream? – Stack Overflow
http://stackoverflow.com/questions/7669311/is-there-a-way-to-convert-a-system-io-stream-to-a-windows-storage-streams-irando
C# – C#で画像ファイルからOCRしたい(14229)|teratail
https://teratail.com/questions/14229

カテゴリー: 開発, C#, WinRT | 1件のコメント

Android Things 上で Xamarin.Android を使って Lチカする

Android Things 上で Xamarin.Android を動かして F# を使う | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/8451

ここから暫く経ってしまいましたが、Android Things + Xamarin.Android の組み合わせでLチカ(GPIO制御)まで出来たので、サンプルを流しておきます。

サンプルコード

android-things-samples/GpioAndroid at master ・ moonmile/android-things-samples
https://github.com/moonmile/android-things-samples/tree/master/GpioAndroid

バインディングプロジェクトを作る

プロジェクトは本体の「GpioAndroid」とバインディングプロジェクトの「GpioBinding」に分かれています。バインディングプロジェクトは、JarをラップするC#コードを自動生成してくれるプロジェクトですね。OpenCV for Android を Xamarin で使うときにも使います。

「Binding Library(Android)」でプロジェクトを作成すると、Jars フォルダなどが作成されます。

この Jars フォルダにバインドしたい Jar ファイルを入れます。ここでは、Android Things のバインドファイルの「androidthings-0.1-devpreview.jar」を入れておきます。このファイルは、Android Studio で Android Things プロジェクトを作るときにダウンロードしてきたものを使うのですが、サンプルコードにも入っているのでそのまま使ってください。

バインドするクラスやメソッドを調節するために、Metadata.xml ファイルに記述します。このあたりは、

jonpryor/SimpleAndroidThingsBinding
https://github.com/jonpryor/SimpleAndroidThingsBinding

を参考にしています。

<metadata>
  <remove-node
      path="/api/package[@name='com.google.android.things.pio']/class[@name='GpioDriver']/method[@name='close' and count(parameter)=0]"
  />
</metadata>

どうやら、Close メソッドがダブっているらしくそのままではビルドエラーになるんですよね。なので、このメソッドを無視するようにします。

本体のプロジェクトを作る

普通に Single-View App プロジェクトを作って、先の「GpioBinding」プロジェクトを参照設定します。

using Com.Google.Android.Things.Pio;
using System.Threading.Tasks;

public class MainActivity : Activity
{
    TextView text1;
    Gpio mLedGpio;

    protected override void OnCreate(Bundle bundle)
    {
        base.OnCreate(bundle);

        // Set our view from the "main" layout resource
        SetContentView(Resource.Layout.Main);

        // Get our button from the layout resource,
        // and attach an event to it
        Button button = FindViewById<Button>(Resource.Id.MyButton);
        button.Click += Button_Click;
        text1 = FindViewById<TextView>(Resource.Id.textView1);

        PeripheralManagerService service = new PeripheralManagerService();
        try
        {
            var pinName = "BCM4";   // RPi3 
            // String pinName = BoardDefaults.getGPIOForLED();
            mLedGpio = service.OpenGpio(pinName);
            mLedGpio.SetDirection(Gpio.DirectionOutInitiallyLow);
        }
        catch 
        {     
        }

        var task = new Task(async() => {
            while (true)
            {
                await Task.Delay(1000);
                mLedGpio.Value = !mLedGpio.Value;
                System.Diagnostics.Debug.WriteLine("led: {0}", mLedGpio.Value);

                RunOnUiThread(() => {
                    if (mLedGpio.Value)
                    {
                        text1.SetBackgroundColor(Android.Graphics.Color.LightPink);
                    }
                    else
                    {
                        text1.SetBackgroundColor(Android.Graphics.Color.White);
                    }
                });
            }
        });
        task.Start();
    }

    private void Button_Click(object sender, EventArgs e)
    {
        mLedGpio.Value = !mLedGpio.Value;
    }
}

書き方は、Windows IoT Core と似ていますね。

  1. PeripheralManagerService で IoT のサービスを作成する。
  2. service.OpenGpio で指定ピンをオープンする。”BCM4″ は 4ピンのことです。ここの名前は https://developer.android.com/things/hardware/raspberrypi-io.html に定義されています。
  3. GPIO の値は Value プロパティで設定します。

基本は、

Interact with Peripherals | Android Things
https://developer.android.com/things/training/first-device/peripherals.html

と動きを合わせちて、ループは.NETらしくTaskを使っています。画面に表示するときは、RunOnUiThreadを使うことを忘れずに。

バインドする androidthings-0.1-devpreview.jar ファイルですが、これはスタブファイルなので中身は空っぽです。実行時には、com.google.android.things が使われるように、Properties/AndroidManifest.xml に uses-library を追加しておきます。これをしないと実行時に PeripheralManagerService が生成できなくてエラーになりあます(かなりハマった)。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="GpioAndroid.GpioAndroid" android:versionCode="1" android:versionName="1.0" android:installLocation="auto">
	<uses-sdk android:minSdkVersion="24" />
  <application android:label="GpioAndroid" android:icon="@drawable/Icon">
    <uses-library android:name="com.google.android.things"/>
  </application>
</manifest>

実行してみる

adb connect android.local で RPi 上の Android に接続して(WiFi経由で接続できます)、Visual Studio から実行すると次のように Lチカができます。

LED がチカチカとするのと同期して、画面のラベルの色が赤と白が交互に点滅します。

カテゴリー: Android, Xamarin | Android Things 上で Xamarin.Android を使って Lチカする はコメントを受け付けていません

Raspbian, Windows IoT Core, Android Things on Raspberry Pi の三役揃い踏み

新しい Raspberry Pi 3 と液晶ディスプレイが到着したので、3つ同時に動かしてみました、の記録です。

image

RPi3 がちょっと安くなってる

aliexpress で Raspberry Pi 3 がちょっとだけ安くなっています。$36 程度で購入が可能になっています。細かい違いや、あれこれを気にしない場合は国内よりもちょっと安く手に入ります。

2016 New Raspberry Pi 3 Model B https://www.aliexpress.com/item/2016-Original-UK-Made-Raspberry-Pi-3-Model-B-1GB-RAM-Quad-Core-1-2GHz-64bit/32643511924.html?spm=2114.13010608.0.0.6S4vZd

RPi に乗せる液晶ディスプレイ(800×480)はこれを愛用しています。GPIO ピンがふさがってしまうのが難点ですが(別途半田付けすればいいんだけど)、HDMI 表示が簡単なのと別電源が要らないので手軽に扱えます。

5 inch 800×480 Touch LCD Screen 5″ Display
https://www.aliexpress.com/item/5-inch-800×480-Touch-LCD-Screen-5-Display-For-Raspberry-Pi-Pi2-Model-B-A-Hot/32707062356.html?spm=2114.13010608.0.0.MHXpmP

RPi3 は、ひとつだけ買ったのですが micro SD カードを差し替えて Windows IoT Core と Android Things を交互に試すのが面倒になって、2個追加しました。これで3つ同時に扱えるようになりました。

これで、Linux, Windows, Android と3種類の OS が揃ったので(iOS が Raspberry Pi に載ることはないでしょうから)、IoT としても用途によって OS を選べる、という段階に来たと思います。もちろん、LattePanda のように組み込みボード自体を変えてしまえば、フルな Windows 10 が動く状況を作るとか色々できますが、Raspberry Pi ボードがあれば3種類のOSが「用途に依って」使い分けができるところが良いところですよね。

以下、ざっと各 OS のダウンロード先と特徴を書いておきます。

Raspbian on Raspberry Pi

Raspberry Pi Downloads – Software for the Raspberry Pi
https://www.raspberrypi.org/downloads/

本家本元からダウンロードして micro SD カードに焼き込みます。中身は Linux の Debian なので、普通に LAMP サーバーとして使うことができます。大抵のものは apt-get で取って来れるので、低電力のミニサーバーを作りたいときはこれで十分かもしれません(micro SD カードの容量と読み書きスピードに依存しますが)。

GPIO を扱うときは、Python を使うのがお手軽ですね。Scratch とか node.js とかが動きます。私の場合、大抵は Tera term で ssh 接続して使うのでコマンドラインで使うことが多いので特に問題はないのですが、テキストベースの扱いに慣れていないとちょっと使い辛いかもしれません。

mono の環境を「apt-get mono-compete」でインストールすれば、C# を使ってプログラミングができます。

Windows IoT Core on Raspberry Pi

Raspberry Pi 3 と Arduino での Windows 10 IoT アプリの開発 | Windows IoT
https://developer.microsoft.com/ja-jp/windows/iot

上記から専用のアプリをダウンロードして micro SD カードに焼き込みます。Windows となっていますが、中身は似て非なるものなので、徹底して UWP アプリ(Windows ストアアプリ)として使います。バックグラウンドで動かす機能もあるけど、それならば Linux で動かしたほうが手軽なので。

アプリを Visual Studio で C# で書いて RPi にデプロイという形式ですね。エンタプライズ版もあるけど、それはこれからといったところなので、パーソナルベースで使うのがベターかと。プログラムが C#(時には F# で)書けるのがミソで、普通の UWP アプリに GPIO のライブラリを追加して使うという方法ができます。

 

Android Things on Raspberry Pi

Android Things | Android Things
https://developer.android.com/things/hardware/index.html

OS のスタイルとしては Windows IoT Core と同じで、スマートフォンの Android のサブセットという形です。元々 Raspberry Pi で Android 4.4 を動かすことができたのですが、Android Things は本家 Google がサポートする OS です。

基本は Android Studio を使って Java で Android アプリを作ります。スマートフォンではないので、カメラ機能やホームの機能もないのですが、UI 廻りや API 廻りはひと通り揃っているようです。GPIO は、別途ライブラリをインポートして使います。

ベースが Android なので、Xamarin.Android が使えます。Visual Studio 2015 で Android アプリを使ってデプロイすることで、C# でプログラミングができます。GPIO まわりはうまく jar 内のクラスに対して設定すればよいらしいので、あとで試します。

 

で、3つ並べてどうするのか?

それぞれのプログラムを作るときは、Python, C#, Java で切り替えることになるのですが、mono/Xamarin の組み合わせだと、C# や F# が使えます。あとは UI をどうするかとか、OpenCV を使うとか、既存ライブラリを流用するとかによって、OS を変えていくのがよいのではと思っています。ひとまず、RPi3 だと WiFi/Bluetooth が載っているので、各 OS が標準で対応してくれます。Raspbian をサーバーにして、Windows IoT Core/Android Things をクライアントにして何かの通信デモとか作ってみようかなと。

カテゴリー: RaspberryPi | 2件のコメント