Codesys add elements to a derived struct - struct

Is it possible to add elements to an instance of struct.
TYPE StructBase:
STRUCT
Start : INT;
Complete : INT;
END_STRUCT
END_TYPE
StructDerived : StructBase;
StructDerived.StateInit : INT;
StructDerived.StateMoveFwd : INT;
The elements of StructDerived are going to be numbered in an 'enum' function.
On completion the StructDerived elements are used for a case statement.
The idea is to have a complete abstract function ready with the basic functionalities i require. When this function is extended, it should be clear where what to add. so all units in the machine for example all have the same lay-out.
What i was looking into was something like this:
TYPE BaseState:
STRUCT
EnumVal : INT;
Name : STRING;
END_STRUCT
END_TYPE
TYPE StructBase:
STRUCT
StateArray : ARRY [..] OF BaseState;
END_STRUCT
END_TYPE
StructDerived : StructBase;
StructDerived[0].Name := 'StateInit';
StructDerived[1].Name := 'StateMoveFwd';
So if i would use it in a case:
CASE AbortingState OF
StructDerived[0].EnumVal:
....
The 'Name' is logged for state tracking. Looks like a lot of work where something like a list or dictionary would do the trick.
Thanks for any advice in this matter.

First, you cannot use strings in CASE. Only ANY_NUM or ANY_BYTE general types. So, if you want to use enumeration, you will use it like this.
TYPE EN_STEPS: (
enStateInit,
enStateMoveFwd
);
END_TYPE
TYPE BaseState:
STRUCT
EnumVal : INT;
Step : EN_STEPS;
END_STRUCT
END_TYPE
GLOBAL_VAR
StateArray : ARRAY [1..c_TOTAL] OF BaseState :=
(EnumVal := 0, Step := enStateInit),
(EnumVal := 0, Step := enStateMoveFwd);
END_VAR
GLOBAL_VAR CONSTANT
c_TOTAL: INT := 2;
END_VAR
Then in program
VAR
currentStep : EN_STEPS;
END_VAR
CASE currentStep OF
StateArray[0].Step:
//do something
END_CASE
But this is not exactly clear why you are doing this. Looks like there is not a practical application for that. You could simply do
TYPE EN_STEPS: (
enStateInit,
enStateMoveFwd
);
END_TYPE
VAR
currentStep : EN_STEPS;
END_VAR
CASE currentStep OF
enStateInit:
//do something
END_CASE
The same thing.
Second, if you want to make a universal function that accepts a different number of elements you must use pointers. This is a general answer, if you edit your question and describe what you want to make in general, I will edit my answer and extend it with a solution.

Structured Text is a strongly typed language and it is not possible to dynamically add elements to a struct at runtime.
A data structure is not a function,therefor you should design a Function Block instead of a Struct if you intend to have a
complete abstract function ready with the basic functionalities i
require
"case of" instructions have one conditional variable which is checked against one or many conditional instructions (integer literals).
You can't use variables as your conditional instructions.
You can use inheritance to extend Structs and Function Blocks in order to create a hierarchy of different modules and instatiate them in different occasions for different purposes.

Related

From VBA to Delphi conversion (Optional arguments issue)

