Iterating over structures in Twincat PLC (Structured text) - automation

Say I have the following structure that is filled with information about an Axis:
TYPE AxisInfo :
STRUCT
AxisStatus : ARRAY [0..3] OF BYTE;
DriveStatis : ARRAY [0..3] OF BYTE;
FeedRate : ARRAY [0..3] OF BYTE;
Inputs : BYTE;
Outputs : BYTE;
Extra : BYTE;
CurPosW: UDINT;
CurPosX: UDINT;
CurPosY: UDINT;
CurPosZ: UDINT;
CurVelX: UDINT;
CurVelY: UDINT;
CurVelZ: UDINT;
ComPos : UDINT;
SetVel : UDINT;
DacVel : UDINT;
WinchErrPos : UDINT;
XYZErrPos : UDINT;
EnFaults : UDINT;
ActFaults : UDINT;
BpFaults : UDINT;
BpTimeLeft : UDINT;
This Structure holds 82 bytes in total. I will have 8 of these structures running at all time (since I have 8 axis). That amounts to 656 bytes combined with all the structures.
Now, I have a variable called Buffer :
Buffer: ARRAY [0..1023] OF BYTE;
I would like to be able to fill up this buffer with each of the 8 structures, in order. For instance:
Buffer[0] := AxisStatus[0]; //this is for the 1st axis
Buffer[1] := AxisStatus[1]; //this is for the 1st axis
….
Buffer[78] := BpTimeLeft; //this is for the 1st axis
…
Buffer[648] := BpFaults;
Buffer[652] := BpTimeLeft; //this is for the 8th axis
Is there a way in ST on the PLC, to iterate over members of a structure and then place those members into a buffer and making sure they are in proper places? Do you know of any tricks to do this?
I ask this because I can do it in the following method,
For axisIndex:=1 to 8 DO
Buffer[0] := AxisStatus[0];
Buffer[1] := AxisStatus[1];
…
Buffer[78] := BpTimeLeft; this is for the 1st axis
END_FOR
but I have to type out every line for which the buffer needs to get allocated to, and then have to do some trick after I have filled the buffer with the first axis to avoid it overwriting the first 82 bytes. There must be some way to do it automatically in case I change the members of the struct in the future.?

I suggest to use {attribute 'pack_mode' := '1'} in struct declaration and then you can easily copy the data with MEMCPY.
Struct declaration:
{attribute 'pack_mode' := '1'}
TYPE AxisInfo :
STRUCT
Now you can copy the whole struct to array of bytes for example, or just some variables. For example, to copy Inputsfrom your struct to byte array, you could use something like
MEMCPY(ADR(Buffer[2]), ADR(AxisStatus) + 12, 1)
which copies one byte from (address of AxisStatus + 12 bytes, which equals to Inputs) to the buffer[2]. If you copy more than one byte, it would copy them to buffer[2], buffer[3], buffer[4] and so on.
The memcpy is very useful in situations like this.
To copy the whole struct to the begining of the buffer, you can just
MEMCPY(ADR(Buffer), ADR(AxisStatus), SIZEOF(AxisStatus))
To copy the next struct after it
MEMCPY(ADR(Buffer) + SIZEOF(AxisStatus), ADR(AxisStatus), SIZEOF(AxisStatus))

Did you try to use the MEMCPY functions? That should be much less effort...
You can Copy the Struct and the Byte Array in both ways. With some index and offset pointer.

Honestly, I would avoid looping through the structure just to retrieve the status info you need. What if u have 100 axis?
It doesn't scale well.
What if you change the design of your program instead?
You could design a function block (let's call it AxisDevice) that models an axis for example.
AxisDevice will have all the function blocks needed to operate an axis inside it. You will then pass an AXIS_REF to AxisDevice and could retrieve the status info of the axis thanks to a property (ex: getStatus := AxisStatusStruct).
By doing so you would just have to decide and implement once what info about the axis is "public".
All your 8 axis could be of type AxisDevice and provide info at runtime when needed.

You cannot guarantee byte location and element location because the compiler will optimize the space based on the hardware target. You cannot win this battle - you fly against what a structure is all about.
1). You could manually pack your own byte array to guarantee position instead of using a structure (but then you might as well program in machine language because you are defeating high level programming...
BUFFER := ARRAY[0..7] OF AxisInfo;
BUFFER[0] would be axis 0,
BUFFER[1] would be axis 1,
Etc
2). You could define your buffer as an array of your structure and stop accessing specific memory locations (which becomes hardware/platform dependent!)
3). If you are sending the data out to an HMI or some device that doesn't know the structure, but only bytes, then you are stuck manually mapping the structure elements to locations in the byte array. This is the normal solution for fieldbus communications like ModbusTCP.

