Edited at

UiPathの RE Frameworkでも使われている、ステートマシンを理解する


イントロ

UiPath Studio で処理を記述する方法として、一本の処理を書きやすい「シーケンス」と、処理の流れを視覚的に表現できる「フローチャート」、あとひとつはいくつかの状態と、状態間を遷移するための条件を記述することでフローを記述する「ステートマシン」があります。

UiPath Studioの新規プロジェクト作成画面にもある「Robotic Enterprise Framework (RE Framework)」の処理フローもこのステートマシンで書かれていますし、ちょっと整理しておこうかなと思いました。

ちなみに下記は、RE Framework のプロジェクトのサンプル。


ステートマシンとは

さてもうすこしステートマシンを説明すると「ある状態」があって


  • その状態に入るときの処理を記述する: Entry

  • その状態から出るときの処理を記述する: Exit

  • その状態から別のある状態へ遷移するための条件/トリガーを指定する: Transition(s)

を駆使して、処理を進めていくしくみです。

以下主要の画面。

状態と、次の状態へ遷移する条件を記述

この状態に入るとき・出るときに実行される(Entry/Exit)

遷移する条件を記述する(Transition(s))


たとえば

たとえば、UiPath Academy にも出てくるおなじみの RandomNumberを当てるヤツ、コレはフローチャートで書かれてますが

コレをステートマシンで記述すると、

こんな感じでしょうか。流れを説明すると、


  • 初期化状態に入る(Entry) ときに RandomNumber を初期化

  • 入力状態に入る(Entry) ときに、入力ダイアログを出してユーザ入力値を得る

  • 下記の場合わけを遷移(Transition(s))として処理


    • 小さい場合 → 再入力

    • 大きい場合 → 再入力

    • 等しい場合 → 終了



などとなります。

ひとつの状態から複数の遷移の条件(Transition(s))を定義できるようになっていることがわかりますね。


やってみる

いくつかやってみます。


環境

UiPath Studio 2018.4.5で確認しています。


まずはHello World レベルのもの

とりあえずとってもシンプルなところから。

RandomNumber を初期化して、画面表示してみます。


  • UiPath Studioのプロジェクトを作成し、シーケンスやフローチャートを置くかわりに「ステートマシン」アクティビティを追加。

  • そのステートマシンの中にダブルクリックで入って、「ステート」と「最終ステート」アクティビティをひとつづつ配置

  • Startアイコン → ステート → 最終ステートを線で結びます。

  • 「ステートマシン」スコープで利用可能な RandomNumber 変数を定義します。

うまくいけばこんな感じになると思います。

つづいてステートをダブルクリック。Entry の枠に、アサインアクティビティを配置してRandomNumberを初期化(RandomNumber = New Random().next(1,100))します。このEntryはこの「ステート」状態に入ってきたときに実行される処理を記述します。

さいごに、最終ステートをダブルクリック。Entryの枠にメッセージボックスを配置しましょう。

実行してみましょう!、、、表示されましたね。

ココまでのソースを取得したい場合は、

https://github.com/masatomix/UiPathStateMachineSample/releases/tag/helloworld

の「Source code (zip)」をダウンロードしてください。


分岐させる

ためしに「偶数はエラー」という処理を追加してみます。

まずは「最終ステート」アクティビティを追加して、名前を「偶数エラー」にします。そして、上のステートから矢印(図中T2)を繋ぎます。

エラーのマークが出ちゃいました。遷移が複数になった場合は、Transition T1,T2にはかならず条件をつけないといけないようですね。というわけで、T1/T2をそれぞれダブルクリックして、Transitionを開き、


  • T1: 偶数じゃなかったら(RandomNumber mod 2 <> 0) 遷移と設定

  • T2: 偶数だったら(RandomNumber mod 2 = 0) 遷移と設定

とします。(下はT2の例)

エラーのマークが消えたと思います。

つづいて、先ほど置いた「偶数エラー」をダブルクリックして中に入って、

メッセージボックスを置けばOK。完成です。

実行してみると、偶数の時だけエラーメッセージが表示されます。

ってことで、初期の「ステート」の状態から、Trasition(s)に定義した条件に沿って別の状態「偶数エラー」「最終ステート」に遷移させられることがわかりました。

ココまでのソースを取得したい場合は、

https://github.com/masatomix/UiPathStateMachineSample/releases/tag/odd_and_even

の「Source code (zip)」をダウンロードしてください。


注意: 条件分岐の評価の順番

さてさて、Transition(s)のT1/T2ですが、


  • T1: 偶数じゃなかったら(RandomNumber mod 2 <> 0) 遷移と設定

  • T2: 偶数だったら(RandomNumber mod 2 = 0) 遷移と設定

