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

Community Tip - Have a PTC product question you need answered fast? Chances are someone has asked it before. Learn about the community search. X

IoT Tips

Sort by:
    Explore the Value Stream, Stream, Data Table, and Info Table storage methods.     GUIDE CONCEPT   This guide will introduce Values Streams, Streams, Data Tables, and Info Tables.   Value Streams and Streams are methods of storage for time-series data, while Data Tables and Info Tables are methods of storage for non-time-series data.   You will learn how to create and utilize these mass data storage methods.   YOU'LL LEARN HOW TO   Differentiate between data storage methods Create a Data Shape to format a Stream, Data Table, and Info Table Create a Value Stream and Stream to store Time-Series Data Create a Data Table and Info Table to store non-Time-Series Data Use built-in methods to log data to a Value Stream or Info Table Create custom Services which log data to a Stream or Data Table Confirm data storage value changes via a built-in Service or Grid Widget   NOTE:  The estimated time to complete this guide is 60 minutes.     Step 1: Choosing Storage   If your data is largely composed of current values of remote IoT devices (and historical values are unimportant), then the simplest solution is most likely to store them as Properties of Things.   However, as opposed to instantaneous current values, there is also “mass data”. Mass data can include large datasets composed of historical records or spreadsheet-like grids.   Properties (at least by themselves) aren’t really appropriate in that case, as they typically only store the most recent value.   When it comes to mass data, different storage methods are good for different types.   At an extremely high level, ThingWorx divides mass data into roughly two categories: Time-Series - The Timestamp of the data is one of the most important elements. Non-Time-Series - The Timestamp of data changes is relatively insignificant.   In addition, ThingWorx also subdivides these two categories into either 1) tied to a Thing or 2) independent of any one Thing. Mass data storage that is tied to a Thing will logically only accept information from that one Thing, while independent storage may be used to aggregate information from many Things into a single location.    Storage Solution      Time-Series        Tied to a single Thing Value Streams YES YES Streams YES NO Data Tables NO NO Info Tables NO YES   The following pages will address these storage types in-depth.     Step 2: Value Streams   In this step, we'll create a Value Stream to be used as a storage location.   Value Streams by themselves do nothing. Instead, they must be tied to a Thing.   Create Value Stream On the ThingWorx Composer Browse tab, click Data Storage > Value Streams, + New.   In the Choose Template pop-up, select ValueSteam and click OK.   In the Name field, enter Test_Value_Stream . If Project is not already set, search for and select PTCDefaultProject. At the top, click Save. Create Thing Since Value Streams must be tied to a Thing, we'll create one now and then attach the previously-created Value Stream. On the ThingWorx Composer Browse tab, click MODELING > Things, + New .   In the Name field, enter Value_Stream_Test_Thing. If Project is not already set, search for and select PTCDefaultProject. In the Thing Template field, search for and select GenericThing. In the Value Stream field, search for and select Test_Value_Stream.   At the top, click Save.   Create Property   Now that we have a Thing with an attached Value Stream, we'll create a Property of that Thing and set it to be Logged.   If a Value Stream is attached to a Thing, value-changes of all Logged Properties will get automatically recorded along with a timestamp.   At the top, click Properties and Alerts.   Click + Add. In the Name field, enter Value_Property. Change the Base Type to Number. Check the Persistent checkbox. This causes the value of the Property to persist through system reboots. Check the Logged checkbox. This causes all value changes to be logged to the attached Value Stream.   At the top-right, click the "Check" button for Done. At the top, click Save. Set Property Values In a real-world application, Property value changes would likely occur through a connection to a remote IoT device. For simplicity, we'll instead manually change the values. Because the Property is Logged and there is a connected Value Stream, each change will be recorded and timestamped. Under the Value column for Value_Property, click the "pencil icon" for Set value of property.   At the top-right in the Set value of property field, enter 1.   At the top-right, click the "Check" button for Done. Again, click the "pencil icon" for Set value of property. At the top-right in the Set value of property field, enter 2.   At the top-right, click the "Check" button for Done. Again, click the "pencil icon" for Set value of property. At the top-right in the Set value of property field, enter 3.   At the top-right, click the "Check" button for Done. At the top, click Save. Retrieve Logged Values One of the most common ways to retrieve time-series information is with the built-in QueryPropertyHistory Service. QueryPropertyHistory may be used to retrieve logged value-changes for usage in a different, custom Service which could manipulate the data, as well as in Mashups for Widgets like a Grid or Line Chart. At the top, click Services.   Expand the Generic section.   Scroll down and find the QueryPropertyHistory Service.   Click the "Play" button for Execute service.   In the bottom-right of the Execute Service: QueryPropertyHistory pop-up, click Execute. Note the previously entered values of 1, 2, and 3, as well as their associated timestamps.   At the bottom-right, click Done.   A Value Stream is a simple and easy way to record all time-series changes of a particular Thing Property's values.   The QueryPropertyHistory built-in Service may be used both in a custom Service to extract the value changes and Timestamps from a Value Stream, as well as in a Mashup to display how values change over time (in a Line Chart, for instance).     Click here to view Part 2 of this guide.  
View full tip
  Create an Engine Failure Prediction GUI to warn about customer issues.   GUIDE CONCEPT   This guide will use ThingWorx Foundation's Mashup Builder to create a Graphical User Interface (GUI) to display results from Analytics Manager comparisons of your analytical model to real-time data.   Following the steps in this guide, you will learn how to utilize Widgets and backend data to quickly visualize customer failure conditions.     YOU'LL LEARN HOW TO   Create a Mashup Add Widgets to represent different data Bring Backend Data into the Mashup Tie data to Widgets View Analytical Results in a convenient GUI   NOTE:  The estimated time to complete all parts of this guide is 30 minutes.     Step 1: Scenario   In this guide, we're finishing up with the MotorCo scenario where an engine can fail catastrophically in a low-grease condition.   In previous guides, you've gathered and exported engine vibration-data from an Edge MicroServer (EMS) and used it to build an engine analytics model. You've even put that analytical model into service to give near-immediate results from current engine-vibration readings.   The goal of this guide is to create a GUI to visualize those predicted "low grease" conditions to facilitate customer warnings.     GUI-creation to visualize analytical model deployment can be extremely helpful for the automative segment in particular. For instance, each car that comes off the factory line could have an EMS constantly sending data from which an analytical model could automatically detect engine trouble.   This could enable your company to offer an engine monitoring subscription service to your customers.   This guide will show you how to visualize the results of an engine analytic model for a "smart, connected products" play.     Step 2: Create Mashup   Mashups are ThingWorx Foundation's method of creating Graphical User Interfaces (GUIs).   Mashups are created through the Mashup Builder interface.   Before you can use this drag-and-drop interface, you must first create a new Mashup.      1. In ThingWorx Foundation Composer, click Browse > Visualization > Mashups.      2. Click + New.        3. Leave the defaults and click OK.        4. In the Name field, type EFPG_Mashup.      5. If Project is not already set, search for and select PTCDefaultProject.        6. At the top, click Save.        7. At the top, click Design. You are now in the Mashup Builder interface, where you can drag-and-drop graphical elements (Widgets) to create your GUI.      8. At the top-left, click the Layout tab.        9. Change Positioning to Static. This removes any auto-positioning or dynamic-sizing from our Mashup, so that we can place and size Widgets manually.      10. At the top, click Save again.       Step 3: Add Widgets   We now want to add several Widgets to our Mashup by dragging-and-dropping them into the central Canvas area.   Return to the Widgets tab in the top-left.       2. In the Filter Widgets field, type label.        3. Drag-and-drop five (5) Label Widgets onto the central Canvas area.      4. Select the Label Widgets, go to the Properties in the bottom-left, and change the Label's LabelText to s1_fb1 through s1_fb5.         5. Filter Widgets for text field, and then drag-and-drop five (5) Text Field Widgets onto the central Canvas area.        6. Drag-and-drop two (2) more Label Widgets onto the central Canvas area.      7. Change their LabelText Properties to Result_low_grease and Result_low_grease_mo.        8. Drag-and-drop two (2) more Text Field Widgets onto the central Canvas area.        9. In the top-left Widget's tab, change Category to Legacy.      10. Drag-and-drop an Auto Refresh Widget onto the Canvas.        11. At the top, click Save.         Step 4: Add Data   Now that we have a rough set of Widgets in place to display Data in our Mashup, we need to bring in backend Data from Composer.      1. In the top-right, ensure the Data tab is active.      2. Click the green + button.        3. In the Entity Filter field, search for and select EdgeThing.      4. In the Services Filter field, type getprop.      5. Click the right-arrow beside GetProperties.      6. On the right, check Execute on Load.        7. In the bottom-right, click Done.      8. On the right, expand GetProperties.        9. At the top, click Save.       Step 5: Bind Data   Now that backend Data is accessible inside Mashup Builder, we need to bind it to the various Widgets.   Drag-and-drop Data > Things_EdgeThing > GetProperties > s1_fb1 to the top-left Text Field under the s1_fb1 Label Widget.           2. On the Select Binding Target pop-up, click Text.         3. Repeat for s1_fb2 through s1_fb5 on the 2nd-5th Text Field Widgets.       4. Drag-and-drop Data > Things_EdgeThing > GetProperties > Result_low_grease to the top-right Text Field under the Result_low_grease Label Widget, and select Text on the Binding pop-up.       5. Repeat for Result_low_grease_mo on the Text Field just below.       6. Click the Auto Refresh Widget to select it.       7. In the bottom-left Properties, set RefreshInterval to 1.         8. With the Auto Refresh Widget still selected, click the top-left corner to reveal a drop-down.       9. Drag-and-drop the Refresh Event onto Things_EdgeThing > GetProperties.       10. On the right, click the GetProperties Mashup Data Service to reveal all of its interactions in the bottom-center Connections window.              11. At the top, click Save.       Step 6: View Mashup   In the last guide, we disabled the Analysis Event after confirming that appropriate analytical jobs were being created and completed whenever new data from our EMS Engine Simulator entered Foundation.   We now need to re-enable that Event so that we can see the results in our Mashup.       1. Return to Analytics > Analytics Manager > Analysis Events.       2. Select the Event, and click Enable.   View Analytical Results   With the Analysis Event enabled, we can now view our Mashup to watch engine-failure-predictions be displayed in real time.       1. Return to EFPG_Mashup.       2. At the top, click View Mashup.       3. Wait and notice how the refresh occurs every second, including both true and false analytics results for the "low grease" condition.      4. When you are satisfied that you have observed enough true/false results, return to Analytics > Analytics Manager > Analysis Events and Disable the event.   Through this Learning Path, you have done all of the following:   Connect a remote device to Foundation using the Edge MicroServer (EMS) Fed relevant product-data to the Foundation backend Formatted and exported that data in a manner which Analytics could understand Imported that data and used it to build an Analytics Model of your remote device Started feeding real-time remote data into the model to achieve immediate failure-prediction results Create a GUI to easily visualize those results   You now have a completed Minimum Viable Product (MVP) for a Service Play with MotorCo's new engines.   These new engines may have a known-failure condition, but (because you've instrumented those engines to provide constant data) you can monitor them and predict failures before they actually happen.   This could easily be an up-sell scenario for MotorCo's customers. They could simply purchase the engine. Or they could both purchase the engine and enroll in a service contract where you notified them of impending issues.   What was originally a single-point source of income is now ongoing        Step 7: Next Steps   Congratulations. You've completed the Engine Failure-Prediction GUI guide. In this guide you learned how to:   Create a Mashup Add Widgets to represent different data Bring Backend Data into the Mashup Tie data to Widgets View Analytical Results in a convenient GUI   The next guide in the Vehicle Predictive Pre-Failure Detection with ThingWorx Platform learning path is Enhanced Engine Failure Visualization.   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 4: Write Data to External Database   You’ve connected to the database, you’re able to query the database. Now let’s handle inserting new data into the database. The update statements and data shown below are based on the table scripts provided in the download. Examples of how the ThingWorx entity should look can be seen in the SQLServerDatabaseController and OracleDatabaseController entities that you've downloaded   Running an Insert   Follow the steps below to set up a helper service to perform queries for the database. While other services might generate the query to be used, this helper service will be your shared execution service. In the DatabaseController entity, go to the Services tab.     2. Create a new service of type SQL (Command) called RunDatabaseCommand. 3. Keep the Output as Integer. 4. Add the following parameter:   Name Base Type Required command String True         5. Add the following code to your new service:   <<command>>       6. Click Save and Continue. Your service signature should look like the below example.     You now have a service that can run commands to the database. Run your service with a simple insert.   There are two ways to go from here. You can either query the database using services that call this service, or you can create more SQL Command services that query the database directly. Let’s go over each method next, starting with a service to call the helper.   In the Services tab of the DatabaseController entity, create a new service of type JavaScript. Name the service JavaScriptInsert_PersonsTable. Set the Output as InfoTable, but do not set the DataShape for the InfoTable. Add the following code to your new service: try { var command = "INSERT INTO Persons (person_key, person_name_first, person_name_last, person_email, person_company_name, " + "person_company_position, person_addr1_line1, person_addr1_line2, person_addr1_line3, person_addr1_city, person_addr1_state, " + "person_addr1_postal_code, person_addr1_country_code, person_addr1_phone_number, person_addr1_fax_number, person_created_by, " + "person_updated_by, person_created_date, person_updated_date) VALUES ('" + key + "', '" + name_first + "', '" + name_last + "', '" + email + "', '" + company_name + "', '" + company_position + "', '" + addr1_line1 + "', '" + addr1_line2 + "', '" + addr1_line3 + "', '" + addr1_city + "', '" + addr1_state + "', '" + addr1_postal_code + "', '" + addr1_country_code + "', '" + addr1_phone_number + "', '" + addr1_fax_number + "', '" + created_by + "', '" + updated_by + "', '" + created_date + "', '" + updated_date + "')"; logger.debug("DatabaseController.JavaScriptInsert_PersonsTable(): Query - " + command); var result = me.RunDatabaseCommand({command: command}); } catch(error) { logger.error("DatabaseController.JavaScriptInsert_PersonsTable(): Error - " + error.message); }         5. Add the following parameter:   Name Base Type Required key String True name_first String True name_last String True company_name String True company_position String True addr1_line1 String True addr1_line2 String True addr1_line3 String True addr1_city String True addr1_state String True addr1_postal_code String True addr1_country_code String True addr1_phone_number String True addr1_fax_number String True created_by String True updated_by String True created_date String True updated_date String True         6. Click Save and Continue.   Any parameter, especially those that were entered by users, that is being passed into a SQL Statement using the Database Connectors should be fully validated and sanitized before executing the statement! Failure to do so could result in the service becoming an SQL Injection vector.   Now, let’s utilize a second method to create a query directly to the database. You can use open and close brackets for parameters for the insert. You can also use <> as a method to mark a value that will need to be replaced. As you build your insert statement, use [[Parameter Name]] for parameters/variables substitution and <<string replacement >> for string substitution.   1. In the Services tab of the DatabaseController entity, create a new service of type SQL (Command).   2. Name the service SQLInsert_PersonsTable. 3. Add the following code to your new service: INSERT INTO Persons (person_key ,person_name_first ,person_name_last ,person_email ,person_company_name ,person_company_position ,person_addr1_line1 ,person_addr1_line2 ,person_addr1_line3 ,person_addr1_city ,person_addr1_state ,person_addr1_postal_code ,person_addr1_country_code ,person_addr1_phone_number ,person_addr1_fax_number ,person_created_by ,person_updated_by ,person_created_date ,person_updated_date) VALUES ([[key]] ,[[name_first]] ,[[name_last]] ,[[email]] ,[[company_name]] ,[[company_position]] ,[[addr1_line1]] ,[[addr1_line2]] ,[[addr1_line3]] ,[[addr1_city]]]] ,[[addr1_state]] ,[[addr1_postal_code]] ,[[addr1_country_code]] ,[[addr1_phone_number]] ,[[addr1_fax_number]] ,[[created_by]] ,[[updated_by]] ,[[created_date]] ,[[updated_date]]);       4. Add the following parameter:   Name Base Type Required key String True name_first String True name_last String True company_name String True company_position String True addr1_line1 String True addr1_line2 String True addr1_line3 String True addr1_city String True addr1_state String True addr1_postal_code String True addr1_country_code String True addr1_phone_number String True addr1_fax_number String True created_by String True updated_by String True created_date String True updated_date String True         5. Click Save and Continue.   Examples of insert services can be seen in the provided downloads.     Step 5: Executing Stored Procedures   There will be times when a singluar query will not be enough to get the job done. This is when you'll need to incorporate stored procedures into your database design.   ThingWorx is able to use the same SQL Command when executing a stored procedure with no data return and a SQL query when executing a stored procedure with an expected result set. Before executing these services or stored procedures, ensure they exist in your database. They can be found in the example file provided.   Execute Stored Procedure   Now, let's create the service to handle calling/executing a stored procedure.   If you are expecting data from this stored procedure, use EXEC to execute the stored procedure. If you only need to execute the stored procedure and do not expect a result set, then using the EXECUTE statement is good enough. You're also able to use the string substitution similar to what we've shown you in the earlier steps.   In the DatabaseController entity, go to the Services tab. Create a new service of type SQL (Command) called RunAssignStudentStoredProcedure. Add the following parameter:   Name Base Type Required student_key String True course_key String True         4. Add the following code to your new service:   EXECUTE AddStudentsToCourse @person_key = N'<<person_key>>', @course_key = N'<<course_key>>'; You can also perform this execute in a service based on JavaScript using the following code: try { var command = "EXECUTE AddStudentsToCourse " + " @student_key = N'" + student_key + "', " + " @course_key = N'" + course_key + "'"; logger.debug("DatabaseController.RunAssignStudentStoredProcedure(): Command - " + command); var result = me.RunDatabaseCommand({command:command}); } catch(error) { logger.error("DatabaseController.RunAssignStudentStoredProcedure(): Error - " + error.message); }         5. Click Save and Continue.   Execute Stored Procedure for Data   Let's create the entity you will use for both methods. This can be seen in the example below:     In the DatabaseController entity, go to the Services tab. Create a new service of type SQL (Query) called GetStudentCoursesStoredProcedure. Set the Output as InfoTable, but do not set the DataShape for the InfoTable. Add the following parameter:   Name Base Type Required course_key String True         5. Add the following code to your new service:   EXEC GetStudentsInCourse @course_key = N'<<course_key>>'   You can also perform this execute in a service based on JavaScript using the following code:   try { var query = "EXEC GetStudentsInCourse " + " @course_key = N'" + course_key + "'"; logger.debug("DatabaseController.GetStudentCoursesStoredProcedure(): Query - " + query); var result = me.RunDatabaseQuery({query:query}); } catch(error) { logger.error("DatabaseController.GetStudentCoursesStoredProcedure(): Error - " + error.message); }       6. Click Save and Continue.   You've now created your first set of services used to call stored procedures for data. Of course, these stored procedures will need to be in the database before they can successfully run.     Click here to view Part 3 of this guide.
