ASP.NET Core MVC の Web API で XML 形式のデータを扱う

ASP.NET Core の Web API は標準で JSON 形式を扱うようになっているので、XML 形式を扱おうとすると苦労します…というか、苦労したのでメモ書き。

送受信の形式

Web API を POST で送信する場合 Body に何の形式を使うのか、というのと、受信に何の形式を使うのか、で組み合わせがある。

送信側
– フォーム形式 application/x-www-form-urlencoded
– JSON 形式 application/json
– XML 形式 application/xml あるいは text/xml

受信側
– JSON 形式 application/json
– XML 形式 application/xml あるいは text/xml

で、最近はブラウザ経由で JSON 形式で送受信することが多いので、そっちの情報は比較的多いのだが、XML 形式の情報がない。というか、WCF がそれを担っていたのだけど、WCF 自体が廃盤になっている。
なので、試験的に

– WPF アプリで XML 形式で送信
– ASP.NET Core Web API で XML 形式で返す

ということが考える。

テストプロジェクトを作る

ASP.NET Core Web Applicaiton(.NET Core) を使う.

Web API 側の設定

project.json に

"Microsoft.AspNetCore.Mvc.Formatters.Xml": "1.0.0"

を加える。

Setup.cs の Setup.ConfigureServices に XmlSerializerOutputFormatter と XmlSerializerInputFormatter を追加する。

services.AddMvc();
// add XML output formatter
services.Configure<Microsoft.AspNetCore.Mvc.MvcOptions>(
    options => {
        options.OutputFormatters.Add(
            new Microsoft.AspNetCore.Mvc.Formatters.XmlSerializerOutputFormatter());
        options.InputFormatters.Add(
            new Microsoft.AspNetCore.Mvc.Formatters.XmlSerializerInputFormatter());
    });

OutputFormatters がアウトプット用で、InputFormattersがインプット用なので、フォーム形式で受けてXML形式で返す場合には、OutputFormattersだけでよい。
XML形式には、XmlDataContractSerializerOutputFormatter もあるの。これはクライアントと形式を揃える必要がある。じゃないとデシリアライズができない。

これで ASP.NET Core 側の XML 形式で送受信する設定は完了。

Web API の PeopleController クラスを作ってみる

Modelクラスである Person クラスを作っておく。

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}

#if false
// これでは XMLデシリアライズできない
public class People : List<Person> { }
#endif

public class People
{
    // プロパティにして List 化すると通る
    public List<Person> Items { get; set; }
}

実は、List<Person> を使いたいのだが、XML形式でシリアライズするときに ArrayOfPerson のように変換されてデシリアライズでうまくいかない。仕方がないので、People クラスのように、中身に List を含んだラップクラスを作る。

この Person クラスからコードファーストでデータベースを作った後、PeopleController クラスを作る。

 [Produces(&quot;application/xml&quot;)]
 [Route(&quot;api/People&quot;)]
public class PeopleController : Controller
{
    private readonly ApplicationDbContext _context;

    public PeopleController(ApplicationDbContext context)
    {
        _context = context;
    }

    // GET: api/People
    [HttpGet]
#if false
	public IEnumerable<Person> GetPerson()
	{
		return _context.Person;
	}
#else
    public async Task<People> GetPerson()
    {
        var people = new People();
        people.Items = new List<Person>();
        await _context.Person.ForEachAsync(p => people.Items.Add(p));
        return people;
    }
#endif
    // GET: api/People/5
    [HttpGet(&quot;{id}&quot;)]
    public async Task<IActionResult> GetPerson([FromRoute] int id)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        Person person = await _context.Person.SingleOrDefaultAsync(m => m.Id == id);

        if (person == null)
        {
            return NotFound();
        }

        return Ok(person);
    }

    // PUT: api/People/5
    [HttpPut(&quot;{id}&quot;)]
    public async Task<IActionResult> PutPerson([FromRoute] int id, [FromBody] Person person)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        if (id != person.Id)
        {
            return BadRequest();
        }

        _context.Entry(person).State = EntityState.Modified;

        try
        {
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!PersonExists(id))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }
        // return NoContent();
        // return CreatedAtAction(&quot;GetPerson&quot;, new { id = person.Id }, person);
        return await GetPerson(person.Id);
    }

    // POST: api/People
    [HttpPost]
    public async Task<IActionResult> PostPerson([FromBody] Person person)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        _context.Person.Add(person);
        try
        {
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateException)
        {
            if (PersonExists(person.Id))
            {
                return new StatusCodeResult(StatusCodes.Status409Conflict);
            }
            else
            {
                throw;
            }
        }

        return CreatedAtAction(&quot;GetPerson&quot;, new { id = person.Id }, person);
    }

    // DELETE: api/People/5
    [HttpDelete(&quot;{id}&quot;)]
    public async Task<IActionResult> DeletePerson([FromRoute] int id)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        Person person = await _context.Person.SingleOrDefaultAsync(m => m.Id == id);
        if (person == null)
        {
            return NotFound();
        }

        _context.Person.Remove(person);
        await _context.SaveChangesAsync();

        return Ok(person);
    }

    private bool PersonExists(int id)
    {
        return _context.Person.Any(e => e.Id == id);
    }

    /// <summary>
    /// 受け口を POST に変換する
    /// </summary>

    [HttpPost(&quot;{id}&quot;)]
    [Route(&quot;Edit/{id}&quot;)]
    public async Task<IActionResult> Edit([FromRoute] int id, [FromBody] Person person)
    {
        return await PutPerson(id, person);
    }
    [HttpPost]
    [Route(&quot;Create&quot;)]
    public async Task<IActionResult> Create([FromBody] Person person)
    {
        return await PostPerson(person);
    }

}

Entity Framework から取得するように修正してあるので、ややこしくなっているけど、ASP.NET Core MVC のスキャフォールディングと合わせるように、Create や Update で通るようにしてある。
フォーム形式の場合は、Bind 属性で値を取るが、JSONやXML形式の場合は FromBody 属性でバインドする。このあたり、ちょっと混乱しているような気がする。

PeopleController クラスの Produces 属性は、デフォルトで返す Content-type を指定するらしい。

WPF クライアントを作る

本来ならば、Person, People クラスをアセンブリで共有するほうがいいのだが、ASP.NET Core は .NET Core のライブラリで、WPF クライアントは .NET Framework のライブラリなので共有できない。が、XML形式やJSON形式でシリアライズしてやり取りするだけなので、クラス名やプロパティ名だけ合わせておけばよい。

