1

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.?

youngEngineer
  • 51
  • 2
  • 5

4 Answers4

1

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))

Quirzo
  • 1,183
  • 8
  • 10
0

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.

Tost
  • 51
  • 7
  • "There must be some way to do it automatically in case I change the members of the struct in the future.?" Or you can even try to place the ARRAY OF STRUCT on the same Memory like the ARRAY OF BYTE by using the explicit Memory functionality. (%MB0, %MB*, ...) (https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_plc_intro/18014398645980299.html&id=) – Tost Sep 25 '17 at 10:27
0

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.

Himanshu sharma
  • 7,487
  • 4
  • 42
  • 75
Filippo Boido
  • 1,136
  • 7
  • 11
-1

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.

Scott
  • 116
  • 2
  • You can guarantee byte location in a structure. There is no optimization by the compiler to the hardware. There is just an attribute "pack mode". You can see it like a ruleset how the variables inside the structure get arranged – Felix Keil Sep 23 '17 at 05:20
  • If you switch from a Motorola to Intel platform, you could have byte swap. Also the compiler will align your bytes and may introduce dead bytes depending how you define your structure (DWORDS have to align to memory locations divisible by 4, etc). – Scott Sep 24 '17 at 05:44
  • Yes that is the pack mode in TwinCat. – Felix Keil Sep 25 '17 at 08:03