How do two parallel processes execute this mutual exclusion code? - operating-system

This is a solution for mutual exclusion problem from one of my exercises:
var blocked : array[0..1] of boolean; (* blocked is an array of two Boolean elements *)
turn : 0..1; (* turn can have value 0 or 1 *)
procedure P ( id : integer )
begin
repeat
blocked[id] := true;
while turn ≠ id do
begin
while blocked[1 – id] do; (* a do-nothing loop to implement busy waiting *)
turn := id
end;
< critical section >
blocked[id] := false;
< remainder of procedure >
until false
end;
begin (* main program *)
blocked[0] := false; blocked[1] := false;
turn := 0;
parbegin (* begin parallel execution of processes *)
P(0); P(1) (* invoke two concurrent processes, both to execute procedure P *)
Parend (* end parallel execution *)
end.
I am unable to understand when exactly a process executes particular statement in the code above. I know we can have multiple execution scenarios.
I want to know if this uncertainty of order of execution exists all the time.
And, is there any way to figure out what execution scenario will occur?
Thanks!

Related

TwinCAT - How to measure program execution time?

I would like to measure the execution time of a structured text (ST) program. The task associated with the program is running at 10 ms.
How do I measure execution time?
You can use free TwinCAT library Tc2_Utilities that has a function block Profiler.
https://infosys.beckhoff.com/english.php?content=../content/1033/tcplclib_tc2_utilities/35053195.html&id=1344160655692967299
The "Profiler" function block can be used to allow the execution time of PLC code to be measured.
The Infosys page has an example code also:
VAR
Profiler1 : PROFILER;
END_VAR
Profiler1(START := TRUE, RESET := TRUE);
//Do something here
Profiler1(START := FALSE);
//Now Profiler1.Data has the execution time
Of course, you can use Profiler but just for demonstration purpose you can measure execution time like this.
PROGRAM PLC_PRG
VAR
tStart: TIME; (* Time program start *)
tWork : TIME; (* Execution time *)
END_VAR
(* First line of main program *)
tStart := TIME();
// Your program here
(* Last line of your program *)
tWork := TIME() - tStart;
END_PROGRAM

Store variable value in each PLC cycle

Is it possible to store a variable value in each PLC cycle? I need the first 10 values each time to perform some calculations. I am using the OpenPCS platform and ST for programming.
You can create an array of values and then store as array values
VAR
aBuffer : ARRAY[1..32] OF WORD;
init:BOOL; (* Init array *)
rest:BOOL; (* Reset *)
val:WORD; (* Value *)
iCount:INT; (* Array index *)
END_VAR
VAR_TEMP
iTmp : INT;
END_VAR
iTmp := UINT_TO_INT(N) - 1;
IF NOT init OR rest THEN
init := TRUE;
FOR iCount := 1 TO iTmp DO
aBuffer[iCount] := val;
END_FOR;
END_IF
iCount := INC1(iCount, 32);
aBuffer[iCount] := val;
This is a code example that created 32 elements array and every new PLC cycle assign new element and rotates.
After that, you can calculate the average or min and max.
INC1 increments given value by one until it reaches 32 and then reset to 1.

TwinCAT CoE: Write to SDO

I'm pretty new to the EtherCAT/CANopen universe and trying to implement a custom slave.
The slave is passing conformance test so far and want to write to one of my Slave Data Objects, the slave is attached to a CX5120, which is found by the XAE and also shows the Slave device.
For that, I copied my ESI-file to the TwinCAT folder (C:\TwinCAT\3.1\Config\Io\EtherCAT).
I've created a small Structured Text PLC program that uses FB_EcCoESdoWrite to write data to address 0x607A. But when I set it active and try to connect, Visual Studio tells me that the device needs at least one Sync Master. Also, when setting bExecute to TRUE, I'm getting an error from the function. As far as I understand, I have to link variables between my ST program and the slave, but I don't see the need of linking variables because afaik the function call should manage the transmission? What are the steps to write to a SDO of an ESC? Can someone tell me what I'm missing or has a small example at hand?
PROGRAM MAIN
VAR
heartbeat : UINT;
fbSdoWrite : FB_EcCoESdoWrite;
sNetId : T_AmsNetId := '5.76.204.148.1.1'; (* NetId of EtherCAT Master *)
nSlaveAddr : UINT := 1001; (* Port Number of EtherCAT Slave *)
nIndex : WORD := 16#607A; (* CoE Object Index *)
nSubIndex : BYTE := 0; (* Subindex of CoE Object *)
nValue : UINT := 16#AAAA; (* variable to be written to the CoE Object *)
bExecute : BOOL; (* rising edge starts writing to the CoE Object *)
bError : BOOL;
nErrId : UDINT;
END_VAR
fbSdoWrite(
sNetId := sNetId,
nSlaveAddr := nSlaveAddr,
nIndex := nIndex,
nSubIndex := nSubIndex,
pSrcBuf := ADR(nValue),
cbBufLen := SIZEOF(nValue),
bExecute := bExecute
);
IF NOT fbSdoWrite.bBusy THEN
bExecute := FALSE;
IF NOT bError THEN
(* write successful *)
bError := FALSE;
nErrId := 0;
ELSE
(* write failed *)
bError := fbSdoWrite.bError;
nErrId := fbSdoWrite.nErrId;
END_IF
fbSdoWrite(bExecute := FALSE);
END_IF
Fixed problem by linking variable from PLC code to DevState-input of the device.
Linking to plain InfoData doesn't seem to work though.
You should assign a task to your devices who is responsible to read/write data. double click your master device, go to EtherCAT tab and click on Sync Unit Assignment
there select your terminals then available tasks and apply!