namespace ClientXml
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private async void clickGet(object sender, RoutedEventArgs e)
        {
            var hc = new HttpClient();
            hc.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(&quot;application/xml&quot;));
            var res = await hc.GetAsync(&quot;http://localhost:5000/api/people&quot;);
            var str = await res.Content.ReadAsStringAsync();
            textXml.Text = str;
#if false
            // ArrayOfPerson は取れない
            var xs = new System.Xml.Serialization.XmlSerializer(typeof(IEnumerable<Person>));
            var items = xs.Deserialize(new System.IO.StringReader(str)) as IEnumerable<Person>;
            textPerson.Text = &quot;&quot;;
            foreach ( var item in items )
            {
                textPerson.Text += $&quot;{item.Id} {item.Name} {item.Age} n&quot;;
            }
#endif
            var xs = new System.Xml.Serialization.XmlSerializer(typeof(People));
            var people = xs.Deserialize(new System.IO.StringReader(str)) as People;
            textPerson.Text = &quot;&quot;;
            foreach (var item in people.Items)
            {
                textPerson.Text += $&quot;{item.Id} {item.Name} {item.Age} n&quot;;
            }
        }

        private async void clickGetById(object sender, RoutedEventArgs e)
        {
            int id = 2;
            var hc = new HttpClient();
            hc.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(&quot;application/xml&quot;));
            var res = await hc.GetAsync($&quot;http://localhost:5000/api/people/{id}&quot;);
            var str = await res.Content.ReadAsStringAsync();
            textXml.Text = str;
            var xs = new System.Xml.Serialization.XmlSerializer(typeof(Person));
            var item = xs.Deserialize(new System.IO.StringReader(str)) as Person;
            textPerson.Text = $&quot;{item.Id} {item.Name} {item.Age}&quot;;
        }

        private async void clickPutById(object sender, RoutedEventArgs e)
        {
            var person = new Person() { Id = 2, Name = &quot;update person&quot;, Age = 99 };
            var xs = new System.Xml.Serialization.XmlSerializer(typeof(Person));
            var hc = new HttpClient();
            hc.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(&quot;application/xml&quot;));
            var sw = new System.IO.StringWriter();
            // 先頭の <?xml ... をカットする
            var settings = new System.Xml.XmlWriterSettings() { OmitXmlDeclaration = true, Encoding = Encoding.UTF8 };
            var xw = System.Xml.XmlWriter.Create(sw, settings);
            xs.Serialize(xw, person);
            var xml = sw.ToString();
            var cont = new StringContent(xml, Encoding.UTF8, &quot;application/xml&quot;);
            var res = await hc.PutAsync($&quot;http://localhost:5000/api/people/{person.Id}&quot;, cont);
            var str = await res.Content.ReadAsStringAsync();
            textXml.Text = str;
            var item = xs.Deserialize(new System.IO.StringReader(str)) as Person;
            textPerson.Text = $&quot;{item.Id} {item.Name} {item.Age}&quot;;
        }

        private async void clickPost(object sender, RoutedEventArgs e)
        {
            var person = new Person() { Id = 0, Name = &quot;new person&quot;, Age = 88 };
            var xs = new System.Xml.Serialization.XmlSerializer(typeof(Person));
            var hc = new HttpClient();
            hc.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(&quot;application/xml&quot;));
            var sw = new System.IO.StringWriter();
            var settings = new System.Xml.XmlWriterSettings() { OmitXmlDeclaration = true, Encoding = Encoding.UTF8 };
            var xw = System.Xml.XmlWriter.Create(sw, settings);
            xs.Serialize(xw, person);
            var xml = sw.ToString();
            var cont = new StringContent(xml, Encoding.UTF8, &quot;application/xml&quot;);
            var res = await hc.PostAsync($&quot;http://localhost:5000/api/people&quot;, cont);
            var str = await res.Content.ReadAsStringAsync();
            textXml.Text = str;
            var item = xs.Deserialize(new System.IO.StringReader(str)) as Person;
            textPerson.Text = $&quot;{item.Id} {item.Name} {item.Age}&quot;;
        }

        private void clickDeleteById(object sender, RoutedEventArgs e)
        {
        }

        private async void clickCreate(object sender, RoutedEventArgs e)
        {
            var person = new Person() { Id = 0, Name = &quot;new person&quot;, Age = 88 };
            var xs = new System.Xml.Serialization.XmlSerializer(typeof(Person));
            var hc = new HttpClient();
            hc.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(&quot;application/xml&quot;));
            var sw = new System.IO.StringWriter();
            var settings = new System.Xml.XmlWriterSettings() { OmitXmlDeclaration = true, Encoding = Encoding.UTF8 };
            var xw = System.Xml.XmlWriter.Create(sw, settings);
            xs.Serialize(xw, person);
            var xml = sw.ToString();
            var cont = new StringContent(xml, Encoding.UTF8, &quot;application/xml&quot;);
            var res = await hc.PostAsync($&quot;http://localhost:5000/api/people/Create&quot;, cont);
            var str = await res.Content.ReadAsStringAsync();
            textXml.Text = str;
            var item = xs.Deserialize(new System.IO.StringReader(str)) as Person;
            textPerson.Text = $&quot;{item.Id} {item.Name} {item.Age}&quot;;
        }

        private async void clickEdit(object sender, RoutedEventArgs e)
        {
            var person = new Person() { Id = 2, Name = &quot;update person&quot;, Age = 99 };
            var xs = new System.Xml.Serialization.XmlSerializer(typeof(Person));
            var hc = new HttpClient();
            hc.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(&quot;application/xml&quot;));
            var sw = new System.IO.StringWriter();
            // 先頭の <?xml ... をカットする
            var settings = new System.Xml.XmlWriterSettings() { OmitXmlDeclaration = true, Encoding = Encoding.UTF8 };
            var xw = System.Xml.XmlWriter.Create(sw, settings);
            xs.Serialize(xw, person);
            var xml = sw.ToString();
            var cont = new StringContent(xml, Encoding.UTF8, &quot;application/xml&quot;);
            var res = await hc.PostAsync($&quot;http://localhost:5000/api/people/Edit/{person.Id}&quot;, cont);
            var str = await res.Content.ReadAsStringAsync();
            textXml.Text = str;
            var item = xs.Deserialize(new System.IO.StringReader(str)) as Person;
            textPerson.Text = $&quot;{item.Id} {item.Name} {item.Age}&quot;;
        }
    }
}
namespace SampleWebApiXml.Models
{
    public class Person
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
    }
    public class People
    {
        public List<Person> Items { get; set; }
    }
}

試行錯誤した結果を載せているので、これが一番良いというわけではない。
いくつか、ポイントがあるのでざっと解説をしておくと、

– XML 形式で受信するために hc.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(“application/xml”)); を付ける。HTTP プロトコルの Accept ヘッダに受信する形式を指定しておくと、Web API が Accept にあわせて送ってくれる。
– POST や PUT で送信するときに、StringContent(xml, Encoding.UTF8, “application/xml”); のように Content-type を指定する。これを指定しないと、Web API 側で XML データとして認識してくれない。
– XmlSerializer は、何故か UTF16 でシリアライズするので、先頭の <?xml … を取り除くために、System.Xml.XmlWriterSettings で設定をする。ASCII 文字でしかテストしていないが、日本語を通す場合はきちんと string 型から UTF8 エンコードをしたほうがいいかもしれない.

うまく実行できると、こんな風にサーバ側を dotnet run で動かして、WPF アプリから送受信できるようになる。

きちっとした説明は別の機会にでも…

サンプルコード

動作できるサンプルコードはこちら
https://1drv.ms/u/s!AmXmBbuizQkXgfsQuu9Ly1NjTHa6nQ

最初に dotnet ef database update を使ってデータベースを作らないといけないかも。

カテゴリー: ASP.NET | ASP.NET Core MVC の Web API で XML 形式のデータを扱う はコメントを受け付けていません

micro:bit 互換機で夏休みの自由研究を

毎度のことながら夏休みの自由研究に親は悩まされるのですが、今年の夏は(建前上ながら)大丈夫です。と、言いますか、子供の自由研究めあてに micro:bit 互換機のモニターに応募して当たりましたので、そのまま有り難く自由研究に使わせて頂きました。micro:bit互換機の試作機モニターへのご応募ありがとうございました【スイッチサイエンスチャンネル】 ちなみに、互換機は MFT2016(今週末の土日です)に販売されるそうなので、MFT2016にて、micro:bit互換機「chibi:bit」のテスト版を販売します | スイッチサイエンス マガジン こっちのほうも是非。

