Blue Prismでファイルトリガー実行を実現してみる

#1. 今回やりたいこと
所定のフォルダにファイルが配置されたら起動するロボットを作りたい。
いわゆる、ファイルトリガーで起動するロボットですね。

後で再掲しますが、こんな感じで実現しました。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Blue Prismでトリガー実行を実現した様子を動画にしてみました^^<br>フォルダ監視用のプロセスを裏でぐるぐる回して、ファイル配置を検知したら業務処理を行うプロセスをAutomateCでキックする構成です。<br>2つの監視プロセスを1台のRRで並行稼働させてます<a href="https://twitter.com/hashtag/blueprism?src=hash&amp;ref_src=twsrc%5Etfw">#blueprism</a> <a href="https://twitter.com/hashtag/RPA?src=hash&amp;ref_src=twsrc%5Etfw">#RPA</a> <a href="https://t.co/botz9jrSwU">pic.twitter.com/botz9jrSwU</a></p>&mdash; キヨミ カズアキ@RPAエンジニアCEO (@kazuaki_kiyomi) <a href="https://twitter.com/kazuaki_kiyomi/status/1471383504311455745?ref_src=twsrc%5Etfw">December 16, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

#2. 前提
- **Blue Prism**というRPA製品を使います。バージョンは6.10.1
    - UiPathやAutomation Anywhereには標準機能でトリガー実行できる仕組みがありますが、Blue Prismにはありません。あったらごめんなさい。
