IoT & Connectivity Tips | ThingWorx & Kepware PTC
Recently active
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 ItemsCSV file to Location OrganizationScript Name: CSV file to Data ItemsDescription: Executed from an expression rule with file hint "datainsert", takes a CSV file and adds data based on values.Parameters:OPTIONAL - only needed for debuggingmodelName - (OPTIONAL) Str - name of the modelserialNumber - (OPTIONAL) Str - name of the serial numberimport com.axeda.drm.sdk.Contextimport com.axeda.drm.sdk.device.DeviceFinderimport com.axeda.drm.sdk.device.ModelFinderimport com.axeda.drm.sdk.device.DataItemFinderimport com.axeda.drm.sdk.device.Data
This article explains how to monitor concurrent user logins on the Axeda Platform. Its going to do this by creating an asset to monitor the solution. This asset will have a dataitem that tracks the users logged in. You can use this dataitem to trend usage during the day, or calculate the max per day. Or you could alarm if the number of users goes over some limit.The following needs to be created on your Platform :A model named “Monitor”, containing an analog DataItem named “userlogins” and an asset named “Metrics” This asset will receive the values.Expression Rule:Create two expression rules that update the number of users, triggered on user login or logout:Name : UserLoginMonitorType: Userlogin and Userlogout (two rules required)IF: trueTHEN: ExecuteCustomObject(“getUserLogins”, User.total)This calls a script and passes in User.total = The total number of users that are logged into the system (Concurrently).Groovy Script (Custom Object) Now, the next step
This is a collection of methods for working with ExtendedObjects based on the functions used in Fleetster.import com.axeda.drm.sdk.device.Deviceimport com.axeda.platform.sdk.v1.services.ServiceFactoryimport com.axeda.platform.sdk.v1.services.extobject.ExtendedObjectSearchCriteriaimport com.axeda.platform.sdk.v1.services.extobject.PropertySearchCriteriaimport com.axeda.platform.sdk.v1.services.extobject.expression.PropertyExpressionFactoryimport com.axeda.drm.sdk.data.CurrentDataFinderimport com.axeda.platform.sdk.v1.services.extobject.ExtendedObjectimport com.axeda.platform.sdk.v1.services.extobject.Propertyimport java.text.DecimalFormatimport com.axeda.drm.sdk.Contextimport com.axeda.drm.sdk.device.DeviceDataFinderimport com.axeda.drm.sdk.user.Userimport com.axeda.platform.sdk.v1.services.extobject.ExtendedObjectServiceimport com.axeda.platform.sdk.v1.services.extobject.PropertyTypeimport com.axeda.platform.sdk.v1.services.extobject.PropertyDataTypeimport com.axeda.platform.sdk.v1.ser
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.MarkupBuilderimport org.custommonkey.xmlunit.*import com.axeda.common.sdk.id.Identifier;def writerdef xmltry { String username = parameters.username Context ctx = Context.create(username); ModelFinder mf = new ModelFinder(ctx); List dList = mf.findAll(); Context.create();&
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
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
This Groovy script is called from an Expression Rule of type Location. For example, in an Expression ruleExecuteCustomObject("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.ParametersVariable Name Display NametwitterUser twitterUsertwitterPassword twitterPasswordtwitterStatus twitterStatus import groovyx.net.http.RESTClientimport static groovyx.net.http.ContentType.*import com.axeda.drm.sdk.geofence.Geofencetwitter = new RESTClient('http://twitter.com/statuses/')twitter.auth.basic parameters.twitterUser,
This Groovy script is called from an ExpressionRule of type Alarm. For example, in an Expression ruleIF: Alarm.severity > 500THEN: 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 ScriptsThe 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'
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: MobileLocationIF: some condition e.g. trueTHEN: 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.RESTClientString rmdHostname = "http://ws.geonames.org";if (mobileLocation != null){rmd = new RESTClient(rmdHostname);try { def resp = rmd.get( path: 'findNearestAddress', &nb
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: FileIF: 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.CompressedFileif (compressedFile != null) { File file = compressedFile.getFiles()[0].extractFile() def result = file.eachLine { line -> return line }}
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.DataItemtry { 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") {
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());}
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: somethingThen: 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.SimpleDateFormatdef 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
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 conditionThen: 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.Userimport com.axeda.drm.sdk.data.DataValueimport groovy.lang.PropertyValueimport com.axeda.drm.sdk.device.DevicePropertyFinderimport com.axeda.drm.sdk.device.Propertyimport com.axeda.drm.sdk.device.PropertyTypeimport com.axeda.drm.sdk.device.DevicePropertylogger.info "Executing groovy script for device: " + context?.device?.serialNumberif (dataItems != null){ logger.info "** Data Items **" // show data item values dataItems?.each {di -> logger.info "dataitem: ${di.name} = ${di.value} = ${di.timestamp}"} def dataItemMap =
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.firstThen: 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.JSONObjectimport com.axeda.drm.sdk.device.DeviceGroupFinderimport com.axeda.drm.sdk.device.DeviceGroupimport com.axeda.drm.sdk.Contextimport com.axeda.common.sdk.id.Identifierimport com.axeda.drm.sdk.device.DeviceFinderdef response = [:], statustry { if (parameters.assetId == null) { throw
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.Contextimport groovyx.net.http.HTTPBuilderimport static groovyx.net.http.ContentType.*import static groovyx.net.http.Method.*import net.sf.json.JSONObjectimport groovy.json.*import com.axeda.drm.sdk.data.*import com.axeda.drm.sdk.device.*import com.axeda.drm.sdk.user.UserFinderimport com.axeda.drm.sdk.user.Userimport 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) &nb
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 modelassetId - (OPTIONAL) String - id of the assetimport com.axeda.common.sdk.id.Identifierimport com.axeda.drm.sdk.Contextimport com.axeda.drm.sdk.audit.AuditCategoryimport com.axeda.drm.sdk.audit.AuditMessageimport com.axeda.drm.sdk.scripto.Requestimport groovy.json.*import net.sf.json.JSONObjectimport java.net.URLDecoderimport static com.axeda.sdk.v2.dsl.Bridges.*import com.axeda.services.v2.CustomObjectCriteriaimport com.axeda.services.v2.CustomObjectTypeimport com.axeda.services.v2.CustomObjectimport com.axeda.services.v2.ExecutionResultimport com.axeda.services.v2.ExtendedMapimport com.axeda.drm.sdk.device.Modelimport com.axeda.drm.sdk.device.ModelFinderimport com.axeda.drm.sdk.device.DeviceFinderimport com.axeda.drm.sdk.device.Deviceimport com.axeda.services.v2.ModelCriteriaimport com.axeda.services.v2.ModelTypeimport com.axeda.
This script finds an existing Expression Rule and applies it to an asset (via asset includes).Parameters:model - model nameserial - serial numberexprRuleName - name of the Expression Ruleimport static com.axeda.sdk.v2.dsl.Bridges.*import net.sf.json.JSONObjectimport com.axeda.drm.sdk.scripto.Requestimport com.axeda.services.v2.Assetimport com.axeda.services.v2.AssetReferenceimport com.axeda.services.v2.AssetCollectionimport com.axeda.services.v2.AssetCriteriaimport com.axeda.services.v2.ExpressionRuleimport com.axeda.services.v2.ExpressionRuleCriteria/** ApplyExpRuleToAsset.groovy** Finds an existing Expression Rule and includes an asset into it.** @param model - (REQ):Str model of the asset.* @param serial - (REQ):Str serial number of the asset.* @param exprRuleName - (REQ):Str name of the Expression Rule.** @author Sara Stre
import com.axeda.drm.sdk.Contextimport com.axeda.drm.sdk.device.ModelFinderimport com.axeda.drm.sdk.device.Modelimport com.axeda.drm.sdk.device.DeviceFinderimport com.axeda.drm.sdk.data.CurrentDataFinderimport com.axeda.drm.sdk.device.Deviceimport com.axeda.drm.sdk.data.HistoricalDataFinderimport net.sf.json.JSONObject/** DataItemEachDevice.groovy** Find the current data item and historical data items for all assets in a given model.** @param model_name - (REQ):Str name of the model.* @param data_item_name - (REQ):Str name of the data item to query on.* @param from_time - (REQ):Long millisecond timestamp to begin query from.* @param to_time - (REQ):Long millisecond timestamp to end query at.** @note from_time and to_time should be provided because it limits the query size.*
Email an attachment using bytes from a FileInfoParameters:fileId - the identifier to a FileInfo that has been previously uploaded to the FileStorefilename - the name of the attachmenttoaddress - the email address to send tofromaddress - the email address to send fromimport com.axeda.drm.util.Emailer;import com.axeda.drm.sdk.contact.Emailimport javax.mail.internet.AddressException;import javax.mail.internet.InternetAddress;import static com.axeda.sdk.v2.dsl.Bridges.*import com.axeda.services.v2.FileInfoCriteriaimport org.apache.commons.io.IOUtilsimport java.security.MessageDigesttry { String fromaddress = parameters.fromaddress String toaddress = parameters.toaddress def fileId = parameters.fileId def filename = parameters.filename String subject = "Axeda Test Attachment" String body = "<html><head/><body><p style='background:blue;'>This email has an attachment and a blue background.</p></body></html>" d
This tutorial applies to Axeda version 6.1.6+, with sections applicable to 6.5+ (indicated below)Custom objects (or Groovy scripts) are the backbone of Axeda custom applications. As the developer, you decide what content type to give the data returned by the script.What this tutorial covers?This tutorial provides examples of outputting data in different formats from Groovy scripts and consuming that data via Javascript using the jQuery framework. While Javascript and jQuery are preferred by the Axeda Innovation team, any front end technology that can consume web services can be used to build applications on the Axeda Machine Cloud. On the same note, the formats discussed in this article are only a few examples of the wide variety of content types that Groovy scripts can output via Scripto. The content types available via Scripto are limited only by their portability over the TCP protocol, a qualification which includes all text-based and downloadable binary mime
Data is NOT free. It is easy to overlook the cost of data collection, but all data incurs some cost when it is collected. Data collection in and of itself does not bring business value. If you don’t know why you’re collecting the data, then you probably won’t use it once you have it.For a wireless product, it is felt in the cost of bytes transferred, which makes for an expensive solution, but happy Telco's. Even for wired installations, data transfer isn’t free. Imagine a supermarket with 20 checkout lanes - with only a 56K DSL line - and the connection is shared with the credit card terminals, so it is important to upload only the necessary data during business hours.For the end user, too much data leads to information clutter. Too much information increases the time necessary to locate and access critical data.All enterprise applications have some associated "Infrastructure Tax", and the Axeda Platform is no exception. This is the cost of maintaining the existing infrastructure, as w
The Axeda Platform has a mature data model that's important to understand when planning to build applications. First, this will introduce the existing objects and how they relate to each other.Axeda Agents can communicateModel – the definition of a type of asset. The model consists of a set of dataitems (its inputs and outputs) and alarms. The platform applies logic to a model, so as assets grow, the system is scalable in terms of management.Asset – or sometimes called Device. An asset has an identifier called a Serial Number which must be unique within its model. Agents report information in terms of the asset. Logic is applied to data and events about that asset.Dataitem – a named reading, such as a sensor or computer value. Dataitems are timestamped values in a sequence. For example, hourly temperatures, or odometer readings, or daily usage statistics. The number of named dataitems is unlimited. Dataitems can be written as well as read, so a value can be sent to an “output”. A datai
Adaptive Machine Messaging ProtocolThe Adaptive Machine Messaging Protocol (AMMP) is a simple, byte-efficient, lightweight messaging protocol used to facilitate Internet of Things (IoT) communications and to build IoT connectivity into your product.Using a RESTful API, AMMP provides a semantic structure for IoT information exchange and leverages HTTPS as the means for sending and receiving messages between an edge device and the Axeda® Machine Cloud®.AMMP uses JavaScript Object Notation (JSON) allowing any device that is capable of making an HTTP transmission to interact with the Axeda Platform. Utilizing a common network transport that is friendly to local network proxies and firewalls, and at the same time using JSON for a compact, human-readable, language-independent, and easily constructed data representation, AMMP simplifies device communication and reduces the work needed to connect to the Axeda Machine Cloud.For complete information about the Adaptive Machine Messaging Protocol,
While working with the Axeda Platform you will come across guard rails that limit sizes, recurrence, and duration of certain actions. When you run into these limitations, it may be an opportunity to re-examine the architecture of your solution and improve efficiency.What this tutorial coversThis tutorial discusses the kinds of limits exist across the Platform, however it does not include the exact values of the limits as these may vary across instances. Skip to the last section on System Configuration to see how to determine the read-only properties of your Axeda Instance. You can also contact your Axeda Support technician to find out more about how these properties are configured.Types of Limits discussed:Rule SniperDomain Object Field Length ConstraintsFile Store LimitsSystem ConfigurationAvoiding Rule Sniper IssuesThere are two ways a rule can be sniped from statistics (recursive rules are done differently) – frequency count and execution time.When a rule is killed
Already have an account? Login
No account yet? Create an account
Enter your E-mail address. We'll send you an e-mail with instructions to reset your password.