2016年1月24日日曜日

Rx亜種を作ってみた(2)


前回のRx亜種を実装してみたからの続き。

前回はデザインパターンである、オブザーバーパターンとステートパターンを組み合わせて実装してみた。

今回はその発展形でIEnumerable型のyieldを活用してスケジューラ機能を備えた構造に仕上げてみた。
そのついでにIObserver、IObservableインターフェースは使わずOnNextでIEnumerable型を返すよう改造したIReactiveインターフェースを使うようにした。

まだ改良の余地はあるけど、方針は
 Stateパターン+Iteratorパターン
 Observerパターン+Iteratorパターン
これを合成したReactiveなもにに仕上げる。
できたらOnNextする前にLINQを挟んで処理できるようになればいいかなって考えているけど、yield returnした後の結果をどうやって受け取ればいいんだ~?
(そもそも内向きの処理でLINQを使おうとしてるのが間違いなんだろうけど。)



using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    /// <summary>
    /// スケジューラー
    /// 123123123 13 1 なし ...終了したタスクはリストから外れる
    /// var s = new Schedular();
    /// s.EnQueue(OnNext(value))
    /// </summary>
    public class Schedular
    {
        object _lock { get; } = new object();
        Queue<IEnumerator> queue { get; } = new Queue<IEnumerator>();
        public Task Dispach()
        {
            return Task.Run
                (() =>
                {
                    IEnumerator que;
                    while (true)
                    {
                        while (0 < queue.Count)
                        {
                            lock(_lock)
                            {                                
                                que = queue.Dequeue();
                            }
                            if (que.MoveNext())
                            {
                                lock (_lock)
                                {
                                    queue.Enqueue(que);
                                }
                            }
                            Thread.Sleep(0);
                        }
                        Thread.Sleep(1);
                    }
                });
        }

        public void Attach(IEnumerator value)
        {
            lock(_lock)
            {
                queue.Contains(value);
            }
        }
    }

    /// <summary>
    /// Push型インターフェース
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public interface IReactive<T> : IDisposable
    {
        LinkedList<T> Follows { get; }
        LinkedList<T> Followers { get; }
        IEnumerable<Func<T, bool>> OnNext(T value);
        void OnError(Exception error);
        void OnCompleted();
        void Subscribe(IReactive<T> observer);
    }

    /// <summary>
    /// フォロー・フォロワーのリンクリスト
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class LinkedList<T> : IEnumerable<IReactive<T>>
    {
        List<IReactive<T>> users { get; } = new List<IReactive<T>>();
        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
        public IEnumerator<IReactive<T>> GetEnumerator() => users.GetEnumerator();
        public void Add(IReactive<T> value) => users.Add(value);
        public void AddRange(LinkedList<T> list) => users.AddRange(list.users);
        public void Remove(IReactive<T> value) => users.Remove(value);
        public void Clear() => users.Clear();

        //並行に処理していく 123123123
        public IEnumerable<Func<T, bool>> OnParallel(T parameter)
        {
            var queue = new Queue<IEnumerator<Func<T, bool>>>();
            foreach (var follower in users)
                queue.Enqueue(follower.OnNext(parameter).GetEnumerator());

            while (0 < queue.Count)
            {
                var que = queue.Dequeue();
                if (que.MoveNext())
                {
                    queue.Enqueue(que);
                    yield return que.Current;
                }
            }
        }

        //木構造を順番に処理していく 111222333
        public IEnumerable<Func<T, bool>> OnSerial(T parameter)
        {
            foreach (var follower in users)
                foreach (var que in follower.OnNext(parameter))
                    yield return que;
        }

    }

    /// <summary>
    /// 関数内の関数をデリゲート化してステートマシンに見立てて実行(ステートパターン)
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public abstract class StateMachine<T>
    {
        public Func<T, bool> Current => methods[SeqNo];
        Func<T, bool>[] methods { get; }
        int SeqNo { get; set; } = 0;
        int JumpSeqNo { get; set; } = -1;

        /// <summary>
        /// コンストラクタ処理
        /// </summary>
        public StateMachine()
        {
            methods = GetType().GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.InvokeMethod)
            .Where(_ => _.ReturnType == typeof(bool) && 1 == _.GetParameters().Length)
            .Where(_ => (_.GetParameters()[0].ParameterType == typeof(T)))
            .Select(_ => (Func<T, bool>)_.CreateDelegate(typeof(Func<T, bool>), this))
            .ToArray();
        }

        /// <summary>
        /// 次の処理関数を指定する
        /// </summary>
        /// <param name="state"></param>
        /// <returns></returns>
        protected bool Jump(Func<T, bool> state)
        {
            for (var i = 0; i < methods.Length; i++)
            {
                if (state == methods[i])
                {
                    JumpSeqNo = i;
                    return true;
                }
            }
            throw new Exception("遷移先が見つからない");
        }

        /// <summary>
        /// 実行ポジション移動(Fetch)
        /// </summary>
        /// <returns></returns>
        bool Fetch()
        {
            if (0 > JumpSeqNo)
            {
                SeqNo++;
            }
            else
            {
                SeqNo = JumpSeqNo;
                JumpSeqNo = -1;
            }
            return (0 <= SeqNo) && (SeqNo < methods.Length);
        }

        /// <summary>
        /// 時間軸ありのシーケンス実行
        /// </summary>
        /// <param name="value"></param>
        /// <returns>True=条件を満たした False=条件を満たしていない</returns>
        protected IEnumerable<Func<T, bool>> Exexute(T value)
        {
            SeqNo = 0;
            do
            {
                while (!Current(value))
                    yield return Current;
            } while (Fetch());
        }
    }

    /// <summary>
    /// ステートマシンベースクラス(Observerパターン)
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public abstract class Workflow<T> : StateMachine<T>, IReactive<T>
    {
        public LinkedList<T> Follows { get; } = new LinkedList<T>();
        public LinkedList<T> Followers { get; } = new LinkedList<T>();
        public virtual void OnCompleted() { }
        public virtual void OnError(Exception error) { throw error; }
        public virtual IEnumerable<Func<T, bool>> OnNext(T value)
        {
            foreach (var exe in Exexute(value))
                yield return exe;
            foreach (var exe in Followers.OnParallel(value))
                yield return exe;
        }

        /// <summary>
        /// 購読開始
        /// </summary>
        /// <param name="follower"></param>
        public void Subscribe(IReactive<T> follower)
        {
            Followers.Add(follower);    //自分のフォロワーリストに登録
            follower.Follows.Add(this); //相手のフォローリストに登録
        }

        /// <summary>
        /// 購読解除
        /// </summary>
        public void Dispose()
        {
            //A -> B(自信) -> C の時、Bを削除したとき
            //A -> C となるようにリストを操作
            foreach (var follow in Follows)
            {
                follow.Followers.Remove(this);      //フォローのフォロワーリストから自分をを削除
                follow.Followers.AddRange(Followers);
            }
            foreach (var _ in Followers)
            {
                _.Follows.Remove(this);             //フォロワーのフォローリストから自分をを削除
                _.Follows.AddRange(Follows);
            }

            Follows.Clear();
            Followers.Clear();
        }

    }

}



