EtherCAT Application with DataScope

From Neuromeka Wiki
Jump to: navigation, search


Overview

This section contains basic instruction of how to operate DataScope and connect it to EtherCAT application for data logging and visualization. The EtherCAT application in this section was generated by NRMKEtherCAT Configuration Tool. Moreover, we used three Elmo GOLD Whistles for this demonstration.

As mentioned in EtherCAT Tutorials section, the source code hierarchy of EtherCAT project in Eclipse appears as below

  • Real-time Control Code
    • main.cpp
  • TCP Socket Implementation
    • EcatDataSocket.h
    • EcatDataSocket.cpp
    • EcatControlSocket.h
  • EtherCAT System Interface
    • SystemInterface_EtherCAT_ElmoDIO.h
  • PDO Configuration Files
    • Elmo_GCON_PDO.h + Elmo_GCON_PDO.c

In this section, we focus on the Data Socket implementation and DataScope-related code snippet in Main.

Data Socket in use - Listening and Transferring Data

Listening to Socket Client

As shown in below code snippet, an instant of DataSocket class must be created to handle all communication with DataScope. Moreover, this socket acts as TCP server socket and will wait for connection from client. As a result, in main() function, we start listening at port of NRMK_PORT_DATA

  • SOCK_TCP defines socket type as TCP (in this moment, UDP socket has not supported yet).
  • NRMK_PORT_DATA is set to 3001. You may change if you want.

Then, we make the socket wait for connection via waitForConnection() function.


#include "EcatDataSocket.h"

// NRMKDataSocket for plotting axes data in Data Scope
EcatDataSocket datasocket;

............................................................................................

int main(int argc, char **argv)
{
	....................................................................................

	// TO DO: Create data socket server
	datasocket.setPeriod(period);

	if (datasocket.startServer(SOCK_TCP, NRMK_PORT_DATA))
		printf("Data server started at IP of : %s on Port: %d\n", datasocket.getAddress(), NRMK_PORT_DATA);

	printf("Waiting for Data Scope to connect...\n");
	datasocket.waitForConnection(0);

	....................................................................................
}


Transferring data via Data Socket

Fundamentally, all communication work is put inside a task named plot_task. Therefore, in main(), we create the plot_task and register plot_run() function as its implementation.

In plot_run, at every cycle, we check whether connection is valid so that we can update the socket's buffer with latest data. As shown in the code snippet, control time (gt) and data including Actual Position and Actual Velocity will be sent to DataScope.

............................................................................................
RT_TASK plot_task;
static int step_data = 10;
volatile int tick_data = 0;

void plot_run(void *arg)
{
	rt_task_set_periodic(NULL, TM_NOW, 1e6);	// period = 1 (msec)

	while (1)
	{
		/// TO DO: You have to prepare data for NRMKDataSocket
		if (datasocket.hasConnection() && tick_data-- == 0)
		{
			tick_data = step_data - 1; // 'minus one' is necessary for intended operation
			datasocket.updateControlData(ActualPos, ActualVel);
			datasocket.update(gt);
		}

		rt_task_wait_period(NULL);
	}
}


int main()
{
	....................................................................................
	// plotting: data socket comm
	rt_task_create(&plot_task, "plotting", 0, 80, 0);
	rt_task_start(&plot_task, &plot_run, NULL);
	....................................................................................
}

Data Socket Implementation - Update() methods and Data Format

All implementation was done in EcatDataSocket class represented by EcatDataSocket.h and EcatDataSocket.cpp.

Update() methods

From the above section, we know that data is sent to Data Socket via updating methods (updateControlData() and update(time)).

While update(time) is a static function in Neuromeka's library, the other one can be absolutely changed by user if needed.

The below code shows two updating functions: updateControlData() and _updatePacketData(). For detail:

  • updateControlData() which is called from outside, replaces buffers of q and qdot with Actual Position and Actual Velocity given by user.
  • _updatePacketData() which is called by internal library, copies the values of q and qdot into the sending buffer.
    • Note that only float data is allowed for valid plot in Data Scope and NUM_DATA in this case demonstrates number of total data (in float) of q and qdot.
inline EcatDataSocket() : _ECAT_DATA_SOCKET(_dataChannelArray, _sizeDataChannelArray)
{
public:
	void updateControlData(INT32 * Q, INT32 * Qdot)
	{
		for (int i=0; i < NUM_JOINT; i++)
		{
			q[i] = (float) Q[i];
			qdot[i] = (float) Qdot[i];
		}
	}

private:
	
	virtual int _updatePacketData(float * const data)
	{
                // Prepare datas
		for (int k = 0; k < NUM_JOINT; k++)
		{
			data[k] = q[k];
			data[k+NUM_JOINT] = qdot[k];
		}

		// Always return the number of bytes of the updated data packet
		return NUM_DATA;
	}

private:
	float q[NUM_JOINT];
	float qdot[NUM_JOINT];
}

Data Format

Data Format specifies the way the socket sends data to DataScope and how data will be displayed in DataScope. As in the content of EcatDataSocket.cpp shown below, Data Format is defined by two variables: _dataChannelArray[] and _sizeDataChannelArray. Although _sizeDataChannelArray is so important as _dataChannelArray[] for socket to transfer data properly, it is automatically calculated from _dataChannelArray[]. Therefore, you have to focus on _dataChannelArray[] only.

In detail, for clearance, definition of _dataChannelArray[] is separated into multiple line of code.

  • Each line specifies one channel in DataScope.
  • Each line includes channel id, plot style, number of signals and signal id.
  • Please refer to related document if this is first time you use DataScope.
/// TO DO: Fill in the number of data and the data channel index array
const unsigned int EcatDataSocket::_dataChannelArray[]
// The following code generates two (or three) channels charts (all of time chart style),
// where each chart plots the signals (as many as the joint dof).
//
// This is the example to plot the joint angles in the first channel, the joint velocities in the second channel,
// the joint torque (in controlled simulation) in the third channel for a seven dof manipulator.
	= {
		NRMK_SCOKET_PACKET_DATA_CH(0), NRMK_SOCKET_PACKET_DATA_STYLE_TIME_CHART, NUM_JOINT, 0, 1, 2, 

		NRMK_SCOKET_PACKET_DATA_CH(1), NRMK_SOCKET_PACKET_DATA_STYLE_TIME_CHART, NUM_JOINT, 3, 4, 5, 
		
	};

// TO DO: Set the actual size of the data channel format array defined above
const unsigned int EcatDataSocket::_sizeDataChannelArray = sizeof(_dataChannelArray)/sizeof(unsigned int);

EtherCAT Application with DataScope - Demonstration