Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

String to Integer to String

Status
Not open for further replies.

jtheis

Technical User
Sep 9, 2004
41
0
0
US
I'm trying to decipher some code I inherited that isn't working. Here's what I'm trying to do...

A 10 character string value is entered at an HMI. This string is sent down to a PLC where it is copied over to an integer file. This integer file is read by the Delphi app via an OPC server. The Delphi app then is supposed to convert the integer back to the string so that it can be searched. Hopefully that explains what it is supposed to do.

Here's the code for reading the data from the OPC server. ArrayToStringSwap is a custom function.
Code:
var
  I : integer;
  Num : array[0..4] of word;
for I := 0 to 4 do
   Num[I] :=  GetRegValue(I + 79);
   IDStr := ArrayToStringSwap(Num,10);

The function ArrayToStringSwap is done below.
Code:
function TDevice.ArrayToStringSwap(var A;Size : integer) : String;
type
  Stype = record
    SZ : byte;
    SA : array[1..255] of char;
  end;

  Wtype = record
    SZ : byte;
    WA : array[1..128] of word;
  end;

 var
   S : String[20];
   ST : Stype absolute S;
   WT : Wtype absolute S;
   I, Temp : word;
   Len : integer;

 begin
   if size > 20 then size := 20;

   Result := '';
   ST.SZ := Size;
   move(A,ST.SA,Size);

   Len := Size div 2;

   for I := 1 to Len do begin
     Temp := Swap(WT.WA[I]);
     WT.WA[I] := Temp;
   end;

   Result := TrimLeft(S);
 end;

The problem is that all of the characters from the original HMI string come back except for one, and the characters are all scrambled as far as the order goes.

EXAMPLE: If I enter 'teststring' at my HMI, it will be stored in the PLC integer files N50:79 as 29797 (decimal equivalent of ASCII 'te'), N50:81 as 29556 (decimal equivalent of ASCII 'st'), etc. When I get my result back I get 'etttsisgr', which is all of the characters except the 'n'.

I know that 'right' way to do this is to probably just read the string from the HMI without all of the converting back and forth, but if I do that I'll have to rewrite the data collection portion of the whole program.

This is something that everyone assumed worked properly until I started looking at it today. If anyone has any ideas, please let me know.

Joe
 
This is something that everyone assumed worked properly until I started looking at it today. If anyone has any ideas, please let me know.

Is it still doing what it is supposed to do?

Are you fiddling around with a PIE system? You mentioned an OPC server.

Steven
 
No, it appears that no one ever looked too closely at this to begin with and it has never worked properly.

As I'm not familiar with a PIE system, I can only assume that's not what I'm dealing with. What I've got is an Allen Bradley PanelView Plus talking via Ethernet to an Allen Bradley SLC 5/05. The OPC communication is being handled using RSLinx.
 
I think that the problem may be to do with the way Intel processors store data; they use a method called low endien. The result of this is that data units greater than byte are stored:

Word: low byte, high byte.
DWord: low word, high word (with each word as low byte, high byte)
etc.
etc.

You can confirm this with:

WhatsInAWordRec = record
Case boolean of
True : (WordVal : Word);
False : (LowByte, HighByte : Byte)
end;

Declare a variable WhatsInAWord as WhatsInAWordRec
then WhatsInAWord.WordVal := 257
Check WhatsInAWord.LowByte and WhatsInAWord.HighByte they should be 2 and 1 respectively.

I think the easiest solution would be to transfer the data in Bytes and not Words.

Hope this helps.


 
I've dug a little deeper and here's (part of) why I think this doesn't work.

If I type a '5' on my HMI, the PLC doesn't turn this in to the 53 I would expect to represent it. Instead it returns a decimal value of 13600. The PLC converts ASCII to some decimal value in the range of -32767 to 32767. If I enter a decimal value of '53' the ASCII comes back as \005.
 
Looks to me like you PLC is returning a 2 byte value (as you would expect) your 13600 is 3520 hex which is '5' and space.



Steve:
A Delphi Programmer
A Feersum Endjinn indeed
 
Yes, I'm a little slow today. I just figured out what is happening there. So basically what needs to happen is that the decimal number (13600) needs to be dvided by 256 (number of ASCII characters) and the whole number returned is the first character. The remainder is then the second character.

So 13600/256 = 53.125 (53, with 32 as remainder) and 53 = 5 and 32 = space. Sorry if this is obvious to everyone else, but I'm just a poor mechanical engineer who has fooled everyone into thinking I'm an electrical engineer.

Now that I understand what is supposed to be happening, I guess I'm not quite sure what is really happening.

earthandfire, I saw your post and I'm not 100% sure I have it right in my head. I'm thinking that both need to be 1 in order for the program to work on my end. What you're saying is that they won't be?

Thanks to everyone for the help!
 
Jtheis, I am just a poor mechanical engineer who has fooled everyone into thinking I'm a Management Information Specialist (at least at Tek-Tips). Pie is software package from Osisoft (I think) to take out data from a plant-network (Distributed Control System) and show a filling tank in excel.
I know a little about programming but this byte, bit and Hex stuff is a little to heavy (Always found a work around in TP / Delphi). But I see that experts have taken over [shadeshappy]
Regards

Steven
 
jthesis, my mistake, I should have previewed my post before hitting submit. Sorry if I confused you.

I meant to type then WhatsInAWord.WordVal := 258.

As you say, 257 shows both bytes 1, which doesn't demonstrate anything. 258 on the other hand does demonstrate the low endien storage with 2 stored before 1.
 
jtheis: per your post 10 May 05 11:45
<snip>
So basically what needs to happen is that the decimal number (13600) needs to be dvided by 256 (number of ASCII characters) and the whole number returned is the first character. The remainder is then the second character.

So 13600/256 = 53.125 (53, with 32 as remainder) and 53 = 5 and 32 = space.
<snip>

Rather than use / which is inefficient for this use and can produce rounding and truncation errors in your results, use the bitwise operators (a list in attached from the Delphi helpfile for your convenience).

So, to separate the upper and lower bytes from a word:
[tt]
upperByte := (yourWord shr 8) and $ff;
lowerByte := (yourWord and $ff);
[/tt]

You can then use those bytes in whatever order you need if you need to get the characters in the correct order for your applications. (I AND'd the first one in case of sign-extension: not really necessary in many cases, but safer when using shr. Look up sign extension in google for an explanation).

Here is the excerpt from Delphi's helpfile:
[tt]
Logical (bitwise) operators
Operator Operation Operand types Result type Examples
not bitwise negation integer integer not X
and bitwise and integer integer X and Y
or bitwise or integer integer X or Y
xor bitwise xor integer integer X xor Y
shl bitwise shift left integer integer X shl 2
shr bitwise shift right integer integer Y shr I
[/tt]


Good luck! [smile]
 
ToTheMetal, why do any calculations when:

WhatsInAWordRec = record
Case boolean of
True : (WordVal : Word);
False : (LowByte, HighByte : Byte)
end;

could be adjusted to:

WhatsInAWordRec = record
Case boolean of
True : (WordVal : Word);
False : (Char1, Char2 : char)
end;

Assign to .WordVal read from .Char1 and .Char2
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top