Files
ENIG/Cs_HMI/StateMachine/StateMachine.cs
2025-04-24 16:24:01 +09:00

335 lines
8.4 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Project.StateMachine
{
public partial class StateMachine : IDisposable
{
public DateTime UpdateTime;
public TimeSpan RunSpeed = new TimeSpan(0);
private Boolean bLoop = true;
private DateTime StepStartTime;
private byte _messageOption;
private System.Threading.Thread worker;
private Boolean firstRun = false;
public Boolean WaitFirstRun = false;
public StateMachine()
{
WaitFirstRun = false;
UpdateTime = DateTime.Now;
_messageOption = 0xFF; //모든메세지가 오도록 한다.
worker = new System.Threading.Thread(new System.Threading.ThreadStart(Loop))
{
IsBackground = false
};
StepStartTime = DateTime.Parse("1982-11-23");
}
~StateMachine()
{
Dispose(false);
}
#region "Dispose"
private bool disposed = false;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if (!this.disposed)
{
if (disposing)
{
// Dispose managed resources.
}
Stop();
// unmanaged resources here.
// If disposing is false,
// only the following code is executed.
disposed = true;
}
}
#endregion
public void Stop()
{
bLoop = false;
//if (worker.IsAlive)
// if (worker.Join(1000) == false)
// worker.Abort();
}
public void Start()
{
if (worker.ThreadState == System.Threading.ThreadState.Stopped)
{
worker = new System.Threading.Thread(new System.Threading.ThreadStart(Loop))
{
IsBackground = false
};
}
bLoop = true;
worker.Start();
}
private Boolean _isthreadrun;
public Boolean IsThreadRun { get { return _isthreadrun; } }
void Loop()
{
_isthreadrun = true;
if (GetMsgOpt(EMsgOpt.NORMAL)) RaiseMessage("SM", "Start");
while (bLoop)
{
RunSpeed = DateTime.Now - UpdateTime; //이전업데이트에서 현재 시간의 오차 한바퀴 시간이 표시됨
UpdateTime = DateTime.Now;
//항상 작동하는 경우
SPS?.Invoke(this, new EventArgs());
//작동스텝이 변경되었다면 그것을 알림 처리한다.
if (GetNewStep != Step)
{
if (GetMsgOpt(EMsgOpt.STEPCHANGE))
RaiseMessage("SM-STEP", string.Format("Step Changed {0} >> {1}", Step, GetNewStep));
StepApply();
firstRun = true;
StepStartTime = DateTime.Now;
}
else
{
//팝업메세지의 종료를 기다리는 식에서 문제가 생긴다
firstRun = WaitFirstRun;
}
//동작중에 발생하는 이벤트
if (Running != null)
{
try
{
Running(this, new RunningEventArgs(Step, firstRun, StepRunTime));
}
catch (Exception ex)
{
RaiseMessage("SM-ERROR", ex.Message);
}
}
System.Threading.Thread.Sleep(1);
}
_isthreadrun = false;
if (GetMsgOpt(EMsgOpt.NORMAL)) RaiseMessage("SM", "Stop");
}
/// <summary>
/// 상태머신의 작동상태
/// </summary>
public eSMStep Step { get { return _step; } }
/// <summary>
/// 상태머신값을 숫자로 반환 합니다. (20이하는 시스템상태이고 그 이상은 사용자 상태)
/// </summary>
public byte StepValue
{
get
{
return (byte)this._step;
}
}
/// <summary>
/// 현재 진행 스텝을 강제로 변경합니다.
/// 일반적인 상태변화는 setNewStep 을 사용하세요.
/// </summary>
/// <param name="step"></param>
public void SetCurrentStep(ERunStep step)
{
RaiseMessage("SM-STEP", string.Format("강제 스텝 변경 {0}->{1}", _runstepo, step));
_runstepo = step;
_runstepn = step;
}
public void SetNewStep(eSMStep newstep_, bool force = false)
{
if (Step != newstep_)
{
if(force)
{
RaiseMessage("SM-STEP", string.Format("강제스텝전환 {0} -> {1}", Step, _newstep));
OldStep = _step;
_step = newstep_;
_newstep = newstep_;
StepChanged?.Invoke(this, new StepChangeEventArgs(OldStep, newstep_));
}
else
{
if (_newstep != newstep_)
{
//바뀌도록 예약을 한다.
_newstep = newstep_;
if (GetMsgOpt(EMsgOpt.STEPCHANGE))
RaiseMessage("SM-STEP", string.Format("Step Reserve {0} -> {1}", Step, _newstep));
}
else
{
//예약은 되어있는데 아직 바뀐것은 아니다.
if (GetMsgOpt(EMsgOpt.STEPCHANGE))
RaiseMessage("SM-STEP", string.Format("Step Already Reserve {0} -> {1}", Step, _newstep));
}
}
}
}
#region "runstep"
private int _runStepSeq = 0;
public int RunStepSeq { get { return _runStepSeq; } }
private DateTime runStepStartTime = DateTime.Parse("1982-11-23");
private ERunStep _runstepn = ERunStep.READY;
private ERunStep _runstepo = ERunStep.READY;
public ERunStep RunStep { get { return _runstepo; } }
public ERunStep RunStepNew { get { return _runstepn; } }
public void SetNewRunStep(ERunStep newStep)
{
if (_runstepn != newStep)
{
// Pub.log.Add(string.Format("set new run step {0}->{1}", _runstepn, newStep));
_runstepn = newStep;
}
}
public void ApplyRunStep()
{
if (_runstepn != _runstepo)
{
//Pub.log.Add(string.Format("apply new run step {0}->{1}", _runstepo, _runstepn));
_runStepSeq = 0;
_runstepo = _runstepn;
UpdateRunStepSeq();// runStepStartTime = DateTime.Now;
}
}
public TimeSpan GetRunSteptime { get { return DateTime.Now - runStepStartTime; } }
public void SetStepSeq(byte value)
{
_runStepSeq = value;
if (GetMsgOpt(EMsgOpt.NORMAL))
{
if (_runStepSeq != value) //변화가잇는겨웅에만 처리 220628
RaiseMessage("STEPSEQ", string.Format("Step Sequence Jump {0} to {1}", _runStepSeq, value));
}
}
public void UpdateRunStepStartTime()
{
runStepStartTime = DateTime.Now;
// Pub.log.Add("Update RunStep Start Time");
}
public void UpdateRunStepSeq(int incvalue = 1)
{
_runStepSeq += incvalue;
UpdateRunStepStartTime();
// Pub.log.Add(string.Format("스텝({0}) 시퀀스증가 신규값={1}", runStep, _runStepSeq));
}
public void ResetRunStepSeq()
{
_runStepSeq = 1;
UpdateRunStepStartTime();
}
/// <summary>
/// runstep 시퀀스값을 1로 설정하고 시작시간도 업데이트 합니다
/// 기본 스텝상태를 READY로 변경 합니다
/// </summary>
public void ClearRunStep()
{
_runStepSeq = 1;
runStepStartTime = DateTime.Now;
_runstepn = ERunStep.READY;
_runstepo = ERunStep.READY;
}
#endregion
public eSMStep GetNewStep
{
get
{
return _newstep;
}
}
private eSMStep _newstep = eSMStep.NOTSET;
public eSMStep OldStep = eSMStep.NOTSET; //171214
private eSMStep _step;
/// <summary>
/// newstep 의 값을 step 에 적용합니다.
/// </summary>
private void StepApply()
{
var ostep = _step;
OldStep = _step; _step = _newstep;
StepChanged?.Invoke(this, new StepChangeEventArgs(ostep, _step));
} //171214
/// <summary>
/// 메세지 출력옵션을 변경 합니다.
/// </summary>
/// <param name="opt"></param>
/// <param name="value"></param>
public void SetMsgOpt(EMsgOpt opt, Boolean value)
{
byte pos = (byte)opt;
if (value)
_messageOption = (byte)(_messageOption | (1 << pos));
else
_messageOption = (byte)(_messageOption & ~(1 << pos));
}
public void SetMegOptOn() { _messageOption = 0xFF; }
public void SetMsgOptOff() { _messageOption = 0; }
public Boolean GetMsgOpt(EMsgOpt opt)
{
byte pos = (byte)opt;
return (_messageOption & (1 << pos)) > 0;
}
public TimeSpan StepRunTime
{
get
{
if (IsThreadRun == false || bLoop == false || StepStartTime.Year == 1982) return new TimeSpan(0);
else return DateTime.Now - StepStartTime;
}
}
public Boolean bPause = false;
/// <summary>
/// 상태머신이 실제 동작중인지 확인합니다.
/// 검사상태 : Step = Run, IsThreadRun, bPause = false
/// </summary>
public Boolean IsRun
{
get
{
if (Step == eSMStep.RUN && _isthreadrun && bPause == false) return true;
else return false;
}
}
}
}