====== Типы тегов ======
===== Формат записи в mtr-файле =====
%EXTERNAL=1E6C
%EXTMAIN=01E3
%EXTREQ=0995
%EXTIRR=12F4
Таблица вшешних тэгов имеет структуру:
^type^size^
|periodic |EXTMAIN|
|requested |EXTREQ|
|irregular |EXTIRR|
===== Irregular tags =====
Все теги в rpsvrtcp и его клиентах имеют свойства:
TWorkRecTagProp = (tpNormal, tpIrregular);
TWorkRecTagFlag = (tfNone, tfInUse, tfUpdated);
Теги типа ''tpIrregular'' запрашиваются из конечного устройства (ПЛК) только тогда, когда этого требует клиент, подключенный к rpsvrtcp. Делается это так.
Пусть клиентом будет приложение visscada. При использовании тега внутри приложения выполняется проверка на тип ''tpIrregular'' и, если да, то тегу устанавливается флаг ''tfInUse'':
// D:\promauto-src\VisScada\Common\TagStorage.pas
function GetTagValue(idx: integer): integer;
begin
result := 0;
if not DataClient.Connected then exit;
if (idx >= 0) and (idx < DataClient.client.TagCount) then begin
if DataClient.client.Tags[idx].TagProp = tpIrregular then
DataClient.client.Tags[idx].TagFlag := tfInUse;
result := DataClient.client.Tags[idx].ValIn;
end else
result := -1;
end;
После этого в самом ближайшем будущем этот флаг будет прочитан модулем, отвечающем за связь с rpsvrtcp, и передан серверу при помощи команды SETFLAG:
// D:\promauto-src\Lib\delphi\rpTcpLib\TcpImportClient.pas
// Передача флага для irregular тегов
if (Tags[i].TagProp = tpIrregular) and (Tags[i].TagFlag <> tfNone) then begin
s := IntToHex(i, 1) + ' ' + IntToHex(integer(Tags[i].TagFlag), 1);
clnt.IOHandler.WriteLn( 'SETFLAG ' + s + ' ' + IntToHex(CRC16($FFFF, s), 1) );
if not GetAnswer('117 !', false, s, c) then raise Exception.Create('');
Tags[i].TagFlag := tfNone;
end;
Комманда SETFLAG принимается сервером rpsvrtcp, после чего флаг соответсвующего тега становится ''tfInUse'':
// D:\promauto-src\Work_shd\rpsvrtcp\sources\TcpExpSvr.pas
procedure TTcpExportSvr.OnCommandSETFLAG(ASender: TIdCommand);
var
i: Integer;
clnt: TTcpExportClient;
s: string;
label
M;
begin
clnt := FindModuleByContext( ASender.Context );
if clnt<>nil then with clnt do begin
ASender.Reply.Text.Text := '?';
if ASender.Params.Count>=3 then begin
s := ASender.Params[0] + ' ' + ASender.Params[1];
if (ASender.Params[2]='****') then goto M;
if CRC16($FFFF, s) = HexToInt(uppercase(ASender.Params[2])) then
M: begin
if SetFlagByNum(
HexToInt(uppercase(ASender.Params[0])),
HexToInt(uppercase(ASender.Params[1]))
)
then
ASender.Reply.Text.Text := '!';
end;
end;
inc(CntRequest);
end;
end;
// D:\promauto-src\Work_shd\rpsvrtcp\sources\ExpSvr.pas
function TCustomExportClient.SetFlagByNum(item_num, value: integer):
Boolean;
begin
result := false;
if (item_num<0) or (item_num >= TagCount) then exit;
Tags[item_num].ptrTag^.TagFlag := TWorkRecTagFlag( value );
result := true;
end;
А в это время в том же rpsvrtcp живет своей жизнью модуль robo - обменивается данными с ПЛК, посылая ему запросы чтения тегов типа ''requested''. При этом он следит и за тегами ''irregular'' - если у тега флаг ''tfInUse'', то посылется запрос на одиночное чтение данного тега.
// D:\promauto-src\Work_shd\rpsvrtcp\sources\Robo.pas
// Прием irrgular тега
if (Tags[i].TagProp = tpIrregular) then begin
if Tags[i].TagFlag = tfUpdated then
Tags[i].TagFlag := tfNone;
if Tags[i].TagFlag = tfInUse then begin
inc(irrCnt);
Tags[i].TagFlag := tfUpdated;
newCmdDollarFlag := cmdDollarSupported;
if (cmdDollarSupported) then begin
TransStr := '$' + Tags[i].Address;
w := GetCRC8(TransStr);
TransStr := TransStr + DecHexB(w and $FF) + #13;
end else
TransStr := '!' + Tags[i].Address + #13;
Attemp := 0;
repeat
sio_sendstring( PortHolder_Port, TransStr);
flag := sio_getstring( PortHolder_Port, s);
if (cmdDollarSupported) then begin
if (not flag) and (s='') then begin
sio_sendstring( PortHolder_Port, '!' + Tags[i].Address + #13);
newCmdDollarFlag := not (sio_getstring( PortHolder_Port, s) and (copy(s,1,1) = '!'));
end else
newCmdDollarFlag := True;
end;
if (flag) and (copy(s,1,1) = '!') then begin
k := pos('=', s);
sum1 := HexDec(copy(s,k+1,length(s)-k));
sum2 := GetCRC8(copy(s,1,k));
if sum1 = sum2 then begin
s := copy(s,2,k-2);
v := HexDec(s);
if Tags[i].ValIn <> v then begin
Tags[i].ValIn := v;
Tags[i].Changed := true;
end;
break;
end;
end;
sleep(50);
sio_flush( PortHolder_Port, 2 );
inc(Attemp);
if Attemp > AttempMax then err := true;
until Attemp > AttempMax;
cmdDollarSupported := newCmdDollarFlag;
end;
end;
При установке значеня тега в ПЛК модуль robo также уделяет irr-тегам немного больше внимания, чем другим: после каждой записи robo сам устанавливает тегу флаг ''tfInUse'', чтобы новое значение обновилось:
// D:\promauto-src\Work_shd\rpsvrtcp\sources\Robo.pas
// Передача тэгов
// writing all tags (per, req, irr)
for i:=0 to TagCount-1 do
begin
// Передача тега
if Tags[i].GotOut then begin
if Tags[i].ValOut > $FFFF then
TransStr := '^'
else
TransStr := '@';
TransStr := TransStr + Tags[i].Address + DecHexW(Tags[i].ValOut and $FFFF);
w:=0;
if UseCRC8 then
w := GetCRC8(TransStr)
else
for k:=1 to 11 do w :=w + ord(TransStr[k]);
TransStr := TransStr + DecHexB(w and $FF) + #13;
Tags[i].GotOut := false;
Attemp := 0;
repeat
sio_sendstring( PortHolder_Port, TransStr);
flag := sio_getstring( PortHolder_Port, s);
if (Tags[i].TagProp = tpIrregular) then
Tags[i].TagFlag := tfInUse;
if UseLogging then
LoggerSaveDayMessage(
inttostr(Attemp) + ': ' +
copy(TransStr,1,13) + '|' + s + ' ' +
iif(copy(TransStr,1,13) = copy(s,2,13), 'ok', 'TAG_WRITE_ERROR') );
if (flag) and (copy(s,1,1) = '!') then
break;
sleep(50);
sio_flush( PortHolder_Port, 2 );
inc(Attemp);
if Attemp > AttempMax then err := true;
until Attemp > AttempMax;
end;
end;
==== Подводный камень ====
Очень редко случается доселе не локализованный баг - после записи в irr-теги ПЛК значения не обновляются в rpsvrtcp, но сами значения уходят в ПЛК нормально. Помогает перезапуск rpsvrtcp.