micro:bit とは

BBC が英国の小学生向けに無償配布した電子工作キットです。BBC micro:bit : home から辿ると micro:bit で何ができそうかが分かります。中身が mbed なので、たぶんインターネット上の mbed 環境でも作れるはずですが、子供用に Scratch に似たような Block Editor を使ってプログラミングをします。

image

ブラウザ上で、ぽちぽちとブロックを重ねるのと、「run」ボタンを押すと左に出てるシミュレーターで動かすことができます。なので、1回ごとに実機に転送しなくてよいのです。けど、試行錯誤的にはこれで十分だけど、傾きセンサーとか音を鳴らすとかの動きの実際の動きまでは試せないので、やっぱり実機への転送は必須です。

まったく宣伝されてませんが、実は Block Editor は Microsoft が作っています(日本で話題になってないだけですかね?)。

image

また、使ってはいないのですが、JavaScript や python も使えるのでがりがり組みたい場合(?)は、そっちのほうがいいかもしれません。

まあ、小6の自由研究にいきなりプログラムコードを…と言っても敷居が高すぎるので、ぽちぽちと並べて作ります。このあたり Scratch に喰い付く子であれば子供だけでも進むんでしょうが、そのあたりは「自由研究」として、説明しながら手を出しながらという具合で進めます。

自由研究の様子

30分ほど micro:bit の解説をして、ぽちぽちとプログラミング状態に突入。ほんとうは、加速度センサーとか電子ブザーとか付ければいいんですが、準備もあるし pin の説明をするのもアレなので、A, B ボタンと、シェイクで LED を光らせるところだけやります。

image

5×5 の LED を光らせるのは簡単になっています。show leds のブロックを使うか、文字列(アルファベットだけど)を流れるように表示する show string のブロックを使います。サンプルコードには30分とか書いてあるけど、最初にやるとそれなりに時間がかかるし工作も入ると1日では済みそうもないので、そこは手抜きで(苦笑)。

変数を使ってとか、if文を使って、とかあれこれやると時間が掛かりそうなので、A/B ボタンを押したときの処理だけ羅列した状態です。LED の配置が連なっているだけでもアニメーションっぽく動きます。途中の pause はなくても、一定時間 LED は光る仕様のようなので省略。

実はループを使っていると、途中でボタンを押しても反応しないんですよね…、ってのが分かったので途中で反応するように組み替えてもいいんですが、それはまあ「自由研究」の範疇を超えるので、また時間があったらということで。

image

micro:bit はパソコンから micro USBケーブルで電源供給するか、コネクタに接続するかなんですが、ひとまず携帯用のモバイル電源で光るのでお試しでやってみます。後から micro USB 経由で電池から供給できるように改造します。そうしないと学校に持って行けないし。

image

単体で持って行っても何やら分からないので、micro:bit の解説と、ちょっとだけ仕様っぽいものを書いておきます。裏には先に書いた Block Editor の画面キャプチャを印刷して貼り付けておしまい。micro:bit の説明から開始して2時間弱で「自由研究」の終了です。まあ、この時間で自由研究にしていいかどうかはさておき、夏休みの自由研究の素材としてはよいかなと。

これからの micro:bit

ひとまず、手元の micro:bit 互換機は、夏休みの自由研究の作品として学校に持って行ってしまうので帰ってきたら、ということになりますが、せっかく BLE もついているし、加速度センサーと磁気センサーもついているので、このあたりは試しておきたいですね。あと、圧電ブザーを鳴らすとかワニクリップを使って定番っぽいものをやっておく予定。

カテゴリー: 雑談 | micro:bit 互換機で夏休みの自由研究を はコメントを受け付けていません

.NETラボ勉強会 in 仙台でロボットアームを動かしたよ

.NETラボ 勉強会 2016年6月 in 仙台 | .NETラボ まで手持ちのロボットアームを持って行って1枠話した来たので、そのひとり反省会。みなさまありがとうございました。

仙台での写真を撮り忘れてしまったのですが、

こんな材料を使って

こんなものを動かします。Raspberry Pi – Arduino – Robot Arm というややこしい感じになっていますが、Windows IoT Core in Raspberry Pi から直接は細かくサーボを制御できないので、Arduino を噛まします。Raspberry Pi – Arduino 間はシリアル通信なので、実は PC – Arduino で USB ケーブル接続でも可能なのですが…そこは Windows IoT Core を無理矢理使うということで。この部分をコマンド制御にしておくことで、ワタクシ的には ROS への練習とか PLEN2 への練習とかを兼ねています。あと、かつてロボットアームでスマホゲームを動かしたかったので、それも兼ねて。

プレゼン資料

Windows IoT Core and Robot Arm

[slideshare id=63470614&doc=windowsiotcore-160627045432]

デモ用のコード

https://1drv.ms/u/s!AmXmBbuizQkXgflgZRXM-PnmEla5ng

デモについて

ここ1年ぐらいは、LEGO EV3 を持って行ったり、Raspberry Pi を持って行って Windows IoT Core で Lチカしてみたりする訳ですが、IoT 自体の説明時間とデモの説明時間のバランスが難しいです。普通の PC の場合、キーボードとモニタで完結することが多いので、自分としてはデモで「モニタの外側」に出ることを意識してやってる…んですが、自分でやってても「だから何?」の疑問が付きまといます。なんか、ソフトウェア技術的にすごいものと組み合わせてあれこれとハードを動かせれば「すごい」感じがするんでしょうが(まだできないけど)、でもそれだと「すごい」だけで終わっちゃうしなー、という感じ。

今回のロボットアームの件で言えば、

  • 適当なモーションを作って、暫く動きっぱなしでもよかったかも。
    • 1コマンド打ち込みだけだと、6軸同時に制御する、ってのがわかりづらい。
  • やっぱり、Raspberry Pi から大型モニタに出したほうがよい。
    • HDMI がないところが多いので、VGA変換ケーブルを買って試しておく。
  • 説明なしで、コントローラはタブレット(Xamarin)で良いかも。
    • あまり説明なしだと「何かのおもちゃが動いているだけ」になるけど、ポイントだけ解説するとか。
  • 当日、配線するのは結構大変なので(大抵、事前に30分ぐらいしか取れない)、そこだけは基盤にしないと駄目かも。

 

ソフトウェア屋さんがハードを触るときの最初のハードル

西村さん と話して、盛り上がったのでメモ的に

  • ハード特有の用語がわからん。そもそも、ブレッドボードが何か?とか。
    • これは、ソフトウェアの専門用語と同じだから、電子工作のはじめて本で覚えるしかないかな。私はオライリーの「Arduino をはじめよう」が最初の一冊です。
  • 電子部品は何処で買うのかわからない。
    • 昔は、秋葉原に行けばあったけど(今でもあるけど、数が減ったから探しあたる必要があり)、地方の人はどうしているんだろう。むしろ通販だから前よりよくなった?
  • 電子部品を何を買うのかわからない。
    • いさぎよく、最初は「初心者キット」(ちょっと割高だけど)を買うのがベター
    • その後、古めの電子工作の本を買ったのだけど、電子部品の番号が今は売っていなくて互換品を自分で探す必要あり。
  • だいたい2か月頑張れば、次に進める気がする。
    • プログラミングと同じで「慣れ」が必要かなと。キーボードじゃなくて、ブレットボードとジャンプワイヤーで手を動かすので、そっちの方面の「慣れ」が必要。
  • Raspberry Pi, Arduino で得意分野が違うので、両方やっておいたほうが良い。
    • Raspberry Pi だと画像処理とか通信処理とかが楽。
    • Arduino だとマイクロ秒単位の細かい制御ができる。
    • ブラウザ経由でLチカできる、ってのもアリだけど、直接ハードを触って「どんなことが起きているのか」を知っておいたほうがいいかな。

