I am trying to read an ADS datastream from a TwinCAT3 Project.
The function I wrote should read the datastream whenever the CycleCount (coming from the SPS) changes its value - so CycleCount is the trigger for the callback function and is checked for a change every millisecond.
The datastream to be read consists of a structure containing the two values "nCycleCount" (DWORD-4Bytes) and "TStamp" (ULINT-8Bytes). Therefore the whole stream is containing 12 bytes of data.
One cycle in TwinCAT is configured as 0.5ms, so the variable CycleCount should change 2 times per second (if the PLC-tasks cycle time is one cycle-tick). As my program is checking every millisecond if the variable CycleCount changed, the callback function should be called every millisecond and write the timestamp to a Buffer ("myBuffer"). But I noticed that for a runtime of 2 seconds I only receive 1000 values (instead of 2000 expected) and I can't find the reason why?
The PLC task in TwinCAT3 seems to show the correct values, but when reading them with MatLab the timestamp values are incorrect and not every millisecond as stated before:
These are some outputs from Matlab where the CycleCounter is written to column 1 and timestamp is written to column 2:
I use the following Codes in TwinCAT to define the structure and Main-Program:
Structure:
TYPE ST_CC :
STRUCT
nCycleCount : DWORD; //4Bytes
TStamp : ULINT; //8Bytes
//Stream with 12Bytes total
END_STRUCT
END_TYPE
MAIN_CC (for PlcTask):
PROGRAM MAIN_CC
VAR
CC_struct : ST_CC;
END_VAR;
CC_struct.nCycleCount := _TaskInfo[1].CycleCount;
CC_struct.TStamp := IO_Mapping.ulint_i_TimeStamp;
Matlab Code to read stream on Notification:
function ReadTwinCAT()
%% Import Ads.dll
AdsAssembly = NET.addAssembly('D:\TwinCat3\AdsApi\.NET\v4.0.30319\TwinCAT.Ads.dll');
import TwinCAT.Ads.*;
%% Create TcAdsClient instance
tcClient = TcAdsClient;
%% Connect to ADS port 851 on the local machine
tcClient.Connect(851);
%% ADS Device Notifications variables
% ADS stream
dataStream = AdsStream(12); %12Bytes necessary
% reader
binRead = AdsBinaryReader(dataStream);
% Variable to trigger notification
CCount = 'MAIN_CC.CC_struct.nCycleCount';
%% Create unique variable handles for structure
try
st_handle = tcClient.CreateVariableHandle('MAIN_CC.CC_struct');
catch err
tcClient.Dispose();
msgbox(err.message,'Fehler beim Erstellen des Variablenhandles','error');
error(err.message);
end
%% Create buffer for values
myBuffer = {};
MAXBUFFLEN = 1000;
%% Register ADS Device
try
% Register callback function
tcClient.addlistener('AdsNotification',@OnNotification);
% Register notifications
% %AddDeviceNotification( variableName As String,
% dataStream As AdsStream,
% offset As Integer,
% length As Integer (in Byte),
% transMode As AdsTransMode,
% cycleTime As Integer,
% maxDelay As Integer,
% userData As Object)
% Notification handle
hConnect = tcClient.AddDeviceNotification(CCount,dataStream,0,4,AdsTransMode.OnChange,1,0,CCount);
% Listen to ADS notifications for x seconds
pause(2);
catch err
msgbox(err.message,'Error reading array via ADS','error');
disp(['Error registering ADS notifications: ' err.message]);
end
%% Delete ADS notifications
for idx=1:length(hConnect)
tcClient.DeleteDeviceNotification(hConnect(idx));
end
%% Dispose ADS client
tcClient.Dispose();
%% MatlabAdsSample_Notification: OnNotification
function OnNotification(sender, e)
e.DataStream.Position = e.Offset; %Startposition = 0
%% load variables from workspace
hConnect = evalin('caller','hConnect');
binRead = evalin('caller','binRead');
%% assign to ADS variable and convert to string
if( e.NotificationHandle == hConnect )
%% Read timestamp and encodervalues & append to Buffer
tcClient.Read(st_handle, dataStream); %Read structure from stream
%nCycleCount
nCycleCount = binRead.ReadInt32;
[bufflen, ~] = size(myBuffer); %Get current buffer length
myBuffer{bufflen+1,1} = nCycleCount;
%Read & Append Timestamp to Buffer
tstamp = binRead.ReadInt64; %Read tstamp from dataStream and shift reading position by 8bytes (int64)
myBuffer{bufflen+1,2} = tstamp;
if bufflen < MAXBUFFLEN-1
return;
else
assignin('base','myBuffer', myBuffer);
disp("buffer assigned in workspace")
myBuffer = {}; %empty Buffer
end
else
%do nothing
end
end
Hope you can help me with my problems - thanks in advance!
I found a solution which seems to work as a 12hour test with 43million datasets was successful.
The way I do it now is appending my structure (containing the values to read) to an array of structs with a size of 10.000. As soon as the array is full, my notification variable triggers the callback function to read the whole array (1.000 * 40 bytes).
But this only seems to work with arrays of a big size. When using smaller arrays with a size of 100 or 1.000 I noticed that there is a higher chance of faulty values caused by incorrect reading.
Structure:
MAIN:
MATLAB