使い方は以下のようになる。




using System;

namespace ConsoleApplication1
{
    public class AFunc1 : Workflow<int>
    {
        int a = 0;
        bool F1(int parameter)
        {
            //Console.WriteLine($"Func11={parameter}");
            return false;// Jump(F2);
        }
        bool F2(int parameter)
        {
            Console.WriteLine($"Func12={parameter}");
            parameter++;
            return (++a % 2) == 0;
        }
    }

    public class AFunc2 : Workflow<int>
    {
        int a = 0;
        bool F1(int parameter)
        {
            Console.WriteLine($"Func21={parameter}");
            return (++a % 2) == 0;
        }
        bool F2(int parameter)
        {
            Console.WriteLine($"Func22={parameter}");
            parameter++;
            return (++a % 2) == 0;
        }
    }

    public class AFunc3 : Workflow<int>
    {
        int a = 0;
        bool F1(int parameter)
        {
            Console.WriteLine($"Func31={parameter}");
            return true;
        }
        bool F2(int parameter)
        {
            Console.WriteLine($"Func32={parameter}");
            return true;
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            var f1 = new AFunc1();
            var f2 = new AFunc2();
            var f3 = new AFunc3();

            //f1.Subscribe(f2);
            //f2.Subscribe(f3);

            var a = f1.OnNext(1).GetEnumerator();
            var dt1 = DateTime.Now;
            for (var i = 0; i < 10000000; i++)
            {
                a.MoveNext();
            }
            var dt2 = DateTime.Now;
            Console.WriteLine((dt2 - dt1).TotalMilliseconds);
            //foreach(var a in f1.OnNext(1))
            //    Console.WriteLine($">>>>>>>>>>>>>{a.Method.ReflectedType.Name}:{a.Method.Name}");

            Console.WriteLine("-----");
            Console.ReadKey();
        }
    }
}

Rx亜種を実装してみた

前回はRx入門について書いてみた。

記事にすることでRxについて理解は深まったけど、C#ではいらないんじゃないかなって思えてきた。
Javascriptなら有用な手段かもしれないけどね。。って感じに。
という心境なので今回はRxの処理についてもう少し深い話を進めていきたいと思う。

LINQについて
 使ったことがあればわかると思うが、データの一連処理を行う事ができるとても便利なもの。


Rx(Observer+Observable)+LINQについて
 RXは受動的なので、イベント引数や値がプッシュされて初めて動き出す。
そこでLINQを使うとイベント引数や値の条件をLINQで設定できる。これが良いようだ。


RxのHotとColdについて
 Hotはセンサーのようなもの。状態変化したら情報発信(OnNext)するだけのようなもの。
 ColdはLINQでつないだパイプ的なもの。
 HotもColdも共通して受動的に動くので、
 Hotの場合は定周期で実行したり、Coldの場合は使いたいときにOnNextを呼んで動かす。


データフローについて
 1つずつ値をプッシュして分岐するところで、同じ値をプッシュしたり交互に割り振ったりすることが可能。実際に使う場合は同じ値を全体にプッシュする方だと思う。

色々突き詰めていくとOnNextがすべて。

具体的に説明すると、
まずObservableクラスを作る

  OnNextで値をプッシュ→OnNext→OnNext→・・・→OnCompleted
 
  例外はOnError

という処理構造が木構造でできているのである。

OnNextは左から右へと値をプッシュ&LINQで条件式が挿入できる感じで
OnCompletedは大体がDisposeみたいに使われる。
OnErrorは例外なので説明省略。


値をプッシュ→何らかの処理→Subscribe{OnNext、OnCompleted、OnError}で処理が実行される。

値をフィードバックしたい時どうするの?って思うが、
最初の発端がイベントからスタートなので値を状態に反映させるだけ、
反映された結果のフィードバックは別で行われるというざっくりした切り分けになっている。
そうすることでイベントを呼び出したスレッドで何もかもやってしまおうって考えはどうなの?って思うけど省略しておく。

話を戻してさっきの仕組みを普通に書いてみると

