====== Типы тегов ====== ===== Формат записи в 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.