How can I set up error recovery in my C++ code to communicate with a Yokogawa WT3000E over Ethernet using the TMCTL library?

0

I am writing C++ code to communicate with a Yokogawa WT3002E power analyzer over Ethernet using the TMCTL library found here: https://tmi.yokogawa.com/us/library/documents-downloads/software/tmctl/ The documentation for the API is included in that .zip file. I am using Visual Studio 2015.

I cannot figure out how to handle errors so that the code can continue pinging the device without interruption when an error occurs. The code works for normal circumstances already. The code needs to be robust enough so that if the Ethernet cable is disconnected, the device power cycles, the .exe is closed unexpectedly, or my computer shuts down unexpectedly, the code will be able to pick back up where it left off. I am running two threads. One of the threads is a pingThread, which queries “*IDN?” to the device to see if the device is still connected. The other thread actually issues the commands for what I want to monitor on the device. I am asking about pingThread because it is more simple and if I can solve the problems with it I should be able to figure out the other one.

Descriptions of user-defined functions/variables:

  • Comms is a class I made which stores all the variables for communication.
  • printError is a function I wrote which returns an error code based on TmcGetLastError.
  • desc is “(IP Address), anonymous,”, where IP Address is my IP address.
  • updateTimingStats and the variables named Responses, Fails, Tries, etc. are all for error checking, and are not relevant to this question.

Specific Problem: When I remove and replace the Ethernet cable connected from the WT3002E to my network switch, depending on the timing of the event, I will often enter an Error state which does not stop once I reconnect the Ethernet cable. When I have both TmcFinish and TmcInitialize, the only thing that is able to allow the program to reset and behave normally is power cycling the device, which is not a satisfactory solution for my application. When I have only TmcFinish, communication with the device still doesn’t reconnect, but restarting my program will allow the program to work again without power cycling the device. (The specific error logs are further down.)

printError definition:

void Comms::printError(int id) {
    int errorNumber = 0;
    errorNumber = TmcGetLastError(id);

    switch (errorNumber) {
    case TMCTL_NO_ERROR: //0x00000000
        printf("ERROR %x: No Error\n", errorNumber);
        break;
        
    case TMCTL_TIMEOUT:
        printf("ERROR %x: Timeout\n", errorNumber);
        break;
//all the other cases
    }
}

excerpts from main:

int main()
{
    Comms comm[MAX_DEVICES];

    // Begin by initializing the system
    for (int i = 0; i < MAX_DEVICES; i++) {
        comm[i].isRunning = true;
    }

    //Assigning IP addresses:
    unsigned char ipAddressTable[8][4] = {
        { 10, 0, 0, 253 }, //ipAddress 0 (change these to desired values)
        { 10, 0, 0, 252 }, //ipAddress 1
        { 10, 0, 0, 251 }, //ipAddress 2
        { 10, 0, 0, 250 }, //ipAddress 3
        { 10, 0, 0, 249 }, //ipAddress 4
        { 10, 0, 0, 248 }, //ipAddress 5
        { 10, 0, 0, 247 }, //ipAddress 6
        { 10, 0, 0, 246 }, //ipAddress 7
    };

    //Open communication with devices
    for (int i = 0; i < MAX_DEVICES; i++) {
        comm[i].ipAddress[0] = ipAddressTable[i][0];
        comm[i].ipAddress[1] = ipAddressTable[i][1];
        comm[i].ipAddress[2] = ipAddressTable[i][2];
        comm[i].ipAddress[3] = ipAddressTable[i][3];
        sprintf_s(comm[i].desc, "%d.%d.%d.%d,anonymous,", comm[i].ipAddress[0], comm[i].ipAddress[1], comm[i].ipAddress[2], comm[i].ipAddress[3]);
        printf("Connecting to IP Address %d: %s\n", i, comm[i].desc);
        //comm[i].status = viOpen(defaultRM, comm[i].desc, VI_NULL, VI_NULL, &comm[i].vi);
        comm[i].status = TmcInitialize(TM_CTL_ETHER, comm[i].desc, &comm[i].id);
        if (comm[i].status == 0) {
            printf("Connection to %s successful\n", comm[i].desc);

        }
        else {
            //if it failed to connect, keep attempting to connect
            while (comm[i].status == 1) {
                //printf("ERROR %x\n", TmcGetLastError(comm[i].id));
                comm[i].printError(comm[i].id);
                printf("Connection to %s failed, reconnecting...\n", comm[i].desc);
                Sleep(3000);
                comm[i].status = TmcInitialize(TM_CTL_ETHER, comm[i].desc, &comm[i].id);
                if (comm[i].status == 0) {
                    printf("Connection to %s successful\n", comm[i].desc);
                }
            }
        }

        //Set Terminal character to LF (\n)
        comm[i].status = TmcSetTerm(comm[i].id, 2, 0);
        if (comm[i].status != 0) {
            comm[i].printError(comm[i].id);
        }

        //Set Timeout to Infinite
        comm[i].status = TmcSetTimeout(comm[i].id, 0);
        if (comm[i].status != 0) {
            comm[i].printError(comm[i].id);
        }

        //Set Remote Mode to On
        comm[i].status = TmcSetRen(comm[i].id, 1);
        if (comm[i].status != 0) {
            comm[i].printError(comm[i].id);
        }
    }
//create threads and mutexes, including pingThread, then close them at the end of the program (not shown)
} //end of main

