cpp

This GEM C++ example uses the code generated from TransSECS using the Native/C++ deployment option.

In the tool you update the variables with simple set/get methods take the name of the variable and the value. In the host, you subscribe to receive data. Complex types are passed as JSON.

Message notifications are received as JSON maps so that each of the published elements in the message are available directly.

Download the sample from: https://www.ergotech.com/docs/cppwrapper.tar.gz Extract the cppwrapper folder and change to that folder. On Linux:

mkdir build
cd build
cmake ..
make 

Copy the GEMToolRuntime.jar from the TransSECS GEMTool project into the build folder. Then run:

./transecswrapper_example
 
 
Started tool GEMTool on port 5010 device id 1
Publish Item id: "gemtool/vids/ppid"
Subscribe Item id: "gemtool/vids/ppid"
Publish Item id: "gemtool/vids/s5f1replybit"
Subscribe Item id: "gemtool/vids/s5f1replybit"
Subscribe Item id: "gemtool/configuration/port"
Subscribe Item id: "gemtool/configuration/deviceid"
Subscribe Item id: "gemtool/configuration/passivet1"
Subscribe Item id: "gemtool/configuration/passivet2"
Subscribe Item id: "gemtool/configuration/passivet3"
Subscribe Item id: "gemtool/configuration/passivet4"
Subscribe Item id: "gemtool/configuration/passivet5"
Subscribe Item id: "gemtool/configuration/passivet6"
Subscribe Item id: "gemtool/configuration/passivet7"
[...]
Subscribe Item id: "gemtool/vids/processtemperature"
Control state changed 3
Getting: gemtool/vids/controlstate
Control State 3
Getting: gemtool/vids/controlstate
Control State 3
New ppid InitialPPID

The tool is then running and ready for connections. You can use the TransSECS GEMHost sample project to test the tool.

For the host,copy the GEMHostRuntime.jar from the TransSECS GEMHost project into the build folder

The samples, even though simple, demonstrates all the features required of a tool and the ability to collect data from a host.

In the host code, publish variables you need to change in the interface, here the port, hostname, deviceid, etc. and subscribe to elements to receive data updates. Here an event (loaded) and to values (ppid and wafer count). The event is received as a JSON structure.

#include <stdio.h>
#include <execinfo.h>
#include <signal.h>
#include "transsecswrapper.h"
#include <unistd.h>
#include "valueobject.h"
#include <chrono>
#include <thread>
 
transsecswrapper::TransSecsWrapper gem_native_wrapper  = transsecswrapper::TransSecsWrapper("./GEMHostRuntime.jar");
 
int main(int argc, char **argv) {   
    //signal(SIGSEGV, handler);   // install our handler 
 
    int port = 5010;
 
    // persistance file saves GEM configuration between restarts (reports, enabled CEIDs, enabled Alarms, etc).
    gem_native_wrapper.Publish("gemhost/configuration/persistencefilename", "/tmp/testpersistence");
    gem_native_wrapper.Publish("gemhost/configuration/equipmenthostname", "localhost");
    gem_native_wrapper.Publish("gemhost/configuration/deviceid", 1);
    gem_native_wrapper.Publish("gemhost/configuration/activeport", port);
    gem_native_wrapper.StartMain("GEMHost");
    gem_native_wrapper.Subscribe("gemhost/variables/ceid/loaded",[](std::string topic, transsecswrapper::ValueObject value_object) {
        printf("New ceid (%s) %s\n\r", topic.c_str(), value_object.GetStringValue().c_str());
    });
 
    gem_native_wrapper.Subscribe("gemhost/variables/vid/ppid", [](std::string topic, transsecswrapper::ValueObject value_object) {
        printf("New ppid (%s) %s\n\r", topic.c_str(), value_object.GetStringValue().c_str());
    });
 
    gem_native_wrapper.Subscribe("gemhost/variables/vid/wafercount", [](std::string topic, transsecswrapper::ValueObject value_object) {
        printf("New wafercount (%s) %s\n\r", topic.c_str(), value_object.GetStringValue().c_str());
    });
}

The tool must keep the SECS interface informed of the current values of all the VIDs. This just a matter of call “Publish” with the name and value of the variable.

            gem_native_wrapper.Publish("gemtool/vids/WaferCount", waferCount);

