cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
Showing results for 
Search instead for 
Did you mean: 

Community Tip - When posting, your subject should be specific and summarize your question. Here are some additional tips on asking a great question. X

IoT Tips

Sort by:
This script will get all contacts (optionally limited to a particular organization) and check whether there is a DeviceContact associated with it.  If there is no DeviceContact (meaning it is not associated with a device), it deletes the contact. Note - It is worthwhile to test this script by commenting out the contact.delete() line first and reviewing which contacts will be deleted.  Also, this script works by finding all contacts, therefore it is not recommended to run the script repeatedly within a short period of time. Parameter: organizationName  (OPTIONAL) - Str - the name of the organization import net.sf.json.JSONObject import com.axeda.drm.sdk.device.DeviceFinder import com.axeda.drm.sdk.device.Device import com.axeda.drm.sdk.device.ModelFinder import com.axeda.drm.sdk.device.Model import com.axeda.drm.sdk.data.CurrentDataFinder import com.axeda.drm.sdk.data.DataValue import net.sf.json.groovy.JsonSlurper import com.axeda.drm.sdk.contact.Contact import com.axeda.drm.sdk.contact.ContactFinder import com.axeda.drm.sdk.contact.Location import com.axeda.drm.sdk.contact.LocationFinder import com.axeda.drm.sdk.contact.OrganizationFinder import com.axeda.drm.sdk.Context import com.axeda.drm.sdk.contact.Organization import com.axeda.drm.sdk.contact.DeviceContact import com.axeda.drm.sdk.contact.ContactMethodType import com.axeda.drm.sdk.contact.DeviceContactFinder import groovy.json.* import com.axeda.drm.sdk.scripto.Request import com.axeda.common.sdk.id.Identifier /** * ContactDelete.groovy * ----------------------- * * Finds all contacts, then finds the device contact for each contact. If null, deletes the contact. * * @params * organizationName (OPTIONAL) Str - limit the contact deletion to an organization * * * @author Sara Streeter <sstreeter@axeda.com> */ /** * 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 = "ContactDelete.groovy" def root = ["result":["deleted":[]]] def timings = [:] timings.contactFinding = 0 timings.contactIterating = 0 wholestart = System.currentTimeMillis() final Context CONTEXT = Context.getSDKContext() try {       def params = Request?.parameters?.size() > 0 ? Request?.parameters : parameters       ContactFinder cfinder = new ContactFinder(CONTEXT)       def start = System.currentTimeMillis()     def organization       if (params.organizationName != null && params.organizationName != ""){         OrganizationFinder oFinder = new OrganizationFinder(CONTEXT)         oFinder.setName(params.organizationName)         organization = oFinder.find()               if (organization){             cfinder.setOrganization(organization)         }     }       List<Contact> contacts = cfinder.findAll()     timings.contactFinding += System.currentTimeMillis()-start       root.result.contactSize = contacts.size()       start = System.currentTimeMillis()     contacts.each{  contact ->           DeviceContactFinder dcfinder = new DeviceContactFinder(CONTEXT)         dcfinder.setContactId(contact.id)         def dc = dcfinder.findAll()         if (dc.size() == 0){             root.result.deleted << [                 id: contact.id.value,                 firstName: contact.firstName,                 lastName: contact.lastName,                 organization: contact.organization?.name             ]             contact.delete()  // comment out this line to check which contacts will be deleted first.         }     }     timings.contactIterating += System.currentTimeMillis()-start     } catch (Exception e) {     processException(scriptName,json,e) } finally {     timings.wholescript = System.currentTimeMillis() - wholestart     root += [timings: timings] } 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 }
View full tip
This is an example to show returning an uploaded file as a binary from a Groovy Script. Parameters: model_name serial_number import java.io.StringWriter import java.io.PrintWriter import com.axeda.drm.sdk.Context import com.axeda.drm.sdk.data.* import com.axeda.drm.sdk.device.* import javax.activation.MimetypesFileTypeMap try {     Context ctx = Context.getUserContext()     ModelFinder modelFinder = new ModelFinder(ctx)     modelFinder.setName(parameters.model_name)     Model model = modelFinder.find()     DeviceFinder dfinder = new DeviceFinder(ctx)     dfinder.setModel(model)     dfinder.setSerialNumber(parameters.serial_number)     Device d = dfinder.find()     UploadedFileFinder uff = new UploadedFileFinder(ctx)     uff.device = d     def ufiles = uff.findAll()     UploadedFile ufile     if (ufiles.size() > 0) {         ufile = ufiles[0]         File f = ufile.extractFile()     def type = getType(f)     return ['Content-Type': type, 'Content': new FileInputStream(f)]      } else {     return ['Content-Type': 'text/plain', 'Content': 'No files have been uploaded'] } } catch (Exception e) {     logger.info(e.message)     StringWriter logStringWriter = new StringWriter();     PrintWriter logPrintWriter = new PrintWriter(logStringWriter)     e.printStackTrace(logPrintWriter)     logger.info(logStringWriter.toString()) } static String getType(File f) {   MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap()   return mimeTypesMap.getContentType(f); }
View full tip
Update properties on a model. Parameter: modelName (REQUIRED) - String - the name of the model to have its properties updated. import com.axeda.drm.sdk.device.DeviceProperty import com.axeda.drm.sdk.device.ModelFinder import com.axeda.drm.sdk.device.Model import com.axeda.drm.sdk.Context import com.axeda.drm.sdk.device.DevicePropertyFinder import com.axeda.drm.sdk.device.Property import com.axeda.drm.sdk.device.PropertyType import com.axeda.common.sdk.id.Identifier Set<String> REQUIRED_PROPERTIES = [     "TestProperty0","TestProperty1","TestProperty2" ] try {       final def Context CONTEXT = Context.getSDKContext()       ModelFinder modelFinder = new ModelFinder(CONTEXT);     modelFinder.setName(parameters.modelName)    Model model = modelFinder.find();    if (model == null){ throw new Exception("No model found") }    modelProperties = findModelProperties(CONTEXT, model.id)     updateProperties(CONTEXT, model.id, modelProperties, REQUIRED_PROPERTIES)     modelProperties.properties.each{ logger.info("$it.name :$it.value") } } catch (Exception e){     logger.info e.localizedMessage } return true private DeviceProperty findModelProperties(Context context, Identifier modelID) {   def finder = new DevicePropertyFinder(context)   finder.id = modelID   finder.type = PropertyType.MODEL_TYPE   return finder.findOne() as DeviceProperty } private void updateProperties(Context context, Identifier modelID, DeviceProperty modelProperties, Set<String> requiredProperties) {   if (!modelProperties) {     modelProperties = new DeviceProperty(context)     modelProperties.id = modelID     modelProperties.type = PropertyType.MODEL_TYPE     modelProperties.properties = []   }   (requiredProperties - new HashSet<String>(modelProperties.properties.collect { it.name })).inject(modelProperties.properties) { list, propertyName -> list << new Property(0, propertyName, "") }   modelProperties.store() }
View full tip
Sends an email with an alarm name passed in by an Expression Rule. Parameters (passed in as arguments to ExecuteCustomObject in the Expression Rule): fromaddress toaddress import com.axeda.drm.sdk.contact.Email /* * ExprRuleAlarmToEmail.groovy * * Sends an email with an alarm name passed in by an Expression Rule. * * @param fromaddress - (REQ):Str email address of sender. * @param toaddress - (REQ): Str email address of recipient * * * @note Should be executed from an Expression Rule like the following: * * Type: Alarm * If: Alarm.severity > 490 && Alarm.severity < 700 * Then: ExecuteCustomObject("ExprRuleAlarmToEmail", "fake_sender@axeda.com","fake_recipient@axeda.com") * * @author Sara Streeter <sstreeter@axeda.com> */ try {   String fromaddress = parameters.fromaddress   String toaddress = parameters.toaddress   String subject = "Axeda Alarm - ${alarm.name}"   String body = "You are receiving this alarm ${alarm.name} because you are subscribed to its updates."   sendEmail(fromaddress, toaddress, subject, body) } catch (Exception e) { logger.error(e.message) }     public void sendEmail(String fromAddress,String toAddress,String subject, String body) {         try {             Email.send(fromAddress, toAddress, subject, body);         } catch (AddressException ae) {             logger.error(ae);         }     }
View full tip
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 modelName - (OPTIONAL) Str - name of the model serialNumber - (OPTIONAL) Str - name of the serial number 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 modelName - (OPTIONAL) Str - name of the model serialNumber - (OPTIONAL) Str - name of the serial number 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 }
View full tip
This is a collection of methods for working with ExtendedObjects based on the functions used in Fleetster. import com.axeda.drm.sdk.device.Device import com.axeda.platform.sdk.v1.services.ServiceFactory import com.axeda.platform.sdk.v1.services.extobject.ExtendedObjectSearchCriteria import com.axeda.platform.sdk.v1.services.extobject.PropertySearchCriteria import com.axeda.platform.sdk.v1.services.extobject.expression.PropertyExpressionFactory import com.axeda.drm.sdk.data.CurrentDataFinder import com.axeda.platform.sdk.v1.services.extobject.ExtendedObject import com.axeda.platform.sdk.v1.services.extobject.Property import java.text.DecimalFormat import com.axeda.drm.sdk.Context import com.axeda.drm.sdk.device.DeviceDataFinder import com.axeda.drm.sdk.user.User import com.axeda.platform.sdk.v1.services.extobject.ExtendedObjectService import com.axeda.platform.sdk.v1.services.extobject.PropertyType import com.axeda.platform.sdk.v1.services.extobject.PropertyDataType import com.axeda.platform.sdk.v1.services.extobject.ExtendedObjectType import com.axeda.common.sdk.id.Identifier import groovy.time.* /* ************************************* */ /* HelperFunctionsExtendedObjects.groovy * Extended Object retrieval/manipulation functions. * * A collection of methods for working with ExtendedObjects. * * author: sara streeter <sstreeter@axeda.com> ************************************* */     def eoSvc = new ServiceFactory().getExtendedObjectService()     def fetchFirstExtendedObject(DataItem dataItem, Map searchcriteria) {         def criteria = new ExtendedObjectSearchCriteria()         criteria.extendedObjectClassName = 'com.axeda.drm.sdk.device.DataItem'         criteria.internalObjectId = dataItem.id.value         criteria.extendedClientKey = "ExtendedDataItem!${dataItem.name}"         def eo       if (searchcriteria != null){             criteria.propertySearchCriteria = advancedPropertySearch(searchcriteria)         }        def queryResult = eoSvc.findExtendedObjects(criteria,  -1, -1, null)         if (queryResult.size() > 0 ){         eo = queryResult.first()         }        return eo     }    def addOrFetchExtendedDataItem(DataItem dataItem, Map searchcriteria) {         def criteria = new ExtendedObjectSearchCriteria()         criteria.extendedObjectClassName = 'com.axeda.drm.sdk.device.DataItem'         criteria.internalObjectId = dataItem.id.value         criteria.extendedClientKey = "ExtendedDataItem!${dataItem.name}"         def eo       if (searchcriteria != null){             criteria.propertySearchCriteria = advancedPropertySearch(searchcriteria)         }        def queryResult = eoSvc.findExtendedObjects(criteria,  -1, -1, null)        if (queryResult.size() == 0 || queryResult == null){               eo = new ExtendedObject()               eo.internalObjectId = dataItem.id.value               eo.extendedObjectType = eoSvc.findExtendedObjectTypeByClassname('com.axeda.drm.sdk.device.DataItem')               eo.externalClientKey = "ExtendedDataItem!${dataItem.name}"               eo = eoSvc.createExtendedObject(eo)             searchcriteria += [eotype: "ExtendedObject"]             createProperties(eoSvc, eo, searchcriteria)          }         else eo = queryResult.first()        return eo     }    def fetchExtendedDataItem(DataItem dataItem) {         def criteria = new ExtendedObjectSearchCriteria()         criteria.extendedObjectClassName = "com.axeda.drm.sdk.device.DataItem"         criteria.extendedClientKey = "ExtendedDataItem!${dataItem.name}"         criteria.internalObjectId = dataItem.id.value        def queryResult = eoSvc.findExtendedObjects(criteria,  -1, -1, null)         return queryResult     }    def fetchExtendedObjectsAdvancedCriteria(DataItem dataItem, Map searchcriteria, String classname, String uniqueKey) {         def criteria = new ExtendedObjectSearchCriteria()         criteria.extendedObjectClassName = "com.axeda.drm.sdk.device.DataItem"         criteria.extendedClientKey = "ExtendedDataItem!${dataItem.name}"         criteria.internalObjectId = dataItem.id.value        if (searchcriteria != null){             criteria.propertySearchCriteria = advancedPropertySearch(searchcriteria)         }        def queryResult = eoSvc.findExtendedObjects(criteria,  -1, -1, null)         return queryResult     }    def fetchExtendedObject(User user, Map searchcriteria) {         def criteria = new ExtendedObjectSearchCriteria()         criteria.extendedObjectClassName = "com.axeda.drm.sdk.user.User"         criteria.extendedClientKey = "ExtendedObject!${user.username}"         criteria.internalObjectId = user.id.value         criteria.propertySearchCriteria = exactdatePropertySearch(searchcriteria)         def queryResult = eoSvc.findExtendedObjects(criteria, -1, -1, null)         return queryResult     }    def addExtendedObject(String eoTypeName, Long referenceId, String referenceName, Map objectProperties) {       def eo = new ExtendedObject()       eo.internalObjectId = referenceId       eo.extendedObjectType = eoSvc.findExtendedObjectTypeByClassname(eoTypeName)       eo.externalClientKey = referenceName       eo = eoSvc.createExtendedObject(eo)       eo = createProperties(eoSvc, eo, objectProperties)        return eo     }    def updateExtendedObject(ExtendedObject ExtendedObject, ExtendedObject ExtendedObject) {                 def ExtendedObjectprop = getProperties(ExtendedObject)                 def ExtendedObjectprop = getProperties(ExtendedObject)                 def newproperties = [:]                 if (ExtendedObjectprop.timestamp != null){                 def ExtendedObjecttype = ExtendedObjectprop.lasttype                 def ExtendedObjecttime = ExtendedObjectprop.lasttime                 if (ExtendedObjecttype == null){                     newproperties["lasttype"] = ExtendedObjectprop.type                     newproperties["lasttime"] = ExtendedObjectprop.timestamp                 }                 else {                     def oldtime = Long.valueOf(ExtendedObjecttime)                     def total = (Long.valueOf(ExtendedObjectprop.timestamp) - oldtime)/1000                     // illustrating getPropertyByName /                     def lasttype = ExtendedObject.getPropertyByName("lasttype")                     lasttype.setValue(ExtendedObjectprop.type)                     def lasttime = ExtendedObject.getPropertyByName("lasttime")                     lasttime.setValue(ExtendedObjectprop.timestamp)                     updateProperty(lasttype)                     updateProperty(lasttime)                     if (ExtendedObjectprop.containsKey(ExtendedObjecttype + "_total")==false)                         {                             newproperties[ExtendedObjecttype + "_total"] = total                         }                     else {                         def totalprop = ExtendedObject.getPropertyByName(ExtendedObjecttype + "_total")                         def lasttotal = Double.valueOf(totalprop.value)                         totalprop.setValue(String.valueOf(Double.valueOf(total) + lasttotal))                         updateProperty(totalprop)                     }                 }                 if (newproperties.size() > 0){                     ExtendedObject = createProperties(eoSvc, ExtendedObject, newproperties)                 }         }         return ExtendedObject    }     def getProperties(ExtendedObject object){         def result = object.properties.inject([:]) { target, property ->               target += [(property.propertyType.name): castPropertyValueToDefinedType(property)]             }         return result     }     def formatProperties(Map properties){         def result = properties.collect { property, value ->                [type: property, time: value]             }         return result     } def updateProperty(Property property){         eoSvc.updateProperty(property)         return property }     //default string version     def createProperties (ExtendedObjectService eoSvc, ExtendedObject object, Map properties) {         return createProperties(eoSvc, object, properties, PropertyDataType.String)     }     //WARNING: PDT may not work if it's a Date; it hasn't been tested.     def createProperties (ExtendedObjectService eoSvc, ExtendedObject object, Map properties, PropertyDataType PDT) {        // http://groovy.codehaus.org/Operators#Operators-ElvisOperator         PDT = PDT?:PropertyDataType.String         properties.each { k , v ->             def property = new com.axeda.platform.sdk.v1.services.extobject.Property()             def propertytype = object.extendedObjectType.getPropertyTypeByName(k.toString())             if (propertytype == null){                 def newPropertyType = new PropertyType()                 newPropertyType.name = k                 newPropertyType.dataType = PDT                 newPropertyType.extendedObjectType = object.extendedObjectType                 eoSvc.createPropertyType(newPropertyType)                 property.propertyType = newPropertyType             } else { property.propertyType = propertytype }             property.value = (PDT == PropertyDataType.Date && v instanceof Date?v.format("yyyy-MM-ddTHH:mm:ssZ"):v.toString())             eoSvc.createPropertyOnExtendedObject(object, property)         }         object = findExtendedObjectById(object.id)         return object     }     def findExtendedObjectById(Long id){         def criteria = new ExtendedObjectSearchCriteria()         criteria.id = id         def queryResult = eoSvc.findExtendedObjects(criteria,  -1, -1, null)         return queryResult.first() }     def findObjectByNextPropertyValue (ExtendedObject object, Map criteria, PropertySearchCriteria psCriteria){        Property prop = object.getPropertyByName(criteria.incrementName)        def incrementedProp = prop.value.toInteger() + criteria.incrementValue.toInteger()            psCriteria.setPropertyExpression(            PropertyExpressionFactory.and(                     psCriteria.propertyExpression,                     PropertyExpressionFactory.eq(criteria.incrementName, incrementedProp.toString())                    )             )        def eoCriteria = new ExtendedObjectSearchCriteria()         eoCriteria.extendedObjectClassName = "com.axeda.drm.sdk.user.User"         eoCriteria.internalObjectId = object.internalObjectId         eoCriteria.propertySearchCriteria = psCriteria        def queryResult = eoSvc.findExtendedObjects(eoCriteria,  -1, -1, null)        return queryResult.first()     }     def advancedPropertySearch (Map searchcriteria){           def propCriteria = new PropertySearchCriteria()        propCriteria.setPropertyExpression(            PropertyExpressionFactory.and(                PropertyExpressionFactory.eq("year", searchcriteria.year),                     PropertyExpressionFactory.and(                         PropertyExpressionFactory.eq("month", searchcriteria.month),                                     PropertyExpressionFactory.eq("date", searchcriteria.date)                             )                    )             )         return propCriteria     }   static def extendedObjectToMap(ExtendedObject object) {     Map result = [:]     result.extendedObjectType = object?.extendedObjectType?.className     result.extendedObjectTypeId = object?.extendedObjectTypeId     result.id = object?.id     result.externalClientKey = object?.externalClientKey     result.internalObjectId = object?.internalObjectId     // build up the properties of the ExtendedObject.     result.properties = object?.properties.inject([:]) { target, property ->       target += [(property?.propertyType?.name): castPropertyValueToDefinedType(property)]     }     return result   }       static def extendedObjectTypeToMap(ExtendedObjectType objectType) {         Map result = [:]         result.className = objectType.className         result.id = objectType.id         result.displayName  = objectType.displayName         result.userDefined = objectType.userDefined         result.description = objectType.description         result.properties = objectType.propertyTypes.inject([]) { List list, PropertyType propertyType ->           list << [                   name: propertyType.name,                   id: propertyType.id,                   description: propertyType.description,                   dataType: propertyType.dataType.toString(),                   extendedObjectType: propertyType.extendedObjectType.className           ]         }         return result       }       private static def castPropertyValueToDefinedType(Property property) {         switch(property.propertyType.dataType) {           case PropertyDataType.Boolean:             return property.value as Boolean           case PropertyDataType.Date:             Calendar calendar = javax.xml.bind.DatatypeConverter.parseDateTime(property.value)             return calendar.getTime()           case PropertyDataType.Double:             return property.value as Double           case PropertyDataType.Integer:             return property.value as Long           case PropertyDataType.String:           default:             return property.value         }       }       static def removeExtendedObject(Long itemId) {         def eoSvc = new ServiceFactory().getExtendedObjectService()         eoSvc.deleteExtendedObject(itemId)       }       def removeUserExtendedObjects(User user) {         def queryresult = fetchUserExtendedObjects(user)         queryresult.each{                 eoSvc.deleteExtendedObject(it.id)         }       }      ExtendedObjectType findExtendedObjectType(String typeName) {     ExtendedObjectType userObjType = eoSvc.findExtendedObjectTypeByClassname(typeName)     return userObjType   }   ExtendedObjectType findOrCreateExtendedObjectType(String typeName) {     ExtendedObjectType type = findExtendedObjectType(typeName)     if (type == null) {       type = new ExtendedObjectType()       type.className = typeName       type.displayName = typeName       type.description = "Autocreated type for $typeName"       type = eoSvc.createExtendedObjectType(type)     }     return type   }
View full tip
This script will return, in XML format, all models for a particular user. It is designed to be called as a web service, in which case the username parameter will be supplied by the platform from the user authentication information passed in the web service call. You should define this script as a Custom Object of type Action. You can test this script in the Groovy development environment on the platform by explicitly supplying the username parameter (i.e., your email address). import com.axeda.drm.sdk.Context; import com.axeda.drm.sdk.user.User; import com.axeda.drm.sdk.device.*; import com.axeda.drm.sdk.model.*; import com.axeda.common.sdk.jdbc.StringQuery; import java.util.*; import groovy.xml.MarkupBuilder import org.custommonkey.xmlunit.* import com.axeda.common.sdk.id.Identifier; def writer def xml try {   String username = parameters.username   Context ctx = Context.create(username);   ModelFinder mf = new ModelFinder(ctx);   List dList = mf.findAll();   Context.create();   writer = new StringWriter()   xml = new MarkupBuilder(writer)   xml.Response() {     for (d in dList) Model('name': d.getName());   } } catch (Exception ex) {   writer = new StringWriter()   xml = new MarkupBuilder(writer)   xml.Response() {     Fault {       Code('Groovy Exception')       Message(ex.getMessage())       StringWriter sw = new StringWriter();       PrintWriter pw = new PrintWriter(sw);       ex.printStackTrace(pw);       Detail(sw.toString())     }   } } //logger.info(writer.toString()); return ['Content-Type': 'text/xml', 'Content': writer.toString()]
View full tip
This script will return, in XML format, the alarms, location information and some data for a particular asset, identified by serial number. It is designed to be called as a web service, in which case the username parameter will be supplied by the platform from the user authentication information passed in the web service call, and the devicename parameter will be passed as an argument to the call. You can test this script in the Groovy development environment on the platform by explicitly supplying the username and devicename parameters (i.e., your email address and "asset1"). The code below will filter data items and return values for the data item "value1". import com.axeda.drm.sdk.Context; import com.axeda.drm.sdk.device.DeviceFinder; import com.axeda.drm.sdk.device.Device; import com.axeda.drm.sdk.data.AlarmFinder; import com.axeda.drm.sdk.data.Alarm; import com.axeda.drm.sdk.mobilelocation.MobileLocation; import com.axeda.common.sdk.jdbc.StringQuery; import com.axeda.common.sdk.id.Identifier; import groovy.xml.MarkupBuilder; try {   logger.info "parameters: " + parameters   if (!parameters.id) {     throw new Exception("parameters.id required");   }   // operate in the context of the user calling the service   Context ctx = Context.create(parameters.username);   // setup the finders   DeviceFinder df = new DeviceFinder(ctx);   df.id = new Identifier(Long.parseLong(parameters.id));   // find the device and its data   logger.info "Finding asset"   Device device = df.find();   if (!device) {     throw new Exception("Unable to find asset with id "+ parameters.id);   }   AlarmFinder af = new AlarmFinder(ctx);   af.device = device;   // generate the XML   writer = new StringWriter();   xml = new MarkupBuilder(writer);   xml.Alarms() {     af.findAll().each() { Alarm alarm ->       xml.Alarm('id':alarm.id) {         xml.DataItemName(alarm.dataItemName);         xml.DataItemValue(alarm.dataItemValue);         xml.Date(alarm.date.time);         xml.Description(alarm.description);         xml.MobileLocation() {           MobileLocation ml = alarm.mobileLocation;           if (ml) {             xml.BeginTimeStamp(ml.beginTimeStamp);             xml.EndTimeStamp(ml.endTimeStamp);             xml.Lat(ml.lat);             xml.Lng(ml.lng);             xml.Alt(ml.alt);             xml.IsCurrentLocation(ml.isCurrentLocation());           }         }         xml.Name(alarm.name);         xml.Note(alarm.note);         xml.Severity(alarm.severity);         xml.State(alarm.state);       }     }   }   // return the results   return ['Content-Type': 'text/xml', 'Content': writer.toString()] } catch (Exception ex) {   // return the exception   writer = new StringWriter();   xml = new MarkupBuilder(writer);   xml.error() {     faultcode("ErrorType.Exception");     faultstring(ex.message);   }   return ['Content-Type': 'text/xml', 'Content': writer.toString()] }
View full tip
This script will return, in XML format, details of all alarms for a particular asset, identified by serial number. It is designed to be called as a web service, in which case the username parameter will be supplied by the platform from the user authentication information passed in the web service call, and the id parameter will be supplied as an argument to the call. You should define this script as a Custom Object of type Action. You can test this script in the Groovy development environment on the platform by explicitly supplying the username and id parameters (i.e., your email address and "asset1"). import com.axeda.drm.sdk.Context; import com.axeda.drm.sdk.device.DeviceFinder; import com.axeda.drm.sdk.device.Device; import com.axeda.drm.sdk.data.AlarmFinder; import com.axeda.drm.sdk.data.Alarm; import com.axeda.drm.sdk.mobilelocation.MobileLocation; import com.axeda.common.sdk.jdbc.StringQuery; import com.axeda.common.sdk.id.Identifier; import groovy.xml.MarkupBuilder; try {   logger.info "parameters: " + parameters   if (!parameters.id) {     throw new Exception("parameters.id required");   }   // operate in the context of the user calling the service   Context ctx = Context.create(parameters.username);   // setup the finders   DeviceFinder df = new DeviceFinder(ctx);   df.id = new Identifier(Long.parseLong(parameters.id));   // find the device and its data   logger.info "Finding asset"   Device device = df.find();   if (!device) {     throw new Exception("Unable to find asset with id "+ parameters.id);   }   AlarmFinder af = new AlarmFinder(ctx);   af.device = device;   // generate the XML   writer = new StringWriter();   xml = new MarkupBuilder(writer);   xml.Alarms() {     af.findAll().each() { Alarm alarm ->       xml.Alarm('id':alarm.id) {         xml.DataItemName(alarm.dataItemName);         xml.DataItemValue(alarm.dataItemValue);         xml.Date(alarm.date.time);         xml.Description(alarm.description);         xml.MobileLocation() {           MobileLocation ml = alarm.mobileLocation;           if (ml) {             xml.BeginTimeStamp(ml.beginTimeStamp);             xml.EndTimeStamp(ml.endTimeStamp);             xml.Lat(ml.lat);             xml.Lng(ml.lng);             xml.Alt(ml.alt);             xml.IsCurrentLocation(ml.isCurrentLocation());           }         }         xml.Name(alarm.name);         xml.Note(alarm.note);         xml.Severity(alarm.severity);         xml.State(alarm.state);       }     }   }   // return the results   return ['Content-Type': 'text/xml', 'Content': writer.toString()] } catch (Exception ex) {   // return the exception   writer = new StringWriter();   xml = new MarkupBuilder(writer);   xml.error() {     faultcode("ErrorType.Exception");     faultstring(ex.message);   }   return ['Content-Type': 'text/xml', 'Content': writer.toString()] }
View full tip
This Groovy script is called from an Expression Rule of type Location. For example, in an Expression rule ExecuteCustomObject("SendTweetWithLocation","user","password","Asset is on the move")   calls the script "SendTweetWithLocation" with the parameters in order. The twitterStatus is the text to send to twitter. Use the user/password for an actual twitter account.  Also, the script uses the implicit objects context and mobileLocation. Parameters Variable Name      Display Name twitterUser                twitterUser twitterPassword      twitterPassword twitterStatus            twitterStatus import groovyx.net.http.RESTClient import static groovyx.net.http.ContentType.* import com.axeda.drm.sdk.geofence.Geofence twitter = new RESTClient('http://twitter.com/statuses/') twitter.auth.basic parameters.twitterUser, parameters.twitterPassword twitter.client.params.setBooleanParameter 'http.protocol.expect-continue', false def statusText = "'${parameters.twitterStatus}' for device: ${context.device.serialNumber} on ${new Date()}" resp = twitter.post(path: 'update.xml',         requestContentType: URLENC,         body: [status: statusText, lat: mobileLocation.lat, long: mobileLocation.lng]) logger.info resp.status logger.info "Posted update $statusText"  
View full tip
This Groovy script is called from an ExpressionRule of type Alarm. For example, in an Expression rule IF: Alarm.severity > 500 THEN: ExecuteCustomObject("SMSMe", "[numberToSMS]")    calls the script "SMSMe" with the parameter phoneNumber.  The ExecuteCustomObject provides a way to call from this simple two-line business rule into a modern programming environment with access to the complete Platform SDK, as well as all the features of the Groovy language.  In Groovy, it's straightforward to use the built-in httpclient library to POST an HTTP request to Twilio to send an SMS to the specified cellphone number. Groovy Scripts The groovy script named SMSMe is executed by the Expression Rule above and connects to the Twilio Server passing in a list of parameters. When you register with Twilio, you will be given an ACCOUNT SID (apiID) and an AUTH TOKEN (apiPass). These two strings need to be in the Groovy Script below: String apiID = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'  String apiPass = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'     Parameters Variable Name      Display Name phoneNumber      phoneNumber import org.apache.commons.httpclient.Credentials import org.apache.commons.httpclient.HostConfiguration import org.apache.commons.httpclient.HttpClient import org.apache.commons.httpclient.UsernamePasswordCredentials import org.apache.commons.httpclient.auth.AuthScope import org.apache.commons.httpclient.methods.GetMethod import org.apache.commons.httpclient.methods.PostMethod import org.apache.commons.httpclient.NameValuePair //logger.info "Calling ${parameters.phoneNumber}" String twilioHost = 'api.twilio.com' String apiID = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' String aipPass = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' HostConfiguration hc = new HostConfiguration() hc.setHost(twilioHost, 443, "https") def url = "/2008-08-01/Accounts/$apiID/SMS/Messages" def client = new HttpClient() Credentials defaultcreds = new UsernamePasswordCredentials(apiID, aipPass) client.getState().setCredentials(null, null, defaultcreds) PostMethod post = new PostMethod(url); post.addParameter 'IfMachine', 'Continue' post.addParameter 'Method', 'POST' post.addParameter 'From', '[YourNumber]' post.addParameter 'To', parameters.phoneNumber post.addParameter 'Body', 'This is an SMS from Axeda' client.executeMethod(hc, post); //logger.info message = "Status:" + post.getStatusText() //logger.info post.getResponseBodyAsString() post.releaseConnection();   
View full tip
When an Expression Rule of type MobileLocation calls a Groovy script, the script is provided with the implicit object mobileLocation.  This example shows how the mobileLocation object can be used. This Expression Rule calls the Groovy script 'getAddress' to retrieve the location and translate it into a street address: Type:  MobileLocation IF:      some condition e.g. true THEN:  SetDataItem("location", str(ExecuteCustomObject("getAddress"))) The 'getAddress' script uses the mobileLocation object to retrieve the asset's reported location, and then calls a REST service to translate a given latitude and longitude to a street address.  The street address is returned. import groovyx.net.http.RESTClient String rmdHostname =  "http://ws.geonames.org"; if (mobileLocation != null) { rmd = new RESTClient(rmdHostname); try {       def resp = rmd.get( path: 'findNearestAddress',                       query:[lat:mobileLocation.lat , lng:mobileLocation.lng] )        streetnum = resp.data.address.streetNumber.text()        street = resp.data.address.street.text()        town = resp.data.address.adminName2.text()        state = resp.data.address.adminCode1.text()        postalcode = resp.data.address.postalcode.text()            return streetnum + " " + street + " " +  town + " " + state + " " + postalcode } catch (groovyx.net.http.HttpResponseException e) {     e.printStackTrace();     } }
View full tip
When an Expression Rule of type File calls a Groovy script, the script is provided with the implicit object compressedFile.  This example shows how the compressedFile object can be used. This Expression Rule uses the Groovy script 'LastLine' to return the last line of the file, and sets the dataItem 'lastLine' to the returned value: Type:  File IF:      some condition e.g. File.hint=="hint" THEN:  SetDataItem("lastline", str(ExecuteCustomObject("LastLine"))) The LastLine script uses the implicit object 'compressedFile': import com.axeda.drm.sdk.scm.CompressedFile if (compressedFile != null) {     File file = compressedFile.getFiles()[0].extractFile()     def result =  file.eachLine { line ->         return line     } }
View full tip
When an Expression Rule of type Data calls a Groovy script, the script is provided with the implicit object dataItems.  This example shows how the dataItems object can be used.to get the dataitem information (value, name, type and update time) import com.axeda.drm.sdk.data.* import com.axeda.drm.sdk.device.DataItem try {         def deviceName = context.device.name         // implicit object dataItems passes a list of dataItem objects         def dataItemsList = dataItems         for(dio in dataItemsList) {                logger.info("Checking " + dio.name + " Value: " + dio.value)                if(dio.name == "updateTime") {                        logger.info("Found: " + dio.name + " Value: " + dio.value + " Type: " +    dio.perceptType + " Last Updated: " + new Date(dio.timeInMillis)) // perceptType = analog, digital or string                }         } } catch (Exception e) {         logger.error e.message }
View full tip
This groovy script will return a list of DeviceGroups based off a given Asset's serialnumber. import com.axeda.drm.sdk.device.*; import com.axeda.drm.sdk.Context; Context sysContext = Context.create(); DeviceGroupFinder dgf = new DeviceGroupFinder(sysContext); DeviceFinder devFinder = new DeviceFinder(sysContext); devFinder.setSerialNumber("[your serialnumber here]"); Device myDevice= devFinder.find(); dgf.setDeviceId(myDevice.getId()); List<DeviceGroup> allGroups = dgf.findAll(); allGroups.each { group -> logger.debug(group.getName()); }
View full tip
This Groovy script gets the weather forecast for a given lat/long by calling an external web service. Use in an Expression rule like this: If: something Then: SetDataItem ("precipitation", round(ExecuteCustomObject ("GetPrecipitation", location) )) This sets the dataitem "precipitation" to the value returned by this script. Parameters: Variable Name               Display Name location                         localtion (lat, lon) import org.apache.commons.httpclient.methods.* import org.apache.commons.httpclient.* import java.text.SimpleDateFormat def location = parameters.location.toString() def locparts = location.split(',') def lat = locparts[0] def lon = locparts[1] def hostname = " www.weather.gov" def url = "/forecasts/xml/sample_products/browser_interface/ndfdXMLclient.php" String ndfdElement = "pop12" // see http://www.weather.gov/forecasts/xml/docs/elementInputNames.php def perceptTimeFormat = new SimpleDateFormat ("yyyy-MM-dd'T'HH:mm:ss"); def cal = Calendar.getInstance(); Date startDate = cal.getTime() cal.add(Calendar.HOUR,12) Date endDate = cal.getTime() def client = new HttpClient () HostConfiguration host = client.getHostConfiguration() host.setHost(hostname, 80, "http") GetMethod get = new GetMethod (url) NameValuePair [] params = new NameValuePair [6] params[0] = new NameValuePair ("lat", lat); params[1] = new NameValuePair ("lon", lon); params[2] = new NameValuePair ("product", 'time-series'); params[3] = new NameValuePair ("begin", perceptTimeFormat.format(startDate)); params[4] = new NameValuePair ("end", perceptTimeFormat.format(endDate)); params[5] = new NameValuePair (ndfdElement, ndfdElement); get.setQueryString(params) client.executeMethod(host, get); message = "Status:" + get.getStatusText() content = get.getResponseBodyAsString() get.releaseConnection() // parse result XML and compute average def dwml = new XmlSlurper ().parseText(content) readings = dwml.data.parameters."probability-of-precipitation".value.collect { Integer.parseInt(it.toString()) } average = readings.sum() / readings.size() //logger.info "Expected precipitation for $location is $readings" return readings[0]
View full tip
This Groovy script takes any dataitem values and writes them to properties of the same name - if they exist. The rule to call this script needs to be a data trigger such as: If: some condition Then: ExecuteCustomObject("CopyParameters") The script uses the default context that contains an asset (device) and the default parameter dataItems that contains the current reported dataitems (from an agent) import com.axeda.drm.sdk.user.User import com.axeda.drm.sdk.data.DataValue import groovy.lang.PropertyValue import com.axeda.drm.sdk.device.DevicePropertyFinder import com.axeda.drm.sdk.device.Property import com.axeda.drm.sdk.device.PropertyType import com.axeda.drm.sdk.device.DeviceProperty logger.info "Executing groovy script for device: " + context?.device?.serialNumber if (dataItems != null) {   logger.info "** Data Items **"   // show data item values   dataItems?.each {di ->     logger.info "dataitem: ${di.name} = ${di.value} = ${di.timestamp}" }   def dataItemMap = [:]   dataItems.each{ dataItemMap[it.name] = it }   DevicePropertyFinder dpf = new DevicePropertyFinder (context.context)   dpf.type = PropertyType.DEVICE_TYPE   dpf.id = context.device.id   DeviceProperty dp = dpf.findOne()   List<Property> props = dp.getProperties()   props.each {Property prop->     if (dataItemMap.containsKey(prop.name)) {       prop.value = dataItemMap[prop.name].value?.toString()       //logger.info "Setting ${prop.name} to ${dataItemMap[prop.name].value?.toString()}"     }   }   dp.store() }
View full tip
When an Expression Rule of type Alarm, AlarmExtendedDataChange, AlarmSeverityChange or AlarmStateChange calls a Groovy script, the script is provided with the implicit object alarm.  This example shows how the alarm object can be used. This Expression Rule uses the CountAlarmsSinceHours Groovy script to check the number of alarms in the past number of hours, and escalate the alarm if more than three alarms have occurred: IF    ExecuteCustomObject("CountAlarmsSinceHours", 1) < 3 THEN SetAlarmState("ACKNOWLEDGED", "Less than three alarms in the past hour") ELSE  SetAlarmState("ESCALATED", "THREE OR MORE alarms in the past hour") Here is the definition of the CountAlarmsSinceHours Groovy script.  The script uses the parameter 'hours' passed from the expression rule and the implicit object 'alarm'.  It returns the number of times the current alarm has occurred within 'hours' hours. import com.axeda.drm.sdk.Context import com.axeda.drm.sdk.data.HistoricalAlarmFinder import java.util.Calendar import com.axeda.common.sdk.jdbc.DateQuery // get Date object for an hour ago Calendar cal = Calendar.getInstance() cal.add(Calendar.HOUR, -parameters.hours.toInteger()) Date sinceTime = cal.getTime() HistoricalAlarmFinder findAlarms = new HistoricalAlarmFinder (Context.create()) findAlarms.device = alarm.device findAlarms.setAlarmName(alarm.name) findAlarms.date = DateQuery.after(sinceTime) List matchingAlarms = findAlarms.findAll()
View full tip
This code snippet shows how to add an existing Device to an existing DeviceGroup using a custom Groovy script executed by the Scripto web service. To call the script create a URL of the following form: http://<HOST>/services/v1/rest/Scripto/execute/addDeviceToDeviceGroup?us... NOTE: Text in angled brackets (< >) indicates a variable. Alternatively, this script can be called by an Expression Rule using the following form: If: Registration.first Then: ExecuteCustomObject("addDeviceToDeviceGroup","<ASSET_ID>","<GROUP_NAME>") It is worth noting that it is important when creating the Groovy script that the parameters be created in the order of the parameter list. import net.sf.json.JSONObject import com.axeda.drm.sdk.device.DeviceGroupFinder import com.axeda.drm.sdk.device.DeviceGroup import com.axeda.drm.sdk.Context import com.axeda.common.sdk.id.Identifier import com.axeda.drm.sdk.device.DeviceFinder def response = [:], status try {   if (parameters.assetId == null) { throw new IllegalArgumentException("parameter 'assetId' was not provided.")}   if (parameters.groupName == null) { throw new IllegalArgumentException("parameter 'groupName was not provided.")}   final def CONTEXT = Context.create(parameters.username)   def dgf = new DeviceGroupFinder(CONTEXT)   dgf.setName(parameters.groupName)   def group = dgf.find()   if (group == null) {     logger.error "could not retrieve group with name of '${parameters.groupName}'"     throw new Exception("could not retrieve group with id of '${parameters.groupName}'")   }   def df = new DeviceFinder(CONTEXT)   df.setId(new Identifier(parameters.assetId))   def device = df.find()   if (device == null) {     logger.error "could not retrieve asset with id of '${parameters.assetId}'"     throw new Exception("could not retrieve asset with id of '${parameters.assetId}'")   }   group.addDevice(device)   group.store()   // do a check to make sure the device is associated with the group.   group = dgf.find()   def devices = group.getDevices()   status = devices.contains(device) ? "success" : "failure"   // prepare the response.   response = [parameters: parameters, status: status] } catch (def e) {   logger.error e.getMessage()   response = [faultcode: e.getCause(), faultstring: e.getMessage()] } return ['Content-Type': 'application/json', 'Content': JSONObject.fromObject(response).toString(2)];
View full tip
This groovy script will return a list of users based off a given UserGroup and allows for filtering by username. import com.axeda.drm.sdk.Context import groovyx.net.http.HTTPBuilder import static groovyx.net.http.ContentType.* import static groovyx.net.http.Method.* import net.sf.json.JSONObject import groovy.json.* import com.axeda.drm.sdk.data.* import com.axeda.drm.sdk.device.* import com.axeda.drm.sdk.user.UserFinder import com.axeda.drm.sdk.user.User import com.axeda.drm.sdk.user.UserGroupFinder //-------------------------------------------------------------------------------------------------------------------- // Example of getting Users from a User Group and filtering by username // //-------------------------------------------------------------------------------------------------------------------- def response = [:] def result = [] try {     final def CONTEXT = Context.create(parameters.username)       UserFinder uFinder = new UserFinder(CONTEXT)     UserGroupFinder ugFinder = new UserGroupFinder(CONTEXT)     List userGroups = getUserGroupsList(ugFinder, "*Demo*")     List SmithsInDemoGroup = userGroups.collect{ usergroup ->         usergroup.getUsers().findResults{ user ->             if (user.username =~ /Smith/){                                      user                                      }                      }     }.flatten()     SmithsInDemoGroup.each{ u ->         result << u.fullName     }   response = [     result: [             items: result     ]   ] } catch (Exception e) {     def m = ""     e.message.each { ex -> m += ex }     response = [                 faultcode: 'Groovy Exception',                 faultstring: m             ]; } return ['Content-Type': 'application/json', 'Content': JSONObject.fromObject(response).toString(2)];   def getUserGroupsList(UserGroupFinder ugFinder, String name){     ugFinder.setName(StringQuery.like(name))     def userGroup = ugFinder.findOne()     List userGroups = new ArrayList();     userGroups.add(userGroup);     return userGroups }
View full tip
This script dumps all the alarms for a model or asset to JSON. Parameters (one or the other must be provided): modelName - (OPTIONAL) String - name of the model assetId - (OPTIONAL) String - id of the asset import com.axeda.common.sdk.id.Identifier import com.axeda.drm.sdk.Context import com.axeda.drm.sdk.audit.AuditCategory import com.axeda.drm.sdk.audit.AuditMessage import com.axeda.drm.sdk.scripto.Request import groovy.json.* import net.sf.json.JSONObject import java.net.URLDecoder import static com.axeda.sdk.v2.dsl.Bridges.* import com.axeda.services.v2.CustomObjectCriteria import com.axeda.services.v2.CustomObjectType import com.axeda.services.v2.CustomObject import com.axeda.services.v2.ExecutionResult import com.axeda.services.v2.ExtendedMap import com.axeda.drm.sdk.device.Model import com.axeda.drm.sdk.device.ModelFinder import com.axeda.drm.sdk.device.DeviceFinder import com.axeda.drm.sdk.device.Device import com.axeda.services.v2.ModelCriteria import com.axeda.services.v2.ModelType import com.axeda.services.v2.FindModelResult import com.axeda.services.v2.AssetCriteria import com.axeda.services.v2.FindAssetResult import com.axeda.services.v2.AlarmCriteria import com.axeda.sdk.v2.bridge.MobileLocationBridge import com.axeda.drm.sdk.mobilelocation.MobileLocationFinder import com.axeda.drm.sdk.mobilelocation.CurrentMobileLocationFinder import com.axeda.drm.sdk.mobilelocation.MobileLocation import com.axeda.drm.sdk.data.AlarmState import com.axeda.drm.sdk.data.AlarmFinder import com.axeda.drm.sdk.data.Alarm import com.axeda.platform.sdk.v1.services.ServiceFactory import com.axeda.drm.sdk.data.CurrentDataFinder import com.axeda.drm.sdk.device.DataItem import com.axeda.drm.sdk.data.HistoricalDataFinder import com.axeda.drm.sdk.data.DataValue import com.axeda.drm.sdk.data.DataValueList import com.axeda.platform.sdk.v1.services.extobject.ExtendedObjectSearchCriteria import com.axeda.common.date.DateRange import com.axeda.common.date.ExplicitDateRange /** * GetModel_Or_Asset_Alarms.groovy * ----------------------- * * Returns assets with organizations, alarms, and current mobile location. * * @params * modelName (OPTIONAL) Str - the name of the model to retrieve assets * assetId (OPTIONAL) Long - the id of the asset - one of the two is REQUIRED * * * @author sara streeter <sstreeter@axeda.com> * */ /** * 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 = "GetModel_Or_Asset_Alarms.groovy" def root = [:] def timings = [:] timings.dataItemList = 0 timings.currentdata = 0 timings.histdata = 0 timings.wholescript = 0 timings.alarms = 0 timings.loop = 0 timings.filter = 0 timings.devices = 0 timings.geocode = 0 wholestart = System.currentTimeMillis() final def Context CONTEXT = Context.getSDKContext() def deviceList List<Device> devices try {     /* BUSINESS LOGIC GOES HERE */       def modelName = Request.parameters.modelName     def assetId     def alarms     AlarmFinder alarmFinder = new AlarmFinder(CONTEXT)       if (Request.parameters.assetId != null && Request.parameters.assetId != ""){         assetId = Request.parameters.assetId         DeviceFinder deviceFinder = new DeviceFinder(CONTEXT, new Identifier(assetId as Long));         def device = deviceFinder.find()         if (device){             alarmFinder.setDevice(device)             modelName = device.model.name         }     }     else if (modelName){               try{         modelName = new URLDecoder().decode(modelName)         }         catch(e){ logger.info(e.localizedMessage) }         if (modelName != null && modelName !=""){             ModelFinder modelFinder = new ModelFinder(CONTEXT)             modelFinder.setName(modelName)             Model model = modelFinder.find()                      if (model){                 modelName = model?.name                 alarmFinder.setModel(model)             }         }      }       alarms = alarmFinder.findAll()     // build the json from the models          root = [              "result": [              "model": modelName,              "assetId": assetId,              "alarms":alarms?.inject([]){ aList, alarm ->                    aList << [                         "deviceId": alarm.device?.id?.value,                        "deviceName": alarm.device.name,                        "deviceSerial": alarm.device.serialNumber,                         "name": alarm.name,                         "id": alarm.id.value,                         "state": alarm.state.name,                         "description": alarm.description,                         "severity": alarm.severity,                         "timestamp": alarm.date.time                    ]                                      aList               }             ]          ]     /* BUSINESS LOGIC ENDS HERE */ } catch (Exception e) {     def errorCode = "123456"     processException(scriptName,json,e,errorCode) } finally {     timings.wholescript = System.currentTimeMillis() - wholestart     root += [params: Request.parameters]     root += [timings: timings] } return ['Content-Type': 'application/json', 'Content': JSONObject.fromObject(root).toString(2)] /* * * ACTIVE CODE ENDS HERE * */ //---------------------------------------------------------------// /* * * HELPER METHODS START BELOW * */ /** * Wrap-up the response in our standard return map * @param contentType The global contentType variable * @param response The contents of the response (String ONLY) */ private def createReturnMap(String contentType, String response) {     return ["Content-Type": contentType,"Content":response] } /*     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, String code) {     // 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 {             errorCode   "${code}"             message     "[${scriptName}]: " + e.getMessage()             timestamp   "${System.currentTimeMillis()}"         }     }     return json } /*     Log a message. This will log a message and add it to info String     @param logger The injected logger     @param scriptName The name of the script being executed     @param info The infoString to append to     @param message The actual message to log */ private def logMessage(def logger, String scriptName, StringBuilder info, String message) {     logger.info(message)     info.append(message+"\n") } /*     Audit a message. This will store a message in the Audit log, based on the supplied category.     @param category The category for this audit message. One of: "scripting", "network", "device" or "data". Anything not recognized will be treated as "data".     @param message The actual message to audit     @param assetId If supplied, will associate the audit message with the asset at this ID */ private def auditMessage(String category, String message, String assetId) {     AuditCategory auditCategory = null     switch (category) {         case "scripting":             auditCategory = AuditCategory.SCRIPTING;             break;         case "network":             auditCategory = AuditCategory.NETWORK;             break;         case "device":             auditCategory = AuditCategory.DEVICE_COMMUNICATION;             break;         default:             auditCategory = AuditCategory.DATA_MANAGEMENT;             break;     }     if (assetId == null) {         new AuditMessage(Context.create(),"com.axeda.drm.rules.functions.AuditLogAction",auditCategory,[message]).store()     } else {         new AuditMessage(Context.create(),"com.axeda.drm.rules.functions.AuditLogAction",auditCategory,[message],new Identifier(Long.valueOf(assetId))).store()     } } def findOrCreateExtendedMap(String name){        // should take a name of Extended Map and output an object of type Extended Map, if it outputs null we throw an Exception        def outcome = [:]        outcome.extendedMap        ExtendedMap extendedMap = extendedMapBridge.find(name)        if (!extendedMap){             extendedMap = new ExtendedMap(name: name)            extendedMapBridge.create(extendedMap)        }        if (extendedMap) {         ExecutionResult result = new ExecutionResult()         result.setSuccessful(true)         result.setTotalCount(1)         outcome.result = result         outcome.extendedMap = extendedMap        }        else {            ExecutionResult result = new ExecutionResult()            result.setSuccessful(false)            result.setTotalCount(1)            outcome.result = result        }         return outcome    }    def retrieveModels(){       // retrieves the list populated by a separate script        def outcome = [:]        outcome.modelList        ModelCriteria modelCriteria = new ModelCriteria()        modelCriteria.type = ModelType.STANDALONE        FindModelResult modelResult = modelBridge.find(modelCriteria)        if (modelResult.models.size() > 0){         ExecutionResult result = new ExecutionResult()         result.setSuccessful(true)         result.setTotalCount(1)         outcome.result = result         outcome.modelList = modelResult.models        }        else {            ExecutionResult result = new ExecutionResult()            result.setSuccessful(false)            result.setTotalCount(1)            outcome.result = result        }         return outcome    }    def returnModelsWithAssets(List<com.axeda.services.v2.Model> modelList){        def outcome = [:]        outcome.modelList        outcome.message        if (!modelList || modelList?.size() ==0){            ExecutionResult result = new ExecutionResult()           result.setSuccessful(false)           result.setTotalCount(1)           outcome.result = result           outcome.message = "returnModelsWithAssets: Model list was not supplied or was of size zero."           return outcome        }        DeviceFinder deviceFinder = new DeviceFinder(CONTEXT)        ModelFinder modelFinder = new ModelFinder(CONTEXT)        List<com.axeda.drm.sdk.device.Model> sortedList = modelList.inject([]){ target, amodel ->             modelFinder.setName(amodel.modelNumber)            com.axeda.drm.sdk.device.Model bmodel = modelFinder.find()            deviceFinder.setModel(bmodel)            def numAssets = deviceFinder.findAll().size()            if (numAssets > 0 ){                   target << bmodel             }             target        }.sort{ amodel, bmodel ->  amodel.name <=> bmodel.name}        if (sortedList.size() > 0){         ExecutionResult result = new ExecutionResult()         result.setSuccessful(true)         result.setTotalCount(1)         outcome.result = result         outcome.modelList = sortedList        }        else {           ExecutionResult result = new ExecutionResult()           result.setSuccessful(false)           result.setTotalCount(1)           outcome.result = result       }         return outcome    }     def addMapEntry(String mapName, String key, String value){        def outcome = [:]         outcome.key         outcome.value         ExecutionResult result = extendedMapBridge.append(mapName, key, value)         outcome.result = result         if (result.successful){             outcome.key = key             outcome.value = value         }         return outcome    }
View full tip