Jazz Soft お問い合わせ   JazzSoft@live.com     日本語   英語  日本語  中国語(繁体字)  中国語(簡体字)
工場自動化と歩留り管理ソリューション 
Skip Navigation Linksホーム > 製品一覧 > Savoy > サンプル > GEM 300 (Visual Basic 2008)
Savoy ActiveXコントロール   




パンフレット

日本語

Savoy サンプル - GEM 300 (Visual Basic 2008)

このサンプルではGEM 300での実装を説明します。




定数の宣言

プログラム中で使う定数をEnumで宣言します。今回はVID、CEID、ALIDを宣言します。
  1. VID

    Visual Basic

    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



  2. CEID

    Visual Basic

    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



  3. ALID

    Visual Basic

    Protected Enum ALIDEnum
      ALIDLoadWaferFailure = 1
    End Enum

フォーム起動時の処理

  1. 設定値の復元
    まずbopファイルの読み込みを行い、物理接続を有効にします。 Withで囲むとオブジェクト名を省略できるという便利な言語仕様がVisual Basicにはあります。

    Visual Basic

    Private Sub MainForm_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
      With gem
        .LoadData()
        .PhysicalConnection = True



  2. イベントの列挙
    bopファイル内で登録されているイベントを列挙します。 ユーザはリストボックスをダブルクリックすることで、任意のイベントを送信することができます。 実際の装置を作成される場合には、必ずしも必要ではありません。

    Visual Basic

        ' 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



  3. レシピの列挙
    レシピを列挙します。 S7F19を受信した際に、このリストを使ってレシピ一覧を作成します。 実際の装置を作成される場合は、データベースへの問い合わせを行ったり、レシピファイルの一覧を調べたりするなど、システムの実装に応じて異なります。 ただしソフトウェア内部にレシピ一覧を保存して随時更新するようにしておくと、S7F20応答でT3タイムアウトになったりするのを防げますし、S2F41やS2F49の処理も迅速になります。

    Visual Basic

      With recipelist
        ' Dummy recipe list
        .Items.Add("Recipe1")
        .Items.Add("Recipe99")
        .Items.Add("RecipeABC")
        .Items.Add("RecipeXYZ")
      End With


  4. オブジェクトのツリー表示
    キャリア、CJ、PJ、ウェハのオブジェクトの関係が視覚的に分かり易くなるよう、ツリー表示します。 ここでは1つのPJは複数のキャリアにまたがらないような構成としましたが、実際の装置を作成される場合は異なるかもしれませんし、必ずしもこのような表示は必要ではありません。

    Visual Basic

        With jobtree
          .Nodes.Add("Port1", gem.get_VIDValue(331))
          .Nodes.Add("Port2", gem.get_VIDValue(332))
        End With
      End Sub



設定画面

メニューから「Tools」-「Options...」を選択することで、設定画面を表示するようにします。 ここではすべてのタブを有効にするために、2番目の引数に「-1」を指定しています。

Visual Basic

  gem.Setup("", -1)



SavoyGemイベント

SavoyGemは状況に応じてイベントでアプリケーションに通知してきます。
  1. Receivedイベント
    基本的にはDefProc()関数がやってくれますが、典型的な返答しか返しません。 またGEM300拡張部分の返信メッセージは、ユーザが指定する必要があります。

    今回はS3F17(Proceed With Carrier)、S7F19(レシピ問い合わせ)、S14F9(CJ生成)、S16F11(PJ生成)の4つのメッセージについて処理することにします。 プログラムを見やすくするために、それぞれ別個の関数として分離します。

    Visual Basic

    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



  2. その他のイベント
    ここではデバッグウィンドゥに簡単なメッセージを表示するだけにとどめます。

    Visual Basic

      Debug.Print("Connected(" + e.lpszIPAddress + ", " + Format$(e.lPortNumber) + ")")