Similarly, alarms are triggered by name:

             gem_native_wrapper.Alarm("TemperatureProblem", temperatureAlarm);

And Events (CEIDs) are triggered with a JSON String that contains the values of any required DVIDs. Here the “WaferCount” and “ADVID”:

      std::string ceid_trigger = std::string("{ \"values\":["
            "    { \"dvid\":\"WaferCount\", \"dvval\":\""+std::to_string(waferCount)+"\"  },"
            "    { \"dvid\":\"ADVID\",  \"dvval\":\"-2\"  }"
            " ] }");
            // the ceid triggers are in JSON format allowing DVIDs to be set.
            gem_native_wrapper.Trigger("Loaded", ceid_trigger.c_str()); 

The tool can subscribe to be notified of the change in value of any VID - used for ECIDs and GEM internal variables:

gem_native_wrapper.Subscribe("gemtool/vids/controlstate", [](std::string, transsecswrapper::ValueObject value_object) {
    printf("Control state changed %s\n\r", value_object.GetStringValue().c_str());
});

And can subscribe to receive notification of received messages:

gem_native_wrapper.Subscribe("gemtool/hostcommandstart",[](std::string topic, transsecswrapper::ValueObject value_object) {

The message is delivered as a JSON Map containing all the parameters that were marked as “Publish” in TransSECS making it very easy to extract particular fields - there's no parsing of complex SECS structures.

Host Command {
"AllParams": { "values": [  { "value":"START", "type":20 }, { "values": [  { "values": [ "PPID","REC5" ] , "type":20 } ]  } ]  },
"Command": { "value":"START", "type":20 },
"Parm1List_0": { "values": [ "PPID","REC5" ] , "type":20 },
"CPValue": { "value":"REC5", "type":20 }
}

TransSECS will have already examined the message for validity and, in the case of Remote Commands (S2F41 and S2F49 messages) will have rejected messages that are missing parameters. The tool should test parameters for validity (for example, is the PPID on the tool and available to be run) and return an appropriate response - by just triggering the “sendmessage” tag.

Here's the code sample from the cppwrapper archive:

#include <stdio.h>
#include "transsecstoolwrapper.h"
#include "valueobject.h"
#include <chrono>
#include <thread>
 
transsecswrapper::TransSecsToolWrapper gem_native_wrapper  = transsecswrapper::TransSecsToolWrapper("GEMToolRuntime.jar");
bool temperatureAlarm = false;
 
// this simulates actual values read from a process PLC or other server
double RandomTemperatureValue() {
    //generate a random double between 50 and 62 (sometimes the value will be >60, this will trigger our test alarm)
    double r = static_cast <double> (rand()) / static_cast <double> (RAND_MAX); // generate a double between 0 and 1
    double testValue = 50.0 + r * 12.0;   
    return testValue;
}
 
// this simulates actual values read from a process PLC or other server
double RandomGasFlowValue() {
    //generate a random float between 8 and 9
    double r = static_cast <double> (rand()) / static_cast <double> (RAND_MAX); // generate a double between 0 and 1
    double testValue = 8.0 + r;
    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).
 */
void CheckAlarms() {
    float temperature=RandomTemperatureValue();
 
    if (temperature>60.0) {
        //set the Alarm. This has the same effect as triggering with a boolean true if you were using a real data source.                
        if (!(temperatureAlarm)) {
            temperatureAlarm=true;
            gem_native_wrapper.Alarm("TemperatureProblem", temperatureAlarm);
        }
    }
    else {
        //clear the alarm. This has the same effect as triggering with a boolean false if you were using a real data source.
        if (temperatureAlarm) {
            temperatureAlarm=false;
            gem_native_wrapper.Alarm("TemperatureProblem", temperatureAlarm);
        }
    }
}
int main(int argc, char **argv) {    
    gem_native_wrapper.StartMain("GEMTool");
 
    int port = 5010;
 
    // persistance file saves GEM configuration between restarts (reports, enabled CEIDs, enabled Alarms, etc).
    gem_native_wrapper.Publish("gemtool/configuration/persistencefilename", "/tmp/testpersistence");
    gem_native_wrapper.Publish("gemtool/configuration/deviceid", 1);
    gem_native_wrapper.Publish("gemtool/configuration/port", port);
 
    gem_native_wrapper.Subscribe("gemtool/vids/controlstate", [](std::string, transsecswrapper::ValueObject value_object) {
        printf("Control state changed %s\n\r", value_object.GetStringValue().c_str());
    });
 
 
    gem_native_wrapper.Subscribe("gemtool/vids/ppid", [](std::string, transsecswrapper::ValueObject value_object) {
        //printf("Current time %li\n\r", value_object.GetLongValue());
        printf("New ppid %s\n\r", value_object.GetStringValue().c_str());
    });
 
    //note: online/offline and remote/local are usually set by a hardware switch on the tool 
    //  and read to get the actual values but here we set these values to defaults
    int online= 1; //online if set to 1 (offline when 0)
    int remote= 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)
    gem_native_wrapper.Publish("OnlineOfflineState", online);//Go Online (still local)
    printf("Control State %s\n\r", gem_native_wrapper.Get("gemtool/vids/controlstate").GetStringValue().c_str() );
    gem_native_wrapper.Publish("LocalRemoteState", remote);//Allow Local-Remote transition
    printf("Control State %s\n\r", gem_native_wrapper.Get("gemtool/vids/ControlState").GetStringValue().c_str() );
 
    //gem_native_wrapper.Publish("EstablishCommunicationsTimeout", 5);//Allow Local-Remote transition
    //gem_native_wrapper.Publish("EnableSpooling", new LongValueObject(1));//Enable Spooling
    gem_native_wrapper.Publish("gemtool/vids/EnableSpooling", 0);//Disable Spooling
 
     //You can also change other GEM parameters to override the defaults set in the tool project
    // Recommendation is to limit MDLN and SoftRev to 6 chars  for backwards compatibility - here we demonstrate a longer version
    gem_native_wrapper.Publish("gemtool/vids/MDLN", "Model ABCDEFG");
    gem_native_wrapper.Publish("gemtool/vids/SoftRev","Rev1.0");//SoftRev
    gem_native_wrapper.Publish("gemtool/vids/ppid","InitialPPID");//set a ppid
 
    // 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 - gemtool/hostcommandstart/allparams
    gem_native_wrapper.Subscribe("gemtool/hostcommandstart",[](std::string topic, transsecswrapper::ValueObject value_object) {
        printf ("Host Command %s\n\r", value_object.GetStringValue().c_str());
 
        const char* response = "{\"HCACK\": { \"value\":\"4\", \"type\":10 },"
        "\"ErrorParamsList\": { \"values\": [  { \"value\":\"STEP\", \"type\":20 },"
        "{ \"value\":\"2\", \"type\":54 } ]  }}\n\r";
 
        //"Parm1List_0": { "values": [ "PPID","RecipeName" ] , "type":20 },
        gem_native_wrapper.PublishJson("gemtool/hostcommandreply/sendmessage", response);
    });
 
    //This next part runs the tool and sets random event and alarms once in awhile
    long waferCount = 1;
    long cycles = 0;
    srand (static_cast <unsigned> (time(0)));
    while (true) {
 
        //set some SVID values
        //gem_native_wrapper.Publish("gemtool/vids/ProcessTemperature",randomTemperatureValue());
        //gem_native_wrapper.Publish("gemtool/vids/GasFlow",randomGasFlowValue());
        std::this_thread::sleep_for(std::chrono::milliseconds(500));  
        cycles ++;
 
        //test event triggering every few wafer counts (events will not be triggered unless host has enabled them)
 
        if ((cycles % 10)==0) { //every 10 counts 
 
            waferCount++;
            gem_native_wrapper.Publish("gemtool/vids/WaferCount", waferCount);
 
            std::string ceid_trigger = std::string("{ \"values\":["
            "    { \"dvid\":\"WaferCount\", \"dvval\":\""+std::to_string(waferCount)+"\"  },"
            "    { \"dvid\":\"ADVID\",  \"dvval\":\"-2\"  }"
            " ] }");
            // the ceid triggers are in JSON format allowing DVIDs to be set.
            gem_native_wrapper.Trigger("Loaded", ceid_trigger.c_str());
        }
 
        //test alarms (alarms will not be triggered unless host has enabled them)
        CheckAlarms();
 
    }
}
  • cpp.txt
  • Last modified: 2022/09/11 04:24
  • by wikiadmin