こんにちは。スマートソリューションセンターの吉田です。
RPAで業務を自動化したい場合、どのくらいの工数(期間)がかかるか、あらかじめ見積もりたいですよね。そのためには、作りたいロボットの規模を測ること、開発生産性を知ることが必要になってきます。
テンプレート型RPAにおいて、ロボットを構成する最小要素はActivity(Action)です。よって、Activity(Action)数を計測することで、ロボットの規模を図ることができると考えます。Activity(Action)はおおざっぱにいうと、自動化したい1操作に該当しますので、5つの操作を自動化したい場合、5Activity(Action)を作れば良いということになります。自動化したい業務を整理し、操作回数をカウントしていくと、ロボット規模が算出できます。
※より正確に算出するには、繰り返し処理の有無や、非構造化データを含むか、対象システムやデータ点数などで重みづけをする必要があります。
「○○Activity(Action)規模のロボットを作成するのに必要な工数」を算出するために、開発生産性を知りたいところです。単純に過去実績から開発生産性を算出するとして、まずはロボットを作ってみて開発生産性を算出する際、開発画面上で、目視で一つずつ部品数を足し上げていくのは非常に骨が折れます。
ですので、ここでは、RPAツールの一つであるUiPathを例にしてソースファイル(.xaml)からActivity数、その他情報を抽出する方法を紹介したいと思います。とはいえ、いきなりXAMLの話をしても面白くないので、まずは、開発者にはお馴染みのHelloWorldでUiPathの使い方を簡単に振り返ったところで、HelloWorldロボットのソースを解析し、どのように成り立っているのかを含めて整理してみようと思います。
ポイントは以下です。
- UiPathロボットがソースでどのように表現されているか
- ソースから、ロボットの規模(Activity数)を算出する方法
では早速見ていきましょう。
まずは、UiPath Studioを開きましょう。
執筆時点のUiPath Studio 2018.1.1バージョンでは、Blank、Flowchart、Assistant、ReFrameworkといったプロジェクトテンプレートがあります。特にReFrameworkは、設定ファイル(Excel)からの情報取得や、Orchestratorサーバ管理機能との連携など、Enterprise向けにも活用できるサンプルコードが組み込まれた形で用意されているので、おすすめです。
さてHelloWorldの続きですが、実はUiPath公式の解説ページに画面キャプチャ付きで紹介されていますので、そちらに沿って項番13辺りまで実施します。
解説ページ通りに進めると、以下のようになっているはずです。
この状態でロボットを実行してみます。
名前の入力ダイアログが表示され、名前を入力後、OKを押すと入力した名前が表示されるHelloWorldロボットができました。
ここまで、おそらく5分かからずに作成できたのではないかと思います。簡単ですね。
それでは、簡単に振り返ってみましょう。
UiPathでは、Activityとよばれる部品(ロボットの最小構成要素)を組み合わせて、ワークフロー(シナリオ)を構築します。Activityは現時点で、およそ300種類あり、アクティビティを使用することで、ウェブブラウザ、デスクトップアプリ、PDF、画像、データベース、Excelスプレッドシート、Eメールアカウントなどとの連携や、HTTPやSOAP要求の作成を簡単に実行できます。
では、HelloWorldロボットはいくつのActivityで構成されているでしょうか。答えは3つです。ユーザ入力を受け付けるInput Dialog、メッセージを表示するMessage Box、そして、コンテナとして使っているFlowchartから構成されます。
- Flowchart
- Input Dialog
- Message Box
ロボットの最小構成単位であるActivityを組み合わせて、一連の手続きを行うワークフロー(シナリオ)を構築します。ワークフローの実態は、XMLの拡張言語である、XAML形式のファイルです。
単一、または複数のワークフローから、ロボットを構築することができます。
では、HelloWorldロボットはいくつのワークフローで構成されているでしょうか。答えは1つです。単一のMainワークフローで構成されています。
- Main.xaml
UiPathでは、変数を定義することができます。こちらもワークフローの構成要素としてXAML上で表現されます。
では、HelloWorldロボットはいくつの変数を使用されているでしょうか。答えは1つです。名前の入力ダイアログで、ユーザが入力した結果を保持する文字列型変数nameを使用しました。
- name
ここまでをまとめると、HelloWorld ロボットのスペックは、以下といえるでしょう。
- Main.xamlという単一のワークフローからなるロボットで、3つのActivityと、1つの変数から構成される。
このように言語化してみると、ソースも単純なソースから構成されていそうですよね。では、ようやくですが、ソースを見てみましょう。
UiPath StudioのGUI上で作成したロボットが、どのようにソースで表現されているか見てみましょう。
Main.xamlをお使いのテキストエディタで開いてください。
<Activity mc:Ignorable="sap sap2010 sads" x:Class="Main" mva:VisualBasic.Settings="{x:Null}" sap2010:WorkflowViewState.IdRef="Main_1"
xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities"
xmlns:av="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mva="clr-namespace:Microsoft.VisualBasic.Activities;assembly=System.Activities"
xmlns:sads="http://schemas.microsoft.com/netfx/2010/xaml/activities/debugger"
xmlns:sap="http://schemas.microsoft.com/netfx/2009/xaml/activities/presentation"
xmlns:sap2010="http://schemas.microsoft.com/netfx/2010/xaml/activities/presentation"
xmlns:scg="clr-namespace:System.Collections.Generic;assembly=mscorlib"
xmlns:sco="clr-namespace:System.Collections.ObjectModel;assembly=mscorlib"
xmlns:ui="http://schemas.uipath.com/workflow/activities"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TextExpression.NamespacesForImplementation>
<sco:Collection x:TypeArguments="x:String">
<x:String>System.Activities</x:String>
<x:String>System.Activities.Statements</x:String>
<x:String>System.Activities.Expressions</x:String>
<x:String>System.Activities.Validation</x:String>
<x:String>System.Activities.XamlIntegration</x:String>
<x:String>Microsoft.VisualBasic</x:String>
<x:String>Microsoft.VisualBasic.Activities</x:String>
<x:String>System</x:String>
<x:String>System.Collections</x:String>
<x:String>System.Collections.Generic</x:String>
<x:String>System.Data</x:String>
<x:String>System.Diagnostics</x:String>
<x:String>System.Drawing</x:String>
<x:String>System.IO</x:String>
<x:String>System.Linq</x:String>
<x:String>System.Net.Mail</x:String>
<x:String>System.Xml</x:String>
<x:String>System.Xml.Linq</x:String>
<x:String>UiPath.Core</x:String>
<x:String>UiPath.Core.Activities</x:String>
<x:String>System.Windows.Markup</x:String>
</sco:Collection>
</TextExpression.NamespacesForImplementation>
<TextExpression.ReferencesForImplementation>
<sco:Collection x:TypeArguments="AssemblyReference">
<AssemblyReference>System.Activities</AssemblyReference>
<AssemblyReference>Microsoft.VisualBasic</AssemblyReference>
<AssemblyReference>mscorlib</AssemblyReference>
<AssemblyReference>System.Data</AssemblyReference>
<AssemblyReference>System</AssemblyReference>
<AssemblyReference>System.Drawing</AssemblyReference>
<AssemblyReference>System.Core</AssemblyReference>
<AssemblyReference>System.Xml</AssemblyReference>
<AssemblyReference>System.Xml.Linq</AssemblyReference>
<AssemblyReference>UiPath.Core</AssemblyReference>
<AssemblyReference>UiPath.Core.Activities</AssemblyReference>
<AssemblyReference>PresentationFramework</AssemblyReference>
<AssemblyReference>WindowsBase</AssemblyReference>
<AssemblyReference>PresentationCore</AssemblyReference>
<AssemblyReference>System.Xaml</AssemblyReference>
<AssemblyReference>System.ComponentModel.Composition</AssemblyReference>
<AssemblyReference>System.ServiceModel</AssemblyReference>
</sco:Collection>
</TextExpression.ReferencesForImplementation>
<Flowchart DisplayName="FlowChart1" sap2010:WorkflowViewState.IdRef="Flowchart_1">
<Flowchart.Variables>
<Variable x:TypeArguments="x:String" Name="name" />
</Flowchart.Variables>
<Flowchart.StartNode>
<FlowStep x:Name="__ReferenceID0" sap2010:WorkflowViewState.IdRef="FlowStep_1">
<ui:InputDialog Options="{x:Null}" DisplayName="Input dialog" sap2010:WorkflowViewState.IdRef="InputDialog_1" IsPassword="False" Label="Type your name" Title="Hello, world!">
<ui:InputDialog.Result>
<OutArgument x:TypeArguments="x:String">[name]</OutArgument>
</ui:InputDialog.Result>
</ui:InputDialog>
<FlowStep.Next>
<FlowStep x:Name="__ReferenceID1" sap2010:WorkflowViewState.IdRef="FlowStep_2">
<ui:MessageBox ChosenButton="{x:Null}" Buttons="Ok" Caption="Hello" DisplayName="Message box" sap2010:WorkflowViewState.IdRef="MessageBox_2" Text="["Hello " + name]" TopMost="True" />
</FlowStep>
</FlowStep.Next>
</FlowStep>
</Flowchart.StartNode>
<x:Reference>__ReferenceID0</x:Reference>
<x:Reference>__ReferenceID1</x:Reference>
<sads:DebugSymbol.Symbol>d0FDOlxVc2Vyc1x1MjAxMzA2Mi5EMTAwMFxEb2N1bWVudHNcdWlwYXRoXE9DUlxIZWxsb1dvcmxkXE1haW4ueGFtbAg7A1APAgEBQQlFGgIBBkgNSM4BAgECQZABQaABAgEKQzVDOwIBCEGnAUG2AQIBB0hJSFACAQVInwFIvAECAQM=</sads:DebugSymbol.Symbol>
</Flowchart>
<sap2010:WorkflowViewState.ViewStateManager>
<sap2010:ViewStateManager>
<sap2010:ViewStateData Id="InputDialog_1" sap:VirtualizedContainerService.HintSize="200,52.6666666666667">
<sap:WorkflowViewStateService.ViewState>
<scg:Dictionary x:TypeArguments="x:String, x:Object">
<x:Boolean x:Key="IsExpanded">True</x:Boolean>
</scg:Dictionary>
</sap:WorkflowViewStateService.ViewState>
</sap2010:ViewStateData>
<sap2010:ViewStateData Id="MessageBox_2" sap:VirtualizedContainerService.HintSize="200,52.6666666666667">
<sap:WorkflowViewStateService.ViewState>
<scg:Dictionary x:TypeArguments="x:String, x:Object">
<x:Boolean x:Key="IsExpanded">True</x:Boolean>
</scg:Dictionary>
</sap:WorkflowViewStateService.ViewState>
</sap2010:ViewStateData>
<sap2010:ViewStateData Id="FlowStep_2">
<sap:WorkflowViewStateService.ViewState>
<scg:Dictionary x:TypeArguments="x:String, x:Object">
<av:Point x:Key="ShapeLocation">200,229.166666666667</av:Point>
<av:Size x:Key="ShapeSize">200,52.6666666666667</av:Size>
</scg:Dictionary>
</sap:WorkflowViewStateService.ViewState>
</sap2010:ViewStateData>
<sap2010:ViewStateData Id="FlowStep_1">
<sap:WorkflowViewStateService.ViewState>
<scg:Dictionary x:TypeArguments="x:String, x:Object">
<av:Point x:Key="ShapeLocation">200,127.166666666667</av:Point>
<av:Size x:Key="ShapeSize">200,52.6666666666667</av:Size>
<av:PointCollection x:Key="ConnectorLocation">300,179.166666666667 300,229.166666666667</av:PointCollection>
</scg:Dictionary>
</sap:WorkflowViewStateService.ViewState>
</sap2010:ViewStateData>
<sap2010:ViewStateData Id="Flowchart_1" sap:VirtualizedContainerService.HintSize="614,636">
<sap:WorkflowViewStateService.ViewState>
<scg:Dictionary x:TypeArguments="x:String, x:Object">
<x:Boolean x:Key="IsExpanded">False</x:Boolean>
<av:Point x:Key="ShapeLocation">270,2.5</av:Point>
<av:Size x:Key="ShapeSize">60,74.6666666666667</av:Size>
<av:PointCollection x:Key="ConnectorLocation">300,77.1666666666667 300,127.166666666667</av:PointCollection>
</scg:Dictionary>
</sap:WorkflowViewStateService.ViewState>
</sap2010:ViewStateData>
<sap2010:ViewStateData Id="Main_1" sap:VirtualizedContainerService.HintSize="654,756" />
</sap2010:ViewStateManager>
</sap2010:WorkflowViewState.ViewStateManager>
</Activity>
長いですよね。笑
数行で済みそうな機能なのに、127行あります。そもそもRPAはノンコーディングなので、ソースを見る意味があるのか、という意見もあるかもしれませんが、せっかくなので、分解してみましょう。
大きく3つのSectionに分けれるかと思います。それぞれ見ていきましょう。
- 名前空間、アセンブリのインポート Section(#1~#58)
- ワークフロー実態 Section(#59~#80)
- UiPath StudioがGUI表示に使用するSection(#81~#127)
<Activity mc:Ignorable="sap sap2010 sads" x:Class="Main" mva:VisualBasic.Settings="{x:Null}" sap2010:WorkflowViewState.IdRef="Main_1" |
xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities" |
xmlns:av="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" |
xmlns:mva="clr-namespace:Microsoft.VisualBasic.Activities;assembly=System.Activities" |
xmlns:sads="http://schemas.microsoft.com/netfx/2010/xaml/activities/debugger" |
xmlns:sap="http://schemas.microsoft.com/netfx/2009/xaml/activities/presentation" |
xmlns:sap2010="http://schemas.microsoft.com/netfx/2010/xaml/activities/presentation" |
xmlns:scg="clr-namespace:System.Collections.Generic;assembly=mscorlib" |
xmlns:sco="clr-namespace:System.Collections.ObjectModel;assembly=mscorlib" |
xmlns:ui="http://schemas.uipath.com/workflow/activities" |
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> |
<TextExpression.NamespacesForImplementation> |
<sco:Collection x:TypeArguments="x:String"> |
<x:String>System.Activities</x:String> |
<x:String>System.Activities.Statements</x:String> |
<x:String>System.Activities.Expressions</x:String> |
<x:String>System.Activities.Validation</x:String> |
<x:String>System.Activities.XamlIntegration</x:String> |
<x:String>Microsoft.VisualBasic</x:String> |
<x:String>Microsoft.VisualBasic.Activities</x:String> |
<x:String>System</x:String> |
<x:String>System.Collections</x:String> |
<x:String>System.Collections.Generic</x:String> |
<x:String>System.Data</x:String> |
<x:String>System.Diagnostics</x:String> |
<x:String>System.Drawing</x:String> |
<x:String>System.IO</x:String> |
<x:String>System.Linq</x:String> |
<x:String>System.Net.Mail</x:String> |
<x:String>System.Xml</x:String> |
<x:String>System.Xml.Linq</x:String> |
<x:String>UiPath.Core</x:String> |
<x:String>UiPath.Core.Activities</x:String> |
<x:String>System.Windows.Markup</x:String> |
</sco:Collection> |
</TextExpression.NamespacesForImplementation> |
<TextExpression.ReferencesForImplementation> |
<sco:Collection x:TypeArguments="AssemblyReference"> |
<AssemblyReference>System.Activities</AssemblyReference> |
<AssemblyReference>Microsoft.VisualBasic</AssemblyReference> |
<AssemblyReference>mscorlib</AssemblyReference> |
<AssemblyReference>System.Data</AssemblyReference> |
<AssemblyReference>System</AssemblyReference> |
<AssemblyReference>System.Drawing</AssemblyReference> |
<AssemblyReference>System.Core</AssemblyReference> |
<AssemblyReference>System.Xml</AssemblyReference> |
<AssemblyReference>System.Xml.Linq</AssemblyReference> |
<AssemblyReference>UiPath.Core</AssemblyReference> |
<AssemblyReference>UiPath.Core.Activities</AssemblyReference> |
<AssemblyReference>PresentationFramework</AssemblyReference> |
<AssemblyReference>WindowsBase</AssemblyReference> |
<AssemblyReference>PresentationCore</AssemblyReference> |
<AssemblyReference>System.Xaml</AssemblyReference> |
<AssemblyReference>System.ComponentModel.Composition</AssemblyReference> |
<AssemblyReference>System.ServiceModel</AssemblyReference> |
</sco:Collection> |
</TextExpression.ReferencesForImplementation> |
本Sectionは、UiPathの独自定義ではなく、Microsoft のXamlアーキテクチャおよび、Windows Workflow Foundationに依存しています。XAML名前空間を最初にインポートする必要があり、これによって、.NET FrameworkのAPIや、UiPathが提供するライブラリの呼び出しが可能となっています。上記に記載がない名前空間のAPIを使用したい場合は、UiPath StudioのImportsタブから追加することが可能です。
<Flowchart DisplayName="FlowChart1" sap2010:WorkflowViewState.IdRef="Flowchart_1"> |
<Flowchart.Variables> |
<Variable x:TypeArguments="x:String" Name="name" /> |
</Flowchart.Variables> |
<Flowchart.StartNode> |
<FlowStep x:Name="__ReferenceID0" sap2010:WorkflowViewState.IdRef="FlowStep_1"> |
<ui:InputDialog Options="{x:Null}" DisplayName="Input dialog" sap2010:WorkflowViewState.IdRef="InputDialog_1" IsPassword="False" Label="Type your name" Title="Hello, world!"> |
<ui:InputDialog.Result> |
<OutArgument x:TypeArguments="x:String">[name]</OutArgument> |
</ui:InputDialog.Result> |
</ui:InputDialog> |
<FlowStep.Next> |
<FlowStep x:Name="__ReferenceID1" sap2010:WorkflowViewState.IdRef="FlowStep_2"> |
<ui:MessageBox ChosenButton="{x:Null}" Buttons="Ok" Caption="Hello" DisplayName="Message box" sap2010:WorkflowViewState.IdRef="MessageBox_2" Text="["Hello " + name]" TopMost="True" /> |
</FlowStep> |
</FlowStep.Next> |
</FlowStep> |
</Flowchart.StartNode> |
<x:Reference>__ReferenceID0</x:Reference> |
<x:Reference>__ReferenceID1</x:Reference> |
<sads:DebugSymbol.Symbol>d0FDOlxVc2Vyc1x1MjAxMzA2Mi5EMTAwMFxEb2N1bWVudHNcdWlwYXRoXE9DUlxIZWxsb1dvcmxkXE1haW4ueGFtbAg7A1APAgEBQQlFGgIBBkgNSM4BAgECQZABQaABAgEKQzVDOwIBCEGnAUG2AQIBB0hJSFACAQVInwFIvAECAQM=</sads:DebugSymbol.Symbol> |
</Flowchart> |
ワークフローの実態となる部分です。
Flowchartコンテナの中に、Flowchart.Variablesで変数定義がありますね。
また、ui:InputDialog、ui:MessageBoxといったActivityの定義があることがわかります。
このように、XAML上でActivityやPropertyはテキストとして保持され、ロボットによって解釈され実行されています。
<sap2010:WorkflowViewState.ViewStateManager> |
<sap2010:ViewStateManager> |
<sap2010:ViewStateData Id="InputDialog_1" sap:VirtualizedContainerService.HintSize="200,52.6666666666667"> |
<sap:WorkflowViewStateService.ViewState> |
<scg:Dictionary x:TypeArguments="x:String, x:Object"> |
<x:Boolean x:Key="IsExpanded">True</x:Boolean> |
</scg:Dictionary> |
</sap:WorkflowViewStateService.ViewState> |
</sap2010:ViewStateData> |
<sap2010:ViewStateData Id="MessageBox_2" sap:VirtualizedContainerService.HintSize="200,52.6666666666667"> |
<sap:WorkflowViewStateService.ViewState> |
<scg:Dictionary x:TypeArguments="x:String, x:Object"> |
<x:Boolean x:Key="IsExpanded">True</x:Boolean> |
</scg:Dictionary> |
</sap:WorkflowViewStateService.ViewState> |
</sap2010:ViewStateData> |
<sap2010:ViewStateData Id="FlowStep_2"> |
<sap:WorkflowViewStateService.ViewState> |
<scg:Dictionary x:TypeArguments="x:String, x:Object"> |
<av:Point x:Key="ShapeLocation">200,229.166666666667</av:Point> |
<av:Size x:Key="ShapeSize">200,52.6666666666667</av:Size> |
</scg:Dictionary> |
</sap:WorkflowViewStateService.ViewState> |
</sap2010:ViewStateData> |
<sap2010:ViewStateData Id="FlowStep_1"> |
<sap:WorkflowViewStateService.ViewState> |
<scg:Dictionary x:TypeArguments="x:String, x:Object"> |
<av:Point x:Key="ShapeLocation">200,127.166666666667</av:Point> |
<av:Size x:Key="ShapeSize">200,52.6666666666667</av:Size> |
<av:PointCollection x:Key="ConnectorLocation">300,179.166666666667 300,229.166666666667</av:PointCollection> |
</scg:Dictionary> |
</sap:WorkflowViewStateService.ViewState> |
</sap2010:ViewStateData> |
<sap2010:ViewStateData Id="Flowchart_1" sap:VirtualizedContainerService.HintSize="614,636"> |
<sap:WorkflowViewStateService.ViewState> |
<scg:Dictionary x:TypeArguments="x:String, x:Object"> |
<x:Boolean x:Key="IsExpanded">False</x:Boolean> |
<av:Point x:Key="ShapeLocation">270,2.5</av:Point> |
<av:Size x:Key="ShapeSize">60,74.6666666666667</av:Size> |
<av:PointCollection x:Key="ConnectorLocation">300,77.1666666666667 300,127.166666666667</av:PointCollection> |
</scg:Dictionary> |
</sap:WorkflowViewStateService.ViewState> |
</sap2010:ViewStateData> |
<sap2010:ViewStateData Id="Main_1" sap:VirtualizedContainerService.HintSize="654,756" /> |
</sap2010:ViewStateManager> |
</sap2010:WorkflowViewState.ViewStateManager> |
</Activity> |
こちらはUiPath Studioでワークフローをビジュアル化する際に使用するレイアウト情報です。特に有益な情報はないので、紹介までにとどめておきます。
さて、ここまででUiPath StudioのGUI上で作成したロボットが、どのようにソースに表現されているか理解いただけたかと思います。続いて、ロボットの規模(Activity数)を算出する方法を見てみましょう。
冒頭で紹介したように、ロボットを構成する最小要素はActivityです。よって、Activity数を計測することで、ロボットの規模を図ることができると考えます。
ソースファイル(.xaml)からActivity数を計測する方法の一つは、全Activityに共通して存在するプロパティ「DisplayName」の件数をカウントすることです。
HelloWorldロボット(Main.xaml)で、「DisplayName」でGrepすると、3件ヒットします。
XAMLはいろいろなタグから構成されていますが、Flowchart、InputDialog、MessageBoxの3つのActivityのみにDisplayNameプロパティが含まれます。
長々と書いたわりに、最終的にはシンプルなやり方ですが、是非試してみて下さい。
応用として、XAMLを解析することで、Argument(ロボットの引数(外部パラメータ))や、変数定義、使用しているActivityの列挙が可能となりますので、ソースから設計書をリバースエンジニアリングすることも可能ですし、使用していない変数定義が残っていないか、社内規定で、使用してはいけないActivityを使っていないかなどをチェックするレビューロボを作ることもできます。
- ロボット規模は、自動化したい業務を整理し、操作回数をカウントしていくことで算出する。
- 開発生産性は、過去に作成したロボット(.xaml)のActivity数と、作成にかかった時間から算出する。Activity数は、過去に作成したロボット(.xaml)を「DisplayName」でGrepすることで抽出できる。
以上です!
今回は少し技術ブログっぽい内容をお届けしてみました。
次回もお楽しみに。