May 8, 2015

Обмен данными через ADS.API, ST и C#

При работе с ADS.API и.NET необходимо учитывать, что у данных есть длина: причем ПЛК-задачи используют 16-разрядное слово, а C# 32-х. В справочной системе есть краткая таблица соответствия типов. Сделаем ее длинношеее.

STC#Знак.Длина
BYTE, USINTbyte+8 бит
INTshort±16 бит
WORD, UINTushort+16 бит
DINTint±32 бита
DWORD, UDINTuint+32 бита
REALfloat±32 бита
LREALdouble±64 бита

Ничего сложного, для базовых типов нужно учитывать разницу в длине слов. Специфические типы данных DT и TIME требуют особого подхода. STRING и ARRAY - особого подхода с элементами эквилибристики. Работа со структурами STRUCT - тема отдельного разговора.


Дата и время


TIME в языке ST - это промежуток времени (длина), DT - дата и время события (момент). Соответственно в C# для TIME подходит TimeSpan, для DT - DateTime.

TIME хранится в памяти ПЛК как 32-х разрядное, целое, беззнаковое число, содержащее количество миллисекунд:

int htime = client.CreateVariableHandle("MAIN.t");
UInt32 ttime = 0;
ttime = (UInt32)client.ReadAny(htime, ttime.GetType());
TimeSpan span = TimeSpan.FromMilliseconds(ttime);

Я специально использовал тип данных UInt32, чтобы был виден размер данных. По желанию его можно заменить на uint.

С DT немного сложнее, но не сильно - это опять-таки 32-х разрядное, целое, беззнаковое, содержащее количество секунд, прошедшее с 1 января 1970 года:

int hdate = client.CreateVariableHandle("MAIN.td");
int tdate = 0;
tdate = (int)client.ReadAny(hdate, tdate.GetType());
DateTime dt = new DateTime(1970, 1, 1).AddSeconds(tdate);
Console.WriteLine(string.Concat("DT td = ", dt.ToLongDateString(), " / ", dt.ToLongTimeString()));

Я немного расширил пример выводом даты и времени в консоль.


Массивы данных


Сразу отмечу, что строки - это одномерные массивы символов. Нет никакой разницы между работой со строками и массивами.
В старых версиях TwinCAT необходимо было учитывать, что в конце строки стоит символ '\0' и соответственно увеличивать на единицу размер массива под строку. Начиная со второй версии, про нулевой символ и увеличение размера можно забыть: просто передавайте длину строки в символах.
Для чтения и записи массивов используется дополнительный параметр, передающий количество элементов массива. Для одномерного массива - число элементов, для двумерного - число строк, затем число столбцов.

VAR
    str   : STRING      := 'abcdef';
    strT  : T_MaxString := '01234abcdef';
    arr   : ARRAY [1..5] OF WORD;
    arr2D : ARRAY [1..3, 1..5] OF WORD;
END_VAR

string    str   = "";
string    strT  = "";
ushort[]  arr   = new ushort[5];
ushort[,] arr2d = new ushort[3, 5];

str   = (string)   (client.ReadAny(h_str,   str.GetType(),   new int[] { 80   }));
strT  = (string)   (client.ReadAny(h_strT,  strT.GetType(),  new int[] { 255  }));
arr   = (ushort[]) (client.ReadAny(h_arr,   arr.GetType(),   new int[] { 5    }));
arr2d = (ushort[,])(client.ReadAny(h_arr2D, arr2d.GetType(), new int[] { 3, 5 }));


При записи строк - длину указывать необходимо, т. к. можно передавать только часть строки, а вот при записи массивов наоборот - длину передавать не только не нужно, но и нельзя:

client.WriteAny(h_str,   str,   new int[] { str.Length });
client.WriteAny(h_arr,   arr);
client.WriteAny(h_arr2D, arr2d);


Все переменные сразу


Существует возможность прочитать список всех переменных ПЛК-программы: получить исчерпывающую информацию об окружении переменной, ее типе, группе, смещении в памяти, родительской структуре и пр. В терминологии TwinCAT - это называется "Symbol Information". Даже комментарий, оставленный в строке с объявленной переменной, будет доступен через информацию окружения.

// Загружаем информацию о всех доступных переменных
TcAdsSymbolInfoLoader symbolLoader = client.CreateSymbolInfoLoader();
TcAdsSymbolInfoCollection infoCollection = symbolLoader.GetSymbols(true);

foreach (TcAdsSymbolInfo symInfo in infoCollection)
{
    string  name        = symInfo.Name;
    string  shortName   = symInfo.ShortName;
    string  type        = symInfo.Type;
    int     size        = symInfo.Size;
    string  comment     = symInfo.Comment;
    long    iGroup      = symInfo.IndexGroup;
    long    iOffset     = symInfo.IndexOffset;
    
    TcAdsSymbolInfo parentInfo = symInfo.Parent;

    Console.WriteLine(string.Concat(
        name,                   Environment.NewLine,
        shortName,              Environment.NewLine,
        type,                   Environment.NewLine,
        size,                   Environment.NewLine,
        comment,                Environment.NewLine,
        iGroup, ":", iOffset,   Environment.NewLine
        ));
}

Name и ShortName ничем, к сожалению, не отличаются, хотя должны. Поля Parent и xxxSubSymbol (в примере выше его нет) организуют переменные в иерархию.

Подробнее про назначение полей можно прочитать в справочной системе "TcAdsSymbolInfo Members".

No comments

Post a Comment

Note: Only a member of this blog may post a comment.