This CSharp example uses the code generated from TransSECS using the .NET deployment option.
1. Start TransSECS and load the GEMTool Sample Project. Generate the code with the .net option selected for deployment.
2. The .net assembly (GEMTool.dll) which is needed for the CSharp example is in the project (GEMTool) directory under DOTNET. Some additional assemblies are required - see below.
To prepare for testing this example, please load the SECSTester provided with TransSECS or load GEMHost Sample Project and generate the code so that TransSECS Builder in run/test mode can be used as a test Host.
One-time .NET Project Setup
When you first create your project you will need to modify your .csproj to include the TransSECS generated jar. You need to modify the ItemGroup to include the generated .jar file Reference and AssemblyName.
The names should follow your TransSECS project name, so if you're using the GEMTool sample project included with TransSECS and you have copied that jar in the lib folder of your project then you may have a lines like this:
<IkvmReference Include=“lib\GEMToolDotNetRuntime.jar”> <AssemblyName>GEMToolDotNetRuntime</AssemblyName>
If your project was “MySpecialToolProject” and you've again copied the jar to the lib then you'd have this:
<IkvmReference Include=“lib\MySpecialToolProject.jar”> <AssemblyName>MySpecialToolProject</AssemblyName>
Here's the detailed steps using the GEMTool sample project as an example:
* Open a command prompt or PowerShell window.
* Navigate to your project directory.
* Run the following commands:
dotnet add package IKVM dotnet add package IKVM.Image.JRE
* Open your project's .csproj file in a text editor; you should see the following lines (your IKVM version can be different):
<ItemGroup> <PackageReference Include="IKVM" Version="8.4.5" /> <PackageReference Include="IKVM.Image.JRE" Version="8.4.5" /> </ItemGroup>
* Add the following lines to your .csproj file:
<ItemGroup> <IkvmReference Include="lib\GEMToolDotNetRuntime.jar"> <AssemblyName>GEMToolDotNetRuntime</AssemblyName> <AssemblyVersion>1.0.0.0</AssemblyVersion> <AssemblyFileVersion>1.0.0.0</AssemblyFileVersion> </IkvmReference> </ItemGroup>
* Remember to replace “lib\GEMToolDotNetRuntime.jar” with the path to the generated jar file. You can also change the assembly information as needed.
* Save and close the .csproj file.
In the sample code below the tool is initialized, a “notifier” is set up to capture and handle a message from the host (the host command), and then it starts a timed loop to change some vid values while it waits for incoming host messages.
using System; using Deploy.GEMTool; using Ergotech; using Ergotech.Util; using Ergotech.Secs; using Ergotech.Secs.Gem; using Ergotech.Transsecs.Secs; using Ergotech.Vib.Exceptions; using Ergotech.Vib.Valueobjects; using Ergotech.Vib.Utils; using System.Collections; using System.Threading; namespace Example { class GEMToolExample { //you must reference your TransSECS generated dll assembly for EquipmentController public EquipmentController EquipmentController { get; set; } //gets and sets GEM variables private GemHandler gemHandler; //whether or not the temperature alarm has been set private bool temperatureAlarm = false; static void Main(string[] args) { GEMToolExample program = new GEMToolExample(); program.StartExample(); } public void StartExample() { Ergotech.Util.SimulationManager.GetSimulationManager().SetSimulating(false); //set up parameters for the connection int portNumber = 5010; // port number int deviceID = 1; // device id //GemHandler.debugLevel = 1000; //TransSecsController.debugLevel = 1000; EquipmentController = new EquipmentController(); EquipmentController.SetPort(portNumber); EquipmentController.SetDeviceId(deviceID); try { Console.WriteLine("initing tool"); EquipmentController.Init(); Console.WriteLine("registering tool"); EquipmentController.Register(); gemHandler = (GemHandler)EquipmentController.GetGemHandler(); Console.WriteLine("starting tool"); EquipmentController.Start(); Console.WriteLine("done starting tool"); } catch (BadParameterException e1) { e1.printStackTrace(); } catch (VIBUpdateFailedException e1) { e1.printStackTrace(); } //equipmentController.SetPersistenceFileName("/tmp/testpersistence"); LongValueObject online = new LongValueObject(1); //online if set to 1 (offline when 0) LongValueObject remote = new LongValueObject(1); //remote if set to 1 (local when 0) // here we set the values of the VIDs OnlineOfflineState and LocalRemoteState // to some initial values (usually read from the tool hardware switches) // The GEM standard requires that you provide a mechanism at the tool to manipulate the control state // changing the OnlineOfflineState enables and disables SECS communication // in offline state all comunication is disabled. Online, the host can communicate with the tool. gemHandler.SetValue("OnlineOfflineState", online);//Go Online (still local) Console.WriteLine("ControlState is " + gemHandler.GetValue(33003)); // Changing the LocalRemoteState to remote allows full host control. Set to local the host can monitor but // not send commands to the tool. In Local state the host should not be able to initiate anything that might // injure the operator or a technician. In remote state the operator is usually prohibited from selecting // recipes and starting the tool - this must be performed through automation. gemHandler.SetValue("LocalRemoteState", remote);//Allow Local-Remote transition //gemHandler.GetOnlineHandler().SetLocal(false); // alternate option for setting online/offline and local/remote //gemHandler.GetOnlineHandler().SetOnline(true); // The ControlState should be "3" at this point (host offline). The host will transition it to online/remote (5). Console.WriteLine("ControlState is " + gemHandler.GetValue(33003)); // spooling is rarely used. It allows messages to be cached by the tool if there is a loss in communication //gemHandler.setValue("EnableSpooling", new LongValueObject(1));//Enable Spooling gemHandler.SetValue("EnableSpooling", new LongValueObject(0));//Disable Spooling //NOTE: if you know the VID you can use the equipmentController to set values to the VIDs //without creating ValueObjects first but you do need to create a SecsFormat with the value //For example, to set LocalRemoteState, which is VID 33005 and U4 (type 54 SECS format), do this: // gemHandler.setValue(33005,new SecsFormat54(1)); //E005 allows 20 character MDLN/SOFTREV SOFTREV.maxLength = 20; //allows 20 chars for both -- be sure your host accepts 20 characters (or only the default 6) //You can also change other GEM parameters to override the defaults set in the tool project gemHandler.SetValue("MDLN", new StringValueObject("Model ABCDEFG")); //MDLN, limited to 6 chars by SEMI definition for backwards compatibility unless -d parameter used on command line gemHandler.SetValue("SoftRev", new StringValueObject("Rev1.0"));//SoftRev // We want to know when the host sends a Host Command. // So we set up a Message Notifier for this unsolicited message. // Once this notifier is set up the code in the class "HostCommandNotifier" //will be called when the Host Command is received // create a notifier for the simple HostCommand message in the tool bool success = EquipmentController.RegisterForReceiveNotification("HostCommandSTART", new HostCommandNotifier(this)); //This next part runs the tool and sets random event and alarms once in awhile long waferCount = 1; long cycles = 0; while (true) { //set some SVID values gemHandler.SetValue("ProcessTemperature", new DoubleValueObject(randomTemperatureValue())); gemHandler.SetValue("GasFlow", new DoubleValueObject(randomGasFlowValue())); Thread.Sleep(500); // 500 milliseconds cycles++; // increment the wafer count every 10 cycles if ((cycles % 10) == 0) { waferCount++; } gemHandler.SetValue("WaferCount", new LongValueObject(waferCount)); //test event triggering every few wafer counts (events will not be triggered unless host has enabled them) GemCEID @event = null; try { @event = (GemCEID)gemHandler.GetServerForName("CEID.Completed"); } catch (BadParameterException) { Console.WriteLine("Problem accessing server for CEID \"Completed\""); } if ((cycles % 10) == 0) { //every 5 wafer counts (every 50 cycles) try { @event.SetBoolValue(true); } catch (VIBUpdateFailedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } else { try { @event.SetBoolValue(false); } catch (VIBUpdateFailedException e) { e.printStackTrace(); } } //test alarms (alarms will not be triggered unless host has enabled them) checkAlarms(); } } // this simulates actual values read from a process PLC or other server private double randomTemperatureValue() { //generate a random float between 50 and 60 (sometimes the value will be >60, this will trigger our test alarm) Random r = new Random(); double testValue = 50.0 + r.NextDouble() * 10.1; return testValue; } // this simulates actual values read from a process PLC or other server private double randomGasFlowValue() { //generate a random float between 8 and 9 Random r = new Random(); double testValue = 8.0 + r.NextDouble(); return testValue; } /**Normally alarm triggers are connected to real PLC values. But for this demo we will * set an alarm if the temperature is >60 (and unset it when it is below 60). */ private void checkAlarms() { float temperature = 0; GemALID alarm = null; try { temperature = gemHandler.GetServerForName("VID.ProcessTemperature").GetFloatValue(); } catch (BadParameterException) { Console.WriteLine("Problem accessing DVVAL \"Process Temperature\""); System.Environment.Exit(-1); } try { alarm = (GemALID)gemHandler.GetServerFor(GemEquipmentInterface.ALID, 5502); } catch (GemException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } catch (BadParameterException) { Console.WriteLine("Problem accessing ALID \"Temperature Problem\""); System.Environment.Exit(-1); } if (temperature > 60.0) { Console.WriteLine("Temperature is " + temperature); Console.WriteLine("ControlState is " + gemHandler.GetValue(33003)); //set the Alarm. This has the same effect as triggering with a boolean true if you were using a real data source. try { if (!(temperatureAlarm)) { alarm.Set(); temperatureAlarm = true; } } catch (SecsException e) { // TODO Auto-generated catch block e.printStackTrace(); } } else { //clear the alarm. This has the same effect as triggering with a boolean false if you were using a real data source. try { if (temperatureAlarm) { temperatureAlarm = false; alarm.Clear(); } } catch (SecsException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
Required libraries are copied to your DOTNET project directory under the “bin” folder. You will need these libraries to build the csharp code.
A list of the most likely required libraries are:
IKVM.Runtime.dll,IKVM.Reflection.dll,IKVM.OpenJDK.Beans.dll, IKVM.OpenJDK.SwingAWT.dll,IKVM.OpenJDK.XML.Bind.dll, IKVM.OpenJDK.XML.API.dll,IKVM.OpenJDK.XML.Parse.dll, IKVM.OpenJDK.Security.dll,IKVM.Runtime.JNI.dll, IKVM.OpenJDK.Core.dll,IKVM.OpenJDK.Util.dll, IKVM.OpenJDK.Naming.dll,IKVM.OpenJDK.Misc.dll, IKVM.OpenJDK.Text.dll, and IKVM.Runtime.dll