At the moment I'm converting a project written in VBA to Delphi and have stumbled upon a problem with converting some Subs with Optional arguments.
Say, there is a Sub declaration (just an example, actual Subs have up to 10 optional parameters):
Sub SetMark
(x0 As Double, y0 As Double,
Optional TextOffset As Integer =5,
Optional TextBefore As String = "",
Optional Text As String = "",
Optional TextAfter As String = "mm",
Optional Color As String = "FFFFFF",
Optional ArrowPresent As Boolean = True)
That Sub subsequently can be called like this:
Call SetMark (15, 100,,,"135")
Call SetMark (100, 100, 8,, "My text here..", "")
'a lot of calls here
The Optional arguments are very flexible here, you can omit any of them, and you can assign a value to any of them as well. Unlike in Delphi.
Procedure SetMark
(x0: real; y0: real,
TextOffset: Integer =5;
TextBefore: ShortString = '';
Text: ShortString = '';
TextAfter: ShortString = 'mm';
Color: ShortString = 'FFFFFF';
ArrowPresent: Boolean = True);
It seems you cannot just make a copy of VBA call:
SetMark (15, 100,,,'135');// error here
So, the question is: is there any way to convert that Subs to Delphi procedures keeping the same flexibility in parameters?
My first idea was to use default parameters, but it doesn't work.
As for now it seems in Delphi I will have to pass all the parameters in the list with their values directly but that means a lot of work for reviewing and proper porting of VBA calls.
Any ideas?
Is there any way to convert the VBA subroutines to Delphi procedures, still keeping the same flexibility in parameters?
There is no way to achieve that – that flexibility to omit parameters, other than at the end of the list, simply does not exist.
For methods of automation objects, you can use named parameters, as described here: Named/optional parameters in Delphi? However, I very much recommend that you don't implement your classes as automation objects just to get that functionality.
Whenever you switch between languages, you will find differences that are inconvenient. That is inevitable. The best approach is to try to find the best way to solve the problem in the new language, rather than trying to force idioms from the old language into the new language.
In this case you might want to use overloaded functions or parameter objects as ways to alleviate this inconvenience.
Just to expand on the idea of refactoring to use a parameter object, you could declare a record like:
TSetMarkParams = record
x0 : double;
y0 : double;
TextOffset : integer;
TextBefore : string;
Text : string;
TextAfter : string;
Color : string;
ArrowPresent : boolean;
constructor Create(Ax0, Ay0 : double);
end;
And implement the constructor to populate default values as :
constructor TSetMarkParams.Create(Ax0, Ay0 : double);
begin
x0 := Ax0;
y0 := Ay0;
TextOffset := 5;
TextBefore := '';
Text := '';
TextAfter := 'mm';
Color := 'FFFFFF';
AllowPresent := true;
end;
Your procedure would then have signature :
procedure SetMark(ASetMarkParams : TSetMarkParams);
Which you could then, using your example of SetMark (15, 100,,,'135'); call as :
var
LSetMarkParams : TSetMarkParams
begin
LSetMarkParams := TSetMarkParams.Create(15, 100);
LSetMarkParams.Text := '135';
SetMark(LSetMarkParams);
end;
As collateral benefit, the above is much more readable as it saves you from going blind trying to count commas when returning to debug a troublesome method call.

Cannot init struct in if construct in Go

