Use user variables between projects
You have learned how log functions work in previous sections. To help you understand how these functions work together, we provide two Visual Studio projects to demonstrate how user variables are created, sent, and retrieved between different applications, and display these variables in Analysis Console. Remember that you don't have to do it this way. You can use one or multiple applications to finish your job. It all depends on your needs.
Before you start this tutorial, you should read Work with user variables and Debug messages, which contain the necessary information for you to learn how to handle user variables and display values in KINGSTAR Analysis Console.
Code
We break the steps down into a few parts for you to learn it easily.
Before we start, in RT_Project_01
, in Log.cpp
, add the following function. We include the header wchar.h
because wide characters will be used. We are writing code in the LogProject function. The first line of code, RtPrintf("Use user variables between projects.\n\n");
, displays the message that describes what this function is going to do.
NOTE: Some header files overlaps with others in different sections.
#include "RT_Project_01.h"
#include <wchar.h>
int LogProject()
{
RtPrintf("Use user variables between projects.\n\n");
return 0;
}
Step I: RT_Project_01
- In LogProject, add the following user variables and set their default values. In addition to the three variables we mentioned in previous sections, we add more variables to be used to check conditions and display values. You can review the section Add variables to know how to add them.
- Next, we want users to start the
RT_Project_01_Reader
project. We use awhile
loop to check whetherRT_Project_01_Reader
is started. If it is not,RT_Project_01
will keep waiting. If it is started, theRT_Project_01
will proceed. - After
RT_Project_01_Reader
is ready, we start to send data to it. In this guide we use a nestedfor
loop to control the timing of data transmission. The outer loop sends the data whenSend
becomes TRUE, and adds some variation to the value of the variableuWarning
. The inner loop uses equations to generate dummy data and sends it three times toRT_Project_01_Reader
.
/* Sender and Reader */
/*This is an example to demonstrate how to pass and use user variables
between different applications. The instances of the UserVariable
structure are in the shared memory, so they can be used by KINGSTAR APIs
and KS applications.
In this example, RT_Project_01 sends data, RT_Project_01_Reader reads the data
and sends it to Analysis Console.*/
/*Declare the instances of UserVariable.
uTemperature, uVoltage, uWarning: user variables.
Ready: checks whether RT_Project_01_Reader is running.
Send: checks whether the data should be sent.
uValues: the values used in the variables that will be displayed
in Analysis Console.*/
UserVariable uTemperature = { 0, L"Temperature", NULL, logDouble };
UserVariable uVoltage = { 0, L"Voltage", NULL, logDouble };
UserVariable uWarning = { 0, L"Warning", NULL, logBool };
UserVariable Ready = { 0, L"Ready", NULL, logBool };
UserVariable Send = { 0, L"Send", NULL, logBool };
UserVariable uValue1 = { 0, L"Value1", NULL, logDouble };
UserVariable uValue2 = { 0, L"Value2", NULL, logDouble };
UserVariable uValue3 = { 0, L"Value3", NULL, logDouble };
UserVariable uValue4 = { 0, L"Value4", NULL, logDouble };
//Add user variables.
KsError nRet = AddVariable(&uTemperature);
if (nRet != errNoError)
RtPrintf("AddVariable failed: %x\n", nRet);
nRet = AddVariable(&uVoltage);
if (nRet != errNoError)
RtPrintf("AddVariable failed: %x\n", nRet);
nRet = AddVariable(&uWarning);
if (nRet != errNoError)
RtPrintf("AddVariable failed: %x\n\n", nRet);
nRet = AddVariable(&Ready);
if (nRet != errNoError)
RtPrintf("AddVariable Ready failed: %x\n", nRet);
nRet = AddVariable(&Send);
if (nRet != errNoError)
RtPrintf("AddVariable Send failed: %x\n", nRet);
nRet = AddVariable(&uValue1);
if (nRet != errNoError)
RtPrintf("AddVariable uValue1 failed: %x\n", nRet);
nRet = AddVariable(&uValue2);
if (nRet != errNoError)
RtPrintf("AddVariable uValue2 failed: %x\n", nRet);
nRet = AddVariable(&uValue3);
if (nRet != errNoError)
RtPrintf("AddVariable uValue3 failed: %x\n", nRet);
nRet = AddVariable(&uValue4);
if (nRet != errNoError)
RtPrintf("AddVariable uValue4 failed: %x\n", nRet);
//Set default values to user variables.
*((double*)uTemperature.Value) = 1;
*((double*)uVoltage.Value) = 1;
*((BOOL*)uWarning.Value) = FALSE;
*((BOOL*)Ready.Value) = FALSE;
*((BOOL*)Send.Value) = TRUE;
*((double*)uValue1.Value) = 0;
*((double*)uValue2.Value) = 0;
*((double*)uValue3.Value) = 0;
*((double*)uValue4.Value) = 0;
RtPrintf("Start RT_Project_01_Reader to continue...\n\n");
//Check whether RT_Project_01_Reader is running.
while (*((BOOL*)Ready.Value) == FALSE)
{
Sleep(10);
}
//After RT_Project_01_Reader is run, display the message.
RtPrintf("The project is ready.\n\n");
//Send the values of user variables to RT_Project_01_Reader.
//This loop sends the variables three times.
for (int i = 0; i < 3; i++)
{
//When Send is FALSE, wait RT_Project_01_Reader to read the data.
while (*((BOOL*)Send.Value) == FALSE)
{
Sleep(10);
}
//When Send is TRUE, send the data.
//Add some variation to uWarning.
if (i % 2 == 0)
*((BOOL*)uWarning.Value) = FALSE;
else
*((BOOL*)uWarning.Value) = TRUE;
//This loop is to calculate the values for a variable.
for (int j = 0; j < 3; j++)
{
//When Send is FALSE, hold the calculation.
while (*((BOOL*)Send.Value) == FALSE)
{
Sleep(10);
}
//When Send is TRUE, reset the values to zero and do the calcuation.
//Reset uValues.
*((double*)uValue1.Value) = 0;
*((double*)uValue2.Value) = 0;
*((double*)uValue3.Value) = 0;
*((double*)uValue4.Value) = 0;
/*To repeatedly use uValues, we use a switch statement to do the trick.
Each uValue will be reset to zero after the values they contain are sent
to RT_Project_01_Reader.
Since the for-i loop is repeated three times, each value is calculated
three times.*/
switch (j)
{
/*These values are used for demonstration. You should determine your
own equation to calculate each value.*/
case 0:
*((double*)uValue1.Value) = *((double*)uTemperature.Value) * 1 + i;
*((double*)uValue2.Value) = *((double*)uTemperature.Value) * 10.5 + i;
*((double*)uValue3.Value) = *((double*)uTemperature.Value) * 20.7 + i;
*((double*)uValue4.Value) = *((double*)uTemperature.Value) * 30.6 + i;
break;
case 1:
*((double*)uValue1.Value) = *((double*)uVoltage.Value) * 1 + i;
*((double*)uValue2.Value) = *((double*)uVoltage.Value) * 5.2 + i;
*((double*)uValue3.Value) = *((double*)uVoltage.Value) * 10.4 + i;
*((double*)uValue4.Value) = *((double*)uVoltage.Value) * 15.3 + i;
break;
case 2:
*((double*)uValue1.Value) = *((BOOL*)uWarning.Value);
*((double*)uValue2.Value) = *((BOOL*)uWarning.Value);
*((double*)uValue3.Value) = *((BOOL*)uWarning.Value);
*((double*)uValue4.Value) = *((BOOL*)uWarning.Value);
break;
default:
RtPrintf("The switch statement is not working properly.\n\n");
}
//Set Send to FALSE for RT_Project_01_Reader to process the data.
*((BOOL*)Send.Value) = FALSE;
}
}
Step II: RT_Project_01_Reader
This is a Visual Studio project used to demonstrate how user variables and debug messages work only. Since this project doesn't have other uses, we write all code in the main
function, which is in main.cpp
.
In the main
function, add the following code:
- Before we deal with the user variables created in
RT_Project_01
, we need to use Create to link the project to the KINGSTAR Subsystem process. Displaying the devices on the EtherCAT network allows us to know whether the the connection is successful. - After connected, we declare the same user variables as we previously declared in
RT_Project_01
, and use GetVariable to get their addresses one by one. GetVariable is required to use in this project because we need to make changes to these variables between two projects. Without GetVariable, even if you declare the same-name variables in second project, they are still different from the variables in the first. - After retrieve the variables, we set
Ready
andSend
to TRUE.Ready
is used to tellRT_Project_01
that we are ready, soRT_Project_01
can continue;Send
is used to determine whetherRT_Project_01
should send the data. We have listed each variable's meaning when we declare them inRT_Project_01
. Take a look at them. - When
Send
is TRUE,RT_Project_01
calculates the values for each variable. After the calculation is done,Send
will be set to FALSE andRT_Project_01_Reader
sends the variable and its calculated values to Analysis Console. Each timeRT_Project_01_Reader
sends the data,Send
will be set to TRUE, so the entire process starts fromRT_Project_01
again, until all the variables and their values are calculated and sent to Analysis Console. - After all variables and their values are sent to Analysis Console, we display a message to inform users that all messages are delivered.
//Launch or connect to the KINGSTAR Subsystem process.
KsError nRet = errNoError;
nRet = Create(0, 0);
if (nRet != errNoError)
{
RtPrintf("InitializeLink failed: %x\n", nRet);
return FALSE;
}
//Create an instance of the SubsystemStatus structure and get the state from it.
SubsystemStatus Subsystem = { ecatOffline, ecatOffline, 0, 0, 0, {ecatOffline}, {ecatOffline}, {axisOffline} };
GetStatus(&Subsystem, NULL);
//Display the details of the EtherCAT network. These should be the same as RT_Project_01.
RtPrintf("Number of Devices found: %d\n", Subsystem.SlaveCount);
RtPrintf("Number of Axes found: %d\n", Subsystem.AxesCount);
RtPrintf("Number of I/O found: %d\n", Subsystem.IOCount);
RtPrintf("\n"); //Launch or connect to the KINGSTAR Subsystem process.
KsError nRet = errNoError;
nRet = Create(0, 0);
if (nRet != errNoError)
{
RtPrintf("InitializeLink failed: %x\n", nRet);
return FALSE;
}
//Create an instance of the SubsystemStatus structure and get the state from it.
SubsystemStatus Subsystem = { ecatOffline, ecatOffline, 0, 0, 0, {ecatOffline}, {ecatOffline}, {axisOffline} };
GetStatus(&Subsystem, NULL);
//Display the details of the EtherCAT network. These should be the same as RT_Project_01.
RtPrintf("Number of Devices found: %d\n", Subsystem.SlaveCount);
RtPrintf("Number of Axes found: %d\n", Subsystem.AxesCount);
RtPrintf("Number of I/O found: %d\n", Subsystem.IOCount);
RtPrintf("\n");
You can also use GetVariables. In this guide we use GetVariable becuase it's intuitive and easy to use.
//Declare the same instances of UserVariable in RT_Project_01.
UserVariable uTemperature = { 0, L"Temperature", NULL, logDouble };
UserVariable uVoltage = { 0, L"Voltage", NULL, logDouble };
UserVariable uWarning = { 0, L"Warning", NULL, logBool };
UserVariable Ready = { 0, L"Ready", NULL, logBool };
UserVariable Send = { 0, L"Send", NULL, logBool };
UserVariable uValue1 = { 0, L"Value1", NULL, logDouble };
UserVariable uValue2 = { 0, L"Value2", NULL, logDouble };
UserVariable uValue3 = { 0, L"Value3", NULL, logDouble };
UserVariable uValue4 = { 0, L"Value4", NULL, logDouble };
//Get the address of the user variables.
/*In Sender application (RT_Project_01), you don't need to use GetVariable because
you can get the address of a variable after you use AddVariable. But in Reader
application (RT_Project_01_Reader), you must use GetVariable to access the variables.*/
GetVariable(&uTemperature);
GetVariable(&uVoltage);
GetVariable(&uWarning);
GetVariable(&uValue1);
GetVariable(&uValue2);
GetVariable(&uValue3);
GetVariable(&uValue4);
GetVariable(&Ready);
GetVariable(&Send);
//By default, we set Ready to TRUE so RT_Project_01 will continue to run.
*((bool*)Ready.Value) = TRUE;
*((bool*)Send.Value) = TRUE;
//Display the logs and their values in Analysis Console.
//These values are for demonstration.
for (int i = 0; i < 3; i++)
{
//When Send is TRUE, wait RT_Project_01 to send the data.
while (*((bool*)Send.Value) == TRUE)
{
Sleep(10);
}
//When Send is FALSE, receive the data.
//Send the logs to Analysis Console.
RtPrintf("DebugMessage %d starts.\n\n", i);
//Get the values of the first variable and send them to Analysis Console.
DebugMessage(0, logInfo, L"Melting temperature of metal", TRUE,
*((double*)uValue1.Value),
*((double*)uValue2.Value),
*((double*)uValue3.Value),
*((double*)uValue4.Value));
/*After the first variable is sent, wait RT_Project_01 to reset
uValues to zero, and calculate the values of the second variable.*/
*((bool*)Send.Value) = TRUE;
while (*((bool*)Send.Value) == TRUE)
{
Sleep(10);
}
//Get the values of the second variable and send them to Analysis Console.
DebugMessage(1, logInfo, L"Voltage of power", TRUE,
*((double*)uValue1.Value),
*((double*)uValue2.Value),
*((double*)uValue3.Value),
*((double*)uValue4.Value));
/*After the first variable is sent, wait RT_Project_01 to reset
uValues to zero, and calculate the values of the third variable.*/
*((bool*)Send.Value) = TRUE;
while (*((bool*)Send.Value) == TRUE)
{
Sleep(10);
}
//Get the values of the third variable and send them to Analysis Console.
DebugMessage(2, logWarning, L"Warning levels", TRUE,
*((double*)uValue1.Value),
*((double*)uValue2.Value),
*((double*)uValue3.Value),
*((double*)uValue4.Value));
//Set Send to TRUE for RT_Project_01 to process the data.
*((bool*)Send.Value) = TRUE;
}
RtPrintf("DebugMessage ends.\n\n");
return 0;
Complete code
The complete code should be as follows:
RT_Project_01:
int LogProject()
{
RtPrintf("Use user variables between projects.\n\n");
/* Sender and Reader */
/*This is an example to demonstrate how to pass and use user variables
between different applications. The instances of the UserVariable
structure are in the shared memory, so they can be used by KINGSTAR APIs
and KS applications.
In this example, RT_Project_01 sends data, RT_Project_01_Reader reads the data
and sends it to Analysis Console.*/
/*Declare the instances of UserVariable.
uTemperature, uVoltage, uWarning: user variables.
Ready: checks whether RT_Project_01_Reader is running.
Send: checks whether the data should be sent.
uValues: the values used in the variables that will be displayed
in Analysis Console.*/
UserVariable uTemperature = { 0, L"Temperature", NULL, logDouble };
UserVariable uVoltage = { 0, L"Voltage", NULL, logDouble };
UserVariable uWarning = { 0, L"Warning", NULL, logBool };
UserVariable Ready = { 0, L"Ready", NULL, logBool };
UserVariable Send = { 0, L"Send", NULL, logBool };
UserVariable uValue1 = { 0, L"Value1", NULL, logDouble };
UserVariable uValue2 = { 0, L"Value2", NULL, logDouble };
UserVariable uValue3 = { 0, L"Value3", NULL, logDouble };
UserVariable uValue4 = { 0, L"Value4", NULL, logDouble };
//Add user variables.
KsError nRet = AddVariable(&uTemperature);
if (nRet != errNoError)
RtPrintf("AddVariable failed: %x\n", nRet);
nRet = AddVariable(&uVoltage);
if (nRet != errNoError)
RtPrintf("AddVariable failed: %x\n", nRet);
nRet = AddVariable(&uWarning);
if (nRet != errNoError)
RtPrintf("AddVariable failed: %x\n\n", nRet);
nRet = AddVariable(&Ready);
if (nRet != errNoError)
RtPrintf("AddVariable Ready failed: %x\n", nRet);
nRet = AddVariable(&Send);
if (nRet != errNoError)
RtPrintf("AddVariable Send failed: %x\n", nRet);
nRet = AddVariable(&uValue1);
if (nRet != errNoError)
RtPrintf("AddVariable uValue1 failed: %x\n", nRet);
nRet = AddVariable(&uValue2);
if (nRet != errNoError)
RtPrintf("AddVariable uValue2 failed: %x\n", nRet);
nRet = AddVariable(&uValue3);
if (nRet != errNoError)
RtPrintf("AddVariable uValue3 failed: %x\n", nRet);
nRet = AddVariable(&uValue4);
if (nRet != errNoError)
RtPrintf("AddVariable uValue4 failed: %x\n", nRet);
//Set default values to user variables.
*((double*)uTemperature.Value) = 1;
*((double*)uVoltage.Value) = 1;
*((BOOL*)uWarning.Value) = FALSE;
*((BOOL*)Ready.Value) = FALSE;
*((BOOL*)Send.Value) = TRUE;
*((double*)uValue1.Value) = 0;
*((double*)uValue2.Value) = 0;
*((double*)uValue3.Value) = 0;
*((double*)uValue4.Value) = 0;
RtPrintf("Start RT_Project_01_Reader to continue...\n\n");
//Check whether RT_Project_01_Reader is running.
while (*((BOOL*)Ready.Value) == FALSE)
{
Sleep(10);
}
//After RT_Project_01_Reader is run, display the message.
RtPrintf("The project is ready.\n\n");
//Send the values of user variables to RT_Project_01_Reader.
//This loop sends the variables three times.
for (int i = 0; i < 3; i++)
{
//When Send is FALSE, wait RT_Project_01_Reader to read the data.
while (*((BOOL*)Send.Value) == FALSE)
{
Sleep(10);
}
//When Send is TRUE, send the data.
//Add some variation to uWarning.
if (i % 2 == 0)
*((BOOL*)uWarning.Value) = FALSE;
else
*((BOOL*)uWarning.Value) = TRUE;
//This loop is to calculate the values for a variable.
for (int j = 0; j < 3; j++)
{
//When Send is FALSE, hold the calculation.
while (*((BOOL*)Send.Value) == FALSE)
{
Sleep(10);
}
//When Send is TRUE, reset the values to zero and do the calcuation.
//Reset uValues.
*((double*)uValue1.Value) = 0;
*((double*)uValue2.Value) = 0;
*((double*)uValue3.Value) = 0;
*((double*)uValue4.Value) = 0;
/*To repeatedly use uValues, we use a switch statement to do the trick.
Each uValue will be reset to zero after the values they contain are sent
to RT_Project_01_Reader.
Since the for-i loop is repeated three times, each value is calculated
three times.*/
switch (j)
{
/*These values are used for demonstration. You should determine your
own equation to calculate each value.*/
case 0:
*((double*)uValue1.Value) = *((double*)uTemperature.Value) * 1 + i;
*((double*)uValue2.Value) = *((double*)uTemperature.Value) * 10.5 + i;
*((double*)uValue3.Value) = *((double*)uTemperature.Value) * 20.7 + i;
*((double*)uValue4.Value) = *((double*)uTemperature.Value) * 30.6 + i;
break;
case 1:
*((double*)uValue1.Value) = *((double*)uVoltage.Value) * 1 + i;
*((double*)uValue2.Value) = *((double*)uVoltage.Value) * 5.2 + i;
*((double*)uValue3.Value) = *((double*)uVoltage.Value) * 10.4 + i;
*((double*)uValue4.Value) = *((double*)uVoltage.Value) * 15.3 + i;
break;
case 2:
*((double*)uValue1.Value) = *((BOOL*)uWarning.Value);
*((double*)uValue2.Value) = *((BOOL*)uWarning.Value);
*((double*)uValue3.Value) = *((BOOL*)uWarning.Value);
*((double*)uValue4.Value) = *((BOOL*)uWarning.Value);
break;
default:
RtPrintf("The switch statement is not working properly.\n\n");
}
//Set Send to FALSE for RT_Project_01_Reader to process the data.
*((BOOL*)Send.Value) = FALSE;
}
}
return 0;
}
RT_Project_01_Reader:
#include "RT_Project_01_Reader.h"
int _tmain(int argc, _TCHAR * argv[])
{
//Launch or connect to the KINGSTAR Subsystem process.
KsError nRet = errNoError;
nRet = Create(0, 0);
if (nRet != errNoError)
{
RtPrintf("InitializeLink failed: %x\n", nRet);
return FALSE;
}
//Create an instance of the SubsystemStatus structure and get the state from it.
SubsystemStatus Subsystem = { ecatOffline, ecatOffline, 0, 0, 0, {ecatOffline}, {ecatOffline}, {axisOffline} };
GetStatus(&Subsystem, NULL);
//Display the details of the EtherCAT network. These should be the same as RT_Project_01.
RtPrintf("Number of Devices found: %d\n", Subsystem.SlaveCount);
RtPrintf("Number of Axes found: %d\n", Subsystem.AxesCount);
RtPrintf("Number of I/O found: %d\n", Subsystem.IOCount);
RtPrintf("\n");
//Declare the same instances of UserVariable in RT_Project_01.
UserVariable uTemperature = { 0, L"Temperature", NULL, logDouble };
UserVariable uVoltage = { 0, L"Voltage", NULL, logDouble };
UserVariable uWarning = { 0, L"Warning", NULL, logBool };
UserVariable Ready = { 0, L"Ready", NULL, logBool };
UserVariable Send = { 0, L"Send", NULL, logBool };
UserVariable uValue1 = { 0, L"Value1", NULL, logDouble };
UserVariable uValue2 = { 0, L"Value2", NULL, logDouble };
UserVariable uValue3 = { 0, L"Value3", NULL, logDouble };
UserVariable uValue4 = { 0, L"Value4", NULL, logDouble };
//Get the address of the user variables.
/*In Sender application (RT_Project_01), you don't need to use GetVariable because
you can get the address of a variable after you use AddVariable. But in Reader
application (RT_Project_01_Reader), you must use GetVariable to access the variables.*/
GetVariable(&uTemperature);
GetVariable(&uVoltage);
GetVariable(&uWarning);
GetVariable(&uValue1);
GetVariable(&uValue2);
GetVariable(&uValue3);
GetVariable(&uValue4);
GetVariable(&Ready);
GetVariable(&Send);
//By default, we set Ready to TRUE so RT_Project_01 will continue to run.
*((bool*)Ready.Value) = TRUE;
*((bool*)Send.Value) = TRUE;
//Display the logs and their values in Analysis Console.
//These values are for demonstration.
for (int i = 0; i < 3; i++)
{
//When Send is TRUE, wait RT_Project_01 to send the data.
while (*((bool*)Send.Value) == TRUE)
{
Sleep(10);
}
//When Send is FALSE, receive the data.
//Send the logs to Analysis Console.
RtPrintf("DebugMessage %d starts.\n\n", i);
//Get the values of the first variable and send them to Analysis Console.
DebugMessage(0, logInfo, L"Melting temperature of metal", TRUE,
*((double*)uValue1.Value),
*((double*)uValue2.Value),
*((double*)uValue3.Value),
*((double*)uValue4.Value));
/*After the first variable is sent, wait RT_Project_01 to reset
uValues to zero, and calculate the values of the second variable.*/
*((bool*)Send.Value) = TRUE;
while (*((bool*)Send.Value) == TRUE)
{
Sleep(10);
}
//Get the values of the second variable and send them to Analysis Console.
DebugMessage(1, logInfo, L"Voltage of power", TRUE,
*((double*)uValue1.Value),
*((double*)uValue2.Value),
*((double*)uValue3.Value),
*((double*)uValue4.Value));
/*After the first variable is sent, wait RT_Project_01 to reset
uValues to zero, and calculate the values of the third variable.*/
*((bool*)Send.Value) = TRUE;
while (*((bool*)Send.Value) == TRUE)
{
Sleep(10);
}
//Get the values of the third variable and send them to Analysis Console.
DebugMessage(2, logWarning, L"Warning levels", TRUE,
*((double*)uValue1.Value),
*((double*)uValue2.Value),
*((double*)uValue3.Value),
*((double*)uValue4.Value));
//Set Send to TRUE for RT_Project_01 to process the data.
*((bool*)Send.Value) = TRUE;
}
RtPrintf("DebugMessage ends.\n\n");
return 0;
}
Output:
RT_Project_01:
RT_Project_01_Reader:
Analysis Console: