この記事は、All About Group(株式会社オールアバウト) Advent Calendar 2022 7日目の記事です。
初めての方ははじめまして、そうでない方はお久しぶりです。
@NACK と申します。
今年も「毎月7日は罠(わな=07)の日」……と言い張ってみます。
※記念日認定されているわけではありませんので、ご注意ください。
というわけで、今年も罠の話を、昔のゲームブック風に書いてみます。
今年のお題はUiPathです。
「そういうお遊びは興味無いから!」という方は、直接 罠の解説まで飛んでください。
罠の話が列挙されています。
UiPathとは
ステキなRPAツールです。コードがかけなくてもアルゴリズムが組めれば、やりたいことがある程度実装できます(いわゆる「ノーコード・ローコード」なツール。ちょっと複雑なことがしたい場合はVB.NETでコードも書けます)。
ちなみに、基本的には有料ですが、個人や小規模なチーム・企業で使う場合は無料です(無料になる条件は色々ありますので、詳細な条件は必ず公式サイト他でご確認を)。
私も、個人でも使っています。
公式
解説ページ
UiPathトラップ(ゲームブック風)
1
あなたが道を歩いていると、祭囃子が聴こえてきた。
祭囃子に誘われるまま進んでいくと、「UiPath縁日」という看板を見つけた。
さてどうしようか?
2
わたあめがそれなりに大きくなってきたところで、機械が急に止まってしまった。
「あー、故障するの、こっちの機械でしたか。スイッチを入れなおしますね」
あなたはそれを断って、わたあめ屋を後にする(#14へ)。
※解説
3
あなたは帰路に就くことにした。
会場を出たところで、近くから「どーん」という音が聞こえてきた。
空を見上げると、大きな花火が打ち上げられている。
縁日を堪能したことを祝われているようで、あなたは少し嬉しくなった……かも知れない。
(お疲れ様でした!)
4
無事に最後まで飲み切れた。ストローとカップを返却して #13へ
5
何もでてこない……。
「残念でしたね」
(#14へ)
※解説
6
「ああ、ちょうど焼けたところですよ。どうぞ」
焼きたてのたこ、いか、ほたてが載ったパックを渡された。
あなたは、それをおいしく食べても良いし、お土産にしても良い。
いすれにせよ、海鮮焼き屋は堪能したので #20へ
7
ポイは流れるように水に差し込まれ、無事にスーパーボールをすくうことができた。
あなたは戦利品をポケットに入れ、次の屋台へ行くことにする(#18へ)
8
何もでてこない……。
「残念でしたね」
(#14へ)
※解説
9
スーパーボールすくいをやっているようだ。
「スーパーボールまでポイを移動したらクリックしてください。うまくすくうコツは、流れるようにポイを水に入れることですよ」
どうやら、ポイの移動はUiPathのクリックアクティビティを使用する必要があるらしい。
ちなみに、「カーソルの動きの種類」プロパティのデフォルト値は Instant
で、他に選べる値としては Smooth
がある。
さて、「カーソルの動きの種類」プロパティに何を設定しようか?
10
くじが1枚でてきた。番号は"9"だ!
「おめでとうございます。こちら、特賞になります」
特賞として「温泉旅行券」をもらった。喜びをかみしめながら #11へ
11
海鮮焼きの屋台だ。
「今から、たこ、いか、ほたてを焼いていきます。全部焼けた頃にまた来てください」
なお、焼くためのロジックはこうなっているらしい。
さて、いつ来ようか?
12
くじが2枚出てきた。番号は"9"と"10"のようだ。
「困りますね、くじは1人1枚ですよ」
叱られてしまったので、あなたはとぼとぼとその場を後にする。
(#14へ)
※解説
13
わたあめの屋台を見つけた。
「わたあめを作っていきませんか?お好きな機械をどうぞ。ただ、どちらかの機械、使い続けているとメモリオーバーフローで故障するんですよね……」
機械は2台あるが、よく見ると「わりばしを1回まわす」というワークフローファイルを呼び出す際の「分離」プロパティのチェック状況が違っているようだ。
ちなみに「わりばしを1回まわす」ワークフローファイルは、内部でかなりのメモリを消費する。
さて、どちらを使おうか?
14
あなたは帰路に就くことにした。
帰宅後、外から「どーん」という音が聞こえてきた。
どうやら花火が打ち上げられているようだが、自宅からは何も見えない。
おそらく、さっきの縁日の会場辺りで上がっているのだろう。
もったいないことをしたような……。
(失敗。またの挑戦をお待ちしております)
15
おおきなわたあめを作ることができた。
あなたは、それをおいしく食べても良いし、お土産にしても良い。
いすれにせよ、わたあめ屋は堪能したので #3へ
16
飲んでいる途中で、ストローがふやけて折れてしまった……。
(#14へ)
※解説
17
「すみません、もう売り切れちゃったんですよ」
あなたはがっかりしながら海鮮焼き屋を後にする(#14へ)。
※解説
18
「くじびきをやっていきませんか?特賞はよいものですよ!ただし、1人1枚までです。2枚以上引かないでくださいね」
くじには番号が振られていて、"1"~"10"までの10枚があるようだ。
ただし、くじの番号は「文字列型(String)」になっているので注意すること。
番号(String)
----------
”1”
”2”
”3”
”4”
”5”
”6”
”7”
”8”
”9”
”10”
なお、壁に貼ってある表を見る限り、特賞のくじは"9"のようだ。
さて、"9"だけを引くにはどうしたら良いだろうか?
- 「代入」で下記の式を設定する(#10へ)
dtくじ.AsEnumerable _
.Where(Function(row As DataRow) row("番号").ToString > "8") _
.CopyToDatatable()
- 「代入」で下記の式を設定する(#12へ)
dtくじ.AsEnumerable _
.Where(Function(row As DataRow) CInt(row("番号")) > 8) _
.CopyToDatatable()
19
ポイは瞬時に水に突っ込まれ、その衝撃で破けてしまった……(#14へ)。
※解説
20
喉が渇いてきたので飲み物を買うことにした。わらび餅ドリンクが売っていたのでそれにしよう。
中身(String)
----------
”わらび餅”
”飲み物”
ストローが2種類あり、片方は途中で壊れそうだ。
全て飲み干して、入れ物を空にしたいのだが……さて、どちらを選ぼうか?
- 「代入」で下記の式を代入(#16へ)
dtわらび餅ドリンク.AsEnumerable _
.Where(Function(row As DataRow) row("中身") Is Nothing) _
.CopyToDatatable()
- 「データ テーブルをフィルター」で"中身" IsEmpty を保持(#4へ)
罠の解説
クリックアクティビティのカーソルの動きの種類プロパティ
参考: https://docs.uipath.com/activities/lang-ja/docs/click
カーソルの動きの種類 にはInstant
とSmooth
があります。
デフォルトはInstans
で、これを選ぶとクリックしたいオブジェクトまでカーソルが瞬時に移動します(カーソルがテレポートするイメージです)。
通常はこれで問題ないのですが、「マウスホバーした状態でしか表示されないメニューを表示した状態で、そのメニュー上のボタンをクリックする」場合、稀にクリックが失敗することがあります(クリックする前にカーソルが外れた結果、メニューが消えてしまう)。
そういう場合はSmooth
を使うと、人間が操作した時と同様、前のオブジェクトから次のオブジェクトまで実際にカーソルを移動してくれるので、メニューが消えずにクリックできます。
私の場合、Googleスプレッドシートの「拡張機能」内のサブメニューを操作している時にこの現象が発生し(毎回失敗するわけではなく、10回に1回くらい失敗する)、地味に困っていました(仕方ないのでリトライ入れたりして何とか……)。
Smooth
に変更してからは一度も失敗していません。
データテーブルのフィルタ結果が型によっては期待した結果にならない
スプレッドシートから値を取得した場合等、「データテーブルの値が全て文字列型」というケースはよくあるかと思います。
それらのデータテーブルに「データ テーブルをフィルター」アクティビティを使う場合、型の違いによる注意が必要です。
- 「データ テーブルをフィルター」の場合、文字列型の大小比較(
<
や>
など)はできないようです(少なくとも、自分がやってみた限りはそうでした。例えば、文字列であっても「"2">"1"」は成立するはずなのですが、成立するはずのフィルタを設定しても、取得結果が0件になります)。 - 「データ テーブルをフィルター」でも
=
は使えますが、比較対象の値と型が合っていない場合、正しく比較できません(データテーブル側の値が文字列の時にフィルター条件に数値を比較したところ、取得結果が0件になりました)。
解決方法
- データテーブルに「フィルタをかけたい型の項目」を追加し、そちらに型を変換した値を格納→「追加した項目」を条件に「データテーブルをフィルタ」を実施する。
例:価格(文字列)
という項目があり、これを数値としてフィルタリングしたい場合-
価格(数値)
という項目を新規作成(型はInt型) - そちらに
価格(文字列)
の値をInt型に置換して格納 -
価格(数値)
でフィルタリングを実施。
-
⇒この方法の場合、UiPathの標準機能だけでできますが、データ件数が大量にある場合、時間がかかりますし、可読性も落ちます。また、価格に変更があった場合、複数の項目を修正する必要があります。
- LINQのWhereメソッドでフィルタをかける(オススメ)
このやり方の場合、文字列のまま大小比較してフィルタリングすることもできますし、別の型(Int型など)に変換した状態でフィルタリングすることも可能です。とても便利なのですが、フィルタリングした結果が0件になる場合はエラーになるので注意してください(詳細は後述)。
dt.AsEnumerable _
.Where(Function(row As DataRow) CInt(row("価格(文字列)")) >= 100) _
.CopyToDatatable()
データテーブルにフィルタをかけた結果が0件の場合
参考: https://it-strategist.blogspot.com/2012/02/datarow.html
「データテーブルをフィルター」アクティビティを使った場合、結果が0件でも特に問題は起きません。
が、前項でご紹介した「LINQのWhereメソッドでフィルタをかける」場合、フィルタをかけた結果が0件だとソースには、DataRow が含まれていません。
というエラーになってしまいます。
エラー回避方法としては、参考記事で触れていらっしゃるように「事前にCountしておき、0件以上だったらCopyToDatatableする」のが素直なように思います。
別案として「try-catchで囲い、エラーになったら0件とみなす」という方法もあります(ただ、こちらの場合、他の原因でエラーになった時に誤認する危険性もあるので、catchするExceptionの種類はSystem.InvalidOperationException
に限定するのがオススメです)。
どちらを採用するかはお好みでどうぞ。
繰り返しと並列繰り返しの使い分け
コレクションの各要素を繰り返す場合、「繰り返し (コレクションの各要素)」と「並列繰り返し (コレクションの各要素)」の2種類があります。
例えば、(ゲームブック部分にも書きましたが)こんな処理があったとします。
これの「並列繰り返し(コレクションの各要素)」のところが「繰り返し(コレクションの各要素)」だった場合、出力結果は下記のようになります。それぞれの処理を順に行うので、所要時間は約30分です。
たこ
たこ焼き
いか
いか焼き
ほたて
ほたて焼き
ですが、「並列繰り返し(コレクションの各要素)」の場合はこうなります。
ほたて
いか
たこ
ほたて焼き
いか焼き
たこ焼き
何故かというと、並列繰り返しの場合、コレクションの各要素に対する処理が同時にまとめて行われるのです。
「全ての要素が同時に処理される」ので、要素の定義順等も関係ありません(そのため、最初に定義した「たこ」より先に「ほたて」が焼かれたりします)。所要時間も約10分に短縮されます(「要素1つにかかる時間」だけで済むので)。
並列繰り返しのメリット
何と言っても処理が早いことです。
コレクションに100個データが設定されていて、1つ辺りの処理に1秒かかる場合、通常の繰り返しなら100秒かかりますが、並列繰り返しなら1秒で終わります。
並列繰り返しのデメリット
残念ながら、実は色々あります。
- 処理が直感的でなくなる
通常、繰り返し処理は「1つずつ順番に実行される」と思っているので、ここだけ「同時に実行される」となると、コードを読んでいる時に若干混乱します(これは、まあ「覚えようよ」で済む話ではありますが……) - 順番が制御できない
例えば「コレクションのデータを加工して、データテーブルに行追加する」といった処理を行う場合、コレクションのデータ順にデータが登録されるとは限りません。全ての処理は非同期で行われますので、「処理が早く終わったもの」から順にデータテーブルに行追加されます。 - メモリ・プロセスの使用量が激増する
コレクションに100個データが設定されている場合、この100個の処理を一度に行いますので、その分、メモリもプロセスも使用量が増えます。 - UiPathで「並列繰り返し」に対応していない処理が多い
例えば、Excelを処理するアクティビティなどは非対応なので、「Excelファイルを同時に100個処理する」といったことはできないようです。
なお、(「並列繰り返し」の説明ではありませんが)類似処理である「並列」についての詳しい記事がありますので、そちらも参考にしていただければ。
https://www.uipath.com/ja/blog/developer/about-parallel-activities
ワークフローファイルを呼び出しアクティビティの分離プロパティ
とあるアプリで、「長時間動かしたままにすると、メモリ不足でエラーになる」という現象に見舞われました。
調べてみたところ、UiPathにおいて、プロセスが終了するまでメモリが解放されないケースがあるようです。
このケースに対処するため、当該アプリ内の、いくつかの「ワークフローファイルを呼び出し」アクティビティの「分離」プロパティにチェックを付けました。
そうすると、そのワークフローファイルの処理は別プロセスで実行されます。そのワークフローファイルの処理が終わった時点で、「別プロセス」は終了するので、そのプロセス内で使用したメモリもその時点で解放されるのです。
なお、デメリットとしては「別プロセスを立ち上げるので、処理実行中の総メモリ使用量は少し増える」そうで……分離プロパティ自体の詳細な説明はこちらをどうぞ。
https://www.uipath.com/ja/blog/developer/invoke-workflow-file-tips
ついでに、「分離プロパティを変更してもメモリ不足が解消しないよ!」という場合に参考になりそうな記事もご紹介しておきます。
https://www.uipath.com/ja/resources/knowledge-base/out-of-memory
(ゲームブック部分の)参考文献
特になし。
最近、古い海外ゲームブックが日本で続々復刻しているため、ちょっとネタとして使いにくくなってきました(小ネタ部分しか参考にしていないものの、絶版ではない本のネタバレは避けたいので……)。
せっかくなので、最近復刻された本のご紹介でも。