メッセージ処理

  1. S7F20送信
    S7F19を受信したらSendS7F20()関数を呼び出します。 返信メッセージはWorkSpace=0のReply=Trueの領域にセットします。 WorkSpaceはReceivedイベント発生時に0にセットされますので、明示的に指定する必要はありません。

    Visual Basic

      .Reply = True


    ルート以外のノードが指定されていると、SavoyGemは子ノードを作成してしまいますので(SavoySecsIIも同様)、Nodeプロパティをルートに変更します。

    Visual Basic

      .Node = ""


    返信するSML文字列を作成します。 実際の装置では、レシピの保存形態によって実装が異なります。 ここではレシピ一覧のリストボックスからレシピ名を取ってくることにします。

    Visual Basic

      Dim strRecipe As String = "s7f20{"

      For Each item In recipelist.Items
        strRecipe += "<a'" + item + "'>"
      Next

      strRecipe += "}"
      .SML = strRecipe


    SML文字列が完成したら、SMLプロパティに文字列をセットします。 この関数を抜けると、DefProc()メソッド内でメッセージを返信します。


  2. S3F17受信
    ここではサンプルということでProceedWithCarrierだけを処理していますが、実際の装置ではその他の機能を追加する必要があるでしょう。 たいていのGEM300装置ではProceedWithCarrierの他に、CancelCarrierやCancelCarrierAtPortなどの命令をサポートするのが望ましいでしょう。 CarrierActionはS3F17メッセージのNode="2"に入ってきます。

    Visual Basic

      .Node = "2"
      Dim strCarrierAction As String = .NodeValue


    比較にToUpper()を使っているのは、大文字・小文字を区別しない比較をするためです。

    Visual Basic

      If strCarrierAction.ToUpper() <> "ProceedWithCarrier".ToUpper() Then
        ' Not supported
        Exit Sub
      End If


    GEM300ではキャリアIDの認証と、スロットマップの認証の2種類が必要です。 FOUPロードポートによっては、キャリアIDがアンドック状態でないと読めないものもありますので、通常はProceedWithCarrierのキャリアID認証を受信したときに、ドックおよびウェハマッピング処理を行うことが多いです。

    ここではサンプルということで、ウェハマッピングが終わったと言う事にしてマッピング完了のイベントを発生させています。 送信前にスロットマップのVIDを更新しています。 VIDの更新方法としては、以下の2種類が可能です。

    1. VIDRawValueプロパティを使って値を文字列で直接指定する方法
      Visual Basic

        .set_VIDValue(VIDEnum.VIDSlotMap1, "1111100000111110000011111")



    2. VIDValueプロパティを使ってSMLで指定する方法
      Visual Basic

        .set_VIDRawValue(VIDEnum.VIDSlotMap1, "<a'0000011111000001111100000'>")



    方法1では型に依存しない指定が可能です。 例えばVIDの型が以前はU2(符号なし16ビット整数)だったが、途中で仕様変更となりU4(符号なし32ビット整数)になった場合でもSMLを変更する必要がありません。 もし方法2でプログラミングしていた場合は、例えば<u2 345>ら<u4 345>に変更する必要があります。

    DVIDの場合は、方法1では指定できない場合があります。 VIDの型がリスト型の場合などがそれに該当します。 この場合は方法2を使って指定します。

    イベントを発生させるにはInvokeEvent()メソッドを使います。

    Visual Basic

      .InvokeEvent(CEIDEnum.CEIDCarrierSlotMapState1)



  3. S14F9受信
    パラメータは、属性IDと属性データのペアで送られてきます。 通常は装置の通信仕様書に書かれている通りの固定フォーマットですが、厳密にはSEMIの仕様では順序を入れ替えることも可能です。 もしホスト側でこのような順序変更を行っている場合は、装置の通信仕様書とは若干異なる順序でパラメータ指定がくる可能性があります。

    そこでまず属性IDと属性データのペアをすべて読み出し、いったんハッシュテーブルに保存することにします。 同様の解析処理はS2F41などでも使えます。 属性データがリスト構造の場合もありますから、すべての状況でこのテクニックが使える訳ではありませんので注意してください。

    Visual Basic

      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となります。 この値は後でチェックします。

    Visual Basic

      Dim strCJID As String = args("ControlJobID")


    キャリアIDはツリー構造になっている場合があります(装置がマルチキャリアCJに対応している場合)。 今回のサンプルでは直接読み出しています。

    Visual Basic

      .Node = "3/3/2/1"
      Dim strCarrierID As String = .NodeValue


    CJIDとキャリアIDが正しいかどうかをチェックします。 値がNothingかどうかで比べています。

    Visual Basic

      If strCJID Is Nothing Then
        SendS14F10(strCJID, strCarrierID, "", "{<u4 444><a'CJID was not specified'>}")
        Exit Sub
      End If


    Visual Basic

      If strCarrierID Is Nothing Then
        SendS14F10(strCJID, strCarrierID, "", "{<u4 555><a'Carrier ID was not specified'>}")
        Exit Sub
      End If


    CJIDが既に使われているかどうかをチェックします。 ここではリストに登録済みかどうかを調べていますが、実際の装置では実装が異なるかもしれません。

    Visual Basic

      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に対応した装置の場合には、このサンプルとは実装が異なりますので注意が必要です。

    Visual Basic

      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, "", "{<u4 777><a'Carrier ID doesn' 0x27 't match'>}")
        Exit Sub
      End If
      End If


    CJに関連付けられたPJを移動します。 具体的にはCJの下にPJが配置されるようにツリーのノードを入れ替えます。

    Visual Basic

      .Node = "3/2/2"
      Dim strSlot As String = ""


    ツリーにはPort1またはPort2というキー名が関連付けられていますので、キャリアIDで文字列比較する必要はありません。

    Visual Basic

      Dim tree As TreeNode = jobtree.Nodes("Port" + Format$(nPort))


    ツリーノードの参照をジェネリックのリストクラスに登録していきます。 ジェネリックはC++のテンプレートと同じです。

    Visual Basic

      Dim listPJID As New List(Of TreeNode)
      nNodeCount = .NodeCount
      For nCnt = 0 To nNodeCount - 1


    CJに関連付けられたPJIDのノードを、CJの下に作成していきます。

    Visual Basic

      .Node = "3/2/2/" + Format$(nCnt + 1) + "/1"


    PJがツリーに登録されていない場合は、エラーとします。

    Visual Basic

      Dim treeFind As TreeNode = FindTreeItem(.NodeValue, tree)
      If treeFind Is Nothing Then
        SendS14F10(strCJID, strCarrierID, "", "{<u4 888><a'PJID doesn' 0x27 't match'>}")
        Exit Sub
      End If


    リストに登録します。

    Visual Basic

      listPJID.Add(treeFind)


    PJノードを移動します。 実際には移動ではなく、クローンを作成してオリジナルを消去します。 ツリーは子ノードを展開します。

    Visual Basic

      Dim treeCJ As TreeNode = tree.Nodes.Add(strCJID)
      For nCnt = 0 To listPJID.Count - 1


    ここでクローンを作成しています。

    Visual Basic

        Dim treePJ As TreeNode = listPJID(nCnt).Clone()
        treeCJ.Nodes.Add(treePJ)
        treePJ.Expand()


    同時にリストの3番目のカラム(CJID)を更新します。

    Visual Basic

        Dim lvi As ListViewItem = FindListItem(listPJID(nCnt).Text, 1, joblist)
        If lvi Is Nothing Then
        Else
          lvi.SubItems(2).Text = strCJID
        End If


    ツリーからオリジナルのPJノードを消去します。

    Visual Basic

        tree.Nodes.RemoveByKey(listPJID(nCnt).Text)
      Next
      treeCJ.Expand()


    正常完了した旨を返信します。 実際の装置では、生成されたオブジェクトの属性情報を返す必要があります。 オブジェクトをどう生成するかは、ソフトの実装によって異なります。

    Visual Basic

      SendS14F10(strCJID, strCarrierID, "<a'TODO : Set attributes'>", "")



  4. S14F10送信
    エラーのSMLノードが空の場合は、「エラーがない」という意味になります。 このため文字列が空かどうかをチェックします。

    Visual Basic

      Dim bAck As Boolean
      If strError = "" Then
        bAck = True
      Else
        bAck = False
      End If


    文字列をセットします。Boolean型をToString()すると「True」または「False」という文字列になりますので、このままSMLで利用することができます。

    Visual Basic

      .SML = "s16f12" & _
        "{" & _
        " <a'" & strCJID & "'>" & _
        " {" & _
        strAttribute & _
        " }" & _
        " {" & _
        " <bool " & bAck.ToString() & ">" & _
        " {" & _
        strError & _
        " }" & _
        " }" & _
        "}"



  5. S16F11受信
    メッセージからPJIDを取得します。

    Visual Basic

      .Node = "2"
      Dim strPJID As String = .NodeValue


    キャリアIDを取得します。

    Visual Basic

      .Node = "4/1/1"
      Dim strCarrierID As String = .NodeValue


    レシピ名を取得します。

    Visual Basic

      .Node = "5/2"
      Dim strRecipe As String = .NodeValue


    レシピ名が登録されているかを調べます。 実際の装置では、これがデータベースや、ファイルシステムとなる可能性があります。

    Visual Basic

      If Not IsValidRecipe(strRecipe) Then
        SendS16F12(strPJID, strCarrierID, "{<u4 111><a'Invalid recipe name'>}")
        Exit Sub
      End If


    PJIDが既に使用されていないかをチェックします。

    Visual Basic

      Dim nCnt As Integer
      For nCnt = 0 To joblist.Items.Count - 1
        If strPJID = joblist.Items(nCnt).SubItems(1).Text Then
          ' Error : Registered
          SendS16F12(strPJID, strCarrierID, "{<u4 222><a'PJID is in use'>}")
          Exit Sub
        End If
      Next


    スロット番号を取得します。 このサンプルでは省略しましたが、実際の装置では数値が1~25の範囲内にあるかどうかのチェックも必要です。 なおキャリアのスロット数は製品によって変わることがあるため、スロット番号を保持する変数には余裕を持たせておくほうがいいかもしれません。

    Visual Basic

      .Node = "4/1/2"
      Dim strSlot As String = ""
      Dim tree As TreeNode = jobtree.Nodes("Port" + Format$(nPort)).Nodes.Add(strPJID, strPJID)
      Dim nNodeCount As Integer = .NodeCount
      For nCnt = 0 To nNodeCount - 1
        .Node = "4/1/2/" + Format$(nCnt + 1)

        tree.Nodes.Add(.NodeValue, .NodeValue)

        If strSlot <> "" Then
          strSlot += ", "
        End If

        strSlot += .NodeValue
      Next
      tree.Expand()
      jobtree.Nodes("Port" + Format$(nPort)).Expand()


    受信したPJの情報をリストに登録します。 実際の装置では画面オブジェクトではなく、内部変数に登録するのがいいでしょう。

    Visual Basic

      Dim lvi As ListViewItem = joblist.Items.Add(strCarrierID)
      lvi.SubItems.Add(strPJID)
      lvi.SubItems.Add("")
      lvi.SubItems.Add(strRecipe)
      lvi.SubItems.Add(strSlot)


    PJが生成されたことをイベントで通知します。 実際の装置では関連するVID変数を更新する必要があります。

    Visual Basic

      SendS16F12(strPJID, strCarrierID, "")



  6. S16F12送信
    やはり追加情報がない場合は正常ということから、文字列が空かどうかをチェックして返信メッセージを作ります。

    Visual Basic

      .SML = "s16f12" & _
        "{" & _
        " " & _
        " {" & _
        " " & _
        " {" & _
        strInfo & _
        " }" & _
        " }" & _
        "}"