View full tip
  Learn how to store and display medical device data for a Service opportunity.   Guide Concept   In this guide, you’ll learn how to combine information from multiple Edge devices into a single, logical Thing.   You’ll then create a GUI to display this combined information (as well as retrieve new information on demand) to facilitate a “Medical Service Play”.     You'll learn how to   Create a Data Shape and Info Table Property to store Medical Data Create a Service to combine data from multiple Edge devices into a single, logical Thing Create a Mashup to view and retrieve Medical data   NOTE:  The estimated time to complete ALL parts of this guide is 60 minutes     Step 1: Medical Learning Path   So far in this Learning Path, you've been able to connect both an embedded controller (simulated by a Raspberry Pi) and a PC to ThingWorx Foundation.   This is important, as medical devices can be complicated pieces of technology controlled by multiple "intelligent" subsystems. It is not always practical (or desirable) to have these subsystems communicate with each other, even if they all need to work together to function optimally.   Fortunately, Foundation has the capability to combine relevant data from multiple Edge devices into a single, logical Thing.   In this step of the Learning Path, you will do just that to facilitate a "Service Play".   The scenario is that your company manufactures and services Magnetic Resonance Imaging (MRI) devices.     Rather than simply servicing the MRI when there is an issue (and after a very expensive device has been damaged from something going wrong), your company maintains a continuing service contract with the hospital to monitor the MRI and perform preventative maintenance on it BEFORE anything goes wrong.   The value proposition to the hospital is the ability to keep their expensive investment in perfect operating order rather than suffering an unexpected failure. In turn, your company reaps the benefits of receiving a steady source of income from said service contract.   In order to achieve this level of preventative maintenance, your company needs to constantly monitor the MRI's various functions.   The MRI is composed of multiple working parts, but for this scenario, we'll limit our Minimum Viable Product (MVP) Service Application to the following:   An embedded device which monitors various hardware elements (such as magnet temperature and remaining coolant) A Windows PC (common in hospital equipment) which is used by a Medical Technician to control the MRI In particular, it's important to note that this PC has access to patient medical data, which can be subject to Health Insurance Portability and Accountability Act (HIPAA) violations. Fortunately here again, it's possible to segregate this information into protected and non-protected sections to limit your company's liability. We'll only propogate non-protected, generalized information to Foundation, such as the total number of scans that have been run thus far. This can facilitate your company getting a connected device approved by a hospital worried about HIPAA-compliance.   To help you run through this guide, we'll also utilize a pair of "simulators" to mimic data coming from the EMS-es on the Pi and PC, rather than directly taking information from them. So if you had any issues with the previous steps getting the EMS running on these devices, you can still complete this guide without issue.     Step 2: Import Simulators   As mentioned, we'll be using a pair of simulators to mimic connections to both an embedded microcontroller and a PC, both of which are part of the MRI.   Perform the following steps to import the simulators.   Download and unzip the MDSD_Entities.zip file attached to this article. In the bottom-left of Foundation Composer, click Import/Export.   Click Import.   On the Import pop-up, click Browse. Navigate to the download location and select the MDSD_Entities.twx file.   Click Open.   Click Import.   Click Close.   Note how there are now several MDSD Entities. These represent Things connected to different parts of the MRI which are communicating to Foundation via the EMS Agent (as per the previous guides in this Learning Path).   Investigate Simulators   Since there are multiple sub-systems which are all communicating directly to Foundation (but we really only want to check the status of the MRI as one logical entity), we need to know what exactly is being communicated back.   What is being communicated was likely determined by your company's Edge Developers when they implemented the EMS agent on said sub-systems.   Perform the following steps to investigate the simulators.   Click MDSD_Embedded_Thing.   Click Properties and Alerts.   So the embedded microcontroller has sensors which are tracking the following:    Property                           Units                       Description Coolant Percent Percent Amount of coolant left to refrigerate the super-conducting magnets Field Strength Tesla Strength of the magnetic field Magnet Temperature Degrees Celsius Temperature of the magnets   Next, let's look at the other simulator. Return to Browse > All. Click MDSD_PC_Thing. Click Properties and Alerts.   The PC is tracking information from the MRI controlling software, including the following:   Property Units  Description Number of Scans Scans Aggregate count of all scans the MRI has performed since last reset SSD Space Open Megabytes Amount of space left on the hard-drive Unused RAM Megabytes Amount of RAM still available to the system   Both of these remote devices are communicating valuable information.   The embedded microcontroller is feeding us information about the hardware of the MRI itself. Refilling coolant as needed is likely one of the service contract stipulations and will be a regular service need. And a Field Strength drop or Magnet Temperature rise could indicate more significant issues which could damage the MRI if not promptly addressed.   The PC gives us information about the operation of the MRI. Hard-drive or RAM running low could indicate that the hardware specs of the PC need to be upgraded, while the Number of Scans can give us a rough estimate of how much refrigerant should have been used versus the currently available level. If only a handful of Scans have been run, but the amount of coolant has dropped by a significant amount, then there could be a leak somewhere which would need to be addressed.   The combination of the Embedded and PC data is enough for us to begin working on a constantly-monitoring application which will facilitate our Service Play.       Step 3: Create Data Shape   Now that we're aware of what information is coming from the separate sub-systems of the MRI, we need to combine them into a single, logical Thing for the purposes of a Service Play.   To do so, we'll create a Thing which represents the MRI as a whole. Additional sub-systems can be added to to the collective information of this Thing as is necessary. But, as stated, this is simply an MVP, so we'll stick to the embedded and PC sub-systems at present.   A good way to aggregate multiple data points into a single item is via an Info Table Property. However, any time you create an Info Table, you also need a Data Shape to format the "columns" of the spreadsheet-like Property.   For more information on the storage of "mass-data", please refer to the Methods for Data Storage guide.   Perform the following steps to create a Data Shape.   Click Browse > Modeling > Data Shapes.   Click + New. In the Name field, type MDSD_DataShape.   If Project is not already set, search for and select PTCDefaultProject. At the top, click Field Definitions.   Click + Add.   Embedded Definitions   We now want to add Field Definitions to the Data Shape which map the information that we want to collate between the various sub-systems.   We'll start with the Embedded Microcontroller Properties.   On the far-right in the Name field, type Coolant_Percent. Change the Base Type to Number.   At the top-right, click the "Check with a +" for Done and Add. In the Name field, type Field_Strength. Change the Base Type to Number.   Click the "Check with a +" for Done and Add. In the Name field, type Magnet_Temperature. Change the Base Type to Number.   Click the "Check" button for Done. At the top, click Save.   PC Definitions   Additional Definitions can be added at anytime as other sub-systems are included.   We'll now include the PC Properties.   Click + Add. In the Name field, type Number_of_Scans. Change the Base Type to Number.   Click the "Check with a +" for Done and Add. In the Name field, type SSD_Space_Open. Change the Base Type to Number.   Click the "Check with a +" for Done and Add. In the Name field, type Unused_RAM. Change the Base Type to Number.   Click the "Check" button for Done. At the top, click Save.   Timestamp   It might also be benificial to include a Timestamp of when the values of both the embedded microcontroller and PC were added.   Click + Add. In the Name field, type Timestamp. Change the Base Type to DATETIME.   Click the Check button for Done. Click Save.     Click here to view Part 2 of this guide.
View full tip
    Step 6: Create Master   Now that we have created our Menu Entity, we can create a Master Mashup to hold a Menu Widget. We'll use the Menu Entity to configure the Menu Widget.     Navigate to Browse > Visualization > Masters.    Click +New.    Leave the defaults and click OK.    In the Name field, type MNWM_Master. If Project is not already set, search for and select PTCDefaultProject.    At the top, click Save.    At the top, click Design.     Add Header   Now that we've created the Master Entity, we need to add space for the Menu Widget.   At the top-left, click the Layout tab.    Click Add Top.   With the new top-section still selected, scroll down in the Layout tab, and select Container Size > Fixed Size.   In the new Height field, type 50, then hit your keyboard's Tab key to apply the change.    At the top, click Save.   Add Menu Widget Now that we have somewhere to put it, we can finally add the Menu Widget and then configure it.   In the top-left, click the Widgets tab.   Drag-and-drop a Menu Widget onto the top Canvas section.   Ensure the Menu Widget is still selected, as well as the Properties tab in the bottom-left.   In the Filter field, type menu.   For the Menu Property, search for and select MNWM_Menu.    At the top, click Save.     Step 7: Menu Navigation   We now have all the elements created to navigate using a Menu. The only thing left is to assign the Master to our Homepage and then view our work.     Return to MNWM_Homepage_Mashup.    In the top-left, click the Explorer tab.   Ensure that Mashup is selected. On the bottom-left Properties tab, in the Filter field, type master.   For the Master Property, search for and select MNWM_Master.    At the top, click Save. At the top, click View Mashup. You may need to enable "pop-ups" in your browser.   Click Park.   Click Amphitheater.   Note that you may wish to set MNWM_Master for the other pages of your application, just in case anyone were to navigate directly there via URL. But simply setting it on the homepage is enough for us to see that Menu navigation is functioning correctly.       Step 8: Next Steps   Congratulations! You've successfully completed the Mashup Navigation with Menus guide. In this guide, you learned how to:   Create a Mashup to be used as a "Home" page Create more Mashups as subpages Create a Menu Entity to track Mashups Create a Master Mashup as a Header Utilize a Menu Widget for navigation   Learn More    Capability     Resource Experience Track Issues with Pareto Chart   Additional Resources   If you have questions, issues, or need additional information, refer to:    Resource       Link Community Developer Community Forum Support Foundation Help Center - Menu Entity Support Foundation Help Center - Menu Widget    
