// <summary>ソースコード：ＨＹＭＣＯ演算モデルクラス</summary>
// <author>CommonMP</author>

using System;
using System.Collections.Generic;
using System.Text;

using CommonMP.HYSSOP.CoreImpl.HSData;
using CommonMP.HYSSOP.CoreImpl.HSTools;

using CommonMP.HYMCO.Interface.Model;
using CommonMP.HYMCO.Interface.Data;
using CommonMP.HYMCO.CoreImpl.Data;
using CommonMP.HYMCO.CoreImpl.Tool;

// ToDo namespace は　モデル開発者が変更して、ユニークな名称にして下さい
namespace CommonMP.HYMCO.OptionImpl.TestModel
{
    /// <summary><para>class outline:</para>
    /// <para>演算モデル型紙</para>
    /// </summary>
    /// <remarks><para>history:</para>
    /// <para>[CommonMP][ver 1.0.0][2008/10/01][新規作成]</para>
    /// </remarks>
    public class MyForecastModel : McForecastModelBase
    {
        /// <summary> 演算データ（キャスト用） </summary>
        MyForecastModelCalInfo m_csMyInf = null;   // 便利の為、キャスト用に定義しておく

        //=========================
        // 演算実行前処理関連メソッド
        //=========================


        /// <summary><para>method outline:</para>
        /// <para>入力側の接続情報チェック</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bRtn = ReceiveConnectionCheck(ref csErrorInf, lInputDataNum, csInputCellData)</para>
        /// </example>
        /// <param name="csErrorInf">エラー出力</param>
        /// <param name="lInputDataNum">入力情報数</param>
        /// <param name="csInputCellData">演算に必要な入力情報配列</param>
        /// <returns>=true:正常、=false:異常</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>受信するデータが自モデルが期待している情報か否かをチェックする</para>
        /// </remarks>
        protected override bool ReceiveConnectionCheck(ref McStructErrorInfo csErrorInf, long lInputDataNum, McReceiveCellDataIF[] csInputCellData)
        {
            bool bRtn = true;

            // 入力数のチェックを行う場合
            if (lInputDataNum == 0)
            {
                // 1) モデルに入力がない場合は、警告を出す例です。
               // ver1.5 エラートレース日本語対応
                csErrorInf.AddCheckWarningData(this.m_csElement.GetID(), TestModelDefine.MyForecastModel_MODEL_KIND, 
                    Properties.Resources.STATEMENT_NO_REC_PORT );
               // csErrorInf.AddCheckWarningData(this.m_csElement.GetID(), TestModelDefine.MyForecastModel_MODEL_KIND, "No Receive Port");
                // モデルチェックエラーにはなりません。
            }
            else if (lInputDataNum > 1)
            {
                // 2) 入力が複数ある場合は、エラーを出す例です。
               // ver1.5 エラートレース日本語対応
                csErrorInf.AddCheckErrorData(this.m_csElement.GetID(), TestModelDefine.MyForecastModel_MODEL_KIND, 
                                             Properties.Resources.STATEMENT_MULTI_REC_PORT );
               // csErrorInf.AddCheckErrorData(this.m_csElement.GetID(), TestModelDefine.MyForecastModel_MODEL_KIND,
               //                              "Too many Receive Ports. (Not support multi-Receive Ports)");
                bRtn = false; // ←モデルチェックエラーになります。
            }

            for (long lLp = 0; lLp < lInputDataNum; lLp++)
            {   // 3) 入力する伝送データ数分繰り返します。

                m_csMyInf.m_lInDataNum += 1; // ←入力数をカウントします。
            }

            // 4) 入力数分、データ配列を作成します。
            m_csMyInf.m_dQInData = new double[m_csMyInf.m_lInDataNum];
            m_csMyInf.m_dQInInternalData = new double[m_csMyInf.m_lInDataNum];

            return bRtn;
        }
        /// <summary><para>method outline:</para>
        /// <para>出力側の接続情報チェック</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bRtn = SendConnectionCheck(ref csErrorInf, lOutputDataNum, csOutputCellData)</para>
        /// </example>
        /// <param name="csErrorInf">エラー出力</param>
        /// <param name="lOutputDataNum">出力情報数</param>
        /// <param name="csOutputCellData">出力情報配列</param>
        /// <returns>=true:正常、=false:異常</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>送信端子に設定されている伝送データが自モデルが期待している情報か否かをチェックする</para>
        /// </remarks>
        protected override bool SendConnectionCheck(ref McStructErrorInfo csErrorInf, long lOutputDataNum, McSendCellDataIF[] csOutputCellData)
        {
            bool bRtn = true;

            // 出力数のチェックを行う場合
            if (lOutputDataNum == 0)
            {
                // 1) モデルに出力がない場合は、エラーを出す例です。
               // ver1.5 エラートレース日本語対応
                csErrorInf.AddCheckErrorData(this.m_csElement.GetID(), TestModelDefine.MyForecastModel_MODEL_KIND, 
                     Properties.Resources.STATEMENT_NO_SND_PORT );
               // csErrorInf.AddCheckErrorData(this.m_csElement.GetID(), TestModelDefine.MyForecastModel_MODEL_KIND, "No Send port.");
                bRtn = false; // ←モデルチェックエラーになります。
            }
            else if (lOutputDataNum > 2)
            {
                // 2) 出力が2つ以上ある場合は、エラーを出す例です。
               // ver1.5 エラートレース日本語対応
                csErrorInf.AddCheckErrorData(this.m_csElement.GetID(), TestModelDefine.MyForecastModel_MODEL_KIND, 
                     Properties.Resources.STATEMENT_TOO_MANY_SND_PORT );
               // csErrorInf.AddCheckErrorData(this.m_csElement.GetID(), TestModelDefine.MyForecastModel_MODEL_KIND, "Too many Send Ports.");
                bRtn = false; // ←モデルチェックエラーになります。
            }

            for (long lLp = 0; lLp < lOutputDataNum; lLp++)
            {   // 3) 出力する伝送データ数分繰り返します。

                m_csMyInf.m_lOutDataNum += 1; // ←出力数をカウントします。
            }

            // 4) 出力数分、データ配列を作成します。
            m_csMyInf.m_dQOutData = new double[m_csMyInf.m_lOutDataNum];
            m_csMyInf.m_dQOutInternalData = new double[m_csMyInf.m_lOutDataNum];

            return bRtn;
        }


