CoDeSys: BOOL becomes "*** INVALID: 16#02 ***" outside case (link between two vars) - codesys

When I trace my program (online with breakpoint and singlestep) I see,
that the VAR SOpen becomes "* INVALID: 16#02 *" when the line Step:=Step+1; is reached.
Watching the trace more careful, I saw, that Step changes from 0 to 1, in the first call of "0:". The two VARs are the two initialized and seem to be linked together. The aditional increase sets Step to 2 (in the 1st run!) and SOpen to invalid.
PROGRAM PLC_PRG
VAR
SerIO: SERCOMM;
Step: BYTE := 0;
Input: ARRAY [0..50] OF DWORD;
SOpen: BOOL := FALSE;
END_VAR
IF Eingang1 THEN
CASE Step OF
0:
IF NOT SOpen THEN
SerIO.FB_ACTION := Open;
SerIO.RECEIVE_BUFFER:=Input[0];
SerIO.COMPORT:=1;
SerIO.EN:=TRUE;
Ausgang1 := NOT SerIO.ERROR;
SOpen :=TRUE;
SerIO.EN:=FALSE;
END_IF;
1:
IF SOpen THEN
SerIO.FB_ACTION:=Read;
SerIO.EN:=TRUE;
Ausgang1 := NOT SerIO.ERROR;
Ausgang2 := (SerIO.BYTES_DONE>3);
SerIO.EN:=FALSE;
END_IF;
2:
IF SOpen THEN
SerIO.FB_ACTION:=Close;
SerIO.EN:=TRUE;
Ausgang1 := NOT SerIO.ERROR;
SOpen :=FALSE;
SerIO.EN:=FALSE;
END_IF;
END_CASE
Step:=Step+1;
IF Step>2 THEN Step :=0; END_IF;
ELSIF SOpen THEN
SerIO.FB_ACTION:=Close;
SerIO.EN:=TRUE;
SerIO.EN:=FALSE;
SOpen :=FALSE;
Step:=0;
END_IF;

I transferred the VAR now to VAR_GLOBAL. That's not really what I want, but now it works. Better solutions are welcome and will be accepted :)

Question why would you place the CASE statement in an IF statement? The IF statement should be calling your steps?
If something then
step = 10; (* start processing *)
else
step = 0;
end_if
case step of
0:
Kill your enable or an idle state here stop state.
10: (* Start *)
step = step +1;
20:
Call FB
40: (*continue *)
step = 10;
30: (* End *)
step = 0;
else (* catch something here *)
end_case
call FB here that gets inputs from above code.
It's hard to tell the INVALID sometimes if you don't perform a clean all and the variable list gets out of wack that can happen.
Sorry not much of a help I have seen the invalid and it came from clean project and looking at invalid pointer that haven't been called yet.

Related

flowing lights in structured text