View full tip
  Utilize the Menu Widget to navigate between multiple Mashups.   Guide Concept   Frequently, your application needs to display more information than can comfortably fit on a single page, but manually linking to multiple Mashups can be tedious.   Instead, you can utilize a Menu Entity, Menu Widget, and a Master Mashup to provide consistent navigation between all the pages of your application.      You'll learn how to   Create a Mashup to be used as a "Home" page Create more Mashups as subpages Create a Menu Entity to track Mashups Create a Master Mashup as a Header Utilize a Menu Widget for navigation   NOTE: This guide's content aligns with ThingWorx 9.3. The estimated time to complete this guide is 30 minutes     Step 1: Scenario   In this guide, we'll assume a scenario where you're an application designer for LightCo, a company that designs and sells smart, connected street, interior, and other lighting products.    In particular, LightCo is interested in using ThingWorx Foundation to operate and control lighting for industrial and city applications.    There's a tentative deal in the works with a city park replacing all their path lighting with high-efficiency models. There's also an outdoor amphitheater in the park that could be added to the deal if the functionality to properly control it is present.     LightCo's intention is to use Foundation to provide a level of control for this application that otherwise wouldn't be possible, as a differentiator versus the competition bidding on the same project.   Since control of both the park-lighting and amphitheater lighting needs to be separate, putting both controls on the same Mashup page would not only be counterintuitive but could possibly lead to errors.    So, in this guide, we'll setup our base page layout for this lighting application. We'll create both a homepage (where we could add login or other elements), as well as separate pages for the park vs the amphitheater. We'll then create a Master Mashup with a Menu for easy navigation between these three initial pages.       Step 2: Create Home Page   First, we want to create a Mashup to represent our homepage.    There doesn't have to be much to it, as we could come back later to develop it further. We just need it to be pre-created to later include it in our Menu.    In Foundation, click Browse > Visualization > Mashups.   At the top-left, click +New.   Leave the defaults and click OK.   In the Name field, type MNWM_Homepage_Mashup. If Project is not already set, search for and select PTCDefaultProject.   At the top, click Save.   At the top, click Design.     Homepage Layout   As mentioned, we don't really want to design our final Homepage right now. Instead, we're simply getting it started to leave ourselves a relatively blank canvas that we can utilize later.    Still, we need to add something. If nothing else, we need something that we can see so that we know we're on the correct page when we click through our Menu options.   We know we'll want our company's name, i.e. LightCo, and probably an image, login, or something else below. So let's subdivide the Mashup into a smaller top part for the name and a bigger bottom section for future functionality.   In the top-left, ensure that the Layout tab is active.   Click Add Top.   Scroll down in the Layout tab to reveal Container Size.   With the top-section of the Canvas selected, click Fixed Size.   In the Height field, type 100 and hit your keyboard's Tab key to apply the change.   Click Save.   Add Company Name   In the top section, we'll add a Label Widget for our company's name.   At the top-left, click the Widgets tab.   Drag-and-drop a Label Widget onto the top section of the Canvas.   With the Label Widget selected, in the bottom-left Properties tab, change LabelText to LightCo and hit your keyboard's Tab key to apply it.   With the Label Widget still selected, in the bottom-left, click the Style Properties tab.   Under Style Properties, expand Base > Label.   Change font-size to 48px.   Click Save.     Step 3: Create Subpage One   Now that we have a basic LightCo homepage in-place, we need subpages to separately control the park-lighting vs the amphitheater-lighting.    But, yet again, we're not designing the actual pages yet. We're just creating them so we can reference them in a Menu.    An easy way to create additional Mashups that have similar layouts is with the Duplicate action. We'll do so now.    Return to Browse > Mashups.   To the left of MNWM_Homepage_Mashup, click the checkbox.   At the top, click Duplicate.   In the Name field, type MNWM_ParkLighting_Mashup. If Project is not already set, search for and select PTCDefaultProject.   At the top, click Save. At the top, click Design.   Change Label Title   Since we used Duplicate to clone the homepage, we see the same header/page Layout which we previously designed. That Layout can be useful for separating page-titles from functionality, so let's keep it.   But we also see the LightCo LabelText we previously created. Since this is the Park Lighting Control page, we need to change it.   In the top section of the Canvas, click the Label Widget to select it.   In the bottom-left, return to the Properties tab.   In the LabelText field, replace the current text with Park Lighting Control, and hit your keyboard's Tab key to apply the change.   At the top, click Save.     Step 4: Create Subpage Two   In the same way that we used Duplicate to mirror the homepage into a park-lighting page, we'll now do the same for the amphitheater-lighting page.    Return to Browse > Mashups. To the left of MNWM_Homepage_Mashup, click the checkbox. At the top, click Duplicate. In the Name field, type MNWM_AmphitheaterLighting_Mashup. If Project is not already set, search for and select PTCDefaultProject.   At the top, click Save. At the top, click Design.   Change LabelText   Just as we did with the Park Lighting Control LabelText, we now want to change this duplicate to read Amphitheater Lighting Control.   In the top section of the Canvas, click the Label Widget to select it. In the bottom-left, return to the Properties tab (if you're not already there). In the LabelText field, replace the current text with Amphitheater Lighting Control, and hit your keyboard's Tab key to apply the change.   Click Save.     Step 5: Create Menu   Having created all three of our starting Mashups, we'll now create a Menu Entity which provides links to each of them.   We'll be able to use this Menu Entity later to configure a Menu Widget for navigation.    Navigate to Browse > Visualization > Menus.   Click +New.    In the Name field, type MNWM_Menu. If Project is not already set, search for and select PTCDefaultProject.    At the top, click Save.   Menu Definitions   Now that we have created the Menu Entity, we can add Menu Definitions with links to all the previously-created pages.   At the top, click Menu Definition. Click +Add.   On the right-side slide-out, in the Title field, type Home. In the Link field, search for and select MNWM_Homepage_Mashup.   In the top-right, click the "check with a plus" button for Done and Add.   In the Title field, type Park. In the Link field, search for and select MNWM_ParkLighting_Mashup.   In the top-right, click the "check with a plus" button for Done and Add. In the Title field, type Amphitheater. In the Link field, search for and select MNWM_AmphitheaterLighting_Mashup.   At the top-right, click the "check" button for Done. At the top, click Save.     Click here to view Part 2 of this guide.
View full tip
  Step 5: Add Data   Now that our Mashup's Layout and Widgets have been set, we need to bring in backend data and tie it to those Widgets for display.    We'll make use of Mashup Data Services for this. The first one we'll add will be UserManagementSubsystem > GetUserCount.   Ensure that the Data tab in the top-right is active.   Click the + icon.   In the Entity field, search for and select UserManagementSubsystem.   In the Services field, type GetUserCount.   Beside the GetUserCount Service, click the right arrow. Under Selected Services, check the box for Execute on Load.     Add LicensingSubsystem > GetCurrentLicenseInfo   You're not limited to only pulling information from one Mashup Data Service.   We already have User Management's GetUserCount. We'll now also bring in information from the Licensing Subsystem.   On the same Add Data pop-up, click the X beside UserManagementSubsystem to clear it.   In the Entity field, search for and select LicensingSubsystem.   In the Services field, type GetCurrentLicenseInfo.   Beside GetCurrentLicenseInfo, click the right arrow. Under Selected Services on the GetCurrentLicenseInfo line, check the box for Execute on Load.   On the bottom-right of the pop-up, click Done.   At the top, click Save.   Bind Data   With our backend data now accessible, let's bind it to our Widgets for display.   On the Data tab, expand Subsystems_LicensingSubsystem > GetCurrentLicenseInfo > Returned Data.   Drag-and-drop Licensing's All Data onto the Grid Advanced Widget in the bottom section.   On the Select Binding Target pop-up, click Data.   Expand Subsystems_UserManagementSubsystem > GetUserCount > Returned Data > All Data.   Drag-and-drop User Management's result onto the Text Field Widget in the top section.   On the Select Binding Target pop-up, click Text.   At the top, click Save.   Automatically Refresh   If we were to click View Mashup now, we would see the User Count and Licensing info displayed in the Mashup's Widgets. However, it would never update unless we manually refreshed the page.   To automate that process, we'll use the Auto Refresh Widget.   In the top section of the Mashup, click the Auto Refresh Widget to select it.   On the top-left of the Auto Refresh Widget, click the drop-down icon to reveal additional options.   Drag-and-drop the Refresh Event onto GetCurrentLicenseInfo.   Repeat steps 1-3 to drag-and-drop the Refresh Event onto GetUserCount also.   With the Auto Refresh Widget still selected, browse the Properties section in the bottom-left.   Change RefreshInterval to 5, and hit your keyboard's Tab key to lock in the change. This will cause the Refresh Event to fire every 5 seconds.   Click Save. Click View Mashup.     Step 6: Replace License   We have now created a Minimum Viable Product (MVP) of a "licensing dashboard" to ease our administration work.   This dashboard could still be improved. For instance, a Mashup Function could be created which automatically recalculates the value from GetUserCount to more accurately match GetCurrentLicenseInfo. This can be accomplished via the Mashup Builder's bottom-right Functions tab. Or, you could even add a separate Text Field Widget to to the top section and directly access the remaining license time via the LicensingSubsystem > GetDaysRemainingInLicense Service.    Whatever you choose to do to improve the Mashup is up to you.   However, what should be done when a license is nearing its expiration? To resolve this issue, we need to replace the existing license via the AcquireLicense Service.   Within the OS of the Foundation server, navigate to the file-system folder where the current license_capability_response.bin is located. This is typically the ThingworxPlatform folder.   Move the existing license_capability_response.bin and any other existing *.bin files to another location for backup. Note that this is EXTREMELY important, as if anything goes wrong, you want to be able to restore the original license. It is very important to ensure that only one *.bin file exists in the appropriate folder Place the new .bin file into the appropriate folder and rename it to license_capability_response.bin. In Foundation, navigate to the LicensingSubsystem > Services page.   On the AcquireLicense row, click the "play" icon for Execute Service.   On the bottom-right of the pop-up, click Execute. Note that if you receive an error, it will be necessary to restart the ThingWorx Tomcat service.  The act of restarting the service will automatically load the new license.   To close the pop-up, click Done.   After acquiring your new license, you should immediately return to the LicensingSubsystem and consult the GetCurrentLicenseInfo Service to confirm that Thing, User, expiration-date, and other issues are accurate.   If anything goes wrong with the new license transfer, you can simply replace the original license_capability_response.bin file and re-run AcquireLicense to return to your old one. Once again, it is import to perform these steps well before your expiration occurs.    Foundation checks for a valid license periodically. If this happens while you have an invalid license_capability_response.bin file in-place, you will no longer be able to access the Foundation GUI.    Fortunately, these issues are self-correcting if a valid license_capability_response.bin is placed in the correct location, as these periodic checks will also pick up a valid license_capability_response.bin file automatically, even after the system has locked you out.    You should also be able to trigger an AcquireLicense action via a REST call even if the GUI is unavailable.      Step 7: Next Steps   Congratulations! You've successfully completed the Manage Licensing with Foundation Subsystems guide.   In this guide, you learned how to:   Access the Foundation Subsystems Execute built-in Services to retrieve: User counts Thing counts License expiration count Create a "License Dashboard" Mashup Update to a new License    Additional Resources   If you have questions, issues, or need additional information, refer to:    Resource       Link Community Developer Community Forum Support Foundation Help Center  
View full tip
  Use Subsystems to retrieve User/Thing-count and License-expiration information   Guide Concept   In this guide, you'll learn how a Foundation Administrator can access important license-accounting Subsystems.    In particular, we'll look at the User Management and Licensing Subsystems, and how you can use them to ensure that you're not getting close to running out of Users, Things, or your time-to-expiration.    We'll also explore how you can import a new license_capability_response.bin file when your original license is running low on time.      You'll learn how to   Access the Foundation Subsystems Execute built-in Services to retrieve: User counts Thing counts License expiration count Create a "License Dashboard" Mashup Update to a new License   NOTE: The estimated time to complete ALL parts of this guide is 30 minutes     Step 1: Introduction   ThingWorx Foundation uses a licensing system based around a file named license_capability_response.bin.  However, if you're using the downloadable installer, then it's possible that you've never even touched this part of the system before.   Besides doing the initial install, you'll also have to check to see that your license has enough Users, Things, and time left before expiration. You need to ensure that a new license is acquired and properly replaced beforehand... or else your Foundation installation may become unusable.    Therefore, this guide will run you through how to access that information, as well as how to update to a new license_capability_response.bin file when the time is right.      Step 2: Get User Count   The first item we'll investigate is checking on our number of Foundation Users.    When first developing an IoT application, low User counts are typically the norm. Only your team really needs access to Foundation itself, so having only a few Users more than your R&D-team-size is possibly going to be sufficient.    And, if your application is something along the lines of factory-monitoring, then it's possible that your User counts, even when deployed, are going to continue to stay relatively low.    However, many IoT applications involve a tremendous number of Users, as your end-customers will generate a Foundation User whenever they sign up for your application. Think something along the lines of a ride-sharing app, or even a Smart Cities play... either of those can result in thousands (if not tens-of-thousands) of Users.    As such, a Foundation system administrator will need to keep a tight track on the User counts to ensure, whenever you're approaching your upper threshold, that enough warning is given to provide time to receive and install a new license_capability_response.bin with a larger User count.    Navigate to Foundation Composer's Browse > All.   On the left-side Navigation, scroll down until you see the System section. Note that you will likely be unable to even see the System section unless you are an Administration-level User.   Click Subsystems.   Click UserManagementSubsystem.   At the top, click Services.   Scroll down and find the built-in GetUserCount Service.   On the line for GetUserCount, click the "play" icon for Execute Service.   At the bottom-right of the pop-up, click Execute.   Click Done to close the pop-up. The return value from GetUserCount is one way to reveal how many current Users are provisioned for your Foundation system.    Moving forward, we'll explore yet another way, while also looking into our Thing counts.    In particular, you might find that the GetUserCount value doesn't completely match due to some internal system accounts. These system accounts are not counted against your license, however, and may need to be accounted for when using the GetUserCount Service.        Step 3: Get Current License Info   While GetUserCounts can be helpful for getting the current amount of provisioned Users, it does nothing to compare that count to the total allowed by the license.    Instead, we'll make use of a different Subsystem, i.e. the LicensingSubsystem.    Return to Browse > System > Subsystems.   Click LicensingSubsystem.   At the top, click Services.   Scroll down until you find GetCurrentLicenseInfo.   On the GetCurrentLicenseInfo line, click the "play" icon for Execute Service.   On the bottom-right of the pop-up, click Execute.   When you're done analyzing the counts, click Done to close the pop-up.   The first thing to notice is that the InUseFeatureCount for twx_named_user possibly does not match the return of GetUserCount. As already mentioned, this is because of system accounts that are not counted against your license.    For example, for a fresh installation, the GetUserCount may return 4, while InUseFeatureCount returns 2. The 2-count is more accurate, as it is used versus your total license-amount. However, GetCurrentLicenseInfo is less useful for doing a simple comparison between a stored "last user amount" vs "current user amount".    The solution is simple. Compare the return of GetUserCount versus the return of GetCurrentLicenseInfo to determine the true total. In this case, the number of system accounts is 2, so some "GetUserCount - 2" custom-Service could be very helpful.   In addition, GetCurrentLicenseInfo returns the very important twx_things value, i.e. how many Thing Entities have been created in the system. This is typically another hard limit in your license, and needs to be watched over.    Finally, the DaysRemaining column shows how long you have before your license becomes inactive. This is something which needs to be constantly monitored to ensure that your Foundation system as a whole is still running!   Next, we'll explore making a Mashup to reference these built-in Services in a more comfortable environment which auto-updates and can be used as, effectively, a Foundation system dashboard.     Step 4: Create Dashboard Mashup   While the User Management and Licensing Subsystems can be used as previously described to determine relevant admin information, traversing through the Foundation backend can be tedious.    To make User, Thing, and License Expiration Date counts easier to monitor, we'll now create a "dashboard" which automatically pulls this information into a convenient Mashup.    Navigate to Browse > Visualization > Mashups.   Click + New.    Leave the defaults and click OK.   In the Name field, type Licensing_Mashup.   If Project is not set, search for and select PTCDefaultProject. Click Save. Click Design.   Set Layout   Now that we have a new Mashup, we'll start adding the items we'll need to display information from the Subsystems.    First, we'll change the Layout.   In the top-left, ensure that the Layout tab is active.   Click Add Top.   Ensure the new top section is selected, and set Positioning to Static.   Scroll down in the Layout tab, and set Container Size to Fixed Size.   In the new Height field, type 100, and then hit your keyboard's Tab key to lock-in your change. Note that the px will be automatically added after hitting the Tab key.   At the top, click Save.   Add Widgets   Now that we have the Layout we want, we can add Widgets to display data coming from the backend.   In the top-left, click the Widgets tab.   Drag-and-drop a Label Widget onto the top section.   With the Label still selected, in the bottom-left Properties section, change LabelText to User Count, and hit Tab.   Drag-and-drop a Text Field Widget, also in the top section, and below the Label.   In the top-left Widgets tab, change Category to Legacy. Drag-and-drop an Auto Refresh Widget in the top section as well.    In the top-left Widgets tab, change Category back to Standard. Drag-and-drop a Grid Advanced Widget onto the Bottom section.   At the top, click Save.   Click here to view Part 2 of this guide.
View full tip
  Step 7: Set-up and Run Demo   The ThingWorx Azure IoT Connector download includes a Java application that simulates a device connecting to your Azure IoT Hub. A ThingTemplate is also included and can be imported into ThingWorx.   Import Demo Templates   In ThingWorx Composer, click Import/Export menu, then click From File and browse to ../demo/edgedevice- demo/platform/entities/CPUDemo_AllEntities.xml   Click Import then click Close when the import successful message is displayed. Create a new Thing using the imported template azureDemo1, enter a name for your Thing and click Save. NOTE: You will enter this name in the demo config file in the next step. Configure Demo Application   In the ../demo/edge-device-demo/conf subdirectory, open the edge-device.conf file with a text editor. Edit the deviceId to be the name of the Thing you created in step 3. Edit the iotHubHostName to use the name of your hub plus the domain: azure-devices.net. For example, sample-iot-hub.azuredevices.net. Edit the registryPolicyKey property to use the Primary Key for the registryReadWrite policy in the Azure IoT Hub. Below is an example configuration: azure-edge-device { deviceId = "alstestedgething" iotHubHostname = "alsiot.azure-devices.net" registryPolicyName = "registryReadWrite" registryPolicyKey = "pzXAi2nonYWsr3R7KVX9WuzV/1234567NZVTuScl/Kg=" }   Run Demo Script   Open a shell or Command Prompt, set the EDGE_DEVICE_DEMO_OPTS environment variable to refer to the file you just edited: Linux - export EDGE_DEVICE_DEMO_OPTS="-Dconfig.file=../conf/edge-device.conf" Windows - set EDGE_DEVICE_DEMO_OPTS="-Dconfig.file=../conf/edge-device.conf" Launch the demo from the ../demo/edge-device-demo/bin subdirectory, using the edge-device-demo command. Return to the ThingWorx Composer and open the Properties page of the Azure Thing that you created previously. Click the refresh button to see the properties change every five seconds. Open the azure-iot-demo Mashup and view the Load Average and CPU gauges, and the increases in the values of the Cycle and Uptime fields. NOTE: If the edgedevice-demo is running on Windows, the Load Average does not register.   Step 8: Next Steps   Congratulations! You've successfully completed the Connect Azure IoT Hub to ThingWorx Quickstart. By following the steps in this lesson, you imported a device created in Azure into ThingWorx and saw how data from an Azure device could be used in a ThingWorx Mashup.   Learn More   We recommend the following resources to continue your learning experience:   If you're following the Azure MXChip Development Kit learning path, the next guide is Create a Thing Shape.    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
  Use the ThingWorx Azure IoT Hub Connector with simulated appliances.   Guide Concept   This project will introduce how to integrate ThingWorx with Azure IoT Hub. The combination of these two platforms extends the ThingWorx utilities to Azure IoT Hub edge devices and allows integration with Azure Blob Storage accounts.   Following the steps in this guide, you will install the ThingWorx Azure IoT Hub Connector and run a simulated Azure device.   We will teach you how to build powerful and scalable IoT applications by integrating ThingWorx and Azure IoT Hub.   You'll learn how to   Install, configure, and run the ThingWorx Azure IoT Hub Connector Import devices that exist in Azure into ThingWorx Connect a simulated Azure device to ThingWorx Foundation server   NOTE: This guide's content aligns with ThingWorx 9.3. The estimated time to complete this guide is 60 minutes       Step 1: Overview Diagram   The ThingWorx Azure IoT Hub Connector maintains a network connection to both an Azure IoT and a ThingWorx Foundation instance:        The ThingWorx Azure IoT Hub Connector enables remote devices that connect to the Azure IoT Hub, to connect to the ThingWorx Foundation server. The Azure IoT Connector handles message routing for the devices that communicate through the Azure IoT system. It also handles message routing from the ThingWorx Foundation server to devices via the Azure IoT Hub.   The ThingWorx Azure IoT Connector is a separate, stand-alone application that must be run on a server that can connect to both the ThingWorx server and the Azure IoT service.     Step 2: Configure Azure IoT Hub   In order to use the ThingWorx Azure IoT Connector, you must first configure an IoT Hub and a storage account in Microsoft Azure. You can provision a free tier account for these resources. In this step, we will create the Azure resources and gather the configuration information that enable you to connect to ThingWorx with the required credentials.   Log into Azure Portal   If you do not already have an Azure account you can create a free account that will work with this guide.   Create Azure IoT Hub   Follow the Microsoft documentation to create an Azure IoT Hub, accepting any defaults.   TIP: The name your IoT Hub must be globally unique and include only lowercase letters and numbers.   1. Create message routes to direct DeviceLifecycleEvents and TwinChangeEvents events to the built-in events endpoint. For a tutorial, refer to Tutorial: Use the Azure CLI and Azure portal to configure IoT Hub message routing 2. Register at least one Azure IoT Device or Azure IoT Edge Device to your Azure IoT Hub. For a tutorial, refer to Register an IoT Edge device in IoT Hub     Create Storage Account   Follow the Microsoft documentation to create an Azure Storage account.   NOTE: Select Blob storage as the account type and the Hot Access Tier.       Step 3: Import Extensions   The ThingWorx Azure IoT Hub Connector distribution bundle comes packaged with all the software you will need to connect ThingWorx and Azure. Download the Azure IoT Hub Connector from PTC Support Extract the application bundle to a directory on the system where it will run. (where v.v.v represents the release number) On Linux, this guide uses the base directory, /opt. The subdirectories and files should reside in the directory, /opt/ThingWorx-Azure-IoTHub-Connector-4.2.0. On Windows, extract the bundle so that the subdirectories and files reside in C:\ThingWorx-Azure-IoT-Hub-Connector-4.2.0 In the lower-left side of Composer, click Import/Export, then Import.  In the Import From File pop-up, under Import Option select Extension from the drop-down, then click Browse. Navigate to the /extensions directory and click on ConnectionServicesExtension-2.2.4.zip file.   Click Import in the Import From File pop-up, then click Close after file is successfully imported. Repeat the above steps for the azure-iot-hub-adapter-extension-4.2.0.4.zip file. Follow the steps to Create an Application Key and note the value, it will be used in the the next step.     Step 4: Install Azure Connector   Configure Connection Server   The Connection Server component of ThingWorx Azure IoT Connector must be configured with information specific to both your Azure IoT Hub and your ThingWorx Foundation server.   Copy the file azure-iot-sample.conf in the connector > conf directory and save the file with the name azure-iot.conf Edit the configuration file to replace the ten placeholder values for the parameters listed below with values copied from your Azure control panel. consumerPolicyName - Whether you have a new or existing hub, you need to provide the name of the consumer policy and its related Primary or Secondary key. The policy to select is typically the built-in, pre-defined policy called service. Navigate to All Resources > your hub > Settings > Shared access policies > service. If you added a custom service policy to your hub with the permission service connect select that policy.   consumerPolicyKey - Once you find the policy name, stay in the Shared access policies screen, and click the name. Copy the content of the Primary key. This key supplies the credentials to access services that are specified in the related policy. registryPolicyName - Specify a policy name that is related to the registryPolicyKey. This policy is typically a built in, predefined policy called registryReadWrite, but it is possible to use a custom policy if you add it to your hub. The shared access policy requires the registry read and registry write permissions. registryPolicyKey - The key that supplies credentials to access services in the policy specified in registryPolicyName. hubName - A name that defines the Azure IoT Hub related to this ThingWorx Connector. This Azure IoT Hub manages your things and their related messages. Hubs can be scaled via hub units at different price tiers per unit. Hubs are related to a resource group, which is related to a subscription Id and a cloud Location. TIP: To find the name associated with your Azure IoT Hub that the ThingWorx Connector will use to communicate with it, navigate to All Resources > your hub > Settings > Properties > NAME eventHubName - The Event Hub-compatible name that is used by SDKs and integrations that expect to read from Event Hubs. An Event Hub is an internal component of an Azure IoT Hub that handles device-to-cloud events for related things. In many cases, the IoT Hub name and Event Hubcompatible name are the same, so this property defaults to the Azure IoT Hub name (hubName). Navigate to All Resources > your hub > Settings > Built-in endpoints > Events > Event Hub-compatible name to find this name.   eventHubNamespace - To find the endpoint that is used by SDKs and integrations that expect to read from Event Hubs, navigate to All resources > your hub > Settings > Built-in endpoints > Events > Event Hub-compatible endpoint, and copy the host name, without the rest of the address (".servicebus.windows.net"). The ThingWorx Azure IoT Connector uses this endpoint to read messages from your hub. consumerGroup - To find a consumer group name to enable the Connector to pull data from the Azure IoT Hub, navigate to All Resources > your hub > Messaging > Endpoints > Built-in endpoints > Events > Consumer groups. To use the $Default consumer group, set this property to null. hubHostname - To find the host name for your hub, navigate to All Resources > your hub > Overview > Hostname. The host name is defined by the hubName plus a domain name that is chosen by Azure, typically azuredevices.net.   blob-storage.account-name - The blob-storage section specifies the settings for an Azure blob storage account. The storage provides containers that are used for device export of an Azure IoT Hub to ThingWorx via the Connector and can also be created by the Connector if you create AzureStorageContainerFileRepository things in ThingWorx. If you do not have one, create a Storage Account in the Azure portal. To find the name of an existing account, navigate to Settings > Access Keys > Storage account name.   blob-storage.account-key - The key to associate with the name of the blob storage account. To find the key for an existing account, navigate to Settings > Access Keys > Primary or secondary key Enter your ThingWorx Foundation server host, port, and appKey  in the transport.websockets section. transport.websockets { app-key = "6d70dfca-fe88-4d8c-83aa-686449b52cb2" platforms = "ws://45.23.12.112:80/Thingworx/WS" }   NOTE: If you are using an SSL connection to your ThingWorx Foundation server use wss in place of ws in the platform parameter. If the URL for the ThingWorx Foundation server does not include a port, use 80 for http connections and 443 for https.       Step 5: Launch IoT Hub Connector   Open a shell or a command prompt window. On a Windows machine, open the command prompt as Administrator. The AZURE_IOT_OPTS environment variable must be set before starting the Azure IoT Hub Connector. Below are sample commands using the default installation directory. On Windows: set AZURE_IOT_OPTS=-Dconfig.file=C:\ThingWorx-Azure-IoT-Connector-<version>\azure-iot-<version>-application\conf\azure-iot.conf -Dlogback.configurationFile=C:\ThingWorx-Azure-IoT-Connector-<version>\azure-iot-<version>-application\conf\logback.xml On Linux: export AZURE_IOT_OPTS="-Dconfig.file=/var/opt/ThingWorx-Azure-IoT-Connector-<version>/azure-iot-<version>-application/conf/azure-iot.conf -Dlogback.configurationFile=/var/opt/ThingWorx-Azure-IoT-Connector-<version>/azure-iot-<version>-application/conf/logback.xml" NOTE: You must run the export command each time you open a shell or command prompt window. Change directories to the bin subdirectory of the Azure IoT Hub Connector installation. Start the Azure IoT Hub Connector with the appropriate command for your operating system. On Windows: azure-iot.bat On Linux: /azureiot NOTE: On Windows you may have to shorten the installation directory name or move the bin directory closer to the root directory of your system to prevent exceeding the Windows limit on the classpath length.   The Connection Server should start with no errors or stack traces displayed. If the program ends, check the following: Java version is 1.8.0, update 92 or greater and is Java(TM) not OpenJDK. Open azure-iot.conf and confirm ThingWorx Foundation is set to the correct URL and port. Confirm the platform scheme is ws if http is used to access ThingWorx. Confirm all Azure credentials are correct for your Azure account. In ThingWorx Foundation click the Monitoring tab then click Connection Servers. You should see a server named azure-iot-cxserver-{server-uuid}, where {server-uuid} is a unique identifier that is assigned automatically to the server.       Step 6: Import Device from Azure   With the ThingWorx Azure IoT Connector, you can import into ThingWorx any existing devices that are currently provisioned to the Azure IoT Hub.   Add Device Azure IoT Hub   If you have not provisioned any devices to your Azure IoT Hub you can learn more about Azure IoT Hub device identity before following the steps below to create a test device.   In your Azure Portal, click All Resources, then select the name of your IoT Hub. Under Explorers click IoT devices, then click + Add. Enter a name for your device, then click Save. When the device name appears in the list it is ready to use.   Import Device into ThingWorx   We will manually execute a service in ThingWorx that will import Azure IoT Hub devices into ThingWorx.   In ThingWorx Composer, navigate to the ConnectionServicesHub Thing. Click Services tab and scroll to the ImportAzureIotDevices service and click the execute Arrow. NOTE: The * in the pattern field will act as a wildcard and import all devices, you can enter a string to match that will only import a subset of all available devices. Click Execute to import the devices then click Done. Click Things in the left column to see the Things that were created.   Click here to view Part 2 of this guide.
View full tip
    Step 6: Map Data   Now that the event is created, we need to map the Properties of EdgeThing to the fields required to invoke an Analysis Job.   We'll start with the Inputs.   Select the previously-created Event, and click Map Data....   Click Inputs Mapping.   In Source Type, select Thing. In Source, search for and select EdgeThing.   On the left, scroll down and select s1_fb1. Note that you do NOT want the s1_fb1 that is part of the InfoTable Property, because the Info Table Property only stores recorded data, not live data. 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 to causalTechnique in the same manner. This is a String Property in EdgeThing with a Default Value of "FULL_RANGE". Map goalField to goalField in the same manner. This is a String Property in EdgeThing with a Default Value of "low_grease".   Map Results   Now that the Inputs are mapped, we also want to map the Results.   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 Enable the Analysis Event so that it can automatically generate and process Analysis Jobs. Select the mapped Analysis Event. Select Enable.   Now that you have enabled the Analysis Event, the new data will be submitted to Analytics Manager whenever EdgeThing's s1_fb1 Property changes.   An Analysis Job will automatically run, with a predictive score sent back and stored in EdgeThing's Result_low_grease (Boolean) and Result_low_grease_mo (Number) Properties.     Step 7: Check Jobs   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, corresponding to either a low grease or no low grease condition. 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 EdgeThing.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 ~1 seconds, this can create a large number of Events, which can fill up your log.       Step 8: Next Steps   Congratulations. You've completed the Manage an Engine Analytical 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   The next guide in the Vehicle Predictive Pre-Failure Detection with ThingWorx Platform learning path is Engine Failure-Prediction GUI.   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
    Use Analytics Manager to automatically perform engine analytical calculations.   Guide Concept   This guide will use ThingWorx Analytics Manager to compare external-data from an Edge MicroServer (EMS) "Engine Simulator" to a previously-built analytical model.   Following the steps in this guide, you will learn how to deploy the model which you created in the earlier Builder guide.   We will teach you how to utilize this deployed model to investigate whether or not live data indicates a potential engine failure.   You'll learn how to   Deploy and execute computational models Define and trigger Analysis Events Map incoming data to the Model Map analytic outputs to applications   NOTE: This guide's content aligns with ThingWorx 9.3. The estimated time to complete this guide is 60 minutes       Step 1: Scenario   In this guide, we're continuing the same MotorCo scenario, where an engine can fail catastrophically in a low-grease condition.   In previous guides, you've gathered and exported engine vibration-data from an Edge MicroServer (EMS) and used it to build an engine analytics model.   The goal of this guide is to now operationalize that previously-created model to analyze individual, external readings to see if the "low grease" condition is currently happening.     Analytical model creation can be extremely helpful for the automotive segment in particular. For instance, each car that comes off the factory line could have an EMS constantly sending data from which an analytical model could automatically detect engine trouble.   This could enable your company to offer an engine monitoring subscription service to your customers.   This guide will show you how to put an analytic model of your engine into service to actively monitor performance.       Step 2: Configure Provider   In ThingWorx terminology, an Analysis Provider is a mathematical analysis engine.   Analytics Manager can use a variety of Providers, such as Excel, Mathcad, or even Analytics Server pre-built ones.   In this guide, we use the built-in AnalyticsServerConnector, a Provider that has been specifically created to work seamlessly in Manager and to use Builder Models.   From the ThingWorx Composer Analytics tab, click Analytics Manager > Analysis Providers > New....   In the Provider Name field, type Vibration_Provider. In the Connector field, search for and select TW.AnalysisServices.AnalyticsServer.AnalyticsServerConnector.   Leave the rest of the options at default and click Save.     Step 3: Publish Analysis Model   Once you have configured an Analysis Provider, you can publish Models from Analytics Builder to Analytics Manager. On the ThingWorx Composer Analytics tab, click Analytics Builder > Models.   Select vibration_model_s1_only and click Publish.   On the Publish Model? pop-up, click Yes. A new browser tab will open with the Analytics Manager's Analysis Models menu. Close that new browser tab, and instead click Analysis Models in the ThingWorx Composer Analytics navigation. This is the same interface as the auto-opened tab which you closed.   Click the model to select it. At the top, click Enable. Note the pop-up indicating that the Enable was successful.       Step 4: Modify EdgeThing   In previous guides in this Vehicle Predictive Pre-Failure Detection Learning Path, you have created various Entities, including Things such as EdgeThing.   In order to automate the process of pushing data from EdgeThing to Analytics Manager, we need to add a few more Properties to EdgeThing.   These Properties are simple STRING variables, and we'll also set Default Values for them to configure parameters of Analytics Manager.   The first is causalTechnique, which tells Analytics Manager which criteria to use when measuring the impact of a feature on a range of goal values.   The second is goalField, which is simply the data field for which Analytics Manager should try to identify the correlation. In this case, it'll be our primary issue, i.e. low_grease.   It is not mandatory that these suggested Property names match, but they are the names used within ThingWorx Analytics. You could use any Property name you wanted, as you'll be mapping from a particular Property to the functionality within Analytics in a later step.   Return to EdgeThing > Properties and Alerts.   Click + Add.   In the Name field, type causalTechnique. Check Has Default Value. In the text field under "Has Default Value", type FULL_RANGE. Note that you MUST type FULL_RANGE, including capitalization, as that is a command within Analytics Server.   Click the "Check with a +" icon for Done and Add. In the Name field, type goalField. Check Has Default Value. In the text field under "Has Default Value", type low_grease. Note that you MUST type low_grease, including being all lower-case, as that is the exact name of the Analytics Server goal.   Click the "Check" icon for Done. Click Save.   Results Storage   We also need a place in which to store the results that Analytics Manager returns. We'll utilize a few additional Properties for that as well.   On the EdgeThing > Properties and Alerts tab, click + Add. In the Name field, type Result_low_grease. Check the Base Type to BOOLEAN. Check Persistent.   Click the "Check with a +" icon for Done and Add. In the Name field, type Result_low_grease_mo. Change the Base Type to NUMBER. Check Persistent.   Click the "Check" icon for Done. Click Save.       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 data-change in our simulated engine.   On the ThingWorx Composer Analytics tab, click Analytics Manager > Analysis Events.   Click New.... If not already selected, change Source Type (Required) to Thing.   In Source, search for and select EdgeThing. In Event, select DataChange. In Property, select s1_fb1. If there are multiple s1_fb1 Properties, select the first one, as the second one is the s1_fb1 entry in the Info Table Property. In Provider Name, select Vibration_Provider. In Model Name, select the published Model.   Click Save.       Click here to view Part 2 of this guide.  
View full tip
  Connect to an existing database and design a connected data model.   GUIDE CONCEPT   There are times you already have your database designed and only need to integrate the ThingWorx environment.   These concepts and steps will allow you to focus on development of your application while still allowing the ability to utilize the power of ThingWorx!   We will teach you how to create a data model around your database design and connect to that database.     YOU'LL LEARN HOW TO   How to connect an external database and created services to be used with it How to design and implement a new data model based on an external resource Using data models with database services   Note: The estimated time to complete this guide is 30 minutes.      Step 1: Examples and Strategy   If you’d like to skip ahead, download and unzip the completed example of the Aerospace and Defense learning path attached to this guide:  AerospaceEntitiesGuide1.zip.   By now, you likely know how to create a data model from scratch. You have likely already created services that work with Info Tables. What you might not have completed, is combining both a new data model, handling data in services, and connecting it all to an external database.   Our organization, PTC Defense Department, has existed for years and has an existing database setup. Developers in our organization refuse to remodel the database, so we must model the ThingWorx data model to our database schema. With ThingWorx, this is not a difficult task, but there are numerous decisions and options that we will explore in this guide.     Step 2: Database Connections   ThingWorx is based on the Java programming language and can make connections to any database that supports a Java-based connection. Dropping the JAR file for the database JDBC driver to the lib folder of Tomcat is all that is needed for connection to the ThingWorx Platform. Follow the below steps to get started creating the connection.   To establish the connection and begin working with an external database, you will need to create a Database Thing and configure the connection string and credentials. Let us start with our database connection. If you have not done so already, download the Aerospace and Defense database scripts: DatabaseApplication.zip. Use the README.txt file to create the database schema. It is based on Microsoft SQL Server, but you can configure the scripts to your database preferences.   NOTE: You will not need to connect to a database to utilize this guide as a learning utility. For your services to work, you will need to connect to a database.   1. In ThingWorx Composer, click the + New at the top-left of the screen.     2. Select Thing in the dropdown.     3. Name the Thing `DatabaseController.Facilities` and select Database as the Base Thing Template.     4.Click Save and go to the Configurations tab.   In this tab, you will enter the class name of your driver, the connection string for that database connection, and the credentials to access the database.   Keep in mind, the JDBC Driver Class Name, JDBC Connection String, and the connection Validation String values are all database type specific. For example, to connect to a SQL Server database, the below configuration can be used.   Title Description  Example   JDBC Driver Class Name  The specific class name of the driver being used for the connection.  net.sourceforge.jtds.jdbc.Driver (SQL Server) or oracle.jdbc.driver.OracleDriver (Oracle)  JDBC Connection String  The connection string to locate the database by host/port/database name.  jdbc:jtds:sqlserver://server:port/databaseName (SQL Server) or jdbc:oracle:thin:@hostname:port:databaseName (Oracle)  connectionValidationString  A simple query that should always work in a database.  SELECT GetDate() (SQL Server) or SELECT SYSDATE FROM DUAL (Oracle)   5. After entering credentials, click Save.     6. Go the Properties and Alerts tab.   The connected Property should be checked. This property informs us of the current connection to the database. The lastConnection Datetime Property should show a current timestamp. This property informs us of the last time there was a valid connection to the database. This is an easy way to confirm the connection to the database.   If you do not have a connection, work on your configurations in the Configurations tab and validate the credentials being used. If you are still having troubles, see the examples section below or use the next section for help to try a query of the database.   Help and Troubleshooting   For help finding the correct configuration for you, check out these JDBC Configuration Examples or try out this Connection String Reference for help with your connection string.   You have just established your first database connection! Now jump to the next section and let us begin to build a data model to match the database schema.   Step 3: Query Data from External Database   Now that you're connected to the database, you can begin querying the database for information and the flow of data. The queries and data shown below are based on the table scripts provided in the download.   Examples of how the ThingWorx entity should look can be seen in the SQLServerDatabaseController and OracleDatabaseController entities.   Running a Query   As you may have noticed by working in ThingWorx and developing applications, an InfoTable is often used to work with large sets of data. An InfoTable is also how data is returned from a database query. If you're expecting only 1 value in the result set, the data will be held in the first index of the InfoTable. If you're expecting rows of data coming back, expect there to be rows of information inside of the InfoTable.   Follow the steps below to set up a helper service to perform queries for the database. While other services might generate the query to be used, this helper service will be a shared execution service.   In the DatabaseController entity, go to the Services tab. Create a new service of type SQL (Query) called RunDatabaseQuery.           3. Set the Output as InfoTable, but do not set the DataShape for the InfoTable.       4. Add the following parameter: Name Base Type Required query String True       5. Add the following code to the new service:   <<query>>       6. Click Save and Continue. Your service signature should look like the below example.       You now have a service that can run queries to the database. This is also a simple method to test/troubleshoot the database connection or a quick query.   Run your service with a simple query. You might notice that no matter the fields in the result set, the InfoTable will match it based on field type and field name.   There are two ways you can go from here. You can either query the database using services that call this service, or you can create more SQL command services that query the database directly. Let's go over each method next, starting with a service to call our helper.   In the Services tab of the DatabaseController entity, create a new service of type JavaScript. Name the service JavaScriptQuery_PersonsTable. Set the Output as InfoTable, but do not set the DataShape for the InfoTable. Add the following code to your new service: try { var query = "SELECT * FROM Persons"; logger.debug("DatabaseController.JavaScriptQuery_PersonsTable(): Query - " + query); var result = me.RunDatabaseQuery({query:query}); } catch(error) { logger.error("DatabaseController.JavaScriptQuery_PersonsTable(): Error - " + error.message); }       5. Click Save and Continue.   Any parameter, especially those that were entered by users, that is being passed into an SQL Statement using the Database Connectors should be fully validated and sanitized before executing the statement! Failure to do so could result in the service becoming an SQL Injection vector.   This is a simple way to query the database since much of your development inside of ThingWorx was already based in JavaScript.   Now, let's utilize the second method to create a query directly to the database. You can use open and close brackets to create parameters for your query. You can also use <> as a method to mark a value that will need to be replaced. As you build your query, use [[Parameter Name]] for parameters/variables substitution and <> for string substitution.   In the Services tab of the DatabaseController entity, create a new service of type JavaScript. Name the service SQLQuery_GetPersonByEmail. Ensure the Output is InfoTable. Add the following code to your new service: SELECT * FROM Persons WHERE person_email = [[email]];       5. Add the following parameter:   Name Base Type Required email String True         6. Click Save and Continue.   An example of using the string replacement is as follows: DELETE FROM <> WHERE (FieldName = '[[MatchName]]'); DELETE FROM << TableName >> WHERE ( FieldName = [[MatchNumber]]);       Click here to view Part 2 of this guide.
View full tip
    Step 8: Tasks   If you are using the built-in Tasker to drive data collection or other types of repetitive or periodic activities, create a function for the task. Task functions are registered with the Tasker and then called at the rate specified after they are registered. The Tasker is a very simple, cooperative multitasker, so these functions should not take long to return and most certainly must not go into an infinite loop.   The signature for a task function is found in [C SDK HOME DIR]/src/utils/twTasker.h. The function is passed a DATETIME value with the current time and a void pointer that is passed into the Tasker when the task is registered. After creating this function, it will need to be registered using the twApi_CreateTask function after the connection is created. Below shows an example of creating this function, registering this function, and how this function can be used.   #define DATA_COLLECTION_RATE_MSEC 2000 void dataCollectionTask(DATETIME now, void * params) { /* TW_LOG(TW_TRACE,"dataCollectionTask: Executing"); */ properties.TotalFlow = rand()/(RAND_MAX/10.0); properties.Pressure = 18 + rand()/(RAND_MAX/5.0); properties.Location.latitude = properties.Location.latitude + ((double)(rand() - RAND_MAX))/RAND_MAX/5; properties.Location.longitude = properties.Location.longitude + ((double)(rand() - RAND_MAX))/RAND_MAX/5; properties.Temperature = 400 + rand()/(RAND_MAX/40); /* Check for a fault. Only do something if we haven't already */ if (properties.Temperature > properties.TemperatureLimit && properties.FaultStatus == FALSE) { twInfoTable * faultData = 0; char msg[140]; properties.FaultStatus = TRUE; properties.InletValve = TRUE; sprintf(msg,"%s Temperature %2f exceeds threshold of %2f", thingName, properties.Temperature, properties.TemperatureLimit); faultData = twInfoTable_CreateFromString("message", msg, TRUE); twApi_FireEvent(TW_THING, thingName, "SteamSensorFault", faultData, -1, TRUE); twInfoTable_Delete(faultData); } /* Update the properties on the server */ sendPropertyUpdate(); } … twApi_CreateTask(DATA_COLLECTION_RATE_MSEC, dataCollectionTask); … while(1) { char in = 0; #ifndef ENABLE_TASKER DATETIME now = twGetSystemTime(TRUE); twApi_TaskerFunction(now, NULL); twMessageHandler_msgHandlerTask(now, NULL); if (twTimeGreaterThan(now, nextDataCollectionTime)) { dataCollectionTask(now, NULL); nextDataCollectionTime = twAddMilliseconds(now, DATA_COLLECTION_RATE_MSEC); } #else in = getch(); if (in == 'q') break; else printf("\n"); #endif twSleepMsec(5); }      Step 9: File Transfer Example    To handle file transfers, a virtual directory is created in the SteamSensor1 entity and in the [C SDK HOME DIR]/examples/FileTransferExample application directory. The source code used for this example is found in [C SDK HOME DIR]/examples/FileTransferExample/src/main.c.   Inside of the [C SDK HOME DIR]/examples/FileTransferExample folder, create the folder structure shown below: /transfer/ /transfer/incoming/ /transfer/outgoing/ Inside of the /transfer/outgoing/ directory, create and open a file with the name outgoing.txt. Once the outgoing.txt document is open, add the following text, save, and close the file: Hello. This is a file coming from the client application. Navigate to the [C SDK HOME DIR]/examples/FileTransferExample/src/main.c code and update the lines below with the appropriate information for your IP, port, and the “admin_key” Application Key’s keyId value in the ThingWorx Composer: /* Server Details */ #define TW_HOST "127.0.0.1" #define TW_PORT 80 #define TW_APP_KEY "ce22e9e4-2834-419c-9656-ef9f844c784c" To support file transfers in your client application, you must use the twFileManager_AddVirtualDir function in order to create the virtual directories in the entity with such a capability. It will also define the directories available for file operations. A virtual directory maps a unique name to an absolute path of a directory in the file system. All subdirectories of the specified directory are exposed to the server. You can define multiple virtual directories. The directories do not need to be contiguous.   Staging Directory   As an optional, but recommended step, you should set the directory that the application should use for staging when performing file transfers. This can be seen in the line below and should be done before initializing the FileManager. The default directory of the FileManager is most likely owned by root and will require a change to either the location of the staging directory and the ownership of the staging directory, or running the application as a User with the correct permissions.   twcfg.file_xfer_staging_dir = "staging"; The example provided uses the TW_SHARE_DIRECTORY macro to create two virtual directories that will act as the root directories in the virtual file system of this application are added. The client is then started as follows with the necessary TW_ADD_FILE_TRANSFER_SHAPE function being called: TW_ADD_FILE_TRANSFER_SHAPE(); TW_SHARE_DIRECTORY("in", "/transfer/incoming/"); TW_SHARE_DIRECTORY("out", "/transfer/outgoing/"); The creations of the payloads used to create the remote directories on the platform have been moved to a helper function below to make the design cleaner: int setupSystemRepo(const char * remoteInPath, const char * remoteOutPath, const char * remoteFile); After our remote directories and files have been setup, it is time to perform the file transfers. Normally, this would mean invoking the Copy service for a Subsystem, but two functions have been created to make this process easier: int twFileManager_GetFile(const char * sourceRepo, const char * sourcePath, const char * sourceFile, const char * targetRepo, const char * targetPath, const char * targetFile, uint32_t timeout, char asynch, char ** tid) int twFileManager_SendFile(const char * sourceRepo, const char * sourcePath, const char * sourceFile, const char * targetRepo, const char * targetPath, const char * targetFile, uint32_t timeout, char asynch, char ** tid) The table below displays an example of the first set of parameters:    Parameter     Example                             Description sourceRepo SystemRepository The name of FileRepository or RemoteThing to transfer the file FROM. sourcePath outgoing The path specifying the location of the source file. sourceFile The name of the source file.   targetRepo SteamSensor1 The name of FileRepository or RemoteThing to transfer the file TO. targetPath incoming The path specifying the destination location of the file. targetFile incoming.txt The name of the file at the target. This name can differ from the sourceName. timeout 15,000 The amount of time (in seconds) to wait for a synchronous transfer to complete before cancelling the transfer. async false If false, the service call will block for timeout seconds or until the transfer completes. tid incoming0123 The unique TID associated with the file.   The C SDK also provides the ability to create a FileCallback function that the FileManager will call that function when any file transfer events occur. You can provide a wildcard filter so that only file transfer Events of files that match the filter call the callback function. In addition, callbacks can be set up as “one-shots” such that the callback is unregistered automatically after it is invoked the first time.   NOTE: An optional file transfer callback is registered in the code and provided. You will see the output from the function as files are sent and received.   After running this application, you will notice a new file in the transfer/incoming folder after refreshing. This is the file that we created in the ThingWorx Composer file system for the SystemRepository Entity and was able to copy from that location to our local project. We have also sent a file to the server’s SystemRepository. The BrowseFileSystem and GetFileListing services can be used to check for the folders and files created.   twFileManager_RegisterFileCallback(fileCallbackFunc, NULL, FALSE, NULL);     Step 10: Support Other Platforms   All Websocket errors indicate some general issue communicating with the ThingWorx platform. If you experience an issue connecting, refer to the table below for a list of websocket errors, their corresponding codes, and an explanation of the issue.    Code     Message                                                                      Troubleshooting 200 TW_UNKNOWN_WEBSOCKET_ERROR An unknown error occurred on the websocket. 201 TW_ERROR_INITIALIZING_WEBSOCKET An error occurred while initializing the websocket. Check your websocket configuration parameters for validity. 202 TW_TIMEOUT_INITIALIZING_WEBSOCKET A timeout occurred while initializing the websocket. Check the status of the connection to ThingWorx. 203 TW_WEBSOCKET_NOT_CONNECTED The websocket is not connected to ThingWorx. The requested operation cannot be performed. 204 TW_ERROR_PARSING_WEBSOCKET_DATA An error occurred while parsing websocket data. The parser could not break down the data from the websocket. 205 TW_ERROR_READING_FROM_WEBSOCKET An error occurred while reading data from the websocket. Retry the read operation. If necessary, resend the data. 206 TW_WEBSOCKET_FRAME_TOO_LARGE The SDK is attempting to send a websocket frame that is too large. The Maximum Frame Size is set when calling twAPI_Initialize and should always be set to the Message Chunk Size (twcfg.message_chunk_size). 207 TW_INVALID_WEBSOCKET_FRAME_TYPE The type of the frame coming in over the websocket is invalid. 208 TW_WEBSOCKET_MSG_TOO_LARGE The application is attempting to send a message that has been broken up in to chunks that are too large to fit in a frame. You should not see this error. 209 TW_ERROR_WRITING_TO_WEBSOCKET An error occurred while writing to the Web socket. 210 TW_INVALID_ACCEPT_KEY The Accept key sent earlier from ThingWorx is not valid.     Step 11: Next Steps   Congratulations! You've successfully completed the Low Level Device Connect Guide, and learned how to utilize the resources provided in the Edge SDK to create your own application.   The next guide in the Utilizing ThingWorx to Secure Your Aerospace and Defense Systems learning path is Tracking Activities and Statistics.    Learn More   We recommend the following resources to continue your learning experience:    Capability      Guide Build Design Your Data Model Build Implement Services, Events, and Subscriptions   Additional Resources   If you have questions, issues, or need additional information, refer to:    Resource       Link Community Developer Community Forum Support C Edge SDK Help Center      
View full tip
  Step 6: Create OPC UA Tag in ThingWorx   We will now create a Device in Kepware with a Tag whose value will be shown in ThingWorx.   Right-click on Channel1 that was just created, then click Next to accept the default name Device1       Click Next six times to accept the default settings, Then click the Select import items button.     Expand the remote OPC UA server URL, then expand OpcPlc and Telemetry.     Click SlowUint1 to select it, then click Add items >>. Click OK, Next, Finish. Return to ThingWorx Composer. Under Browse > Modeling > Industrial Connections, open IndConn_Server. Expand Channel1 > Device1 > OpcPlc, then click Telemetry.     Click the check-box next to SlowUint1. Click Bind to New Entity.     Select RemoteThing, then click OK. Enter azure-opcua-plc in the Name field, then click Save.     Click Properties and Alerts, then Click Refresh to see the property value changing every 10 seconds.     Step 7: Next Steps   Congratulations! You've successfully completed the Connect to an Azure OPC UA Server guide, and learned how to:   Create an OPC UA Server in Azure Configure Kepware as on OPC UA Client Connect Kepware to ThingWorx Foundation Monitor OPC UA data in ThingWorx Composer   Learn More   We recommend the following resources to continue your learning experience: Capability Guide 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 Documentation Kepware documentation Support Kepware Support site
View full tip
    Step 5: Properties   In the Delivery Truck application, there are three Delivery Truck Things. Each Thing has a number of Properties based on its location, speed, and its deliveries carried out. In this design, when a delivery is made or the truck is no longer moving, the Property values are updated. The deliveryTruck.c helper C file is based on the DeliveryTruck Entities in the Composer. After calling the construct function, there are a number of steps necessary to get going. For the SimpleThing application, there are a number of methods for creating Properties, Events, Services, and Data Shapes for ease of use.   Properties can be created in the client or just registered and utilized. In the SimpleThingClient application, Properties are created. In the DeliveryTruckClient application, Properties are bound to their ThingWorx Platform counterpart. Two types of structures are used by the C SDK to define Properties when it is seen fit to do so and can be found in [C SDK HOME DIR]/src/api/twProperties.h:    Name                   Structure            Description Property Definitions twPropertyDef Describes the basic information for the Properties that will be available to ThingWorx and can be added to a client application. Property Values twProperty Associates the Property name with a value, timestamp, and quality.   NOTE: The C SDK provides a number of Macros located in [C SDK HOME DIR]/src/api/twMacros.h. This guide will use these Macros while providing input on the use of pure function calls.   The Macro example below can be found in the main source file for the SimpleThingClient application and the accompanying helper file simple_thing.c.   TW_PROPERTY("TempProperty", "Description for TempProperty", TW_NUMBER); TW_ADD_BOOLEAN_ASPECT("TempProperty", TW_ASPECT_ISREADONLY,TRUE); TW_ADD_BOOLEAN_ASPECT("TempProperty", TW_ASPECT_ISLOGGED,TRUE);   NOTE: The list of aspect configurations can be seen in [C SDK HOME DIR]/src/api/twConstants.h. Property values can be set with defaults using the aspects setting. Setting a default value in the client will affect the Property in the ThingWorx platform after binding. It will not set a local value in the client application.   For the DeliveryTruckClient, we registered, read, and update Properties without using the Property definitions. Which method of using Properties is based on the application being built.   NOTE: Updating Properties in the ThingWorx Platform while the application is running, will update the values in the client application. To update the values in the platform to match, end the Property read section of your property handler function with a function to set the platform value.   The createTruckThing function for the deliveryTruck.c source code takes a truck name as a parameter and is used to register the Properties, functions, and handlers for each truck.   The updateTruckThing function for the deliveryTruck.c source code takes a truck name as a parameter and is used to either initialize a struct for DeliveryTruck Properties, or simulate a truck stop Event, update Properties, then fire an Event for the ThingWorx platform.   Connecting properties to be used on the platform is as easy as registering the property and optionally adding aspects. The following shows the properties that correlate to those in the DeliveryTruck entities in the Composer. To do this within the code, you would use the TW_PROPERTY macro as shown in the deliveryTruck.c. This macro must be proceeded by either TW_DECLARE_SHAPE, TW_DECLARE_TEMPLATE or TW_MAKE_THING because these macros declare variables used by the TW_PROPERTY that follow them.   //TW_PROPERTY(propertyName,description,type) TW_PROPERTY(PROPERTY_NAME_DRIVER, NO_DESCRIPTION, TW_STRING); TW_PROPERTY(PROPERTY_NAME_DELIVERIES_LEFT, NO_DESCRIPTION, TW_NUMBER); TW_PROPERTY(PROPERTY_NAME_TOTAL_DELIVERIES, NO_DESCRIPTION, TW_NUMBER); TW_PROPERTY(PROPERTY_NAME_DELIVERIES_MADE, NO_DESCRIPTION, TW_NUMBER); TW_PROPERTY(PROPERTY_NAME_LOCATION, NO_DESCRIPTION, TW_LOCATION); TW_PROPERTY(PROPERTY_NAME_SPEED, NO_DESCRIPTION, "TW_NUMBER);   Read Properties   Reading Properties from a ThingWorx platform Thing or the returned Properties of a Service can be done using the TW_GET_PROPERTY macro. Examples of its use can be seen in all of the provided applications. An example can be seen below:   int flow = TW_GET_PROPERTY(thingName, "TotalFlow").number; int pressue = TW_GET_PROPERTY(thingName, "Pressure").number; twLocation location = TW_GET_PROPERTY(thingName, "Location").location; int temperature = TW_GET_PROPERTY(thingName, "Temperature").number;   Write Properties   Writing Properties to a ThingWorx platform Thing from a variable storing is value uses a similarly named method. Using the TW_SET_PROPERTY macro will be able to send values to the platform. Examples of its use can be seen in all of the provided applications. An example is shown below:   TW_SET_PROPERTY(thingName, "TotalFlow", TW_MAKE_NUMBER(rand() / (RAND_MAX / 10.0))); TW_SET_PROPERTY(thingName, "Pressure", TW_MAKE_NUMBER(18 + rand() / (RAND_MAX / 5.0))); TW_SET_PROPERTY(thingName, "Location", TW_MAKE_LOC(gpsroute[location_step].latitude,gpsroute[location_step].longitude,gpsroute[location_step].elevation));   This macro utilizes the twApi_PushSubscribedProperties function call to push all property updates to the server. This can be seen in the updateTruckThing function in deliveryTruck.c.   Property Change Listeners   Using the Observer pattern, you can take advantage of the Property change listener functionality. With this pattern, you create functions that will be notified when a value of a Property has been changed (whether on the server or locally by your program when the TW_SET_PROPERTY macro is called).   Add a Property Change Listener   In order to add a Property change listener, call the twExt_AddPropertyChangeListener function using the:   Name of the Thing (entityName) Property this listener should watch Function that will be called when the property has changed   void simplePropertyObserver(const char * entityName, const char * thingName,twPrimitive* newValue){ printf("My Value has changed\n"); } void test_simplePropertyChangeListener() { { TW_MAKE_THING("observedThing",TW_THING_TEMPLATE_GENERIC); TW_PROPERTY("TotalFlow", TW_NO_DESCRIPTION, TW_NUMBER); } twExt_AddPropertyChangeListener("observedThing",TW_OBSERVE_ALL_PROPERTIES,simplePropertyObserver); TW_SET_PROPERTY("observedThing","TotalFlow",TW_MAKE_NUMBER(50)); } NOTE: Setting the propertyName parameter to NULL or TW_OBSERVE_ALL_PROPERTIES, the function specified by the propertyChangeListenerFunction parameter will be used for ALL properties.   Remove a Property Change Listener   In order to release the memory for your application when done with utilizing listeners for the Property, call the twExt_RemovePropertyChangeListener function.   void simplePropertyObserver(const char * entityName, const char * thingName,twPrimitive* newValue){ printf("My Value has changed\n"); } twExt_RemovePropertyChangeListener(simplePropertyObserver);       Step 6: Data Shapes   Data Shapes are an important part of creating/firing Events and also invoking Services.   Define With Macros   In order to define a Data Shape using a macro, use TW_MAKE_DATASHAPE.   NOTE: The macros are all defined in the twMacros.h header file.   TW_MAKE_DATASHAPE("SteamSensorReadingShape", TW_DS_ENTRY("ActivationTime", TW_NO_DESCRIPTION ,TW_DATETIME), TW_DS_ENTRY("SensorName", TW_NO_DESCRIPTION ,TW_NUMBER), TW_DS_ENTRY("Temperature", TW_NO_DESCRIPTION ,TW_NUMBER), TW_DS_ENTRY("Pressure", TW_NO_DESCRIPTION ,TW_NUMBER), TW_DS_ENTRY("FaultStatus", TW_NO_DESCRIPTION ,TW_BOOLEAN), TW_DS_ENTRY("InletValve", TW_NO_DESCRIPTION ,TW_BOOLEAN), TW_DS_ENTRY("TemperatureLimit", TW_NO_DESCRIPTION ,TW_NUMBER), TW_DS_ENTRY("TotalFlow", TW_NO_DESCRIPTION ,TW_INTEGER) );   Define Without Macros   In order to define a Data Shape without using a macro, use the twDataShape_CreateFromEntries function. In the example below, we are creating a Data Shape called SteamSensorReadings that has two numbers as Field Definitions.   twDataShape * ds = twDataShape_Create(twDataShapeEntry_Create("a",NULL,TW_NUMBER)); twDataShape_AddEntry(ds, twDataShapeEntry_Create("b",NULL,TW_NUMBER)); /* Name the DataShape for the SteamSensorReadings service output */ twDataShape_SetName(ds, "SteamSensorReadings");     Step 7: Events and Services   Events and Services provide useful functionality. Events are a good way to make a Service be asynchronous. You can call a Service, let it return, then your Entity can subscribe to your Event and not keep the original Service function waiting. Events are also a good way to allow the platform to respond to data when it arrives on the edge device without it having to poll the edge device for updates.   Fire Events   To fire an Event you first need to register the Event and load it with the necessary fields for the Data Shape of that Event using the twApi_RegisterEvent function. Afterwards, you would send a request to the ThingWorx server with the collected values using the twApi_FireEvent function. An example is as follows:   twDataShape * ds = twDataShape_Create(twDataShapeEntry_Create("message", NULL,TW_STRING)); /* Event datashapes require a name */ twDataShape_SetName(ds, "SteamSensorFault"); /* Register the service */ twApi_RegisterEvent(TW_THING, thingName, "SteamSensorFault", "Steam sensor event", ds); …. struct { char FaultStatus; double Temperature; double TemperatureLimit; } properties; …. properties. TemperatureLimit = rand() + RAND_MAX/5.0; properties.Temperature = rand() + RAND_MAX/5.0; properties.FaultStatus = FALSE; if (properties.Temperature > properties.TemperatureLimit && properties.FaultStatus == FALSE) { twInfoTable * faultData = 0; char msg[140]; properties.FaultStatus = TRUE; sprintf(msg,"%s Temperature %2f exceeds threshold of %2f", thingName, properties.Temperature, properties.TemperatureLimit); faultData = twInfoTable_CreateFromString("message", msg, TRUE); twApi_FireEvent(TW_THING, thingName, "SteamSensorFault", faultData, -1, TRUE); twInfoTable_Delete(faultData); }   Invoke Services   In order to invoke a Service, you will use the twApi_InvokeService function. The full documentation for this function can be found in [C SDK HOME DIR]/src/api/twApi.h. Refer to the table below for additional information.    Parameter         Type                  Description entityType Input The type of Entity that the service belongs to. Enumeration values can be found in twDefinitions.h. entityName Input The name of the Entity that the service belongs to. serviceName Input The name of the Service to execute. params Input A pointer to an Info Table containing the parameters to be passed into the Service. The calling function will retain ownership of this pointer and is responsible for cleaning up the memory after the call is complete. result Input/Output A pointer to a twInfoTable pointer. In a successful request, this parameter will end up with a valid pointer to a twInfoTable that is the result of the Service invocation. The caller is responsible for deleting the returned primitive using twInfoTable_Delete. It is possible for the returned pointer to be NULL if an error occurred or no data is returned. timeout Input The time (in milliseconds) to wait for a response from the server. A value of -1 uses the DEFAULT_MESSAGE_TIMEOUT as defined in twDefaultSettings.h. forceConnect Input A Boolean value. If TRUE and the API is in the disconnected state of the duty cycle, the API will force a reconnect to send the request.   See below for an example in which the Copy service from the FileTransferSubsystem is called:   twDataShape * ds = NULL; twInfoTable * it = NULL; twInfoTableRow * row = NULL; twInfoTable * transferInfo = NULL; int res = 0; const char * sourceRepo = "SimpleThing_1"; const char * sourcePath = "tw/hotfolder/"; const char * sourceFile = "source.txt"; const char * targetRepo = "SystemRepository"; const char * targetPath = "/"; const char * targetFile = "source.txt"; uint32_t timeout = 60; char asynch = TRUE; char * tid = 0; /* Create an infotable out of the parameters */ ds = twDataShape_Create(twDataShapeEntry_Create("sourceRepo", NULL, TW_STRING)); res = twDataShape_AddEntry(ds, twDataShapeEntry_Create("sourcePath", NULL, TW_STRING)); res |= twDataShape_AddEntry(ds, twDataShapeEntry_Create("sourceFile", NULL, TW_STRING)); res |= twDataShape_AddEntry(ds, twDataShapeEntry_Create("targetRepo", NULL, TW_STRING)); res |= twDataShape_AddEntry(ds, twDataShapeEntry_Create("targetPath", NULL, TW_STRING)); res |= twDataShape_AddEntry(ds, twDataShapeEntry_Create("targetFile", NULL, TW_STRING)); res |= twDataShape_AddEntry(ds, twDataShapeEntry_Create("async", NULL, TW_BOOLEAN)); res |= twDataShape_AddEntry(ds, twDataShapeEntry_Create("timeout", NULL, TW_INTEGER)); it = twInfoTable_Create(ds); row = twInfoTableRow_Create(twPrimitive_CreateFromString(sourceRepo, TRUE)); res = twInfoTableRow_AddEntry(row, twPrimitive_CreateFromString(sourcePath, TRUE)); res |= twInfoTableRow_AddEntry(row, twPrimitive_CreateFromString(sourceFile, TRUE)); res |= twInfoTableRow_AddEntry(row, twPrimitive_CreateFromString(targetRepo, TRUE)); res |= twInfoTableRow_AddEntry(row, twPrimitive_CreateFromString(targetPath, TRUE)); res |= twInfoTableRow_AddEntry(row, twPrimitive_CreateFromString(targetFile, TRUE)); res |= twInfoTableRow_AddEntry(row, twPrimitive_CreateFromBoolean(asynch)); res |= twInfoTableRow_AddEntry(row, twPrimitive_CreateFromInteger(timeout)); twInfoTable_AddRow(it,row); /* Make the service call */ res = twApi_InvokeService(TW_SUBSYSTEM, "FileTransferSubsystem", "Copy", it, &transferInfo, timeout ? (timeout * 2): -1, FALSE); twInfoTable_Delete(it); /* Grab the tid */ res = twInfoTable_GetString(transferInfo,"transferId",0, &tid);   Bind Event Handling   You may want to track exactly when your edge Entities are successfully bound to or unbound from the server. The reason for this is that only bound items should be interacting with the ThingWorx Platform and the ThingWorx Platform will never send any requests targeted at an Entity that is not bound. A simple example that only logs the bound Thing can be seen below. After creating this function, it will need to be registered using the twApi_RegisterBindEventCallback function before the connection is made.   void BindEventHandler(char * entityName, char isBound, void * userdata) { if (isBound) TW_LOG(TW_FORCE,"BindEventHandler: Entity %s was Bound", entityName); else TW_LOG(TW_FORCE,"BindEventHandler: Entity %s was Unbound", entityName); } …. twApi_RegisterBindEventCallback(thingName, BindEventHandler, NULL);   OnAuthenticated Event Handling   You may also want to know exactly when your Edge device has successfully authenticated and made a connection to the ThingWorx platform. Like the bind Event handling, this function will need to be made and registered. To register this handler, use the twApi_RegisterOnAuthenticatedCallback function before the connection is made. This handler form can also be used to do a delay bind for all Things.   void AuthEventHandler(char * credType, char * credValue, void * userdata) { if (!credType || !credValue) return; TW_LOG(TW_FORCE,"AuthEventHandler: Authenticated using %s = %s. Userdata = 0x%x", credType, credValue, userdata); /* Could do a delayed bind here */ /* twApi_BindThing(thingName); */ } … twApi_RegisterOnAuthenticatedCallback(AuthEventHandler, NULL);     Click here to view Part 3 of this guide.
View full tip
  Utilize the C SDK to build an app that creates a secure connection to ThingWorx with low level device access.   Guide Concept   This project will cover using the ThingWorx C SDK to develop applications for the purpose of secure and low level development.   Following the steps in this this guide, you will be ready to develop your own IoT application with the ThingWorx C SDK.   We will teach you how to use the C programming language to connect and build IoT applications to be used with the ThingWorx Platform.       You'll learn how to   Establish and manage a secure connection with a ThingWorx server, including SSL negotiation and connection maintenance. Enable easy programmatic interaction with the Properties, Services, and Events that are exposed by Entities running on a ThingWorx server. Basic concepts of the C Edge SDK How to create an application that can communicate with other devices   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: Completed Examples   Download the completed files attached to this tutorial: C_SDK.zip.   This tutorial will guide you through working with the C SDK on differing levels. Utilize this file to see a finished example and return to it as a reference if you become stuck creating your own fully flushed out application. With the C SDK, you can connect to whatever device you have at a level most programming languages wouldn't be able to. Now think of your secure system, your fighter jet, or even you communication devices. All these systems and levels need to have a way to secure transfer data and the C SDK is how we do it.   Keep in mind, this download uses the exact names for entities used in this tutorial. If you would like to import this example and also create entities on your own, change the names of the entities you create.     Step 2: Environment Setup   In order to compile C code, download the attached C compiler: C-SDK-2-2-12-1052.zip.    Operating System          Notes Windows You will need a 3rd party compiler such as MinGW GCC, Cygwin GCC or you can follow these Microsoft instructions to download and use the Microsoft Visual C++ Build Tool. Mac Download the Apple Developer Tools. Linux/Ubuntu A compiler is included by default.   NOTE: You can use CMake, version 2.6.1 or later to build projects or make files, which then are used to build the applications that you develop with the C SDK. Before you can begin developing with the ThingWorx C SDK, you need to generate an Application Key and modify the source code file. You can use the Create an Application Key as a reference.   Modify Source File   Extract the files from the C SDK samples zip file. At the top level of the extracted files, you will see a folder called examples, which provides examples of how to utilize the C SDK. Open a terminal, go to your workspace, and create a new project. After you've created this project in your workspace, import the entire C SDK downloaded from the PTC Support site for ease of use (mainly the src folder and the CMakeList.txt file). You can start creating your connection code or open the main.c source file in the examples\SteamSensor\src directory for an example.    Operating System        Code Linux/Ubuntu gedit main.c OR vi main.c Mac open –e main.c Windows start main.c   5. Modify the Server Details section at the top with the IP address for your ThingWorx Platform instance and the application key you would like to use. Change the TW_HOST definition accordingly. Change the TW_PORT definition accordingly. Change the TW_APP_KEY definition to the keyId value saved from the last step. /* Server Details */ #define TW_HOST "https://pp-XXXXXXXXX.devportal.ptc.i" #define TW_PORT 80 #define TW_APP_KEY "e1d78abf-cfd2-47a6-92b7-37dc6dd34618" NOTE: Using the Application Key for the default Administrator is not recommended. If administrative access is absolutely necessary, create a user and place the user as a member of Admins.   Compile and Run Code   To test your connection, you will only need to update the main.c in the SteamSensor example folder.   CMake can generate visual studio projects, make build files or even target IDEs such as Eclipse, or XCode. CMake generates a general description into a build for your specific toolchain or IDE.   If you have not downloaded and installed CMake, do it now at the CMake website. NOTE: CMake comes as a command line and a GUI application. The steps in this guide use the command line version only. Inside the specific example folder you would like to run, ie SteamSensor. Create a directory to build in, for this example call it cmake. mkdir cmake cd cmake 4. Run the CMake command listed below. This assumes CMake is already on your PATH. cmake -G "Visual Studio 15 2012" .. 5. CMake has now produced a set of project files which should be compatible with your development environment.  Operating System    Command                                        Notes Unix command-> make A set of make files Windows **msbuild tw-c-sdk.sln /t:build** A visual studio solution NOTE: CMake does its best to determine what version of Visual Studio you have but you may wish to specify which version to use if you have more than one installed on your computer. Below is an example of forcing CMake to use a specific version of Visual Studio: cmake -G "Visual Studio 15 2017" .. If your version of Visual Studio or other IDE is unknown, use cmake -G to see a list of supported IDEs.   You also have the alternative of opening the tw-c-sdk.sln from within Visual Studio and building in this IDE.   NOTE: By default, CMake will generate a build for the creation of a release binary. If you want to generate a debug build, use the command-> cmake -DBUILD_DEBUG=ON.   6. Once your build completes you will find the build products in the cmake directory. From here, open the project in your IDE of choice. NOTE: You should receive messages confirming successful binding, authentication, and connection after the main.c file edits have been made.       Step 3: Run Sample Code   The C code in the sample download is configured to run and connect to the Entities provided in the ThingWorxEntitiesExport.xml file. Make note of the IP address of your ThingWorx Composer instance. The top level of the exported zip file will be referred to as [C SDK HOME DIR]. Navigate to the [C SDK HOME DIR]/examples/ExampleClient/src directory. Open the main.c source file.  Operating System      Command Linux/Ubuntu gedit main.c OR vi main.c Mac open –e main.c Windows start main.c   3. Modify the Server Details section at the top with the IP address for your ThingWorx Platform instance and the Application Key you would like to use. Change the TW_HOST definition accordingly. NOTE: By default, TW_APP_KEY has been set to the Application Key from the admin_key in the import step completed earlier. Using the Application Key for the default Administrator is not recommended. If administrative access is absolutely necessary, create a user and place the user as a member of the Admins security group. /* Server Details */ #define TW_HOST "127.0.0.1" #define TW_APP_KEY "ce22e9e4-2834-419c-9656-e98f9f844c784c"   4. If you are working on a port other than 80, you will need to update the conditional statement within the main.c source file. Search for and edit the first line within the main function. Based on your settings, set the int16_t port to the ThingWorx platform port. 5. Click Save and close the file. 6. Create a directory to build in, for this example call it bin.  Operating System            Command Linux/Ubuntu mkdir bin Mac mkdir bin Windows mkdir bin   7. Change to the newly created bin directory.  Operating System              Command Linux/Ubuntu cd bin Mac cd bin Windows cd bin   8. Run the CMake command using your specific IDE of choice. NOTE: Include the two periods at the end of the code as shown below. Use cmake -G to see a list of supported IDEs. cmake ..   9. Once your build completes, you will find the build products in the bin directory, and you can open the project in your IDE of choice. NOTE: You should receive messages confirming successful binding, authentication, and connection after building and running the application. 10. You should be able to see a Thing in your ThingWorx Composer called SimpleThing_1 with updated lastConnection and isConnected properties. SimpleThing_1 is bound for the duration of the application run time.   The below instructions will help to verify the connection.   Click Monitoring. Click Remote Things from the list to see the connection status. You will now be able to see and select the Entity within the list.       Step 4: ExampleClient Connection   The C code provided in the main.c source file is preconfigured to initialize the ThingWorx C Edge SDK API with a connection to the ThingWorx platform and register handlers. In order to set up the connection, a number of parameters must be defined. This can be seen in the code below.   #define TW_HOST "127.0.0.1" #define TW_APP_KEY "ce22e9e4-2834-419c-9656-ef9f844c784c #if defined NO_TLS #define TW_PORT = 80; #else #define TW_PORT = 443; #endif   The first step of connecting to the platform: Establish Physical Websocket, we call the twApi_Initialize function with the information needed to point to the websocket of the ThingWorx Composer. This function:   Registers messaging handlers Allocates space for the API structures Creates a secure websocket err = twApi_Initialize(hostname, port, TW_URI, appKey, NULL, MESSAGE_CHUNK_SIZE, MESSAGE_CHUNK_SIZE, TRUE); if (TW_OK != err) { TW_LOG(TW_ERROR, "Error initializing the API"); exit(err); }   If you are not using SSL/TLS, use the following line to test against a server with a self-signed certificate: twApi_SetSelfSignedOk();   In order to disable HTTPS support and use HTTP only, call the twApi_DisableEncryption function. This is needed when using ports such as 80 or 8080. A call can be seen below:   twApi_DisableEncryption();   The following event handlers are all optional. The twApi_RegisterBindEventCallback function registers a function that will be called on the event of a Thing being bound or unbound to the ThingWorx platform. The twApi_RegisterOnAuthenticatedCallback function registered a function that will be called on the event the SDK has been authenticated by the ThingWorx Platform. The twApi_RegisterSynchronizeStateEventCallback function registers a function that will be called after binding and used to notify your application about fields that have been bound to the Thingworx Platform.   twApi_RegisterOnAuthenticatedCallback(authEventHandler, TW_NO_USER_DATA); twApi_RegisterBindEventCallback(NULL, bindEventHandler, TW_NO_USER_DATA); twApi_RegisterSynchronizeStateEventCallback(NULL, synchronizeStateHandler, TW_NO_USER_DATA);   NOTE: Binding a Thing within the ThingWorx platform is not mandatory, but there are a number of advantages, including updating Properties while offline.    You can then start the client, which will establish the AlwaysOn protocol with the ThingWorx Composer. This protocol provides bi-directional communication between the ThingWorx Composer and the running client application. To start this connection, use the line below:   err = twApi_Connect(CONNECT_TIMEOUT, RETRY_COUNT); if(TW_OK != err){ exit(-1); }   Click here to view Part 2 of this guide.      
View full tip
  Step 3: Creating Machine Processes   Having data and properties without moving parts would be useless. We already setup services on how to perform an inspection in the last guide. This time around, let's form processes to create our products and generate any alerts whenever we've finished production. We won't show too much into our recipe (it's a family secret), but we will highlight some concepts to help with making our machine process smoother. We won't go into creating an SMT line. For a complete guide on how to create such an application you can view our Monitor an SMT Assembly Line guide.   Events and Subscriptions   Let's create events for the cook time being done and a subscriptions on how to handle these scenarios.   Open the Fizos.BrautsMachine.ThingTemplate template and go to the Events tab. Click the + Add button and create the below alerts. Name Data Shape Description CookCompleted AlertEvent This alert is fired when the machine has completed a batch of our product TemperatureReached AlertEvent This alert is fired when the machine has reached the desired temperature.   3. Save these changes and replicate them with the Fizos.SausageMachine.ThingTemplate template. We now have alerts for the machines (alerts on properties and custom alerts). We can create subscriptions on the individual machines that will later inherit these alerts. One way to have a standard amongst these machines is to create a service in the template that will be called from subscriptions.   Let's create the simple services from now, and later, we will call these services from subscriptions. We will also use Schedules initiate the overall process. This can be a daily process, a timed process, or a process based on alerts and subscriptions. To keep this guide simple, we'll make this a process that runs every 3 hours. Create the below services in each of the Fizos.BrautsMachine.ThingTemplate and Fizos.SausageMachine.ThingTemplate templates:     Name InputReturn Type Override Async Description StartCookingProcess N/A Nothing Yes Yes Start overall product cooking process.Triggedby schedule. StartCookingIngredients N/A Nothing Yes Yes Start cooking the ingredients for the product.Triggedby subscription.   Add the following code for the StartCookingProcess service: // Mix our family secret recipe // Add in meats // Add heat me.TemperatureReached({ aackTimestamp: new Date(), alertType: "Completed", ackBy: "CookUser", ack: true, name: "TemperatureAlert", sourceProperty: "Service", description: "Optimal cooking temperature reached", message: "The desired cooking temperature has been reached. Begin next process.", priority: 1 }); // Perform top secret cooking techniques   2. Add the following code for the StartCookingIngredients service: // Temperature has already been reached if(true) { // IF COOK COMPLETED me.CookCompleted({ ackTimestamp: new Date(), alertType: "Information", ackBy: "CookUser", ack: true, name: "CookingAlert", sourceProperty: "Service", description: "Cooking process has completed", message: "The cooking time has completed. Move to next machine.", priority: 1 }); } else { //ELSE IF COOKING FAILED OR HALTED me.CookCompleted({ ackTimestamp: new Date(), alertType: "Exception", ackBy: "CookUser", ack: true, name: "CookingAlert", sourceProperty: "Service", description: "Cooking process has failed", message: "The cooking process has stopped. See logs for failure cause.", priority: 1 }); }   Let's keep in mind, these services will be triggered by by a schedule or a subscription to an alert. There will be no manual processed performed here. We also allowed for these services to be overridden, in order to allow more customization for machines that might need to be treated differently. These services are also not on the main machine templates, which allows us to create templates for machines that might have a different role in the process, ie, packaging or freeze drying.       Step 4: Automating Processes   Let's create the schedule that will start the cooking process. In the ThingWorx Composer, click the + New in the top left of the screen.   Select Schedules in the dropdown.   3. Name the new Schedule Fizos.MachineCooking.Schedule and set the Project (ie, PTCDefaultProject). 4. For the Run As User field, select the Fizos.Machine.User that was provided in the download. 5. Click Save and your entity should match the below configuration.     6. For the Schedule field, set it to 0 0 0/3 * * ?. This will run the process every 3 hours. 7. Switch to the Subscriptions tab and add a new subscription. 8. Name this new subscription StartCooking and select ScheduledEvent as the event input.   9. Add the following code to the source section:   var index = 0; var brauts = Resources["SearchFunctions"].SearchThingsByTemplate({ thingTemplate: "Fizos.BrautsMachine.ThingTemplate" }); var sausage = Resources["SearchFunctions"].SearchThingsByTemplate({ thingTemplate: "Fizos.SausageMachine.ThingTemplate" }); for(index = 0; index < brauts.rows.length; index++) { brauts.StartCookingProcess(); } for(index = 0; index < brauts.rows.length; index++) { sausage.StartCookingProcess(); } // This will begin the process to start each machines cooking process. // The process itself will then be ran by alerts, subscriptions, and services   So far the process will go as the following: A scheduler will search for all Things that inherit from the Fizos.BrautsMachine.ThingTemplate and Fizos.SausageMachine.ThingTemplate templates. Each list of these entities will have it's StartCookingProcess service called. When a temperature value is reached inside of the StartCookingProcess service, the TemperatureReached alert will be triggered. A subscription waiting for this event at the Thing level (which we create in the next section), will call the StartCookingIngredients service. This StartCookingIngredients service will finish the cooking part of the process and trigger a CookCompleted alert with a status update. A subscription waiting for this event at the Thing level (which we create in the next section), can call for another machine to take over the process.   We now have our moving parts. Let's create some examples to see how it can unfold.       Step 5: Handling Cooking Machines   We have many of our main features compeleted. Now we need to handle situations where our machine status needs to be a part of how we handle the cooking process. While much of this work can be handled in a remote process using one of our SDKs or extensions, our events and subscriptions can be a great method to do this also.   Creating Cooking Entities   In the ThingWorx Composer, click the + New in the top left of the screen.   Select Thing in the dropdown.   Set the Project (ie, PTCDefaultProject) and in the name field, enter Fizos.BrautsMachine.M02X35. In the Base Thing Template field, enter Fizos.BrautsMachine.ThingTemplate. Click Save.   6. In the ThingWorx Composer, click the + New in the top left of the screen.     7. Select Thing in the dropdown.     8. In the name field, enter Fizos.SausageMachine.KGYX20. 9. In the Base Thing Template field, enter Fizos.SausageMachine.ThingTemplate. 10, Click Save.     Creating Cooking Subscriptions   These steps will need to be in both of the entities we just created.   Switch to the Subscriptions tab and add a new subscription. Name this new subscription TemperatureReady and select TemperatureReached as the event input.   3. Add the following code to the source section: //The temperature is high enough. Start cooking me.StartCookingIngredients();   You now have a system (when enabled) will run every three hours. It will start our secret cooking process (that can be added in the service or created remotely).   To make things more fun. Add some logging or fun code to see how this works step by step.   To make things more realistic to a real world scenario, add subscriptions for our property alerts on the status or state properties.       Step 6: Next Steps   Congratulations! You've successfully completed the Factory Line Automation guide. In this guide, you learned how to:   Create automated machine processes Use services, alerts, and subscriptions to handle processes without human interaction Integrating complex logic with straight forward step by step systems   The next guide in the Complex and Automatic Food and Beverage Systems learning path is Automated Distribution and Logistics.    Learn More   We recommend the following resources to continue your learning experience:    Capability     Guide Build ThingWorx Solutions in Food Industry Build Design Your Data Model Build Implement Services, Events, and Subscriptions   Additional Resources   If you have questions, issues, or need additional information, refer to:    Resource       Link Community Developer Community Forum  