ロボットアームのその先

実験的に自前シリアル通信を作ったけど、Firmata を使うか ROS にしたいですよね。少しずつそれっぽい感じにする予定。ロボットアームからのフィードバックが必要なので、ポテンショメータの値の取得と、加速度センサーの追加、あと Raspberry Pi 側からカメラを引っ張ってきて画像解析できるように、ってところまでが目標。

カテゴリー: Win IoT | .NETラボ勉強会 in 仙台でロボットアームを動かしたよ はコメントを受け付けていません

PLEN2 が到着して初期設定まで

本当は自作ロボットがいいのですが、チートして Kickstarter 経由で PLEN2 を購入しました。日本では Kibidango  のほうが先なのですが、組み立てキットの早期募集が終わっていたので値段が同じぐらいだった Kickstarter のほうに応募。3D プリンタは持っていないのですが、せめて組み立てるということで。発送は日本郵便できました。

開封

元になった PLEN よりもちょっと小さ目です。身長20cm位なので、サーボも小さいですね。バッテリーは充電式の単四x5なもので動かします。胸の部分に充電器、背中の部分に Arduino が乗っている状態になります。

開けて中身を確認したものの、途中で水圧式のロボットアームの組み立てをしているので間があいていますが、組み立て自体はだいたい半日仕事じゃないかなと。特にロボットの組み立てが始めての場合、サーボの基準値とかはめ込みの方向とかあれこれ悩むのでちょっと時間が掛かります。

組み立て

Tutorials [PLEN Playground – Wiki]
http://plen.jp/playground/wiki/tutorials/index.html

に従って、Firmware のインストールからスタートします。バッテリーは既に充電済みなので、即つなげて試すことができました。Arduino にインストールした後に、サーボを繋いでスイッチを入れてピニオンギアにはめ込んで、を18回繰り返します。サーボが18個ありますからね。

サーボに電源を入れた状態で、

な感じで十字の軸を揃えます。揃えますが、きっちりと揃う訳ではありません。どうしてもサーボの基準点とピニオンギアの歯の山とのずれがあるので、ちょっとだけずれます。

この基準点は後でアプリを使って調節していくので、だいたい揃えておけばokです。

ひとまず、こんな感じまで完成させて、電源を入れない状態で立つぐらいまで仕上げます。ロボットアームだと、アームの部分の自重で電源を入れないと関節部分(サーボ部分)で曲がってしまうのですが、PLEN2 の場合は、足裏に重心が乗っかる感じなので、電源が入らない状態でもそのまま立てます。

サーボの原点位置を調節する

背中のスイッチ(Arduino 側とサーボ電源の両方)に電源を入れた状態だと、先のサーボの原点位置がずれているので、ちょっとずれた感じになります。私の場合は、なんか前傾姿勢になっていて、倒れそうな感じになっているし、足もちょっとがに股っぽくなります。実際、このまま歩きのモーションを入れると倒れます(苦笑)。

Tuning Up Home Positions [PLEN Playground – Wiki]
http://plen.jp/playground/wiki/tutorials/plen2/tune

にある ControlServer を使ってサーボの基準点を調節します。Windows の場合は、ControlServer.exe を起動して、PLENUtilities.url でブラウザから調節します。

こんな風なコンソールのサーバーが立ち上がって USB ケーブル経由で PLEN2 のサーボを調節します。

image

ブラウザのほうで、調節したいサーボの番号(位置)をクリックして、Angle をマウスで動かします。このとき PELN2 の実機が動くのでまっすぐになるようにします。調節が終わったら、Home ボタンを押して、そのサーボの設定を PLEN2 に送ります。これをすべての関節分(18回)繰り返します。現状のバージョンでは、サーボの1と13が逆になっています。モーションは問題ないので、ここの表示だけの問題かなと。

image

サーボから角度が送られてくるわけではなく、一方的にサーボ側に角度を通知するだけなので、再び Reset ボタンを押して0度を送ってから調節します。0度から大きく外れてしまった場合は、サーボを外してピニオンギアへのはめ込みをやり直したほうがよさそうです。単位が1/10 deg.(度)なので、歯車1個分(300位?)を目安にすればいいでしょう。

調節するときは、手足がまっすぐになるように、寝かせた状態か手でつりさげた状態でやるとやりやすいです。

ジーっと音がしなくなるまで調節する

サーボの位置を調節しているときに「ジー」という音が鳴ることがあります。これはサーボが鳴っているのですが、ただ立っているだけなのに鳴っているのはうるさいので、音がでなくなるように調節します。

何故、音が鳴るかというと、理由が2パターンあります。

  1. サーボが位置を変えようとする。
  2. 角度が限界までいっているので、それ以上回せない。
  3. けれども、位置設定まで動かそうとするので、モーターがジーっと鳴る。

 

  1. サーボの位置を設定する。
  2. 歯車と重心位置の関係で、ちょっとだけ行き過ぎる。
  3. サーボが位置を戻そうとするが、重さの関係でぎりぎり戻らない。
  4. でもってモーターがジーっと鳴る。

のパターンです。

最初のパターンは可動域よりも大きく廻し過ぎるときで、PLEN2 の場合、股(4,16)、膝(6,18)、足首(8,20)のところで音が鳴りやすくなっています。股の可動域が PLEN 狭すぎるような気がするので、ここは 3D プリンタで要調節かなと思います。

後者の重心が関わる場合は、サーボの角度の分解能に関わるものなので、完全に調節するのは無理です。PELN2 を立たせた状態で、音が鳴らないように膝などを調節します。歯車の遊びの部分があるので、ここは実機調節になります。

既存のモーションを試してみる

iPhone か Android のアプリを入れて既存のモーションを動かしてみます。いきなり、歩かせるとまだバランスが悪くて(微調節が必要な状態)ばたっと倒れますが、踊りのほうはなんとか動きます。モーションが PELN 用なのか、サッカーのモーションを PELN2 に適用すると倒れます(苦笑)。ここが股関節と足首のサーボの可動域の問題かなと。

 

 

 

自分でモーションを作る

自前のモーションは http://plen.jp/playground/motion-editor/ で作れます。可動範囲の制限がないので、コツがいるのですが、まあ、そのあたりはおいおいに。

できあがったモーションは JSON 形式で保存されるので、角度などをエディタで変更できます。

PLEN2 へのアップロードは Releases · plenprojectcompany/plen-MotionInstaller_Win を使って USB 経由で送信します。Arudino 側のスロットに割り当てる方式なので、モーションの空きスロットに転送します(あるいは上書き)。

PLEN2 の各コードは MIT ライセンスで github で配布されているので、自前で修正が可能です。モーションの実行は、ストアアプリ版がないので、プロトコルを見て自前で作ろうかなと思っているところです。

 

 

そんな訳で、ひとまず PLEN2 の組み立てからちょっと動かすところまで。これから先は自前でアプリを作るところからスタート。

  • Windows アプリでモーションの転送
  • Xamarin で手足を個別に動かすツール(モーションの作成用)
  • 加速度センサー(AccelerationGyroSensor)を使った制御(あるのかな?)

な感じです。

