首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >TwinCAT3 -使用Matlab读取ADS数据时时间戳的错误值

TwinCAT3 -使用Matlab读取ADS数据时时间戳的错误值
EN

Stack Overflow用户
提问于 2020-03-26 15:42:36
回答 2查看 790关注 0票数 1

我正在尝试阅读一个来自TwinCAT3项目的广告数据。

当CycleCount (来自SPS)更改其值时,我编写的函数应该读取数据存储,因此CycleCount是回调函数的触发器,并且每毫秒检查一次更改。

要读取的数据区域由一个包含两个值"nCycleCount“(DWORD-4 8Bytes)和"TStamp”(ULINT-8 8Bytes)的结构组成。因此,整个流包含12字节的数据。

TwinCAT中的一个周期被配置为0.5ms,因此变量CycleCount应该每秒更改2次(如果PLC-任务周期时间是一个循环-滴答)。由于我的程序正在检查变量CycleCount是否发生变化,因此应该每毫秒调用回调函数,并将时间戳写入缓冲区("myBuffer")。但我注意到,在2秒的运行时,我只接收到1000个值(而不是预期的2000值),我无法找到原因?

TwinCAT3中的可编程控制器任务似乎显示了正确的值,但是当用MatLab读取它们时,时间戳值是不正确的,而且不是像前面所说的每毫秒一毫秒:

这些是Matlab的一些输出,其中CycleCounter写到列1,时间戳写到列2:

我在TwinCAT中使用了以下代码来定义结构和主程序:

结构:

代码语言:javascript
复制
   TYPE ST_CC :
   STRUCT
    nCycleCount       : DWORD;              //4Bytes
    TStamp            : ULINT;              //8Bytes
                                            //Stream with 12Bytes total     
   END_STRUCT
   END_TYPE

MAIN_CC ( PlcTask):

代码语言:javascript
复制
   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代码读取通知流:

代码语言:javascript
复制
    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

希望你能帮我解决我的问题-谢谢提前!

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-04-02 09:51:09

我发现了一种解决方案,这个解决方案看起来像对4300万个数据集进行12小时测试一样成功。

我现在的做法是将我的结构(包含要读取的值)附加到一个大小为10.000的结构数组中。一旦数组满了,我的通知变量就会触发回调函数来读取整个数组(1.000 * 40字节)。

但这似乎只适用于大小较大的数组。当使用大小为100或1.000的较小数组时,我注意到错误读取导致错误值的可能性更大。

结构:

代码语言:javascript
复制
TYPE ST_ENC :
  STRUCT    
    TStamp            : ULINT;              //8Bytes
    EncRAx1           : DINT;               //4Bytes
    EncRAx2           : DINT;               //4Bytes    
    EncRAx3           : DINT;               //4Bytes
    EncRAx4           : DINT;               //4Bytes
    EncRAx5           : DINT;               //4Bytes
    EncRAx6           : DINT;               //4Bytes
    EncEAx1           : DINT;               //4Bytes
    EncEAx2           : DINT;               //4Bytes
  END_STRUCT
END_TYPE

MAIN:

代码语言:javascript
复制
PROGRAM MAIN_Array
VAR
   encVal : ST_ENC; //Structure of encoder values and timestamp
   arr2write : ARRAY [0..9999] OF ST_ENC; //array of structure to write to
   arr2read  : ARRAY [0..9999] OF ST_ENC; //array of structure to read from
   ARR_SIZE : INT := 9999;
   counter : INT := 0; //Counter for arraysize
END_VAR;

代码语言:javascript
复制
// --Timestamp & Encoderwerte
encVal.TStamp   :=  IO_Mapping.ulint_i_TimeStamp; 
encVal.EncRAx1  :=  IO_Mapping.dint_i_EncoderValue_RAx1; 
encVal.EncRAx2  :=  IO_Mapping.dint_i_EncoderValue_RAx2;
encVal.EncRAx3  :=  IO_Mapping.dint_i_EncoderValue_RAx3;
encVal.EncRAx4  :=  IO_Mapping.dint_i_EncoderValue_RAx4;
encVal.EncRAx5  :=  IO_Mapping.dint_i_EncoderValue_RAx5;
encVal.EncRAx6  :=  IO_Mapping.dint_i_EncoderValue_RAx6;
encVal.EncEAx1  :=  IO_Mapping.dint_i_EncoderValue_EAx1;
encVal.EncEAx2  :=  IO_Mapping.dint_i_EncoderValue_EAx2;

//Append to array 
IF counter < ARR_SIZE
THEN
    arr2write[counter] := encVal;
    counter := counter + 1;
ELSE
    arr2write[ARR_SIZE] := encVal; //Write last Bufferentry - otherwise 1 cycle of struct missing   
    arr2read := arr2write;  
    counter := 0;
END_IF

MATLAB