検索処理

  1. ツリー検索
    ツリーの指定されたノード以下から、対象となる文字列のノードを検索します。 ここではFor文を使用していますが、For Each文の方が若干処理が早くなるようです。

    Visual Basic

      Protected Function FindTreeItem(ByVal strFind As String, ByRef tree As TreeNode) As TreeNode
        FindTreeItem = Nothing
        Dim nCnt As Integer
        For nCnt = 0 To tree.Nodes.Count - 1
          If strFind = tree.Nodes(nCnt).Text Then
            FindTreeItem = tree.Nodes(nCnt)
            Exit Function
          End If
        Next
      End Function



  2. リスト検索
    リストの指定されたカラムに、その文字列があるか検索します。 このサンプルでは検索対象となるリストは1つだけなので、直接アクセスすれば引数3は不要です。

    Visual Basic

      Protected Function FindListItem(ByVal strFind As String, ByVal nColumn As Integer, ByRef list As ListView) As ListViewItem
        FindListItem = Nothing
        Dim nCnt As Integer
        For nCnt = 0 To list.Items.Count - 1
          If strFind = list.Items(nCnt).SubItems(nColumn).Text Then
            FindListItem = list.Items(nCnt)
            Exit Function
          End If
        Next
      End Function



  3. レシピ確認
    指定されたレシピが一覧に登録されているかチェックします。

    Visual Basic

      Protected Function IsValidRecipe(ByVal strRecipe) As Boolean
        IsValidRecipe = False
        Dim nCnt As Integer
        For nCnt = 0 To recipelist.Items.Count - 1
          If strRecipe = recipelist.Items(nCnt) Then
            IsValidRecipe = True
            Exit Function
          End If
        Next
      End Function



