#string #types #string-concatenation #twincat
#строка #типы #строка-конкатенация #twincat
Вопрос:
Я хочу создать оператор SELECT из результатов запроса в среде ПЛК Twicat3.
Запрос выполняется правильно, когда переменная жестко запрограммирована, но не выполняется, когда переменная передается из результатов другого запроса. Определенные типы данных одинаковы. Ошибка в представлении информационного журнала указывает на то, что «Данный ключ отсутствует в словаре». когда запрос выполняется с использованием результатов первого запроса.
PROGRAM MAIN
VAR
conv_QueryDB : Query_tblConveyorQueue;
conv_queryRes : ARRAY [0..9] OF tblConveyorQueue;
conv_str_query : STRING(2000);
conv_query : WSTRING(2000);
conv_query1 : WSTRING(255) := "SELECT TOP 1 LEFT(AreaID,5) FROM ConveyorQueue WHERE CartonID = '";
conv_barcode : WSTRING(255) := "900981-1";
conv_query2 : WSTRING(255) := "' ORDER BY AreaID";
zone_QueryDB : Query_tblZoneMap;
zone_queryRes : ARRAY [0..18] OF tblZoneMap;
zone_str_query : STRING(2000);
zone_query : WSTRING(2000);
zone_query1 : WSTRING(255) := "SELECT transfer, zone, direction, neighborZone, deliveryLoc FROM tblZoneMap WHERE transfer = 1 AND zone = 0 AND deliveryLoc = '";
zone_dest : WSTRING(255);
zone_query2 : WSTRING(255) := "' ";
END_VAR
conv_query := WCONCAT(conv_query1, WCONCAT(conv_barcode, conv_query2));
conv_str_query := WSTRING_TO_STRING(conv_query);
conv_QueryDB(queryString := conv_str_query, dbConn := 2, bConnect := 1);
conv_queryRes := conv_QueryDB.queryResults;
zone_dest := STRING_TO_WSTRING(conv_queryRes[0].AreaID);
//zone_dest := "P1030"; //This works when uncommented??!!??
zone_query := WCONCAT(zone_query1, WCONCAT(zone_dest, zone_query2));
zone_str_query := WSTRING_TO_STRING(zone_query);
zone_QueryDB(queryString := zone_str_query, dbConn := 1, bConnect := 1);
zone_queryRes := zone_QueryDB.queryResults;
Я бы ожидал, что zone_queryRes будут одинаковыми при выполнении zone_dest в качестве жестко заданного значения по сравнению с когда он извлекается из массива conv_queryRes . Я не уверен, что это проблема с типом данных. При выполнении строки запроса, по-видимому, имеют правильный синтаксис.
—————РЕДАКТИРОВАТЬ——————————————————————
Включая функциональный блок SQL.
FUNCTION_BLOCK Query_tblZoneMap
VAR_INPUT
queryString : STRING(2000); //Select statement
dbConn : UDINT; //Int of connection configured in TF6420
bCONNECT : BOOL; //Rising trigger to connect to db. Need to make this re-fireable
END_VAR
VAR_OUTPUT
queryResults : ARRAY [0..18] OF tblZoneMap;
nError : INT;
END_VAR
VAR
fbSQLDatabase : FB_SQLDatabase(sNetID:='', tTimeout:=T#500MS);
fbSqlCommand : FB_SQLCommand(sNetID := '', tTimeout := T#500MS);
fbSQLResult : FB_SQLResult(sNetID:='', tTimeout:=T#500MS);
nState : INT;
R_TRIG1 : R_TRIG;
END_VAR
(*
Welcome to the QueryDB function block!
This function exists to query a database that is configured using the TF6420 Connectivity module.
When executed, it establishes a connection with the database, creates an instance of the FB_SQLCommand,
executes the instance of FB_SQLCommand, then reads the cached results using FB_SQLResult into the
queryResults array.
When results are received successfully, the database connection is closed.
*)
R_TRIG1(CLK:=bCONNECT);
IF R_TRIG1.Q AND nState = 0 THEN
nState := 1;
END_IF
CASE nState OF
0:(*Idle*)
IF bConnect THEN
bCONNECT := 0;
END_IF
1: // Connect to database
IF fbSqlDatabase.Connect(dbConn) THEN
IF fbSqlDatabase.bError THEN
nState := 255;
ELSE
nState := nState 1;
END_IF
END_IF
2: // Create a command reference
IF fbSqlDatabase.CreateCmd(ADR(fbSqlCommand)) THEN
IF fbSqlDatabase.bError THEN
nState := 255;
ELSE
nState := nState 1;
END_IF
END_IF
3://Execute SELECT Statement FB_SQLCommand.ExecuteDataReturn(pSQLCmd:= , cbSQLCmd:= , pSQLDBResult:= )
IF fbSQLCommand.ExecuteDataReturn(pSQLCmd:= ADR(queryString)
, cbSQLCmd:= SIZEOF(queryString)
, pSQLDBResult:= ADR(fbSqlResult)) THEN
IF fbSQLCommand.bError THEN
nState := 255;
ELSE
nState := nState 1;
END_IF
END_IF
4://Read Results
IF fbSqlResult.Read(nStartIndex:= 0
,nRecordCount:= 19
,pData:= ADR(queryResults)
,cbData:= SIZEOF(queryResults)
,bWithVerifying:= FALSE
,bDataRelease:= FALSE) THEN
IF fbSqlResult.bError THEN
nState := 255;
ELSE
nState := nState 1;
END_IF
END_IF
5:// disconnect from database
IF fbSqlDatabase.Disconnect() THEN
IF fbSqlDatabase.bError THEN
nState := 255;
ELSE
nState := 0;
bConnect := 0;
END_IF
END_IF
255:
nError := 1; // If errored, check SQL logs.
bConnect := 0;
nState := 0;
END_CASE
Ответ №1:
почему вы используете WSTRING в первую очередь? Используйте обычные строки, чтобы вам не приходилось конвертировать туда и обратно, а ваш код был более читаемым.
Если вы объединяете много строк вместе, создайте функцию, которая способна на это. Что-то, что выглядит так, когда вы его вызываете:
F_CONCAT3(
zone_query1,
zone_dest,
zone_query2,
);
Я советую вам использовать соглашение о кодировании Бекхоффа, чтобы другие разработчики могли читать ваш код:
Тогда ваш код может выглядеть следующим образом:
sConvQuery := F_CONCAT3(sConvQuery1, sConvBarcode, sConvQuery2);
//Use Enums instead of numbers
fbQueryDB(queryString := sConvQuery , dbConn := 2, bConnect := TRUE);
aConvQueryRes := fbQueryDB.queryResults;
sZoneDest := aConvQueryRes[0].AreaID;
sZoneQuery := F_CONCAT3(sZoneQuery1,sZoneDest,sZoneQuery2);
fbQueryDB(queryString := sZoneQuery, dbConn := 1, bConnect := TRUE);
aZoneQueryRes := fbQueryDB.queryResults;
Не могли бы вы опубликовать код функционального блока zone_QueryDB?
Комментарии:
1. Спасибо за ответ! Соглашение о коде было задано кем-то другим. У меня нет контроля над этим. Я использовал WSTRING для очистки построения строки запроса и включения одинарных кавычек в построенную строку, например
"SELECT * FROM tblZoneMap WHERE col = 'sVar' "
. Собираюсь отредактировать основной пост, чтобы включить функциональный блок QueryDB.2. Вы можете использовать $’ для одинарных кавычек marks:infosys.beckhoff.de/english.php?content=../content/1033 /…
Ответ №2:
Я также думаю, что вам следует размещать свои запросы в последовательности шагов, чтобы избежать любого конфликта в функциональном блоке Query_tblZoneMap:
CASE nStep OF
eFIRST_QUERY:
sConvQuery := F_CONCAT3(sConvQuery1, sConvBarcode, sConvQuery2);
//Use Enums instead of numbers
fbQueryDB(queryString := sConvQuery , dbConn := 2, bConnect := TRUE);
nStep := eWAIT_FOR_FIRST_RESULT;
eWAIT_FOR_FIRST_RESULT:
IF NOT fbQueryDB.bBusy THEN
aConvQueryRes := fbQueryDB.queryResults;
nStep := eSECOND_QUERY;
END_IF
//Error handling
//ELSIF fbQueryDB.bError ...
eSECOND_QUERY:
sZoneDest := aConvQueryRes[0].AreaID;
sZoneQuery := F_CONCAT3(sZoneQuery1,sZoneDest,sZoneQuery2);
fbQueryDB(queryString := sZoneQuery, dbConn := 1, bConnect := TRUE);
nStep := eWAIT_FOR_SECOND_RESULT;
eWAIT_FOR_SECOND_RESULT:
IF NOT fbQueryDB.bBusy THEN
aZoneQueryRes := fbQueryDB.queryResults;
nStep := eCHECK_RESULT;
END_IF
//Error handling
//ELSIF fbQueryDB.bError ...
eCHECK_RESULT: //...
END_CASE
Комментарии:
1. Это отличный совет, спасибо. Запросы выполняются последовательно. Проблема, с которой я здесь сталкиваюсь, заключается в передаче результатов из одного запроса в другой запрос. Я могу запросить таблицу MSSQL и вернуть строку char (20), например, ‘Zone14’ . Когда я жестко кодирую ‘Zone14’ , второй запрос возвращает желаемые результаты. Когда я передаю результаты из MSSQL в запрос SQLite, он ничего не возвращает. Есть ли у вас какие-либо мысли о том, в чем заключается несоответствие? Рассматриваемая переменная является
zone_dest
.2. Если вы переработали код, как я вам показал, и у вас все еще возникают проблемы, затем переключите точку останова на sZoneQuery := F_CONCAT3(sZoneQuery1,sZoneDest,sZoneQuery2); и взгляните на вторую строку запроса.