View full tip
  Learn how to create factory line solutions the can help take you to the next level     Guide Concept   This project will introduce more complex aspects of the ThingWorx application and solution building.   Following the steps in this guide, you will develop your own IoT application or get a jump start in how to utilize ThingWorx for your needs.   We will teach you how to create a focused rules engine or service level logic to be used with the ThingWorx Platform.     You'll learn how to   Create automated machine processes Use Services, Alerts, and Subscriptions to handle processes without human interaction Integrating complex logic with straight forward step by step systems   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: Examples and Strategy   Download the attached FoodIndustry.zip and extract/import the contents. This is to be used as you work through this learning path. For the completed example, download, extract, and import FoodIndustryComplete.zip. These downloads provide three Users and a Security Group. Please watch your count for Users or the import could fail. If you're at your User count limit, delete Users before attempting to import these downloads. Unzip the download and import the Entities included.   In this tutorial we continue with our real-world scenario for the Fizos food company. We should already have our factory data in data tables and will expand on how we can make the factory line smarter. The best processed are built on great timing, quality materials, a great design, and a method to keep track of everything that is happening. We won't have control of materials here, but we can formulate a simple, but great design. Our design will be based on Alerts, Subscriptions, and Schedules.   One of the same questions we will need to ask ourselves that we did in the last guide pertaining to products, is whether to create an entity per machine used in the factory OR create data entries in a data table and process everything through services. Since we would like to have more detailed and up to date information about our machines, I will create a data model that is tied back to our factories.   We will need to start by creating our templates for overall machines, templates for specialized machines, and then entities for the particular machines we have in our factories.       Click here to view Part 2 of this guide.
