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

ThingWorx Navigate is now Windchill Navigate Learn More

IoT & Connectivity Tips

Sort by:
Pushbullet is a lightweight notifications platform and can be a way to explore Alerts and Subscriptions Basically create an an Alert on a property and Subscribe to that Alert Adding Alert to Property Humidity Adding Subscription The PTC-PushBulletHelper is just a generic Thing with a service called PushNotification var json = {     "body": Message,     "title":"Temperature fault",     "type":"note" }; var accessHeader = {     "Access-Token": "o.Hnm2DeiABcmbwuc7FSDmfWjfadiLXx2M" }; var params = {      proxyScheme: undefined /* STRING */,     headers: accessHeader /* JSON */,      ignoreSSLErrors: undefined /* BOOLEAN */,      useNTLM: undefined /* BOOLEAN */,      workstation: undefined /* STRING */,      useProxy: undefined /* BOOLEAN */,      withCookies: undefined /* BOOLEAN */,      proxyHost: undefined /* STRING */,      url: 'https://api.pushbullet.com/v2/pushes' /* STRING */,      content: json /* JSON */,      timeout: undefined /* NUMBER */,      proxyPort: undefined /* INTEGER */,      password: undefined /* STRING */,      domain: undefined /* STRING */,      username: undefined /* STRING */ }; // result: JSON var result = Resources["ContentLoaderFunctions"].PostJSON(params); You can test the Helper PushNotification service Next you can test the subscription
View full tip
Video Author:                     Mohammed Amine Chehaibi Original Post Date:            April 25, 2017 Applicable Releases:        ThingWorx Analytics 52.x to 8.0   Description: In this video, you will learn how to: Execute a “Signals” Job Retrieve the results of the “Signals” Job Execute a “Training Model” Job Retrieve the results of the “Training Model” Job    
View full tip
ThingWorx Manufacturing Apps Setup and Configuration Guide 8.0.1   Note: The ThingWorx Manufacturing Apps Freemium (Express) 8.1 and Dev Kit 8.1 installers will be available in late November 2017. This document contains information on the installation and use of the ThingWorx Manufacturing Apps Freemium (Express) installer 8.0.1 and ThingWorx Manufacturing Apps Dev Kit installer 8.0.1.   The ThingWorx Manufacturing Apps Extensions 8.1.0 have now been released. For documentation on this version, please see these updated documents:   ThingWorx Manufacturing Apps 8.1.0 Documentation ThingWorx Manufacturing Apps 8.1.0 Available for Download!
View full tip
First of all wishing everyone a blessed 2017 So here is a little something that hopefully can be helpful for all you Thingworx developers! This is a 'Remote Monitoring Application Starter' Mainly this is created around Best Practices for Security and provides a lot of powerful Modeling and Mashup techniques. Also has some cool Dashboard techniques Everything is documented in accompanying documents also in the zip (sorry went through a few steps to get this up properly. Install instructions: Thingworx Remote Monitoring Starter Application – Installation Guide Files All files needed are in a Folder called: RemoteMonitoringStarter, this is an Export to ThingworxStorage Extensions Not included, but the application uses the GoogleWidgetsExtension (Google Map) Steps Import Google Map extension. Place RemoteMonitoringStarter folder in the ThingworxStorage exports folder. From Thingworx do an Import from ThingworxStorage – Include Data, Use Default Persistence Provider, do NOT ignore Subsystems. After the import has finished, go to Organizations and open Everyone. In the Organization remove Users from the Everyone organization unit. Go to DataTables and open PTC.RemoteMonitoring.Simulation.DT Go to Services and execute SetSimulationValues Go to the UserManagementSubsystem In the Configuration section add PTC.RemoteMonitoring.Session.TS to the Session. Note: This step may already be done. Note: Screen shots provided at the end. Account Passwords FullAdmin/FullAdmin All other users have a password of: password. NOTE: You may have to Reset your Administrator password using the FullAdmin account. I also recommend changing the passwords after installing.
View full tip
Calling external services from M2M applications is a critical aspect of building end-to-end solutions.  Knowing how to apply network timeouts when connecting to external servers can prevent unexpected and problematic network hang-ups. Let's investigate how to create a safe networking flow using HttpClient, HttpBuilder, and Apache’s FTPClient class. Background Custom Objects called from Expression Rules have a configurable maximum execution time.  This is set by the com.axeda.drm.rules.statistics.rule-time-threshold property.  Without this safeguard in place long running or misbehaved Custom Objects can cause internal processing queues to fill and the server will suffer a performance degradation. In Java (and Groovy) all network calls internally use InputStream.read() to establish the socket connection and to read data from the socket.  It is possible for faulty external servers (such as an FTP server) to hang and not properly respond.  This means that the InputStream.read() method will continuously wait for the server to respond with data, and the server will never respond.  According to the Java spec, InputStream.read() may be uninterruptable while it is waiting for data.  This means that if a Custom Object has exceeded the com.axeda.drm.rules.statistics.rule-time-threshold the Rule Sniper will still not be able to interrupt the Custom Object’s execution if it is waiting on InputStream.read().  Because the Custom Object cannot be stopped, the internal processing queues will eventually fill. Even though InputStream.read() is uninterruptable it is still possible to set timeouts for it to be able to give up on a connection.  Beyond that, we want to make sure that the connection is completely disconnected. Types of Timeouts There are typically two types of timeouts that should be set when making calls over the web: the Connection Timeout and the Socket Timeout.  The Connection Timeout is the maximum amount of time that should be allowed when establishing the bi-directional socket connection between the client and server.  Behind the scenes socket connection involves resolving the domain name of the server to an IP address, and then the server opening a port to connect with the client’s port.  The Socket Timeout is the timeout that limits the amount of time each socket operation is allowed to take.  It limits the amount of time InputStream.read() will listen for a server’s response.  If a server is faulty or overloaded it may take a long time (or forever) to respond to a request.  This timeout limits the amount of time the client will wait for the server to respond. When making any calls from a Custom Object to an external server (either making WebService calls, or FTP transfers), you should always set the Connection Timeout and the Socket Timeout.  Always try to keep the timeouts as reasonably small as possible.  Failure to do so could unexpectedly impact your Axeda server.  Consider a Custom Object that takes an average of 10 seconds to run is called to make an external WebService call once a minute. This will not cause any issues and the  system will be stable.  If the external server suddenly has a performance degredation and now the external WebService call takes over a minute to run, the execution queue will eventually fill, causing performance degradation to the Axeda system.  To protect against this scenario, set the timeouts to limit the call to one minute, and log whenever the time limit is exceeded. Examples Provided below are examples of properly set timeouts and thorough connection management use HttpClient, HttpBuilder, and FTPClient.  All of these examples assume they are being executed from Custom Objects. By default, set the Connection Timeout to 10 seconds.  In normal circumstances, connections should not take more then 10 seconds.  If they are exceeding this time there is a good chance of networking issues between the client and server. The Socket Timeout can vary per use-case.  The examples provided set the Socket Timeout to 30 seconds, which should be sufficient for typical WebService calls and small to medium sized FTP file transfers.  Depending exactly on what is being done, the timout may have to be increased.  If you expect the call to go over 5 minutes please contact Axeda Support to investigate increasing  com.axeda.drm.rules.statistics.rule-time-threshold property (which defaults to 5 minutes). ​HttpClient​ //HttpClient import org.apache.http.client.HttpClient import org.apache.http.impl.client.DefaultHttpClient import org.apache.http.client.methods.HttpGet import org.apache.http.HttpResponse import org.apache.http.params.BasicHttpParams import org.apache.http.params.HttpParams import org.apache.http.params.HttpConnectionParams int TENSECONDS  = 10*1000 int THIRTYSECONDS = 30*1000 final HttpParams httpParams = new BasicHttpParams() //Establishing the connection should take <10 seconds in most circumstances HttpConnectionParams.setConnectionTimeout(httpParams, TENSECONDS) //The data transfer/call should take <30 seconds.  Adjust as necessary if receiving large data sets. HttpConnectionParams.setSoTimeout(httpParams, THIRTYSECONDS) HttpClient hc = new DefaultHttpClient(httpParams) try {   //Simply get the contents of http://www.axeda.com and log it to the Custom Object Log   HttpGet get = new HttpGet("http://www.axeda.com")   HttpResponse response = hc.execute(get)   BufferedReader br = new BufferedReader( new InputStreamReader( response.getEntity().getContent()))   br.readLines().each {     logger.info it   } } finally {   //Make sure to shutdown the connectionManager   hc.getConnectionManager().shutdown() } return true https://gist.github.com/axeda/5189092/raw/2f7b93c5f96ed8f445df4364b885486bc6fa1feb/HttpClientTimeouts.groovy HttpBuilder import groovyx.net.http.HTTPBuilder import static groovyx.net.http.ContentType.* import static groovyx.net.http.Method.* int TENSECONDS  = 10*1000; int THIRTYSECONDS = 30*1000; HTTPBuilder builder = new HTTPBuilder('http://www.axeda.com') //HTTPBuilder has no direct methods to add timeouts.  We have to add them to the HttpParams of the underlying HttpClient builder.getClient().getParams().setParameter("http.connection.timeout", new Integer(TENSECONDS)) builder.getClient().getParams().setParameter("http.socket.timeout", new Integer(THIRTYSECONDS)) try {   //Simply get the contents of http://www.axeda.com and log it to the Custom Object Log   builder.request(GET, TEXT){     response.success = { resp, res ->       res.readLines().each {         logger.info it       }       }   } } finally {   //Make sure to always shut down the HTTPBuilder when you’re done with it   builder.shutdown() } return true https://gist.github.com/axeda/5189102/raw/66bb3a4f4f096681847de1d2d38971e6293c4c6b/HttpBuilderTimeouts.groovy FtpClient Apache’s FTPClient has a third type of timeout, the Default Timeout.  The Default Timeout is a timeout that further ensures that socket timeouts are always used.  Note: Default Timeout does not set a timeout for the .connect() method. import org.apache.commons.net.ftp.* import java.io.InputStream import java.io.ByteArrayInputStream String ftphost = "127.0.0.1" String ftpuser = "test" String ftppwd = "test" int ftpport = 21 String ftpDir = "tmp/FTP" int TENSECONDS  = 10*1000 int THIRTYSECONDS = 30*1000 //Declare FTP client FTPClient ftp = new FTPClient() try {   ftp.setConnectTimeout(TENSECONDS)   ftp.setDefaultTimeout(TENSECONDS)   ftp.connect(ftphost, ftpport)   //30 seconds to log on.  Also 30 seconds to change to working directory.   ftp.setSoTimeout(THIRTYSECONDS)   def reply = ftp.getReplyCode()   if (!FTPReply.isPositiveCompletion(reply))   {     throw new Exception("Unable to connect to FTP server")   }   if (!ftp.login(ftpuser, ftppwd))   {     throw new Exception("Unable to login to FTP server")   }   if (!ftp.changeWorkingDirectory(ftpDir))   {     throw new Exception("Unable to change working directory on FTP server")   }   //Change the timeout here for a large file transfer that will take over 30 seconds   //ftp.setSoTimeout(THIRTYSECONDS);   ftp.setFileType(FTPClient.ASCII_FILE_TYPE)   ftp.enterLocalPassiveMode()   String filetxt = "Some String file content"   InputStream is = new ByteArrayInputStream(filetxt.getBytes('US-ASCII'))   try   {     if (!ftp.storeFile("myFile.txt", is))     {       throw new Exception("Unable to write file to FTP server")     }   } finally   {     //Make sure to always close the inputStream     is.close()   } } catch(Exception e) {   //handle exceptions here by logging or auditing } finally {   //if the IO is timed out or force disconnected, exceptions may be thrown when trying to logout/disconnect   try   {     //10 seconds to log off.  Also 10 seconds to disconnect.     ftp.setSoTimeout(TENSECONDS);     ftp.logout();     //depending on the state of the server the .logout() may throw an exception,     //we want to ensure complete disconnect.   }   catch(Exception innerException)   {       //You potentially just want to log that there was a logout exception.     }   finally   {     //Make sure to always disconnect.  If not, there is a chance you will leave hanging sockects     ftp.disconnect();   } } return true https://gist.github.com/axeda/5189120/raw/83545305a38d03b6a73a80fbf4999be3d6b3e74e/FtpClientConnectionTimeouts.groovy
View full tip
Video Author:                     Mohammed Amine Chehaibi Original Post Date:            December 2, 2016 Applicable Releases:        ThingWorx Analytics 52 to 8.0   Description: In this video, we will be using Postman to: Create a dataset Enter the dataset configuration Upload the CSV data file to the ThingWorx Analytics Server  
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
Applicable Releases: ThingWorx Platform 7.0 to 8.4   Description:   Strategy and tools for Thingworx application backups Backup Terminology and concepts Drivers to define a backup strategy Tips for executing backup in a Thingworx instance: Tomcat, certificates, Configuration and file system data, application specific files, database     Neo4J database mentioned in the session is no longer supported For more information check Best Practices for ThingWorx Backup
View full tip
Finally there is an article which combines all of the available resources on certificate configuration to better enable developers to complete their production-worthy edge devices. Please see the official PTC documentation located here. Please feel free to comment with any questions, comments, or feedback on this! Happy developing!
View full tip
  You’ve seen him before. You’ve heard him before. Fans around the globe can’t get enough of him. He’s…   ...Joe Biron—our CTO of IoT!   Hear Joe share his thoughts on the future of the industrial IoT with ThingWorx in Episode 03 of our “ThingWorx on Air” podcast!   Any questions? Just Ask Kaya.   Stay connected!
View full tip
Updates: App Keys defaults - Now stored in secure keystore - Newly created app keys stored automatically - On upgrated existing app keys are migrated to secure keystore Change the app key default expiration time to 1 day - Changed from 100 years - UI date picker - If date not picked now defaults to 1 day Best Practice: - Carefully consider expiration - Set to desired value at time of creation - Scripts should carefully choose time -Knowledge base article in the works Edge SSL updates C SDK TLS/SSL: C-SDK support for OpenSSL: - Version 1.0.2 that supports tls  1.2. - Tomcat 8 compatible ciphers - EMS will follow soon BYO SSL - Abstraction layer &Documentation - Path to building any SSL for supported environments - Porting - Different version of open ssl: straight forward - Other SSL: some expertise required - Enables other SSL providers: - Burden to validate on SDK developer Possibilities: -AxTLS -WolfSSL -Mocana EMS improvements SafeInt Library -C++ library -Helps prevent integer overflows Better certificate loading support -EMS and LUA script resource can authenticate -Bidirectional EMS's HTTP server now defaults to requiring authentication for LSR Overall theme: secure by default Q: If appkey expired in 1 day, does a new one get automatically created? A: Automatic one is not created, change the expiration date when creating the app key. When it expires - have to create a new one.
View full tip
This video walks through the dataset requirements when working with time series in ThingWorx Analytics Server.  Starting release 52.2 - ending release 8.2.   Also view: - Written version of those steps mentioned in the video - Help Center  
View full tip
Help Center link on how to control file transfers from the edge client using the EdgeControlled ThingShape.   The EdgeControlled ThingShape is a default entity included with ThingWorx that allows you to manage the amount of egress being sent from the platform to the Edge.   At the time of writing this post, the available 'When Disconnected' settings for a remotely bound property in ThingWorx are 'Fold' and 'Ignore'. Setting a property to 'Fold' while using this EdgeControlled ThingShape is necessary whether the device is connected all the time or only for brief updates.   To use this ThingShape in a real world scenario you might code your edge client to invoke the DequeueEgress REST API function available through this ThingShape. The parameter you pass in is then the number of messages you would like the client to receive. The result of this function is how many messages the platform then actually sent.   A quick setup: 1. Create a RemoteThing entity in ThingWorx 2. Create an ApplicationKey entity in ThingWorx 3. Setup an edge client to bind to that RemoteThing using the specified ApplicationKey 4. Manage Bindings on the Properties page of the RemoteThing, and pull in a few properties you would like to send property updates to 5. Set the 'When Disconnected' value to 'Fold' for each property you want to queue messages for   5a. Set any other settings on the properties you'd like; ie. persistence, logged 6. Save the Thing 7. Add the EdgeControlled ThingShape to the Thing 8. Save the Thing 9. Update property values, see exceptions thrown, but the value will be queued 10. Invoke DequeueEgress on the RemoteThing, with the number of messages to send to the edge client passed in as the parameter value   10a. Notice 'Fold' means only the last value set for a property will be sent to the edge client. There is currently no retention available for any values previously set to the property and stored as the message to be sent. Those values are lost upon a new value coming in before it's dequeued. 11. Verify the edge client has received the expected egress, and the return result of the DequeueEgress function was the expected # of messages sent.
View full tip
This post is part of the series Forced Root Cause Monitoring via Mashups and Modal Popups To not feel lost or out of context, it's recommended to read the main post first. Create a Popup Mashup Create a new Mashup called "rcp_MashupPopup" as Page and Static Save and switch to the Design tab Design Edit the Mashup Properties Set "Width" to 500 Set "Height" to 300 Add a new Label Set "Text" to "Something went wrong - what happend?" Set "Alignment" to "Center Aligned" Set "Width" to 230 Set "Top" to 55 Set "Left" to 130 Add a new Radio Button Set "Button States" to "rcp_AlertStateDefinition" Set "Top" to 145 Set "Left" to 25 Set "Width" to 450 Set "Height" to 100 In the Workspace tab, select the "Mashup" Click on Configure Mashup Parameters Add Parameter Name: "selectedState" BaseType: NUMBER Click Done Save the Mashup Connections Select the Radio Button Drag and drop its Selected Value property to the Mashup and bind it to the selectedState Mashup Parameter Drag and drop its SelectionChanged event to the Mashup and bind it to the CloseIfPopup service Save the Mashup
View full tip
Shown below is example code that when deployed in the appropriate container, will allow an end-user to talk to the Axeda Platform Integration Queue. A customer should supply their unique values for the following properties: queueName user password url import java.util.Properties; import javax.jms.*; import javax.naming.*; public class SampleConsumer {     private String queueName = "com.axeda.integration.ACME.queue";     private String user = "system";     private String password = "manager"; //private String url = "ssl://hostname:61616";   private String url = "tcp://hostname:61616";     private boolean transacted;     private boolean isRunning = false;     public static void main(String[] args) throws NamingException, JMSException     {         SampleConsumer consumer = new SampleConsumer();         consumer.run();     }     public SampleConsumer()     {         /** For SSL connections only, add the following: **/ //        System.setProperty("javax.net.ssl.keyStore", "path/to/client.ks"); //        System.setProperty("javax.net.ssl.keyStorePassword", "password"); //        System.setProperty("javax.net.ssl.trustStore", "path/to/client.ts");     }     public void run() throws NamingException, JMSException     {           isRunning = true;            //JNDI properties         Properties props = new Properties();         props.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.activemq.jndi.ActiveMQInitialContextFactory");         props.setProperty(Context.PROVIDER_URL, url);            //specify queue propertyname as queue.jndiname         props.setProperty("queue.slQueue", queueName);            javax.naming.Context ctx = new InitialContext(props);         ConnectionFactory connectionFactory = (ConnectionFactory)ctx.lookup("ConnectionFactory");         Connection connection = connectionFactory.createConnection(user, password);         connection.start();            Session session = connection.createSession(transacted, Session.AUTO_ACKNOWLEDGE);            Destination destination = (Destination)ctx.lookup("slQueue");         //Using Message selector ObjectClass = ‘AlarmImpl’         MessageConsumer consumer = session.createConsumer(destination, "ObjectClass= 'LinkedList'");            while (isRunning)         {             System.out.println("Waiting for message...");             Message message = consumer.receive(1000);             if (message != null && message instanceof TextMessage) {                 TextMessage txtMsg = (TextMessage)message;                 System.out.println("Received: " + txtMsg.getText());             }         }         System.out.println("Closing connection");         consumer.close();         session.close();         connection.close();     } }
View full tip
  Step 4: Write Data to External Database   You’ve connected to the database, you’re able to query the database. Now let’s handle inserting new data into the database. The update statements and data shown below are based on the table scripts provided in the download. Examples of how the ThingWorx entity should look can be seen in the SQLServerDatabaseController and OracleDatabaseController entities that you've downloaded   Running an Insert   Follow the steps below to set up a helper service to perform queries for the database. While other services might generate the query to be used, this helper service will be your shared execution service. In the DatabaseController entity, go to the Services tab.     2. Create a new service of type SQL (Command) called RunDatabaseCommand. 3. Keep the Output as Integer. 4. Add the following parameter:   Name Base Type Required command String True         5. Add the following code to your new service:   <<command>>       6. Click Save and Continue. Your service signature should look like the below example.     You now have a service that can run commands to the database. Run your service with a simple insert.   There are two ways to go from here. You can either query the database using services that call this service, or you can create more SQL Command services that query the database directly. Let’s go over each method next, starting with a service to call the helper.   In the Services tab of the DatabaseController entity, create a new service of type JavaScript. Name the service JavaScriptInsert_PersonsTable. Set the Output as InfoTable, but do not set the DataShape for the InfoTable. Add the following code to your new service: try { var command = "INSERT INTO Persons (person_key, person_name_first, person_name_last, person_email, person_company_name, " + "person_company_position, person_addr1_line1, person_addr1_line2, person_addr1_line3, person_addr1_city, person_addr1_state, " + "person_addr1_postal_code, person_addr1_country_code, person_addr1_phone_number, person_addr1_fax_number, person_created_by, " + "person_updated_by, person_created_date, person_updated_date) VALUES ('" + key + "', '" + name_first + "', '" + name_last + "', '" + email + "', '" + company_name + "', '" + company_position + "', '" + addr1_line1 + "', '" + addr1_line2 + "', '" + addr1_line3 + "', '" + addr1_city + "', '" + addr1_state + "', '" + addr1_postal_code + "', '" + addr1_country_code + "', '" + addr1_phone_number + "', '" + addr1_fax_number + "', '" + created_by + "', '" + updated_by + "', '" + created_date + "', '" + updated_date + "')"; logger.debug("DatabaseController.JavaScriptInsert_PersonsTable(): Query - " + command); var result = me.RunDatabaseCommand({command: command}); } catch(error) { logger.error("DatabaseController.JavaScriptInsert_PersonsTable(): Error - " + error.message); }         5. Add the following parameter:   Name Base Type Required key String True name_first String True name_last String True company_name String True company_position String True addr1_line1 String True addr1_line2 String True addr1_line3 String True addr1_city String True addr1_state String True addr1_postal_code String True addr1_country_code String True addr1_phone_number String True addr1_fax_number String True created_by String True updated_by String True created_date String True updated_date String True         6. Click Save and Continue.   Any parameter, especially those that were entered by users, that is being passed into a SQL Statement using the Database Connectors should be fully validated and sanitized before executing the statement! Failure to do so could result in the service becoming an SQL Injection vector.   Now, let’s utilize a second method to create a query directly to the database. You can use open and close brackets for parameters for the insert. You can also use <> as a method to mark a value that will need to be replaced. As you build your insert statement, use [[Parameter Name]] for parameters/variables substitution and <<string replacement >> for string substitution.   1. In the Services tab of the DatabaseController entity, create a new service of type SQL (Command).   2. Name the service SQLInsert_PersonsTable. 3. Add the following code to your new service: INSERT INTO Persons (person_key ,person_name_first ,person_name_last ,person_email ,person_company_name ,person_company_position ,person_addr1_line1 ,person_addr1_line2 ,person_addr1_line3 ,person_addr1_city ,person_addr1_state ,person_addr1_postal_code ,person_addr1_country_code ,person_addr1_phone_number ,person_addr1_fax_number ,person_created_by ,person_updated_by ,person_created_date ,person_updated_date) VALUES ([[key]] ,[[name_first]] ,[[name_last]] ,[[email]] ,[[company_name]] ,[[company_position]] ,[[addr1_line1]] ,[[addr1_line2]] ,[[addr1_line3]] ,[[addr1_city]]]] ,[[addr1_state]] ,[[addr1_postal_code]] ,[[addr1_country_code]] ,[[addr1_phone_number]] ,[[addr1_fax_number]] ,[[created_by]] ,[[updated_by]] ,[[created_date]] ,[[updated_date]]);       4. Add the following parameter:   Name Base Type Required key String True name_first String True name_last String True company_name String True company_position String True addr1_line1 String True addr1_line2 String True addr1_line3 String True addr1_city String True addr1_state String True addr1_postal_code String True addr1_country_code String True addr1_phone_number String True addr1_fax_number String True created_by String True updated_by String True created_date String True updated_date String True         5. Click Save and Continue.   Examples of insert services can be seen in the provided downloads.     Step 5: Executing Stored Procedures   There will be times when a singluar query will not be enough to get the job done. This is when you'll need to incorporate stored procedures into your database design.   ThingWorx is able to use the same SQL Command when executing a stored procedure with no data return and a SQL query when executing a stored procedure with an expected result set. Before executing these services or stored procedures, ensure they exist in your database. They can be found in the example file provided.   Execute Stored Procedure   Now, let's create the service to handle calling/executing a stored procedure.   If you are expecting data from this stored procedure, use EXEC to execute the stored procedure. If you only need to execute the stored procedure and do not expect a result set, then using the EXECUTE statement is good enough. You're also able to use the string substitution similar to what we've shown you in the earlier steps.   In the DatabaseController entity, go to the Services tab. Create a new service of type SQL (Command) called RunAssignStudentStoredProcedure. Add the following parameter:   Name Base Type Required student_key String True course_key String True         4. Add the following code to your new service:   EXECUTE AddStudentsToCourse @person_key = N'<<person_key>>', @course_key = N'<<course_key>>'; You can also perform this execute in a service based on JavaScript using the following code: try { var command = "EXECUTE AddStudentsToCourse " + " @student_key = N'" + student_key + "', " + " @course_key = N'" + course_key + "'"; logger.debug("DatabaseController.RunAssignStudentStoredProcedure(): Command - " + command); var result = me.RunDatabaseCommand({command:command}); } catch(error) { logger.error("DatabaseController.RunAssignStudentStoredProcedure(): Error - " + error.message); }         5. Click Save and Continue.   Execute Stored Procedure for Data   Let's create the entity you will use for both methods. This can be seen in the example below:     In the DatabaseController entity, go to the Services tab. Create a new service of type SQL (Query) called GetStudentCoursesStoredProcedure. Set the Output as InfoTable, but do not set the DataShape for the InfoTable. Add the following parameter:   Name Base Type Required course_key String True         5. Add the following code to your new service:   EXEC GetStudentsInCourse @course_key = N'<<course_key>>'   You can also perform this execute in a service based on JavaScript using the following code:   try { var query = "EXEC GetStudentsInCourse " + " @course_key = N'" + course_key + "'"; logger.debug("DatabaseController.GetStudentCoursesStoredProcedure(): Query - " + query); var result = me.RunDatabaseQuery({query:query}); } catch(error) { logger.error("DatabaseController.GetStudentCoursesStoredProcedure(): Error - " + error.message); }       6. Click Save and Continue.   You've now created your first set of services used to call stored procedures for data. Of course, these stored procedures will need to be in the database before they can successfully run.     Click here to view Part 3 of this guide.
View full tip
Challenges Manufacturers of factory equipment often rely on manual processes to maintain adequate supply levels of consumables in equipment at customer sites. But a clipboard-and-pen process for ordering supplies are error prone, and can quickly grow wrought with problems that often go unreported until they affect assembly line production, resulting in unexpected costs—such as overnight shipping charges—and risking downtime, which ultimately leads to customer dissatisfaction.   Solution One such manufacturer implemented ThingWorx to connect to equipment installed at customer sites, and created a custom web application using ThingWorx Foundation to remotely monitor supply levels. ThingWorx Industrial Connectivity provides the bi-directional connection to send data that is displayed in graphs on the web application created using ThingWorx Mashup Builder. Features of ThingWorx Analytics were used to generate alerts before maintenance problems affected production.   Outcomes The manufacturer is able to monitor supply levels to more effectively anticipate when consumables will need to be replenished. Supplies are now consistently ground shipped on time to meet assembly line demand, reducing interruptions to operations and allowing expansion to multiple plants with improved service level.   NOTE: Complete the following guides in sequential order. The estimated time to complete this learning path is 120 minutes.   1. Implement Services, Events, and Subscriptions  Part 1 Part 2 2. Build a Predictive Analytics Model  Part 1 Part 2
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
As many already know, ThingWorx versions 8.0+ now support both Apache Tomcat versions 8.0.44+ and versions of 8.5.13+. For this reason, many will want to consider using the latest Apache version for their ThingWorx instance, even despite the fact that the installation documentation does not seem to provide examples for the 8.5.x versions. This is because much of the configuration between the two versions remains the same. One may question these similarities when looking at the updated documentation from Apache on configuring SSL Connector ports. It would seem like some of the more traditional elements are now unusable, since they are marked for deprecation (including keystoreFile, keystorePass, and clientAuth). However, for now, these elements are still usable because Tomcat will convert whatever is provided in the Connector tag to the brand new SSLHostConfig tag (used primarily by Tomcat going forward). Apache has noted that these configuration options will continue to be usable throughout the next few major versions of Tomcat. PTC is already working on documentation which helps utilize the new configuration options in the future, but it won't be available for some time. In the meantime, for step-by-step instructions and further reading, see our Knowledgebase Article using self-signed certs (this article uses a CA). Happy developing!
View full tip
Recording of the Friday, November 17, 2017 ThingWorx Manufacturing Tips & Tricks Web Session. Agenda: - Overview & Application Demo - Aron Semle - Architecture Overview - Varathan Ranganathan - Q&A
View full tip
This post is part of the series Forced Root Cause Monitoring via Mashups and Modal Popups To not feel lost or out of context, it's recommended to read the main post first. Required Logic The following logic will help us realizing this particular use case: The trigger property on the AlertThing switches from false to true. The MashupMain will receive dynamic Property updates via the AlertThing.GetProperties service. It will validate the value of the trigger Property and if it's true the MashupMain will show the MashupPopup as a modal popup. A modal popup will be exclusively in the foreground, so the user cannot interact with anything else in the Mashup except the modal popup. In the modal popup the user chooses one of the pre-defined AlertStateDefinitions. When a State is selected, the popup will set the State as a Mashup Parameter, pass this to the MashupMain and the popup close itself. When the MashupPopup is closed, the MashupMain will read the Mashup Parameter The MashupMain will set the selectedReason in the AlertThing to the selected value. It will also reset the trigger property to false. This allows the property to be set to true again to trigger another forced popup. On any value change the AlertThing will store the selectedReason State in a ValueStream to capture historic information on which root causes were selected at which time. The ValueStream information will be displayed as a table in a GridWidget in the MashupMain once the new properties have been set.
View full tip
Announcements