カテゴリー: PLEN2 | PLEN2 が到着して初期設定まで はコメントを受け付けていません

Windows IoT Core にリモートで接続する

世の中?は、dotnetConf で盛り上がっていますが、Windows IoT Core のリモート接続の方法を残しておきます。実は、まだ Inside Preview の状態でしか使えないのだけど、Closed Loop Control, Remote Sensors and Remote UX on RPi3 – Hackster.io のサンプルを見ると既に3月からできていたわけで、つまりは //Build/ 2016 の頃からできました、って話ですね。Build で Windows IoT Core の話があったかな…というのは後で調べます。

Remote Display Experience – Windows IoT

Remote display experience

そんな訳で、3か月遅れで試してます。が、結論から言えば、Windows 10 Inside Preview (現状では Slow Ring)を使うのと、Visual Studio で作成する UWP のバージョンが微妙に低いのと、Windows IoT Core 自身のバージョンが微妙に高いのとの兼ね合いで、画面のリモート接続はできるけど、リモートデバッグができません。つまりは Visual Studio からデプロイできないので、なんか変な感じで食い違っていますが、まあ、夏の正式リリースまでには揃えられるかと。というか、揃わないと困る。

Raspberry Pi 3 に Inside Preview 版を入れる

Windows IoT Core の Inside 版は Get Started – Windows IoT から、Raspberry Pi 3 の NOOBS 版からインストールするか、https://www.microsoft.com/en-us/software-download/windowsiot から直接ダウンロードします。どうやら、現時点で RPi3 対応は Inside Preview 扱いなので(正式リリース版ではない)どちらの方法でも 、14342 が使えます。NOOBS 版のほうは、微妙にサイズが大きくなって SD カードは 16 MB が必要です。

リモート接続を有効にする

ブラウザから http://miniwinpc:8080/ で接続すると「Remote」というメニューが増えています。このチェックを入れると、リモート接続が有効になります。

image

リモートアクセスのアプリを入れる

https://www.microsoft.com/en-us/store/apps/iot-remote-client/9nblggh5mnxz から Windows IoT Remote Client をインストールします。

無事接続できると、こんな風に Raspberry Pi 上のモニタが表示できます。マウスでぽちぽちすることもできるし、タッチパネルがあればタッチ操作ができます。

image

ただし、全画面転送しているらしくて妙に遅かったりします。現時点では解像度を 800×600 に落とすと使えるぐらいのスピードになります。

 

 

 

以下は、リモートデバッグしようと思ってあれこれやった結果です。

 

Windows のほうも Inside Preview 版にする

Windows IoT Core のアプリは UWP なのですが、このターゲットバージョンを揃えないといけません。さっき、14342 を入れたのだから、Windows 10 PC のほうも、14342 か、それ以上にします。現状では Slow Ring にして Windows Update すると自動的に入ります。

業務 PC に入れるのは難なので、Hyper-V 上に Windows 10 の環境を作ってバージョンを揃えています。

Windows 10 SDK を入れる

ホーム ページ – Windows Insider Program に参加して、プレビュー版の Windows 10 SDK をインストールします。実は、ここのバージョンが 14332 なので、PC や Windows IoT Core のバージョン 14342 よりも低いんですよね。たぶん、このおかげで Windows IoT Core に対してリモートデバッグができない状態なのだと思います。

でもって、Windows 10 SDK 14332 を入れた状態で Visual Studio からデプロイしようとすると、

image

2>DEP0800 : 必要なフレームワーク “C:\Program Files (x86)\Microsoft SDKs\Windows Kits\10\ExtensionSDKs\Microsoft.NET.CoreRuntime\1.0\.\AppX\ARM\Microsoft.NET.CoreRuntime.1.0.appx” のインストールに失敗しました。

のようなエラーが発生します。このエラー自体は、StackOverflow でよく見かけるのですが、解決できていません。大抵の回答は、SD カードの焼き直しなんだけど、どうもバージョン違いのような気がする。

アプリのデプロイ自体は、ブラウザから「Apps」→「Install app」で、appx ファイルをアップロードすれば ok です。このあたりはストアアプリと同じなので、デバッグはできませんが、ひとまずアップロードして Windows IoT Core 上で動かすことはできます。

image

そんな訳で、中途半端ですが、ひとまずリモート接続ができるところまで。

カテゴリー: Win IoT | Windows IoT Core にリモートで接続する はコメントを受け付けていません

WordPress に XSS が埋め込まれてから復旧まで

ざっと記録として残しておきます。

5/30 に悪質サイトに登録される

仕事上、執筆やらプログラミングの折りには自分のサイトを Google で検索するのが多いのですが、5/30 の朝に検索すると悪質サイトとして登録されていました。

Google ウェブマスターからはこんな感じ。

image

なんぞ、クラッキングされたのか?と思ったものの、直で moonmile.net からアクセスして当 blog にアクセスすると大丈夫。でも、Google 検索をさせると駄目という状態で。なんら表面上は大丈夫なように見えるんですが、妙にアクセスが遅いのが気になるところでした。ページにアクセスしようとすると、windows defender が警告を発する状態です。

ソースコードが保存できない

表面上、何もないということは XSS を仕組まれたか何かなので、ソースコードを見ます。Firefox ではガードされて見れなかったので、ひとまず Edge で見てエディタに貼り付けて保存しようとすると、windows defender が警告を出します。ってことは、コード自身に何かがある訳ですよね。

そんな訳でちょっとずつ HTML コードを削っていくと、妙な javascript が head タグにくっついているのを発見。それを削ると保存できるので、どうやらここが原因のようです。コード自体は公開できないのですが(悪質サイトになってしまうし)、kicrea[.]it に情報を送っています。

こんなスクリプトが wp_head() の後ろに埋め込まれます。IP のところは怪しいサイトに誘導されています。

image

wordpress の header.php を見る

google の Search Console https://www.google.com/webmasters/tools/home?hl=ja 名にたくさんのページが改竄されています。URL を見るとRSSまで改竄されているのと、先の head タグに書きもまれているので、どうやらヘッダ部分があやしいと推測します。

案の定 header.php に XSS が仕込まれているので削除します。

問題は、ここの blog のテーマだけでなく使っていないテーマの header.php や、相乗りしている header.php にも改竄がはいっていたことです。アクセスログを見ても wordpress からログインした形跡がないし(admin は消し去っています)、そもそも使っていないテーマの header.php を改竄すること自体おかしいし、ファイルの日付を見ると変更されていないので、どうやらターミナルコンソールのほうで何かあった感じ。

フォルダをリネームして google へ再申請

ひとまず、Google 検索で悪質サイトが出続けるのも困るので、フォルダをリネームして、Google に再申請します。元ページを削除した旨を連絡して、まっさらな index.html だけおきます。再申請のチェックは 12時間ぐらい掛かるので、まあ、早めに手を打っておこうかと。

たぶん、共有サーバーだから?

いくつかのサブドメインで運用している wordpress の header.php もやられてしまったので、これも修正します。当然、それらのドメイン(dotnetlab.net や openccpm.com)も悪質サイト扱いになっているので、同じように再申請しておきます。

このブログが乗っているのは、共有サーバーなのでたぶん100名ぐらいが同時に使っているんですよね。改竄された形跡から考えると、wordpress 経由とは考えづらく、ターミナルのパスワードが盗られたとも考えにくいので、共有の他のユーザーから漏れたのかなと。ただし、header.php のパーミッションは 644 -rw-r–r– なので、以前ロリポップ等でやられてしまったようなグループからは外れていると思うんですけどね。ルート権限からユーザーのパスワードが抜かれてしまったのかもしれません(ホスト側から連絡がないのでわからず)。

