Using the TransSecs Native Wrapper to Characterize a Tool and Define a Host from C++
To use the TransSecs native wrapper for characterizing a tool and defining a host in C++, you will need the following files:
- 'transsecs.h'
- 'transsecs.hpp'
- 'transsecs.dll' (for Windows) or 'libtranssecs.so' (for Linux)
This article will guide you through the steps needed to define and start a host, characterize the tool, send messages, subscribe to topics, and define reports with associated CEIDs.
Defining and Starting a Host
To define and start a host, you need to set several configuration parameters using the 'publish' method. Once configured, you can start the host with the 'startMain' method. Here is an example:
#include <iostream> #include "transsecs.hpp" #include <thread> #include <chrono> #include <fstream> int main(int argc, char **argv) { transsecs::TransSecsWrapper* wrapper = new transsecs::TransSecsWrapper(); int port = 5010; wrapper->publish("gemhost/configuration/persistencefilename", "/tmp/testpersistence"); wrapper->publish("gemhost/configuration/equipmenthostname", "localhost"); wrapper->publish("gemhost/configuration/deviceid", 1); wrapper->publish("gemhost/configuration/activeport", port); // For most cases, this should always be "GEMHost" wrapper->startMain("GEMHost"); // Additional code follows... }
Characterizing the Tool
Characterizing the tool involves capturing the GEM model configuration. The 'characterize' method generates a JSON representation of the tool's configuration, which can be saved to a file:
// Characterizing the tool and saving the JSON to a file std::string json = wrapper->characterize(); std::ofstream outputFile("characterized.json"); outputFile << json; outputFile.close();
Sending Messages
To send messages, use the 'publish' method with the appropriate topic and message content. Here is an example of sending an 's2f41' (host command) and an 's1f3' (SVID request):
// Send an example s2f41 (host command). std::string s2f41 = R"({"Command":{"value":"START","type":20},"CommandParams":{"values":[{"values":["PPID","recipe_name"]},{"values":[{"value":"slotmap","type":20},{"values":[0,1,1,0],"type":51}]}]}})"; wrapper->publish("gemhost/hostcommand/sendmessage", s2f41); wrapper->publish("gemhost/svidrequest/sendmessage", "{\"SVID\": { \"value\":\"33003\", \"type\":54 }}");
Summary of JSON Format for publishing
In this JSON format:
- The 'value' property is used to represent a single item.
- The 'values' property is used to represent an array of items.
In both cases, the value itself must be a JSON string, and not a number, even for numeric types.
Note that types are SECS standard types.
'value' Property
- Holds a single object or primitive value.
- Example:
{ "value": "START", "type": 20 }
'values' Property
- Holds an array of items.
- There are two ways to write an array in this format:
- With a single 'type' for the entire array:
- The 'type' specifies the type for all elements in the array.
- Example:
{ "values": ["PPID", "recipe_name"], "type": 20 }
- With nested objects having individual 'type' properties:
- Each element can have its own 'value' and 'type'.
- Example:
{ "values": [ { "value": "slotmap", "type": 20 }, { "values": [0, 1, 1, 0], "type": 51 } ] }
This format allows flexibility in defining both simple and complex data structures with explicit type information for each element.
Defining a HostCommand (S2F41)
Root Object
- Contains two main properties: '“Command”' and '“CommandParams”'.
"Command" Object
- Properties:
'“value”': A string representing the command (e.g., '“START”').
'"type"': An integer representing the
SECS type (e.g., '20' for ASCII strings).
"CommandParams" Object
- Property:
'“values”': An array containing parameter objects.
Parameter Objects
- Each parameter can either be a simple list or a nested structure.
- Simple List:
Contains a '“values”' array and a '“type”' indicating the type of the list elements.
Example:
{ "values": ["PPID", "recipe_name"], "type": 20 }
- Nested Structure:
Contains a '“values”' array with nested objects, each with their own '“value”' and '“type”'.
Example:
{ "values": [ { "value": "slotmap", "type": 20 }, { "values": [0, 1, 1, 0], "type": 51 } ] }
Publishing
Publish your json to “gemhost/hostcommand/sendmessage”
Defining an SVIDRequest (S1F3)
SVID request is much simpler; you publish a single value to “gemhost/svidrequest/sendmessage” with an “SVID” key. For example:
{ "SVID": { "value": "33003", "type": 54 } }
Notice again that the “value” SVID must be a string, even though it has a numeric type.
Here is the end of the document converted into DokuWiki format:
Subscribing to Topics
Subscribing to topics allows you to receive updates for specific variables. Use the 'subscribe' method to listen for changes on desired topics:
// Subscribe to CEID events wrapper->subscribe("gemhost/variables/ceid/loaded", [](const char* topic, const transsecs::TransSecsValueObject* value) { std::cout << "Received topic: " << topic << std::endl; std::cout << "Received value: " << value->getValue()->getStringValue() << std::endl; }); // Subscribe to VID events wrapper->subscribe("gemhost/variables/vid/lotid", [](const char* topic, the transsecs::TransSecsValueObject* value) { std::cout << "LOTID received: " << value->getValue()->getStringValue() << std::endl; }); // Subscribe to control state updates wrapper->subscribe("gemhost/variables/vid/controlstate", [](const char* topic, the transsecs::TransSecsValueObject* value) { std::cout << "Control State: " << value->getValue()->getLongValue() << std::endl; });
VID subscriptions will provide just the expected value. For example, the controlstate above might be called with a value with a long value of 5.
CEID subscriptions will provide a JSON object with the CEID and any associated reports. For example, the loaded subscription above Might return something like:
{ "LOADED": [ { "LOADED_REPORT": [ {"CLOCK":"2024052115063000", "type":20 },{"LOTID":"10101a", "type":20 } ], "rptid":80} ], "ceid":7503, "timestamp":"2024-05-21 15:06:30.030"}
Defining Reports and Associating Them with CEIDs
Reports can be defined and associated with CEIDs in the resulting JSON configuration. After characterizing the tool, the JSON configuration needs to be updated to include the report definitions and their associations.
Here is an excerpt of the JSON configuration before and after updating the RPTIDs:
Before Updating RPTIDs:
{ "ceidSecsFormat": 54, "vidSecsFormat": 54, "dataIdSecsFormat": 54, "alidSecsFormat": 54, "rptidSecsFormat": 54, "tridSecsFormat": 54, "totsmpSecsFormat": 54, "enableAllEvents": false, "vids": [ { "vidType": 2, "valueType": 20, "name": "PPID", "id": 1516 }, // ... Other VIDs ... ], "ceids": [ { "associatedIDs": [], "variableType": "CEID", "configured": false, "sort": false, "name": "LOADED", "id": 7503 }, // ... Other CEIDs ... ], "rptids": [] }
After Updating RPTIDs:
{ "ceidSecsFormat": 54, "vidSecsFormat": 54, "dataIdSecsFormat": 54, "alidSecsFormat": 54, "rptidSecsFormat": 51, "tridSecsFormat": 54, "totsmpSecsFormat": 54, "enableAllEvents": false, "vids": [ { "vidType": 2, "valueType": 20, "name": "PPID", "id": 1516 }, // ... Other VIDs ... ], "ceids": [ { "associatedIDs": [80], "variableType": "CEID", "configured": false, "sort": false, "name": "LOADED", "id": 7503 }, // ... Other CEIDs ... ], "rptids": [ { "associatedIDs": [33008, 1514], "variableType": "RPTID", "configured": false, "sort": false, "name": "LOADED_REPORT", "id": 80 } ] }
In the initial characterized JSON, the rptids and ceids are not fully configured. In this example, CEID 7503 (LOADED) is associated with RPTID 80, which includes variables with IDs 33008 (CLOCK) and 1514 (LOTID). These will then be properly configured and linked when the new GEM model is set.
Setting the GEM model
The host's GEM model can be set from a JSON string.
std::ifstream inputFile("GEMHostModel.json"); std::string gemModel((std::istreambuf_iterator<char>(inputFile)), std::istreambuf_iterator<char>()); inputFile.close(); wrapper->setGemModel(gemModel);
Full C++ Example
Here is the complete C++ example combining all
the steps:
#include <iostream> #include "transsecs.hpp" #include <thread> #include <chrono> #include <fstream> int main(int argc, char **argv) { transsecs::TransSecsWrapper* wrapper = new transsecs::TransSecsWrapper(); int port = 5010; wrapper->publish("gemhost/configuration/persistencefilename", "/tmp/testpersistence"); wrapper->publish("gemhost/configuration/equipmenthostname", "localhost"); wrapper->publish("gemhost/configuration/deviceid", 1); wrapper->publish("gemhost/configuration/activeport", port); // For most cases, this should always be "GEMHost" wrapper->startMain("GEMHost"); // This section will characterize the tool and save the json to a file // Characterizing the tool will cause the gem model to be overwritten, so in this example it is done first std::string json = wrapper->characterize(); std::ofstream outputFile("characterized.json"); outputFile << json; outputFile.close(); // Set the gem model to a prepared model for this example std::ifstream inputFile("../../../GEMHostModel.json"); std::string gemModel((std::istreambuf_iterator<char>(inputFile)), std::istreambuf_iterator<char>()); inputFile.close(); wrapper->setGemModel(gemModel); // The ceid listener will receive a json string of any associated reports, as well as the ceid value wrapper->subscribe("gemhost/variables/ceid/loaded", [](const char* topic, the transsecs::TransSecsValueObject* value) { std::cout << "Received topic: " << topic << std::endl; std::cout << "Received value: " << value->getValue()->getStringValue() << std::endl; }); // This listener will update with the values from lotid wherever it is received, for example in a report wrapper->subscribe("gemhost/variables/vid/lotid", [](const char* topic, the transsecs::TransSecsValueObject* value) { std::cout << "LOTID received: " << value->getValue()->getStringValue() << std::endl; }); // Can subscribe to any variable to receive updates. In this case, this will be triggered by an s1f3 we send in the main loop wrapper->subscribe("gemhost/variables/vid/controlstate", [](const char* topic, the transsecs::TransSecsValueObject* value) { std::cout << "Control State: " << value->getValue()->getLongValue() << std::endl; }); // Send an example s2f41 (host command). std::string s2f41 = R"({"Command":{"value":"START","type":20},"CommandParams":{"values":[{"values":["PPID","recipe_name"]},{"values":[{"value":"slotmap","type":20},{"values":[0,1,1,0],"type":51}]}]}})"; wrapper->publish("gemhost/hostcommand/sendmessage", s2f41); // Need to loop here because if the application exits, the wrapper will exit as well while (true) { std::this_thread::sleep_for(std::chrono::seconds(5)); // Send an example s1f3 (svid request) // In this example 33003 is the controlstate SVID, so this will cause the listener above to print the received control state wrapper->publish("gemhost/svidrequest/sendmessage", "{\"SVID\": { \"value\":\"33003\", \"type\":54 }}"); } }