Eventタブ

イベント一覧をダブルクリックすると、そのイベントをS6F11メッセージとして送信します。 ただしイベントが無効の場合は送信されません。

まずリストボックスで現在選択されている項目をチェックします。 何も選択されていないときは抜けます。

Visual Basic

  If eventlist.SelectedIndex < 0 Then
    Exit Sub
  End If


イベント一覧はCEIDのインデックスと同じ順序で並んでいますので、これをToCEID()メソッドを使ってCEID番号に変換します。 CEID番号を得たらInvokeEvent()メソッドでイベントを送信します。

レポートがリンクされている場合には、SavoyGemによってレポートが自動的に作られます。 このためInvokeEvent()メソッドを呼び出す直前に、関連するVID変数を更新する必要があります。 実際の装置を作成される場合には、膨大な数のVIDを更新する必要があるかもしれません。 さらにソフトの至る所でInvokeEvent()メソッドを呼び出すことになると思います。 その場合はVIDの更新処理を別関数にするのもいい方法です。

Visual Basic

  Dim lCEID As Long = .ToCEID(eventlist.SelectedIndex)
  .InvokeEvent(lCEID)



データファイル

当初ソリューション(SavoySampleGem300VB.sln)には、データソースファイル(SavoySampleGem300VB.bopsource)をBopCompilerでコンパイルするための、カスタムビルドプロジェクト(BopDataFile.vcproj)が含まれていました。 このカスタムビルドプロジェクトはVisual C++プロジェクトをベースにしていたため、Visual Basic 2008 Express Editionではこのプロジェクトを開くことができませんでした。 この対策としてデータソースファイルのコンパイルをバッチファイル(MakeDataFile.bat)で行うように変更しました。

