Translators

Each Ingestor will have a specific inbound translator with which it is associated,
and each Command will have a specific command translator with which it is associated. The inbound translator's purpose is to convert the data received via the ingestor listener and handler to the EdgeIQ report JSON format. The command translator's purpose is to convert a command represented as JSON into the device's native command format.

Cloud vs Edge Translators

Translators can be run either in the cloud or on a downstream device such as a gateway running EdgeIQ's Edge service. If the cloud_translator parameter is true, the translator will be installed in the EdgeIQ Cloud ingest engine. If it is false, the translator will be passed to the associated Edge Gateway.

Translator Types (inbound "ingest")

Translator TypeAttributesExample
ankotype, name, script{ "type" : "anko", "name" : "Anko Translator 01", "value" : "...escaped anko script..." }
javascripttype, name, script{ "type" : "javascript", "name" : "JS Translator 02", "script" : "...escaped javascript script..." }
csvtype, nameSee detailed example below.

The anko type is only valid for edge translators (i.e., cloud_translator=false).

Translator Types (command)

Translator TypeAttributesExample
anko_cmdtype, name, script{ "type" : "anko_cmd", "name" : "Anko Translator 01", "script" : "...escaped anko script..." }
javascript_cmdtype, name, script{ "type" : "javascript_cmd", "name" : "JS Translator 02", "script" : "...escaped javascript script..." }
template_cmdtype, name, script{ "type" : "template_cmd", "name" : "Template Translator 02", "script" : "...escaped golang template..." }
csv_cmdtype, name, options{ "type" : "csv_cmd", "name" : "CSV Translator 03", "options": { "delim": "
shell_cmdtype, name{ "type" : "shell_cmd", "name" : "Shell Translator" }

The anko_cmd type is only valid for edge translators (i.e., cloud_translator=false).

Translator Variables

Inside the translator, for translators of type template, anko, and javascript, certain variables are available based on the type of listener configured for the ingestor.

All Polling Listeners

NameDescription
gateway_unique_idUnique ID of Gateway device where Ingestor is running
device_unique_idUnique ID of device that Ingestor is associated with. If device is attached to a gateway, than gateway_unique_id and device_unique_id will be different. They will be identical when the Ingestor is associated with the gateway.
unique_idalias for device_unique_id
deviceThe Device resource that is associated with the Ingestor. All data fields in Device are available
device_typeThe Device resource that is associated with the Ingestor. All data fields in DeviceType are available

HTTP Client Listener

NameDescription
outputThe HTTP response body. If the Content Type is application/json, then the output variable will support JSON querypaths, i.e. output.foo for { "foo": "bar" }
bodyalias for output
status_codeThe HTTP Response status code value as an integer.

ICMP Client Listener

NameDescription
outputsuccess/fail
statusalias for output
errorif status is fail, then the error value as a string. if status is success, than an empty string.
time_in_mslatency in milliseconds

SNMP Client Listener

NameDescription
outputA JSON map where key is the SNMP Object ID and its value as either a string or integer
errorError information as a string

Script Example

This is an example of an inbound anko Edge Translator that translates a fixed-length binary message to the EdgeIQ JSON report format. The script follows a pattern

The anko script:

# anko "package" imports
time = import("time")
math = import("math")
json = import("encoding/json")

# vars that will be set and/or read from go
incomingMsg = nil
dataSourceId = ""
errMsg = ""

func isValid() {
	if len(incomingMsg) == 21 {
		return true
	}
	return false
}

func getMfrId() {
	return toHexString(incomingMsg[2:8])
}


func translate() {
	now = time.Now().UTC()
	battery = readIntFromBytes(incomingMsg[16:17])
	onExtPower = battery >= 128
	level = battery
	if onExtPower {
		level -= 128
	}
	tempUint32 = readUint32FromBytes(incomingMsg[17:21])
	tempFloat = math.Float32frombits(tempUint32)
		retMap = {
		"device_id": dataSourceId,
		"device_datetime": now.Format("2006-01-02T15:04:05.999Z07:00"),
		"payload": {
			"device_name": getMfrId(),
			"function_code": readIntFromBytes(incomingMsg[0:2]),
			"interval": readIntFromBytes(incomingMsg[12:16]),
			"external_power": onExtPower,
			"battery_level": level,
			"temperature": tempFloat
		},
		"raw_data": toHexString(incomingMsg)
	}

	marshaledBytes, err = json.Marshal(retMap)
	return toString(marshaledBytes)
}

The full translator JSON (including above script):

   {
      "name": "XW110 Sensor Translator",
      "type": "anko",
      "cloud_translator": false,
      "script": "# anko \"package\" imports\ntime = import(\"time\")\nmath = import(\"math\")\njson = import(\"encoding/json\")\n\n# vars that will be set and/or read from go\nincomingMsg = nil\ndataSourceId = \"\"\nerrMsg = \"\"\n\nfunc isValid() {\n\tif len(incomingMsg) == 21 {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc getMfrId() {\n\treturn toHexString(incomingMsg[2:8])\n}\n\n\nfunc translate() {\n\tnow = time.Now().UTC()\n\tbattery = readIntFromBytes(incomingMsg[16:17])\n\tonExtPower = battery >= 128\n\tlevel = battery\n\tif onExtPower {\n\t\tlevel -= 128\n\t}\n\ttempUint32 = readUint32FromBytes(incomingMsg[17:21])\n\ttempFloat = math.Float32frombits(tempUint32)\n\t\tretMap = {\n\t\t\"device_id\": dataSourceId,\n\t\t\"device_datetime\": now.Format(\"2006-01-02T15:04:05.999Z07:00\"),\n\t\t\"payload\": {\n\t\t\t\"device_name\": getMfrId(),\n\t\t\t\"function_code\": readIntFromBytes(incomingMsg[0:2]),\n\t\t\t\"interval\": readIntFromBytes(incomingMsg[12:16]),\n\t\t\t\"external_power\": onExtPower,\n\t\t\t\"battery_level\": level,\n\t\t\t\"temperature\": tempFloat\n\t\t},\n\t\t\"raw_data\": toHexString(incomingMsg)\n\t}\n\n\tmarshaledBytes, err = json.Marshal(retMap)\n\treturn toString(marshaledBytes)\n}\n"
   }

CSV Example

The next example shows a translator for a CSV message from a fictitious temperature sensor. The
message contains the following fields:

  1. MAC Address
  2. Timestamp
  3. Message type identifier
  4. Temperature value
  5. Temperature Unit (F or C)
  6. Newline char (all CSV records must end with a newline)

An example message:

cd53e1825a01,170113132307,TMP,041.27,C\n

Handler options are configured in the handler (see Ingestors example). Whereas the handler controls how CSV records are read, the options field in the JSON below specifies characteristics about the message type and individual fields. The options field is an array and can contain multiple different CSV message formats. This is useful for dealing with devices that emit multiple types of messages over the same connection.

The following table enumerates properties contained within an element of the options array. Note that in the following descriptions, "property" refers to a JSON property, and "field" refers to a value within the CSV message.

FieldRequired?Description
mfr_id_field_numYThis property indicates the position of the field value that uniquely identifies the device. This value is used to look up the device, so this value must match the device name or unique_id property.
msg_id_field_numNThis property is required when the options array contains more than one element, and indicates the position of the field that identifies the message type. A single device might emit multiple CSV message types, and this field is used to distinguish them.
msg_id_field_valNThis property is required when the options array contains more than one element, and indicates the expected contents of the msg_id_field_num field for this message type.
timestamp_field_numNThis property indicates the position of the field containing a message timestamp. If no field in the message contains a timestamp, a current timestamp will be generated during translation.
timestamp_formatNIf timestamp_field_num is specified, this property specifies the format of the timestamp so it can be correctly parsed. This property must be specified according to the rules specified in the Golang time package.
field_metadataYThis array is required. Each element in the array specifies how to handle the field within the message at the same position.

This table lists the properties of each field_metadata element:

FieldRequired?Description
nameYThe name to be given to the property within the JSON report. To omit the corresponding field from the report, specify "SKIPME" as the name.
typeNThe type of data contained in the field value (defaults to "string" if not specified).. Valid property values are "string", "integer", "float", and "boolean". When "integer" is specified, the value will be parsed as a base 10 64-bit signed integer. A "float" will be parsed as a 64-bit floating point number. A "boolean" will use the default boolean parse function.

Here is the complete JSON for this CSV translator example:

   {
      "name": "CSV Translator Example",
      "type": "csv",
      "cloud_translator": false,
      "options": {
         "mfr_id_field_num": 0,
         "msg_id_field_num": 2,
         "msg_id_field_val": "TMP",
         "timestamp_field_num": 1,
         "timestamp_format": "060102150405",
         "field_metadata": [
            {
               "name": "device_name"
            },
            {
               "name": "SKIPME"
            },
            {
               "name": "report_type"
            },
            {
               "name": "temperature",
               "type": "float"
            },
            {
               "name": "temperature_unit"
            }
         ]
      }
   }

And for the example message above, the translated report would look like this:

{
  "id": "587971121ab7161c37bbcd57",
  "device_datetime": "2017-01-13T13:23:07Z",
  "created_at": "2017-01-14T00:30:10Z",
  "payload": {
    "device_name": "cd53e1825a01",
    "report_type": "TMP",
    "temperature": 41.27,
    "temperature_unit": "C"
  },
  "device_id": "587971121ab7161c37bbcd54",
  "raw_data": "cd53e1825a01,170113132307,TMP,041.27,C",
  "seq": 0
}