- 2つの業務を1台の端末で実行するものとします。
    - **顧客管理システムへの入力**、**交通費検索**の2業務とします。（いずれもRPAのデモ用サイトとして用意しました。）
        - [顧客管理システム](https://yawarakait.github.io/crmrpatest/index.html)
        - [交通費検索](https://yawarakait.github.io/rpatransit/index.html)
- 2業務とも、所定フォルダに任意のExcelファイルが配置されたことをトリガーにして実行されるものとする。

### 2.1 なぜトリガー実行？
RPAのロボットを実行させる方法として、パッと思いつくのは

- **手動実行**
    - ボタンを押したら実行される。利用者のデスクトップで実行する場合はこの形式が多い。
- **スケジュール実行**
    - 複数のプロセス（他のRPA製品では「シナリオ」とも言います）を順次実行するスケジュールを組み、一定の時間間隔で回し続ける。

Blue Prismを使う上で**手動実行**させるプロセスを作ることは稀だと思われるのでスルーします。
Blue Prismにおいては**スケジュール実行**でRPAを運用するのが基本です。

**スケジュール実行**のメリットは完全自動でプロセスが動き続けるため、人手が介在することがなく、運用コストがかからないことです。（もちろんエラー発生時のリカバリ対応などは必要です）
ただ、例えば1分おきにスケジュールを回し続けるような運用にしていると、**セッションログ**が溜まりまくってDBの容量を圧迫してしまいます。
プロセスが業務を行っていればまだ良いのですが、空振りし続けた分もログが溜まっていくのは何とかしたいものです。

また、Blue Prismでは**本番稼働している最大同時実行数＝必要なライセンス数**となっており、
前述したようなスケジュールで、1台の**ランタイムリソース**（プロセスが稼働する端末。以下、**RR**と表記）を1つの業務が占有してしまうことで、ライセンスを食いつぶしてしまいます。

つまり、**必要なときだけ自動でプロセスが稼働する仕組み**があればライセンスを節約することができます。
その仕組みを実現する方法が**トリガー実行**というわけです。

#3. 構成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186221/269af512-4bbd-feec-2db2-44874e2fe195.png)

各業務ごとに**フォルダ監視プロセス**をバックグラウンドで実行可能なプロセスとし、1台のRRで2業務の監視プロセスを常に稼働させます。
**プロセスをバックグラウンド実行可能にするためには、プロセスで使っている全てのオブジェクトの実行モードがバックグラウンドである必要があります。**

所定のフォルダへのファイル配置を検知したら、AutomateC.exeを用いてコマンドラインで**業務処理プロセス**を実行します。

後ほど触れますが、監視プロセスと業務処理プロセスを分けたのは、監視プロセスをバックグラウンドで実行したいためです。
業務処理プロセスは、システムを画面操作する処理がメインなのでフォアグラウンドで実行させます。
そのため、監視プロセス内でサブプロセスとして業務処理プロセスを呼び出す仕様としてしまうとフォアグラウンドでしか実行できなくなってしまい、複数の業務の監視プロセスが1台のRRでマルチに稼働できなくなってしまいます。

業務処理プロセスの構成は、メインとサブに分けています。
メインプロセスから業務処理を行うサブプロセスを順次実行する仕組みとしました。
この理由については後ほど触れます。

#4. 実装
###4.1 フォルダ監視プロセス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186221/84daca51-98e0-61a9-593b-ea3ea47dcff3.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186221/caed3ea3-f470-c4bb-f6fd-ef041013ef21.png)

時系列で処理を追っていくと、

```
1. RRの端末名を取得する。
2. 未処理フォルダにファイルが存在するかチェックする。
3. ファイルが存在する場合、環境ロックをかけてRRを占有し、他の業務処理プロセスは実行不可にする。
   環境ロック名は1.で取得した端末名とする。
4. 業務処理プロセス実行ページにて、コマンドラインで業務処理プロセスを呼び出す。
呼び出してからプロセスが実行されるまでに若干ライムラグがあるため、5秒程度Waitを挟み、プロセスが実行される前に環境ロック解除されることを防ぐ。
5. 環境ロックを解除し、RRの占有を解く。
6. 次の監視まで15秒Wait。
以降1～6をループ。
```

###4.2 業務処理プロセス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186221/f87474c5-e7ba-9a78-e681-001205565214.png)

業務を行うプロセスを順に繋いでいるだけなので、説明は割愛します。

##5. ポイント
各プロセスの実装におけるポイントです。

###5.1 コマンド設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186221/f8621972-4070-1286-45df-199cd015278f.png)

コマンドラインオプションについては↓を参照してください。
[Blue Prism 6.10 コマンドラインオプション](https://bpdocs.blueprism.com/bp-6-10/ja-jp/helpCommandLine.htm)

特定のRRに対してプロセスを実行するためのコマンドは以下のようになります。

```
/C AutomateC.exe /run {$実行プロセス名} /resource {$RR名} /port {$ポート番号} /user {$BPログインユーザー名} {$BPログインパスワード}
```
監視プロセスの冒頭で{$…}の中を埋めてコマンドを完成させます。
**{$BPログインパスワード}**にはパスワード型のデータアイテムを代入しており、代入後にパスワードが丸見えになってしまっていますが、「プロセス実行コマンド」自体をパスワード型にしてしまっても、コマンドラインでの実行はできました。
気になる人はコマンドをパスワード型にしておくと良いでしょう。

###5.2 ファイルの存在チェック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186221/4864a56f-4981-5c1f-dbf1-b89b9aa3c9a9.png)
今回はExcelファイルであることを前提としているので、Get Filesの入力：Patterns CSVのパラメータは*.xlsxとしています。
取得したExcelファイルの数が>0であれば、業務処理プロセスを呼び出す処理へ遷移することにします。

###5.3 環境ロック
Excelファイルが存在していたら業務処理プロセスを実行させるわけですが、実行前にRRを占有するために**環境ロック**という機能を使います。
**環境ロック**とは、他のセッション（＝RRに割り当てられているプロセス）のアクセスをロックする仕組みです。
例えば、特定のプロセスでExcelファイルへの書き込みを行う処理があったとして、それを3台のRRで同時にファイルにアクセスしてしまうと2台は書き込みができずエラーとなってしまいます。
このような時に環境ロックを使い、

1. 書き込み前にロックをかける。他のセッションはロック照会⇒ロックがかかっていたら指定した時間だけ待機。
2. 書き込みが終わったらロックを解除する。
3. 順番待ちしていた他のセッションがロックをかける。

…といった具合に、ファイルへのアクセスを制御することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186221/3a55644f-df23-28a5-567e-2809e9645e41.png)

この機能を活用して、AutomateCからの業務処理プロセス実行指示が完了するまでRRを占有する仕組みを作ります。
ロックをかけるには名前を指定する必要があるのですが、名前はRR名が分かりやすいでしょう。
そのRR名でロックがかかっていないか照会し、かかっていなければロックをかけます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186221/30a7a9ba-53be-af7e-e7a5-94770eb8479f.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186221/8cd418de-6517-992f-e29e-03648c2640a1.png)

ロックをかけることに無事成功したらトークンが返されます。
このトークンはロックを解除するときに使います。

###5.4 業務処理プロセスのコマンドライン実行
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186221/91785aab-dda5-2345-1855-a3a922fa8905.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186221/cda084ea-99fe-9ca4-c3e1-9ec417546c97.png)

ここが一番のポイントかと思います。

コマンドラインを使ってコマンドを実行するためには**Utility - Environment**オブジェクトの**Start Process**アクションを使います。
ただ、**このアクションではコマンドの実行結果が返却されないため、プロセスが正しくキックできたかどうかを判別することができません。**
例えばRRが他のセッションで使われている場合は、同RRにてプロセスを実行させることができずエラーとなります。（※）
※実行中のプロセスの実行モードがバックグラウンドであれば、バックグラウンド or フォアグラウンドのプロセスは並行して実行することができます。実行モードについては↓を参照してください。

[オブジェクトの実行モード（フォア / バック / 排他）](https://falcslab.com/blueprism/technique/556/)

コマンドの実行結果が返却されるように、Start Processをベースに別のアクションを作ることにします。
余談ですが、アクションを自作するときはオブジェクトを複製し、拡張用のオブジェクトを用意することをおすすめします。
今回は**プロセス実行（ExitCode返却）**というアクションを自作しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186221/d0d7bbb7-ce7d-8182-ad33-675471c6a713.png)