I am very new to structured text, so pardon my simple question.
I am using OpenPLC to create this simple program. I have been following the example from the link below to create flowing lights simple program with structured text. In this video, they used 5LEDs and controlled it with case statements.
However, my question is, if my program needs to turn on 100 lights, how should I change the code?
Should I use for loops? How?
https://www.youtube.com/watch?v=PXnaULHpxC8&t=25s
Yes you can use for loops etc. to make the program more "dynamic".
Unfortunately most of the PLC's don't give you dynamic access to their digital outputs. This means that at the end you will have to write code that will translate the value from array (which you will be looping through) into digital outputs.
There are a few ways to do that. First let me show how you can create chasing light for up to 16.
PROGRAM PLC_PRG
VAR
iNumOfLights : INT := 6;
fbCounter : CTU := ;
fbTicker : BLINK := (ENABLE := TRUE, TIMELOW := T#100MS, TIMEHIGH := T#1S);
wOut: WORD;
END_VAR
fbTicker();
fbCounter(CU := fbTicker.OUT, RESET := fbCounter.Q, PV := iNumOfLights);
wOut := SHL(2#0000_0000_0000_0001, fbCounter.CV);
A := wOut.0;
B := wOut.1;
C := wOut.2;
D := wOut.3;
E := wOut.4;
F := wOut.5;
G := wOut.6;
END_PROGRAM
Or if you know output address you can do it directly to outputs.
PROGRAM PLC_PRG
VAR
iNumOfLights : INT := 6;
fbCounter : CTU := ;
fbTicker : BLINK := (ENABLE := TRUE, TIMELOW := T#100MS, TIMEHIGH := T#1S);
wOut AT %QB0.1: WORD;
END_VAR
fbTicker();
fbCounter(CU := fbTicker.OUT, RESET := fbCounter.Q, PV := iNumOfLights);
wOut := SHL(2#0000_0000_0000_0001, fbCounter.CV);
END_PROGRAM
You can also change type of chasing lights by something like.
IF fbCounter.CV = 0 THEN
wOut := 0;
END_IF;
wOut := wOut OR SHL(2#0000_0000_0000_0001, fbCounter.CV);
Now what is behind this. SHl operator will move 1 to the left on set number. For example SHL(2#0000_0000_0000_0001, 3) will result in 2#0000_0000_0000_1000. So we assign it to wOut and then access individual bits by wOut.[n].

Delay Timer in Structured Text

I have Just started working on PLC using Structured text, I have to store values in Array of Temperature variable after delay of 1 min every time but i am not able to do that.
FOR i := 0 TO 5 DO
Temp[i] := tempsensor;
END_FOR;
This is kind a pseudo code.
I just need to bring in the delay in the loop that after every 1 min it could read the value and store it in the array location.
Even if there is any other way then I will really appreciate that.
Try this
VAR
i:INT;
Temp: ARRAY[0..10000] OF LREAL;
delayTimer: TON;
END_VAR
delayTimer(IN := not delayTimer.Q, PT := T#1m);
IF delayTimer.Q THEN
Temp[i] := tempsensor;
i := i + 1;
IF i > 10000 THEN
i := 0;
END_IF;
END_IF;
After 1 minute it will record 1 temperature value and index the array. If it reaches the end of the array it will start to write over at the beginning.
Once every minute you cycle through array and set values.
VAR
i: INT := 1; (* Cycle number *)
temp: ARRAY[1..5] OF REAL; (* Array of temperatures *)
ton1: TON; (* Timer *)
END_VAR
ton1(IN := NOT ton1.Q, PT := T#1m);
IF ton1.Q THEN
temp[i] := tempsensor;
IF i >= 5 THEN i := 1 ELSE i := i + 1 END_IF;
END_IF;

Why does TTcpClient drop data on SendStream()?

When I call TTcpClient.SendStream(MyStream), it advances MyStream.Position to 8704, and I have to call SendStream() repeatedly to get it to completely send my stream. However, the data received is missing chunks of 512 bytes about every 8K.
Note: This question is rhetorical, because I suffered through trying and failing to find a solution on the web. I found the bug in Delphi 7 Sockets.pas, and want to publish the solution for the good of the community.
The problem is a coding bug in Delphi 7 Sockets.pas. The bug causes any stream larger than about 8K (exact size is OS-dependent) to lose 512-byte chunks of data. The SendStream implementation uses a repeat..until loop to pull 512-byte buffers from the caller's stream for sending with SendBuf(), and it continues as long as the stream has data and SendBuf() does not return equal to SOCKET_ERROR. The loss occurs when the Windows socket buffer fills, causing SendBuf() to return equal to SOCKET_ERROR, but at that point up to 512 bytes have already been read from the caller's stream and the stream Position has been advanced - but that Position is not restored on exit. Original Sockets.pas code:
function TBaseSocket.SendStream(AStream: TStream): Integer;
var
BufLen : Integer;
Buffer: array[0..511] of Byte;
begin
Result := 0;
if Assigned(AStream) then begin
repeat
BufLen := AStream.Read(Buffer, SizeOf(Buffer));
until (BufLen = 0) or (SendBuf(Buffer, BufLen) = SOCKET_ERROR);
end;
end;
And here's a fix:
function TBaseSocket.SendStream(AStream: TStream): Integer;
var
Quit : boolean;
BufLen,OldPosition : Integer;
Buffer: array[0..511] of Byte;
begin
Result := 0;
if Assigned(AStream) then begin
repeat
OldPosition := AStream.Position;
BufLen := AStream.Read(Buffer, SizeOf(Buffer));
if (BufLen > 0) then begin
Quit := (SendBuf(Buffer, BufLen) = SOCKET_ERROR);
if Quit then AStream.Position := OldPosition; //restore!
end else begin //BufLen = 0
Quit := true;
end;
until Quit;
end;
end;

Maple: RNG is not random

i was "finding Pi" with Monte Carlo Method, but the answer was incorrect. The oryginal code was:
RandomTools[MersenneTwister]: with(Statistics):
tries := 10000:
s := 0;
for i to tries do
if GenerateFloat()^2+GenerateFloat()^2 < 1 then s := s+1 end if;
end do:
evalf(4*s/tries)
It gives answer aroud 2.8-2.85
when I change the code to
s := 0;
x := Array([seq(GenerateFloat(), i = 1 .. tries)]);
y := Array([seq(GenerateFloat(), i = 1 .. tries)]);
for i to tries do
if x[i]^2+y[i]^2 < 1 then s := s+1 end if;
end do:
evalf(4*s/tries)
Then the answer is correct. I have no idea why i can't generate number in "for" loop.
I've founded that the mean of it is the same, but the variance is different.
For:
tries := 100000;
A := Array([seq(GenerateFloat(), i = 1 .. 2*tries)]);
s1 := Array([seq(A[i]^2+A[tries+i]^2, i = 1 .. tries)]);
Mean(s1);
Variance(s1);
s2 := Array([seq(GenerateFloat()^2+GenerateFloat()^2, i = 1 .. tries)]);
Mean(s2);
Variance(s2);
output is:
0.6702112097021581
0.17845439723457215
0.664707674135025
0.35463131700965245
What's wrong with it? GenerateFloat() should be as uniform as possible.
Automatic simplification is turning your,
GenerateFloat()^2+GenerateFloat()^2
into,
2*GenerateFloat()^2
before GenerateFloat() is evaluated.
One simple change to get it to work as you expected would be separate them. Eg,
restart:
with(RandomTools[MersenneTwister]):
tries := 10^4:
s := 0:
for i to tries do
t1,t2 := GenerateFloat(),GenerateFloat();
if t1^2+t2^2 < 1 then s := s+1 end if;
end do:
evalf(4*s/tries);
Another way is to use a slightly different construction which doesn't automatically simplify. Consider, single right quotes (uneval quotes) don't stop automatic simplification (which is a definition of the term if you want).
'f()^2 + f()^2';
2
2 f()
But the following does not automatically simplify,
a:=1:
'f()^2 + a*f()^2';
2 2
f() + a f()
Therefore another easy workaround is,
restart:
with(RandomTools[MersenneTwister]):
tries := 10^4:
s := 0:
a := 1;
for i to tries do
if GenerateFloat()^2 + a*GenerateFloat()^2 < 1 then s := s+1 end if;
end do:
evalf(4*s/tries);

How to get/find the variable that caused Division By Zero error in delphi?

I know how to do basic exception handling. So i can raise a message on divide by zero using the 'try except' method.
What i would like to do is, find the variable that causes this error and then change its value on run time.
For Ex:
procedure Calculate();
var
a, b, c : Double;
begin
try
a := 4; //suppose i take this value from user and he enters 4
b := 0; //suppose i take this value from user and he enters 0
c := a/b;
ShowMessage(FloatToStr(c));
except
on E : EZeroDivide do
begin
ShowMessage('Exception message = '+E.Message);
//i am not sure how to identify that its variable 'b' that is causing the error and has to be changed by a default value
get(E....errorVaraiable);
E....errorVaraiable := 0.00001;
c := a/E....errorVariable;
ShowMessage(FloatToStr(c));
end;
end;
Please, can anyone help me with this?
Here's a modified version of your example that does what you want.
procedure Calculate();
var
a, b, c : Double;
begin
a := 4; //suppose i take this value from user and he enters 4
b := 0; //suppose i take this value from user and he enters 0
if IsZero(b) then
begin
ShowMessage('b cannot be 0')
end
else
begin
c := a/b;
ShowMessage(FloatToStr(c));
end;
end;