        /// <summary><para>method outline:</para>
        /// <para>モデルを初期化する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>Initialize(csInitialData, lInputDataNum, csInputCellData)</para>
        /// </example>
        /// <param name="csInitialData">初期化設定情報</param>
        /// <param name="lInputDataNum">入力情報数</param>
        /// <param name="csInputCellData">演算に必要な入力情報配列</param>
        /// <returns>=true:正常、=false:異常</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        protected override bool Initialize(ref McPropertyInfoRoot csInitialData, long lInputDataNum, ref McReceiveCellDataIF[] csInputCellData)
        {
            bool bRtn = false;
            // 引数で与えられたデータを　キャストしています。
            McInitialInfo csInDt = csInitialData as McInitialInfo;

            if (csInDt != null)
            {
                for (long lLp = 0; lLp < m_csMyInf.m_lOutDataNum; lLp++)
                {
                    // 1) 初期化情報よりモデルの状態（出力情報）を初期化します。
                    csInDt.GetInfo("Q_Initial", ref m_csMyInf.m_dQOutData[lLp]);
                    // 2) 初期化情報よりモデルの状態（内部状態）を初期化します。
                    csInDt.GetInfo("Q_internal_Initial", ref m_csMyInf.m_dQOutInternalData[lLp]);
                }

                bRtn = true;
            }
            return bRtn;
        }
        /// <summary><para>method outline:</para>
        /// <para>モデルを初期化する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>OnlineInitialize(csInitialData)</para>
        /// </example>
        /// <param name="csInitialData">初期化設定情報</param>
        /// <param name="lInputDataNum">入力情報数</param>
        /// <param name="csInputCellData">演算に必要な入力情報配列</param>
        /// <returns>bool</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        protected override bool OnlineInitialize(ref McPropertyInfoRoot csInitialData, long lInputDataNum, ref McReceiveCellDataIF[] csInputCellData)
        {
            // To Do
            // モデルの係数等、必要な情報に対して、引数で与えられたプロパティ情報の内容を読み取って、設定します。
            // 但し、実行中に変更して困る情報は設定しないで下さい。
            return true;
        }
        //=======================
        // 演算実行処理関連メソッド
        //=======================