```vb
If Arguments<>"" Then
  Dim proc1 As System.Diagnostics.Process = System.Diagnostics.Process.Start(Application, Arguments)
  proc1.WaitForExit()
  ReturnCode = proc1.ExitCode.ToString()
Else
  Dim proc2 As System.Diagnostics.Process = System.Diagnostics.Process.Start(Application)
  proc2.WaitForExit()
  ReturnCode = proc2.ExitCode.ToString()
End If
```

コマンドの実行結果はReturnCodeとして返却されます。
ReturnCodeが0のときはコマンド実行成功。1のときはエラーすなわちプロセスが実行されなかったことになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186221/e1c6b1ed-e7be-74f1-ac56-6a5bad0b2780.png)

今回の場合はロジックを簡易にするため、プロセスのキックに失敗した場合はロックを解除する処理に遷移する以外は特に何も処理を入れていません。
他のセッションによってRRが占有されていて待ち状態になることは多々あると想定されるため、例外のログも出力しないようにし、無駄なセッションログは出力しないようにしました。
監視プロセス全体としても、ログの制御は全体的に**エラーのみ**もしくは**無効**にしています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186221/b7f4c9f8-276c-d0dc-e99e-b44e31386b69.png)

キックに成功した場合もロック解除の処理に遷移しますが、キックしてからプロセスが実行されるまでに若干タイムラグがあるため、実行されないうちにロックが解除されないように5秒ほどWaitを入れています。

###5.5 環境ロック解除＆スリープ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186221/b18acbbc-b656-a14e-4ab6-22f7adc4e804.png)

業務処理プロセスが実行されたことが確認できた後、環境ロックを解除します。
解除したら、再び所定フォルダへのファイル配置を監視する処理に戻ります。
すぐに監視に戻っても良いのですが、15秒程度Waitを入れています。
秒刻みでファイルが投入される業務はあまりないと思うので、もっと監視間隔を空けても良いかもしれません。

###5.6 業務処理プロセス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186221/0a532c93-17d2-cbe4-7077-9fd60b5d5d3f.png)

顧客管理システムへの入力業務を例とし、業務を構成するプロセスが2つあるものとします。

今回は1つのメインプロセスから2つのサブプロセス（業務を実行するプロセス）を呼ぶ構成としていますが、Blue Prismのベストプラクティスではサブプロセスの呼び出しはあまり推奨されていません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186221/92da39da-c0d0-ffb2-b505-3b9813b8e8d9.png)
引用元：[Blue Prism 開発ベストプラクティス 第３回](https://www.blueprism.com/uploads/resources/white-papers/Blue-Prism-Webinar-20200603_JP.pdf)

なのでできれば避けたかった手法なのですが、他業務の割込みなしに1業務を完遂するためにこのような構成にしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186221/67e98b39-51d1-8242-446f-c9b89da7e5be.png)

例えば、Aという業務を行うために作業a1, a2を行う必要があるとし、作業a, bとも1プロセスで完結するものとします。（プロセスa1,a2とする）
プロセスa1⇒プロセスa2の順で作業を行うとすると、a1が完了してa2が実行されるまでに数秒でもRRがアイドル状態（＝プロセスが稼働していない状態）があると、他の業務のプロセスが割り込む可能性があります。そうなると、業務Aが作業a2を残して終わってしまうことになります。

そのため、**業務が複数の作業で構成される場合はすべての作業が終わるまで、他の業務が割り込まない仕組みとしなければなりません。**
その仕組みとして、メインプロセスからのサブプロセスを呼び出す手法をとったというわけです。

この方法がベストというわけではなく、例えば前述した**環境ロック**を応用して1業務を完遂するまでRRをロックする仕組みが作れるかもしれません。（筆者は未検証です。。。）
良い方法があればコメントお願いします。

#6. で、うまく動くの？
一応うまく動くようです。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Blue Prismでトリガー実行を実現した様子を動画にしてみました^^<br>フォルダ監視用のプロセスを裏でぐるぐる回して、ファイル配置を検知したら業務処理を行うプロセスをAutomateCでキックする構成です。<br>2つの監視プロセスを1台のRRで並行稼働させてます<a href="https://twitter.com/hashtag/blueprism?src=hash&amp;ref_src=twsrc%5Etfw">#blueprism</a> <a href="https://twitter.com/hashtag/RPA?src=hash&amp;ref_src=twsrc%5Etfw">#RPA</a> <a href="https://t.co/botz9jrSwU">pic.twitter.com/botz9jrSwU</a></p>&mdash; キヨミ カズアキ@RPAエンジニアCEO (@kazuaki_kiyomi) <a href="https://twitter.com/kazuaki_kiyomi/status/1471383504311455745?ref_src=twsrc%5Etfw">December 16, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

#7. 最後に
一応トリガー実行は実現できました。
ただ、業務処理プロセスの構成はもっと考えたいですし、運用時のリカバリがしやすい仕組みかと言われると？かもしれません。参考になれば幸いです。