View full tip
  Step 6: Create Event Router   What do you do when a user can perform multiple events in which data is generated, and want those outputs to go through the same exact process? An Events Router Function is your solution. The Events Router Function allows for multiple data sources to be funneled to one location. Let's create a simple example in our MyFunctionsMashup Mashup. In this Mashup, we'll add two Text Field Widgets and a Label Widget. The two Text Field Widgets will take user input and then an Events Router will send the output to the Label. Let's start!   Open the MyFunctionsMashup Mashup to the Design tab. Click on the Widgets tab. Type in the Filter text box for Text Field.   Drag and drop TWO (2) Text Field Widgets to the Mashup Canvas. Type in the Filter text box for Label.   Drag and drop ONE (1) Label Widget to the Mashup Canvas. We now have all the Widgets we need for this example. Let's get started on the Events Router Function. Click the + button in the Functions panel. Select Events Router in the dropdown.   Set the Name to routeUserInput.   Click Next. Set the Inputs field to 2.   Click Done. We have our Events Router setup. Now, we'll bind our new items together. Click the Bind (arrows) button on the routeUserInput Events Router.   Click the down arrow next to Input1. Select Add Source.   In the Widgets tab, scroll to the bottom and select Text Property of the first of the two recent Text Fields we created (it should be third to last).   Click Next. Click the down arrow next to Input2. Select Add Source.   In the Widgets tab, scroll to the bottom and select Text Property of the second of the two recent Text Fields we created (it should be second to last).   Click Next. Click the down arrow next to Output. Select Add Target.   In the Widgets tab, scroll to the bottom and select LabelText Property of the recent Label we created (it should be last).   Click Next. Click Done. Click Save for the Mashup. You have just created an Events Router that will update a Label based on the typed input from two Text Fields. View your Mashup and play around with the bottom two text boxes. For a completed example, download and unzip, then import the attached FunctionsGuide_Entities.zip.     Step 7: Next Steps   Congratulations! You've successfully completed the Explore UI Functions guide, and learned best practices for building a complex Mashup that navigations, multiple data inputs, confirmations, and all working together effectively for an enhanced user experience.   Learn More   We recommend the following resources to continue your learning experience:    Capability    Guide 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 Mashup Builder Support Help Center