Related

How to define a polymorphic `Vector` type in Lean 4 as a `Subtype` of `List`?

The List type in Lean 4's prelude has a lot of nice goodies implemented, e.g. List.map, List.join, etc.
A classic example in dependently-typed languages is Vector a n where a is the type of the elements of the container, and n is the length. This allows you to do nice things like write a function concat (u : Vector a m) (v : Vector a n) : Vector a (m + n) whose type signature guarantees that the function returns a vector of the expected length.
Following a comment on a previous answer I'd like to write a Vector type as a Subtype of List, as a first step towards getting these methods for a sized container. But I'm new to Lean, and I don't exactly understand how this works. Subtype takes a predicate a -> Prop, which appears to work a bit like a filter on the parent type, which in this case is a := List. But all lists are valid vectors, so it doesn't seem like this is much use. The predicate should always return true, no?
My question is:
How can I use Subtype to get a subtype of List whose length is encoded in its type? And as a follow-up, how can I construct an element of this type from an existing List?
You're right that all lists are valid vectors, but the predicate is exactly how to specify that the Vector has a certain number of elements.
def Vec (n : Nat) (a : Type) := { ls : List a // ls.length = n }
def myVec : Vec 3 Nat := ⟨[1, 2, 3], rfl⟩

Twincat 3 - SizeOf returning wrong structure size

I have a structure, and am trying to get the size of this structure. SizeOf returns 16, but I am expecting 14 as answer.
2+2+4+2+2+2=14
By using pointers I noticed that there are 2 empty bytes at the end of the structure.
If I replace the UDINT with UINT then the size is correct. If I put the UDINT at the end of the structure, then the two empty bytes are placed after iCrateCnt.
This leads me to believe that the sizeOf is working properly, but for some unknown reason there are two additional bytes placed somewhere in my structure that I am not using.
Why is this happening and how can it be solved?
The unexpected size returned by SIZEOF() are due to so called 'padding bytes'.
Where these padding bytes occur depends on:
The system that is used (Tc2 x86, Tc2 ARM, Tc3)
The data types that are used
The order in which these datatypes (c.q. variables) are defined
For more information about padding bytes see Alignment and Structures
As Kolyur has rightfully mentioned the attribute Pack_Mode can be used to control these padding bytes.
For example in Tc3:
TYPE HMI_POPUPSTRUCT : // The total size of this struct is 8 bytes
STRUCT
bVar1: BOOL; // At byte 0.
// At byte 1 there will be a padding byte
bVar2: INT; // At byte 2 and 3
bVar3: BOOL; // At byte 4
bVar4: BOOL; // At byte 5
bVar5: BOOL; // At byte 6.
// At byte 7 there will be a padding byte (8th byte)
END_STRUCT
When inserting either
{attribute 'pack_mode' := '0'}
or
{attribute 'pack_mode' := '1'}
just above the struct then there won't be any padding bytes resulting in a struct-size of 6 bytes instead of 8.
The pack_mode attribute can be used to eliminate unused bytes in a structure.
https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_plc_intro/2529746059.html&id=3686945105176987925

Using arrays or bytes to set multiple outputs in Structured Text

I am fooling around in TwinCAT3 trying and getting familiar with ST. I now have a simple question.
Say I have 8 LEDS. Each assigned to an output 1-8. Now I want to be able to send in a byte looking like such: 10101010. Lets call that variable to hold that byte setOUTPUTS. Would I initalize setOUTPUTS as follows to hold that?
bsetOUTPUTS := BYTE;
After I initialize that variable, how could I loop through it to set each LED to the corresponding bit?
For instance: setOUTPUTS = 10001000, how would i loop through setOUTPUTS variable to set LED 8 and LED 4 ON , while leaving the others OFF.
IF this is not possible, what is the alternative way using arrays?
Thanks!!
To initialize a byte you would
setOUTPUTS : BYTE:=86; (* equiavlent to 01010101 *)
you can set the outputs based on a bit as follows
out1 := setOUTPUTS.0; (* bit 0 of byte *)
out2:=setOUTPUTS.1; (* bit 1 of byte *)
you might think that you could do something like to loop through the bits in the byte
FOR i:=0 TO 8 BY 1 DO
(* out is an array of outputs *)
out[i] := setOUTPUTS.i;
END_FOR
but unfortunately you are unable to do this. As far as I know setting them individually is the only way to accomplish this.
You can compare each bit of your setOutputs variable with a bit that is shifted through the length of setOutputs:
FOR i := 0 TO 7 DO
out[i] := setOutputs AND SHL(1, i);
END_FOR;
I would simply map the bit of "bsetOUTPUTS" to the IO.
Offset 0 means bit 0, offset 1 means bit 1, etc.
So I will link my channel 1 (LED 1) to bsetOUTPUTS offset 0, channel 2 to bsetOUTPUTS offset 1, etc.

How does labview distinguish between array size info and the array data?

This isn't really a question about how to do something, more just to satisfy my curiosity.
According to this, Labview stores arrays in memory as a series of int32s describing the size of each dimension followed by the actual data. So, e.g., a 2-d array of size 3x5 would be stored as
0: 3
4: 5
8: data starts here
Now suppose you have an array of int32s. How would labview tell the difference between the actual data and the array size information? In the example above, for instance, how does labview know it's a 3x5 array and not a 1-d array of length 3 and then just ignore the remaining elements? Sorry if there is something obvious that I am missing.
If you look at the LabVIEW KB Article How LabVIEW stores data in memory, you'll see that every data-type is stored with type information. For an array it first stores an I32 for each dimension, followed by the flattenend data.
The actual data-type is stored in it's type-descriptor, it consists of a list of the different contained type descriptors. For an array the minimum is two:
The array
The data in the array
The array's type descriptor is
<nn> xx40 <k> <k dims> <k elems> <element type descriptor>
where nn is the total data-packet size
xx40 is the array datatype
k is the total number of dimensions
For the contained I32 the type descriptor is:
0004 xx03 xx
0004 is the length of the type descriptor
03 is the I32 type identifier
However it's has been changed between LabVIEW 7 and 8. Relying on the type descriptor is something you shouldn't mess with yourself. Let LabVIEW handle this.
When references to data are passed around in LabVIEW internally, the data type is always passed around, too. Data is passed around as void pointers and the type is passed along with them. So any time LabVIEW sees your array, it'll also see that the type is a 2d array of int32s. (I work on the LabVIEW team at National Instruments)

Memcpy and Memset on structures of Short Type in C

I have a query about using memset and memcopy on structures and their reliablity. For eg:
I have a code looks like this
typedef struct
{
short a[10];
short b[10];
}tDataStruct;
tDataStruct m,n;
memset(&m, 2, sizeof(m));
memcpy(&n,&m,sizeof(m));
My question is,
1): in memset if i set to 0 it is fine. But when setting 2 i get m.a and m.b as 514 instead of 2. When I make them as char instead of short it is fine. Does it mean we cannot use memset for any initialization other than 0? Is it a limitation on short for eg
2): Is it reliable to do memcopy between two structures above of type short. I have a huge
strings of a,b,c,d,e... I need to make sure copy is perfect one to one.
3): Am I better off using memset and memcopy on individual arrays rather than collecting in a structure as above?
One more query,
In the structue above i have array of variables. But if I am passed pointer to these arrays
and I want to collect these pointers in a structure
typedef struct
{
short *pa[10];
short *pb[10];
}tDataStruct;
tDataStruct m,n;
memset(&m, 2, sizeof(m));
memcpy(&n,&m,sizeof(m));
In this case if i or memset of memcopy it only changes the address rather than value. How do i change the values instead? Is the prototype wrong?
Please suggest. Your inputs are very imp
Thanks
dsp guy
memset set's bytes, not shorts. always. 514 = (256*2) + (1*2)... 2s appearing on byte boundaries.
1.a. This does, admittedly, lessen it's usefulness for purposes such as you're trying to do (array fill).
reliable as long as both structs are of the same type. Just to be clear, these structures are NOT of "type short" as you suggest.
if I understand your question, I don't believe it matters as long as they are of the same type.
Just remember, these are byte level operations, nothing more, nothing less. See also this.
For the second part of your question, try
memset(m.pa, 0, sizeof(*(m.pa));
memset(m.pb, 0, sizeof(*(m.pb));
Note two operations to copy from two different addresses (m.pa, m.pb are effectively addresses as you recognized). Note also the sizeof: not sizeof the references, but sizeof what's being referenced. Similarly for memcopy.