void func1(int value)
{
    try
    {
        if(条件式1) ; // OnNext=Trueなら次へ
        {
            if(条件式2)
            {
                OnCompleted();
            }
        }catch (Exception ex)
        {
        }
    }
}
実際はOnNext部分とかDelegateで処理を挿入するので少し変わるけど、基本構造はこんなものだ。

なんかObserverパターンを使うことで小難しくなってしまう場合があるんじゃないかって疑問がでてきたわけだ。

ということでObserverパターンを簡素化したもので実装してみた。

基本構造は
 WorkerクラスとDispatcher クラスがいる。
 Dispatcherクラスは、自クラス内にあるシーケンス処理をRefrectionで呼び出して処理実行する。StateパターンのDelegate版
  TrueならOnNext、Falseなら実行しない。 
  ここでDelegateしたOnNext部分とLINQ部分の処理をまとめた。
 WorkerクラスはObserverパターンの簡素版、1:Nになるようにした。
 この辺りはツイッターをイメージするとわかりやすいのでフォローとフォロワーで変数名を定義してある。
 ちなみにColdとHotの説明をしたけど、Hotは定周期タスクに登録して定期的に実行して変化があればOnNextしてもらえればいいので概念だけわかっていればOK。
出力条件はDispatchクラスがやってくれるし、下のコードで特に問題なさそう。


    public class Dispatcher<T>
    {
        Func<T, bool>[] methods { get; }
        int SeqNo { get; set; } = 0;
        int JumpSeqNo { get; set; } = -1;

        public Dispatcher()
        {
            methods = GetType().GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.InvokeMethod)
            .Where(_ => _.ReturnType == typeof(bool) && 1 == _.GetParameters().Length)
            .Where(_ => (_.GetParameters()[0].ParameterType == typeof(T)))
            .Select(_ => (Func<T, bool>)_.CreateDelegate(typeof(Func<T, bool>), this))
            .ToArray();
        }
        protected bool Jump(Func<T, bool> state)
        {
            for (var i = 0; i < methods.Length; i++)
            {
                if (state == methods[i])
                {
                    JumpSeqNo = i;
                    return true;
                }
            }
            throw new Exception("遷移先が見つからない");
        }

        protected bool Dispatch(T value)
        {
            SeqNo = -1;
            while (Next())
            {
                if (!methods[SeqNo](value))
                    return false;
            }
            return true;
        }

        bool Next()
        {
            if (0 > JumpSeqNo)
            {
                SeqNo++;
            }
            else
            {
                SeqNo = JumpSeqNo;
                JumpSeqNo = -1;
            }
            return (0 <= SeqNo) && (SeqNo < methods.Length);
        }
    }

    public class Worker<T> : Dispatcher<T>, IDisposable
    {
        internal Worker<T> follow { get; set; } = null;
        internal List<Worker<T>> followers { get; } = new List<Worker<T>>();

        public void SubScribe(Worker<T> follower)
        {
            if(null != follower.follow) throw new Exception("複数フォローできません");
            followers.Add(follower);
            follower.follow = this;
        }

        public bool OnNext(T value)
        {
            if (Dispatch(value))
                return followers.TrueForAll(_ => _.OnNext(value));
            return false;
        }

        public void Dispose()
        {
            follow.followers.Remove(this);
            follow = null;
            followers.ForEach(_ => _.follow = null);
            followers.Clear();
        }
    }


使い方はこんな感じ


    class Worker1 : Worker<int>
    {
        bool Func1(int value)
        {
            Console.WriteLine($"Func11={value}");
            return Jump(Func2);
        }

        bool Func2(int value)
        {
            Console.WriteLine($"Func12={value}");
            return true;
        }
    }

    class Worker2 : Worker<int>
    {
        bool Func1(int value)
        {
            var ret = 0 == value % 2;
            Console.WriteLine($"Func21={ret}");
            return ret;
        }

        bool Func2(int value)
        {
            var ret = 0 != value % 3;
            Console.WriteLine($"Func22={ret}");
            return ret;
        }
    }

    class Worker3 : Worker<int>
    {
        bool Func1(int value)
        {
            Console.WriteLine($"Func31={value}");
            return true;
        }

    }
    class Program
 {
        static void Test(int i)
        {
            var proc1 = new Worker1();
            var proc2 = new Worker2();
            var proc3 = new Worker3();

            proc1.SubScribe(proc2);
            proc1.SubScribe(proc3);


            proc1.OnNext(2);
            proc2.Dispose();

            proc1.OnNext(2);
            proc3.Dispose();

            proc1.OnNext(2);
            proc1.SubScribe(proc2);

            proc1.OnNext(2);
            proc1.SubScribe(proc3);

            proc1.OnNext(2);
        }
    }

2016年1月22日金曜日