ソリューションのコンパイルが完了したら、binフォルダ下のDebugまたはReleaseフォルダ(ターゲットフォルダ)に実行ファイルが生成されます。 ビルドの際にバッチファイルとデータソースファイルは、ターゲットフォルダにコピーされるように設定されています。 SavoySampleGem300VB.exeを実行する前に、バッチファイルを実行するとBopCompilerが起動し、データファイル(SavoySampleGem300VB.bop)が生成されます。

ただしバッチファイルを実行するには、BopCompilerがインストールされているフォルダが環境変数Pathに含まれている必要があります。 ここでは環境変数への追加手順を説明します。
  1. 「Computer」(Windows XPでは「My Computer」)のアイコンを右クリックします。 ポップアップメニューから「Properties」をクリックします。




  2. 左のTasksペインに「Advanced system settings」という項目がありますので、これをクリックします。




  3. 「Advanced」タブの一番下に「Environment Variables」というボタンがありますので、これをクリックします。




  4. 環境変数のダイアログボックスが開きますので、下段の「System variables」の一覧から「Path」を選択し、「Edit」ボタンをクリックします。




  5. 「Variable value」の一番最後にセミコロン「;」を入力し、「C:\Program Files\Jazz Soft\Bop」フォルダを追加します。 入力が完了したらOKボタンをクリックします。



以上の手順で事前準備をしておくと、Explorerからバッチファイルをダブルクリックるだけで、データファイルが生成できるようになります。
お問い合わせ   JazzSoft@live.com
  Copyright © 1997 - 2018 Jazz Soft, Inc.