This GEM Rust host example uses the code generated from TransSECS using the Native/C++ deployment option. More detailed information is available at [[https://ergotech.com/files/guides/doc/transsecs/index.html|Rust Documentation]]
Most of the complexity is removed by the definitions you created in TransSECS. In Rust you subscribe to receive data and publish values. 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.
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 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.
For example, to set the hostname, you publish to that variable:
wrapper.publish_string("gemhost/configuration/equipmenthostname", "localhost").unwrap();
And Events (CEIDs) received as JSON Strings that contains the values configured in TransSECS. For example, a subscription to the "loaded" event might deliver a JSON string similar to this:
{ "LOADED": [ { "RPTID103": [ {"LOTID":"", "type":20 },{"PPID":"recipename", "type":20 },{"WaferCount":"1", "type":54 } ], "rptid":103} ], "ceid":7503, "timestamp":"2021-08-10 19:58:28.802"}
The sample code receives and unpacks that structure:
wrapper.subscribe("gemhost/variables/ceid/loaded", |topic,value| {
println!("Callback called on {}:", topic);
if let Value::String(string_value) = value.value {
// The ceid appears as a json object, which we can parse for example with serde_json
let parsed:LoadedCeid = serde_json::from_str(&string_value).unwrap();
// We'll just immediately reserialize the json to demonstrate that we have the fields
println!("{}", serde_json::to_string_pretty(&parsed).unwrap());
}
}).unwrap();
The tool can subscribe to be notified of the change in value of any VID - used for ECIDs and GEM internal variables:
wrapper.subscribe("gemhost/variables/vid/lotid", |topic,value| {
println!("Callback called on {} with {:?}", topic, value)
}).unwrap();
Here's the code sample from the rustwrapper archive:
//! Simple GEM host example showing how to start a host and subscribe to some variables
use transsecs::transsecs_wrapper::TransSecsWrapper;
use transsecs::value_object::Value;
use std::{thread, time};
use serde::{Deserialize, Serialize, Serializer};
use serde::ser::SerializeStruct;
use anyhow::Result;
#[derive(Serialize, Deserialize, Debug)]
struct LoadedCeid {
#[serde(rename="LOADED")]
reports:Vec
}
#[derive(Serialize, Deserialize, Debug)]
struct Report {
#[serde(rename="LOADED_REPORT")]
values:Vec
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(untagged)]
enum ReportValue {
LotID{
#[serde(rename="LOTID")]
lot_id:String,
#[serde(rename="type")]
type_int:i32
},
CLOCK{
#[serde(rename="CLOCK")]
clock:String,
#[serde(rename="type")]
type_int:i32
}
}
#[derive(Deserialize)]
struct SecsValue {
value: String,
}
impl Serialize for SecsValue {
fn serialize(&self, serializer: S) -> Result
where
S: Serializer,
{
let mut s = serializer.serialize_struct("SecsValue", 2)?;
s.serialize_field("value", &self.value)?;
s.serialize_field("type", &20)?;
s.end()
}
}
#[derive(Deserialize)]
struct CommandParams {
values: Vec<(SecsValue, SecsValue)>,
}
struct TupleAsObject<'a>(&'a (SecsValue, SecsValue));
impl<'a> Serialize for TupleAsObject<'a> {
fn serialize(&self, serializer: S) -> Result
where
S: Serializer,
{
let mut s = serializer.serialize_struct("TupleAsObject", 2)?;
s.serialize_field("values", &vec![&self.0 .0.value, &self.0 .1.value])?;
s.serialize_field("type", &20)?;
s.end()
}
}
impl Serialize for CommandParams {
fn serialize(&self, serializer: S) -> Result
where
S: Serializer,
{
let values: Vec<_> = self.values.iter().map(TupleAsObject).collect();
let mut s = serializer.serialize_struct("CommandParams", 1)?;
s.serialize_field("values", &values)?;
s.end()
}
}
#[derive(Serialize, Deserialize)]
struct S2F41 {
#[serde(rename = "Command")]
command: SecsValue,
#[serde(rename = "CommandParams")]
command_params: CommandParams,
}
// examples/gem_host_example.rs
fn main() -> Result<()>{
let mut wrapper = TransSecsWrapper::new::(None)?;
let port = 5010;
wrapper.publish_string("gemhost/configuration/persistencefilename", "/tmp/testpersistence").unwrap();
wrapper.publish_string("gemhost/configuration/equipmenthostname", "localhost").unwrap();
wrapper.publish_int("gemhost/configuration/deviceid", 1).unwrap();
wrapper.publish_int("gemhost/configuration/activeport", port).unwrap();
wrapper.start_main("GEMHost").unwrap();
wrapper.subscribe("gemhost/variables/ceid/loaded", |topic,value| {
println!("Callback called on {}:", topic);
if let Value::String(string_value) = value.value {
// The ceid appears as a json object, which we can parse for example with serde_json
let parsed:LoadedCeid = serde_json::from_str(&string_value).unwrap();
// We'll just immediately reserialize the json to demonstrate that we have the fields
println!("{}", serde_json::to_string_pretty(&parsed).unwrap());
}
}).unwrap();
wrapper.subscribe("gemhost/variables/vid/clock", |topic,value| {
println!("Callback called on {} with {:?}", topic, value)
}).unwrap();
wrapper.subscribe("gemhost/variables/vid/lotid", |topic,value| {
println!("Callback called on {} with {:?}", topic, value)
}).unwrap();
let gem_model_json = include_str!("../GEMHostModel.json");
wrapper.set_gem_model(gem_model_json).unwrap();
loop{
let host_command_start = S2F41 {
command: SecsValue {
value: "START".to_string(),
},
command_params: CommandParams {
values: vec![
(SecsValue {
value: "PPID".to_string(),
},
SecsValue {
value: "warm".to_string(),
})
]
},
};
// wrapper.publish_json("gemhost/hostcommand/sendmessage", serde_json::to_string(&host_command_start).unwrap()).unwrap();
// wrapper.publish_json("gemhost/svidrequest/sendmessage", "{\"SVID\": { \"value\":\"33003\", \"type\":54 }}").unwrap();
let complicated_s2f41 = include_str!("../s2f41.json");
wrapper.publish_json("gemhost/hostcommand/sendmessage", complicated_s2f41).unwrap();
thread::sleep(time::Duration::from_secs(10));
} // If we drop the wrapper, it will cleanup the jvm
}