Log complete code

This page contains the complete code of Log.

In Log.cpp, your log code should be as follows:

Copy
#include "RT_Project_01.h"
#include <ctime>
#include <wchar.h>
using namespace std;

int CreateUserVariables()
{
    RtPrintf("Work with user variables.\n\n");

    /*------Add variables------*/

    /*Declare three instances of UserVariable. These variables are in share
      memory. They can be used by KS APIs, KS tools, and user applications.*/
    //u = user.
    UserVariable uTemperature = { 0, L"Temperature", NULL, logDouble };
    UserVariable uVoltage = { 0, L"Voltage", NULL, logDouble };
    UserVariable uWarning = { 0, L"Warning", NULL, logBool };

    //Add three user variables to the KS Subsystem.
    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);

    //Set a default value to each variable.
    *((double*)uTemperature.Value) = 1;
    *((double*)uVoltage.Value) = 1;
    *((BOOL*)uWarning.Value) = FALSE;

    //Use string to display uWarning's value.
    string uWarningText = "NULL";

    if (*((BOOL*)uWarning.Value))
        uWarningText = "TRUE";
    else
        uWarningText = "FALSE";

    printf("Temperature: %f\n", *((double*)uTemperature.Value));
    printf("Voltage: %f\n", *((double*)uVoltage.Value));
    RtPrintf("Warning: %s\n\n", uWarningText.c_str());
    
    /*------Get a variable------*/

    RtPrintf("Get user variables one by one.\n\n");

    //Pass the address of the variables to GetVariable.
    GetVariable(&uTemperature);
    GetVariable(&uVoltage);
    GetVariable(&uTemperature);

    /*------Get variables------*/

    RtPrintf("Get the list of user variables.\n\n");

    //Declare the variables for the buffer length and the total of the variables.
    int BufferLength = 3;
    int Variables = 0;

    /*There are two ways to allocate memory space to store the list of user variables.
      One is malloc() - free(). The other is array.*/

    //UserVariable *Space = (UserVariable*)malloc(BufferLength * sizeof(UserVariable));

    //Declare an array to store the list of user variables. Max is 64.
    UserVariable Space[3];

    //The types of a log.
    wstring logType[11] = { L"logBool", L"logByte", L"logSInt", L"logWord", L"logInt",
                            L"logDWord", L"logDInt", L"logFloat", L"logLWord", L"logLInt",
                            L"logDouble" };

    //The array used to store the types of user variables.
    wstring Type[3] = { L"NULL" };

    GetVariables(BufferLength, &Variables, Space);
    RtPrintf("Total variables: %d\n\n", Variables);

    //Convert the Type value into string.
    for (int i = 0; i < 3; i++)   //For Type array.
    {
        for (int j = 0; j < 11; j++)   //For logType array.
        {
            if (Space[i].Type == j)
                Type[i] = logType[j];   //Use Type elements to save the result.
        }
    }

    //Display each user variable's attributes.
    wprintf(L"Variables details: ID: %d, Name: %s, Value: %f, Type: %s\n\n",
        Space[0].Id, Space[0].Name, *((double*)Space[0].Value), Type[0].c_str());
    wprintf(L"Variables details: ID: %d, Name: %s, Value: %f, Type: %s\n\n",
        Space[1].Id, Space[1].Name, *((double*)Space[1].Value), Type[1].c_str());
    wprintf(L"Variables details: ID: %d, Name: %s, Value: %d, Type: %s\n\n",
        Space[2].Id, Space[2].Name, *((BOOL*)Space[2].Value), Type[2].c_str());

    //Free the space allocated to read user variables.
    //If you use malloc(), you need to use free(). 
    //free(Space);

    /*------Remove variables------*/

    RtPrintf("Remove user variables.\n\n");

    //Remove the added user variables.
    RemoveVariable(uTemperature.Id);
    RemoveVariable(uVoltage.Id);
    RemoveVariable(uWarning.Id);

    return 0;
}