        /// <summary><para>method outline:</para>
        /// <para>モデル演算</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>long lRtn = Calculate(lInputDataNum, ref csInputCellData)</para>
        /// </example>
        /// <param name="lInputDataNum">入力情報数</param>
        /// <param name="csInputCellData">演算に必要な入力情報配列</param>
        /// <returns>=0:正常 -1:異常</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        protected override long Calculate(long lInputDataNum, ref McReceiveCellDataIF[] csInputCellData)
        {
            // ↓データを受信して、受信した情報を取得する例です。↓
            long lRtn = 0;
            double dQOut = 0.0;
            double dQOutInternalData = 0.0;
            HySCellData csRcvCellData = null;
            // 入力数分、受信処理を行います。
            for (long lLp = 0; lLp < lInputDataNum; lLp++)
            {
                // 1) ポイント時系列伝送データを取得します。（1次元配列の0番目のセルを取得します。）
                csRcvCellData = csInputCellData[lLp].GetInterpolatedCell(0);
                // 2) セル内にある、流量(入力情報)データを取得します。（セル内の0番目の情報を取得します。）
                m_csMyInf.m_dQInData[lLp] = csInputCellData[lLp].Data(csRcvCellData, 0); // ←受信した情報は、演算データクラスに格納します。
                // 3) セル内にある、流量(内部状態)データを取得します。（セル内の1番目の情報を取得します。）
                m_csMyInf.m_dQInInternalData[lLp] = csInputCellData[lLp].Data(csRcvCellData, 1); // ←受信した情報は、演算データクラスに格納します。
            }
            // ↓取得した受信情報を元に、未来計算型モデルが行う演算例です。↓
            // 入力数分、演算を行います。
            for (long lLp = 0; lLp < lInputDataNum; lLp++)
            {
                // 4) 未来計算型モデルでは、現在の入力状況から、未来（δt後）の状態を算出します。
                dQOut = m_csMyInf.m_dQInData[lLp] + (m_csMyInf.m_dConstant * m_csTotalPassingTime.GetTime());
                // 5) 未来計算型モデルでは、自モデルの内部状態は変化しません。
                dQOutInternalData = m_csMyInf.m_dQInInternalData[lLp];
            }
            if (lInputDataNum != 0)
            {
                for (long lLp = 0; lLp < m_csMyInf.m_lOutDataNum; lLp++)
                {
                    // 6) 演算した情報は、演算データクラスに格納します。
                    m_csMyInf.m_dQOutData[lLp] = dQOut;
                    m_csMyInf.m_dQOutInternalData[lLp] = dQOutInternalData;
                }
                // ===ログ出力の例です===
                // 書式は(演算時刻、要素ID、クラス名、メソッド名、確認したいデータ)
                // デバッグが完了したらコメントにしてください!! ⇒演算速度を落とさないために
                McLog.DebugOut(GetSimulationTime(), GetID(), "MyForecastModel", "Calculate", "m_dQInData[0]=" + ((double)m_csMyInf.m_dQInData[0]).ToString());
                McLog.DebugOut(GetSimulationTime(), GetID(), "MyForecastModel", "Calculate", "m_dQInInternalData[0]=" + ((double)m_csMyInf.m_dQInInternalData[0]).ToString());
                // ======================
            }
            return lRtn;
        }


