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:
Unlocking the Power of Industrial Data Presentation by Mike Jasperson, VP of the IoT Enterprise Deployment Center   his video presentation was performed at the Digital Transformations in Manufacturing conference of 2021, hosted by Enterprise Digital. In this presentation, Mike Jasperson goes over the benefits to modernizing and consolidating access to time-stamped data that is ingested from equipment and sensors into a central location like ThingWorx. Moving away from monolithic, legacy, and siloed systems, and towards more agile solutions, has never been more critical in order to increase machine, operational, and business efficiencies while also opening up visibility into data systems and infrastructure deployments.   This video partners with InfluxData to help customers extract value from IoT data systems, maximizing both performance and operational capabilities of their monitoring systems. To stay competitive in the IoT market, it's important to review the best practices for scaling and testing your industrial metrics solutions, as well as how to get the best performance out of your digital data solutions by using time-series optimized databases like InfluxDB. Open source technologies discussed here are a great way to create modular and upgradable solutions and accelerate IoT innovation.     (view in My Videos)
View full tip
In this video we are going through a few steps to ensure the installation process went fine.   Updated Link for access to this video:  Validating a ThingWorx Analytics Server 8.1 Installation
View full tip
Since the marketplace extension is no longer supported and the drivers may be outdated, you may build your own jdbc package/extension: Download the Extension Metadata file Here Download the appropriate JDBC driver Build the extension structure by creating the directory lib/common Place the JAR file in this directory location: lib/common/<JDBC driver jar file> Modify the name attribute of the ExtensionPackage entity in the metadata.xml file as needed Point the file attribute of the FileResource entity to the name of the JDBC JAR file The metadata also contains a ThingTemplate the name is set to MySqlServer, but can be modified as needed Select the lib folder and metadata.xml file and send to a zip archive Tip: The name of the zip archive should match the name given in the name attribute of the ExtensionPackage entity in the metadata.xml file Import the newly created extension as usual To the JDBC extension, simply create a new thing and assign it the new ThingTemplate that was imported with the JDBC extension Configuration Field Explanation: JDBC Driver Class Name ​Depends on the driver being used Refer to documentation JDBC Connection String ​Defines the information needed to establish a connection with the database Connection string examples can be found in the ThingWorx Help Center ConnectionValidationString ​A simple query that will work regardless of table names to be executed to verify return values from the database   Alternatively, you may download the jdbc connector creator from the marketplace here https://marketplace.thingworx.com/Items/jdbc-connector-extension Then you may just view the mashup and use it to package your jdbc jar into an extension (which can be later imported into ThingWorx).  
View full tip
This is part 3 out of 3 videos on Getting Started with ThingWorx Analytics During this video you will learn:   Executing a “Signals” Job Getting the results of the “Signals” Job Executing a “Training Model” Job Getting the results of the “Training Model” Job   Updated Link for access to this video:  Getting Started with ThingWorx Analytics: Part 3 of 3
View full tip
    Step 6: Configure EMS   The EMS consists of two distinct components that perform slightly different operations and communicate with each other.   The first is the EMS itself which creates an AlwaysOn™ connection to ThingWorx Foundation. It binds Things to the platform and automatically provides features like file transfer and tunneling.   The second is the Lua Script Resource (LSR). It is used as a scripting language so that you can add Properties, Services, and Events to the Things that you create in the EMS.     Now that you have "installed" (i.e. downloaded, unzipped, and moved to an appropriate location) the EMS on your Raspberry Pi, it needs to be configured.   The primary method of doing so is via the config.json and config.lua files.   In this step, we'll create these files and paste some JSON / Lua configuration into them.   Navigate to the /etc directory.       Right-click inside the folder’s white-space and select New File.... Enter config.json and click OK.   Right-click on the new config.json file and select Text Editor.     Copy and Paste the following code into the empty config.json file: Note that the backslashes (\) in the JSON below are escape characters necessary to properly address special characters, such as the forward-slashes (/) indicating path directories. { "ws_servers": [{ "host": "YOUR_IP_ADDRESS_HERE", "port": 443 }], "appKey": "YOUR_APP_KEY_HERE", "logger": { "level": "INFO", "publish_directory": "\/home\/pi\/Downloads\/microserver\/logs", "publish_level": "INFO", "max_file_storage": 2000000, "auto_flush": true }, "http_server": { "ssl": false, "authenticate": false }, "ws_connection": { "encryption": "ssl" }, "certificates": { "validate": false, "disable_hostname_validation": true }, "tunnel": { "buffer_size": 8192, "read_timeout": 10, "idle_timeout": 300000, "max_concurrent": 4 }, "file": { "buffer_size": 8192, "max_file_size": 8000000000, "virtual_dirs": [ {"other": "\/home\/pi\/Downloads\/microserver\/other"}, {"tw": "\/home\/pi\/Downloads\/microserver\/tw"}, {"updates": "\/home\/pi\/Downloads\/microserver\/updates"} ], "staging_dir": "\/home\/pi\/Downloads\/microserver\/staging" } } When the EMS runs, the config.json file will answer the following questions: Code Section Questions Answered ws_servers At what IP address / port is the ThingWorx Server located? appKey What is your Application Key? logger Where, and at what level, should we log errors? http_server What port should the WSEMS use to setup an HTTP server? ws_connection Should we use encryption? certificates Are we using Security Certificates? tunnel What are the configuration parameters for remote-tunneling? file What are the configuration parameters for file-transfer? We pre-defined the parameters for everything that we could, but you will still need to tell the WSEMS the IP address where the ThingWorx instance is located and a valid Application Key you either created earlier or may create now.   TIP: You may have noticed the pre-existing config.json.complete and config.json.minimal files. These are example files that come with the WSEMS and are provided as an aid. The code above which you copied into your own config.json file is simply a customization of these aids. In particular, you may wish to look through the config.json.complete file, as it shows every available option which you might want to configure if you choose to make a custom application with the WSEMS. The config.json.complete file also contains comments for each of these options. However, a functional config.json file may NOT contain comments of any kind, so you would need to remove all comments if you choose to copy/paste some code from that file into a functional config.json of your own making.     Modify config.json to point to ThingWorx Foundation   Change YOUR_IP_ADDRESS_HERE to the IP address of your hosted ThingWorx instance. You may wish to e-mail yourself the Foundation IP address using a web-mail account so that you can copy/paste on the Pi from your e-mail to the config.json file. Note that you may use a URL, such as "pp-180207xx36jd.devportal.ptc.io". Either way, the IP or URL must be enclosed in quotation marks (""). Also, Port 443 is the appropriate port for the ThingWorx hosted server. Ports for local-install may vary. 2. Change YOUR_APP_KEY_HERE to an Application Key which you have previously created. Or create a new Application Key now. You may wish to e-mail yourself the Application Key using a web-mail account so that you can copy/paste on the Pi from your e-mail to the config.json file.   3. Save and exit the file.   Create a config.lua file   Navigate to the /etc directory. Right-click inside the folder’s white-space and select New File.... Enter config.lua and click OK. Right-click on the new config.lua file and select Text Editor. Copy and Paste the following code into the empty config.lua file: scripts.log_level = "WARN" scripts.script_resource_ssl = false scripts.script_resource_authenticate = false scripts.PiThing = { file = "thing.lua", template = "YourEdgeThingTemplate", scanRate = 120000, sw_update_dir = "\/home\/pi\/Downloads\/microserver\/updates" } Save and exit the file.   Create a Custom Template for the EdgeThing   Navigate to /etc/custom/templates. Right-click inside the folder’s white-space and select New File.... Enter YourEdgeThingTemplate.lua and click OK. Right-click on the new YourEdgeThingTemplate.lua file and select Text Editor. Copy and Paste the following code into the empty YourEdgeThingTemplate.lua file: require "shapes.swupdate" module ("templates.YourEdgeThingTemplate", thingworx.template.extend) Save and exit the file.      Step 7: Connect EMS   In this step, you'll launch the EMS so that it can communicate with your ThingWorx Foundation platform.   On the Raspberry Pi , open a Terminal by clicking the Terminal icon in the top-left.   Navigate to the EMS's root folder, i.e. /home/pi/Downloads/microserver , by issuing the following command and then pressing Enter : cd /home/pi/Downloads/microserver   In the Terminal window, enter the command sudo ./wsems and press Enter . Note: Do not close this window, or the connection to the ThingWorx platform will close. Also, look through the output in the wsems window. Near the end, you should see Successfully connected. Saving .booted config file . If you do not see the Saving .booted comment, then you likely have an error in your config.json file... especially with either the address or Application Key .   Open another Terminal window as per the above instructions.   In this second Terminal window, Navigate to the EMS's root directory, i.e. /home/pi/Downloads/microserver , by issuing the following command and pressing Enter : cd /home/pi/Downloads/microserver In the second Terminal window, enter the command sudo ./luaScriptResource and press Enter . Note: Do not close this second Terminal window, or the connection to the ThingWorx platform will close.   NOTE: When the scripts start running, the EMS attempts to talk to the ThingWorx platform. However, at this point in the tutorial, ThingWorx does not have detailed information about the Edge device that is attempting to connect to it, so you will see an error message. This is the expected behavior which we will resolve in the next step. The wsems program runs through the config.json file in order to extract the basic connectivity information to reach the ThingWorx platform. The luaScriptResource program runs through the config.lua file to extract to which Thing the WSEMS should be connecting. Both programs must be running in order to achieve connectivity with ThingWorx. Program File Accessed Purpose wsems config.json Extracts basic connectivity information to reach the ThingWorx platform. luaScriptResource config.lua Determines to which Thing the WSEMS should connect. NOTE : Since the config.lua file which we previously created has a reference to a custom template, it also accesses the YourEdgeThingTemplate.lua file to extend the base functionality. Both programs must be running in order to achieve connectivity with ThingWorx. Troubleshoot Connectivity Issues   If the websocket does not connect successfully, check the following:   Issue Solution WEBSOCKET CLOSED - Warning seen immediately after Websocket gets connected. Ensure that the host IP address, port, and appKey of the ThingWorx composer instance are accurately set. If, in config.json, you have selected the option to validate certification, then make sure the path to the certificate file is correctly set. twWs_Connect - Error trying to connect. Ensure that the host IP address and port running the ThingWorx Composer is accurately set. Check if the certification parameter is set or not. By default, the WS EMS validates certificates. To ensure that the validation is performed correctly without errors, ensure that the certificates configuration parameters are set accurately with the correct path to the certificate file. If you do not wish to validate the certificate, you may explicitly set the validate parameter in certificates parameter set to false. twTlsClient_Connect - Error intializing TLS connection. Invalid certificate. Check if the ws_encryption parameter is present in your config.json file. By default, WS EMS enables TLS connection to the ThingWorx Platform. Ensure that the certificate file mentioned in config.json is valid and stored in the path specified. For debugging purposes, you can set the ssl parameter to none in ws_encryption configuration parameter. [WARN ] ... Main - Unable to connect to server. Trying .booted config file. Ensure that the host is up and running at the IP address and port number mentioned in the config.json file. Ensure that ThingWorx is running on the host address at the correct port number. Ensure that all necessary ports are open for communication.     Click here to view Part 4 of this guide.
