Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » 4DIAC - Framework for Distributed Industrial Automation and Control » How to create IEC 61131-3 callable function in 4diac?
How to create IEC 61131-3 callable function in 4diac? [message #1863567] Sat, 10 February 2024 17:28 Go to next message
Ketut Kumajaya is currently offline Ketut KumajayaFriend
Messages: 24
Registered: January 2024
Junior Member
IEC 61131-3 SECANTF function:
FUNCTION SECANTF : REAL
  VAR_INPUT
    X : REAL;
    Y : REAL;
    P : UINT;
  END_VAR

  (* Ketut Kumajaya, 20/08/2018 *)
  (* Reference: https://lar.bnl.gov/properties/basic.html *)
  (* Reference: http://edge.rit.edu/edge/P07106/public/Nox.pdf *)

  IF (P = 3) THEN SECANTF := -Y + 4.863 * EXP((-5.9409785*(1.0 - X) + 1.3553888*EXPT(1.0 - X,1.5) - 0.46497607*EXPT(1.0 - X,2) - 1.5399043*EXPT(1.0 - X,4.5))/X);
    ELSE IF (P = 5) THEN SECANTF := -Y + 7251.0 * EXP((-6.71893 * (1.0 - X) + 1.35966 * EXPT((1.0 - X),1.5) - 1.3779 * EXPT((1.0 - X),2.5) - 4.051 * EXPT((1.0 - X),5))/X);
      ELSE SECANTF := 0.0;
    END_IF;
  END_IF;
END_FUNCTION


IEC 61131-3 SECANT function than call SECANTF function:
FUNCTION SECANT : REAL
  VAR_INPUT
    X1 : REAL;
    X2 : REAL;
    E1 : REAL;
    Y1 : REAL;
    P1 : UINT;
  END_VAR
  VAR
    XM1 : REAL;
    X0 : REAL;
    C1 : REAL;
  END_VAR

  (* Ketut Kumajaya, 20/08/2018 *)
  (* Adapted from: https://www.geeksforgeeks.org/program-to-find-root-of-an-equations-using-secant-method/ *)

  XM1 := E1 + 0.1; (* make sure to initially enter the loop *)

  IF (SECANTF(X1,Y1,P1) * SECANTF(X2,Y1,P1) < 0.0) THEN
    (* repeat the loop until the convergence *)
    WHILE (ABS(XM1 - X0) >= E1) DO
      (* calculate the intermediate value *)
       X0 := (X1 * SECANTF(X2,Y1,P1) - X2 * SECANTF(X1,Y1,P1)) / (SECANTF(X2,Y1,P1) - SECANTF(X1,Y1,P1));

      (* check if x0 is root of equation or not *)
      C1 := SECANTF(X1,Y1,P1) * SECANTF(X0,Y1,P1);

      (* update the value of interval *)
      X1 := X2;
      X2 := X0;

      (* if x0 is the root of equation then break the loop *)
      IF (C1 = 0.0) THEN
        EXIT;
      END_IF;
      XM1 := (X1 * SECANTF(X2,Y1,P1) - X2 * SECANTF(X1,Y1,P1)) / (SECANTF(X2,Y1,P1) - SECANTF(X1,Y1,P1));
    END_WHILE;
    SECANT := X0;
  ELSE
    SECANT := 0.0;
  END_IF;
END_FUNCTION


IEC 61499 SECANTF FB:
ALGORITHM calculate
(* Ketut Kumajaya, 20/08/2018 *)
(* Reference: https://lar.bnl.gov/properties/basic.html *)
(* Reference: http://edge.rit.edu/edge/P07106/public/Nox.pdf *)

IF (QI = 3) THEN (* argon function *)
	OUT := -Y + 4.863 * EXP((-5.9409785 * (1.0 - X) + 1.3553888 * EXPT(1.0 - X, 1.5) - 0.46497607 * EXPT(1.0 - X, 2) - 1.5399043 * EXPT(1.0 - X, 4.5)) / X);
ELSE
	IF (QI = 5) THEN (* nitrous oxide function *)
		OUT := -Y + 7251.0 * EXP((-6.71893 * (1.0 - X) + 1.35966 * EXPT((1.0 - X), 1.5) - 1.3779 * EXPT((1.0 - X), 2.5)	- 4.051 * EXPT((1.0	- X), 5)) / X);
	ELSE
		OUT := 0.0;
	END_IF;
END_IF;
END_ALGORITHM


IEC 61499 SECANT FB that call SECANTF FB. SECANTF1 is a SECANTF function block type registered inside SECANT FB:
ALGORITHM calculate
(* Ketut Kumajaya, 20/08/2018 *)
(* Adapted from: https://www.geeksforgeeks.org/program-to-find-root-of-an-equations-using-secant-method/ *)

XM1 := E1 + 0.1; (* make sure to initially enter the loop *)

(* IF (SECANTF(QI, X1, Y1) * SECANTF(QI, X2, Y1) < 0.0) THEN *)
SECANTF1(QI := QI, X := X1, Y := Y1);
Z1 := SECANTF1.OUT;
SECANTF1(QI := QI, X := X2, Y := Y1);
Z1 := Z1 * SECANTF1.OUT;
IF (Z1 < 0.0) THEN
	(* repeat the loop until the convergence *)
	WHILE (ABS(XM1 - X0) >= E1) DO
		(* calculate the intermediate value *)
		(* X0 := (X1 * SECANTF(QI, X2, Y1) - X2 * SECANTF(QI, X1, Y1)) / (SECANTF(QI, X2, Y1) - SECANTF(QI, X1, Y1)); *)
		SECANTF1(QI := QI, X := X2, Y := Y1);
		Z1 := X1 * SECANTF1.OUT;
		SECANTF1(QI := QI, X := X1, Y := Y1);
		Z1 := Z1 - X2 * SECANTF1.OUT;
		SECANTF1(QI := QI, X := X2, Y := Y1);
		X0 := SECANTF1.OUT;
		SECANTF1(QI := QI, X := X1, Y := Y1);
		X0 := X0 - SECANTF1.OUT;
		X0 := Z1 / X0;

		(* check if x0 is root of equation or not *)
		(* C1 := SECANTF(QI, X1, Y1) * SECANTF(QI, X0, Y1) *)
		SECANTF1(QI := QI, X := X1, Y := Y1);
		C1 := SECANTF1.OUT;
		SECANTF1(QI := QI, X := X0, Y := Y1);
		C1 := C1 * SECANTF1.OUT;

		(* update the value of interval *)
		X1 := X2;
		X2 := X0;

		(* if x0 is the root of equation then break the loop *)
		IF (C1 = 0.0) THEN
			EXIT;
		END_IF;
		(* XM1 := (X1 * SECANTF(QI, X2, Y1) - X2 * SECANTF(QI, X1, Y1)) / (SECANTF(QI, X2, Y1) - SECANTF(QI, X1, Y1)); *)
		SECANTF1(QI := QI, X := X2, Y := Y1);
		Z1 := X1 * SECANTF1.OUT;
		SECANTF1(QI := QI, X := X1, Y := Y1);
		Z1 := Z1 - X2 * SECANTF1.OUT;
		SECANTF1(QI := QI, X := X2, Y := Y1);
		XM1 := SECANTF1.OUT;
		SECANTF1(QI := QI, X := X1, Y := Y1);
		XM1 := XM1 - SECANTF1.OUT;
		XM1 := Z1 / XM1;
	END_WHILE;
	OUT := X0;
ELSE
	OUT := 0.0;
END_IF;
END_ALGORITHM


Is my implementation above correct or is there any a simpler way? It looks complicated but works.

EDIT:
I can make a C function call inside exported FB easily but I just want to write my FB completely in ST language.

[Updated on: Sun, 11 February 2024 01:29]

Report message to a moderator

Re: How to create IEC 61131-3 callable function in 4diac? [message #1863571 is a reply to message #1863567] Sun, 11 February 2024 11:27 Go to previous messageGo to next message
Alois Zoitl is currently offline Alois ZoitlFriend
Messages: 1585
Registered: January 2014
Senior Member

I think having the code in ST is definitely the better solution then in C. Especially from a maintainability and portability perspective.

As there is lot of math involved I have a hard time to say if it is correct. But I would have some general observations that may help you making your code more readable:

- Functions and Function blocks may not have the same name
- if your function is only used by one function block you can define the function as method inside of the function block.
- It loooks like your SECANTF Function is rather expensive. From what I see you always invoke it with two different parameter sets. Doing this once before all your ifs and storing the results in two variables leads to better performance but also to better readable code.
- why is SECANTF an FB and not a method of SECANT FB? A method can access inputs and outputs of the FB. In your case this means you could avoid the QI parameter which again results in a bit better readability of your code.

I hope this helps.
Re: How to create IEC 61131-3 callable function in 4diac? [message #1863572 is a reply to message #1863571] Sun, 11 February 2024 17:59 Go to previous messageGo to next message
Ketut Kumajaya is currently offline Ketut KumajayaFriend
Messages: 24
Registered: January 2024
Junior Member
METHOD SECANT_FUNC : REAL
VAR_INPUT
	SECANT_FUNC_X : REAL;
END_VAR

(* Ketut Kumajaya, 20/08/2018 *)
(* Reference: https://lar.bnl.gov/properties/basic.html *)
(* Reference: http://edge.rit.edu/edge/P07106/public/Nox.pdf *)
IF (SECANT_F = 3) THEN (* argon function *)
	SECANT_FUNC := -SECANT_Y + 4.863 * EXP((-5.9409785 * (1.0 - SECANT_FUNC_X) + 1.3553888 * EXPT(1.0 - SECANT_FUNC_X,
	1.5) - 0.46497607 * EXPT(1.0 - SECANT_FUNC_X, 2.0) - 1.5399043 * EXPT(1.0 - SECANT_FUNC_X, 4.5)) / SECANT_FUNC_X);
ELSE
	IF (SECANT_F = 5) THEN (* nitrous oxide function *)
		SECANT_FUNC := -SECANT_Y + 7251.0 * EXP((-6.71893 * (1.0 - SECANT_FUNC_X) + 1.35966 * EXPT((1.0 - SECANT_FUNC_X)
		, 1.5) - 1.3779 * EXPT((1.0 - SECANT_FUNC_X), 2.5) - 4.051 * EXPT((1.0 - SECANT_FUNC_X), 5)) / SECANT_FUNC_X);
	ELSE
		SECANT_FUNC := 0.0; (* for future function *)

	END_IF;
END_IF;
END_METHOD

METHOD SECANT : REAL
VAR_INPUT
	SECANT_X1 : REAL;
	SECANT_X2 : REAL;
	SECANT_E : REAL;
END_VAR

VAR_TEMP
	SECANT_XM : REAL;
	SECANT_X0 : REAL;
	SECANT_C : REAL;
	SECANT_X11 : REAL;
	SECANT_X21 : REAL;
END_VAR

(* Ketut Kumajaya, 20/08/2018 *)
(* Adapted from: https://www.geeksforgeeks.org/program-to-find-root-of-an-equations-using-secant-method/ *)
SECANT_X11 := SECANT_X1; (* to avoid writing to input variable *)

SECANT_X21 := SECANT_X2; (* to avoid writing to input variable *)

SECANT_XM := SECANT_E + 0.1; (* make sure to initially enter the loop *)

IF (SECANT_FUNC(SECANT_X11) * SECANT_FUNC(SECANT_X21) < 0.0) THEN
	(* repeat the loop until the convergence *)
	WHILE (ABS(SECANT_XM - SECANT_X0) >= SECANT_E) DO
		(* calculate the intermediate value *)
		SECANT_X0 := (SECANT_X11 * SECANT_FUNC(SECANT_X21) - SECANT_X21 * SECANT_FUNC(SECANT_X11)) / (SECANT_FUNC(
		SECANT_X21)
			- SECANT_FUNC(SECANT_X11));

		(* check if SECANT_X0 is root of equation or not *)
		SECANT_C := SECANT_FUNC(SECANT_X11) * SECANT_FUNC(SECANT_X0);

		(* update the value of interval *)
		SECANT_X11 := SECANT_X21;
		SECANT_X21 := SECANT_X0;

		(* if SECANT_X0 is the root of equation then break the loop *)
		IF (SECANT_C = 0.0) THEN
			EXIT;
		END_IF;

		SECANT_XM := (SECANT_X11 * SECANT_FUNC(SECANT_X21) - SECANT_X21 * SECANT_FUNC(SECANT_X11)) / (SECANT_FUNC(
		SECANT_X21)
			- SECANT_FUNC(SECANT_X11));
	END_WHILE;
	SECANT := SECANT_X0;
ELSE
	SECANT := 0.0;
END_IF;
END_METHOD

ALGORITHM calculate
...
Y1 := Y1 + 1.01325; (* absolute pressure *)
SECANT_Y := Y1 / 10.0;
SECANT_F := 3;
Y1 := -273.15 + SECANT(0.55615813, 1.0, 0.000001) * 150.687;
...
END_ALGORITHM


My function block implementation much better now. I set SECANT_F and SECANT_Y variable first and then call SECANT method per as your recommendation. Thank you very much.

[Updated on: Mon, 12 February 2024 01:31]

Report message to a moderator

Re: How to create IEC 61131-3 callable function in 4diac? [message #1863573 is a reply to message #1863572] Sun, 11 February 2024 20:44 Go to previous message
Alois Zoitl is currently offline Alois ZoitlFriend
Messages: 1585
Registered: January 2014
Senior Member

Great! Happy to help!
Previous Topic:Develop 4diac FORTE on Visual Studio Code
Next Topic:BeagleBone Black Industrial
Goto Forum:
  


Current Time: Sun Apr 28 00:56:39 GMT 2024

Powered by FUDForum. Page generated in 0.04073 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software

Back to the top