代码语言:javascript
复制
function ReadTwinCAT() 

   %% Import Ads.dll
   AdsAssembly = NET.addAssembly('D:\TwinCat3\AdsApi\.NET\v4.0.30319\TwinCAT.Ads.dll');
   import TwinCAT.Ads.*;

   %% Initialize POOL
   pool = gcp();
   disp("Worker pool for parallel computing initalized");

   %% Create TcAdsClient instance
   tcClient = TcAdsClient;

   %% Connect to ADS port 851 on the local machine
   tcClient.Connect(851);

   %% ADS Device Notifications variables
   % ADS stream
   ARR_SIZE = 10000; %Groesse des auszulesenden Arrays of Struct
   STREAM_SIZE = 40; %in Byte 

   dataStream = AdsStream(ARR_SIZE * STREAM_SIZE); %40Bytes per entry

   % Binary reader
   binRead = AdsBinaryReader(dataStream);

   % Variable to trigger notification
   arr2read = 'MAIN_Array.arr2read[0].TStamp'; %Notification handle = first TStamp entry

   %% Create unique variable handles for encoder-array
   try
       arr_handle = tcClient.CreateVariableHandle('MAIN_Array.arr2read');
   catch err
       tcClient.Dispose();
       msgbox(err.message,'Fehler beim Erstellen des Variablenhandles','error');
       error(err.message);
   end

   %% Create buffer for values
   myBuffer = {}; %Creates empty buffer
   buffcount = 0; %Nur fuer Workspace-Ausgabe

   %% Register ADS Device
   try   
       % Register callback function
       tcClient.addlistener('AdsNotification',@OnNotification);

       % Notification handle
       hConnect = tcClient.AddDeviceNotification(arr2read,dataStream,0,8,AdsTransMode.OnChange,1,0,arr2read);

       % Listen to ADS notifications for x seconds
       pause(15);

   catch err
       msgbox(err.message,'Error reading array via ADS','error');
       disp(['Error registering ADS notifications: ' err.message]);
   end

   %% Delete ADS notifications
   tcClient.DeleteDeviceNotification(hConnect);

   %% Dispose ADS client
   tcClient.Dispose();

   %% MatlabAdsSample_Notification: OnNotification
   function OnNotification(sender, e)

       e.DataStream.Position = e.Offset; 

       %% 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(arr_handle, dataStream);   %Read structure from stream

           for idx=1:ARR_SIZE               

               %Read & Append Timestamp to Buffer
               [bufflen, ~] = size(myBuffer);           %Get current buffer length
               tstamp = binRead.ReadUInt64;             %Read tstamp from dataStream and shift reading position by 8bytes (int64)        
               myBuffer{bufflen+1,1} = tstamp; 

               %Read & Append Encodervalues to Buffer
               for n=1:8
                   encval = binRead.ReadInt32;             %Read tstamp from dataStream and shift reading position by 8bytes (int64)        
                   myBuffer{bufflen+1,n+1} = encval; 
               end

           end

           Assign arraybuffer
           buffname = 'myBuffer';
           buffcount = buffcount+1;
           buffcount_str = num2str(buffcount);
           assignin('base',strcat(buffname, buffcount_str), myBuffer);
           myBuffer = {}; %empty Buffer for next array
           disp("buffer assigned")         

       else
           %do nothing
       end
   end
end
票数 0
EN

Stack Overflow用户

发布于 2020-03-26 20:59:02

据我所知,你的节目是正确的。

1)

因为通知是异步的,所以它们可能会在等待时间为over.At之后到达,尽管您已经释放了通知。

为了检验这个理论是否正确,在你的Twincat程序中添加一个计时器。

声明:

代码语言:javascript
复制
fbTimer : TON;

执行情况:

代码语言:javascript
复制
fbTimer(IN:=TRUE,PT:=T#2s);
IF NOT fbTimer.Q
THEN
 cc_struct.nCycleCount := _TaskInfo[1].CycleCount;
END_IF

确保在启动plc之前已经启动了matlab程序,并将Matlab中的暂停时间提高到120 s。

如果您获得了2000的值,那么您就知道问题来自于通信的异步性质。

2)

转换错误来自于ReadInt64方法,该方法:

从当前流中读取一个8字节有符号整数,并将流的当前位置提升8个字节。

https://learn.microsoft.com/en-us/dotnet/api/system.io.binaryreader.readint64?redirectedfrom=MSDN&view=netframework-4.8#System_IO_BinaryReader_ReadInt64

您应该使用ReadUInt64代替。

为了看我是否能复制您的相同行为,我创建了一个小型c#测试程序。测试程序正常运行,我能够收到正确数量的通知。

这里的ST代码:

声明:

代码语言:javascript
复制
PROGRAM MAIN
VAR
    fbTimer: TON;
    nCycleCount : DWORD;
END_VAR

执行情况:

代码语言:javascript
复制
fbTimer(IN:=TRUE,PT:=T#2S);
IF NOT fbTimer.Q
THEN
 nCycleCount := _TaskInfo[1].CycleCount;
END_IF

这里是C#代码:

代码语言:javascript
复制
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TwinCAT.Ads;

namespace AdsNotificationTest
{
    class Program
    {
        static TcAdsClient tcClient;
        static int hConnect;
        static AdsStream dataStream;
        static BinaryReader binReader;
        static uint uVal, huValHandle;
        static int counter = 0;

        static void Main(string[] args)
        {
            tcClient = new TcAdsClient();
            dataStream = new AdsStream(31);

            binReader = new BinaryReader(dataStream, System.Text.Encoding.ASCII);
            tcClient.Connect(851);
            try
            {
                hConnect = tcClient.AddDeviceNotification("MAIN.nCycleCount", dataStream, 0, 4, AdsTransMode.OnChange, 1, 0, huValHandle);
                tcClient.AdsNotification += new AdsNotificationEventHandler(OnNotification);
            }
            catch (Exception err)
            {
                Console.WriteLine("Exception.");
            }

            Console.ReadKey();

            tcClient.DeleteDeviceNotification(hConnect);
            tcClient.Dispose();

        }

        private static void OnNotification(object sender, AdsNotificationEventArgs e)
        {

            if (e.NotificationHandle == hConnect)
            {
                counter += 1;
                uVal = binReader.ReadUInt32();
                Console.WriteLine(counter + ": " + uVal);
            }


        }
    }
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60870945

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档