View full tip
  Step 3: Create Validation and Status   With our MyFunctionsMashup Mashup open, let's add a Validation. A Validation is similar to an Expression, except you have the added capability of triggering Events based on a True or False outcome of your validation. We will use the Validation to check and confirm the Text Field we created only has the values we added in our Functions. Let's also add two Status Message Functions that will show whether or not a user has added any text outside of what we want.   Open the MyFunctionsMashup Mashup to the Design tab. Click the green + button in the Functions area.    In the New Function modal, select Validator.   Set the Name to isDataClean.   Click Next.  Click Add Parameter. Set the Name to text and ensure the Base Type is STRING.   Add the following code to the Expression are: if(text === "NO") { result = true; } else if(text === "YES") { result = true; } else { let array = text.split("YES").join(""); array = array.split(",").join(""); let count = array.trim().length; if(count) { result = false; } else { result = true; } }   9. Click Done.   We have our Validator in place, now we need our two Status Message Functions. Why two? You can setup one Status Message to perform the task, but for this case, we're keeping things simple.   Click the + button in the Functions area. Select Status Message in the dropdown.    Set the Name to GoodInputProvided.   Click Next. Ensure Message Type is Info. In the Message field, enter Text is all good!.   Click Done. Let's create another Status Message Function. Set the Name to BadnputProvided.   Click Next. Change Message Type to Error. In the Message field, enter Text is BAD!.   Click Done.   We now have two Status Message Functions and a Validator to help with checking our text data. Let's connect everything together. This time, let's use the Bind button.   Expand the Validator section in the Functions tab. Click the Bind (arrows) button on the isDataClean Validator. This window will help us configure connections a bit easier.    Click the down arrow by the True Event. Click Add Trigger Service.   Click Functions. Check the checkbox by GoodInputProvided.   Click Next. Click the down arrow by the False Event. Click Add Trigger Service.   Click Functions. Check the checkbox by BadInputProvided.   Click Next. You should currently have the following setup:    Let's add in our connections to the Text Field and when we'll run this Validation.    Click the down arrow by the text Property.   Click Add Source. With the Widgets tab selected, scroll down and select the Text Property of our Text Field.   Click Next. Click the down arrow by Evaluate Service. Select Add Event Trigger.   With the Widgets tab selected, scroll down and select the Clicked Property of our Button.   Click Next. You should currently have the following setup:   Click Done. Click Save and view your updated Mashup.   Your Validator is complete. You now have a way to tell when a user has inputed their own text into the text box. To try things out, add some crazy characters, hit the button, and see what happens. You might notice that you have your Expressions running at the same time as your Validator. Switch up the bindings to get it to run the way you want it to.     Step 4: Create Confirmation Modal   With our MyFunctionsMashup Mashup open, let's add a Confirmation Function. A Confirmation Function provides a quick modal that will give users a method to confirm actions or events before they take place. If you've ever almost deleted a production database (don't judge me!), then you know how handy a confirmation screen can be. Let's add a button that will trigger a confirmation as to whether we would like to run the Validator we created in the last section.   Open the MyFunctionsMashup Mashup to the Design tab. Click the + button in the Functions panel. Select Confirmation in the dropdown.   Set the Name to confirmDataValidation. Click Next.   Set the Title Text to Confirm Data Validation?. Set the Message Text to Would you like to perform a data validation?.   Set the Cancel Button Label to No Thanks!. Scroll down and set the Action Button Label to Yes Sir!.   Click Done. Click the Widgets tab in the top left. Filter for and select a Button Widget.   Drag and drop a Button Widget onto the Canvas.   With the new Button selected, click the down arrow that appears on the Button. Drag and drop the Clicked Event of the Button to the OpenConfirmation Service of our Confirmation Function.     We now have our Confirmation Function and a Button that will open the Confirmation when clicked. Let's add the final step by connecting the Confirmation to our Validation Function.   Click the Bind (arrows) button for our isDataClean Validator.   Click the down arrow by the Evaluate Service. Select Add Event Trigger.   Click the Functions tab. Select the ActionClick Event of our Confirmation Function.   Click Next. Click Done. Click Save for our Mashup.   We now have a way to independently validate that the text in our text box does not contain random values added by the user. View the Mashup and test things out by clicking on the second button and adding some crazy characters to our text box.       Step 5: Create Navigation   Thus far, we have been sticking to one Mashup. Let's venture out a bit by showing a different Mashup. Inside of our MyFunctionsMashup Mashup, we will add a Navigation Function. This will allow us to go to or just show a different UI based on some kind of user input or event. For our example, when a user clicks No Thanks! in our Confirmation Function, let's send them to a different Mashup.   Follow the steps to create a Navigation Function, a destination Mashup, and tie the two together.    Create Destination Mashup Navigate to Browse > Visualization > Mashups.   Click + New. Keep the defaults and click OK. In the Name field, type MyNavigationDestination.   Click Save. Click the Design tab at the top to open the Mashup canvas. Click the Widgets tab.   Filter and search for the word Label. Drag and drop a Label Widget to the Mashup canvas. If you like, enlarge the Label sizing.    In the Label Widget Properties section, scroll down to the LabelType Property.       Click the dropdown and select Large Header.   In the LabelText Property field, type MY DESTINATION UI SCREEN. Click Save. You have now created a simple UI that we will go to when we click our navigation button. Next, we'll tie together our navigation button and our freshly created Mashup.   Create Navigation Function Reopen the MyFunctionsMashup Mashup to the Design tab. Click the + button in the Functions panel. Select Navigation in the dropdown.   Set the Name to travelToDestination.   Click Next. Set the Target Window Type to ModalPopup. Set the Pop-up Title to New Popup Here.   Set the Pop-up Width to 400. Set the Pop-up Height to 400.   Click the + button at Target Mashup. Type MyNavigationDestination into the search bar. Select the MyNavigationDestination Mashup when it appears.   Click Done. Select the Bind (arrows) button for our new travelToDestination Navigation Function.   Click the down arrow next to Navigate Service. Click Add Event Trigger.   Click the Functions tab. Select the CancelClick Event of our confirmDataValidation Confirmation Function.   Click Done. Click Save for the Mashup.   We now have a modal that will appear after we click the No Thanks! button in our Confirmation Function. View the Mashup and try out what you've done by clicking the bottom button, then clicking No Thanks!     Click here to view Part 3 of this guide.  
View full tip
Announcements