Good morning and welcome back to my Tutorial.In this topic I will talk a little bit more about “Container”.Container is a very important concept in Tc3_Vision – you need to use it in any vision function, for example barcode or qr code reading, object detection.Let’s start!
Last Post:
Beckhoff#Using TwinCAT TF7000 to read a QRCode | (soup01.com)
Functions
Let’s Explain the Tc3_Vision Functions concept before the Container.All the Tc3_Vision function are started with F_VN_XX and the format is like this:
hr
The result of your function.
F_VN_
All the Tc3_Vision functions are started with F_VN.
Parameter<..>
You would fill-in all the parameters while the function is called.There are 3 types of parameters:
- Images or Container
- algoritim settings
- return Values
Expert Functions
Sometimes you may see the Functions are ended with F_VN_xxxExp. It is the short-name of Expert and These functions are the Advance version – you need to pass more parameters and can return more information about the image or process more operations to the images.
Define the Container
OK, time to start with the Container.Let’s Define a new container.
Please imagine this container is a box that you can put anything inside, and the size is dynamic.you only need to be careful the element inside the container – they need to be the same type.
After you defined the Container and login to the runtime, the value of Container is 16#0.
F_VN_CreateContainer
Now we can use F_VN_CreateContainer to create the container. The Container is still empty now because we just defined it in the VAR Area. F_VN_CreateContainer () lets you define your “Box”, how many elements, what is their type,etc.
The parameters that pass inside the function – is just the specification of your container.
You can see ipContainer is containing some value like 16#xxxx.
This value is not meaningful and just remember if its value is zero, the Container is “Empty” and does not reference anything.
Take a look at what Container that we created.
ContainerType_Vector_REAL – we defined the element type as Real.
nElementNum=10 – 10 elements are defined defaultly.
Function Block
VAR_INPUT
ipContainer | ITcVnContainer | Returns the created container |
nTypeGid | GUID | Type GUID of the container to be created |
nElementNum | ULINT | Number of elements |
hrPrev | HRESULT | the result of previous operations |
Return Value
F_VN_CreateContainer | HRESULT | the result of operations |
Code
//Create a Container IF bcreateContiner THEN hr:=F_VN_CreateContainer( ipContainer:=ipContainer ,nTypeGuid:=ContainerType_Vector_REAL ,nElementNum:=10 ,hrPrev:=hr ); IF SUCCEEDED(hr) THEN bcreateContiner:=FALSE; END_IF END_IF |
Adding Element
This part I will explain how to operate the element inside the Container.Their operations are very like arrays.
F_VN_AppendToContainer_REAL
This function can be used to append the elements at the end of your Container.
The element with a default value 1.23 is appended at the end of your Container.
Now your Container had 11 elements.
Function Block
VAR_INPUT
fElement | Reference To REAL | Single element to append to ipContainer |
ipContainer | ITcVnContainer | Container to which the element will be appended |
hrPrev | HRESULT | the result of previous operation |
Return Value
F_VN_AppendToContainer_REAL | HRESULT | the result of operation |
F_VN_InsertIntoContainer_REAL
This Function is used to insert the element in any position that you like, depending on the index parameter.The element that is named FrealTest is inserted at the position 0 of your Container.Now your Container has 12 elements.
Function Block
VAR_INPUT
fElement | Reference To REAL | Single element to insert into ipContainer |
ipContainer | ITcVnContainer | Container in which to insert the element |
nIndex | ULINT | Position, before which the element will be inserted |
hrPrev | HRESULT | the result of previous operations |
Return Value
F_VN_InsertIntoContainer_REAL | HRESULT | the result of operations |
Code
IF bAddElement AND ipContainer <>0 THEN hr := F_VN_AppendToContainer_REAL(1.23, ipContainer, hr); hr := F_VN_AppendToContainer_REAL(2.23, ipContainer, hr); hr := F_VN_InsertIntoContainer_REAL(FrealTest, ipContainer, 0, hr); IF SUCCEEDED(hr) THEN ii:=ii+1; END_IF bAddElement:=FALSE; END_IF |
Accessing Element
Tc3_Vision provides functions that start with F_VN_GetAT_XX and F_VN_SetAt_XX to let you access the element inside the Container.
XX is the Data type of the elements.
Becarefully index is started from 0 and the data type is changed depending on your Container.
F_VN_GetAt_REAL
Function F_VN_GetAt_REAL allows you to get the value of Element, depending on the nIndex parameter.In the following example you are accessing the Element of Index5.
Function Block
VAR_INPUT
ipContainer | ITcVnContainer | Container with REAL elements |
fElement | Reference To REAL | Returns the element at the specified index |
nIndex | ULINT | Index of the requested element |
hrPrev | HRESULT | the result of previous operations |
Return Value
F_VN_GetAt_REAL | HRESULT | the result of operations |
F_VN_SetAt_REAL
Function F_VN_GetAt_REAL allows you to get the value of Element, depending on the nIndex parameter.In the following example you are accessing the Element of Index3.
Function Block
VAR_INPUT
fElement | Reference To REAL | Element to set at the specified container position |
ipContainer | ITcVnContainer | Container with REAL elements, in which the element at positionnIndex is replaced by fElement |
nIndex | ULINT | Index |
hrPrev | HRESULT | the result of previous operations |
Return Value
F_VN_SetAt_REAL | HRESULT | the result of operations |
Code
IF ipContainer <> 0 THEN hr:=F_VN_GetAt_REAL( ipContainer:=ipContainer ,fElement:=fElement ,nIndex:=myIndex ,hrPrev:=hr ); END_IF IF ipContainer <>0 AND bset3 THEN hr:=F_VN_SetAt_REAL( fElement:=fVarables ,ipContainer:=ipContainer ,nIndex:=3 ,hrPrev:=hr ); bSet3:=FALSE; END_IF |
Looping
In the Previous example, we can access the Single Element in one line.If we would like to loop all Elements inside the Container, an iterator can be used and have better performance.
ITcVnForwardIterator
Offers an interface for a forward iterator.
Methods – TcQueryInterface
It is an interface to get the Query interface of your Element.
HRESULT TcQueryInterface(RITCID iid, PPVOID pipItf )
VAR_INPUT
iid | RITCID | Interface IID |
pipItf | PPVOID | interface pointer |
Return Value
実行に成功したらS_OK(0)や他の正の整数が戻ります。
HRESULT | the result of operations |
Methods – CheckIfEnd
Method to check if the Iterator is end or not.
you can also use F_VN_CheckIfIteratorIsAtEnd().
Methods – Increment
Method the Iterator to loop the next element.
you can also use F_VN_IncrementIterator().
ITcVnAccess_REAL
An Interface to access the Real Data type Elements.
Methods – Get
Get the value.
Methods – Set
Set the Value.
Flow
Here is the flow for the above Sample Flow.
Program
//Get From Loop IF bGet THEN hr := F_VN_GetForwardIterator(ipContainer, ipIterator, hr); index:=0; IF SUCCEEDED(hr) AND ipIterator <> 0 THEN hr := ipIterator.TcQueryInterface(IID_ITcVnAccess_REAL, ADR(ipAccess)); IF SUCCEEDED(hr) AND ipAccess<>0 THEN WHILE SUCCEEDED(hr) AND ipIterator.CheckIfEnd() <>S_OK DO hr:=ipAccess.Get(fValue:=fElement); IF SUCCEEDED(hr) THEN fElement:=fElement+1; hr:=ipAccess.Set(fValue:=fElement); END_IF IF SUCCEEDED(hr) THEN arrReal[index]:=fElement; hr:=ipIterator.Increment(); index:=index+1; END_IF END_WHILE END_IF hr:=FW_SafeRelease(ADR(ipAccess)); END_IF bGet:=FALSE; hr:=FW_SafeRelease(adr(ipIterator)); bGet:=FALSE; END_IF |
+in Python
But if you just read it in Python, we explain the operation in these line:
elements=[4,1,2,4,5,7,8,1,2,66,224,4] for element in elements: #Do you stuff |
Export Container as Array
Now we will Explain how to export the Elements inside Container as Array.
F_VN_ExportContainerSize
Determine the required buffer size in bytes to store all container elements (number_of_Elements *size_per_Element). Only possible for containers with basic elements.
VAR_INPUT
ipContainer | ITcVnContainer | Container with basic elements |
nBufferSize | ULINT | Output parameter containing the required buffer size |
hrPrev | HRESULT | the result of previous operations |
Return Value
F_VN_SetAt_REAL | HRESULT | the result of operations |
F_VN_ExportContainer
Export the container elements into a buffer (e.g. an array). Only possible for containers with basic elements.
VAR_INPUT
ipContainer | ITcVnContainer | Container with basic elements |
pBuffer | PVOID | Buffer to store the container elements |
nBufferSize | ULINT | Size of the buffer memory in bytes |
hrPrev | HRESULT | the result of previous operations |
Return Value
F_VN_SetAt_REAL | HRESULT | the result of operations |
Code
//Export it IF bExport THEN nBufferSize:=0; FOR i:=0 TO 99 DO arrReal[i]:=0.0; END_FOR hr:=F_VN_ExportContainerSize( ipContainer:=ipContainer ,nBufferSize:=nBufferSize ,hrPrev:=hr ); hr:=F_VN_ExportContainer( ipContainer:=ipContainer ,pBuffer:=ADR(arrReal) ,nBufferSize:=nBufferSize ,hrPrev:=hr ); bExport:=FALSE; END_IF |
Container In Container
Finally I will explain an Advanced concept – Container in Container. Let’s go through what we learnt before. There is 1 Container and it works like arrays,with same data type.
What happens If we Configure a Container inside a Container?
You can see the below pictures, the Subcontainers are saved like an array, the Container remains One Data type.
Here is the interesting point.we can see array elements are assigned in each Subcontainer.
And each Subcontainer had Real Elements. The numbers of these Elements are not fixed, you can see Container[0] had 6 elements, Container[4] had 2 elements.
It make sense that each container save the information of One Object, each object has their own information, and the information of each object may have different numbers of Elements.
Here is a flow to explain how my sample code works.
You do not need to 100% follow it and can do it by yourself:)
F_VN_AppendToContainer_ITcVnContainer
Append a single element to a container or concatenate containers (if ipElement has the same typeId as ipContainer).
Function block
VAR_INPUT
ipElement | ITcVnContainer | Single element to append to ipContainer or container withseveral elements to be concatenated |
ipContainer | ITcVnContainer | Container to which the element(s) will be appended |
hrPrev | HRESULT | the result of previous operations |
Return Value
F_VN_AppendToContainer_ITcVnContainer | HRESULT | the result of operations |
F_VN_ReserveContainerMemory
Reserve container memory (call with maximum required number of elements before manually appending elements for better performance)
VAR_INPUT
ipContainer | ITcVnContainer | Container for which to reserve the memory |
nElement | ULINT | Number of elements for which the container should reserve memory |
hrPrev | HRESULT | the result of previous operations |
Return Value
F_VN_AppendToContainer_ITcVnContainer | HRESULT | the result of operations |
Code
IF bSubContainerExample THEN hr := F_VN_CreateContainer(ipContainerBase, ContainerType_Vector_Vector_REAL, 0, hr); hr := F_VN_ReserveContainerMemory(ipContainerBase, TO_ULINT(cNumberOfSubContainers), hr); FOR i:=0 TO (cNumberOfSubContainers-1) DO hr := F_VN_CreateContainer(ipHelper, ContainerType_Vector_REAL, TO_ULINT(aContainerStructure[i]), hr); hr := F_VN_AppendToContainer_ITcVnContainer(ipHelper, ipContainerBase, hr); END_FOR bSubContainerExample:=FALSE; END_IF IF ipContainerBase <> 0 THEN F_VN_GetNumberOfElements( ipContainer:=ipContainerBase ,nNumberOfElements:=nNumberOfElementsInSubContainter ,hrPrev:=hr ); hr := F_VN_GetForwardIterator(ipContainerBase,ipIterator,hr); hr := ipIterator.GetContainer(ADR(ipSubContainer)); IF ipSubContainer <> 0 AND SUCCEEDED(hr) THEN iOpeartionCounter[9]:=iOpeartionCounter[9]+1; hr:=F_VN_GetForwardIterator(ipSubContainer,ipIteratorInSubContainer,hr); IF SUCCEEDED(hr) AND ipIteratorInSubContainer <> 0 THEN iOpeartionCounter[10]:=iOpeartionCounter[10]+1; ipIteratorInSubContainer.GetValueSize((nBufferSize)); ipIteratorInSubContainer.TcQueryInterface( iid:=IID_ITcVnAccess_REAL ,ADR(ipAccessSubContainer) ); IF SUCCEEDED(hr) AND ipAccessSubContainer<>0 THEN iOpeartionCounter[11]:=iOpeartionCounter[11]+1; ipAccessSubContainer.Get(fValue:=fElement); ipAccessSubContainer.Set(fValue:=2); ipAccessSubContainer.Get(fValue:=fElement); END_IF END_IF END_IF END_IF |
Sample Code Download
TwinCAT3/TwinCAT Project_Vision_Container.tnzip at main · soup01Threes/TwinCAT3 (github.com)
Summary
In this tutorial, we used F_VN_AppendToContainer_REAL(),F_VN_InsertIntoContainer_REAL(),F_VN_GetAt_REAL(),F_VN_StAt_REAL() of access the element inside the Container, if we can changed it to らF_VN_AppendToContainer_INT() while the Elements Type is INT.
Because the Container is very flexible and allows you to re-size ,re-use it.
(please remember to release the memory if the pointer is not used any more. )