Logix 5000 UDT within UDT

cardosocea

Member
Join Date
Nov 2016
Location
Fields of corn
Posts
2,639
Hello,

I'm writing an interface program to a machine and because the bit data is packed into 16 bit variables, it's nicer to create an UDT to describe what each bit does and then enclose it within the data map from the machine.

This way, in my mind, I'd copy the IO data (two arrays with 100 INTs) into the structure and eveything would match nicely.

However, I'm now seeing that even though my UDT's only have 16 bits, the size is larger than that and as such creates shifts in the data making it impossible to map nicely.

Am I doing something wrong or is the minimum size of an UDT 32 bits because that's all the processor can understand?
 
It's not that the minimum size is 4 bytes, UDT's are packed in 4 byte chunks. So if you had a UDT that was a DINT and a BOOL, your UDT will be 8 bytes. The bool will occupy the next 4 byte chunk.

Edit: For fun, create a UDT with 3 members, in this order, SINT, INT, SINT. Check the size. Then rearrange the members so that they are SINT, SINT, INT, check the size.
 
Last edited:
Look up bit overlays, that may help achieve what you want.

This is what I was trying to do to begin with. I receive 100INT from the machine. Create a UDT with INTs and UDTs that bit overlay so that I can read directly and only use one COP instruction.

It's not that the minimum size is 4 bytes, UDT's are packed in 4 byte chunks. So if you had a UDT that was a DINT and a BOOL, your UDT will be 8 bytes. The bool will occupy the next 4 byte chunk.

Edit: For fun, create a UDT with 3 members, in this order, SINT, INT, SINT. Check the size. Then rearrange the members so that they are SINT, SINT, INT, check the size.
That's what I found. It's such a ball ache to be used to stuff like this and unions in C and not being able to do it because of how the memory maps. At least it doesn't "round up" bits to bytes as a certain platform does.

In the end, since the values were multiplied by 10 or 100, I ended up creating two mapping routines to do this and simply copy the INT into the structure.
 
Post your routine and structure.


Why would the routine make a difference? The whole point is not to have a routine and use a single COP instruction to move an array of INTs into a nested UDT.


This is not possible with Rockwell, as confirmed by DMRoeder. Another notch on why not to use Rockwell list.
 
Why would the routine make a difference? The whole point is not to have a routine and use a single COP instruction to move an array of INTs into a nested UDT.


This is not possible with Rockwell, as confirmed by DMRoeder. Another notch on why not to use Rockwell list.

JeremyM is a bonafide UDT/AOI Ninja. If anyone can help with a solution, it is him.
 
The UDT is something along the lines of:

UDT {
SpeedReference:INT,;
TorqueReference: INT;
Commands:UDT_Commands;
Flow: INT;}

UDT_Commands{
remoteStart:BOOL;
remoteStop:BOOL;
LocalMode:BOOL;
StartProduction:BOOL;
AckAlarms:BOOL
Spare5:BOOL;
Spare6:BOOL;
Spare7:BOOL;
Spare8:BOOL;
Spare9:BOOL;
Spare10:BOOL;
Spare11:BOOL;
Spare12:BOOL;
Spare13:BOOL;
Spare14:BOOL;
Spare15:BOOL;}

I receive 4 INTs from the machine via EthernetIP.

I'd like the mapping to be done with a COP instruction, COP(UDTInstance, Machine:O, 1);

However, when doing this, the 16 boolean UDT takes up 32 bits and as such my flow signal is displaced and not transmitted in the correct place.

So this is a bit overlay and there was no routine intended in the first place, just a COP instruction.

So the question is can the Logix5000 and the rockwell processors have UDT's with sizes different than multiples of 32 bits.

I've now created a routine (AOI is out of the question and it doesn't matter much since it sits nicely in the program for the machine control) that does this INT by INT applying the division/multiplication factor as required to convert from INT to REAL. But it's a shame if it can't be done with an overlay different than 32 bits in size.
 
Can you Re-Order the Incoming Data?

What about this UDT structure ->
Code:
UDT{
SpeedReference:INT,;
TorqueReference: INT;
remoteStart:BOOL;
remoteStop:BOOL;
LocalMode:BOOL;
StartProduction:BOOL;
AckAlarms:BOOL
Spare5:BOOL;
Spare6:BOOL;
Spare7:BOOL;
Spare8:BOOL;
Spare9:BOOL;
Spare10:BOOL;
Spare11:BOOL;
Spare12:BOOL;
Spare13:BOOL;
Spare14:BOOL;
Spare15:BOOL;
Flow: INT;}
 
