データ構造
目次
多くのルーティング構造はデータのタスク処理に依存します。 Control Plan Editor には下記の二つのタイプのデータ処理があります。
- プレースベースのデータ
- トークン(あるいはエンベロープ)ベースのデータ
トークンベースのデータはトークンと関係するるデータです。パラメータはトークンにアサインされ、そのトークンが入るプレース内のどのタスクでもこのデータをアクセスできます。ペトリネット用語では、これは色付きのペトリネットとして参照されます。
プレース変数の定義
このセクションではプレースタスクで使用する変数を定義するさまざまの方法を定義します。編集パラメータの使用
プレースパラメータはノード上で右クリックし、Edit Parametersを選ぶことで定義されます。パラメータの名前と値を入力できる表が表示されます。その後、これらはプレース内のプログラムからアクセスできるようになります。この方法により、スクリプトの変更なしにパラメータを定義することができます。これは、再利用可能なテンプレートを作成しやすくします。例えば、プレースに下記のパラメータが定義されたとします。
名前 |
値 |
var1 |
1 |
var2 |
2 |
var3 |
3 |
Java スクリプトでは、その変数は myself オブジェクトにアサインされ、getInt メソッドでアクセスできます。
例:
var1 = myself.getInt("var1"); var2 = myself.getInt("var2"); var3 = myself.getInt("var3"); sum = var1 + var2 + var3; print(sum);
これは、コンソール上に "6" をプリントします。
Ruby スクリプトでは、$を先頭に置くmyself オブジェクトでアクセスします。
例:
var1 = $myself.getInt("var1"); var2 = $myself.getInt("var2"); var3 = $myself.getInt("var3"); sum = var1 + var2 + var3; puts sum;
これは、コンソール上に "6" をプリントします。
入力仕様の使用
入力仕様もパラメータの変わりに使用することができます。この場合、変数は個別に参照されます。Java スクリプトでは、その変数は myself オブジェクトにアサインされ、getInt メソッドでアクセスできます。
例:
sum = Integer.valueOf(var1) + Integer.valueOf(var2) + Integer.valueOf(var3); print(sum);
これは、コンソール上に "6" をプリントします。注:var1、var2 とvar3 は文字列であって、sum(加算)操作のためにはintegerへの変換が必要です。
Ruby スクリプトでは、$を先頭に置くmyself オブジェクトでアクセスします。
例:
sum = Integer.valueOf(var1) + Integer.valueOf(var2) + Integer.valueOf(var3); puts sum;
これは、コンソール上に "6" をプリントします。
これらの操作のコントロールプランのサンプルはここからダウンロードできます。
インピーダンス
インピーダンスは、あるプレースから後のプレースにデータを渡すもうひとつの方法です。多くの場合、プレースタスクは次のタスクに渡すためにデータを計算しています。インピーダンスを使用することで、このデータを受け渡すためにプレース間で共通したコードが必要なくなり、それにより各プレースの独立性を守ります。現在のプレースから次のプレースに出力される変数は、出力仕様で定義されます。次に続くプレース内で、前のプレースから読み込む変数は、入力仕様で定義されます。入力仕様と出力仕様が定義された後、"editing impedances"によって、各出力変数を入力変数と関係つけることができます。モデルが実行されると、その変数内の値はこの仕様の従って受け渡されます。出力仕様
出力仕様はプレース上で右クリックし、Edit Output Spec をクリックして定義します。 表形式のビューが表示され、変数名を追加できるようになります。タスクプロセスの中で、これらの変数の値をアサインすることができます。Javaスクリプトでは変数名を使用します。Rubyスクリプトでは、$サインを先頭につけた変数名を使用します。入力仕様
入力仕様はプレース上で右クリックし、Edit Input Specをクリックして定義します。 表形式のビューが表示され、変数名を追加できるようになります。タスクプロセスの中で、これらの変数の値をアサインすることができます。Javaスクリプトでは変数名を使用します。Rubyスクリプトでは、$サインを先頭につけた変数名を使用します。インピーダンスの例
プレースP1がプレースP2に接続しているモデルがあるとします。 P1 には下記のようなJavaスクリプトが含まれます。out_var1 = 100;P2には下記のようなJavaスクリプトが含まれます。
print(in_var1);P1 の出力仕様ではout_var1 の名前で出力を定義します。P2の入力仕様では、in_var1 の名前で入力を定義します。インピーダンスを編集する際、ソースの変数out_var1 が in_var1にマッチングされます。このモデルが実行されると、"100" が P2 のコンソール上に表示されるでしょう。 このコントロールプランの例は、ここからダウンロードできます。
トークンのエンベロープ・データ
もう一つのデータ受け渡し方法は、データをトークンに割り当てることです。モデル全体を進んでいくトークンに含まれるエンベロープ・データを考えてみてください。どのノードのタスクもエンベロープ内にデータを置くことができ、エンベロープ内のデータを見ることができます。下記の例では、プレースP2に接続しているプレースP1があります。P1の中で、エンベロープ内に100の値を持つ"var1"という名前の変数を定義しています。Java では:
myself.add("var1",100);Rubyでは:
$myself.add("var1",100)それ以降、どのプレースでも、エンベロープからvar1 の値を取り出すことができます。P2に下記のコードを持っています。
Java では:
x = myself.get("var1"); print(x);Ruby では:
x = $myself.get("var1") puts xP2 のコンソールの出力結果は"100" です。
メソッド
myself オブジェクトに下記のメソッドが適用できます:Ruby と Java
- add(key, value) - key名にvalueの値をセットする。"set"と同じ。
- get(key) - key名の値を取得する
- rm(key) - エンベロープからkey名を削除する
- expose(key, value) - 次のプレースに(次プレース内で)この名前でのアクセスを認める
- addError(string) - エラーキーにエラー文字列を連結する
- ("error")によって取り出す
- getKeys - キー名のベクターを返す
- dumpKeys - キー名の文字列を返す
- kill - このタスクの処理を停止し、モデルは次のプレースに進まない。Implicit-OR構造と共に使用する
- blinkOn - ノードのブリンキングを開始
- blinkOff - ノードのブリンキングを停止
エンベロープの分割
エンベロープのデータは並行処理ルーティング構造が使用されている場合、少し厄介です。このケースでは、AND-分割トランジションになった場合にエンベロープのコピーが作られ、各並行パスに沿って送られます。独立して各エンベロープにデータを追加することができます。AND-結合トランジションに到達したら、前のトランジションからの複数のエンベロープが結合されます。データは、値を追加したプレース参照でアドレス指定されます。下記のモデルについて考えて見ましょう。AND分割でP8とP9が並行して処理されます。下記のコード例はRubyです。
P1 には、下記が含まれます:
$myself.add "value2", "20"P8 には、下記が含まれます:
$myself.add "value1", "10"P9 には、下記が含まれます:
$myself.add "value1", "5"P4 はデータのエンベロープをアクセスするでしょう。それには下記が含まれます:
envs = $myself.getEnvelopes() puts "Number of envelopes = #{envs.length}" envs.each { |e| if e.get("value1").nil? then puts e.getName()+" has no key value1." else puts e.getName()+" value1 =" +e.get("value1") end if e.get("value2").nil? then puts e.getName()+" has no key value2." else puts e.getName()+" value2 =" +e.get("value2") end }このプランが実行されると、value2に"20"を割り当てた、一つのエンベロープがP1に存在します。その後、そのエンベロープは分割され、それぞれvalue2に"20"がセットされます。 P8 プレースが、そのエンベロープのvalue1に10を加えます。 P9 がvalue1に5を加えます。 P4のコンソールの出力は下記のようになります:
Number of envelopes = 3 P4 has no key value1. P4 has no key value2. P9 value1 =5 P9 value2 =20 P8 value1 =10 P8 value2 =20ここでP4のコードがどのように動くかを説明します。 getEnvelopes メソッドはすべてのエンベロープを取得し、配列envの中に置きます。配列の中には3つのエンベロープがあります。 注:getNameメソッドは値を追加した最後のプレースによってセットされたエンベロープ名を取得します。現在のプレースは、value1とvalue2の名前のための値を持たないエンベロープP4です。ここで、P8パスとP9パスからのエンベロープを持っています。それぞれ、AND分割前のvalue2の20を持っています。それぞれが、それぞれのパスからのvalue1の内容を持っています。このコードから、あなたが必要とする値を参照できるでしょう。
注:エンベロープに適用できる getKeys メソッドがあります。これはエンベロープに関連付けられたキー名をリストします(この例では、value1 と value2)。P4の別の記述として、下記の記述が有ります:
envs.each { |e| puts "Envelope name is "+e.getName() e.getKeys.each { |k| puts "\t#{k} = #{e.get(k)}" } }これは、下記のコンソール出力をもたらすでしょう。
Envelope name is P4 envelope[0] = com.tapin.petri.frame.Envelope@1f8b6e0 timestamp = 1257295507930 envelope[1] = com.tapin.petri.frame.Envelope@10599cc Envelope name is P9 timestamp = 1257295497922 value1 = 5 value2 = 20 Envelope name is P8 timestamp = 1257295497922 value1 = 10 value2 = 20注: myself オブジェクトの構造はAND-結合の後で変わります。エンベロープが分割されたため、各並行パスがキーの値を変える可能性があります。 結合後、この差を解消する必要があります。 AND-分割ので後キーの値が変更されなかった場合、下記のコードを使ってmyself オブジェクトを元のAND-分割前の構造にリストアすることができます。
# Restore myself structure from before AND split # envs = $myself.getEnvelopes newkeys = Hash.new envs.each { |e| puts e.getName e.getKeys.each { |k| if newkeys[k].nil? then # Set this key if new newkeys[k] = e.get(k) puts "\tSetting key #{k} = #{e.get(k)}" elsif newkeys[k] == e.get(k) then puts "\tDuplicate found for key #{k}" else puts "\tConflict found. Multiple values for key #{k}: #{newkeys[k]} and #{e.get(k)}" end } } $myself.getKeys().each { |k| $myself.rm(k) } # Clear myself [[User Interface Dialogs#Data Table View|here]]newkeys.each { |k,v| $myself.add(k,v) }
# Set to new keys
制約変数
制約変数(constraint variable)はモデル中の条件付ルーティングのどちらのパスが採られたかを調べるために使用されます。 接続が定義されたとき、コネクターを右クリックで選び、Edit Constraintを選択することで制約を追加することができます。使用される条件テストの入力します。 テストの値が真(true)であれば、このパスが採られます。制約はコネクター上に現われます。 Javaスクリプトでは、タスク内のどのような変数でも使用できます。Rubyスクリプトでは、$をプレフィックスとするどのような変数でも使用できます。下記に条件付ルーティングの簡単な例を説明します。この例は、Rubyスクリプトを使用します。$okが真であったら、P1はP2に遷移します。そうでない場合、P3に遷移します。
P1 は下記を含みます:
$ok=true注:採られたパスは P2 を通っています。
データテーブルのオブジェクトとビュー
データテーブルのビューは表形式のデータビューを提供します。コントロールプランはイン-メモリのSQLテーブルであるオブジェクトをサポートします。一度作成されると、このオブジェクトはモデル内のノードにトークンのデータエンベロープを解して渡されます。このオブジェクトは、さまざまのノードによってアクセスされる大量のデータがある場合、便利でしょう。
データテーブルとオブジェクトはユーザインタフェース・ダイアログの データテーブル表示のセクションに記述されています。