Delay Timer in Structured Text - plc

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;

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

Converting LREAL to binary and interpreting it as base 10 LINT

Running "CODESYS V3.5 SP16" here, I am trying to implement the hashcode algorithm mentioned in
Best implementation for hashCode method for a collection
and had to built my own solution to replicate Java's Float.floatToIntBits(f) which resulted in the following function
FUNCTION F_lrealToLintBits : LINT
VAR_INPUT
lrVal : LREAL;
END_VAR
VAR
arrBytes : ARRAY[0..7] OF BYTE; // LREAL contains 64 bits and each byte contains 8 bits
pVal : POINTER TO LREAL := ADR(arrBytes);
diIndx : DINT;
uiExpt : UINT := 0; // exponent goes from 0 to 63
END_VAR
pVal^ := lrVal; // maps LREAL to array of bytes
// little endian? cause it seems that least significant bit is at lowest address
FOR diIndx := LOWER_BOUND(arrBytes, 1) TO UPPER_BOUND(arrBytes, 1) BY 1 DO
// bit access seems to be manual only so no loops
IF arrBytes[diIndx].0 THEN
F_lrealToLintBits := F_lrealToLintBits + LREAL_TO_LINT(EXPT(2, uiExpt));
END_IF
uiExpt := uiExpt + 1; // have to increment exponent after every bit
IF arrBytes[diIndx].1 THEN
F_lrealToLintBits := F_lrealToLintBits + LREAL_TO_LINT(EXPT(2, uiExpt));
END_IF
uiExpt := uiExpt + 1; // have to increment exponent after every bit
IF arrBytes[diIndx].2 THEN
F_lrealToLintBits := F_lrealToLintBits + LREAL_TO_LINT(EXPT(2, uiExpt));
END_IF
uiExpt := uiExpt + 1; // have to increment exponent after every bit
IF arrBytes[diIndx].3 THEN
F_lrealToLintBits := F_lrealToLintBits + LREAL_TO_LINT(EXPT(2, uiExpt));
END_IF
uiExpt := uiExpt + 1; // have to increment exponent after every bit
IF arrBytes[diIndx].4 THEN
F_lrealToLintBits := F_lrealToLintBits + LREAL_TO_LINT(EXPT(2, uiExpt));
END_IF
uiExpt := uiExpt + 1; // have to increment exponent after every bit
IF arrBytes[diIndx].5 THEN
F_lrealToLintBits := F_lrealToLintBits + LREAL_TO_LINT(EXPT(2, uiExpt));
END_IF
uiExpt := uiExpt + 1; // have to increment exponent after every bit
IF arrBytes[diIndx].6 THEN
F_lrealToLintBits := F_lrealToLintBits + LREAL_TO_LINT(EXPT(2, uiExpt));
END_IF
uiExpt := uiExpt + 1; // have to increment exponent after every bit
IF arrBytes[diIndx].7 THEN
F_lrealToLintBits := F_lrealToLintBits + LREAL_TO_LINT(EXPT(2, uiExpt));
END_IF
uiExpt := uiExpt + 1; // have to increment exponent after every bit
END_FOR
Firstly, I would like to know if there is an existing function in some library that does this already?
I looked in many places including in the OSCAT "Basic, 3.31" library but could not find anything similar (even a set of functions that I could chain together would be OK).
Secondly, can bit access only be done manually?
I would prefer to use a loop but it seems that is not possible? Is there a less copy-and-paste method of accessing the bits that would involve automatically detecting the number of bits if the data type in the array changes from BYTE to something else (e.g. DWORD)?
Potential alternative
Seems like this is where unions come in handy, as mentioned in https://forge.codesys.com/forge/talk/Engineering/thread/02a65a50b2/#e5f4/1911/c524 where if the value does not come from an external source (i.e. no need to check for endian-ness), Float.floatToIntBits(f) would be as simple as
TYPE
U_lrealToRawLintBits :
UNION
lrVal : LREAL;
liVal : LINT;
END_UNION
END_TYPE
// supposed to replicate floatToIntBits() in Java at
// https://github.com/openjdk/jdk/blob/739769c8fc4b496f08a92225a12d07414537b6c0/src/java.base/share/classes/java/lang/Float.java#L775
FUNCTION F_lrealToLintBits : LINT
VAR_INPUT
lrVal : LREAL;
END_VAR
VAR
uLrealToLintBits : U_lrealToRawLintBits; // for interpreting LREAL as base 10 LINT
END_VAR
IF FPU.IsLRealPosInfinity(lrVal) THEN
F_lrealToLintBits := 2139095040; // corresponds to 0x7f800000
ELSIF FPU.IsLRealNegInfinity(lrVal) THEN
F_lrealToLintBits := 4286578688; // corresponds to 0xff800000
ELSIF FPU.IsLRealNaN(lrVal) THEN
F_lrealToLintBits := 2143289344; // corresponds to 0x7fc00000
ELSE
uLrealToLintBits.lrVal := lrVal;
F_lrealToLintBits := uLrealToLintBits.liVal;
END_IF
I think what you are trying to achieve is to take as input an LREAL but with the bits inside really being those of a LINT, but byte-swapped.
If so, there should be a more straightforward solution. Here is an example :
FUNCTION lreal_to_int64 : LINT
VAR_INPUT
value: LREAL;
value_is_little_endian: BOOL;
END_VAR
VAR
no_swap: BOOL;
source_bytes, target_bytes: POINTER TO BYTE;
value_as_lint: POINTER TO LINT;
END_VAR
{IF defined (IsLittleEndian)}
no_swap := value_is_little_endian;
{ELSE}
no_swap := NOT value_is_little_endian;
{END_IF}
IF
no_swap
THEN
value_as_lint := ADR(value);
lreal_to_int64 := value_as_lint^;
RETURN;
END_IF
target_bytes := ADR(lreal_to_int64);
source_bytes := ADR(value);
target_bytes[0] := source_bytes[7];
target_bytes[1] := source_bytes[6];
target_bytes[2] := source_bytes[5];
target_bytes[3] := source_bytes[4];
target_bytes[4] := source_bytes[3];
target_bytes[5] := source_bytes[2];
target_bytes[6] := source_bytes[1];
target_bytes[7] := source_bytes[0];
Programmatic access to bits is certainly possible using bitwise operators. Try something like this.
FUNCTION get_bit : BOOL
VAR_INPUT
value: LWORD;
bit_position: USINT;
END_VAR
VAR
shifted: LWORD;
END_VAR
shifted := SHR(value, bit_position);
get_bit := shifted.0;

