CODESYS V3.5 SP14 Patch 2XmlResultFormatter05a4fdda-1086-47b5-ad10-7c2bac568355a429e267-db3b-4872-871e-1ca509b971e2XmlPac();
IF NOT DoneGeneratingXml THEN
xUnitFileComposer.Format(NumberOfTestSuites := NumberOfTestSuitesFinished,
NumberOfTestCases := NumberOfTestCases,
NumberOfSuccessfulTestCases := NumberOfSuccessfulTestCases,
NumberOfFailedTestCases := NumberOfFailedTestCases,
Busy => BusyGeneratingXml);
DoneGeneratingXml := NOT BusyGeneratingXml;
END_IF;917c49f4-3643-42fe-b791-ac2edc0df35b Default file access mode Is buffer Initialised? Buffer : ARRAY [0..GVL_Param_XmlControl.MaxFileSize-1] OF BYTE;
OrI_TestResultFormatter
This function block reports the results from the tests into a xUnit compatible XmlFile
The total number of test suites The total number of test cases (for all test function blocks) The total number of test cases that had all ASSERTS successful The total number of test cases that had at least one ASSERT failed Responsible for formatting the xml file skeleton
<testsuites> => the aggregated result OF all xunit testfiles
<testsuite> => the output from a single TestSuite
<properties> => the defined properties at test execution
<property> => name/value pair for a single property
...
</properties>
<error></error> => optional information, in place of a test case - normally if the tests in the suite could not be found etc.
<testcase> => the results from executing a test method
<system-out> => data written to System.out during the test run
<system-err> => data written to System.err during the test run
<skipped/> => test was skipped
<failure> => test failed
<error> => test encountered an error
</testcase>
...
</testsuite>
...
</testsuites>Busy := TRUE;
NumberOfTestsInCurrentSuite := 3;
IF NumberOfFailedTestCases > 0 THEN
LOGSTR(msgCtrlMask := UDINT_TO_DWORD(CmpLog.LogClass.LOG_INFO),
msgFmtStr := '%s',
strArg := '| ========= Export Xml Report =========');
(* <?xml version="1.0" encoding="UTF-8"?> *)
Xml.writeDocumentHeader(Header := '<?xml version="1.0" encoding="UTF-8"?>');
xml.NewTag('testsuites');
FOR SuiteCounter := 1 TO NumberOfTestSuites BY 1 DO
(* <testsuite> *)
xml.NewTag('testsuite');
(* name="name" *)
//TestSuiteInstancePath := 'TestInstancePath'
TestSuiteName := 'TestSuiteName';
Xml.NewParameter('name', TestSuiteName);
(* <testsuite errors="errors"> *)
//NumberOfFailedTestsInCurrentSuite := UINT_TO_STRING(NumberOfFailedTestCases);
Xml.NewParameter('errors', UINT_TO_STRING(NumberOfFailedTestsInCurrentSuite));
(* <testsuite errors="errors" tests="tests">*)
Xml.NewParameter('tests', UINT_TO_STRING(NumberOfTestsInCurrentSuite));
(* close testsuite *)
Xml.CloseTag();
FOR TestCounter := 1 TO NumberOfTestsInCurrentSuite BY 1 DO
//IF GVL_CfUnit.TestSuiteAddresses[SuiteCounter]^.Tests[TestCounter].IsFailed() THEN
IF TRUE THEN
(* get testcase name *)
//TestCaseName := GVL_CfUnit.TestSuiteAddresses[SuiteCounter]^.Tests[TestCounter].GetName();
TestCaseName := 'TestCaseName';
Xml.NewTag('testcase');
Xml.NewParameter('name', TestCaseName);
(* close testcase *)
Xml.CloseTag();
Xml.NewTag('failure');
//AssertResult := GVL_CfUnit.TestSuiteAddresses[SuiteCounter]^.AssertResults.AssertResults[TestCounter];
//Xml.NewParameter('message', AssertResult.Message );
Xml.NewParameter('message', 'AssertResultMessage' );
Xml.NewTagData('Assertion failed');
(* close failure *)
Xml.CloseTag();
END_IF
END_FOR
END_FOR
(* Close testsuites *)
Xml.CloseTag();
(* Open, save and close the file *)
OpenSaveAndClose();
END_IF
Busy := FALSE; TRUE: the retain variables are initialized (reset warm / reset cold) TRUE: the instance will be copied to the copy code afterward (online change)FB_Init is always available implicitly and it is used primarily for initialization.
The return value is not evaluated. For a specific influence, you can also declare the
methods explicitly and provide additional code there with the standard initialization
code. You can evaluate the return value.// Set Filepath
szFilename := GVL_Param_XmlControl.FilePath;
// Set buffer and flag
Xml.SetBuffer(pString := ADR(Buffer), iSizeOf := SIZEOF(Buffer));
xBufferInitialised := TRUE;// write buffer to a file
File.Open(Filename := szFilename, FileAccessMode := stFileAccessMode);
File.Save(pString := ADR(Buffer), xml.Length );
File.Close();
Xml.ClearBuffer();6597c0b3-0890-4d5d-8d3a-2d0d0b2355db Closes the current fileIF FileHandle <> SysFile.SysTypes.RTS_INVALID_HANDLE THEN
Close := SysFile.SysFileClose(hFile := FileHandle);
END_IF File name can contain an absolute or relative path to the file. Path entries must be separated with a Slash (/) Opens a fileFileHandle := SysFile.SysFileOpen(szFile := Filename,
am := FileAccessMode,
pResult := ADR(Open)); Call with ADR(); Call with SIZEOF(); IF FileHandle <> SysFile.SysTypes.RTS_INVALID_HANDLE THEN
FileSize := SysFile.SysFileread(hFile := FileHandle,
pbyBuffer := pString,
ulSize := Size,
pResult := ADR(Read));
END_IF Call with ADR(); Call with SIZEOF(); Saves the contents of the buffer into a file
Be sure to call Open() before calling Save()IF FileHandle <> SysFile.SysTypes.RTS_INVALID_HANDLE THEN
SysFile.SysFileWrite(hFile := FileHandle,
pbyBuffer := pString,
ulSize := Size,
pResult := ADR(Save));
END_IFc9bec8fc-2de3-45f4-b839-e6c4276a6b5a
This functionblock acts as a stream buffer for use with FB_XmlControl
Clears the buffer and sets the length to 0IF (pStrBuf = 0) OR (udiBufsize = 0) THEN
RETURN;
END_IF
FOR i := 0 TO (udiBufsize-1) DO
pStrBuf[i] := 0;
END_FOR
udiLength := 0;
Copies a string from the character buffer
udiLoop := 0;
pByteCopy := ADR(Copy);
pByteBuffer := pStrBuf + udiStart - 1;
WHILE(udiLoop < SIZEOF(Copy)) AND udiStart - 1 + udiLoop < udiLength AND udiStart + udiLoop < udiEnd DO
pByteCopy^ := pByteBuffer^;
udiLoop := udiLoop + 1;
pByteCopy := ADR(Copy) + udiLoop;
pByteBuffer := pStrBuf + udiStart + udiLoop -1;
END_WHILE;
IF udiLoop = SIZEOF(Copy) THEN
XmlError := E_XmlError.ErrorStringLen;
ELSIF udiStart - 1 + udiLoop = udiLength THEN
XmlError := E_XmlError.ErrorMaxBufferLen;
ELSE
XmlError := E_XmlError.OK;
END_IF;
pByteCopy^ := 0;
udiCopyLen := udiLoop;iLoop := 0;
pByteCut := ADR(CutOff);
pByteBuffer := pStrBuf + udiStartPos - 1;
WHILE pByteBuffer^ <> 0 AND(iLoop < SIZEOF(CutOff)) AND udiStartPos -1 + iLoop < udiLength DO
pByteCut^ := pByteBuffer^;
iLoop := iLoop + 1;
pByteCut := ADR(CutOff) + iLoop;
pByteBuffer := pStrBuf + udiStartPos - 1 + iLoop;
END_WHILE;
IF pByteBuffer^ = 0 THEN
XmlError := E_XmlError.OK;
ELSIF iLoop = SIZEOF(CutOff) THEN
XmlError := E_XmlError.ErrorStringLen;
ELSIF udiStartPos -1 + iLoop = udiLength THEN
XmlError := E_XmlError.ErrorMaxBufferLen;
END_IF;
pByteCut^ := 0;
udiLength := udiStartPos -1;
pByteBuffer := pStrBuf + udiStartPos - 1;
pByteBuffer^ := 0;
udiCutLen := iLoop;
Find a searchstring in the buffer and returns its position.
It's possible to add a preffered startposition within buffer
udiLoop := 0;
iSearch := 0;
pBuffer := pStrBuf + udiStartPos;
pSearch := ADR( sSearchString);
WHILE(pSearch^ <> 0 ) AND udiLoop + udiStartPos < udiLength DO
IF pBuffer^ <> pSearch^ THEN
udiLoop := udiLoop + 1;
pBuffer := pStrBuf + udiStartPos + udiLoop;
pSearch := ADR( sSearchString);
iSearch := 0;
ELSE
iSearch := iSearch +1;
pBuffer := pStrBuf + udiStartPos + udiLoop + iSearch;
pSearch := ADR( sSearchString ) + iSearch;
END_IF;
END_WHILE;
Find := udiLoop + 1 + udiStartPos;udiLoop := 0;
udiSearch := 0;
pBuffer := pStrBuf + udiLength;
pSearch := ADR(sSearchString);
WHILE(pSearch^ <> 0 ) AND udiLoop < udiLength DO
IF pBuffer^ <> pSearch^ THEN
udiLoop := udiLoop + 1;
pBuffer := pStrBuf + udiLength - udiLoop;
pSearch := ADR( sSearchString);
udiSearch := 0;
ELSE
udiSearch := udiSearch + 1;
pBuffer := pStrBuf + udiLength - udiLoop + udiSearch;
pSearch := ADR( sSearchString ) + udiSearch;
END_IF;
END_WHILE;
FindBack := udiLength - udiLoop + 1; Set buffer adress (ADR ...) Set buffer size (SIZEOF ...) Sets the Buffer IF (pbyAdr = 0) OR (udiSize = 0) THEN
Set := FALSE;
RETURN;
END_IF;
udiBufSize := udiSize;
pStrBuf := pbyAdr;
Set := TRUE; Appends a string to the bufferpByteIn := ADR(Append);
pByteBuffer := pStrBuf + udiLength;
WHILE pByteIn^ <> 0 AND (udiLength < udiBufSize ) DO
pByteBuffer^ := pByteIn^;
udiLength := udiLength + 1;
pByteIn := pByteIn + 1;
pByteBuffer := pByteBuffer + 1;
END_WHILE;
pByteBuffer := pStrBuf + udiLength; (* String End *)
pByteBuffer^ := 0; (* null terminated string *)
Read current BuffersizeBufferSize := udiBufSize;Length := udiLength;udiLength := Length; Prepends a string to the buffer//save current buffer to temporary buffer
pByteBuffer := pStrBuf;
FOR i := 0 TO (udiBufsize-1) DO
TempBuf[i] := pByteBuffer[i];
END_FOR
pByteIn := ADR(Prepend);
pByteBuffer := pStrBuf;
WHILE pByteIn^ <> 0 AND (udiLength < udiBufSize ) DO
pByteBuffer^ := pByteIn^;
udiLength := udiLength + 1;
pByteIn := pByteIn + 1;
pByteBuffer := pByteBuffer + 1;
END_WHILE;
pTempBuf := ADR(TempBuf);
WHILE pTempBuf^ <> 0 AND (udiLength < udiBufSize ) DO
pByteBuffer^ := pTempBuf^;
pTempBuf := pTempBuf + 1;
pByteBuffer := pByteBuffer + 1;
END_WHILE;
pByteBuffer := pStrBuf + udiLength; (* String End *)
pByteBuffer^ := 0; (* nul terminated string *)
fbccbed5-94ee-455b-a9d5-e8b750ed732e
Organizes parsing and composing of XML data. Data can be treated as STRING or char array.
Filebuffersize is default 4096 bytes and can be set via Gvl_Param_XmlControl
Usage example:
1. Initiate
VAR
XML : FB_XMLControl;
Buffer: STRING(GVL_Param_XmlControl.udiMaxFileSize);
//or
Buffer: ARRAY [0..GVL_Param_XmlControl.udiMaxFileSize] OF BYTE;
END_VAR
XML.pBuffer: = ADR (buffer);
XML.LenBuffer: = SIZEOF (buffer);
Add your own preferred fileheader like so:
XML: <?xml version="1.0" encoding="UTF-8"?>
XML.WriteDocumentHeader( '<?xml version="1.0" encoding="UTF-8"?>');
2. Compose a tag with a parameters:
XML: <MyTag ParaName = "11" />
XML.newTag(sTagName: = 'MyTag');
XML.newPara(sName: = 'ParaName', sPara: = sValue);
XML.CloseTag();
3. Add tag value:
XML: <MyTag> MyText </ MyTag>
XML.newTag(sName := 'MyTag');
XML.newTagData(sTagData :='MyText');
XML.CloseTag();
4. Jump to the beginning of the XML data
XML.toStartBuffer();
5. Add a comment:
XML: <!-- MyComment -->
XML.newComment(sTagName: = 'MyComment');
6. Returns the next tag from the current position in buffer
XML.NextTag();
7. Output the parameter of the tag
XML.NextPara(sPara: = LastValue);
Feedback: sPara returns the value found (string)
Creates a new Tag:
XML: <MyTag>
XML.NewTag(Name: = 'MyTag');
IF xTagOpen THEN
Buffer.Append := TAG_CLOSE;
END_IF;
Buffer.Append := TAG_OPEN;
Buffer.Append := Name;
xTagOpen := TRUE;
TagList.Append := FORWARD_SLASH;
TagList.Append := Name;
Clears the contents of the entire buffer
udiSearchPos := 0;
TagListSeek.Length := 0;
Buffer.Length := 0;
sTagsSeek := '';
sTag := '';
Closes a Tag:
XML: <MyTag />'
Method: XML.CloseTag();
IF xTagOpen THEN
Buffer.Append := END_TAG_CLOSE;
iSelect := TagList.FindBack(sSearchString := FORWARD_SLASH);
CloseTag := TagList.CutOff(udiStartPos:= iSelect);
xTagOpen := FALSE;
ELSE
iSelect := TagList.FindBack(sSearchString := FORWARD_SLASH);
CloseTag := TagList.CutOff(udiStartPos:= iSelect);
Buffer.Append := TAG_OPEN;
Buffer.Append := CloseTag;
Buffer.Append := TAG_CLOSE;
END_IF
Adds a comment;
XML: <!-- MyComment -->
XML.NewComment(Comment: = 'MyComment');
IF xTagOpen THEN
Buffer.Append := TAG_CLOSE;
xTagOpen := FALSE;
END_IF;
Buffer.Append := OPEN_COMMENT;
Buffer.Append := Comment;
Buffer.Append := CLOSE_COMMENT;
Must be called after opening a new tag
XML.NewParameter(Name: = 'ParaName', Value: = 'Value');
Buffer.Append := SPACE;
Buffer.Append := Name;
Buffer.Append := EQUALS;
Buffer.Append := QUOTE;
Buffer.Append := Value;
Buffer.Append := QUOTE;Buffer.Append := TAG_CLOSE;
Buffer.Append := Data;
xTagOpen := FALSE;iSelectStart := Buffer.Find(sSearchString := TAG_OPEN, udiStartPos := udiSearchPos);
iSelectEnd := Buffer.Find(sSearchString := TAG_CLOSE, udiStartPos := iSelectStart);
iSelectStartPara := Buffer.Find(sSearchString := SPACE, udiStartPos := iSelectStart);
iSelectTagClose := Buffer.Find(sSearchString := FORWARD_SLASH, udiStartPos := iSelectStart);
IF iSelectStart < Buffer.Length AND iSelectEnd < Buffer.Length THEN
udiSearchPos := iSelectEnd;
sTag := Buffer.Copy( udiStart:= iSelectStart + 1, udiEnd:= iSelectEnd);
IF iSelectEnd < iSelectStartPara THEN
NextTag := Buffer.Copy( udiStart:= iSelectStart + 1, udiEnd:= iSelectEnd);
ELSE
NextTag := Buffer.Copy( udiStart:= iSelectStart + 1, udiEnd:= iSelectStartPara);
END_IF;
IF iSelectTagClose = iSelectStart + 1 THEN
iSelect := TagListSeek.FindBack(sSearchString := FORWARD_SLASH);
TagListSeek.CutOff(udiStartPos:= iSelect);
ELSIF iSelectTagClose > iSelectStart AND iSelectTagClose < iSelectEnd THEN
TagListSeek.Append := FORWARD_SLASH;
TagListSeek.Append := NextTag;
iSelect := TagListSeek.FindBack(sSearchString := FORWARD_SLASH);
TagListSeek.CutOff(udiStartPos:= iSelect);
ELSE
TagListSeek.Append := FORWARD_SLASH;
TagListSeek.Append := NextTag;
END_IF;
ELSE
udiSearchPos := Buffer.Length;
NextTag := '';
END_IF;
Jump to the beginning of the XML data
XML.ToStartBuffer();
udiSearchPos := 0;
TagListSeek.Length := 0;
sTagsSeek := '';
sTag := '';
Add your own preffered fileheader like:
XML: <?xml version="1.0" encoding="UTF-8"?>
XML.WriteDocumentHeader('<?xml version="1.0" encoding="UTF-8"?>');
//Buffer.Prepend := Concat(Header, CR_LF);
Buffer.Prepend := Header;iSelectEndPara := Buffer.Find(sSearchString := EQUALS, udiStartPos := iSelectStartPara);
IF iSelectStartPara < iSelectEnd AND iSelectEndPara < iSelectEnd THEN
Name := Buffer.Copy( udiStart := iSelectStartPara + 1, udiEnd := iSelectEndPara);
iSelectStartValue := Buffer.Find(sSearchString := QUOTE, udiStartPos := iSelectEndPara);
iSelectEndValue := Buffer.Find(sSearchString := QUOTE, udiStartPos := iSelectStartValue);
Parameter := Buffer.Copy( udiStart := iSelectStartValue + 1, udiEnd := iSelectEndValue);
iSelectStartPara := iSelectEndValue + 1;
ELSE
iSelectEndValue := Buffer.Find(sSearchString := TAG_OPEN, udiStartPos := udiSearchPos);
Name := Buffer.Copy( udiStart := udiSearchPos + 1, udiEnd := iSelectEndValue);
END_IF;
NextParameter := Name;Buffer.Set(pbyAdr := pString, udiSize := iSizeOf);
TagList.Set(pbyAdr := ADR(sTags), udiSize := SIZEOF(sTags));
TagListSeek.Set(pbyAdr := ADR(sTagsSeek), udiSize := SIZEOF(sTagsSeek));
Tag.Set(pbyAdr := ADR(sTag), udiSize := SIZEOF(sTag));Length := Buffer.Length;f48b7de2-5c07-4763-a8be-3d8ab3c1f58c7e465938-43b3-4ac5-bae5-c19fce2bd2bf Maximum filesize in bytes (32 kb default) Filepath for testresult.xml, e.g.: c:/jenkins/workspace/XmlResultTester/testresult.xml69588962-bd3e-4958-957e-7bfb94609af1 The total number of test suites The total number of test cases (for all test function blocks) The total number of test cases that had all ASSERTS successful The total number of test cases that had at least one ASSERT failed Responsible for formatting the xml file skeleton
<testsuites> => the aggregated result OF all xunit testfiles
<testsuite> => the output from a single TestSuite
<properties> => the defined properties at test execution
<property> => name/value pair for a single property
...
</properties>
<error></error> => optional information, in place of a test case - normally if the tests in the suite could not be found etc.
<testcase> => the results from executing a test method
<system-out> => data written to System.out during the test run
<system-err> => data written to System.err during the test run
<skipped/> => test was skipped
<failure> => test failed
<error> => test encountered an error
</testcase>
...
</testsuite>
...
</testsuites>Imported from XML parsing and composing library (xml-pac) 0.3.1.0Imported from XML parsing and composing library (xml-pac) 0.3.1.0