Strings in FB_init - twincat

Is there a way to declare a undefined length string as a constructor or a work around?
I need to write a log file for a function block.
The file name needs to be declared in the FB declaration but there are compiler errors which don't allow me to use strings as constructors.
FUNCTION_BLOCK FB_LogFile
VAR
sfileName : REFERENCE TO T_MaxString;
END_VAR
METHOD FB_init : BOOL
VAR_INPUT
bFileName: REFERENCE TO T_MaxString;
END_VAR
THIS^.sfileName:=sFileName;
PROGRAM Main
VAR
LogTest:FB_LogFile(sFileName:='c:/config/config.csv';
END_VAR
LogTest();

To answer the question you asked, If you want to pass in a string of an undefined length then the generally accepted method is using a pointer to the dataspace occupied by the string, and the length of the string.
Specifically:
VAR_INPUT
pString : POINTER TO BYTE; // Pointer to the first char of a string
nString : UDINT; // Length of the string
END_VAR
In regard to other points raised by your code, I believe the error you are specifically referring to is due to your reference handling.
When using references it is necessary to understand that they handle dereferencing differently to pointers. To quote InfoSys:
refA REF= stA; // represents => refA := ADR(stA);
refB REF= stB1; // represents => refB := ADR(stB1);
refA := refB; // represents => refA^ := refB^;
(value assignment of refB as refA and refB are implicitly dereferenced)
refB := stB2; // represents => refB^ := stB2;
(value assignment of stB2 as refB is implicitly dereferenced)
This applies to FB_Init as well so your code should actually read:
FUNCTION_BLOCK FB_LogFile
VAR
rFilePath: REFERENCE TO T_MaxString;
END_VAR
METHOD FB_init : BOOL
VAR_INPUT
rLocalPath : REFERENCE TO T_MaxString;
END_VAR
rFilePath REF= rLocalPath;

Related

How to throw an exception in TwinCAT

When using object-oriented programming aspects with Beckhoff TwinCAT 3 I recently tried to implement some Assert-like function which should throw an exception in case a guard clause evaluates to false.
Something like this:
FUNCTION GuardI
VAR_INPUT
Condition : BOOL;
Format : T_MaxString;
Value : INT;
END_VAR
IF (NOT Condition) THEN
ADSLOGDINT(
msgCtrlMask := ADSLOG_MSGTYPE_ERROR OR ADSLOG_MSGTYPE_LOG,
msgFmtStr := Format,
dintArg := Value);
// throw exception here
END_IF
So - how would I have it throw an exception?
I don't think it is possible to throw an exception from a quick look at the documentation. Note that exception handling (__TRY, __CATCH) only works for TwinCAT >= 4024.0 and for 32-bit systems.
You can do this like:
Create a Function called ASSERT
FUNCTION ASSERT
VAR_INPUT
E: BOOL;
END_VAR
VAR
EXCEPTION_THROWN, PURR: INT;
END_VAR
// body
IF NOT E THEN
EXCEPTION_THROWN := 0 / PURR;
END_IF
END_FUNCTION
Once you do this it will throw an exception guaranteed by Beckhoff because of the division by zero you would invoke this function like this (be sure that your are within the scope of that function via namespace or library reference):
PROGRAM MAIN
VAR
MEOW: INT; // zero by default
CONDITION: BOOL; // false by default
END_VAR
// body
IF MEOW = 1 THEN
ASSERT(FALSE);
END_IF
// ...
ASSERT(CONDITION); // key off your condition true or false
END_PROGRAM
Once you set "MEOW" to "1" or your condition evaluates to "FALSE" This will produce a Core Dump and you can load this core dump and review the call stack.
Hope this helps.

How to make inputs of a function block method optional?

When calling a method of a function block, is it possible to make certain input variables optional? If I call fbA.methA() without assignments for all input variables, TwinCAT throws an error: "Function methA requires exactly 'x' inputs." There are times when some inputs are unnecessary or irrelevant, but so far I've had to assign dummy values to those inputs to get the code to compile.
I don't think that that is possible. You could make extra methods which all call a base method.
For example:
FUNCTION_BLOCK Multiplier
METHOD Multiply : REAL
VAR_INPUT
number1 : REAL;
number2 : REAL;
END_VAR
METHOD MultiplyByTwo : REAL
VAR_INPUT
number : REAL;
END_VAR
MultiplyByTwo := Multiply(2, number);
That way you also reduce the number of inputs of your method, thereby making it easier to test and use.
You also could screen the parameters as they are passed in (still requires parameters but they have no meaning aka always pass "0").
FUNCTION_BLOCK CAT
METHOD DECIBELS: REAL
VAR_INPUT
MEOW, PURR: BOOL;
END_VAR
// body
DECIBELS := 0.0;
IF MEOW <> 0
DECIBELS := DECIBELS + 10.0;
END_IF;
IF PURR <> 0
DECIBELS := DECIBELS + 5.0;
END_IF;
END_METHOD
END_FUNCTION_BLOCK
you can invoke this like:
PROGRAM MAIN
VAR
C: CAT;
RESULT: ARRAY [1..4] OF REAL;
END_VAR
// body
RESULT[1] := C.DECIBELS(TRUE, TRUE); // will return 15.0
RESULT[2] := C.DECIBELS(TRUE, 0); // will return 10.0
RESULT[3] := C.DECIBELS(0, TRUE); // will return 5.0
RESULT[4] := C.DECIBELS(0, 0); // will return 0.0
END_PROGRAM
Hope this helps

Passing an array with integer values and char indexes to a function in Pascal

I really would like to see how it's done, the compiler keeps assuming I have integer indexes and returns errors.
How to pass the following array:
countc: Array['a'..'z'] of Integer;
to a function?
In traditional Pascal, before you can pass something like your array to a function, you have to declare a type that your array is an instance of, like this
type
TSimpleArray = Array['A'..'Z', '0'..'9'] of integer;
var
ASimpleArray : TSimpleArray;
In other words, you can't specify the array's bounds in the definition of the function/procedure.
Once you've defined your array type like the above, you can declare a function (or procedure) that has a parameter of the defined type, like this:
function ArrayFunction(SimpleArray : TSimpleArray) : Integer;
var
C1,
C2 : Char;
begin
ArrayFunction := 0;
for C1 := 'A' to 'Z' do
for C2 := '0' to '9' do
ArrayFunction := ArrayFunction + SimpleArray[C1, C2];
end;
which obviously totals the contents of the array.
More modern Pascals like Delphi and FPC's ObjectPascals also support other ways of declaring an array-type parameter, but they have to be zero-based (which precludes the use of char indexes). Delphi and FPC also support the use of `Result' as an alias for the function name, as in
function ArrayFunction(SimpleArray : TSimpleArray) : Integer;
[...]
begin
Result := 0;
which saves time and effort if you rename a function or copy/paste it to define another function.

Cannot find what is wrong with the array declaration

It says
fatal: syntax error, OF expected but [ found" for the variable "lo"
But I really can't see whats wrong with it.
I tried to change variable name but seems not working.
procedure reg( index, gen : char;
fname, sname, loginname, passwords, pid : string;
var lo : array [1..26,1..1025] of bucket ;
var main : array[1..1025] of detail);
var
convertedindex, i, j : integer;
found, found2 : boolean;
It's supposed to be without error but it says syntax error.
You can't define an array while you are in the middle of declaring the parameters of a procedure (or function). You need to define the array type beforehand by doing something like this instead:
program arraydecl;
type
Bucket = integer;
Detail = integer;
type
BucketArray = array [1..26,1..1025] of Bucket;
DetailArray = array[1..1025] of Detail;
procedure reg(index, gen : char; fname, sname, loginname, passwords, pid : string ; var lo : BucketArray; var main : DetailArray);
begin
end;
The error message says that the compiler expected the keyword of, but instead it found an opening bracket [.
The reason is (I guess) that in procedure declarations, you cannot define the bounds of an array. For example, you cannot say main: array[1..2] of integer, you can only say main: array of integer.
You can try to define an array type and then use that type as the procedure parameter:
type TwoInts = array[1..2] of integer;
procedure PrintTwoInts(ti: TwoInts)
begin
WriteLn(ti[1], ti[2])
end;
I haven't programmed in Pascal for a long time, so the above may or may not work. I don't remember whether ti would be passed by value or by reference, and whether the array indices inside the procedure would always start at 0. That's some things you would need to find out.
Parameter lists
In some Pascal versions, like FreePascal or Delphi, parameter lists of functions or procedures cannot contain type declarations, only type specifications.
So, to specify such an array as parameter, you must declare its type first, before the function/procedure declaration:
type
// Type declarations
Bucket = ...
Detail = ...
TBuckets = array[1..26, 1..1025] of Bucket;
TDetails = array[1..1025] of Detail;
procedure Reg(Index, Gen: Char; FName, SName, LoginName, Passwords, PID: string;
var Lo: TBuckets; var Main: TDetails);
Note that other Pascals (including ISO Pascal, if I remember correctly) do allow these ad hoc (on the spot) declarations, even in parameter lists. But obviously your dialect of Pascal doesn't.
Open array parameters
Now if you see a parameter specifications like x: array of Integer or similar, then you are dealing with open array parameters. This is not a declaration and it doesn't specify one single type, it accepts all kinds of one-dimensional arrays of that base type. More on that in my article Open array parameters and array of const.
This explains the error message: only of can follow array in a parameter list, to specify an open array parameter.
For what it's worth: if you are using FreePascal or Delphi, then you should get in the habit of passing strings as const, if possible: const FName, SName, etc...: string.

Pointer dereference in VHDL

I have not been able to understand how to dereference a pointer in VHDL.
What I have in mind is a C code like :
int a;
int* ptr_a;
a = 42;
ptr_a=&a;
*ptr_a=451;// how can I do this ?
I tried to mimick this code in VHDL :
ptr_test : process
type ptr_integer is access integer;
variable a : integer;
variable ptr_a : ptr_integer;
begin
a := 42;
ptr_a := new integer'(a);
report "ptr now points to a : ptr=" & str(ptr_a.all);
ptr_a.all := 451;
report "ptr modified : ptr=" & str(ptr_a.all);
report "a is NOT modified : a =" & str(a);
wait;
end process;
So how can I correctly modify a value through a pointer ?
You can't directly. Access types are not "just like pointers" - they are to at least some extent distinct types of data storage.
This line does not create a pointer to a:
ptr_a := new integer'(a);
It creates a data object with the same value as a and sets up ptr_a to reference it.
If you were to create another access type variable :
variable ptr_b : ptr_integer;
and set it to to point to ptr_a:
ptr_b := ptr_a;
then changes to ptr_b.all will reflect in ptr_a.all.
"new" is the equivalent of the (C++ rather than C) "new" operation; invoking a constructor allocating an integer on the heap and initialising it to "a". (Naturally, you can "deallocate" it when done)
What you are looking for is ptr_a := a'access; which is the Ada way of accessing a global or local (stack) variable via a pointer : this is only legal if said variable has been declared "aliased" alerting the compiler to the fact there may be more than one view of it (and thus, preventing some nice optimisations). In C, everything is "aliased" whether you want it or not.
This is one of the aspects of Ada that didn't make it through the simplification process into VHDL : and it's difficult to see a good use for it. So there isn't an exact equivalent in VHDL.
Martin's answer just popped up : as he says, you CAN have 2 or more pointers to the same heap object.
Alternatively, explain what you are trying to achieve this way; there may be a VHDL alternative way of doing it.

Resources