ひとまず、ターミナルへのパスワードを変更して、header.php を手作業で修正して様子見という状態です。2日間経って元に戻る気配はないので、ここで対処は終了。現時点では google から以前通りアクセスができます。

これ、個人の相乗り wordpress だったからよかったけど、商用サイトとかアフリエイトサイトとかだったら復旧が大変だったろうなと。

カテゴリー: トラブルシューティング | WordPress に XSS が埋め込まれてから復旧まで はコメントを受け付けていません

和風ペンタとブルーノートの比較の続き

和風ペンタトニックとブルーノートは酷似している | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/7893

の続き。タブ譜だけでは分かりづらいので、実際に弾いてみます。ブルーノートを弾いていてもちょっと和風ペンタっぽくなるのは私が日本人だから、ってことで勘弁してもらって、実際弾いてみるこんな感じになる。

和風ペンタとブルーノートの比較

ヨナ抜き(ファとシ)を覚えると大抵の童謡/民謡がこの音に乗っているのが分かる。ただし、完全に乗っているわけではなくてちょいちょいアクセントをつけるのがミソなんだけど、基本はヨナ抜きになる。サザエさんのオープニングもそうだし、ソーラン節もそうだし、植木等のスーダラ節もヨナ抜きになっている。実際に弾いてみると解るけど、ギターでヨナ抜きで上がり下がりをしていると無限に曲が沸いてくる、というか人工無能的にランダムに音を出したとしても和風ペンタに乗っている限りなんとなく曲に聴こえるから不思議だ。

で、ヨナ抜きのスケールをちょっとずらしたのがブルーノートになる。基本は同じスケールになるので、日本人にはヨナ抜きもブルーノートも時に同じように聴こえる。基音が違うので、そこだけ強調するのと、それっぽいリズム(和風ペンタの場合は音頭とか演歌風に弾く、ブルーノートの場合はブルース風にチョーキングを加える)になると、途端にそうなるから不思議だ。

動画の後ろのほうでは、ひとつの曲(っぽいもの)に和風ペンタとブルーノートを混ぜている。強引に混ぜるものだから途中でコードが変わったように聴こえるけども、実際は同じスケールを上がり下がりしているだけになる。

ちなみに、ブルースケールも、どんな風に弾いても大丈夫な感じなのは和風ペンタに似ている。

オレオレブルース G開放弦で – YouTube

ライトン・ホプキンズとジョン・リー・フッカーの真似事をしてみる。

ほんものはこっち

Baby, Please Don’t Go – Lightnin’ Hopkins – YouTube

Lightnin Hopkins – Lightnins Blues – YouTube

https://www.youtube.com/watch?v=DCSacpEIYWY

カテゴリー: ギター | 和風ペンタとブルーノートの比較の続き はコメントを受け付けていません

zo-3 のボリュームを交換する

yahoo! オークションで底値 2,400円也で手に入れた zo-3 なのですが、内蔵スピーカーから音が鳴ったり鳴らなかったりします。ボリュームを動かしてちまちまラジオのチューニングみたいなことをやると、がりがりという音と共に音が鳴るようになったりするのですが、ちょっと動かすとすぐに鳴らなくなります。どうやら、増幅回路んほうは大丈夫そうなので、ボリューム(ポテンショメーター)の交換をします。

image

amazon で見ると、エレキギターのボリュームスイッチは 500 ~ 2,000円で売っているようなのですが、そんな高価なのはいらないので秋月の 10kΩ のポテンショメータ 30円也を使います(どうやら、25kΩらしいんだけど、実測すると 10kΩちょっとだったので、手元にあるものでいいや、ということで)。

小型ボリューム 10KΩB: パーツ一般 秋月電子通商 電子部品 ネット通販
http://akizukidenshi.com/catalog/g/gP-00246/

裏側にある 9V 電池ボックスを取り外すとボリュームで使っているポテンショメータが見えます。これは交換した後に写したものです。

image

元についていたのがこれ。

  • ピックアップの線
  • 回路への線
  • コネクタケーブルへの線
  • グランド

が入ってくるので、まぜこぜになりますが。元のボリュームを参考にして配線&半田付け。一回、白い線を間違えて「あれ、鳴らん…」と焦ったは内緒です。テスターを持ち出してあれこれ調べました。

image

zo-3 の良いところは、スピーカーが内蔵されているところでアンプが無くても鳴らせるところです。定価だと 3万円ぐらい。中古だと1万円位で買えます。オークションだと 5,000円前後まで下がりますが塗装剥げや鳴らなかったりするのが曲者です。

回路はアナログ増幅回路だけ(だと思う)ので、適当なエフェクトを自作できるかなと思ったり思わなかったり。スペース的には RasPi Zero か Arduino Nano あたりが入りますね。いっそのことスピーカーを外してしまって、液晶モニタを埋め込むのもアリかもしれません。

image

この zo-3 が底値で買えたのは(競合が誰もいなかった)、弦がなかったのとナットが無かったからですね、たぶん。ネックの先のほうで弦を支えているのがナットです。…って、私も交換したことはなかった(交換するものとは思わなかった)ので、いくつか調べて池袋のギター屋さんで買いました。いくつか専用の器具がいるらしいのですが、面倒なので鉄やすりと糸のこで調節しています。ちょっと、削りが甘くて(疲れてしまって)弦高が高めなのですが、まあブルースを弾く分にはいいか、というのでそのままです。

image

そんな訳で、きれいに音がなるようになった zo-3 です。

指板のすれは、以前使っていた人のものです。きれいに12フレットまですれているので、きちんと弾き込んで使っていたものですね。フレットは減っている感じですが、まあ構わない。ネックの反りはないので、たぶん弦を外して、どこかでナットが外れてしまったまましまい込んでしまったのかなと。

ひとまず、ライトニン・ホプキンスとジョン・リー・フッカーの真似事からスタート。和風ペンタとブルーノートの組み合わせは後日。

カテゴリー: ギター | zo-3 のボリュームを交換する はコメントを受け付けていません

和風ペンタトニックとブルーノートは酷似している

プログラミングするときの手癖もあるのだから、ギターで作曲をするときの手癖があるだろうと感じで、あれこれやっていたらやっと原因が分かったという話です。

和風ペンタ(日本五音階)については下記を見てもらうことにして(’80年代は YMO のテクノブームがあったので、そのあたりから流れたところが真相っぽいですが)、

和風ペンタトニック・スケール(ヨナ抜き)~日本人の深層意識に響く民謡・演歌な音階:のびやかな暮らし
http://bossanovaday.hamazo.tv/e5967160.html

同じく ‘80年代の頃にギターを覚えたときに、何故かブルーノートのスケールを弾いているのに途中で演歌っぽくなるのが不思議でした。

 

image

でもって、改めて開放弦を活用して書き出してみたのがこれ。コードを押さえてじゃがじゃがやらないので、開放弦なんか使わないのだけど、ブルーノートで E コードで弾いたときと、和風ペンタの G コードがほぼ同じなんだな。なので、途中で三連符を打ったときに、ブルーノートから和風ペンタになって、何故かブルースから演歌に移行してダサい感じになってしまうのです。

