view Implab/Components/RunnableComponent.cs @ 184:d6a8cba73acc ref20160224

working on runnable component
author cin
date Sat, 16 Apr 2016 03:23:26 +0300
parents c32688129f14
children 822aab37b107
line wrap: on
line source

using System;
using Implab.Formats;

namespace Implab.Components {
    public class RunnableComponent : Disposable, IRunnable, IInitializable {
        enum Commands {
            Ok = 0,
            Fail,
            Init,
            Start,
            Stop,
            Dispose,
            Last = Dispose
        }

        class StateMachine {
            static readonly ExecutionState[,] _transitions;

            static StateMachine() {
                _transitions = new ExecutionState[(int)ExecutionState.Last + 1, (int)Commands.Last + 1];

                Edge(ExecutionState.Created, ExecutionState.Ready, Commands.Ok);
                Edge(ExecutionState.Created, ExecutionState.Failed, Commands.Fail);

                Edge(ExecutionState.Ready, ExecutionState.Starting, Commands.Start);
                Edge(ExecutionState.Ready, ExecutionState.Disposed, Commands.Dispose);

                Edge(ExecutionState.Starting, ExecutionState.Running, Commands.Ok);
                Edge(ExecutionState.Starting, ExecutionState.Failed, Commands.Fail);
                Edge(ExecutionState.Starting, ExecutionState.Stopping, Commands.Stop);
                Edge(ExecutionState.Starting, ExecutionState.Disposed, Commands.Dispose);

                Edge(ExecutionState.Running, ExecutionState.Failed, Commands.Fail);
                Edge(ExecutionState.Running, ExecutionState.Stopping, Commands.Stop);
                Edge(ExecutionState.Running, ExecutionState.Disposed, Commands.Dispose);

                Edge(ExecutionState.Stopping, ExecutionState.Failed, Commands.Fail);
                Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Ok);
                Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Dispose);
            }

            static void Edge(ExecutionState s1, ExecutionState s2, Commands cmd) {
                _transitions[(int)s1, (int)cmd] = s2;
            }

            public ExecutionState State {
                get;
                private set;
            }

            public StateMachine(ExecutionState initial) {
                State = initial;
            }

            public bool Move(Commands cmd) {
                var next = _transitions[(int)State, (int)cmd];
                if (next == ExecutionState.Undefined)
                    return false;
                State = next;
                return true;
            }
        }

        IPromise m_pending;
        Exception m_lastError;

        readonly StateMachine m_stateMachine;

        protected RunnableComponent(bool initialized) {
            m_stateMachine = new StateMachine(initialized ? ExecutionState.Ready : ExecutionState.Created);
        }

        void ThrowInvalidCommand(Commands cmd) {
            throw new InvalidOperationException(String.Format("Commnd {0} is not allowed in the state {1}", cmd, m_stateMachine.State));
        }

        protected void Move(Commands cmd) {
            lock (m_stateMachine)
                if (!m_stateMachine.Move(cmd))
                    ThrowInvalidCommand(cmd);
        }

        protected void Fail(Exception err) {
            lock (m_stateMachine) {
                if (!m_stateMachine.Move(Commands.Fail))
                    ThrowInvalidCommand(Commands.Fail);

                m_lastError = err;
            }
        }

        protected void Success() {
            Move(Commands.Ok);
        }

        protected void Invoke(Commands cmd, Action action) {
            Move(cmd);
            try {
                action();
                Move(Commands.Ok);
            } catch (Exception err) {
                Fail(err);
                throw;
            }
        }

        protected IPromise InvokeAsync(Commands cmd, Func<IPromise> action) {
            Move(cmd);
            var medium = new Promise();

            IPromise promise = null;

            promise = medium.Then(
                () => {
                    lock(m_stateMachine) {
                        if (m_pending == promise) {
                            m_pending = null;
                            Move(Commands.Ok);
                        }
                    }
                }, e => {
                    if (m_pending == promise) {
                        m_pending = null;
                        Fail(
                    }
                }
            );



            return Safe.InvokePromise(action).Then(
                Success,
                Fail
            );
        }

        void AddPending(IPromise result) {

        }


        #region IInitializable implementation

        public void Init() {
            Invoke(Commands.Init, OnInitialize);
        }

        protected virtual void OnInitialize() {
        }

        #endregion

        #region IRunnable implementation

        public IPromise Start() {
            Move(Commands.Start);

            return Safe.InvokePromise(OnStart).Then(
                () => {
                    Move(Commands.Ok);
                    Run();
                },
                () => {
                    Move(Commands.Fail);
                }
            );
        }

        protected virtual IPromise OnStart() {
            return Promise.SUCCESS;
        }

        protected virtual void Run() {
        }

        public IPromise Stop() {
            throw new NotImplementedException();
        }

        public ExecutionState State {
            get {
                throw new NotImplementedException();
            }
        }

        public Exception LastError {
            get {
                throw new NotImplementedException();
            }
        }

        #endregion
    }
}