はじめに
今回は 2023.10 にて正式リリースとなったコード化されたワークフローを使って RPA Challenge を自動化してみようと思います。
この記事は、UiPath (produced with UiPath Friends) Advent Calendar 2023 の 13日目です。
コード化されたワークフローとは
コード化されたオートメーションの一つで、アクティビティの代わりに C# でコードを書くことにより自動化処理を実現する仕組みです。Coded Workflow とも呼ばれています。
コード化されたワークフローの他にも、コード化されたテストケース、コードソースファイルがあります。それぞれ以下の役割となっています。
- コード化されたワークフロー:Xaml 形式のワークフローに相当
- コード化されたテストケース:Xaml 形式のテストケースに相当
- コードソースファイル:上記2つから呼び出すことができる処理を切り出したロジックを記載できる
コード化されたオートメーションは UiPath Studio 2023.10 以降の Windows またはクロスプラットフォームプロジェクトでのみ利用可能です。Windows レガシでは利用できないので注意しましょう。
作ってみる
RPA Challenge から challenge_ja.xlsx をダウンロードしてプロジェクトフォルダに配置してください。
ワークフローファイルの作成
UiPath Studio でプロジェクトを作成したら、デザインリボンの新規
からコード化されたワークフロー
を選択します。
名前を入力して作成すると、空の Execute
メソッドが定義されたクラスが出てきました。プロジェクト名がネームスペース、ワークフロー名がクラス名となっているようですね。
また、Execute
メソッドには [Workflow]
という属性が付与されておりこのクラスのエントリポイントとなっているようです。[Workflow]
属性が付与されているメソッドであればメソッドの名前は Execute
でなくても問題ないようです。
とはいえ、まぎらわしいのでエントリポイントのメソッド名は Execute
としておくのが良いかと思います。
今回はあまり難しいことは考えずに、Execute
メソッドに処理を追加していきます。
using CodedWorkflow_RPAChallenge.ObjectRepository;
using System;
using System.Collections.Generic;
using System.Data;
using UiPath.CodedWorkflows;
using UiPath.Core;
using UiPath.Core.Activities.Storage;
using UiPath.Orchestrator.Client.Models;
using UiPath.Testing;
using UiPath.Testing.Activities.TestData;
using UiPath.Testing.Activities.TestDataQueues.Enums;
using UiPath.Testing.Enums;
using UiPath.UIAutomationNext.API.Contracts;
using UiPath.UIAutomationNext.API.Models;
using UiPath.UIAutomationNext.Enums;
namespace CodedWorkflow_RPAChallenge
{
public class RPAChallenge : CodedWorkflow
{
[Workflow]
public void Execute()
{
// To start using services, use IntelliSense (CTRL + Space) to discover the available services:
// e.g. system.GetAsset(...)
// For accessing UI Elements from Object Repository, you can use the Descriptors class e.g:
// var screen = uiAutomation.Open(Descriptors.MyApp.FirstScreen);
// screen.Click(Descriptors.MyApp.FirstScreen.SettingsButton);
}
}
}
オブジェクトリポジトリ
コード化されたオートメーションでの UI 要素の指定はオブジェクトリポジトリを使うことになります。
セレクターを直接指定する方法もサポートされていますが、コードと UI 要素の分離ができるオブジェクトリポジトリを使うことが推奨になります。
var screen = uiAutomation.Open(Target.FromSelector("<html app = 'chrome.exe'/>"));
オブジェクトリポジトリについて非常に分かりやすい記事が投稿されているのでそちらを確認ください。
早速オブジェクトリポジトリに UI 要素を追加していきたいところですが、追加方法は省略します。上記記事を参考に皆さんでチャレンジしてください。
こちらが出来上がりのイメージです。3分クッキングみたいですみません。。
画面を開く
では、早速 RPA Challenge の画面を開く処理を追加しましょう。uiAutomation
の Open
メソッドにオブジェクトリポジトリに登録されている画面名を渡すことで画面を開くことができます。
画面名の指定は、Descriptors
の定数を使う方法と文字列での指定の2通りがあります。文字列のハードコーディングを避ける意味で Descriptors
の定数を利用したほうが良いかもです。
// できればこちら
UiTargetApp screen = uiAutomation.Open(Descriptors.Chrome__Rpa_Challenge_app.Chrome__Rpa_Challenge);
// この方法でもOK
UiTargetApp screen = uiAutomation.Open("Chrome: Rpa Challenge");
作成されたワークフローの基底クラスである CodedWorkflow
クラスには下記メンバ変数が存在しており主要アクティビティパッケージのアクティビティをメソッドとして公開しています。
system : UiPath.System.Activities に相当
uiautomation : UiPath.UIAutomation.Activities に相当
testing : UiPath.Testing.Activities に相当
uiautomation.Open
の戻り値として UiTargetApp
型の変数(今回は screen
)が返ってきます。この UiTargetApp
型の変数を使ってクリックや文字の入力等の UI 操作をしていくことになります。
続いて開始ボタンをクリックする処理を追加します。UI 要素のクリックは screen
変数の Click
メソッドにて行います。メソッドの引数にオブジェクトリポジトリに登録している開始ボタンの UI 要素を指定してください。
screen.Click(Descriptors.Chrome__Rpa_Challenge_app.Chrome__Rpa_Challenge.開始);
Excel ファイルの処理
2023年12月6日現在のコード化されたオートメーションでは UiPath.Excel.Activities に該当する C# のクラスの提供がありません。そのため、何らかの方法で Excel ファイルからデータを取得する必要があります。
今回は NPOI を利用して実装を進めていこうかと思います。
NPOI を使うにあたり、ワークフローに以下のネームスペースを追加します。
using NPOI.SS.UserModel;
NPOI で Excel ファイルの操作が出来るようになったので、ファイルを開いてシートを取得する処理を追加します。
IWorkbook book = WorkbookFactory.Create(@".\challenge_ja.xlsx");
ISheet sheet = book.GetSheetAt(0);
ここまでのコードはこちらになります。
RPAChallenge.cs
using System;
using System.Collections.Generic;
using System.Data;
using System.Threading;
using System.Threading.Tasks;
using UiPath.CodedWorkflows;
using UiPath.Core;
using UiPath.Core.Activities.Storage;
using UiPath.Orchestrator.Client.Models;
using UiPath.Testing;
using UiPath.Testing.Activities.TestData;
using UiPath.Testing.Activities.TestDataQueues.Enums;
using UiPath.Testing.Enums;
using UiPath.UIAutomationNext.API.Contracts;
using UiPath.UIAutomationNext.API.Models;
using UiPath.UIAutomationNext.Enums;
using CodedWorkflow_RPAChallenge.ObjectRepository;
using NPOI.SS.UserModel;
namespace CodedWorkflow_RPAChallenge
{
public class RPAChallenge : CodedWorkflow
{
[Workflow]
public void Execute()
{
//UiTargetApp screen = uiAutomation.Open("Chrome: Rpa Challenge");
UiTargetApp screen = uiAutomation.Open(Descriptors.Chrome__Rpa_Challenge_app.Chrome__Rpa_Challenge);
screen.Click(Descriptors.Chrome__Rpa_Challenge_app.Chrome__Rpa_Challenge.開始);
IWorkbook book = WorkbookFactory.Create(@".\challenge_ja.xlsx");
ISheet sheet = book.GetSheetAt(0);
}
}
}
データを入力していく
続いて、1件づつデータを入力していきましょう。Excelファイルからシートの取得までできているので1行ずつのループを入れていきます。
ループの条件に sheet.LastRowNum
を使うことでシートの最終行まで繰り返すようにしています。ループ内では行が取れないまたはインデックス 0番のセル(苗字)に値がなければループを抜けるようにしています。
for (int i = 1; i <= sheet.LastRowNum; i++) {
IRow row = sheet.GetRow(i);
if (row == null) {
break;
}
if (row.GetCell(0) == null || string.IsNullOrEmpty(row.GetCell(0).ToString())) {
break;
}
// ここに1件ずづの処理を記述していきます
}
文字の入力には screen
変数の TypeInto
メソッドを使用します。文字を入力
アクティビティの英語名と同じなので直感的にイメージしやすいですね。
TypeInto
メソッドの第一引数に入力先の UI 要素、第二引数に入力したい文字を指定します。ループ内に以下のコードを追加してください。
Excelのセルの値は row.GetCell(インデックス番号)
で取得しています。
screen.TypeInto(Descriptors.Chrome__Rpa_Challenge_app.Chrome__Rpa_Challenge.苗字, row.GetCell(0).ToString());
screen.TypeInto(Descriptors.Chrome__Rpa_Challenge_app.Chrome__Rpa_Challenge.名前, row.GetCell(1).ToString());
screen.TypeInto(Descriptors.Chrome__Rpa_Challenge_app.Chrome__Rpa_Challenge.会社名, row.GetCell(2).ToString());
screen.TypeInto(Descriptors.Chrome__Rpa_Challenge_app.Chrome__Rpa_Challenge.部署, row.GetCell(3).ToString());
screen.TypeInto(Descriptors.Chrome__Rpa_Challenge_app.Chrome__Rpa_Challenge.住所, row.GetCell(4).ToString());
screen.TypeInto(Descriptors.Chrome__Rpa_Challenge_app.Chrome__Rpa_Challenge.メールアドレス, row.GetCell(5).ToString());
screen.TypeInto(Descriptors.Chrome__Rpa_Challenge_app.Chrome__Rpa_Challenge.電話番号, row.GetCell(6).ToString());
最後に登録ボタンのクリック処理を追加して完成です。
screen.Click(Descriptors.Chrome__Rpa_Challenge_app.Chrome__Rpa_Challenge.登録);
実行してみる
通常のワークフローと同様でデザインリボンから実行することができます。ファイルを実行
を選択して実行してみましょう。
無事完走できましたが、イメージしていたより遅く感じませんでしたか?遅く感じた人は鋭いです。実は入力モードが ハードウェアイベント
だったため処理がもっさりしていました。
オプションを変えてみる
入力モードを ハードウェアイベント
から シミュレート
に変更してみましょう。クリックや文字を入力ごとに細かく変更することも出来ますが今回は一括での変更をしてみます。
全体に影響を与える変更は TargetAppOptions
クラスを使います。 RPAChallenge クラスのメンバ変数として以下を追加します。
TargetAppOptions
でプロパティを変更するときは、OpenMode
も必ず指定してください。
public TargetAppOptions targetAppOptions => new TargetAppOptions(){
InteractionMode = NInteractionMode.Simulate,
OpenMode = NAppOpenMode.IfNotOpen
};
メンバ変数 targetAppOptions
を uiautomation.Open
メソッドの第二引数として渡します。
UiTargetApp screen = uiAutomation.Open(Descriptors.Chrome__Rpa_Challenge_app.Chrome__Rpa_Challenge, targetAppOptions);
この状態でもう一度実行してみましょう。処理も安定して実行も倍近く早くなったことが確認できるかと思います。
コードは最終的にこのようになっているはずです。
using System;
using System.Collections.Generic;
using System.Data;
using System.Threading;
using System.Threading.Tasks;
using UiPath.CodedWorkflows;
using UiPath.Core;
using UiPath.Core.Activities.Storage;
using UiPath.Orchestrator.Client.Models;
using UiPath.Testing;
using UiPath.Testing.Activities.TestData;
using UiPath.Testing.Activities.TestDataQueues.Enums;
using UiPath.Testing.Enums;
using UiPath.UIAutomationNext.API.Contracts;
using UiPath.UIAutomationNext.API.Models;
using UiPath.UIAutomationNext.Enums;
using CodedWorkflow_RPAChallenge.ObjectRepository;
using NPOI.SS.UserModel;
namespace CodedWorkflow_RPAChallenge
{
public class RPAChallenge : CodedWorkflow
{
public TargetAppOptions targetAppOptions => new TargetAppOptions(){
InteractionMode = NInteractionMode.Simulate,
OpenMode = NAppOpenMode.IfNotOpen
};
[Workflow]
public void Execute()
{
//UiTargetApp screen = uiAutomation.Open("Chrome: Rpa Challenge");
UiTargetApp screen = uiAutomation.Open(Descriptors.Chrome__Rpa_Challenge_app.Chrome__Rpa_Challenge, targetAppOptions);
IWorkbook book = WorkbookFactory.Create(@".\challenge_ja.xlsx");
ISheet sheet = book.GetSheetAt(0);
screen.Click(Descriptors.Chrome__Rpa_Challenge_app.Chrome__Rpa_Challenge.開始);
for (int i = 1; i <= sheet.LastRowNum; i++) {
IRow row = sheet.GetRow(i);
if (row == null) {
break;
}
if (row.GetCell(0) == null || string.IsNullOrEmpty(row.GetCell(0).ToString())) {
break;
}
screen.TypeInto(Descriptors.Chrome__Rpa_Challenge_app.Chrome__Rpa_Challenge.苗字, row.GetCell(0).ToString());
screen.TypeInto(Descriptors.Chrome__Rpa_Challenge_app.Chrome__Rpa_Challenge.名前, row.GetCell(1).ToString());
screen.TypeInto(Descriptors.Chrome__Rpa_Challenge_app.Chrome__Rpa_Challenge.会社名, row.GetCell(2).ToString());
screen.TypeInto(Descriptors.Chrome__Rpa_Challenge_app.Chrome__Rpa_Challenge.部署, row.GetCell(3).ToString());
screen.TypeInto(Descriptors.Chrome__Rpa_Challenge_app.Chrome__Rpa_Challenge.住所, row.GetCell(4).ToString());
screen.TypeInto(Descriptors.Chrome__Rpa_Challenge_app.Chrome__Rpa_Challenge.メールアドレス, row.GetCell(5).ToString());
screen.TypeInto(Descriptors.Chrome__Rpa_Challenge_app.Chrome__Rpa_Challenge.電話番号, row.GetCell(6).ToString());
screen.Click(Descriptors.Chrome__Rpa_Challenge_app.Chrome__Rpa_Challenge.登録);
}
}
}
}
おわりに
颯爽と現れたコード化されたオートメーションですがいかがだったでしょうか?
今までのローコード開発とは逆のアプローチの機能がリリースされましたが、今までのアクティビティでの開発が主流であることは変わらないのでご安心ください。この機能はプログラミング経験のある開発者やチームが生産性を損なわずに自動化処理を実装していくのに役に立つものと理解しています(Selenium 等で画面の自動化テストを実装していた方が UiPath でテストケースを記述する際に効果を発揮する気がしています)。
個人的にはオブジェクトリポジトリを使うことで UI 要素とコードの分離ができていてすっきりした記述が可能となっており今後の成長が楽しみな機能の一つになりました。
是非皆さんもお試しください!