pingThread (where I need help):

unsigned int __stdcall pingThread(void * data)
{
    Comms * pComm = (Comms *)data; //pointer to objects of type Comms

    printf("pingThread Created: %d\n", GetCurrentThreadId());
    while (pComm->isRunning == true) {
        Sleep(5000);
        WaitForSingleObject(ghMutex[0], INFINITE);

        //ping fxn:
        updateTimingStats(pComm->pPingTime);
        //Ask Device for Identification:
        pComm->status = TmcSend(pComm->id, "*IDN?\n");
        pComm->pingWTries++;
        if (pComm->status != 0) { //write fail
            // something went wrong, so close this handle and clear it from our cache
            pComm->pingWFails++;
            //Print Error Message
            printf("TmcSend: ");
            pComm->printError(pComm->id);
            pComm->status = TmcFinish(pComm->id);
            if (pComm->status != 0) {
                printf("pingWrite TmcFinish: ");
                pComm->printError(pComm->id);
            }
            pComm->status = TmcInitialize(TM_CTL_ETHER, pComm->desc, &pComm->id);
            if (pComm->status != 0) {
                printf("pingWrite TmcInitialize: ");
                pComm->printError(pComm->id);
            }
        }
        else { //write success
            pComm->pingWResponses++;

            //Clear buffer and read the response:
            memset(pComm->buf, 0, sizeof(pComm->buf));
            pComm->status = TmcReceive(pComm->id, pComm->buf, sizeof(pComm->buf), &pComm->rlen);
            pComm->pingRTries++;
            if (pComm->status == 0) {   //read success
                printf("Connected to: %s\n", pComm->buf);
                pComm->isConnected = true;
                pComm->pingRResponses++;

                //calculate time taken for RTT
                updateTimingStats(pComm->pPingTime);
                printf("pingThread Round Trip Time: %I64d us\n", pComm->pPingTime->usLast);
            }
            else {  //read fail
                pComm->isConnected = false;
                pComm->pingRFails++;
                printf("TmcReceive: ");
                pComm->printError(pComm->id);

                pComm->status = TmcFinish(pComm->id); //This part works fine
                if (pComm->status != 0) {
                    printf("TmcFinish: ");
                    pComm->printError(pComm->id);
                }
                pComm->status = TmcInitialize(TM_CTL_ETHER, pComm->desc, &pComm->id); //Can not re-establish communication after TmcFinish.
                if (pComm->status != 0) {
                    printf("TmcInit: ");
                    pComm->printError(pComm->id);
                }
            }   //end of else (read fail)
        }   //end of else (write success)
        ReleaseMutex(ghMutex[0]);
    }   //end of while

I have tried multiple things for the case when TmcSend fails without success. Here are my results:

TmcFinish & TmcInitialize after TmcSend Fail:

  • TmcSend: Error 100: Send Error
  • TmcFinish successful (status = 0)
  • TmcInitialize fail. Error 20: Controller not found. (status = 1, id = -1)
  • TmcSend: Error 200: Device ID is illegal.
  • TmcFinish: Error 200: Device ID is illegal.
  • Error 200 repeats for both
  • Must power cycle device to be able to debug again

Only TmcFinish After TmcSend Fail:

  • TmcSend: Error 100: Send Error
  • TmcFinish successful (status = 0)
  • TmcSend: Error 8: Device Not Open
  • TmcFinish: Error 8: Device Not Open
  • Error 8 repeats for both
  • Can debug again by restarting debug on Visual Studio without power cycling device.

No TmcFinish & TmcInitialize in TmcSend Fail:

  • TmcSend: Error 100: Send Error (status = 1)
  • Repeats TmcSend: Error 100: Send Error
  • Can debug again by restarting debug on Visual Studio without power cycling device.
c++
error-handling
visual-studio-2015
ethernet
asked on Stack Overflow Apr 7, 2021 by Methodical Madman • edited Apr 7, 2021 by Methodical Madman

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0