[C#] Reactive Extension入門(Rx)

Reactive Extension入門

 Rx入門を何回読んでも難しい。
ネット上の記事をみているとみんなが揃って良いと書いてあるのでステマ?って気がするし理解が進まない。
ということで自分でも記事を書いてみる事にしました。
最初にいっておくとRxを完全に理解しつくせていません。


そもそもRxって何なの?
 OvserverパターンをベースにLinQ化できるもの。


Observerパターンとは何か?
 Observer(監視役)ーObservable(労働者)の関係が1:Nの時に有用であり
労働者が監視役へ通知{OnNext、OnCompleted、OnError}するといった考えである。


Rxにすると?
 イベントが発生するとObservable(労働者)が動き、結果をObserver(監視役)へ通知する。
このオブザーバーパターンの性質をLINQ化できるようになる。


メリットは?
 イベント→Observable(労働者が何らかの処理の行う)→結果(OnNext, OnError, OnComplete)を返し
 さらに次の動作へ引き継がれていく連鎖が出来上がる。
 まるでドミノ倒しのように1つのイベントから連鎖反応が起きるのがメリットだと思う。


使い道は?
 例えばメール受信したら他のメールアドレスへ転送するとかができる。
  メール:Observable
  転送処理:Observer

としたときの実装は、

 「メール」は、受信したらOnNextを呼ぶ
 「転送処理」は、OnNextが呼ばれたら転送処理を実行

というようになる。

転送処理は「メールを受信したら」の続きを行いたいので、
 メールに対してSubscribe(購読)しておく。

すると、メール受信したら
 Observer側OnNextが呼ばれるので、メール受信したらの転送するという処理ができあがる。

こういった使い道が有用なケースであり、
LinQの技術を合わせることでWhereなどが使えるようになるのがとても良さそう。


イケてないと思うのは?
 初見、個人的にイケていないなと思った点を書くと、だれが処理するの?ってところ。
Observerでメール受信したよって、みんなに通知するのはOK。
が、しかしOnNextの中に転送処理が入ってると
メール受信したよってOnNextで通知して続けて転送処理も行うので
イベント出した人が一連の処理を行っているのがとても気になる。
一連の処理の塊があってイベント出したらその人が最後までやり遂げる必要が出てくる。

さっきのメールの例をあげると、
郵便屋さんがある宅にはがきを届けたついでに秘書のやるようなやってしまう。そんなところ。
(RxではIEnumerableインターフェイスを使ってスケジューラで処理させることでうまく解決してるんだろうけどね。)


処理の流れについて

 イベント
  ↓
 Observable
  ↓
 (Observer兼Observableの二役)
  ↓
  …
  ↓
 Observer(OnComplate、OnNext、OnError)


最初はObservableなオブジェクトで始まり、
中間は板挟みの監視し監視される立場なので両方の性質を持ち合わせていて、最後はObserverで終える。
中間を省いて考えればイベント入力で結果が返ってくる仕組みになっているのが分かるだろう。
この一連の処理をLinQで処理するのでIEnumerableインターフェイスを追加してあげると
Foreachで処理を回せるようになり、それが後々のスケジューラとなる。


実装例

今回ツイッターのタイムライン情報を見てフォロワーのAさんとBさんがRTするという例を
自作Subjectで実装してみた。
これで仕組みが理解できた気がする。

 public class Subject<T> : IObserver<T>, IObservable<T>, IDisposable, IEnumerable
 {
  protected Queue<T> EventQueue { get; } = new Queue<T>();    // events
  internal List<Subject<T>> follows { get; } = new List<Subject<T>>();  // observable
  internal List<Subject<T>> followers { get; } = new List<Subject<T>>(); // observer
  public virtual void OnCompleted() => followers.ForEach(_ => _.OnCompleted());
  public virtual void OnError(Exception error) => followers.ForEach(_ => _.OnError(error));
  public virtual void OnNext(T value) => followers.ForEach(_ => _.OnNext(value));
  public virtual IEnumerator GetEnumerator()
  {
   if (0 == EventQueue.Count)
    yield break;
   while (0 < EventQueue.Count)
   {
    var queue = EventQueue.Dequeue();
    followers.ForEach(_ => _.OnNext(queue));
    yield return this;
   }
   followers.ForEach(_ => _.OnCompleted());
  }
  public IDisposable Subscribe(IObserver<T> observer)
  {
   //購読すると、自分のフォローリストに追加、相手のフォロワーリストに追加
   var _observer = (Subject<T>)observer;
   followers.Add(_observer);       // フォロワーを受け入れる
   _observer.follows.Add(this);    // 相手側のフォローにも登録
   return (IDisposable)observer;
  }
  public void Dispose()
  {
   //フォロー(observable)・フォロワー(observer)の関係を破棄する
   follows.ForEach(_ => _.followers.Remove(this)); //フォローした人フォロワーリストから自身を削除
   followers.ForEach(_ => _.follows.Remove(this)); //フォロワーしてくれた人のフォローリストから自身を削除
   followers.Clear();
   follows.Clear();
  }
  //無条件永久ループ
  public Task MainLoop() =>
    Task.Run(() =>
    {
     while (true)
     {
      foreach (var _ in this)
       Thread.Sleep(0);
      Thread.Sleep(1);
     }
    });
 }

 public class Timeline : Subject<string>
 {
  /// <summary>
  /// 受信データがあったときの処理
  /// </summary>
  /// <returns></returns>
  public override IEnumerator GetEnumerator()
  {
   Console.WriteLine("\r\n---------------------------");
   while (0 < timelines.Count)
   {
    var message = timelines.Dequeue();
    Console.WriteLine($"TL上の新着メッセージ:{message}");
    OnNext(message);
   }
   yield break;
  }
  Queue<string> timelines { get; } = new Queue<string>();
  public void Send(string message) => timelines.Enqueue(message);
 }

 public class RTer : Subject<string>
 {
  string UserName { get; }

  public RTer(string userName)
  {
   UserName = userName;
  }

  public override void OnNext(string value)
  {
   Console.WriteLine($"{UserName}: {value}のツイートをRTした");
  }
 }


 class Program
 {
  static void Main(string[] args)
  {
   var tl = new Timeline();
   var u1 = new RTer("Aさん");
   var u2 = new RTer("Bさん");

   tl.Send("Hello");
   foreach (var _ in tl) ;

   tl.Subscribe(u1);
   tl.Send("World");
   foreach (var _ in tl) ;

   tl.Subscribe(u2);
   tl.Send("foo");
   foreach (var _ in tl) ;

   u2.Dispose();
   tl.Send("bar");
   foreach (var _ in tl) ;
  }
 }
}



2016年1月21日木曜日

