The Connect button

It functions as a Connect or Disconnect button. After you click this button, the KINGSTAR Subsystem will connect to hardware. If hardware is already connected, click the button to disconnect.

The button's name is btnConnect, which has the signal clicked connected to the slot btnConnectClicked. After we click the button, we use setEnabled to disable it. This way, the button can't be pressed while the connection is being created. Timeout is a variable that determines whether the EtherCAT connection takes too much time to be created.

The following code is in QtGui.cpp:

void QtGui::btnConnectClicked()
   int Timeout = 0;
   ui->btnConnect->setEnabled(false);

Before we connect to hardware, we use the getecState function to check the EtherCAT connection state. getecState returns the value of maSts.State, by which we know whether an EtherCAT connection has been established. If it has, it means this button functions as a Disconnect button, otherwise it functions as a Connect.

Either the button functions as Connect or Disconnect, it calls two functions: processEvents and commandConnect. processEvents is a member function of QApplication that keeps the user interface (UI) responsive while the EtherCAT connection is being created or broken. commandConnect is a signal connected to the slot actionConnect. When we emit commandConnect, it immediately triggers actionConnect.

The following code is in QtGui.cpp:

while (ks->getecState() != ecatOP && Timeout < 2000)
{
   QApplication::processEvents();
   emit commandConnect();

actionConnect is written in ksworker.cpp. It uses a switch statement to test wkState, which is a variable of the enum workerState, in which there are three states: disconnected, connecting, and connected.

Connect

As a Connect button, the state is disconnected, in which we set wkState to connecting, which runs the connecting block in the switch statement.

Before connection

When we click Connect, the code in the disconnected block is run. The following functions initialize and create the EtherCAT link, and start the KINGSTAR Subsystem.

The following code is in ksworker.cpp:

ksWorker::ksWorker(QObject *parent)
: QObject(parent), dataTimer(new QTimer(this)), data({false, 0.0, 0.0}),
   maSts({ ecatOffline, ecatOffline, 0, 0, 0, { ecatOffline }, { ecatOffline }, { axisOffline } }), 
   wkState(disconnected), currentIndex(-1), commandVelocity(360)
{

   nRet = Create(0, 0);

   if (nRet != errNoError) {
      return;
   }
   ......
}

void ksWorker::actionConnect()
{
   switch (wkState)
   {
      case disconnected:
      wkState = connecting;

      nRet = SetAxisAccessMode(KsAccessMode::accessPos);
      nRet = SetCycleTime(cycle1000);
      nRet = SetConfiguredAxesCount(1);
      nRet = ConfigureDc(TRUE, TRUE, TRUE, 0);

      WaitForCommand(0.2, FALSE, Start());

      GetStatus(&maSts, nullptr);

      break;

      case connecting:
      GetStatus(&maSts, nullptr);
      if (maSts.State == EthercatState::ecatOP)
      {
         wkState = connected;

         SlaveStatus *ss = new SlaveStatus;

         McProfileSettings Motion = { 
            3,
            360,
            5000,
            50000,
            50000,
            5000000,
            0
         };

         for (int i = 0; i < maSts.AxesCount; i++)
         {
            int Resolution = 10000;
            DWORD InputVariables = 0;
            DWORD OutputVariables = 0;

            GetAxisByIndex(i, ss, &Resolution, &InputVariables, &OutputVariables);
            SetAxisMotionProfile(i, profileUnitPerSecond, Motion);
            SetAxisCountsPerUnit(i, Resolution, 360, FALSE);
            EnableAxisUnitConversion(i, TRUE);
            SetAxisPositionOffset(i, 0, FALSE, McExecutionMode::mcImmediately);
            powerStatus.push_back(FALSE);
           emit sendName(i, ss->Name);
         }
         delete ss;

         dataTimer->start(100);
      }
      break;
      ...........
   }
   ...........
}

During connection

In the connecting block, we use the following functions to initialize the motion settings:

Timer

We use dataTimer as a timer at the end of connecting. start sets the interval of dataTimer to 100 milliseconds. Every 100 milliseconds dataTimer emits the QTimer::timeout signal, which is connected to a lambda function as the third parameter.

When timeout is triggered, the lambda function checks whether the EtherCAT link state is Operational (Op) and currentIndex is greater than zero. If they are, we get the power state of the selected axis and assign it to data.powerStatus. currentIndex is the index of an axis. data is a variable of the axisData structure that is used get the state of an axis. We then use GetAxisPosition and GetAxisVelocity to get the actual position and actual velocity of the axis. Finally, we emit the sendData signal that is connected to the slot updateAxisData, which is run immediately. updateAxisData updates the GUI elements according to their states.

The following code is in ksworker.cpp:

QObject::connect(dataTimer, &QTimer::timeout, [this]()
{
   if (maSts.State == ecatOP && currentIndex >= 0)
   { 
      double pos = 0.0;
      double vel = 0.0;

      data.powerStatus = powerStatus[currentIndex];
      nRet = GetAxisPosition(currentIndex, McSource::mcActualValue, &pos);
      if (nRet == KsError::errNoError)
         data.actualPos = pos;
      else
         data.actualPos = (double)nRet;

      nRet = GetAxisVelocity(currentIndex, McSource::mcActualValue, &(vel));
      if (nRet == KsError::errNoError)
         data.actualVel = vel;
      else
         data.actualVel = (double)nRet;
   }

   emit sendData(data);
});

The following code is in QtGui.cpp:

void QtGui::updateAxisData(axisData data)
{
   if (!ui->btnEnable->isEnabled())
   {
      if (ui->btnEnable->text() == "Enable" && data.powerStatus)
      {
         ui->btnEnable->setText("Disable");
         ui->btnEnable->setEnabled(true);
         ui->btnJogForward->setEnabled(true);
         ui->btnJogBackward->setEnabled(true);
      }
      else if (ui->btnEnable->text() == "Disable" && !data.powerStatus)
      {
         ui->btnEnable->setText("Enable");
         ui->btnEnable->setEnabled(true);
         ui->btnJogForward->setEnabled(false);
         ui->btnJogBackward->setEnabled(false);
      }
   }
   else
   {
      if (data.powerStatus)
      {
         ...........
      }
      else
      {
         ...........
      }
   }

   ui->leActPosition->setText(QString::number(data.actualPos, 'f', 3));
   ui->leActVelocity->setText(QString::number(data.actualVel, 'f', 3));
}

After the connection is established, we change the settings of the following GUI elements.

The following code is in QtGui.cpp:

ui->listDevices->setCurrentRow(0);
ui->leCommVelocity->setEnabled(true); 	//Enable the Command Velocity box.
ui->leCommVelocity->setText("360");	//Set the value to 360 in the Command Velocity box.
ui->btnEnable->setEnabled(true);	//Enable the Enable button.
ui->btnReset->setEnabled(true);		//Enable the Reset button.
ui->btnJogForward->setEnabled(true);	//Enable the Jog Forward button.
ui->btnJogBackward->setEnabled(true);	//Enable the Jog Backward button.

ui->btnConnect->setText("Disconnect");	//Change the text of the Connect button to "Disconnect."

statusLabel->setText("EC state: OP");	//Change the text of the Status label to "EC state: OP."
statusProgress->setMaximum(100);	//Change the maximum step of the progress bar to 100.
statusProgress->setValue(100);		//Set the current value of the progress bar to 100.

Disconnect

As a Disconnect button, the state is connected. We stop the timer, use PowerAxis to disable all the axes, and use Stop to stop the EtherCAT network and KINGSTAR Subsystem. WaitForCommand is used to give some time for PowerAxis and Stop to complete their work. We give PowerAxis 5 seconds and Stop 2 seconds. wkState is set to disconnected to set the button as a Connect. currentIndex is set to -1, which means there is no axis. maSts is restored to its default setting.

The following code is in ksworker.cpp:

case connected:
   dataTimer->stop();

   for (int i = 0; i < maSts.AxesCount; i++)
   {
      KsCommandStatus Command = { 0 };
      Command = WaitForCommand(5, FALSE, PowerAxis(i, FALSE, FALSE, FALSE));
   }

   WaitForCommand(2, FALSE, Stop());
   wkState = disconnected;
   currentIndex = -1;
   maSts = { ecatOffline, ecatOffline, 0, 0, 0, { ecatOffline }, { ecatOffline }, { axisOffline } };
   break;

default:
   break;
}