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

C SDK Tutorial Part 4

No ratings

 

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.

  1. Inside of the [C SDK HOME DIR]/examples/FileTransferExample folder, create the folder structure shown below:

    /transfer/
    /transfer/incoming/
    /transfer/outgoing/

  2. Inside of the /transfer/outgoing/ directory, create and open a file with the name outgoing.txt.

  3. 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.
  4. 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_keyApplication 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"
  5. 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
sourceRepoSystemRepositoryThe name of FileRepository or RemoteThing to transfer the file FROM.
sourcePathoutgoingThe path specifying the location of the source file.
sourceFileThe name of the source file. 
targetRepoSteamSensor1The name of FileRepository or RemoteThing to transfer the file TO.
targetPathincomingThe path specifying the destination location of the file.
targetFileincoming.txtThe name of the file at the target. This name can differ from the sourceName.
timeout15,000The amount of time (in seconds) to wait for a synchronous transfer to complete before cancelling the transfer.
asyncfalseIf false, the service call will block for timeout seconds or until the transfer completes.
tidincoming0123The 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
200TW_UNKNOWN_WEBSOCKET_ERRORAn unknown error occurred on the websocket.
201TW_ERROR_INITIALIZING_WEBSOCKETAn error occurred while initializing the websocket. Check your websocket configuration parameters for validity.
202TW_TIMEOUT_INITIALIZING_WEBSOCKETA timeout occurred while initializing the websocket. Check the status of the connection to ThingWorx.
203TW_WEBSOCKET_NOT_CONNECTEDThe websocket is not connected to ThingWorx. The requested operation cannot be performed.
204TW_ERROR_PARSING_WEBSOCKET_DATAAn error occurred while parsing websocket data. The parser could not break down the data from the websocket.
205TW_ERROR_READING_FROM_WEBSOCKETAn error occurred while reading data from the websocket. Retry the read operation. If necessary, resend the data.
206TW_WEBSOCKET_FRAME_TOO_LARGEThe 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).
207TW_INVALID_WEBSOCKET_FRAME_TYPEThe type of the frame coming in over the websocket is invalid.
208TW_WEBSOCKET_MSG_TOO_LARGEThe 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.
209TW_ERROR_WRITING_TO_WEBSOCKETAn error occurred while writing to the Web socket.
210TW_INVALID_ACCEPT_KEYThe Accept key sent earlier from ThingWorx is not valid.
 

Next Steps

Congratulations! You've successfully completed the C SDK Tutorial, and learned how to utilize the resources provided in the Edge SDK to create your own application.

 

This is the last guide in the Connect and Configure Industrial Devices and Systems Learning Path.  If you wish to return to the Learning Path, click the link.

 

Additional Resources

If you have questions, issues, or need additional information, refer to:

 Resource Link
CommunityDeveloper Community Forum
SupportC Edge SDK Help Center
Version history
Last update:
‎Nov 18, 2022 04:29 PM
Updated by:
Labels (1)
Contributors