View full tip
Check our expert session recorded library! The recordings will also be published in our Customer events library, posted on each event. Stay tunned!   Your feedback is very important to us! After watching the recordings, please take 2 min to complete this survey   Thingworx Foundation Session Name Link Duration Thingworx Mashup 101 - Do's and Don'ts Recording link 00:33:41 Thingworx Active Active Clustering (High Availability Recording link 00:26:24 Upgrade to Thingworx 9 – How to Plan / Evaluate Impacts Recording link 00:27:02 Thingworx Flow Overview Recording link 00:43:40 Top 5 items to check for Thingworx Performance Troubleshooting Recording link 00:26:55 ThingWorx DEVOPS QuickStart Guide Recording link 00:45:05 ThingWorx Backup And Recovery Recording Link 00:20:14 Expert Session - Designing your Data Model in Thingworx Recording link 00:26:45 ThingWorx Installation Recording link 00:15:07 Expert Session - Introduction To Edge Connectivity Recording link 00:15:56 Expert Session - Basic Mashup Design in Thingworx Recording link 00:36:31 Expert Session - Extensions101 Recording Link 00:30:08 Expert Session – Developing your Data Model in Thingworx Recording link 00:39:19 Thingworx Scalability Recording link 00:09:18 Expert Sessions - ThingWorx Patch Upgrade Recording link 00:03:19   Thingworx Navigate Session Name Link Duration Understanding license requirements for Thingworx Navigate Recording link 00:32:40 Navigate SSL and Authentication Recording Link 00:34:30 Navigate 3D Viewer Recording Link 00:43:25 Component Based App Development Recording Link 00:24:07 Navigate 9.0 – What’s new Recording link 00:27:07 Overview of SSO Implementation for ThingWorx Navigate and Windchill with PingFederate Recording link 00:18:36 Identifying the right SSO mix for Navigate 1 6 Recording link 00:57:56 Navigate Configuration - PingFederate Automation Script Recording link 00:51:07 Expert Session - Navigate Configuration/Windchill Authentication Recording link 00:23:07 What’s new with Navigate 1.8 and the new Navigate 1.8 installer Recording link 01:05:26 Creating an I*E task for use in Navigate Recording link 00:05:36   Vuforia Expert Capture Session Name Link Duration VEC In a Nutshell Video Link 00:31:39
View full tip
In the summer heat, keep your operators cool with Operator Advisor. Sit by the pool and relax to the tunes of Episode 05 of “ThingWorx on Air.”     High five! We’re back with Episode 05 of “ThingWorx on Air,” our developer-focused IoT podcast.   In today’s episode Jordan Chaisson, a super talented product manager, joins me to share even more about Operator Advisor (OA). You may remember that we introduced OA in our very first episode of “ThingWorx on Air”. Today, we dive deeper into its business value and reveal what’s on its roadmap. Plus, hear the coolest use case she’s seen yet with Operator Advisor!   OA is an accelerator application built on the ThingWorx platform that enables manufacturing operators through digital work instructions and a comprehensive user experience to receive the right data at the right time to minimize scrap and maximize efficiency.   Looking for more? Check out the Operator Advisor Guide or discover where to download Operator Advisor today.   Reach out with any questions, and, as always, stay connected!   -  Kaya
View full tip
  Discover how ThingWorx Widgets can be implemented in a compelling Mashup design.   Guide Concept   This project will introduce how to create complex user interfaces that are built by combining simple Widgets and basic styling.   Following the steps in this guide, you will build a web application showing both geographic and table based information.   We will teach you how to create a professional user interface that effectively conveys information to users.     You'll learn how to   Organize UI elements in a Mashup layout Use the Repeater widget effectively Use Styles to customize UI elements within a Mashup   NOTE: This guide's content aligns with ThingWorx 9.3. The estimated time to complete ALL parts of this guide is 60 minutes     Step 1: Top Level Layout   A Mashup like this does take some time to assemble, but is built by layering simple Widgets. In this guide, we will break down how this Mashup was created to give you a behind-the-scenes look and provide tips to use when developing your IoT application.     This professional-looking Mashup was created using only these Widgets:   Layout with both left and right sidebars and a footer Tabs - Responsive Repeater Navigation Image Property Display Gauge Google Map   The first Widget placed on this Mashup canvas was a Layout widget with both a Left and a Right Sidebar defined. The center portion of the layout has only one GoogleMap widget. The tractor icons displayed on the map were created by uploading a .png to create a Media entity then specifying the image as a custom MarkerStyle in the GoogleMap widget. In the following steps you will duplicate this Mashup.         Step 2: Import Data   In this step we will download and them import both a Thing Template and the sample data that are used throughout the exercise.   Download and save ConnectedTractors.xml which contains the Thing Template and example Things. In the lower left of Composer, click Import/Export, then Import.     In the Import From File pop-up, keep the defaults and click Browse.     Navigate to the ConnectedTractors.xml file you previously downloaded. Select the Entities file, then click Open. Click Import in the bottom right of the pop up, then click Close after file is successfully imported.     Step 3: Map Mashup   Follow the steps in this part of the lesson to create a Mashup with an organized, structured layout.   Create Mashup   Navigate to Browse > Visualization > Mashups.   Click + New.   Keep the defaults and click OK.   In the Name field, type TractorDemoMashup. If Project is not already set, click the + in the Project text box and select the PTCDefaultProject.   Click Save Click the Design tab in the Mashup Information panel Click the Layout tab, then click Add Left . Scroll down to Container Size, click Fixed Size then enter200 in the Width text box and press Tab to record your entry.   Click on the right side of your Mashup to select the larger container. Click Add Right before again scrolling down to Container Size, click Fixed Size then enter200 in the Width text box and press Tab to record your entry.        NOTE: The next step uses the Google Map Widget which may need to be downloaded and imported from IQNOX.com. A Step-by-step guide for using Google Maps with ThingWorx is available.   12. Click the Widgets tab on the top left of the Widget panel, then enter map in the search box of the Widget panel. 13. Click and drag the Google Map Widget onto the center area of the canvas. 14. Click Save   15. In the Data panel on the right click the + to add a data source for the Mashup. 16. Enter the name of the Template used to create the Things that will be shown on the Mashup. Click on the Template ConnectedTractor to use the sample data 17. Click the arrow to the right of GetImplementingThingsWithData, then click the Execute on Load check box before clicking Done.     Setup Map Data   In the Data panel on the right, if neccesary, expand the GetImplementingThingsWithData data source and then drag the All Data row onto the map widget.   Click Data in the Select Binding Target pop-up to connect the data source to the Data property of the Google Maps widget. In the Properties panel in the lower left, scroll to the LocationField property then select TractorLatLng   NOTE: The imported sample data provided for this lesson has a property named TractorLatLng that contains location information. Fields in the bound data with a type Location will be available in the drop down. 4. Click Save at the top of your Mashup. Click View Mashup to see the live map. NOTE: The map uses the standard markers, click on one of the markers to see that the marker changes to indicate that it is selected.     Customize Map Markers   In this part of the lesson we will show how to use a custom image for the map markers. Right-click on each of the images below to download and save them to use in the next step.       We will upload these images to create new Media entities and apply them to the Google Map widget. Click Browse > Visualization > Media.   Click + New. In the Name field, type TractorSelected.   If Project is not already set, click the + in the Project text box and select the PTCDefaultProject. Under Image, click Change. Navigate to and select the TractorSelected.png file you just downloaded. On the navigation pop-up, click Open to close the pop-up and confirm the image selection. At the top of Foundation, click Save.    Repeat steps 1 through 8 for TracktorUnselected. Open your Mashup and click on the Google Map widget. Click on Style Properties tab in the lower left and scroll to the MarkerStyle Property and click on the Default map marker image, then click edit.   Click Style Information in the left panel, then clear the default image by clicking the X next to Image.   Click the + to select a new image then scroll to the Media entity you just created. In this case, the TractorUnselected media. Click Save. NOTE: The Google Map widget is packaged with default style entities. By editing these styles we will change the markers for all Mashups running on this ThingWorx server. In later steps we show how to create one time use Styles instead of modifying the default Styles.   14. Repeat steps 1 through 8 to create and assign the TractorSelected image for the SelectedMarkerStyle Property. 15. Click Save for the Mashup, then View Mashup to see the custom map icons.       Step 4: Navigation Panel   The left sidebar defines the green background color in its style and contains just one Collection widget with custom embedded Mashups specified for both the Selected and the Unselected items. The logo at the bottom is an image defined in the Footer style.   The Collection widget works by duplicating an embedded panel Mashup that controls the display of data from each entity in collection of entities.   Add Footer   Select the left side container of your Mashup, then click the Layout tab Click Add Bottom and scroll down to Container Size, click Fixed Size then enter 60 in the Height text box and press Tab to record your entry.   Click Save.   Style Sidebar   Next, we are going to add a custom background color and image to the sidebar footer.   Click the Explorer tab in the widget panel then select the top container for the left sidebar.   Click the Style Properties tab in the lower left properties panel, then click to expand Base and flexcontainer. In the background tex box, enter #39736C for a dark green color, then press Tab.   Click the top left container, then, in the Style Properties tab, click to expand Base and flexcontainer. Click X in background property   Click the lower left container, and also click X to remove the background property.   Right-click on the image below to download and save it for use when we add a footer in the next step.   Create a Media entity as we did for the map markers by clicking the Browse folder icon, then click Media in the Visualization section. Click the + New to create a new Media entity. Enter twxpwr in the Name field for the footer image. If Project is not already set, click the + in the Project text box and select the PTCDefaultProject. Click Change in the Image section, then browse to the image you saved. Click Open, then Save.   Click in the lower-left footer container on your mashup, then click the Properties tab. Scroll to see the SourceURL property in the properties panel, then click the + to open the media selection window. Scroll to browse for the Media you just created.   Click the footer Media and then slick Save in Mashup Builder before continuing to the next step.     Click here to view Part 2 of this guide.
View full tip
    Step 5: Create Event   Events are automatic analysis jobs which are submitted based on a pre-defined condition. In this step, we'll configure an Analysis Event which will execute automatically whenever there is a datachange in our simulated engine.   On the ThingWorx Composer Analytics tab, click ANALYTICS MANAGER > Analysis Events.   Click New....   In Source, search for and select AMQS_Thing. In Event, select DataChange. In Property, select FlipFlop. In Provider Name, select Vibration_Provider. In Model Name, select the published Model.   Click Save.     Map Event Data   Map the Properties of AMQS_Thing, to the fields required to evoke an Analysis Job.   Select the previously-created Event, and click Map Data....   Click Inputs Mapping.   In Source Type, select Thing. In Source, search for and select AMQS_Thing.   On the left, select s1_fb1, one of the sub-fields of AMQS_Thing's InfoTable Property. On the right, select _s1_fb1, the first frequency band required for the Model to make a prediction.   Click the Map button in the center.   Repeat this mapping process for for s1_fb2 through s1_fb5.   Map causalTechnique in the same manner. This is a String Property in AMQS_Thing with a Default Value of FULL_RANGE, the same technique we used in our earlier true/false testing. Map goalName to goalField in the same manner. This is a String Property in AMQS_Thing with a Default Value of low_grease, the same goal we used in our earlier testing.   Click Results Mapping on the left.   Map _low_grease to Result_low_grease.   Map _low_grease_mo to Result_low_grease_mo.   Click Close to close the mapping pop-up.   Enable Event   Now that we've done the mapping from Foundation to Analytics, let's confirm that everything is correct.   If so, we'll then Enable the Analysis Event so that it can automatically generate and process Analysis Jobs.   Select the mapped Analysis Event.   Click View....   Click Inputs Mapping and confirm that all mappings are correct.   Click Results Mapping and confirm that all mappings are correct.   At the top-left, expand the Actions... drop-down.   Select Enable.   Now that you have enabled the Analysis Event, whenever AMQS_Thing's InfoTable Property changes, the new data will be submitted to Analytics Manager.   An Analysis Job will automatically run, with a predictive score sent back and stored in AMQS_Thing's Result_low_grease (Boolean) and Result_low_grease_mo (Number) Properties.       Step 6: Test Model   In this step, we'll confirm that the automatic analysis of information coming from remote devices is operational.   On the ThingWorx Composer Analytics tab, click ANALYTICS MANAGER > Analysis Jobs.   Uncheck Filter Completed Jobs.   Select a Job and click View.... Click Results. NOTE: You will see true or false, just as when you manually tested the Model, so you know that Analytics Manager is now automatically submitting and completing jobs with a resulting prediction. Test Mashup   Follow these steps to confirm that the results are being sent back to ThingWorx Foundation in a way in an actionable way.   Return to the AMQS_Mashup browser tab. Wait at least ~20 seconds to see multiple cycles of good and bad data (which should generate a false or true result from Analytics Manager). Note the Text Field Widgets on the right now have data.   This analytical result is coming from Analytics Manager, and is the exact same output you saw in ANALYSIS JOBS.   Using this technology, you could create a paid customer service, where you offered to monitor remote engines, in return for automatically shutting them down before they experience catastrophic engine failure.   For that example implementation, you would utilize the AMQS_Thing.Result_low_grease BOOLEAN Property to trigger other actions.   For instance, you could create an Alert Event which would be triggered on a true reading.   You could then have a Subscription which paid attention to that Alert Event, and performed an action, such as sending an automatic shutdown command to the engine when it was experiencing a likely low grease event.   NOTE: We recommend that you return to the ThingWorx Composer Analytics > ANALYTICS MANAGER > Analysis Events tab and Disable the Event prior to continuing. Since the simulator generates an Event every ~10 seconds, this can create a large number of Events, which can fill up your log.       Step 7: Next Steps   Congratulations. You've completed the Operationalize an Analytics Model guide. In this guide you learned how to:   Define an Analysis Provider that uses the built-in Analytics Server Connector Publish a Model from Analytics Builder to Manager Create an Analysis Event which takes data from ThingWorx Foundation and decides whether or not a failure is likely   This is the last guide in the Design and Implement Data Models to Enable Predictive Analytics learning path.   Learn More   We recommend the following resources to continue your learning experience:       Capability Guide Build Implement Services, Events, and Subscriptions Guide   Additional Resources   If you have questions, issues, or need additional information, refer to:       Resource Link Community Developer Community Forum Support Analytics Manager Help Center
View full tip
Please refer to the release notes to find information on the new features/changes: PTC Here are some common questions and answers in regards to the Installers feature: Does that mean Thingworx 8 only support docker installation? Or standalone installation is still allowed? Only if using the new installer.  The war file download will still be available for non Docker installs. The WAR files will still be available and usable  the same way as in the past.  Users only need to use Docker if they use the installer. How do customers download/build the docker image? The image is not provided separetly, it is installed and configured by the installer. Does the installer install docker when necessary as well? Or is it expected that the user already has docker installed? No, the user must install it on their operating system before using the installer.  The installer will detect if Docker is properly installed.
View full tip
This video begins Module 5: Descriptive Analytics of the ThingWorx Analytics Training videos. It covers signals, profiles, and clusters, and how these forms of descriptive analytics provide crucial insight into your data.
View full tip
  Happy New Year, everyone! New year, same mission. As we continue to improve ThingWorx, we remain committed to taking into account what you, our users, are saying. What are you using ThingWorx for? What do you want it to do? What additional tools are you looking for?   To hear from you directly, our PM team has created a quick survey to understand your identity management and SaaS strategies a little better.   Complete the survey below for a chance to win a free Solution Central t-shirt! Loading…   Thanks in advance! At the end of the survey, all respondents will receive a link to check out the latest in our ThingWorx 8.5 release. One lucky winner will receive the Solution Central t-shirt.   Thanks for sharing your info! We’ll be sure to study it as we continue to develop our robust IoT solutions platform.   Stay connected in the New Year! Kaya
View full tip
    Use Thing Shapes to create groups of related Properties   Guide Concept   Save time and effort by modeling a solution in ThingWorx using Thing Shapes to group Properties. A logical group of Properties can be applied to Things and Thing Templates.     You'll learn how to   Create a Thing Shape Add Properties to a Thing Shape   NOTE: This guide's content aligns with ThingWorx 9.3. The estimated time to complete this guide is 30 minutes       Step 1: Create Thing Shape   In this section you will create a Thing Shape for sensor properties of a MXChip development board.   Thing Shapes are components that contain Properties and Services. In Java programming terms, they are similar to an interface.   Start on the Browse folder icon of ThingWorx Composer. Under the Modeling section of the left-hand navigation panel hover over Thing Shapes, then click the + button.   Type ThermostatShape in the Name field.   If Project is not already set, click the + in the Project text box and select the PTCDefaultProject. Click Save.     Step 2: Add Properties to Thing Shape   Click Properties and Alerts tab at the top of your Thing Shape.   Click + Add. Enter the Property name in the Name field as shown in the table below. Name Base     Type           Persistent?       Logged? humidity NUMBER   X messageId STRING X   temperature NUMBER   X Select the appropriate Base Type from the drop-down menu.   Check Persistent and/or Logged according to the table. NOTE: When Persistent is selected, the property value will be retained during a system restart. Properties that are not persisted will be reset to the default during a system restart. When Logged is selected, every property value change will be automatically logged to a specified Value Stream. Click Check +. TIP: When adding multiple properties at once, click Done and Add after each, once you've entered a Name, selected a Base Type and any other criteria. If adding a single property, click Done. Repeat steps 4 through 6 for each of the properties in the rows of the table. Click the done Check. You'll see that these Properties have been created for the ThermostatShape.   Click Save.     Step 3: Next Steps   Congratulations! You've successfully completed the Create A Thing Shape, and learned how to: Create a new Thing Shape Add Properties to the Thing Shape   This is the last guide in the Azure MXChip Development Kit Learning Path.  If you wish to return to the Learning Path, click the link.   Learn More   The following resources continue your learning experience:  Resource       Link Build Data Model Introduction Experience Object-Oriented UI Design Tips   Additional Resources   If you have questions, issues, or need additional information, refer to:  Resource       Link Community Developer Community Forum Support Thing Shape Support Help Center      
View full tip
This video begins Module 8: Time Series Modeling of the ThingWorx Analytics Training videos. It describes the differences between time series and cross-sectional datasets. It begins to show how ThingWorx Analytics automatically transforms time series datasets into ones that are ready for machine learning. 
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
Presentation for MFG Apps Tips & Tricks Session #3 - PTC IoT Starter Kit, Presented by Serge Romano 1DEC2017
View full tip
  Hi everyone,   Today I’m here with a very exciting guest—Janie! Janie recently joined PTC after spending the last two years at a global professional services firm where she held many roles including that of a lead ThingWorx developer. While Janie did do some font-end development work and UI creation, the bulk of her time was spent on the backend writing services that enabled the application’s functionality. In this role, she not only wrote code herself, she also played a large part in the in-platform architectural decisions, determining the best way to utilize the platform in order to satisfy complex business requirements.   Throughout her last two years, she worked on various IIoT projects in the manufacturing industry developing a real-time asset health monitoring and OEE/production tracking application to monitor key metrics like downtime and output. For each customer engagement an assessment of IoT platforms was often undertaken in order to assess strengths and weaknesses based off-of the business requirements of the customer and while she spent time looking into a variety of platforms, ThingWorx always came out on top.   That said, her experience with ThingWorx showed her how much and how quickly that technology can can change the way a manufacturing company operates. She decided that instead of doing implementations of the technology, she wanted to have an impact on the technology itself and have a hand in the direction it goes and the way it continues to change the businesses we know today. This desire is what led her to product management and the role she currently holds at PTC today.   I recently sat down with Janie to hear how it has been moving from a developer on the platform to a PM for it to learn about her past ThingWorx experiences and how those will influence the work she does at PTC.     Here’s how our convo went.   Kaya: In your previous role as an IoT developer at another company, what made you choose ThingWorx over other IoT solution platforms? Janie: We chose ThingWorx over other IoT platforms due to its strong ability to connect such a wide variety of machines using ThingWorx Industrial Connectivity via Kepware, its industry-leading standing in the market and the fact that it did not require its users to have extensive development knowledge given its low-code environment, which enabled non-developers to be successful.   Kaya: Why do you think ThingWorx saved you time over other development platforms/products? Janie: There were three key ways the platform helped to save me time. First, I didn’t have to write code from scratch. For example, when needing to manipulate arrays or parse through uploaded csv files, there were pieces of code readily available for me to do so without having to know how to do it by myself. Second, due to the widgets ThingWorx already had available, I didn’t have to spend time making custom front-end UIs or widgets. And, thirdly, I saved a ton of time during the connectivity phase because connectivity to devices was supported OOTB through Kepware’s extensive suite of drivers available including some of the key ones we utilized such as Allen-Bradley Control Logix, Modbus, Siemens, and user configurable drivers.   Kaya: Along the lines of saving time, how would your experience with the platform have differed had you leveraged one of our manufacturing apps like Production KPIs? Janie: As I talked about previously, having access to a pre-built solution would have evidently saved a ton of development time and accelerated time to value. It also would have reduced the complexity of our completed app, which would have made it more scalable. While I understand the Manufacturing apps are not 100% ready-to-go out of the box but rather configurable stepping stones into a larger and mole holistic solution, if we could have had Production KPI’s as our development starting point, we would have had a more sound and already proven way of tackling Production and OEE tracking and could have added even more value on top of that.   Kaya: What were some of your favorite aspects about the platform? Janie: The code-snippets to get started were a big favorite, as were the OOTB widgets that allowed for quick visualization of important data. The OOTB industrial connectivity with seamless integration to ThingWorx was huge—we were able to connect multiple devices and stream information in real-time with little difficulty, which enabled us to derive even more value from the platform and really focus on becoming a fully smart and connected operation. Finally, the drag-and-drop UI was simple and intuitive.   Kaya: What do you think the top misconception is about IoT? Janie: I would say repeatability. Scaling an IoT solution across an enterprise is not as simple as it is often represented. I know we’re releasing a new functionality to help users more easily deploy their solutions, so I’m excited about that.   Kaya: So, now that you work as PM for the platform, what were a few things you wish you knew as a developer working on ThingWorx? Janie: Seeing everything we’re working on for the platform now is very exciting; I wish I could have been more aware of what’s on the roadmap so I knew what was coming as I was developing on the platform.   Kaya: On a similar note, now that you are working on the ThingWorx PM team, what items are you hoping to drive/change based on your experience? Janie: First, I’d like to drive stronger interaction with our system integrators (SIs) and partners by providing them insights into our roadmaps and allowing for them to provide feedback in a more seamless way. Next, I’d like to improve upon our essential platform developer capabilities, such as CI/CD, debugging, versioning, testing, etc. That said, Solution Central and PTC’s commitment to and integration with Microsoft products are very exciting roadmap features for me.   Kaya: I completely agree. Those are some great points. As I’m sure you’re aware, but our readers might not be aware of, we are working within the platform itself and a new feature, Solution Central, to provide more of that basic functionality so that, in addition to building blocks, low-code and a drag-and-drop UI, we can continue to help you accelerate your time to value.   Readers, I hope you enjoyed hearing from Janie! We’re excited to have her on the ThingWorx team and we look forward to making ThingWorx even stronger.   Let me know what you think in the comments below.   Stay connected, Kaya
View full tip
This script creates a csv file from the audit log filtered by the User Access category, so dates of when users logged in or logged out. *** see update below *** Note:  The csv file has the same name as the Groovy script and does NOT have the .csv extension . To get the .csv extension, the Groovy script has to be renamed to AuditEntryToCSV.csv.groovy .  Suggestions on how to improve this are welcome. *** Update ***: The download works without the renamed groovy script by returning text instead of an input stream.  The script has been modified to illustrate this. Parameters: days - the number of days past to fetch audit logs model_name - the model name of the asset serial_number - the serial number of the asset import com.axeda.drm.sdk.device.ModelFinder import com.axeda.drm.sdk.Context import com.axeda.common.sdk.id.Identifier import com.axeda.drm.sdk.device.Model import com.axeda.drm.sdk.device.DeviceFinder import com.axeda.drm.sdk.device.Device import com.axeda.drm.sdk.audit.AuditCategoryList import com.axeda.drm.sdk.audit.AuditCategory import com.axeda.drm.sdk.audit.AuditEntryFinder import com.axeda.drm.sdk.audit.SortType import com.axeda.drm.sdk.audit.AuditEntry import groovy.xml.MarkupBuilder import com.axeda.platform.sdk.v1.services.ServiceFactory /* * AuditEntryToCSV.groovy * * Creates a csv file from the audit log filtered by the User Access category, so dates of when users logged in or logged out. * * @param days        -   (REQ):Str number of days to search. * @param model_name        -   (REQ):Str name of the model. * @param serial_number        -   (REQ):Str serial number of the device. * * @note - the csv file has the same name as the Groovy script and does NOT have the .csv extension . To get * the .csv extension, the Groovy script has to be renamed to AuditEntryToCSV.csv.groovy . * * @author Sara Streeter <sstreeter@axeda.com> */ def writer = new StringWriter() def xml = new MarkupBuilder(writer) try {    def ctx = Context.getUserContext()    ModelFinder modelFinder = new ModelFinder(ctx)    modelFinder.setName(parameters.model_name)    Model model = modelFinder.find()    DeviceFinder deviceFinder = new DeviceFinder(ctx)    deviceFinder.setSerialNumber(parameters.serial_number)    Device device = deviceFinder.find()    AuditCategoryList acl = new AuditCategoryList()    acl.add(AuditCategory.USER_ACCESS)    long now = System.currentTimeMillis()    Date today = new Date(now)    def paramdays = parameters.days ? parameters.days: 5    long days = 1000 * 60 * 60 * 24 * Integer.valueOf(paramdays)    AuditEntryFinder aef = new AuditEntryFinder(ctx)    aef.setCategories(acl)    aef.setToDate(today)    aef.setFromDate(new Date(now - (days)))    aef.setSortType(SortType.DATE)    aef.sortDescending()    List<AuditEntry> audits = aef.findAll() // use a Data Accumulator to store the information def dataStoreIdentifier = "FILE-CSV-audit_log" def daSvc = new ServiceFactory().dataAccumulatorService if (daSvc.doesAccumulationExist(dataStoreIdentifier, device.id.value)) {     daSvc.deleteAccumulation(dataStoreIdentifier, device.id.value) } // assemble the response    audits.each { AuditEntry audit ->            def row = [                audit?.id.value,                audit?.user?.username,                audit?.date,                audit?.category?.bundleKey,                audit?.message            ]         row = row.join(',')         row += '\n'         daSvc.writeChunk(dataStoreIdentifier, device.id.value, row);        } // stream the data accumulator to create the file    InputStream is = daSvc.streamAccumulation(dataStoreIdentifier, device.id.value) return ['Content-Type': 'text/csv', 'Content-Disposition':'attachment; filename=AuditEntryCSVFile.csv', 'Content': is.text] } catch (def ex) {    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()) }
View full tip
  Explore the ThingWorx Foundation IoT application-building platform in a convenient video format.     Guide Concept   This project will introduce you to the principles of ThingWorx Foundation by creating a working web application, guided by a convenient video.   Following the steps in this guide, you will create the building blocks of your first application for the Internet of Things (IoT). You will use ThingWorx Composer to create Thing Templates, which are then used to create Things that model the application domain. A simulator is imported to generate time-series data that is saved to a Value Stream.   After modeling the application in ThingWorx Composer, you'll use Mashup Builder to create the web application Graphical User Interface (GUI).   You'll learn how to   Create a Thing Shape, Thing Template, and Thing Store data in a Value Stream Download and install a data simulator Create an application UI    NOTE:  The estimated time to complete this guide is 30 minutes     Step 1: Video   Click the link below to enjoy the video.   Get Started with ThingWorx for IoT       Step 2: Next Steps   Congratulations! You've successfully completed the Get Started with ThingWorx for IoT Video Guide, and learned how to:   Use Composer to create a Thing based on Thing Shapes and Thing Templates Store Property change history in a Value Stream Define application logic using custom defined Services and Subscriptions Create an application UI with Mashup Builder Display data from connected devices Test a sample application   Learn More   We recommend the following resources to continue your learning experience:    Capability     Guide Connect Choose a Connectivity Method Build Design Your Data Model Experience Create Your Application UI   Additional Resources   If you have questions, issues, or need additional information, refer to:    Resource       Link Community Developer Community Forum Support Getting Started with ThingWorx
View full tip
The IoT Building Block Design Framework By Victoria Firewind and Ward Bowman, Sr. Director of the IoT EDC   Building Block Overview As detailed quite extensively on its own designated Help Center page, building blocks are the future of scalable and maintainable IoT architecture. They are a way to organize development and customization of ThingWorx solutions into modular, well-defined components or packages. Each building block serves a specific purpose and exists as independently as possible from other modules. Some blocks facilitate external data integration, some user interface features, and others the manipulation or management of different kinds of equipment. There are really no limits to how custom a ThingWorx solution can be, and customizations are often a major hurdle to a well-oiled dev ops pipeline. It’s therefore crucial for us all to use a standard framework, to ensure that each piece of customization is insular, easy-to-replace, and much more maintainable. This is the foundation of good IoT application design.   PTC’s Building Block Framework At PTC, building blocks are broken down in a couple different ways: categories and types. The category of a building block is primarily in reference to its visibility and availability for use by the greater ThingWorx community. We use our own framework here at PTC, so our solution offerings are based around solution-specific building blocks, things which we provide as complete, SaaS solutions. These solution-specific building blocks combine up into single solutions like DPM, offerings which require a license, but can then easily be deployed to a number of systems. PTC solutions provide many, complex, OOTB features, like the Production Dashboard of DPM, and the OperationKPI building block.   Anyone doing any sort of development with ThingWorx, however, does still have access to many other building blocks, included with the domain specific building block category. These are the pieces used to build the solution building blocks like DPM, and they can be used to build other more custom solutions as well. Take the OperationKPI block which can vary greatly from one customer to the next in how it is calculated or analyzed. The pieces used to build the version that ships with DPM are right there, for instance, Shift and ReasonCode. They are designed to have minimal dependencies themselves, meant to be used as dependencies by custom blocks which do custom versions of the module logic found within the PTC solution-specific blocks. Then there are the common blocks as well, and these are used even more widely for things like user management and database connectivity.   The type of the building block refers really to where that building block falls in the greater design. A UI building block consumes data and displays it, so that is the View of a classic MVC design pattern. However, sometimes user input is needed, so perhaps that UI building block will depend on another block. This other block could have a utility entity that fuels UI logic, and the benefit to having that be a separate block is then the ease with which it can be subbed out from one ThingWorx instance to the next.   Let’s say there are regional differences in how people make use of technologies. If the differences are largely driven by what data is available from physical devices at a particular site, then perhaps these differences require different services to process user input or queries on the same UI mashups used across every site. Well in this case, having the UI block stand alone is smart, because then the Model and Controller blocks can be abstracted out and instantiated differently at different sites.   The two types that largely define the Model and Controller of the classic MVC are called Abstract and Implementation building blocks, and the purposes are intertwined, but distinct: abstract building blocks expose the common API endpoints which allow for the implementation blocks to vary so readily. The implementation blocks are then those which actually alter the data model, which is what happens when the InitializeSolution method is called. In that service, they are given everything they need to generate their data tables and data constraints, so that they are ready to be used once the devices are connected.   Occasionally, UI differences will also be necessary when factories or regions have different ways of doing things. When this happens, even mashups can be abstracted using abstract building blocks. Modular mashups which can be combined into larger ones can be provided in an abstract building block, and these can be used to form custom mashups from common parts across many sites in different implementation blocks all based on the same abstract one.   The last type of building block is the standard building block, which is the most generic. This one is not intended to be overridden, often serving as the combination of other building blocks into final solutions. It is also the most basic combination of components necessary to adhere to the building block design framework and interface with the shared deployment infrastructure. The necessary components include a project entity, which contains all of the entities for the building block, an entry point, which contains the metadata (name, description, version, list of dependencies, etc.) and overrides the service for automatic model generation (DeployComponent), and the building block manager. The dashed arrows indicate an entity implements another, and the solid arrows, extension. The manager is the primary service layer for the building block, and it makes most of the implementation decisions. It also has all of the information required to configure menus and select which contained mashups to use when combining modular mashups into larger views. The manager really consists of 3 entities: a thing template with the properties and configuration tables, a thing which makes use of these, and a thing shape which defines all of the services (which can often be overridden) which the manager thing may make use of.   Most building blocks will also contain security entities which handle user permissions, like groups which can be updated with users. Anyone requiring access to the contents of a particular building block then simply needs to be added to the right groups later on. This as well as model logic entities like thing shapes can be used concurrently to use organizational security for visibility controls on individual equipment, allowing some users to see some machines and not others, and so on.   All of the Managers must be registered in the DefaultGlobalManagerConfiguration table on the PTC.BaseManager thing, or in the ManagerConfiguration table of any entity that implements the PTC.Base.ConfigManagement_TS thing shape. Naming conventions should also be kept standard across blocks, and details on those best practices can be found in the Build Block Help Center.   Extending the Data Model Using Building Blocks Customizing the data model using building blocks is pretty straight-forward, but there are some design considerations to be made. One way to customize the data model is to add custom properties to existing data model entities. For instance, let’s say you need an additional field to keep track of the location of a job order, and so you could add a City field to PTC.JobOrder.JobOrder_AP data shape. However, doing this also requires substantial modification to the PTC.JobOrder.Manager thing, if the new data shape field is meant to interface with the database.   So this method is not as straight-forward as it may seem, but it is the easiest solution in an “object-oriented” design pattern, one where the data varies very little from site to site, but the logic that handles that data does vary. In this design pattern, there will be a thing shape for each implementing building block that handles the same data shape the abstract building block uses, just in different ways. A common use case for why this may happen is if the UI components vary from site to site or region to region, and the logic that powers them must also vary, but the data source is relatively consistent.   Another way to extend the data model is to add custom data shapes and custom managers to go with them. This requires you to create a new custom building block, one which extends the base entry point as needed for the type of block. This method may seem more complicated than just extending a data shape, but it is also easier to do programmatically: all you have to do is create a bunch of entities (thing templates, thing shapes, etc.) which implement base entities. A fresh data shape can be created with complete deliberation for the use case, and then services which already exist can be overridden to handle the new data shape instead. It is a cleaner, more automation friendly approach.   However, the database will still need to be updated, and this time CRUD services are necessary as well, those for creating and managing instances of the data shape. So, this option is not really less effort in the short term. In the long-term, though, scripts that automate much of the process for building block generation can be used to quickly and easily allow for development of new modules in a more complex ThingWorx solution. The ideal is to use abstract blocks defined by the nature of the data shape which each of their implementing blocks will use to hook the view into the data model.   This “data-driven” design pattern is one which involves creating a brand-new block for each customization to the data model. The functionality of each of these blocks centers around what the data table and constraints must look like for that data store, and the logic to handle the different data types should vary within each implementation block, but override the common abstract block interface so that any data source can be plugged into the thing model, and ThingWorx will know what to do with it.   The requests can then contain whatever information they contain (like MQTT), and which logic is chosen to process that data will be selected based on what information is received by the ThingWorx solution for each message. Data shapes can be abstracted in this way, so that a single subscription need exist to the data shape used in the abstract building block, and its logic knows how to call whatever functions are necessary based on whatever data it receives. This allows for very maintainable API creation for both data ingestion and event processing.
View full tip
Starting in ThingWorx 8.0, Application keys are now encrypted and stored in the database. The Key used to encrypt the Application key id value is stored in /ThingworxStorage/Keystore.jks and the password for the keystore is stored in /ThingworxPlatform/keystore-password. These files are created automatically by ThingWorx and unique to that instance. ThingWorx 8.1 In addition to the handling application key decryption, the Instance device ID is also stored in keystore.jks. To properly configure an HA landscape using ThingWorx 8.1, consider either; In a dark Site scenario, copying the license_capability_response.bin from the primary lead server to the ThingworxPlatform folder of all slave instances In a connected scenario, removing or renaming the existing capability response on the slave servers after replacing the keystore.jks and password-keystore to automatically retrieve a new capability response based on the encrypted device ID Failure to do so will result in a Host ID mismatch error [message: Trusted storage hostid does not match system hostid.]
View full tip
Announcements