        /// <summary><para>method outline:</para>
        /// <para>モデル演算結果を外部のエレメントに対して公開する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>long lRtn = DataFusion( lOutputDataNum, ref csOutputCellData)</para>
        /// </example>
        /// <param name="lOutputDataNum">出力情報数</param>
        /// <param name="csOutputCellData">出力情報配列</param>
        /// <returns>=0:正常 -1:異常</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        protected override long DataFusion(long lOutputDataNum, ref McSendCellDataIF[] csOutputCellData)
        {
            // ↓演算結果を設定して、データを送信する例です。↓
            long lRtn = 0;
            HySCellData[] csSndCellData = null;
            // 出力数分、送信処理を行います。
            for (long lLp = 0; lLp < lOutputDataNum; lLp++)
            {
                // 1) ポイント時系列伝送データを取得します。（送信情報格納のための1次元配列のセルを準備します。）
                csSndCellData = csOutputCellData[lLp].PrepareSendCellD1(); 
                // 2) 流量(出力情報)データを設定します。（セル内の0番目に情報を設定します。）
                csSndCellData[0].m_dData[0] = m_csMyInf.m_dQOutData[lLp]; // ←送信する情報は、演算データクラスより取得します。
                // 3) 流量(内部状態)データを設定します。（セル内の1番目に情報を設定します。）
                csSndCellData[0].m_dData[1] = m_csMyInf.m_dQOutInternalData[lLp]; // ←送信する情報は、演算データクラスより取得します。
            }
            // ===ログ出力の例です===
            // 書式は(演算時刻、要素ID、クラス名、メソッド名、確認したいデータ)
            // デバッグが完了したらコメントにしてください!! ⇒演算速度を落とさないために
            McLog.DebugOut(GetSimulationTime(), GetID(), "MyForecastModel", "DataFusion", "m_dQOutData[0]=" + ((double)m_csMyInf.m_dQOutData[0]).ToString());
            McLog.DebugOut(GetSimulationTime(), GetID(), "MyForecastModel", "DataFusion", "m_dQOutInternalData[0]=" + ((double)m_csMyInf.m_dQOutInternalData[0]).ToString());
            // ======================
            return lRtn;
        }


        //====================
        // その他必要なメソッド
        //====================

