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. 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 Community Developer Community Forum Support C Edge SDK Help Center
View full tip