Community Tip - Your Friends List is a way to easily have access to the community members that you interact with the most! X
These code snippets illustrate parsing CSV files and populating the Axeda Enterprise with data, locations and organizations. These files are incoming to the Axeda Platform.
Note: These snippets do NOT handle null values in the CSV due to the lack of a CSV parsing library. Workaround is to populate empty values with an empty or null signifier (such as a blank space) and test for these on the Groovy side.
Code Snippets:
CSV file to Data Items
CSV file to Location Organization
Script Name: CSV file to Data Items
Description: Executed from an expression rule with file hint "datainsert", takes a CSV file and adds data based on values.
Parameters:
OPTIONAL - only needed for debugging
import com.axeda.drm.sdk.Context
import com.axeda.drm.sdk.device.DeviceFinder
import com.axeda.drm.sdk.device.ModelFinder
import com.axeda.drm.sdk.device.DataItemFinder
import com.axeda.drm.sdk.device.DataItem
import com.axeda.drm.sdk.data.DataValueEntry
import java.util.regex.Pattern
import groovy.json.*
import com.axeda.drm.services.device.DataItemType
import net.sf.json.JSONObject
/**
* CSVToData.groovy
* -----------------------
*
* Executed from an expression rule with file hint "datainsert", takes a CSV file and adds data based on values.
*
* @note There must be a column with "model" and one with "serial". The rest of the columns should be data item names with values
* in the rows. DOES NOT handle null values in CSV. Workaround is to insert blank spaces in null values and test for those on the Groovy side.
* Solution would be to add a library for CSV parsing such as open csv.
*
* @params - only needed if NOT executed from expression rule - primarily for debugging
* modelName - (OPTIONAL) Str - name of the model
* serialNumber - (OPTIONAL) Str - name of the serial number
*
*
*/
/**
* initialize our global variables
* json = the contents of our response
* infoString = a stringBuilder used to collect debug information during the script
* contentType = the content type we will return
* scriptname = The name of this Script, used in multiple places
*/
def json = new groovy.json.JsonBuilder()
def infoString = new StringBuilder()
def contentType = "application/json"
def scriptName = "CSVToData.groovy"
def root = ["result":["items":[]]]
def columns = []
try {
Context CONTEXT = Context.getSDKContext()
def modelIndex
def serialIndex
// initialize Model and Device Finders
ModelFinder modelFinder = new ModelFinder(CONTEXT)
DeviceFinder deviceFinder = new DeviceFinder(CONTEXT)
// implicit object compressedFile
File file = compressedFile.getFiles()[0].extractFile()
/* //begin non-expression rule code, useful for debugging
File file
modelFinder.setName(Request.parameters.modelname)
def model1 = modelFinder.find()
deviceFinder.setSerialNumber(Request.parameters.serialNumber)
deviceFinder.setModel(model1)
def d = deviceFinder.find()
UploadedFileFinder uff = new UploadedFileFinder(CONTEXT)
uff.device = d
def ufiles = uff.findAll()
UploadedFile ufile
if (ufiles.size() > 0) {
ufile = ufiles[0]
file = ufile.extractFile()
}
*/ //end non-expression rule code
file.eachLine {line ->
def row = line.tokenize(',')
// set the column headings
if (columns.size() == 0){
columns = row
// find model and serial index, assumes there's a column that has "model" and "serial", otherwise take columns 0 and 1
def modelpatt = Pattern.compile(/[A-Za-z_\-]{0,}model[A-Za-z_\-]{0,}/, Pattern.CASE_INSENSITIVE)
def serialpatt = Pattern.compile(/[A-Za-z_\-]{0,}serial[A-Za-z_\-]{0,}/, Pattern.CASE_INSENSITIVE)
modelIndex = columns.findIndexOf{ it ==~ modelpatt } > -1 ? columns.findIndexOf{ it ==~ modelpatt } : 0
serialIndex = columns.findIndexOf{ it ==~ serialpatt } > -1 ? columns.findIndexOf{ it ==~ serialpatt } : 1
}
// otherwise populate data
else {
modelFinder.setName(row.get(modelIndex))
def model = modelFinder.find()
deviceFinder.setModel(model)
deviceFinder.setSerialNumber(row.get(serialIndex))
def device = deviceFinder.find()
def assetInfo = [
"model": model.name,
"serial": device.serialNumber,
"data":[]
]
row.eachWithIndex{ item, index ->
if (index != modelIndex && index != serialIndex){
def dataItemName = columns[index].replace(" ","")
DataItemFinder dif = new DataItemFinder(CONTEXT);
dif.setDataItemName(dataItemName);
dif.setModel(model);
DataItem dataItem = dif.find();
if (dataItem){
if (item.isNumber()){
item = Double.valueOf(item)
}
DataValueEntry dve = new DataValueEntry(CONTEXT, device, dataItem, item)
dve.store()
}
else {
DataItem newDataItem
if (item.isNumber()){
newDataItem = new DataItem(CONTEXT, model,DataItemType.ANALOG, dataItemName)
item = Double.valueOf(item)
}
else {
newDataItem = new DataItem(CONTEXT, model,DataItemType.STRING, dataItemName)
}
newDataItem.store()
DataValueEntry dve = new DataValueEntry(CONTEXT, device, newDataItem, item)
dve.store()
}
assetInfo.data << [
"name": dataItemName,
"value": item
]
}
root.result.items << assetInfo
}
}
}
logger.info(JSONObject.fromObject(root).toString(2))
} catch (Exception e) {
processException(scriptName,json,e)
}
//return ['Content-Type': 'application/json', 'Content': JSONObject.fromObject(root).toString(2)]
/*
Processes the contents of an Exception and add it to the Errors collection
@param json The markup builder
*/
private def processException(String scriptName, JsonBuilder json, Exception e) {
// catch the exception output
def logStringWriter = new StringWriter()
e.printStackTrace(new PrintWriter(logStringWriter))
logger.error("Exception occurred in ${scriptName}: ${logStringWriter.toString()}")
/*
Construct the error response
- errorCode Will be an element from an agreed upon enum
- errorMessage The text of the exception
*/
json.errors {
error {
message "[${scriptName}]: " + e.getMessage()
timestamp "${System.currentTimeMillis()}"
}
}
return json
}
Script Name: CSV file to Location Organization
Description: Executed from an expression rule with file hint "locorginsert", takes a CSV file and adds orgs and locations based on values.
Parameters:
OPTIONAL - only needed for debugging
import com.axeda.drm.sdk.Context
import com.axeda.drm.sdk.device.DeviceFinder
import com.axeda.drm.sdk.device.ModelFinder
import com.axeda.drm.sdk.device.DataItemFinder
import com.axeda.drm.sdk.device.DataItem
import com.axeda.drm.sdk.data.DataValueEntry
import java.util.regex.Pattern
import groovy.json.*
import com.axeda.drm.services.device.DataItemType
import net.sf.json.JSONObject
import com.axeda.drm.sdk.contact.Organization
import com.axeda.drm.sdk.contact.Location
import com.axeda.drm.sdk.contact.OrganizationFinder
import com.axeda.drm.sdk.contact.LocationFinder
import com.axeda.drm.sdk.data.UploadedFile
import com.axeda.drm.sdk.data.UploadedFileFinder
/**
* CSVToLocOrg.groovy
* -----------------------
*
* Executed from an expression rule with file hint "locorginsert", takes a CSV file and adds orgs and locations based on values.
*
* @note There must be a column with "model" and one with "serial". The rest of the columns should be either parts of a
* location or an organization. The location parts columns should be prefixed with the org# that they correspond to.
* DOES NOT handle null values in CSV. Workaround is to insert blank spaces in null values and test for those on the Groovy side.
* Solution would be to add a library for CSV parsing such as open csv.
*
* @params - only needed if NOT executed from expression rule - primarily for debugging
* modelName - (OPTIONAL) Str - name of the model
* serialNumber - (OPTIONAL) Str - name of the serial number
*
*
*
*/
/**
* initialize our global variables
* json = the contents of our response
* infoString = a stringBuilder used to collect debug information during the script
* contentType = the content type we will return
* scriptname = The name of this Script, used in multiple places
*/
def json = new groovy.json.JsonBuilder()
def infoString = new StringBuilder()
def contentType = "application/json"
def scriptName = "CSVToLocOrg.groovy"
def root = ["result":["items":[]]]
def columns = []
try {
Context CONTEXT = Context.getSDKContext()
def modelIndex
def serialIndex
def locIndices = [:]
def locKeys = ["line1","line2", "address1", "address2", "city","state","zip","country", "org"]
// initialize Finders
ModelFinder modelFinder = new ModelFinder(CONTEXT)
DeviceFinder deviceFinder = new DeviceFinder(CONTEXT)
LocationFinder locationFinder = new LocationFinder(CONTEXT)
OrganizationFinder organizationFinder = new OrganizationFinder(CONTEXT)
// implicit object compressedFile
File file = compressedFile.getFiles()[0].extractFile()
/* //begin non-expression rule code, useful for debugging
File file
modelFinder.setName(Request.parameters.modelname)
def model1 = modelFinder.find()
deviceFinder.setSerialNumber(Request.parameters.serialNumber)
deviceFinder.setModel(model1)
def d = deviceFinder.find()
UploadedFileFinder uff = new UploadedFileFinder(CONTEXT)
uff.device = d
def ufiles = uff.findAll()
UploadedFile ufile
if (ufiles.size() > 0) {
ufile = ufiles[0]
file = ufile.extractFile()
}
*/ //end non-expression rule code
file.eachLine {line ->
def row = line.tokenize(',')
// set the column headings
if (columns.size() == 0){
columns = row
// find model and serial index, assumes there's a column that has "model" and "serial", otherwise take columns 0 and 1
def modelpatt = Pattern.compile(/[A-Za-z_\-]{0,}model[A-Za-z_\-]{0,}/, Pattern.CASE_INSENSITIVE)
def serialpatt = Pattern.compile(/[A-Za-z_\-]{0,}serial[A-Za-z_\-]{0,}/, Pattern.CASE_INSENSITIVE)
modelIndex = columns.findIndexOf{ it ==~ modelpatt } > -1 ? columns.findIndexOf{ it ==~ modelpatt } : 0
serialIndex = columns.findIndexOf{ it ==~ serialpatt } > -1 ? columns.findIndexOf{ it ==~ serialpatt } : 1
locKeys.each{ key ->
// construct a regex for each key and create a map for finding/creating
def locPatt = Pattern.compile(/[A-Za-z0-9_\-]{0,}${key}[A-Za-z0-9_\-]{0,}/, Pattern.CASE_INSENSITIVE)
def colIndex = columns.findIndexOf{
def match = it =~ locPatt
if (match){
return match?.getAt(0)
}
}
if (colIndex > -1){
locIndices[colIndex] = key
}
}
}
// otherwise populate data
else {
modelFinder.setName(row.get(modelIndex))
def model = modelFinder.find()
deviceFinder.setModel(model)
deviceFinder.setSerialNumber(row.get(serialIndex))
def device = deviceFinder.find()
def assetInfo = [
"model": model.name,
"serial": device.serialNumber,
"locs":[]
]
def locMap = [:]
def orgName
def locKey
def locBool = false // make sure we get some criteria
row.eachWithIndex{ item, index ->
if (index != modelIndex && index != serialIndex && item && item != ""){
locKey = locIndices[index]
if (locKey){
locBool = true
if (locKey == "address1"){
locKey = "line1"
}
if (locKey == "address2"){
locKey = "line2"
}
if (locKey == "org"){
orgName = item
}
// don't execute if we've got an organization key
else {
// for finding
locationFinder[locKey] = item
// for creating (if needed)
locMap[locKey] = item
}
}
}
}
assetInfo.org
Organization org
if (orgName){
organizationFinder.setName(orgName)
org = organizationFinder.find()
if (!org){
org = new Organization(CONTEXT, orgName)
org.store()
}
}
Location loc
if (locBool){
logger.info("with bool")
loc = locationFinder.find()
logger.info(loc?.name)
}
if (!loc){
def line1 = locMap["line1"]
def name = line1?.replace(" ","")?.replace(/\./,"")?.replace("_","") + "_Loc"
def line2 = locMap["line2"]
def city = locMap["city"]
def state = locMap["state"]
def zip = locMap["zip"]
def country = locMap["country"]
if (line1 && city){
loc = new Location(CONTEXT,name,line1,line2,city,state,zip,country)
loc.store()
}
if (loc && org){
org.addLocation(loc)
org.store()
}
}
assetInfo.locs << [
"name": loc.name,
"line1": loc.line1,
"line2": loc.line2,
"city": loc.city,
"state": loc.state,
"zip": loc.zip,
"country": loc.country
]
assetInfo.org = [
"name": org.name
]
root.result.items << assetInfo
}
}
logger.info(JSONObject.fromObject(root).toString(2))
} catch (Exception e) {
processException(scriptName,json,e)
}
//return ['Content-Type': 'application/json', 'Content': JSONObject.fromObject(root).toString(2)]
/*
Processes the contents of an Exception and add it to the Errors collection
@param json The markup builder
*/
private def processException(String scriptName, JsonBuilder json, Exception e) {
// catch the exception output
def logStringWriter = new StringWriter()
e.printStackTrace(new PrintWriter(logStringWriter))
logger.error("Exception occurred in ${scriptName}: ${logStringWriter.toString()}")
/*
Construct the error response
- errorCode Will be an element from an agreed upon enum
- errorMessage The text of the exception
*/
json.errors {
error {
message "[${scriptName}]: " + e.getMessage()
timestamp "${System.currentTimeMillis()}"
}
}
return json
}