モレなくダブりなくMECEに設定したわけですが、ダブりありで設定したい場合もありますよね。「4の倍数の場合T3、偶数の場合T2、それ以外の場合T1」とかです。

このような場合、Transition(s)に設定した条件が評価される順番が気になりますが、結論をいうと右部の概要ペインに並んでいる順番になるっぽいんですよね。上記のキャプチャの例だと、T1,T2,T3の順番で評価されるので、T3の「4の倍数の条件」は評価されないって事になります。。この順番の制御が緻密に出来れば「まあプログラム書くときもif文の並び変えればアルゴリズムかわるよね」ってのと同じ、と割り切りますが、この概要ペインの順番の制御はGUI上でTransitionの矢印をドラッグしたりするとかわる(いじったTransitionが最後にいく) とっても繊細な挙動。。というか逆にTransitionの矢印をいじる以外の制御方法がよく分かりません。。

↓こうやってドラッグすると、T3が最後に移動したりする(あ、最初からT3が最後で、、例がイマイチでしたね。)。

さらに、ダブりではなく「モレ」があった場合、モレの条件になると遷移先が決まらず、ダンマリになってしまいます。偶数奇数の判定で「偶数かそれ以外」って書ければと思いましたが、それ以外つまりSwitch文でいうdefault節のような仕様もなさそうで、、なんだかこのへんの仕様はとても微妙だよねぇ、とおもいます。。

まあ、遷移を制御する条件はモレなくダブりなく設定するようにしましょう、という結論になりそうです。


遷移する条件には、トリガー待ちを設定出来る

ステートマシンは、Transition(s)で指定した条件の評価を行うタイミングを制御するトリガーを張ることができます。ある指定したイベントの発生をトリガーにして、次の状態に遷移するための条件チェックを実行する ことができるわけですね。

たとえばいわゆるファイル待ちトリガーのように「所定の箇所にファイルが置かれたら、次の状態へ遷移する」などです。

やってみます。


  • トリガーファイル c:/temp/data.trg が更新されるのを待つ

  • 更新されたら、次の処理を実行 (処理は、c:/temp/data.txtの中身を表示するだけ)

  • トリガー更新まちに戻る。

  • トリガーファイルの中身が「end」で更新されたら、次の処理を最後に終了する

なんてのをつくってみましょう。


STEP1: ファイル待ちの設定

先のプロジェクトのステートたちは一旦削除。

「初期」「メイン処理」「終了」というステートを追加。スタートからそれらをTransitionで繋ぎます。また、メイン処理は繰り返すので処理へ戻るTransitionも追加します。

トリガーファイルが来たらメイン処理を実行するので、トリガーまちを仕掛けるのは図中の T1 が良さそうです。なので T1 をダブルクリックして


  • 上部のTriggerに「イベントを監視」アクティビティ

  • そのなかに「ファイル変更トリガー」を配置

  • イベントハンドラの中は「テキストファイルを読み込む」アクティビティを配置

します。

それぞれのアクティビティに値を設定していきますが、、、その前にまず変数を定義しておきます。

変数名

スコープ
規定値

TriggerData
String
ステートマシン

Path
String
ステートマシン
"c:/temp/"

TriggerFile
String
ステートマシン
"data.trg"

さて、それぞれのアクティビティに値を設定していきます。

「イベントを監視」アクティビティ:


  • 「無限に繰り返す」プロパティをFalse

とします。

「ファイル変更トリガー」アクティビティ:


  • 通知フィルタ を Size

  • パス を Path

  • ファイル名 を TriggerFile

とします。

イベントハンドラ内の「テキストファイルを読み込む」アクティビティ:


  • ファイル名: System.IO.Path.Combine(Path,TriggerFile) ←パスを連結するだけ

  • コンテンツ: TriggerData

とします。

エラーが消えましたね。以上で

トリガーファイル c:/temp/data.trg が更新されたら、メイン処理 状態へ遷移するところが完成です。ちなみに、トリガーファイルの中身が「end」だったら〜、を実装するためにトリガファイルの内容は TriggerData変数に格納するようにしました。


STEP2: メイン処理

ステートマシン全体図に戻って「メイン処理」をダブルクリック。この「メイン処理」に入ってきたときにうごくのは「Entry」節なので、そこで「c:/temp/data.txt」を読んでメッセージボックスを出す処理を記載します。だいたいこんな感じ1


STEP3:トリガーファイルの中身に「end」と書いてあったら、次の処理を最後に終了する

最後に終了条件の仕様の実装です。Transition T2に設定すればよさそうです。T2をダブルクリックします。T2に進む条件に"end".Equals(TriggerData)と設定します。

