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:
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
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 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
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
    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
    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
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
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 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
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
  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
  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
    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
Hey Community Members – I have exciting news to share! Last week, PTC announced that ThingWorx was recognized as a frontrunner IIOT Platform in four leading analyst research reports on the industrial software market.   In alphabetical order, the reports PTC/ThingWorx was named in were: ABI Research’s Smart Manufacturing Platforms Competitive Ranking The Forrester Wave™: Industrial IoT Software Platforms, Q3 2021 The Gartner® Magic QuadrantTM for Industrial IoT Platforms IDC MarketScape for Industrial IoT Platforms and Applications for Manufacturing Not only is PTC the only company included in all four reports, but it also placed as a leader in all four, which hopefully makes you feel confident in your choice to use ThingWorx. The research criteria the analyst firms use to make selections is unique to each firm, but to us, all of them measure the value ThingWorx can bring to an industrial business.   What value does ThingWorx bring to you?   Stay connected, Rachel               Gartner, Magic Quadrant for Industrial IoT Platforms, Alfonso Velosa, Ted Friedman, Katell Thielemann, Emil Berthelsen, Peter Havart-Simkin, Eric Goodness, Matthew Flatley, Lloyd Jones, Kevin Quinn, 18 October 2021 Gartner does not endorse any vendor, product or service depicted in its research publications, and does not advise technology users to select only those vendors with the highest ratings or other designation. Gartner research publications consist of the opinions of Gartner's research organization and should not be construed as statements of fact. Gartner disclaims all warranties, expressed or implied, with respect to this research, including any warranties of merchantability or fitness for a particular purpose. Gartner and Magic Quadrant are registered trademarks of Gartner, Inc. and/or its affiliates in the U.S. and internationally and is used herein with permission. All rights reserved. (this will be soon updated in our Policy as well)  
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
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
  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
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
    Step 3: Streams Mass data is typically stored in a structure functionally similar to a spreadsheet. Each separate column of the spreadsheet is exclusively one type of data (with each field directly beneath it holding different entries of that same type), while the rows determine the specific entry. Combine a column’s “type” with the row’s “index”, and you have the particular piece of data about which you care. ThingWorx mass data storage functions in roughly the same way. However, ThingWorx does have one differentiation from many spreadsheet implementations. In ThingWorx, the type that defines each column is not held internally to the data structure itself. Instead, it is abstracted out as a Data Shape. A Data Shape is the definition of what each column contains. A Stream requires a Data Shape in order to define its structure. Create Data Shape On the ThingWorx Composer Browse tab, click Modeling -> Data Shapes, + New.   In the Name field, enter Test_Data_Shape.   If Project is not already set, search for and select PTCDefaultProject.  At the top, click Field Definitions.   Click + Add. On the right in the Name field, enter Index_Field . Change the Base Type to INTEGER. Check the Is Primary Key checkbox. All Data Shapes must have a Primary Key, and Index_Field is an appropriate choice, as it will keep track of the number of entries.   At the top-right, click the "Check with a +" button for Done and Add. In the Name field, enter Value_Field. Change the Base Type to NUMBER.   At the top-right, click the "Check" button for Done. At the top, click Save. Once again, the Data Shape does nothing by itself. It simply defines that some other mass data structure (such as a Stream) follows the particular format of the Data Shape. So a Stream assigned the Test_Data_Shape above would have two fields per entry, i.e. an Index and a Value. Create Stream Streams are a mass data storage option optimized for Time-Series data. They are utilized in much the same way as Value Streams, but possess additional functionality.   For one, Streams do not have to be tied to Things in the way that Value Streams are. They can be entirely independent, or even accept inputs from multiple different Things. This can be useful when you want to have a master list of what every similar Thing was doing at particular points throughout some timeframe. On the ThingWorx Composer Browse tab, click DATA STORAGE -> Streams, + New.   On the Choose Template pop-up, select Stream and click OK.   In the Name field, enter Test_Stream.   If Project is not already set, search for and select PTCDefaultProject. In the Data Shape field, search for and select Test_Data_Shape. At the top, click Save.   Create Thing Now that the Stream is created, you'll create a Thing from which you can log some Properties. On the ThingWorx Composer Browse tab, click MODELING -> Things, + New.   In the Name field, enter Stream_Test_Thing. If Project is not already set, search for and select PTCDefaultProject. In the Thing Template field, search for and select GenericThing.   At the top, click Properties and Alerts.   Click + Add. In the Name field, enter Index_Property. Change the Base Type to Integer. Check the Persistent checkbox.   At the top-right, click the "Check with a +" button for Done and Add. In the Name field, enter Value_Property. Change the Base Type to Number. Check the Persistent checkbox.   At the top-right, click the "Check" button for Done. Add Service Since you now have both a Thing and a Stream, we can write a Service which logs the Properties' values when the Service is executed. At the top, click Services.   At the top, click + Add. In the Name field, enter Add_Stream_Entry_Service.   On the left under New Service, click the Snippets tab.   In the Filter field, type add str.   Expand the Stream, Blog, Data Table section to reveal the Add Stream Entry Snippet.   Click the right arrow beside Add Stream Entry. An Add Stream Entry pop-up will open.   In the Search Streams field, enter test.   Select Test_Stream.   Click the Insert Code Snippet button. Note that a section of Javascript code has now been added to the Script window.     Modify Snippet The inserted Javascript Snippet has already done most of the work of creating a custom Service for us. All which you need to do now is modify the code Snippet to store the particular Property values from your Thing. On the 10th line of code, double-click undefined to select it.   On the left, click the Me/Entities tab.   Under the Me/Entities tab, expand Properties. Note that this is not the Properties and Alerts at the top of Composer.   Click the right arrow beside Value_Property. Note that undefined has been replaced by me.Value_Property.   On the 11th line of code, double-click the remaining undefined to select it.   Click the right arrow beside Index_Property. Note that the second “undefined” has been replaced by “me.Index_Property”.   At the top, click Done to close the Add_Stream_Entry_Service editing window.   At the top, click Save.     Click here to view Part 3 of this guide.
View full tip
Announcements