int RecordData()
{
    RtPrintf("Use user variables to record data.\n\n");

    //Add user variables.
    UserVariable uTemperature = { 0, L"Temperature", NULL, logDouble };
    UserVariable uVoltage = { 0, L"Voltage", NULL, logDouble };
    UserVariable uWarning = { 0, L"Warning", NULL, logBool };

    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);

    //Set a default value to each variable.
    *((double*)uTemperature.Value) = 1;
    *((double*)uVoltage.Value) = 1;
    *((BOOL*)uWarning.Value) = FALSE;

    //Set three channels.
    //KsLogVariable is inactive when you use logUser.
    /*The indexes are determined by the order you add variables.
      Temperature(0), Voltage(1), Warning(2).*/
    KsLogChannel Channel[3] =
    {
        { logUser, 0, logNone, 0, logDouble },
        { logUser, 1, logNone, 0, logDouble },
        { logUser, 2, logNone, 0, logBool }
    };

    /*Start the log. We record 5 seconds. We don't use WaitForCommand
      because we are recording dummy data generated by the program.*/
    KsCommandStatus logState = Log(3, Channel, 0, 0, logImmediately, 5);

    //Set a seed to create random numbers for user variables.
    srand(static_cast<double>(time(0)));
    double Num1 = 0;
    double Num2 = 0;
    BOOL Switch = 0;

    for (int i = 0; i < 30000000; i++)
    {
        //Set a seed to create random numbers for user variables.
        Num1 = 0;
        Num2 = 0;
        Num1 = (rand() % 1000 + 100) / 1.05237;
        *((double*)uTemperature.Value) = Num1;
        Num2 = (rand() % 100 + 20) / 1.02496;
        *((double*)uVoltage.Value) = Num2;
        Switch = rand() & 1;
        *((BOOL*)uWarning.Value) = Switch;
    }

    //Get the Log state to make sure it is done.
    logState = GetCommandStatus(logState);
    RtPrintf("logState.ValueLength: %d\n\n", logState.ValueLength);
    RtPrintf("logState.Done: %d\n\n", logState.Done);

    //If Log is done, start to copy the recorded data to the specified location.
    if (logState.Done && logState.ValueLength > 0)
    {
        /*Declare a pointer, and use the address of this pointer to receive the log data
          in the shared memory, which must be opened by RtOpenSharedMemory.*/
        double* logData = 0;
        HANDLE hlogSpace = RtOpenSharedMemory(SHM_MAP_READ, FALSE, L"KSLogSpace", (void**)&logData);
        if (hlogSpace == NULL || logData == NULL)
        {
            RtPrintf("Shared memory is not opened.\n\n");
            return -1;
        }

        /*The value length is determined by how long the recording lasts
          and how fast the cycle time. For example, a 10-second recording
          with 1-millisecond cycle time produces 10000 values.

          We use 500 microseconds for a cycle and record 5 seconds. Each variable
          in each cycle is recorded once, so each variable's ValueLength is 10000.
          The total ValueLength is 30000.

          1 second = 1000 milliseconds
          1 millisecond = 1000 microseconds*/

          //Declare a pointer.
        double* pLog = 0;

        //Allocate memory and assign the address to the pointer.
        pLog = (double*)(malloc(sizeof(double) * 3 * logState.ValueLength));

        //Copy the content from the address of logData to pLog.
        memcpy(pLog, logData, sizeof(double) * 3 * logState.ValueLength);

        //Create a file stream and save it to a .txt file.
        FILE* logFile = fopen("C:\\LogData.txt", "w");
        if (logFile == NULL)
            RtPrintf("Cannot open the file.");

        double Value = 0;

        //Save the log data o the .txt file.
        for (int i = 0; i < 3; i++)
        {
            //Print titles.
            switch (i)
            {
            case 0:
                fprintf(logFile, "Temperature:\n\n");
                break;
            case 1:
                fprintf(logFile, "Voltage:\n\n");
                break;
            case 2:
                fprintf(logFile, "Warning:\n\n");
                break;
            default:
                RtPrintf("Unable to write data into the log file.\n\n");
            }

            //Print data.
            /*Because the data stream is ABCABCABC...not AAABBBCCC,
              we need to extract it by position.*/
            for (int j = 0; j < 3 * logState.ValueLength; j++)
            {
                Value = pLog[j];

                //Extract Temperature.
                if (i == 0 && j % 3 == 0)
                    fprintf(logFile, "%lf\t", Value);

                //Extract Voltage.
                else if (i == 1 && j % 3 == 1)
                    fprintf(logFile, "%lf\t", Value);

                //Extract Warning.
                else if (i == 2 && j % 3 == 2)
                    fprintf(logFile, "%d\t", Value);
            }

            if (i != 2)
                fprintf(logFile, "\n\n");
        }

        RtCloseHandle(hlogSpace);   //Close the open RTSS object handle.
        free(pLog);   //Free the allocated memory.
        pLog = 0;     //Assign zero to avoid a dangling pointer.
        logData = 0;  //Assign zero to avoid a dangling pointer.
        fclose(logFile);   //Close the file stream.
    }

    return 0;
}

int DebugMessages()
{
    RtPrintf("Display customized messages in Analysis Console.\n\n");

    //Set log IDs and names.
    SetCategoryName(0, L"Temperature");
    SetCategoryName(1, L"Voltage");
    SetCategoryName(2, L"Warning");

    //Initialize log names to NULL.
    wchar_t Log0[] = L"NULL";
    wchar_t Log1[] = L"NULL";
    wchar_t Log2[] = L"NULL";

    //Get the log names.
    GetCategoryName(0, Log0);
    wprintf(L"Log 0 is: %s\n", Log0);
    GetCategoryName(1, Log1);
    wprintf(L"Log 1 is: %s\n", Log1);
    GetCategoryName(2, Log2);
    wprintf(L"Log 2 is: %s\n\n", Log2);

    //Display the user log in Analysis Console.
    DebugMessage(0, logInfo, L"Melting temperature of metal", TRUE, 328, 660, 1860, 1204);
    DebugMessage(1, logInfo, L"Voltage of power", TRUE, 23, 12, 60, 45);
    DebugMessage(2, logInfo, L"Warning levels", TRUE, 0, 1, 0, 1);

    return 0;
}

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;
}