実際のソフトウェア開発、特に産業用ソフトウェアでは、各デバイスは複雑な状態と状態間の切り替え機能要件を持っており、この場合、状態と状態間の切り替えを管理する方法、および対応する状態の機能制御が非常に重要な問題となっています。処理がうまくいかない場合、この複雑な状態は、“麺のような”もつれたカップリング、混乱、本当に“常にカット、混乱”になります。どうすれば問題を解決できますか?今日は、Statelessコンポーネントを使用して状態の管理とトリガーを完了する方法を簡単な例で説明します。学習と共有のためにのみ使用します。
State Modeとは?
ステートモードとステートマシンについては、次のようになります。
ステートモード:オブジェクトの内部状態が変化すると動作を変更できます。オブジェクトは、それが属するクラス"を変更したように見えます。State Pattern The object will appear to change its class ".。この定義は少し抽象的であり、別の解釈では、状態所有者は変更の動作を状態オブジェクトに委任し、状態所有者自身は状態のみを所有し(もちろん、状態オブジェクトを破棄することもできる)、状態オブジェクトは変更の責任を果たす。
ステートマシン:“指定された状態フローチャートに従い、現在実行されている動作に応じて、現在の状態を予め定められた条件に従って新しい状態に変更する”。ステートマシンには4つの要素、すなわち現状態、条件、動作、副状態がある。このうち、現状と条件は“原因”であり、行動と次の状態は“果”である。
- 現在-現在のオブジェクトの状態を示します。
- Condition-条件が満たされると、現在のオブジェクトがアクションをトリガーする
- アクション- 条件が満たされた後に実行するアクション
- セカンダリステート- 条件が満たされた後の現在のオブジェクトの新しい状態。二次状態は現在状態に対するものであり、一度トリガーされると現在状態になる。
状態遷移図“UMLモデリングでは、ある特定のオブジェクトの可能なすべての状態、および様々なイベントの発生に起因する状態間の遷移と変化を記述するために使用されることがよく見られます。
Statelessとは
Statelessは軽量で高性能なステートライブラリで、. NET Standardに実装されており、. NET Frameworkと. NET Coreプロジェクトの両方で使用でき、状態遷移ロジックを簡単に実装するのに役立ちます。
- github:https://github.com/dotnet-state-machine/stateless

シナリオの説明例
この記事では、Statelessコンポーネントを使用して、次の状態の管理と起動を実現します。現在、デバイスソフトウェアには次のような状態Stateがあります。
- アイドル状態は、現在実行中のタスクがなく、アイドル状態であることを示します。
- Running状態は、現在作業中で実行中であることを示します。
- Malfunctionステータスは、現在例外があり、障害状態にあることを示します。
- 回復状態は、修理後、障害から回復し、修復状態にあることを示します。
トリガーアクションTriggerを使用します。
- 作業は、現在作業を開始していることを示し、アイドル状態から入力することができ、回復状態から入力することができ、プログラムは実行状態に入ります。
- Fatalは例外が発生したことを示し、実行状態からのみアクセスでき、プログラムはMalfunction状態に入ります。
- Repairは例外を修復することを意味し、Malfunction状態からのみアクセスでき、プログラムは回復状態に入ります。
- 終了は完了を意味し、実行状態から入力することも、回復状態から入力することもでき、プログラムはアイドル状態に入ります。
上記の状態StateとトリガーアクションTrigger、および間のフローは、次の図に示されています。

プロジェクトを作成する
状態の変化を示すために、Win Formアプリケーション“Okcoder. Stateless.Win Tool”と、共通ライブラリ“Okcoder. Stateless.Common”を以下のように作成します。

Statelessコンポーネントのインストール
Visual Studio 2022開発ツールでは、NuGetパッケージマネージャーからインストールできます。ソリューションの右クリックで、右クリックメニューを開き、“ソリューションのNuGetパッケージの管理”をクリックし、NuGetパッケージのソリューションを開いてStatelessを検索してインストールします。現在の最新バージョンはv5.18.0で、次のとおりです。