        /// <summary><para>method outline:</para>
        /// <para>プロパティ情報を設定する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>SetProperty(csCellMdlPropertyInfo)</para>
        /// </example>
        /// <param name="csCellMdlPropertyInfo">セル型プロパティ情報</param>
        /// <returns>=true:正常、=false:異常</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public override bool SetProperty(McCellModelPropertyIF csCellMdlPropertyInfo)
        {
            bool bRtn = false;
            // 使用しやすいようにキャストしておく
            m_csMyInf = (MyForecastModelCalInfo)m_csCalInfo;

            // プロパティ設定
            McCellModelPropertyInfo csPrptyInfo = csCellMdlPropertyInfo as McCellModelPropertyInfo;
            if (csPrptyInfo != null)
            {
                // 1) 演算ステップ時刻設定
                this.m_csDltTime = new HySTime(csPrptyInfo.GetStepTime());

                // 2) プロパティ情報から未来計算型モデル定数を設定します。
                csPrptyInfo.GetInfo("m_dForeModelConst", ref m_csMyInf.m_dConstant);

                bRtn = true;

            }
            return bRtn;
        }
        /// <summary><para>method outline:</para>
        /// <para>演算実行中断中のプロパティ等情報設定</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bRtn = SetOnlineProperty(csCellMdlPropertyInfo)</para>
        /// </example>
        /// <param name="csCellMdlPropertyInfo">プロパティ情報</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>演算中断中に　プロパティ情報を変更した場合にコールされる</para>
        /// </remarks>
        public override bool SetOnlineProperty(McCellModelPropertyIF csCellMdlPropertyInfo)
        {
            bool bRtn = false;
            // プロパティ設定
            McCellModelPropertyInfo csPrptyInfo = csCellMdlPropertyInfo as McCellModelPropertyInfo;
            if (csPrptyInfo != null)
            {
                bRtn = true;

                // 演算ステップ時刻設定（不要ならば削除）
                // this.m_csDltTime = new HySTime(csPrptyInfo.GetStepTime());

                // To Do
                // モデルの係数等、必要な情報に対して、引数で与えられたプロパティ情報の内容を読み取って、設定します。
                // 但し、実行中に変更して困る情報は設定しないで下さい。
            }
            return bRtn;
        }
        /// <summary><para>method outline:</para>
        /// <para>計算開始時に動作する処理</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bRtn = ReadyCalculation( )</para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>true :正常 , false:異常</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>計算開始の　最初に１回だけコールされるメソッドです。不要ならば、本メソッドをオーバーライドする必要はありません。</para>
        /// </remarks>
        public override bool ReadyCalculation()
        {
            //McLog.DebugOut(GetSimulationTime(), GetID(), "MyModel", "ReadyCalculation", "in");

            // ToDo
            // 計算開始の　最初に１回だけコールされるメソッドです。不要ならば、本メソッドをオーバーライドする必要はありません。
            // （メソッド自身を削除してください）

            //McLog.DebugOut(GetSimulationTime(), GetID(), "MyModel", "ReadyCalculation", "out");
            return true;
        }
        /// <summary><para>method outline:</para>
        /// <para>計算中断時に動作する処理</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bRtn = SuspendCalculation( )</para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>true :正常 , false:異常</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>オペレーター操作等により計算中断時に　コールされるメソッドです。不要ならば、本メソッドをオーバーライドする必要はありません。</para>
        /// </remarks>
        public override bool SuspendCalculation()
        {
            //McLog.DebugOut(GetSimulationTime(), GetID(), "MyModel", "SuspendCalculation", "in");

            // ToDo
            // オペレーター操作等により計算中断時に　コールされるメソッドです。不要ならば、本メソッドをオーバーライドする必要はありません。
            // （メソッド自身を削除してください）

            //McLog.DebugOut(GetSimulationTime(), GetID(), "MyModel", "SuspendCalculation", "out");
            return true;
        }
        /// <summary><para>method outline:</para>
        /// <para>計算終了時に動作する処理</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bRtn = CompleteCalculation( )</para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>true :正常 , false:異常</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>計算終了時　最後の最初に１回だけコールされるメソッドです。不要ならば、本メソッドをオーバーライドする必要はありません。</para>
        /// </remarks>
        public override bool CompleteCalculation()
        {
            //McLog.DebugOut(GetSimulationTime(), GetID(), "MyModel", "CompleteCalculation", "in");

            // ToDo
            // 計算終了時　最後の最初に１回だけコールされるメソッドです。不要ならば、本メソッドをオーバーライドする必要はありません。
            // （メソッド自身を削除してください）

            //McLog.DebugOut(GetSimulationTime(), GetID(), "MyModel", "CompleteCalculation", "out");
            return true;
        }

        /// <summary><para>method outline:</para>
        /// <para>演算用刻み時間を自動的に変更する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>ChangeDeltaTimeAutomatically()</para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public override void ChangeDeltaTimeAutomatically()
        {
            //McLog.DebugOut(GetSimulationTime(), GetID(), "MyModel", "ChangeDeltaTimeAutomatically", "in");

            // ToDo
            // 演算中条件によって　自身のδTを(this.m_csDltTime) を変更するメソッドです。
            // 不要ならば、本メソッドをオーバーライドする必要はありません。（メソッド自身を削除してください）

            //McLog.DebugOut(GetSimulationTime(), GetID(), "MyModel", "ChangeDeltaTimeAutomatically", "out");
        }

    }
    //
}
