|
Savoy ActiveXコントロール
|
|
このサンプルではGEM 300での実装を説明します。
|
|
|
定数の宣言
プログラム中で使う定数をEnumで宣言します。今回はVID、CEID、ALIDを宣言します。
-
VID
|
|
|
Protected Enum VIDEnum
VIDECTimer = 201
VIDTimeFormat
VIDControlState
' Load port state
VIDLoadPortState1 = 301
VIDLoadPortState2
' Access mode
VIDAccessMode1 = 311
VIDAccessMode2
' Slot map
VIDSlotMap1 = 321
VIDSlotMap2
' Carrier ID
VIDCarrierID1 = 331
VIDCarrierID2
End Enum
|
-
CEID
|
|
|
Protected Enum CEIDEnum
CEIDOffline = 51
CEIDOnlineLocal
CEIDOnlineRemote
CEIDPJState = 60
CEIDCJState = 70
CEIDWaferProcessData = 80
' Carrier transfer request
CEIDCarrierTransferRequest1 = 101
CEIDCarrierTransferRequest2
' Load port transfer
CEIDLoadPortTransfer1 = 111
CEIDLoadPortTransfer2
' FOUP lock state
CEIDFoupLockState1 = 121
CEIDFoupLockState2
' Carrier transfer
CEIDCarrierTransfer1 = 131
CEIDCarrierTransfer2
' Carrier iD status
CEIDCarrierIDStatus1 = 141
CEIDCarrierIDStatus2
' Carrier location
CEIDCarrierLocation1 = 151
CEIDCarrierLocation2
' FOUP door state
CEIDFoupDoorState1 = 161
CEIDFoupDoorState2
' Carrier slot map state
CEIDCarrierSlotMapState1 = 171
CEIDCarrierSlotMapState2
' Access mode
CEIDAccessMode1 = 181
CEIDAccessMode2
End Enum
|
-
ALID
|
|
|
Protected Enum ALIDEnum
ALIDLoadWaferFailure = 1
End Enum
|
|
|
フォーム起動時の処理
-
設定値の復元
まずbopファイルの読み込みを行い、物理接続を有効にします。
Withで囲むとオブジェクト名を省略できるという便利な言語仕様がVisual Basicにはあります。
|
|
|
Private Sub MainForm_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
With gem
.LoadData()
.PhysicalConnection = True
|
-
イベントの列挙
bopファイル内で登録されているイベントを列挙します。
ユーザはリストボックスをダブルクリックすることで、任意のイベントを送信することができます。
実際の装置を作成される場合には、必ずしも必要ではありません。
|
|
|
' Enumerate all events
Dim nCnt As Integer
For nCnt = 0 To .CEIDCount - 1
Dim lCEID As Long
lCEID = .ToCEID(nCnt)
Dim nIndex As Integer
nIndex = eventlist.Items.Add(.get_CEIDDescription(lCEID))
Next
End With
|
-
レシピの列挙
レシピを列挙します。
S7F19を受信した際に、このリストを使ってレシピ一覧を作成します。
実際の装置を作成される場合は、データベースへの問い合わせを行ったり、レシピファイルの一覧を調べたりするなど、システムの実装に応じて異なります。
ただしソフトウェア内部にレシピ一覧を保存して随時更新するようにしておくと、S7F20応答でT3タイムアウトになったりするのを防げますし、S2F41やS2F49の処理も迅速になります。
|
|
|
With recipelist
' Dummy recipe list
.Items.Add("Recipe1")
.Items.Add("Recipe99")
.Items.Add("RecipeABC")
.Items.Add("RecipeXYZ")
End With
|
-
オブジェクトのツリー表示
キャリア、CJ、PJ、ウェハのオブジェクトの関係が視覚的に分かり易くなるよう、ツリー表示します。
ここでは1つのPJは複数のキャリアにまたがらないような構成としましたが、実際の装置を作成される場合は異なるかもしれませんし、必ずしもこのような表示は必要ではありません。
|
|
|
With jobtree
.Nodes.Add("Port1", gem.get_VIDValue(331))
.Nodes.Add("Port2", gem.get_VIDValue(332))
End With
End Sub
|
|
設定画面
メニューから「Tools」-「Options...」を選択することで、設定画面を表示するようにします。
ここではすべてのタブを有効にするために、2番目の引数に「-1」を指定しています。
|
SavoyGemイベント
SavoyGemは状況に応じてイベントでアプリケーションに通知してきます。
-
Receivedイベント
基本的にはDefProc()関数がやってくれますが、典型的な返答しか返しません。
またGEM300拡張部分の返信メッセージは、ユーザが指定する必要があります。
今回はS3F17(Proceed With Carrier)、S7F19(レシピ問い合わせ)、S14F9(CJ生成)、S16F11(PJ生成)の4つのメッセージについて処理することにします。
プログラムを見やすくするために、それぞれ別個の関数として分離します。
|
|
|
Private Sub gem_Received(ByVal sender As System.Object, ByVal e As AxSAVOYLib._DSavoyGemEvents_ReceivedEvent) Handles gem.Received
Debug.Print("Received(" + e.lpszIPAddress + ", " + Format$(e.lPortNumber) + ")")
With gem
If .ControlState = 4 Then
If .Stream = 3 And .Function = 17 Then
' S3F17 Proceed with carrier
ReceivedS3F17()
End If
If .Stream = 7 And .Function = 19 Then
' S7F19 Query recipe list
SendS7F20()
End If
If .Stream = 14 And .Function = 9 Then
' S14F9 CJ create
ReceivedS14F9()
End If
If .Stream = 16 And .Function = 11 Then
' S16F11 PJ create
ReceivedS16F11()
End If
End If
.DefProc()
End With
End Sub
|
-
その他のイベント
ここではデバッグウィンドゥに簡単なメッセージを表示するだけにとどめます。
|
|
|
Debug.Print("Connected(" + e.lpszIPAddress + ", " + Format$(e.lPortNumber) + ")")
|
|
メッセージ処理
-
S7F20送信
S7F19を受信したらSendS7F20()関数を呼び出します。
返信メッセージはWorkSpace=0のReply=Trueの領域にセットします。
WorkSpaceはReceivedイベント発生時に0にセットされますので、明示的に指定する必要はありません。
ルート以外のノードが指定されていると、SavoyGemは子ノードを作成してしまいますので(SavoySecsIIも同様)、Nodeプロパティをルートに変更します。
返信するSML文字列を作成します。
実際の装置では、レシピの保存形態によって実装が異なります。
ここではレシピ一覧のリストボックスからレシピ名を取ってくることにします。
|
|
|
Dim strRecipe As String = "s7f20{"
For Each item In recipelist.Items
strRecipe += "<a'" + item + "'>"
Next
strRecipe += "}"
.SML = strRecipe
|
SML文字列が完成したら、SMLプロパティに文字列をセットします。
この関数を抜けると、DefProc()メソッド内でメッセージを返信します。
-
S3F17受信
ここではサンプルということでProceedWithCarrierだけを処理していますが、実際の装置ではその他の機能を追加する必要があるでしょう。
たいていのGEM300装置ではProceedWithCarrierの他に、CancelCarrierやCancelCarrierAtPortなどの命令をサポートするのが望ましいでしょう。
CarrierActionはS3F17メッセージのNode="2"に入ってきます。
|
|
|
.Node = "2"
Dim strCarrierAction As String = .NodeValue
|
比較にToUpper()を使っているのは、大文字・小文字を区別しない比較をするためです。
|
|
|
If strCarrierAction.ToUpper() <> "ProceedWithCarrier".ToUpper() Then
' Not supported
Exit Sub
End If
|
GEM300ではキャリアIDの認証と、スロットマップの認証の2種類が必要です。
FOUPロードポートによっては、キャリアIDがアンドック状態でないと読めないものもありますので、通常はProceedWithCarrierのキャリアID認証を受信したときに、ドックおよびウェハマッピング処理を行うことが多いです。
ここではサンプルということで、ウェハマッピングが終わったと言う事にしてマッピング完了のイベントを発生させています。
送信前にスロットマップのVIDを更新しています。
VIDの更新方法としては、以下の2種類が可能です。
-
VIDRawValueプロパティを使って値を文字列で直接指定する方法
|
|
|
.set_VIDValue(VIDEnum.VIDSlotMap1, "1111100000111110000011111")
|
-
VIDValueプロパティを使ってSMLで指定する方法
|
|
|
.set_VIDRawValue(VIDEnum.VIDSlotMap1, "<a'0000011111000001111100000'>")
|
方法1では型に依存しない指定が可能です。
例えばVIDの型が以前はU2(符号なし16ビット整数)だったが、途中で仕様変更となりU4(符号なし32ビット整数)になった場合でもSMLを変更する必要がありません。
もし方法2でプログラミングしていた場合は、例えば<u2 345>ら<u4 345>に変更する必要があります。
DVIDの場合は、方法1では指定できない場合があります。
VIDの型がリスト型の場合などがそれに該当します。
この場合は方法2を使って指定します。
イベントを発生させるにはInvokeEvent()メソッドを使います。
|
|
|
.InvokeEvent(CEIDEnum.CEIDCarrierSlotMapState1)
|
-
S14F9受信
パラメータは、属性IDと属性データのペアで送られてきます。
通常は装置の通信仕様書に書かれている通りの固定フォーマットですが、厳密にはSEMIの仕様では順序を入れ替えることも可能です。
もしホスト側でこのような順序変更を行っている場合は、装置の通信仕様書とは若干異なる順序でパラメータ指定がくる可能性があります。
そこでまず属性IDと属性データのペアをすべて読み出し、いったんハッシュテーブルに保存することにします。
同様の解析処理はS2F41などでも使えます。
属性データがリスト構造の場合もありますから、すべての状況でこのテクニックが使える訳ではありませんので注意してください。
|
|
|
Dim args As New Hashtable()
.Node = "3"
Dim nCnt As Integer
Dim nNodeCount As Integer = .NodeCount
For nCnt = 1 To nNodeCount
' Attribute ID
.Node = "3/" + Format$(nCnt) + "/1"
Dim strAttributeID As String = .NodeValue
' Attribute data
.Node = "3/" + Format$(nCnt) + "/2"
Dim strAttributeData As String = .NodeValue
args.Add(strAttributeID, strAttributeData)
Next
|
CJIDをハッシュテーブルから「ControlJobID」に一致するデータを検索します。
見つからない場合(ホストから来なかった場合)には、Nothingとなります。
この値は後でチェックします。
|
|
|
Dim strCJID As String = args("ControlJobID")
|
キャリアIDはツリー構造になっている場合があります(装置がマルチキャリアCJに対応している場合)。
今回のサンプルでは直接読み出しています。
|
|
|
.Node = "3/3/2/1"
Dim strCarrierID As String = .NodeValue
|
CJIDとキャリアIDが正しいかどうかをチェックします。
値がNothingかどうかで比べています。
|
|
|
If strCJID Is Nothing Then
SendS14F10(strCJID, strCarrierID, "", "{<u4 444><a'CJID was not specified'>}")
Exit Sub
End If
|
|
|
|
If strCarrierID Is Nothing Then
SendS14F10(strCJID, strCarrierID, "", "{<u4 555><a'Carrier ID was not specified'>}")
Exit Sub
End If
|
CJIDが既に使われているかどうかをチェックします。
ここではリストに登録済みかどうかを調べていますが、実際の装置では実装が異なるかもしれません。
|
|
|
For nCnt = 0 To joblist.Items.Count - 1
If strCJID = joblist.Items(nCnt).SubItems(2).Text Then
' Error : Registered
SendS14F10(strCJID, strCarrierID, "", "{<u4 666><a'CJID is in use'>}")
Exit Sub
End If
Next
|
ポート番号を調べます。
もしキャリアIDが一致しない場合はエラーとしています。
ただしマルチポートCJに対応した装置の場合には、このサンプルとは実装が異なりますので注意が必要です。
|
|
|
Dim nPort As Integer
If strCarrierID = .get_VIDValue(VIDEnum.VIDCarrierID1) Then
' Port 1
nPort = 1
Else
If strCarrierID = .get_VIDValue(VIDEnum.VIDCarrierID2) Then
' Port 2
nPort = 2
Else
' Error : Carrier ID doesn't match
SendS14F10(strCJID, strCarrierID, "", "{& | | |