ステータスとトリガーの定義
ステートマシンは状態の変更とイベントのトリガーを管理するので、状態とトリガーを定義します。これらは列挙型であり、インスタンスシナリオの説明に従ってToolStateを定義します。
namespace Okcoder.Stateless.Common
{
/// <summary>
/// 工具状态
/// </summary>
public enum ToolState
{
Idle=0, //空闲状态
Running=1, //运行状态
Malfunction = 2, // 故障状态
Recovery=3, //已恢复状态
}
}
ToolTriggerは次のように定義されます。
namespace Okcoder.Stateless.Common
{
public enum ToolTrigger
{
Work = 0, // 开始运转
Fatal = 1, // 出异常
Finish = 2, // 完成
Repair=3, //维修
}
}
インターフェイスの定義
次のように、ステートマシンが持つ機能(トリガーアクション、状態判断、ステートマシンからDotGraphへのエクスポートなど)を表すIToolState Serviceインターフェイスを定義します。
namespace Okcoder.Stateless.Common
{
/// <summary>
/// 状态机接口
/// </summary>
public interface IToolStateService
{
/// <summary>
/// 初始化状态
/// </summary>
void Init();
/// <summary>
/// 开始工作
/// </summary>
/// <returns></returns>
ToolResult Work();
/// <summary>
/// 遇到严重错误
/// </summary>
/// <returns></returns>
ToolResult Fatal();
/// <summary>
/// 维修
/// </summary>
/// <returns></returns>
ToolResult Repair();
/// <summary>
/// 完成
/// </summary>
/// <returns></returns>
ToolResult Finish();
/// <summary>
/// 是否是Idle状态
/// </summary>
/// <returns></returns>
bool IsIdleState();
/// <summary>
/// 是否运行状态
/// </summary>
/// <returns></returns>
bool IsRunningState();
/// <summary>
/// 是否故障状态
/// </summary>
/// <returns></returns>
bool IsMalfunctionState();
/// <summary>
/// 是否恢复状态
/// </summary>
/// <returns></returns>
bool IsRecoveryState();
/// <summary>
/// 获取当前状态
/// </summary>
/// <returns></returns>
ToolState GetToolState();
/// <summary>
/// 状态机导出DotGraph
/// </summary>
/// <param name="path"></param>
void ExportDotGraph(string path);
}
}
デバイスジョブを定義するIToolWork Serviceインターフェイス。デバイスがジョブを実行し、アクションを実行することを示します。以下の通り。
namespace Okcoder.Stateless.Common
{
/// <summary>
/// 设备工作流程接口
/// </summary>
public interface IToolWorkService
{
/// <summary>
/// 是否制造故障
/// </summary>
bool IsMakeFatal { get; set; }
/// <summary>
/// 开始工作
/// </summary>
void DoWork();
/// <summary>
/// 维修
/// </summary>
void Repair();
}
}
サービスの実現
ステートマシンサービスToolState Serviceを実装します。これは主にStatelessコンポーネントを使用して状態の管理とアクション切り替え状態をトリガーします。
using Stateless;
using Stateless.Graph;
using System;
using System.IO;
using System.Text;
namespace Okcoder.Stateless.Common
{
/// <summary>
/// 状态机服务
/// </summary>
public class ToolStateService : IToolStateService
{
private Action<ToolState> toolAction;// 动作
private StateMachine<ToolState, ToolTrigger> toolStateMachine;
public ToolStateService(Action<ToolState> toolAction)
{
//定义状态机
this.toolStateMachine = new StateMachine<ToolState, ToolTrigger>(ToolState.Idle);
//配置每一个状态所允许的动作
this.toolStateMachine.Configure(ToolState.Idle).Permit(ToolTrigger.Work, ToolState.Running);
this.toolStateMachine.Configure(ToolState.Running).Permit(ToolTrigger.Fatal, ToolState.Malfunction).Permit(ToolTrigger.Finish, ToolState.Idle);
this.toolStateMachine.Configure(ToolState.Malfunction).Permit(ToolTrigger.Repair, ToolState.Recovery);
this.toolStateMachine.Configure(ToolState.Recovery).Permit(ToolTrigger.Work, ToolState.Running).Permit(ToolTrigger.Finish, ToolState.Idle);
this.toolAction = toolAction;
}
/// <summary>
/// 初始化状态
/// </summary>
public void Init()
{
this.toolAction(ToolState.Idle);
}
/// <summary>
/// 响应触发动作
/// </summary>
/// <param name="trigger"></param>
private ToolResult Fire(ToolTrigger trigger)
{
ToolResult toolResult = new ToolResult();
try
{
if (!this.toolStateMachine.CanFire(trigger))
{
toolResult.IsOk = false;
toolResult.Desc = $"当前状态是{this.toolStateMachine.State},不允许执行{trigger}操作";
}
else
{
this.toolStateMachine.Fire(trigger);
toolResult.IsOk = true;
toolResult.Desc = "Ok";
DoAction();
}
}
catch (InvalidOperationException ex)
{
toolResult.IsOk = false;
toolResult.Desc = ex.Message;
}
return toolResult;
}
/// <summary>
/// 判断是否在状态
/// </summary>
/// <param name="toolState"></param>
/// <returns></returns>
private bool IsInState(ToolState toolState)
{
return this.toolStateMachine.IsInState(toolState);
}
/// <summary>
/// 事件通知
/// </summary>
private void DoAction()
{
if (this.toolAction != null)
{
this.toolAction(this.toolStateMachine.State);
}
}
/// <summary>
/// 开始工作
/// </summary>
public ToolResult Work()
{
return this.Fire(ToolTrigger.Work);
}
/// <summary>
/// 遇到严重错误
/// </summary>
public ToolResult Fatal()
{
return this.Fire(ToolTrigger.Fatal);
}
/// <summary>
/// 维修
/// </summary>
public ToolResult Repair()
{
return this.Fire(ToolTrigger.Repair);
}
/// <summary>
/// 完成
/// </summary>
public ToolResult Finish()
{
return this.Fire(ToolTrigger.Finish);
}
/// <summary>
/// 是否是Idle状态
/// </summary>
/// <returns></returns>
public bool IsIdleState()
{
return this.IsInState(ToolState.Idle);
}
/// <summary>
/// 是否运行状态
/// </summary>
/// <returns></returns>
public bool IsRunningState()
{
return this.IsInState(ToolState.Running);
}
/// <summary>
/// 是否故障状态
/// </summary>
/// <returns></returns>
public bool IsMalfunctionState()
{
return this.IsInState(ToolState.Malfunction);
}
/// <summary>
/// 是否恢复状态
/// </summary>
/// <returns></returns>
public bool IsRecoveryState()
{
return this.IsInState(ToolState.Recovery);
}
public ToolState GetToolState()
{
return this.toolStateMachine.State;
}
/// <summary>
/// 状态机导出DotGraph
/// </summary>
/// <param name="path"></param>
public void ExportDotGraph(string path)
{
var info = this.toolStateMachine.GetInfo();
string graph = UmlDotGraph.Format(info);
using (FileStream stream = File.OpenWrite(path))
{
using (StreamWriter writer = new StreamWriter(stream))
{
writer.Write(graph);
writer.Close();
}
stream.Close();
}
}
}
}
StateMachineはステートマシンのコアクラスで、ToolStateとToolTriggerを表す2つのジェネリックパラメータを持ち、コンストラクタに初期状態を渡します。この例ではToolStage.Idleです。
StateMachineは、オブジェクトインスタンスのConfigureメソッドで許可される状態を設定し、Permitメソッドで現在の状態でTriggerで遷移できる新しい状態を設定します。
設定が完了すると、Fireメソッドでトリガーアクションに応答し、IsInStateメソッドで現在指定された状態にあるかどうかを判定します。
在状态机服务类构造函数中,接收一个Action<string>的委托方法,用于输出状态到UI层,当然也可以采用其他事件订阅发布的解耦方法。
ここで、ToolResultは、ステートマシンの実行後に返されるインタフェースを表します。これは、次のようなクラスです。
namespace Okcoder.Stateless.Common
{
/// <summary>
/// 工具执行结果
/// </summary>
public class ToolResult
{
public bool IsOk { get; set; }
public string Desc { get; set; }
}
}
作業サービスToolWork Serviceを実装し、主にデバイスツールの実行をシミュレートし、作業を開始すると、UIページにテキストを出力し、例外がある場合は停止します。例外がない場合は、実行が完了します。
namespace Okcoder.Stateless.Common
{
/// <summary>
/// 设备工作服务
/// </summary>
public class ToolWorkService:IToolWorkService
{
private Action<string> _progress;
private IToolStateService _toolStateService;
private bool _isMakeFatal=false;
public bool IsMakeFatal
{
get { return _isMakeFatal; }
set { _isMakeFatal = value; }
}
public ToolWorkService(IToolStateService toolStateService, Action<string> progress,bool isMakeFatal=false)
{
_progress = progress;
_toolStateService = toolStateService;
_isMakeFatal = isMakeFatal;
}
public void DoWork()
{
BeginWork();
Work();
Finish();
}
public void Repair()
{
var result = this._toolStateService.Repair();
if (result.IsOk)
{
OutPutInfo($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} 故障被修好了");
}
else
{
OutPutInfo($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} 故障没有被修好");
}
}
/// <summary>
/// 开始工作
/// </summary>
private void BeginWork()
{
var result = this._toolStateService.Work();
if (result.IsOk)
{
OutPutInfo($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} 开始工作了");
}
else
{
OutPutInfo($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} 开始工作失败了");
}
}
/// <summary>
/// 工作过程
/// </summary>
private void Work()
{
for (int i = 0; i < 30; i++)
{
if (_isMakeFatal)
{
if (i > 10)
{
MarkFatal();// 运行一段时间,报故障
break;
}
}
OutPutInfo($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} 当前正在工作 - {i}");
Thread.Sleep(300);
}
}
/// <summary>
/// 完成工作
/// </summary>
private void Finish()
{
var result = this._toolStateService.Finish();
if (result.IsOk)
{
OutPutInfo($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} 完成工作了");
}
else
{
OutPutInfo($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} {result.Desc}");
}
}
private void MarkFatal()
{
var result = this._toolStateService.Fatal();
if (result.IsOk)
{
OutPutInfo($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} 哎呀,出错了呢?");
}
else
{
OutPutInfo($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} 制造故障失败");
}
}
/// <summary>
/// 输出信息
/// </summary>
/// <param name="msg"></param>
private void OutPutInfo(string msg)
{
if (_progress != null)
{
_progress(msg);
}
}
}
}
其中构造函数接收一个Action<string>的委托,用于向UI输出信息,当然也可以是其他的形式。
UI呼び出し
Okcoder. Stateless.Win Toolプロジェクトで、FrmMainページを作成し、コンストラクタでIToolStateServiceとIToolWork Serviceのオブジェクトインスタンスを定義し、Loadメソッドで初期化します。
ユーザーが“Work”をクリックすると、IToolWork Serviceの“Work”メソッドが呼び出され始め、その実行中にUIページに情報が出力されます。
UIページには、例外をトリガーするかどうかを設定するチェックボックスがあり、例外のあるシナリオをシミュレートし、例外が発生した場合は修復ボタンをクリックして修復します。
個々のアクションを実行する際に、異なる状態変化が見られる。以下の通り。
using Okcoder.Stateless.Common;
namespace Okcoder.Stateless.WinTool
{
public partial class FrmMain : Form
{
private IToolStateService toolStateService;
private IToolWorkService toolWorkService;
public FrmMain()
{
InitializeComponent();
this.toolStateService = new ToolStateService(ToolStateAction);
this.toolWorkService = new ToolWorkService(toolStateService, ToolWorkProcessAction, this.chkFatal.Checked);
}
private void FrmMain_Load(object sender, EventArgs e)
{
this.toolStateService.Init();
}
/// <summary>
/// 开始工作
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnWork_Click(object sender, EventArgs e)
{
this.txtInfo.Text = string.Empty;
this.toolWorkService.IsMakeFatal = this.chkFatal.Checked;
Task.Run(() =>
{
this.toolWorkService.DoWork();
});
}
/// <summary>
/// 维修
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnRepair_Click(object sender, EventArgs e)
{
this.toolWorkService.Repair();
}
/// <summary>
/// 导出状态机DotGraph文件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnExport_Click(object sender, EventArgs e)
{
SaveFileDialog saveFileDialog = new SaveFileDialog();
saveFileDialog.DefaultExt = "txt";
saveFileDialog.Filter = "txt文件|*.txt";
saveFileDialog.FileName = "Okcoder.txt";
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
var filePath = saveFileDialog.FileName;
this.toolStateService.ExportDotGraph(filePath);
MessageBox.Show("导出成功");
}
}
private void ToolStateAction(ToolState toolState)
{
this.Invoke(() =>
{
this.lblState.Text = toolState.ToString();
switch (toolState)
{
case ToolState.Idle:
case ToolState.Recovery:
this.btnWork.Enabled = true;
this.btnRepair.Enabled = false;
this.lblState.ForeColor = Color.Black;
break;
case ToolState.Running:
this.btnWork.Enabled = false;
this.btnRepair.Enabled = false;
this.lblState.ForeColor = Color.Goldenrod;
break;
case ToolState.Malfunction:
this.btnWork.Enabled = false;
this.btnRepair.Enabled = true;
this.lblState.ForeColor = Color.Red;
break;
}
this.Refresh();
});
}
private void ToolWorkProcessAction(string msg)
{
this.Invoke(() =>
{
this.txtInfo.AppendText(msg + "\r\n");
});
}
}
}
例の紹介。
上記の手順を経て、プログラムを実行してからWorkボタンをクリックすると、デフォルトで障害がない場合は以下のようになります。

“障害”を選択すると、障害が発生すると次のようになります。

以上が“高性能ステートマシン管理ソリューションの推奨”の内容です。詳細については、公式ドキュメントを参照してください。一緒に学び、一緒に進歩したい。
プログラミングを学び、“古いコードを知る道”に焦点を当てて、より多くの記事を共有しましょう!