ジャズとかブルースは普通、G か A で演奏するので(A のほうがフレットが狭くて少し抑えやすい)のと、E はカントリーでよく使われる(開放弦をじゃかじゃかやる)。日本人以外には、和風五音階はなじみがないので、そっちには勝手に動かないけど、日本人には民謡が染みついているから、そっちにスライドしてしまう。1音半ずらせばいいので、あれこれと曲を弾いている間(ベース音がわからない間)は、ブルーノートなのか和風ペンタなのかが区別がつかないわけです。そして、どちらかのベース音が響くと、その瞬間にブルーノートになったり和風ペンタになったりする。

この部分は、実際に音を聴かないとわからないので、またあとで。

服部良一から渡辺岳夫の流れで完成された?

仮説ですが、ジャズやブルースを持ち込んだ服部良一が作った民謡や東京ブギウギを踏まえて、渡辺岳夫が作った「アタック No.1」や「魔女っ子メグちゃん」、「キューティハニー」を通して、その後のアニメ曲が作らてしまったのではないか、と考えています。まあ、最近のアニメの主題歌は JPOP だったりロックだったりするので、そこから外れていますが、いわゆるアニメっぽくない主題歌(ロボット系とかヒーローものは別)の感じはそこから出てきているのかなと。

編曲した後は別なんですが、主旋律を追っていくとギター特有の手癖が出てきます。運指の都合と、弾きやすさの都合から、ああやっぱりこの装飾音が効いてくるってな感じです。このあたり、同時期のアニメ曲と戦前からのジャズ/ブルーノートの流れを追っていこうかなと、主旋律だけを流してみたり。

人工知能的に和風ペンタを眺める

ここで、人工知能的に和風ペンタを眺めてみます。この話は、Electroharmonix フォントが何故、日本人に読めないのかを Tesseract-OCR で紐解く と同じです。Electroharmonix フォントは、カタカナをもとにしたアルファベットフォントなのですが、カタカナを知っている日本人には「カタカナ」に見えてしまって却って読めない。逆に、カタカナを知らない日本人以外にはアルファベットとして読める(と同時に、異国の「カタカナ」という文字にも似ているように思える)という訳です。OCR 学習をさせると、当然ですがカタカナを覚えさせたときには、カタカナにマッチさえつつ Electroharmonix フォントを読もうとするし、カタカナを教えずにアルファベットだけならば、アルファベットとして読もうします。

同じことが、ブルーノートと和風ペンタにも言えて、日本の民謡で育った日本人(育ってないひともいるからw)には、ブルーノートを弾いても、和風ペンタとごっちゃになってしまう。逆にそもそも日本の民謡を知らなければ、単に不思議な音階でしかなくてブルーススケールはブルースケールのままです。

試しに、ブルースの G コードでたらたらとフリージャズっぽくやっている間に、和風ペンタのヨナ抜きを入れると一気に演歌に聞こえてきます(リズム的にそうするのもあるけど)。でも、G のベース音を強調するように音を入れ替えるとブルースになるという不思議…というか、本能的にそうなってしまうところがあります。

で、機械学習で絵画とか文章はあるけど、音楽はちょっと難しいですよね。それらしく聞こえるとか、曲がいいとか悪いとか、そういうのはどういう「評価関数」を作るのか、どうやって「聴く」のかが思い描けないところがあります。Jukedeck – Create unique, royalty-free soundtracks for your videos. というのがあって、ある程度の曲調(コード)と転回だけ合わせておけばイージーリスニングっぽいものが量産できます。おそらく、既存の曲あるいは譜面を入れて、ブルースっぽくするとか演歌っぽくするのも可能かな(スケールとリズムを合わせれば、それっぽく聴こえるので)と思われるのですが、では、そこで「ブルースのように聴こえる」あるいは「和風ペンタのように聴こえる」とはどういうことか?という点が気になりますよね。

和風ペンタとブルーススケールの混在は日本人にしかないのだから、「日本人的な耳」を作って評価関数を作れば「既存のブルースが和風ペンタのように聴こえてダサい」と評価だろうし、「欧米人の耳」を作れば「ブルースだけに聴こえる」という感じになるかもしれません。このあたり、評価関数の取り方≒学習アルゴリズムになるので、将棋ソフトと同じようにソフトとによって戦略が異なる(将棋の場合、勝ち負けが明確なので、トップ以外の評価が難しいのですが)≒人によって指し方が異なる≒好みがある、と言えるでしょう。

ってなところまで考えたのですが、続きはもう少しブルーススケールとカントリーを演奏ってみてから。あと戦後のアニメ曲も。

カテゴリー: 雑談 | 和風ペンタトニックとブルーノートは酷似している はコメントを受け付けていません

Xamarin.Forms を XAML を使って書くために

昨日の続きを少し書き足しておきます。

Visual Studio Community 2015 で Xamarin.Forms を使う | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/7873

Xamarin.Android/iOS を使ってそれぞれのプラットフォームに向けて C# で書けるのはそれはそれで充分です。UI にしても最新機種にあわせる場合は、Java/Swift で書くだろうし、iOS のほうはタイムラグ無しで Xamarin.iOS が出ていたし、Android のほうも数週間ほどで追随してきます。おそらく、Xamarin が Microsoft に買収された今後も同じ方針でしょう。同じ方針だけど、プラットフォーム三社の思惑があるし、そこはどう動くかわからない。ただし、あまり「最新機能」にとらわれず、Android/iOS かつ UWP の3つのプラットフォームに対して同時にリリースすることを考えると Xamarin.Forms が非常に有効に働きます。v1 の場合は、実質 Android/iOS の二機種だったけど、v2 からは UWP が入ったので Android/iOS/Windows の三機種なのでメリットが大きいのです。

サンプルは XAML がコードで書かれている

Cross-Platform で PCL や Shared をすると、UI のサンプルコードも一緒についてきます。単純に画面に “Welcome to Xamarin Forms!” と表示するだけのものです。PCL(移植可能)なプロジェクトの App.cs に入っています。

public App()
{
    // The root page of your application
    MainPage = new ContentPage
    {
        Content = new StackLayout
        {
            VerticalOptions = LayoutOptions.Center,
            Children = {
                new Label {
                    XAlign = TextAlignment.Center,
                    Text = "Welcome to Xamarin Forms!"
                }
            }
        }
    };
}

見慣れたような見慣れないような、スタイルで ContentPage を構築します。WPF などでデザイナを使って XAML を書いたり、Android で AXML を使ったり、Xcode で storyboard を使ったりしていると「え?」な気分になるのですが…まあ、え?ってなります。

最初に断っておきますが、モバイル機種のような小さい画面の場合には、結構 XAML のコードベースで作業をするほうが楽だったりします。PC などの液晶モニタに対して画面が小さい(解像度は同じだったりするけど)ので、操作するボタンとかアイコンを細かく配置するよりも、おおざっぱに位置を自動計算させたほうがうまくいきます。また、Android 機種は画面がまちまちなのでそれに揃えるためにもフローレイアウトみたいな感じで作るといいですよね、って感じです。Web のグリッドシステムと同じ感じで作るほうが手早いし、Xamarin.Forms との相性もよいです。

Forms Xaml Page を追加する

これをXAML形式で書けるようにします。

  1. PCL のプロジェクトに「Forms Xaml Page」を追加します。

PCL プロジェクトに Page1.xaml が追加されます。

  1. Label の Text プロパティを「Welcome to Xamarin Forms!」に書き換える。
<?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?>
<ContentPage xmlns=&quot;http://xamarin.com/schemas/2014/forms&quot;
             xmlns:x=&quot;http://schemas.microsoft.com/winfx/2009/xaml&quot;
             x:Class=&quot;App4.Page1&quot;>
  <Label Text=&quot;{Binding MainText}&quot; VerticalOptions=&quot;Center&quot; HorizontalOptions=&quot;Center&quot; />