TwinCAT 3 Task Start/Stop from PLC

I need to run some code every time the PLC starts. This code should only be run once and then never again until the PLC is restarted. I initialize some global variables and validate the persistent data before allowing the main PLC to run. This is because the actions of the machine can be damaging if some of these variables are not setup correctly.
Is there a way to start/stop the other PLC tasks? I noticed TwinCAT doesn't support initialization and shutdown interrupts for PLC tasks.
TwinCAT has a 'PlcTaskSystemInfo' struct containing a boolean for FirstCycle. You can use that to run the initializing code only once.
VAR fbGetCurTaskIdx: GETCURTASKINDEX; (* Further example+explanation in Infosys *)
fbGetCurTaskIdx();
IF _TaskInfo[fbGetCurTaskIdx.index].FirstCycle THEN
(* Initialization code here *)
ELSE
(* Normal code here *)
END_IF;
I don't know of a way to start/stop individual PLC tasks. You can start/stop a runtime though.
But perhaps it can be as simple as this code below, which will only run when your PLC starts.
VAR initialized: BOOL := FALSE;
IF NOT initialized THEN
(* Run your initialization code here *)
initialized := TRUE;
END_IF
(* Rest of your program here *)
Edit:
I used a state machine inside the initialization code to help with the task allowed time issue.
Example:
VAR
Initialized : BOOL := FALSE;
Init_State : UINT := 0;
END_VAR
IF NOT Initialized THEN
(* Initialization State Machine *)
CASE Init_State OF
0: (* First step in initialization *)
Init_State := Init_State + 1;
1: (* Second step in initialization *)
Init_State := Init_State + 1;
.
.
.
n: (* Last step in initialization *)
Initialized := TRUE;
END_CASE
END_IF

How do I check if numbers are repeating in pascal?

I want to make a program which I will check if here are any Armstron numbers (numbers which are equal to the cubes of it's figures, for example 153).
Which are also perfect numbers (numbers equal to the sum of it's divisors, not including itself, for example 28 (1+2+4+7+14))
below 1000. So I made a program to see if a number is Armstrong number and to see if it's perfect number.
program Armstrong;
var i,n,j,d,s,p:integer;
begin
for i:=1 to 1000 do
begin
j:=i mod 10;
d:=i div 10 mod 10;
s:=i div 100;
n:=j*j*j+d*d*d+s*s*s;
if n=i then
writeln(i);
end;
end.
And for perfect numbers it's
program Perfect;
var n,s,i:integer;
begin
for n:=1 to 1000 do
begin
s:=0;
for i:=1 to n do
begin
if n mod i = 0 then
s:=s+i;
end;
if s=n then
writeln(n);
end;
end.
So I don't know how to merge them and see if any number I get from the first program appears in the second as well.
I did Pascal long time back. So assume I have used pseudo code for this answer.
Remove your loop from Armstrong and put the code in this function.
function Armstrong(i: integer): integer;
(* code goes here. And return 0 or 1 based on Armstrong *)
Remove your loop from Perfect, and put this code in this function
function Perfect(i: integer): integer;
(* code goes here. And return 0 or 1 based on Perfect *)
Then call it from a loop like this way:
for i:=1 to 1000 do
x = Armstrong(i);
y = Perfect(i);
if(x == 1 and y == 1) then
(* do something *)
end;