[C#]WinAPIを用いたシリアル通信

シリアル通信の機能をフル活用したい場合にどうぞ


 public class WinSerialPort : IDisposable
 {
  #region Win32 API宣言 

  [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
  private static extern IntPtr CreateFile(
   string lpFileName,                          // ファイル名 
   DesiredAccess dwDesiredAccess,              // アクセスモード 
   ShareMode dwShareMode,                      // 共有モード 
   int lpSecurityAttributes,                   // セキュリティ記述子 
   CreationDisposition dwCreationDisposition,  // 作成方法 
   FlagsAndAttributes dwFlagsAndAttributes,    // ファイル属性 
   IntPtr hTemplateFile                        // テンプレートファイルのハンドル 
   );

  [DllImport("kernel32.dll", SetLastError = true)]
  public static extern bool CloseHandle(IntPtr hObject);

  [DllImport("kernel32.dll")]
  static extern bool GetCommState(IntPtr hFile, ref DCB lpDCB);

  [DllImport("kernel32.dll")]
  static extern bool SetCommState(IntPtr hFile, ref DCB lpDCB);

  [DllImport("kernel32.dll", SetLastError = true)]
  //static extern bool ReadFile(IntPtr hFile, [Out] byte[] lpBuffer, uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, IntPtr lpOverlapped);
  static extern bool ReadFile(IntPtr hFile, [Out] byte[] lpBuffer, uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, int lpOverlapped);

  [DllImport("kernel32.dll")]
  //static extern bool WriteFile(IntPtr hFile, byte[] lpBuffer, uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten, [In] ref NativeOverlapped lpOverlapped);
  static extern bool WriteFile(IntPtr hFile, byte[] lpBuffer, uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten, int lpOverlapped);

  [DllImport("kernel32.dll", SetLastError = true)]
  static extern bool GetCommTimeouts(IntPtr hFile, ref COMMTIMEOUTS lpCommTimeouts);

  [DllImport("kernel32.dll", SetLastError = true)]
  static extern bool SetCommTimeouts(IntPtr hFile, [In] ref COMMTIMEOUTS lpCommTimeouts);

  [DllImport("kernel32.dll")]
  static extern bool PurgeComm(IntPtr hFile, Purge dwFlags);
  #endregion
  #region 列挙体 

  private enum DesiredAccess : uint
  {
   GENERIC_READ = 0x80000000,
   GENERIC_WRITE = 0x40000000,
   GENERIC_EXECUTE = 0x20000000
  }

  private enum ShareMode : uint
  {
   FILE_SHARE_READ = 0x00000001,
   FILE_SHARE_WRITE = 0x00000002,
   FILE_SHARE_DELETE = 0x00000004
  }

  private enum CreationDisposition : uint
  {
   CREATE_NEW = 1,
   CREATE_ALWAYS = 2,
   OPEN_EXISTING = 3,
   OPEN_ALWAYS = 4,
   TRUNCATE_EXISTING = 5
  }

  private enum FlagsAndAttributes : uint
  {
   FILE_ATTRIBUTE_ARCHIVE = 0x00000020,
   FILE_ATTRIBUTE_ENCRYPTED = 0x00004000,
   FILE_ATTRIBUTE_HIDDEN = 0x00000002,
   FILE_ATTRIBUTE_NORMAL = 0x00000080,
   FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x00002000,
   FILE_ATTRIBUTE_OFFLINE = 0x00001000,
   FILE_ATTRIBUTE_READONLY = 0x00000001,
   FILE_ATTRIBUTE_SYSTEM = 0x00000004,
   FILE_ATTRIBUTE_TEMPORARY = 0x00000100
  }

  public enum DtrControl : int
  {
   /// <summary>
   /// Disables the DTR line when the device is opened and leaves it disabled.
   /// </summary>
   Disable = 0,

   /// <summary>
   /// Enables the DTR line when the device is opened and leaves it on.
   /// </summary>
   Enable = 1,

   /// <summary>
   /// Enables DTR handshaking. If handshaking is enabled, it is an error for the application to adjust the line by 
   /// using the EscapeCommFunction function.
   /// </summary>
   Handshake = 2
  }

  public enum RtsControl : int
  {
   /// <summary>
   /// Disables the RTS line when the device is opened and leaves it disabled.
   /// </summary>
   Disable = 0,

   /// <summary>
   /// Enables the RTS line when the device is opened and leaves it on.
   /// </summary>
   Enable = 1,

   /// <summary>
   /// Enables RTS handshaking. The driver raises the RTS line when the "type-ahead" (input) buffer 
   /// is less than one-half full and lowers the RTS line when the buffer is more than 
   /// three-quarters full. If handshaking is enabled, it is an error for the application to 
   /// adjust the line by using the EscapeCommFunction function.
   /// </summary>
   Handshake = 2,

   /// <summary>
   /// Specifies that the RTS line will be high if bytes are available for transmission. After 
   /// all buffered bytes have been sent, the RTS line will be low.
   /// </summary>
   Toggle = 3
  }

  public enum Purge : uint
  {
   TxAbort = 1,
   ExAbort = 2,
   TxClear = 4,
   RxClear = 8,
  }
  #endregion
  #region "構造体"
  struct COMMTIMEOUTS
  {
   public UInt32 ReadIntervalTimeout;
   public UInt32 ReadTotalTimeoutMultiplier;
   public UInt32 ReadTotalTimeoutConstant;
   public UInt32 WriteTotalTimeoutMultiplier;
   public UInt32 WriteTotalTimeoutConstant;
  }
  #endregion
  #region "DCB構造体"
  [StructLayout(LayoutKind.Sequential)]
  internal struct DCB
  {
   internal uint DCBLength;
   internal uint BaudRate;
   private BitVector32 Flags;

   private ushort wReserved;        // not currently used 
   internal ushort XonLim;           // transmit XON threshold 
   internal ushort XoffLim;          // transmit XOFF threshold             

   internal byte ByteSize;
   internal Parity Parity;
   internal StopBits StopBits;

   internal sbyte XonChar;          // Tx and Rx XON character 
   internal sbyte XoffChar;         // Tx and Rx XOFF character 
   internal sbyte ErrorChar;        // error replacement character 
   internal sbyte EofChar;          // end of input character 
   internal sbyte EvtChar;          // received event character 
   private ushort wReserved1;       // reserved; do not use     

   private static readonly int fBinary;
   private static readonly int fParity;
   private static readonly int fOutxCtsFlow;
   private static readonly int fOutxDsrFlow;
   private static readonly BitVector32.Section fDtrControl;
   private static readonly int fDsrSensitivity;
   private static readonly int fTXContinueOnXoff;
   private static readonly int fOutX;
   private static readonly int fInX;
   private static readonly int fErrorChar;
   private static readonly int fNull;
   private static readonly BitVector32.Section fRtsControl;
   private static readonly int fAbortOnError;

   static DCB()
   {
    // Create Boolean Mask
    int previousMask;
    fBinary = BitVector32.CreateMask();
    fParity = BitVector32.CreateMask(fBinary);
    fOutxCtsFlow = BitVector32.CreateMask(fParity);
    fOutxDsrFlow = BitVector32.CreateMask(fOutxCtsFlow);
    previousMask = BitVector32.CreateMask(fOutxDsrFlow);
    previousMask = BitVector32.CreateMask(previousMask);
    fDsrSensitivity = BitVector32.CreateMask(previousMask);
    fTXContinueOnXoff = BitVector32.CreateMask(fDsrSensitivity);
    fOutX = BitVector32.CreateMask(fTXContinueOnXoff);
    fInX = BitVector32.CreateMask(fOutX);
    fErrorChar = BitVector32.CreateMask(fInX);
    fNull = BitVector32.CreateMask(fErrorChar);
    previousMask = BitVector32.CreateMask(fNull);
    previousMask = BitVector32.CreateMask(previousMask);
    fAbortOnError = BitVector32.CreateMask(previousMask);

    // Create section Mask
    BitVector32.Section previousSection;
    previousSection = BitVector32.CreateSection(1);
    previousSection = BitVector32.CreateSection(1, previousSection);
    previousSection = BitVector32.CreateSection(1, previousSection);
    previousSection = BitVector32.CreateSection(1, previousSection);
    fDtrControl = BitVector32.CreateSection(2, previousSection);
    previousSection = BitVector32.CreateSection(1, fDtrControl);
    previousSection = BitVector32.CreateSection(1, previousSection);
    previousSection = BitVector32.CreateSection(1, previousSection);
    previousSection = BitVector32.CreateSection(1, previousSection);
    previousSection = BitVector32.CreateSection(1, previousSection);
    previousSection = BitVector32.CreateSection(1, previousSection);
    fRtsControl = BitVector32.CreateSection(3, previousSection);
    previousSection = BitVector32.CreateSection(1, fRtsControl);
   }

   public bool Binary
   {
    get { return Flags[fBinary]; }
    set { Flags[fBinary] = value; }
   }

   public bool CheckParity
   {
    get { return Flags[fParity]; }
    set { Flags[fParity] = value; }
   }

   public bool OutxCtsFlow
   {
    get { return Flags[fOutxCtsFlow]; }
    set { Flags[fOutxCtsFlow] = value; }
   }

   public bool OutxDsrFlow
   {
    get { return Flags[fOutxDsrFlow]; }
    set { Flags[fOutxDsrFlow] = value; }
   }

   public DtrControl DtrControl
   {
    get { return (DtrControl)Flags[fDtrControl]; }
    set { Flags[fDtrControl] = (int)value; }
   }

   public bool DsrSensitivity
   {
    get { return Flags[fDsrSensitivity]; }
    set { Flags[fDsrSensitivity] = value; }
   }

   public bool TxContinueOnXoff
   {
    get { return Flags[fTXContinueOnXoff]; }
    set { Flags[fTXContinueOnXoff] = value; }
   }

   public bool OutX
   {
    get { return Flags[fOutX]; }
    set { Flags[fOutX] = value; }
   }

   public bool InX
   {
    get { return Flags[fInX]; }
    set { Flags[fInX] = value; }
   }

   public bool ReplaceErrorChar
   {
    get { return Flags[fErrorChar]; }
    set { Flags[fErrorChar] = value; }
   }

   public bool Null
   {
    get { return Flags[fNull]; }
    set { Flags[fNull] = value; }
   }

   public RtsControl RtsControl
   {
    get { return (RtsControl)Flags[fRtsControl]; }
    set { Flags[fRtsControl] = (int)value; }
   }

   public bool AbortOnError
   {
    get { return Flags[fAbortOnError]; }
    set { Flags[fAbortOnError] = value; }
   }
  }
  #endregion

  IntPtr port { get; set; }
  string portName { get; }
  uint baudRate { get; }
  Parity parity { get; }
  byte dataBits { get; }
  StopBits stopBits { get; }
  byte[] recvBuffer { get; } = new byte[1];

  public WinSerialPort(string portName, uint baudRate, Parity parity, byte dataBits, StopBits stopBits)
  {
   this.portName = portName;
   this.baudRate = baudRate;
   this.parity = parity;
   this.dataBits = dataBits;
   this.stopBits = stopBits;
  }

  public bool Open()
  {
   port = CreateFile(portName, DesiredAccess.GENERIC_READ | DesiredAccess.GENERIC_WRITE, 0, 0, CreationDisposition.OPEN_EXISTING, FlagsAndAttributes.FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);

   //ポートオープン
   if (0 >= port.ToInt32())
    return false;

   //通信設定
   var dcb = new DCB();
   if (!GetCommState(port, ref dcb))
    return false;
   dcb.RtsControl = RtsControl.Enable;
   dcb.DtrControl = DtrControl.Enable;
   dcb.BaudRate = 250 * 1000;
   dcb.ByteSize = 8;
   dcb.Parity = parity;
   dcb.StopBits = stopBits;
   dcb.Binary = true;
   dcb.CheckParity = true;
   if (!SetCommState(port, ref dcb))
    return false;

   //タイムアウト設定
   var Commtimeouts = new COMMTIMEOUTS();
   if (!GetCommTimeouts(port, ref Commtimeouts))
    return false;

   Commtimeouts.ReadIntervalTimeout = 100;

   if (!SetCommTimeouts(port, ref Commtimeouts))
    return false;

   return true;
  }

  public void Close()
  {
   CloseHandle(port);
   port = IntPtr.Zero;
  }

  public void Write(byte[] buffer, uint offset, uint size)
  {
   uint writen = 0;
   WriteFile(port, buffer, (uint)size, out writen, 0);
  }

  public int ReadByte()
  {
   uint recved = 0;
   if (ReadFile(port, recvBuffer, 1, out recved, 0))
   {
    if (0 < recved)
    {
     return recvBuffer[0];
    }
   }
   return -1;
  }

  public void DiscardInBuffer()
  {
   PurgeComm(port, Purge.RxClear);
  }

  public void DiscardOutBuffer()
  {
   PurgeComm(port, Purge.TxClear);
  }

  public void Dispose()
  {
   if (port != IntPtr.Zero)
   {
    Close();
   }
  }
 }

2016年1月20日水曜日

[C#]コンテナのベンチマーク

C#のコンテナの処理コストはどれくらいなのか調べてみた。

結果的には、
ListのRemoveAtは先頭を削除すると前詰めでo(n)の処理時間がかかるので重くなる
Dictionaryの登録時にハッシュを作る処理時間分遅くなっている。
それ以外はどれも同じぐらいだった。


1000万回繰り返したときの時間[ms]

Queue
 Enqueue: 239.6727
 Dequeue:142.5946

Stack
 Push:147.5971
 Pop:103.0714

List
 Add:173.6246
 RemoveAt:134.5897

Dictionary
 Add.688.0982
 Remove:285.1807


下はソースコード

   var q = new Queue<int>();
   var s = new Stack<int>();
   var l = new List<int>();
   var d = new Dictionary<int, int>();
   var dt0 = DateTime.Now;
   for (int i = 0; i < 10000000; i++)
   {
    q.Enqueue(i);
   }
   var dt1 = DateTime.Now;
   Console.WriteLine($"Enqueue:{(dt1 - dt0).TotalMilliseconds}");

   dt0 = DateTime.Now;
   for (int i = 0; i < 10000000; i++)
   {
    q.Dequeue();
   }
   dt1 = DateTime.Now;
   Console.WriteLine($"Dequeue:{(dt1 - dt0).TotalMilliseconds}");

   dt0 = DateTime.Now;
   for (int i = 0; i < 10000000; i++)
   {
    s.Push(i);
   }
   dt1 = DateTime.Now;
   Console.WriteLine($"Push:{(dt1 - dt0).TotalMilliseconds}");

   dt0 = DateTime.Now;
   for (int i = 0; i < 10000000; i++)
   {
    s.Pop();
   }
   dt1 = DateTime.Now;
   Console.WriteLine($"Pop:{(dt1 - dt0).TotalMilliseconds}");

   dt0 = DateTime.Now;
   for (int i = 0; i < 10000000; i++)
   {
    l.Add(i);
   }
   dt1 = DateTime.Now;
   Console.WriteLine($"Add:{(dt1 - dt0).TotalMilliseconds}");

   dt0 = DateTime.Now;
   for (int i = 0; i < 10000000; i++)
   {
    l.RemoveAt(l.Count - 1);
   }
   dt1 = DateTime.Now;
   Console.WriteLine($"RemoveAt:{(dt1 - dt0).TotalMilliseconds}");

   dt0 = DateTime.Now;
   for (int i = 0; i < 10000000; i++)
   {
    d[i] = i;
   }
   dt1 = DateTime.Now;
   Console.WriteLine($"Add:{(dt1 - dt0).TotalMilliseconds}");

   dt0 = DateTime.Now;
   for (int i = 0; i < 10000000; i++)
   {
    d.Remove(i);
   }
   dt1 = DateTime.Now;
   Console.WriteLine($"Remove:{(dt1 - dt0).TotalMilliseconds}");

2016年1月19日火曜日

NON GENUINE DEVICE FOUND! エラーが出る

USBシリアル変換ケーブルにFTDI製互換チップが使われているのが原因。

対処方法は古いドライバを使えば解決する。

1つ目


2つ目


1つ目はバージョンダウンするだけで良いが、
2つ目はVIDとPIDが写真通りになっていないといけない。

もし異なっている場合は、FTDI製のドライバをインストールした時に一緒に入ってくる「FT-PROG」というツールで書き換えができる。
書き換えたらドライバーをバージョンダウンする事。



2016年1月11日月曜日

[C#]MVCフレームワークのController

オープンソースなIoT向けフレームワークの公開に向けて情報整理。
プログラムで一番やっかいなものといえば、「バグ」である。

排除するのはとても難しい。

人によって書き方が変わるし、バグが混入する箇所も様々あるが、フレームワークの規定で書き方を統一することで自由度を減らす代わりバグ混入を制限することができる。
Modelは過去に書いたので今回はController部分。


WEBアプリの場合は、ゲームでいうところターン制のようなもので、

1リクエストのレスポンス(結果)を返し、ステップアップ方式でページを遷移させて目的の結果を表示する、

というのが1つの戦略になると思う。


IoTの場合はリアルタイムに変わっていくので、常時状態を監視しなくてはならない。

例えば料理で目玉焼きを作るとき。

 ・フライパンの温度はどれくらいか?
 ・温まったらベーコンと卵を入れる
 ・おおよそ何分くらいになったらお皿に移す

というようなレシピがあったとしよう。

人によって毎回同じのものができるかといったらできないと思う。
その原因は毎回条件が変わっているから同じものができないということになる。


これをプログラムするのには、大体3つの要素

 状態をチェックする、イベントを起こす、行動に移す

あれば事足りるんじゃないかって言うことで試しに実装してみた。



using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using System.Collections;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            var sec = new section1();
        }
    }

    /// 
    /// 状態リスト
    ///  セクション内にある状態一覧
    /// 
    public class StateList : Dictionary>
    {
        List states { get; } = new List();
        public bool Contains(object obj) => states.Contains(obj);

        public void Update()
        {
            states.Clear();
            states.AddRange(this.Where(_ => _.Value()).Select(_ => _.Key).ToArray());
        }
    }

    /// 
    /// アクションリスト
    ///  セクション内にあるアクション一覧
    /// 
    public class ActionList
    {
        List> actionList { get; } = new List>();
        List actions { get; } = new List();
        public bool Contains(Type type) => null != actions.Where(_=>_.GetType() == type).FirstOrDefault();

        public void Update()
        {
            var acts = actionList.Select(_ => _()).Where(_ => null != _).Where(_ => !Contains(_.GetType())).ToArray();
            actions.AddRange(acts);
        }

        public void Dispatch(StateList states, EventList events)
        {
            var next = actions.Where(_ => _.Dispatch()).ToArray();
            actions.Clear();
            actions.AddRange(next);
        }

        public void Add(Func action)
        {
            actionList.Add(action);
        }
    }

    /// 
    /// イベントリスト
    ///  セクション内にあるイベント一覧
    /// 
    public class EventList
    {
        Dictionary> eventList { get; } = new Dictionary>();
        List events { get; } = new List();
        public Func this[object key]
        {
            set
            {
                eventList[key] = value;
            }
        }

        public void Update(StateList states)
        {
            var evts = eventList.Where(_ => _.Value()).Select(_ => _.Key);
            foreach (var evt in evts)
            {
                if (!events.Contains(evt))
                {
                    events.Add(evt);
                }
            }
        }

        public bool Check(object evt)
        {
            if (events.Contains(evt))
            {
                events.Remove(evt);
                return true;
            }
            return false;
        }

        public void ResetAll()
        {
            events.Clear();
        }

    }

    /// 
    /// シーケンス処理
    ///  手続き型のバッチ処理
    /// 
    public class Sequence
    {
        protected delegate dlgStep dlgStep();
        protected dlgStep Current { get; set; }

        public bool Dispatch()
        {
            Current = Current();
            return null != Current;
        }
    }


    /// 
    /// セクション
    ///  作業領域の中にある一部ワークフロー
    /// 
    public class Section
    {
        public bool IsPower { get; set; } = true;
        public bool IsPause { get; private set; } = false;

        protected StateList States { get; } = new StateList();
        protected EventList Events { get; } = new EventList();
        protected ActionList Actions { get; } = new ActionList();
        Task task { get; set; }

        public void Start()
        {
            task = Task.Run((Action)TaskMain);
        }

        void TaskMain()
        {
            while (IsPower)
            {
                States.Update();
                Events.Update(States);
                Actions.Update();
                Actions.Dispatch(States, Events);
                Thread.Sleep(1);
            }
        }

        public void Pause() => IsPause = true;
        public void Resume() => IsPause = false;
        public void Stop() => IsPower = false;
    }


    public class section1 : Section
    {
        public object Context { get; set; }

        public section1()
        {
            States[Door.On] = () => true;
            //States[Door.Off] = null;
            //States[Switch.On] = null;
            //States[Switch.Off] = null;
            Events["event1"] = () => true;
            //Events["event2"] = () => false;
            //Events["event3"] = () => false;
            Actions.Add(Action1);
            //Actions.Add(Action2);
            //Actions.Add(Action3);
            Start();
        }

        Sequence Action1()
        {
            if(Events.Check("event1"))
            {
                return new DoorOpen(1);
            }
            return null;
        }

        //Sequence Action2()
        //{
        //    return null;
        //}

        //Sequence Action3()
        //{
        //    return null;
        //}

        enum Door { On, Off }
        enum Switch { On, Off }
    }

    public class DoorOpen : Sequence
    {
        object Arguments { get; }

        public DoorOpen(object arguments)
        {
            Current = Step1;
            Arguments = arguments;
            Console.WriteLine("init");
        }

        dlgStep Step1()
        {
            Console.WriteLine("1");
            return Step2;
        }

        dlgStep Step2()
        {
            Console.WriteLine("2");
            return Step3;
        }

        dlgStep Step3()
        {
            Console.WriteLine("3");
            return null;
        }
    }
}


{状態・イベント・アクション}これをセクション別に分けて置くことで
同じ状態でも異なったイベントやアクションを起こすことが可能になるというのが確認できたところで次回へ続く。


Androider