I receive 4 INTs from the machine via EthernetIP.

I'd like the mapping to be done with a COP instruction, COP(UDTInstance, Machine:O, 1);


Are you copying from the UDT instance to the 4 INTs, or vice versa? Because from the first statement above you are receiving the values of the 4 INTs from a machine external to the PLC, but the second statement sounds like you are writing the values of the 4 INTs on the PLC.

-
 
Attached is a UDT matching your spec. (updated just now to reflect spares)

Your UDT with nested command UDT is not an overlay and cannot guarantee byte alignment. It only guarantees, at minimum, that a 32-bit blob of something exists in the middle of the nesting UDT.

desination unknown is heading in the right direction with a "flat" UDT. The problem is that the assembled type occupies 12 bytes. The next steps would be to export the built type, delete the auto-inserted SINTs supporting the BOOLs, then change the Target specifiers for each BOOL to point to bits of the Commands INT. Overlays can even be DINT. The new type occupies 8 bytes and is DWORD-aligned (to 4 bytes).

When you do a COP to this structure, it could be something along the lines of:
Code:
COP(I.Data[0], udtInstance.SpeedReference, 4); // always land on first element. 4 = write first INT plus 3 more.
Code:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RSLogix5000Content SchemaRevision="1.0" SoftwareRevision="34.00" TargetName="MachineUDT" TargetType="DataType" ContainsContext="true" Owner="Jeremy" ExportDate="Mon May 09 07:21:09 2022" ExportOptions="References NoRawData L5KData DecoratedData Context Dependencies ForceProtectedEncoding AllProjDocTrans">
<Controller Use="Context" Name="DEVPAC">
<DataTypes Use="Context">
<DataType Use="Target" Name="MachineUDT" Family="NoFamily" Class="User">
<Members>
<Member Name="SpeedReference" DataType="INT" Dimension="0" Radix="Decimal" Hidden="false" ExternalAccess="Read/Write">
<Description>
<![CDATA[Speed reference.]]>
</Description>
</Member>
<Member Name="TorqueReference" DataType="INT" Dimension="0" Radix="Decimal" Hidden="false" ExternalAccess="Read/Write">
<Description>
<![CDATA[Torque reference.]]>
</Description>
</Member>
<Member Name="Commands" DataType="INT" Dimension="0" Radix="Decimal" Hidden="false" ExternalAccess="Read/Write">
<Description>
<![CDATA[Machine commands (overlay).]]>
</Description>
</Member>

