Различия
Здесь показаны различия между двумя версиями данной страницы.
Предыдущая версия справа и слева Предыдущая версия Следующая версия | Предыдущая версия | ||
doc:rpsvrtcp:irregular [2013/09/09 12:51] denis |
doc:rpsvrtcp:irregular [2013/09/09 13:46] (текущий) denis [Подводный камень] |
||
---|---|---|---|
Строка 16: | Строка 16: | ||
===== Irregular tags ===== | ===== Irregular tags ===== | ||
- | Все теги в rpsvrtcp имеют свойства: | + | Все теги в rpsvrtcp и его клиентах имеют свойства: |
TWorkRecTagProp = (tpNormal, tpIrregular); | TWorkRecTagProp = (tpNormal, tpIrregular); | ||
TWorkRecTagFlag = (tfNone, tfInUse, tfUpdated); | TWorkRecTagFlag = (tfNone, tfInUse, tfUpdated); | ||
- | Теги типа ''tpIrregular'' запрашиваются из конечного устройства (ПЛК) только тогда, когда этого требует клиент, подключенный к rpsvrtcp. | + | Теги типа ''tpIrregular'' запрашиваются из конечного устройства (ПЛК) только тогда, когда этого требует клиент, подключенный к rpsvrtcp. Делается это так. |
- | Запрос от клиента выполняется коммандами: | + | Пусть клиентом будет приложение visscada. При использовании тега внутри приложения выполняется проверка на тип ''tpIrregular'' и, если да, то тегу устанавливается флаг ''tfInUse'': |
- | | + | <code delphi> |
+ | // 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; | ||
+ | </code> | ||
+ | |||
+ | |||
+ | После этого в самом ближайшем будущем этот флаг будет прочитан модулем, отвечающем за связь с rpsvrtcp, и передан серверу при помощи команды SETFLAG: | ||
+ | |||
+ | <code delphi> | ||
+ | // 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; | ||
+ | </code> | ||
+ | |||
+ | Комманда SETFLAG принимается сервером rpsvrtcp, после чего флаг соответсвующего тега становится ''tfInUse'': | ||
+ | |||
+ | <code delphi> | ||
+ | // 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; | ||
+ | </code> | ||
+ | |||
+ | А в это время в том же rpsvrtcp живет своей жизнью модуль robo - обменивается данными с ПЛК, посылая ему запросы чтения тегов типа ''requested''. При этом он следит и за тегами ''irregular'' - если у тега флаг ''tfInUse'', то посылется запрос на одиночное чтение данного тега. | ||
+ | |||
+ | <code delphi> | ||
+ | // 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; | ||
+ | </code> | ||
+ | |||
+ | При установке значеня тега в ПЛК модуль robo также уделяет irr-тегам немного больше внимания, чем другим: после каждой записи robo сам устанавливает тегу флаг ''tfInUse'', чтобы новое значение обновилось: | ||
+ | |||
+ | <code delphi> | ||
+ | // 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; | ||
+ | |||
+ | </code> | ||
+ | |||
+ | ==== Подводный камень ==== | ||
+ | Очень редко случается доселе не локализованный баг - после записи в irr-теги ПЛК значения не обновляются в rpsvrtcp, но сами значения уходят в ПЛК нормально. Помогает перезапуск rpsvrtcp. | ||