Transition T3は、それ以外って条件を追加する必要があります。さきほど書いたとおりSwitch文のdefault節は存在しないので、それ以外はnot "end".Equals(TriggerData) としました。

下記の通り、エラーも消えたとおもいます。ではワークフローを実行してみましょう。

ワークフローは起動後トリガまちになるので「c:/temp/」ディレクトリにトリガーファイル「data.trg」とデータファイル「data.txt」を置いて、いろいろと動かしてみてください。トリガファイルを更新して保存する度に、データファイルが読み込まれて画面に表示されると思います。またトリガーファイルに「end」という文字を書いて保存すると、データファイルを処理したと、ワークフローが終了することを確認してください。

以上トリガーファイルを用いたステートマシンのサンプルでしたー。。

ココまでのソースを取得したい場合は、

https://github.com/masatomix/UiPathStateMachineSample/releases/tag/trigger

の「Source code (zip)」をダウンロードしてください。

あ、そうだ、Transition(s)のラベルはわかりやすい表記にしておくとよいと思います。


ちなみにRE Framework はザックリこんな感じ

さてさて、ステートマシンがある程度理解できると、また簡易版のRE Frameworkである「Attended Framework」(参考:Attended Framework テンプレートを使ってみた) の知識もあわせてみてみると、下記のRE Framework のステートマシンも、ある程度理解が出来るかもしれません。



だいたい、こんな感じです


  • Initステートでは、Attended Frameworkとほぼおなじ仕組みで設定ファイルの読み込みを行っている。例外が発生したらSystemError変数にその例外をセットしている



    • SystemError変数 が Nothingでないばあい、Transition:「System Error」で、End Processステートへ遷移して終了


    • SystemError変数 が Nothingのばあい、Transition:「Success」で、Get Transaction Data ステートへ遷移



  • Get Transaction Data ステート はEntryで Orchestratorのキューへデータを取得しに行く。データが取れたら TransactionItemへ格納。取れなかったりエラーだったらその変数はNothingのまま。



    • TransactionItem変数 が Nothingのばあい、Transition:「No Data」で、End Processステートへ遷移して終了


    • TransactionItem変数 が Nothingでないばあい、Transition:「New Transaction」で、Process Transaction ステートへ遷移



  • Process Transaction ステートはEntryで、プロジェクトごとの処理となる「Process.xaml」を実行


  • (「Process.xaml」は業務例外が発生した場合は「BusinessRuleException」をスローするルールとしておきます(コレはAttended Frameworkでもおなじルールでしたね))

  • Process Transaction ステートは「Process.xaml」がスローしてきたBusinessRuleExceptionはBusinessRuleException変数にセットしている。「Process.xaml」がスローしてきたその他のExceptionはSystemError変数にセットしている。



    • SystemError変数 が Nothingでない場合、Transition:「Error」で、Initステートへ遷移 (初期化からやり直しとなるが、キューにデータがリトライとして再投入されているので、結果再ラン)


    • BusinessRuleException変数 が Nothingでない場合、Transition:「Rule Exception」で、Get Transaction Data ステートへ遷移 (該当データの処理はあきらめて、次のデータを取得)


    • SystemError変数 も、BusinessRuleException変数 も Nothingの場合は正常終了として、Transition:「Success」 でGet Transaction Data ステートへ遷移 (次のデータを取得)

    • (条件の評価の順番も、上の通り。ただMECEになってるから、順番変わっても問題なさそう)



ながかったですが、一つ一つ整理すると、こんなかんじとなります。。よくよく読んでみると、Transitionの条件指定はキレイに「モレなくダブりなく」な定義となっていました。。


まとめ

さてステートマシンやその他のナレッジをまとめておきます。


  • ある「状態(State)」と状態間を「遷移する条件(Transition)」をつかって、比較的複雑なワークフローを記述するための仕組み。

  • 条件を評価する前にトリガーを張ることで、ファイルが到達したら処理を開始する、なんて事も可能で、とても便利。

  • 条件の評価は、評価順がとっても微妙な仕様なので、モレなくダブりなくを心がけた方が安全。マウスでフローをいじったら評価順がかわってバグっちゃう、とかヤバすぎる

  • RE Frameworkはステートマシンで処理フローが記述されているが、ザックリとしたフローは割とシンプル

  • よくよむと Attended Frameworkと似ている箇所が多数あり(設定ファイルよみこみ、例外処理の考え方)、Attended Frameworkから入ると理解しやすいかも。

おつかれさまでしたーー。。


関連リンク





  1. resultって変数定義したりとか、細かい説明は省略です:-)