</ContentPage>

Binding を使って ViewModel を使うようになっていますが、ここでは Text プロパティを直接変更します。変更はちょっとずつやると解りやすいので。

<?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?>
<ContentPage xmlns=&quot;http://xamarin.com/schemas/2014/forms&quot;
             xmlns:x=&quot;http://schemas.microsoft.com/winfx/2009/xaml&quot;
             x:Class=&quot;App4.Page1&quot;>
  <Label Text=&quot;Welcome to Xamarin Forms and XAML!&quot; VerticalOptions=&quot;Center&quot; HorizontalOptions=&quot;Center&quot; />
</ContentPage>
  1. App.cs のコンストラクタを書き換える。

追加した XAML が表示されるように App コンストラクタを書き換えます。

public App()
{
    // The root page of your application
    this.MainPage = new Page1();
}

MainPage プロパティに表示したいページクラスを設定すれば ok です。名前が「Page1」になっています。

これをビルドして実行するだけでエミュレータ上での動作が変わります。

試しにボタンのクリックイベントを付けてみる

XAML界隈では嫌われ者のコードビハイドですが、まあ手軽に作るにはこっちのほうが便利です。

XAML を書き換えて、x:Name プロパティにボタンとラベルに名前をつけます。これで、Page1 クラスのプロパティに追加されます。

<?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?>
<ContentPage xmlns=&quot;http://xamarin.com/schemas/2014/forms&quot;
             xmlns:x=&quot;http://schemas.microsoft.com/winfx/2009/xaml&quot;
             x:Class=&quot;App4.Page1&quot;>
  <StackLayout>
    <Label x:Name=&quot;text1&quot; Text=&quot;Welcome to Xamarin Forms and XAML!&quot; />
    <Button x:Name=&quot;btn1&quot; Text=&quot;Click Me&quot; />
  </StackLayout>
</ContentPage>

コードビハイドを書く。

public partial class Page1 : ContentPage
{
    public Page1()
    {
        InitializeComponent();
        this.btn1.Clicked += Btn1_Clicked;
    }

    private void Btn1_Clicked(object sender, EventArgs e)
    {
        this.text1.Text = "クリックした";
    }
}

ボタンの Clicked イベントにラムダ式で書いても良いし、イベント用のメソッドを作ってよいでしょう。

実行すると、こんな感じでボタンが効くようになります。

現時点の Hyper-V のエミュレータのほうは、以下のようなエラーが出て2回目以降のコード変更が反映されません。エミュレータ再起動するか、Android 内で対象のアプリをアンインストールするかの対処が必要です。

04-06 11:01:12.659 D/OpenGLRenderer( 2235): Use EGL_SWAP_BEHAVIOR_PRESERVED: true
04-06 11:01:12.661 D/GLHostConnection( 2235): Waiting for host to establish connection for PID 2235 (App4.Droid)
04-06 11:01:12.662 D/GLHostConnection( 2235): HostConnection::get() New Host Connection established 0x9c319f40, tid 2235
04-06 11:01:12.687 D/GLHostConnection( 2235): Waiting for host to establish connection for PID 2235 (App4.Droid)
04-06 11:01:12.711 D/GLHostConnection( 2235): HostConnection::get() New Host Connection established 0x9c6e3050, tid 2260
04-06 11:01:12.713 I/OpenGLRenderer( 2235): Initialized EGL, version 1.4
04-06 11:01:12.719 W/EGL_emulation( 2235): eglSurfaceAttrib not implemented

MVVM化する

このままコードビハイドで書いていっても良いのですが、どうせなので Binding を使って MVVM 化します。Clicked の ICommand はそのままにして、Label のほうだけ。

  1. ViewModel クラスを追加する。

PCL プロジェクトに ViewModel のクラスを追加します。いろいろ引き継いできた BindableBase を継承します。
ラベルに表示する文字列を Text プロパティで連携させます。

using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace App4
{
    class MyViewModel : BindableBase
    {
        private string _text;
        public string Text
        {
            get { return _text; }
            set { this.SetProperty(ref this._text, value); }
        }
    }
    public abstract class BindableBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
        {
            if (object.Equals(storage, value)) return false;

            storage = value;
            this.OnPropertyChanged(propertyName);
            return true;
        }
        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            var eventHandler = this.PropertyChanged;
            if (eventHandler != null)
            {
                eventHandler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}
  1. XAML で Binding を使う

Label の Text プロパティを Binding に変えます。

<?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?>
<ContentPage xmlns=&quot;http://xamarin.com/schemas/2014/forms&quot;
             xmlns:x=&quot;http://schemas.microsoft.com/winfx/2009/xaml&quot;
             x:Class=&quot;App4.Page1&quot;>
  <StackLayout>
    <Label Text=&quot;{Binding Text}&quot; />
    <Button x:Name=&quot;btn1&quot; Text=&quot;Click Me&quot; />
  </StackLayout>
</ContentPage>

  1. ViewModel を BindingContext に割り当てる

いろいろ XAML+MVVM でどのプロパティが使われているのかが混乱していますが、Xamarin.Forms の場合は BindingContext という名前になっています。

public partial class Page1 : ContentPage
{
    public Page1()
    {
        InitializeComponent();
        this.btn1.Clicked += Btn1_Clicked;
        _vm = new MyViewModel();
        this.BindingContext = _vm;
    }
    MyViewModel _vm;
    private void Btn1_Clicked(object sender, EventArgs e)
    {
        _vm.Text = "MVVM でクリックした";
    }
}

これで無事 MVVM 化ができます。

本家のサンプル

本家のサンプルが github にあるので、ごっそりダウンロードして試してみるとよいです。

GitHub – xamarin/xamarin-forms-samples: Sample apps built using the Xamarin.Forms framework
https://github.com/xamarin/xamarin-forms-samples

これを何処に使うのか?

まあ、一般的な話で言えば Android/iOS のアプリを同時に作れるという話が順当です。が、もうちょっと話を飛躍させると、こんな感じになります。

「FlashAir+Arduino鉄道模型制御-1」 by 綾瀬ヒロ│MakersHub
https://makershub.jp/make/1030

綾瀬ヒロさんも Xamarin を使っていますが、この手のタブレット操作画面を作るのに Xamarin.Forms が有効なのです。スマートフォンじゃなくて、iPad や Surface、大き目の Android タブレット(候補としては Amazon タブレットぐらいしかないのですが)で操作系のアプリを作ろうと思うと、スマートフォンのような小さな画面とは異なった作りが必要になります。かつ、タッチパネル使える/必須なので、操作としてはスマートフォンっぽい感じで使えるのが良い訳です。
UWP が使えるので、Windows IoT Core on Raspberry Pi でも使えますよね(実際使える)。なので、このあたり UWP だけで閉じていれば、Surface, IoT Core 画面でデバッグができる&操作が同じにできるという話なのですが、もうちょっと Android/iPad に範囲を広げると Xamarin.Forms で画面を構成するのは結構有効です。IoT 絡みになれば、スマートフォンの通信や加速度センサーだけでなく、具体的に自作したセンサーの類を使えるわけですから、UI まわりをさっくりと作る(ある意味で、スマートフォンの最新機能には依存しない)ことができます。高めではありますが、Android 組み込みボードのほうでも Xamarin が使えるようになるとよいかなと思ったりしてます。

そのあたりを含めて、入門的に手を付けるのがお薦めです。

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