<Member Name="RemoteStart" DataType="BIT" Dimension="0" Radix="Decimal" Hidden="false" Target="Commands" BitNumber="0" ExternalAccess="Read/Write">
<Description>
<![CDATA[Cmd.0: Remote command to start.]]>
</Description>
</Member>
<Member Name="RemoteStop" DataType="BIT" Dimension="0" Radix="Decimal" Hidden="false" Target="Commands" BitNumber="1" ExternalAccess="Read/Write">
<Description>
<![CDATA[Cmd.1: Remote command to stop.]]>
</Description>
</Member>
<Member Name="LocalMode" DataType="BIT" Dimension="0" Radix="Decimal" Hidden="false" Target="Commands" BitNumber="2" ExternalAccess="Read/Write">
<Description>
<![CDATA[Cmd.2: Command to set to local control.]]>
</Description>
</Member>
<Member Name="StartProduction" DataType="BIT" Dimension="0" Radix="Decimal" Hidden="false" Target="Commands" BitNumber="3" ExternalAccess="Read/Write">
<Description>
<![CDATA[Cmd.3: Command to start production.]]>
</Description>
</Member>
<Member Name="AckAlarms" DataType="BIT" Dimension="0" Radix="Decimal" Hidden="false" Target="Commands" BitNumber="4" ExternalAccess="Read/Write">
<Description>
<![CDATA[Cmd.4: Command to ack alarms.]]>
</Description>
</Member>
<Member Name="Spare5" DataType="BIT" Dimension="0" Radix="Decimal" Hidden="false" Target="Commands" BitNumber="5" ExternalAccess="Read/Write">
<Description>
<![CDATA[Cmd.5: Spare.]]>
</Description>
</Member>
<Member Name="Spare6" DataType="BIT" Dimension="0" Radix="Decimal" Hidden="false" Target="Commands" BitNumber="6" ExternalAccess="Read/Write">
<Description>
<![CDATA[Cmd.6: Spare.]]>
</Description>
</Member>
<Member Name="Spare7" DataType="BIT" Dimension="0" Radix="Decimal" Hidden="false" Target="Commands" BitNumber="7" ExternalAccess="Read/Write">
<Description>
<![CDATA[Cmd.7: Spare.]]>
</Description>
</Member>
<Member Name="Spare8" DataType="BIT" Dimension="0" Radix="Decimal" Hidden="false" Target="Commands" BitNumber="8" ExternalAccess="Read/Write">
<Description>
<![CDATA[Cmd.8: Spare.]]>
</Description>
</Member>
<Member Name="Spare9" DataType="BIT" Dimension="0" Radix="Decimal" Hidden="false" Target="Commands" BitNumber="9" ExternalAccess="Read/Write">
<Description>
<![CDATA[Cmd.9: Spare.]]>
</Description>
</Member>
<Member Name="Spare10" DataType="BIT" Dimension="0" Radix="Decimal" Hidden="false" Target="Commands" BitNumber="10" ExternalAccess="Read/Write">
<Description>
<![CDATA[Cmd.10: Spare.]]>
</Description>
</Member>
<Member Name="Spare11" DataType="BIT" Dimension="0" Radix="Decimal" Hidden="false" Target="Commands" BitNumber="11" ExternalAccess="Read/Write">
<Description>
<![CDATA[Cmd.11: Spare.]]>
</Description>
</Member>
<Member Name="Spare12" DataType="BIT" Dimension="0" Radix="Decimal" Hidden="false" Target="Commands" BitNumber="12" ExternalAccess="Read/Write">
<Description>
<![CDATA[Cmd.12: Spare.]]>
</Description>
</Member>
<Member Name="Spare13" DataType="BIT" Dimension="0" Radix="Decimal" Hidden="false" Target="Commands" BitNumber="13" ExternalAccess="Read/Write">
<Description>
<![CDATA[Cmd.13: Spare.]]>
</Description>
</Member>
<Member Name="Spare14" DataType="BIT" Dimension="0" Radix="Decimal" Hidden="false" Target="Commands" BitNumber="14" ExternalAccess="Read/Write">
<Description>
<![CDATA[Cmd.14: Spare.]]>
</Description>
</Member>
<Member Name="Spare15" DataType="BIT" Dimension="0" Radix="Decimal" Hidden="false" Target="Commands" BitNumber="15" ExternalAccess="Read/Write">
<Description>
<![CDATA[Cmd.15: Spare.]]>
</Description>
</Member>

<Member Name="Flow" DataType="INT" Dimension="0" Radix="Decimal" Hidden="false" ExternalAccess="Read/Write">
<Description>
<![CDATA[Flow rate.]]>
</Description>
</Member>
</Members>
</DataType>
</DataTypes>
</Controller>
</RSLogix5000Content>

udtBeforeOverlay.png udtAfterOverlay.png
 

Attachments

  • MachineUDT_DataType.zip
    36.9 KB · Views: 2
Last edited:
Nice. Changing the extension to XML and opening it in a browswer is handy (e.g. indentation; example Firefox shown below).

Is there any documentation for the L5X syntax? I wrote a Python script to auto-generate some duplication once for my brother, but it was all guesswork.
L5X_xml.png
 

Similar Topics

Hello, Wondering if the following setup would work. 1. Local PLC on the network has an array of 25 REALs 2. Supervisory PLC on the network needs...
Replies
14
Views
1,820
Hi there, I am doing work for a customer who has a UDT that uses parameters like this: PLC_REG_TO_SEND.GROUP_15.1234_DINT[1].0 I believe these...
Replies
5
Views
1,810
Ok, I am having a tough time with an application and figuring out how the best way would be for me. So here is what I would like to accomplish: I...
Replies
28
Views
9,270
Good Evening , I still have many "Mental Blocks" when it comes down to programming. Could you tell me the difference between a UDT and a AOI ...
Replies
26
Views
16,827
I am sure everyone is saying not this again. I have looked and could not find an answer to this. I created a UDT with elements DW0, DW4, DW8...
Replies
1
Views
2,530
Back
Top Bottom