I got a great surprise when I noticed the following snippet not compiling:
aTime := time.Time{}
if defaultTime := time.Time{} ; aTime != defaultTime {}
The compiler returns:
type time.Time is not an expression
defaultTime := time.Time used as
value undefined: defaultTime
The intent here is to test the aTime variable if it's set to it's default value.
It also does not compile if I get the pointer of the struct (defaultTime := &time.Time{}).
However, it does compile if I init defaultTime outside of the if construct, or do the init using the new() builtin:
aTime := time.Time{}
if defaultTime := new(time.Time) ; aTime != *defaultTime {}
From what I've read everywhere, new(myStruct) it supposed to be completely equivalent to &myStruct{}.
As I interprate it, defaultValue := time.Time{} qualifies as a SimpleStmt (specifically an Assignment), as detailed in the If statement spec.
I've come up with no explanation for this behavior, despite my best googling efforts. Would be grateful if someone could make my head stop spinning.
The { is recognized as the beginning of a Block, terminating the parsing of the SimpleStmt. After committing to that decision, the compiler decides that, as a SimpleStmt, aTime := time.Time isn't valid because time.Time isn't a value that can be assigned. However, it's presumably too late for the parser to try another interpretation of the {.
The version with new works because it doesn't contain a { character, and so avoids confusing the parser in this way.
You can also use the literal format by wrapping it in parentheses, because a block can't legally begin in the middle of an expression, so this also works:
if defaultTime := (time.Time{}); aTime != defaultTime {
// ...
}
gofmt gives the helpful message "expected boolean expression, found simple statement (missing parentheses around composite literal?)", but oddly, the go compiler itself does not.
I don't think what I am going to suggest is necessarily a better solution. But for your use case, you can try this to be concise
if !aTime.IsZero() {
// ...
}
Ymmv

Relational override on 'objects'?

I have a signature
sig Test {
a: Int,
b: Int,
c: Int
}
If I have two instances (atoms?) of this ( x,y:Test )
can I define a relation between these where only some parameters has changed without having to list all the other parameters as equal?
I want to avoid having to list all unchanged fields
as this can be error-prone assuming I have many fields.
Currently I am using x.(a+b+c) = y.(a+next[b]+c) but would like to use something like x = y ++ (b->next[y.b])
from what I understand about Alloy I think the answer is No: you cannot talk about all relations where some atom is involved in without explicitly naming these relations. But some experts may correct me if I'm wrong.

Alloy4.2 - Ternary relation to a binary relation

So I'm trying to write a function that takes a set of ternary relations and one of the middle elements which returns the set of relations where the element matches, but doesn't contain itself. (We already know what it is)
So something like this:
// addr gives us: {Book -> Name -> Addr}
fun [n:Name] : Set Book -> Addr {
//return {b->a} where {b->n->a}
}
With joins and domain restrictions, I've only been able to manage to get the binary relations: {Book -> Name} and {Name -> Addr}. I'm not sure how I would splice these together as the Name is constant, so you can't tell the difference.
Is it possible to do this with a function, or do I need something else?
I'm an absolute beginner at this, and it seems fairly simple in a normal procedural language. However, I can't find very good documentation and it looks to me I've just got completely the wrong end of the stick on terms of how functions work in this.
Or even more simply:
fun [n:Name]: Book -> Addr {
{b:Book,a:Addr | b->n->a in addr}
}
However, your use of the term "set of relations" and the keyword "set" in your function declaration makes me wonder if you mean something different. Note that this function returns a set of tuples, not a set of relations.
You can probably calculate that with a definition by comprehension:
fun [n:Name]: Book -> Addr {
{b:Book,a:Addr | b in (addr.a).n }
}

How to use sets of string in Pascal?

I am writing a little game in which the user is asked for their race and class.
There are five possible races of string[5] and four possible classes of string[9].
How do I program pascal to 1. define the five races and four classes as constants, 2. check the user input to see whether the input is within the possible races and classes - without using multiple IF statements?
Any hints will be appreciated.
Since your input is strictly defined, my first question is, must you use strings as the user input? Can you not give the user a choice? Say a drop down? Then you can map each choice to an enumeration.
type
race = (rcRace0, rcRace1, rcRace2 rcRace3, rcRace4);
case race(Input) of //input is an integer, index of the drop down list for example
rcRace0 : //perform processing for race 0
rcRace1 : //perform processing for race 1
rcRace2 : //perform processing for race 2
rcRace3 : //perform processing for race 3
rcRace4 : //perform processing for race 4
end;
Same for class.
I would recommend Steves solution as the starting point, but go a bit further with the use of enumerated types and sets...
type
TRace = (rcRace0, rcRace1, rcRace2, rcRace3, rcRace4);
TCharacterClass = (ccClass0, ccClass1, ccClass2, ccClass3);
TCharacterClassSet = set of TCharacterClass;
const
validCombinations : array[TRace] of TCharacterClassSet = (
[ccClass0, ccClass1, ccClass2, ccClass3], // race0 can be any class
[ccClass0, ccClass2], // race1
[ccClass0, ccClass1, ccClass2], // race2
[ccClass0, ccClass3], // race3
[ccClass0] // race4
);
You could also set up constants for the race names and character classes:
const
raceNames : array[TRace] of string = (
'Race 0',
'Race 1',
'Race 2',
'Race 3',
'Race 4'
);
characterClassNames = array[TCharacterClass] of string = (
'Class 0',
'Class 1 ',
'Class 2',
'Class 3'
);
Now, if you use comboboxes for user input and map the input to these enumerated types, the check if a combination is valid is simple:
function ValidRaceAndClass( aRace : TRace; aClass : TCharacterClass ) : Boolean;
begin
result := aClass in validCombinations[ aRace ];
end;
I find you should follow Steves solution. But if using strings is your way, you could use a TStringList (in Delphi/possibly FreePascal). You could fill it with your races and then evaluate the players answer using the IndexOf function of the TStringList. It returns the index of the passed string or -1 when the passed string isn't in the List.
Anyway i'd strongly recommend Steves solution :)

Resources