Multi independent input timer

Imagine that we have independent boolean variables that can occur independently. Now, if at least one of the variables occurs for a certain period of time, an alert will be activated. The common solution is that we use a timer for each variable.
Is there an optimal solution that can only be used with a single timer?
Example for 2 variables with 1 second as passed time:
VAR
Timer1,Timer2:TON;
bVar1,bVar2:BOOL;
tSetDelay:TIME:=T#1S;
Alarm:BOOL;
END_VAR
Timer1(IN:=bVar1,PT:=tSetDelay);
Timer2(IN:=bVar2,PT:=tSetDelay);
IF Timer1.Q RO Timer2.Q THEN
Alarm:=TRUE;
END_IF
If we use OR it won't be true
Timer(IN:=bVar1 OR bVar2,PT:=tSetDelay);
cause the vars may have overlap in the same tSetDelay time, it means that they may happen less than the delay, but the timer output be true.
In this example, we have only 2 variables, but if we have many more variables it will be more important to find a better solution.
It it not possible to manage this with a single timer.
Given the that you want to ensure that the triggering source is a single variable (without any logical interference from additional variables), each variable must be compared against it own history (timer).
Recommended path forward would be to build an Alarm function block that handles the majority of the logic here in a consistent manner. An example of this kind of function block is below:
fb_AlarmMonitor
VAR_INPUT
monitor : ARRAY [ 0..7 ] OF POINTER TO BOOL;
duration : TIME;
END_VAR
VAR_OUTPUT
alarm : BOOL;
END_VAR
VAR
timers : ARRAY [ 0..7 ] OF TON;
END_VAR
VAR_TEMP
i : UDINT;
END_VAR
alarm := false;
FOR i := 0 TO 7 BY 1 DO
// Only run process if linked boolean
IF monitor[ i ] <> 0 THEN
timers[ i ]( in := monitor[ i ]^,
pt := duration );
alarm := timers[ i ].Q OR alarm;
END_IF
END_FOR
Implementation
VAR
// Alarm flags
flag_1, flag_2, flag_3
: BOOL;
// Pointers to alarm flags
flag_array : ARRAY [ 0..7 ] OF POINTER TO BOOL
:=[ ADR( flag_1 ),
ADR( flag_2 ),
ADR( flag_3 ) ];
// Monitoring flags
monitor : fb_AlarmMonitor
:=( monitor := flag_array,
duration := T#10S );
END_VAR
monitor();
You cannot do this without individual timers. Here is how I would approach this.
Set global constant and variables
VAR_GLOBAL
glbEvents: ARRAY[1..c_EventsNum] OF stMyEvents; (* Events array *)
END_VAR
VAR_GLOBAL CONSTANT
c_EventsNum: INT := 3; (* Number of events *)
END_VAR
Now you can map glbEvents[1].State to inputs of PLC
Define new structure
TYPE stMyEvents : STRUCT
State : BOOL; (* Event current state *)
StateM : BOOL; (* Memmory of state in previouse cycle to get the trigger *)
Timer: TON := (PT := T#1S); (* Timer *)
END_STRUCT
END_TYPE
Create function
FUNCTION ProcessEvents : BOOL
VAR
iCount: INT; (* Counter *)
END_VAR
FOR iCount := 1 TO c_EventsNum DO
glbEvents[iCount].Timer(IN := glbEvents[iCount].State);
IF glbEvents[iCount].Timer.Q THEN
ProcessEvents := TRUE;
EXIT;
END_IF;
END_FOR;
END_FUNCTION
Implementation
PROGRAM PLC_PRG
VAR
xAlarm: BOOL; (* Alarm *)
END_VAR
IF ProcessEvents() THEN
// Alarm happened
END_IF;
END_PROGRAM
With this approach although you do not have 1 Timer, you have certain level of abstraction that makes it more flexible to support and modify.
But if you absolutely do not want to have so many TON timers, you can create your own timer. It will be single timer in one FB
VAR_GLOBAL
glbEvents: ARRAY[1..c_EventsNum] OF stMyEvents; (* Events array *)
END_VAR
VAR_GLOBAL CONSTANT
c_EventsNum: INT := 3; (* Number of events *)
END_VAR
TYPE stMyEvents : STRUCT
State : BOOL; (* Event current state *)
StateM : BOOL; (* Memmory of state in previouse cycle to get the trigger *)
TimeM: TIME; (* Memmory of event start time *)
END_STRUCT
END_TYPE
FUNCTION_BLOCK ProcessEvents
VAR
iCount: INT; (* Counter *)
END_VAR
VAR_OUTPUT
Q: BOOL; (* Impulse if alarm *)
END_VAR
Q := FALSE;
FOR iCount := 1 TO c_EventsNum DO
(* Get raising edge and save the timer *)
IF glbEvents[iCount].State AND NOT glbEvents[iCount].StateM THEN
glbEvents[iCount].TimeM := TIME();
END_IF;
glbEvents[iCount].StateM := glbEvents[iCount].State;
(* If event is low reset timer *)
IF NOT glbEvents[iCount].State THEN
glbEvents[iCount].TimeM := T#0S;
END_IF;
(* if more than a second make impuls on Q *)
IF (glbEvents[iCount].TimeM > T#0S) AND ((TIME() - glbEvents[iCount].TimeM) >= T#1S) THEN
Q := TRUE;
glbEvents[iCount].TimeM := T#0S;
END_IF;
END_FOR;
END_FUNCTION_BLOCK
PROGRAM PLC_PRG
VAR
fbeventProcess: ProcessEvents; (* function block *)
END_VAR
fbeventProcess();
IF fbeventProcess.Q THEN
// Alarm happened
END_IF;
END_PROGRAM

How to fix a bug on my queue of devices in PLC

I am trying to create a simple queue in .st with 6 devices that should be turned on and off in the queue order only the ones that are available should be connected. For example I did a test with 6 devices available and then I was unavailable one by one but always the last one does not turn off at the output and leaves the program stopped.
I use the OpenPCS IDE of infoteam.
VAR_INPUT
ENABLE : BOOL ;
STATE_DEVICE1 : BOOL ;
STATE_DEVICE2 : BOOL ;
STATE_DEVICE3 : BOOL ;
STATE_DEVICE4 : BOOL ;
STATE_DEVICE5 : BOOL ;
STATE_DEVICE6 : BOOL ;
NUMBER_DEVICES : USINT ;
POWER_REQUEST : USINT ;
END_VAR
VAR_OUTPUT
REQUEST_DEVICE1 : BOOL ;
REQUEST_DEVICE2 : BOOL ;
REQUEST_DEVICE3 : BOOL ;
REQUEST_DEVICE4 : BOOL ;
REQUEST_DEVICE5 : BOOL ;
REQUEST_DEVICE6 : BOOL ;
END_VAR
VAR
STATE_DEVICES_ARR : ARRAY[1..6] OF BOOL ;
REQUEST_DEVICES_ARR : ARRAY[1..6] OF BOOL ;
NUMBER_DEVICES_STATE : USINT ;
NUM_DEV_REAL : USINT ;
NUM_DEV_ON : USINT ;
DEVICES_TO_ON : USINT ;
DEVICES_TO_OFF : USINT ;
P_ON : USINT := 0 ;
P_OFF : USINT := 0 ;
COUNT : USINT ;
END_VAR
IF ENABLE = TRUE THEN
STATE_DEVICES_ARR[1] := STATE_DEVICE1;
STATE_DEVICES_ARR[2] := STATE_DEVICE2;
STATE_DEVICES_ARR[3] := STATE_DEVICE3;
STATE_DEVICES_ARR[4] := STATE_DEVICE4;
STATE_DEVICES_ARR[5] := STATE_DEVICE5;
STATE_DEVICES_ARR[6] := STATE_DEVICE6;
NUM_DEV_ON := 0;
FOR COUNT := 1 TO 6 DO
IF STATE_DEVICES_ARR[COUNT] = FALSE THEN
REQUEST_DEVICES_ARR[COUNT] := FALSE;
END_IF;
IF STATE_DEVICES_ARR[COUNT] = TRUE THEN
NUMBER_DEVICES_STATE := NUMBER_DEVICES_STATE + 1;
END_IF;
IF REQUEST_DEVICES_ARR[COUNT] = TRUE THEN
DEVICES_TO_ON := DEVICES_TO_ON + 1;
END_IF;
END_FOR;
IF POWER_REQUEST > NUM_DEV_ON THEN
DEVICES_TO_ON := POWER_REQUEST-NUM_DEV_ON;
DEVICES_TO_OFF := 0;
END_IF;
IF POWER_REQUEST < NUM_DEV_ON THEN
DEVICES_TO_ON := 0;
DEVICES_TO_OFF := NUM_DEV_ON-POWER_REQUEST;
END_IF;
IF POWER_REQUEST = NUM_DEV_ON THEN
DEVICES_TO_ON := 0;
DEVICES_TO_OFF := 0;
END_IF;
IF NUMBER_DEVICES_STATE = 0 THEN
DEVICES_TO_ON := 0;
END_IF;
(*===============================================================================================================*)
(*switches the devices on or off according to FIFO logic.*)
(*===============================================================================================================*)
IF DEVICES_TO_ON > 0 THEN (* check if a device was requested to connect*)
WHILE DEVICES_TO_ON > 0 DO (* as long as there are devices to be connected *)
P_ON := P_ON + 1; (* increase the "pointer" connect devices *)
IF P_ON > 6 THEN (* check if the pointer position is at the end of the device queue *)
P_ON :=1; (* if it is at the end, it returns to the start *)
END_IF;
IF STATE_DEVICES_ARR[P_ON] = TRUE THEN (* check if the device is available to be connected *)
REQUEST_DEVICES_ARR[P_ON] := TRUE; (* connect the device of position P_ON *)
DEVICES_TO_ON := DEVICES_TO_ON-1; (* decrements the number of devices to be connected *)
END_IF;
END_WHILE;
END_IF;
IF DEVICES_TO_OFF > 0 THEN (* check if you are asked to disconnect from some device *)
WHILE DEVICES_TO_OFF > 0 DO (* as long as there are devices to be switched off *)
P_OFF := P_OFF + 1; (* increments the "pointer" to turn off devices *)
IF P_OFF > 6 THEN (* check if the pointer position is at the end of the device queue *)
P_OFF :=1; (* check if the pointer position is at the end of the device queue *)
END_IF;
IF STATE_DEVICES_ARR[P_OFF] = TRUE THEN (* check if the device is available to be switched off *)
REQUEST_DEVICES_ARR[P_OFF] := FALSE; (* disconnect device from position P_OFF ​​*)
DEVICES_TO_OFF := DEVICES_TO_OFF-1; (* decrements the number of devices to be disconnected *)
END_IF;
END_WHILE;
END_IF;
(* I THINK THE BUG WAS HERE *)
REQUEST_DEVICE1 := REQUEST_DEVICES_ARR[1];
REQUEST_DEVICE2 := REQUEST_DEVICES_ARR[2];
REQUEST_DEVICE3 := REQUEST_DEVICES_ARR[3];
REQUEST_DEVICE4 := REQUEST_DEVICES_ARR[4];
REQUEST_DEVICE5 := REQUEST_DEVICES_ARR[5];
REQUEST_DEVICE6 := REQUEST_DEVICES_ARR[6];
END_IF;
IF ENABLE = FALSE THEN
REQUEST_DEVICE1 := FALSE;
REQUEST_DEVICE2 := FALSE;
REQUEST_DEVICE3 := FALSE;
REQUEST_DEVICE4 := FALSE;
REQUEST_DEVICE5 := FALSE;
REQUEST_DEVICE6 := FALSE;
END_IF;
;
There are many things to improve in your code. For instance:
IF REQUEST_DEVICES_ARR[COUNT] = TRUE THEN
DEVICES_TO_ON := DEVICES_TO_ON + 1;
END_IF;
This is senseless because right after it you override DEVICES_TO_ON and do not use it. So why would you set it?
Or you do this
IF POWER_REQUEST > NUM_DEV_ON THEN
DEVICES_TO_ON := POWER_REQUEST-NUM_DEV_ON;
DEVICES_TO_OFF := 0;
END_IF;
But nowhere before you set NUM_DEV_ON.
Or you have an input variable NUMBER_DEVICES but nowhere used in the code.
But Generally, you've chosen the wrong approach to the problem.
So, first of all, you have to create a type
TYPE MY_DEVICE: STRUCT
Available: BOOL; (* If a device is available *)
State: BOOL; (* Current device state *)
Queue: BOOL; (* What to do with device *)
END_STRUCT
END_TYPE
Then set global variables
VAR_GLOBAL
garDevices: ARARY[1.._DEVICE_NUM] OF MY_DEVICE; (* Comment *)
END_VAR
VAR_GLOBAL CONSTANT
_DEVICE_NUM: USINT := 6; (* Comment *)
END_VAR
This way you can change the number of devices by simply changing _DEVICE_NUM constant without changing the rest of the code.
Now your function
FUNCTION QUEUE_DEVICES: BOOL
VAR_INPUT
ENABLE : BOOL;
POWER_REQUEST : USINT;
END_VAR
VAR
iDeviceOnOff: INT;
usiCount: USINT;
usiCountOnDevices: USINT;
END_VAR
(* If not enabled, set all devices to turn off and quite function *)
IF NOT ENABLE THEN
FOR usiCount TO _DEVICE_NUM DO
garDevices[usiCount].Queue := FALSE;
END_FOR;
RETURN;
END_IF;
(* Count how many devices is on already *)
usiCountOnDevices := 0;
FOR usiCount := 1 TO _DEVICE_NUM DO
IF garDevices[usiCount].State THEN
usiCountOnDevices := usiCountOnDevices + 1;
END_IF;
END_FOR;
(* Find the difference between power request and power on.
Might be negative or positive *)
iDeviceOnOff := POWER_REQUEST - usiCountOnDevices;
FOR usiCount := 1 TO _DEVICE_NUM DO
(* If device is not available for turning on or off
continue to the other device *)
IF garDevices[usiCount].Available THEN
(* if iDeviceOnOff is positive, then we have to turn on devices *)
IF iDeviceOnOff > 0 AND NOT garDevices[usiCount].Queue THEN
garDevices[usiCount].Queue := TRUE;
iDeviceOnOff := iDeviceOnOff - 1;
END_IF;
(* if iDeviceOnOff is negative we have to turn OFF devices *)
IF iDeviceOnOff < 0 AND garDevices[usiCount].Queue THEN
garDevices[usiCount].Queue := FALSE;
iDeviceOnOff := iDeviceOnOff + 1;
END_IF;
(* If iDeviceOnOff is 0 means balance is reached *)
IF iDeviceOnOff = 0 THEN
EXIT;
END_IF;
END_IF;
END_FOR;
END_FUNCTION
Then, you can add some other tests at the end of the function. For example.
IF iDeviceOnOff > 0 THEN
_ERROR: = 'More power requested than available devices';
END_IF;
IF iDeviceOnOff < 0 THEN
_ERROR: = 'There is a power excess';
END_IF;
Programming in a PLC is a bit different than in a normal OS application where the application runs once. It looks to me like you have alot of while and for loops, and I don't think these would be necessary.
Looks, to me, like your program could benefit from the use of a state machine. You can also use enumerated types to improve readability of state machines. This is a simple concept used to control sequences of events in a PLC. Simple State Machine example:
PROGRAM MAIN
VAR
bPizzaToCook : BOOL;
bPizzaCooking : BOOL;
bPizzaCooked : BOOL;
bLoadPizza : BOOL;
bUnloadPizza : BOOL;
fb_t_CookTimer : TON;
iPizzasCooked : UDINT;
bBuzzer : BOOL;
iPizzaState : DINT;
sPizzaState : STRING;
END_VAR
IF bPizzaToCook AND NOT bPizzaCooking THEN
//simulates conveyor moving pizza into oven
bPizzaToCook := FALSE;
bPizzaCooking := TRUE;
END_IF
IF bLoadPizza THEN //pizza loaded onto conveyor
bLoadPizza := FALSE;
bPizzaToCook := TRUE;
END_IF
IF bUnloadPizza THEN //pizza unloaded off of conveyor
bUnloadPizza := FALSE;
bPizzaCooked := FALSE;
END_IF
CASE iOvenState OF
0 : //wait for pizza to cook
sPizzaState := ‘Waiting for pizza…’;
IF bPizzaCooking THEN
iPizzaState := 10;
END_IF
10: //cook the pizza (start the timer)
sPizzaState := ‘Baking Pizza…’
fb_t_CookTimer(INT := TRUE, PT := T#10s);
IF fb_t_CookTimer.Q THEN
fb_t_CookTimer(IN := FALSE);
iPizzaState := 20;
END_IF
20: //is there space to move pizza out of oven?
IF NOT bPizzaCooked THEN
bPizzaCooking := FALSE;
bPizzaCooked := TRUE;
bBuzzer := FALSE;
iPizzaState := 30;
ELSE //pizza burning
sPizzaState := ‘BURNING’;
bBuzzer := TRUE;
END_IF
30://pizza ready on conveyor
iPizzasCooked := iPizzasCooked + 1;
iPizzaState := 0; //reset state
ELSE
sPizzaState := ‘Invalid State!’;
END_CASE
Plenty of other state machine examples out there. This one is from https://www.youtube.com/watch?v=XmcXRZXPRWs

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