I'm trying to test an value coming from a TEdit, that needs to be converted to a Double. The problem I'm having is that the function TryStrToFloat keeps on returning false, no matter what I do.
var
str : String;
value : Double;
begin
str := '950.00';
value := 0;
if TryStrToFloat(str, value) then
result := value
else
begin
showMessage('Not a legal value');
result := 0;
end;
end;
for the example above I supplied the value '950.00' which I then test with the function, but I keep on getting the message that it is "not a legal value". Even if I send '950.00' from the TEdit, I still get the same problem. What am I missing?
You are missing the decimal separator which default to Windows own setting. For example, on the French Windows, the decimal separator is the coma.
Try like this:
var
str : String;
value : Double;
begin
FormatSettings.DecimalSeparator := '.';
str := '950.00';
value := 0;
if TryStrToFloat(str, value) then
ShowMessage('OK')
else
ShowMessage('Not a legal value');
end;
Related
I need to change the length of the array dynamically.Right now the code looks like this:
VAR
arrData : ARRAY[1..200] OF INT;
length : INT := 200;
END_VAR
The size of the array depends on the length variable.Here the value of length variable can be changed during runtime using the VISU(gui). So if I change the value of the length = 180 then 20 bytes of arrData are unused.Is there a way to declare the array as variable length similar to vectors in c++ such that the memory is not assigned during declaration but during runtime.
Edit:
How to deallocate the memory safely?
PROGRAM MAIN
VAR
arrData : POINTER TO INT;
length : INT := 200; // can be changed at runtime
bNew : BOOL := TRUE;
oldLength : INT; // to hold the old length variable
isInit : BOOL := FALSE;
END_VAR
IF NOT isInit THEN
oldLength := length; // initialise only once
isInit := TRUE;
END_IF
// if length is changed during runtime then delete the array
IF oldLength <> length THEN
IF arrData <> 0 THEN
__DELETE(arrData);
bNew := TRUE;
END_IF
oldLength := length;
END_IF
// during the first run or when the length is changed
IF bNew THEN
arrData := __NEW(INT,length);
bNew := FALSE;
END_IF
// deallocate the memory when the MAIN program goes out of scope
// as we are not deleting the array when the length variable is not
// changed during runtime
END_CASE
The way to do it is to use __NEW __NEW in Infosys
pMyPointer := __NEW(INT, length);
__NEW will return a pointer to first element of an array. You can access latter elements by offsetting this pointer.
You can check if length was changed by comparing value from this and previous cycle. If so, __DELETE the old array and initialize a new one.
Edit:
I think, that you get your error the moment TwinCAT runtime is stopped, as the momory allocated by __NEW is not freed at that point.
Your code should be placed not in a Program (PRG) but in a Function Block (FB). The reason for that is that you need to implement FB_exit method (This method is called implicitly when FB instance is destroyed, i.e. when stopping TwinCAT runtime like you do by activating configuration). There is no equivalent method for a PRG as far as I know.
To do this:
Create a new FB, instantiate it and call it in your MAIN and move your code from MAIN to the FB
Add a FB_exit method to this FB. Exact naming is crucial
In your FB_exit method write the following code:
IF __ISVALIDREF(arrData) THEN
__DELETE(arrData);
END_IF
This method will be called every time you stop your runtime and free the memory.
Links to Infosys:
__ISVALIDREF - equal to pMyPointer <> 0 but more readable
FB_exit
How can assign a new variable to an array in TwinCAT?
in TwinCAT you can initialize all your array's argument directly for example for array a we can use:
a : ARRAY [1..3] OF INT := [3(0)];
or
a : ARRAY [1..3] OF INT := [0,0,0];
but if you want to assign the array in the main program(not initializing part) for example
a:=[2,8,5];
you will face this error tip: Unexpected array initialisation.
any help would be appreciated.
You cannot directly initialize arrays inside the program part.
That beeing said, the best option is porbably to define a constant containing the desired initialization values and then assigning the value of that constant to your array:
VAR
aiMyArray : ARRAY [1..2] OF INT;
END_VAR
VAR CONSTANT
aiInitializerMyArrayOptionA : ARRAY [1..2] OF INT := [1,2];
aiInitializerMyArrayOptionB : ARRAY [1..2] OF INT := [3,4];
END_VAR
IF bCondition THEN
aiMyArray := aiInitializerMyArrayOptionA;
ELSE
aiMyArray := aiInitializerMyArrayOptionB;
END_IF
The other option would be to manually initialize each index one by one which easily gets impracticale with decent array sizes:
IF bCondition THEN
aiMyArray[1] := 1;
aiMyArray[2] := 2;
ELSE
aiMyArray[1] := 3;
aiMyArray[2] := 4;
END_IF
Looping thorugh all elements and assigning values might be an option too. But that would only be usefull when the values are no arbitrary constatns but you are able to calculate the values from some formula.
with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Characters.Handling;
with Ada.Exceptions; use Ada.Exceptions;
USE Ada, Ada.Text_Io;
WITH connectfour;
PROCEDURE Main IS
PACKAGE board is new connectfour;
USE board;
col : Character;
function checkInput (input : Character) RETURN BOOLEAN is
ins : Character := input;
begin
ins := Ada.Characters.Handling.To_Lower(ins);
if ins = 'a' or ins = 'b' or ins = 'c' or ins = 'd' or
ins = 'e' or ins = 'f' or ins = 'g' or ins = 'h' then
return true;
end if;
return false;
end checkInput;
begin
board.initialize;
board.print;
while (not board.isFull) loop
loop
PUT("Player"&Integer'Image(board.turn)&": ");
Ada.Text_IO.Get(col);
exit when checkInput(col);
end loop;
exit when col = '0';
Text_IO.New_Line;
Text_IO.Put ("");
board.play(col);
end loop;
end Main;
So when I run my program I get:
Player 1: a --> (I entered the character 'a' and clicked enter)
Then I keep getting this error right after, at the line "Ada.Text_IO.Get(col);"
raised ADA.IO_EXCEPTIONS.DATA_ERROR : a-tiinio.adb:86 instantiated at a-inteio.ads:18
What I want to do, is get a single character input from the user and check if it is within the range A .. H, if yes, then exit the loop, otherwise keep asking...
I cannot find out what my issue is...
I allow the user to enter lowercase or uppercase characters, and I convert uppercase to lowercase and perform a check.
Please help...
I am not sure how to read in a single Character....
Okay the exception message could be clearer (and there are ways to get stack traces when one happens but let's work with what we've got...)
locate a-tiinio.adb
/usr/lib/gcc/x86_64-linux-gnu/4.9/rts-native/adainclude/a-tiinio.adb
which is
package body Ada.Text_IO.Integer_IO
and line 86 is an exception raised in
procedure Get
(Item : out Num;
Width : Field := 0)
Details don't matter (yet) but I cannot see any calls to Ada.Text_IO.Integer_IO.Get.
So my suspicion is that the code shown is working
(as well as can be expected : NOTE HOWEVER there is no way to get to exit when col = '0'; with col outside a .. h) and there is another call to Get (this time Integer_IO.Get) buried in board.play. It would be easy to test this by printing col= before calling board.play.
Minor style comment:
function checkInput (input : Character) RETURN BOOLEAN is
ins : Character := input;
begin
ins := Ada.Characters.Handling.To_Lower(ins);
can be simplified
function checkInput (input : Character) RETURN BOOLEAN is
ins : Character := Ada.Characters.Handling.To_Lower(input);
begin
The exact question would be "is the equation you want to use x=f(Xo)". This is in an if statement already so if true then continue if not then prompt user to enter a different function.
Your bit about its already being inside an if statement isn't very workable because that doesn't allow for an alternative value to be assigned to something in the case that the initial response is negative.
You should be able to work with something like this. Call p(), and assign its result to a ans, say, and then work with that value (and/or test it for some properties).
restart:
p := proc()
local answer, oldprompt, res1, res2;
oldprompt := interface(':-prompt'=``);
try
printf("Is the equation you want to use x=f(Xo)? (y/n)\n");
res1 := readline(-1);
if member(res1,{"y;","y","yes;","yes"}) then
answer := x=f(Xo);
elif member(res1,{"n;","n","no;","no"}) then
printf("Enter your equation.\n");
res2 := readline(-1);
answer := parse(res2);
else
printf("Response not recognized\n");
end if;
catch:
finally
interface(':-prompt'=oldprompt);
end try;
if answer='answer' then NULL else answer end if;
end proc:
ans := p();
[edited below]
It is possible to get it a little closer to your original. With procedure p as below the returned result will be one of true/false/FAIL and could be used in a conditional. In the case that the return values if false (because of the response to the initial query) then a second query is made about the choice of another expression.
This version of p takes two arguments, the first is the suggested initial equation. The second is a name which can be assigned any alternative.
restart:
p := proc(candidate, resultvar)
local result, oldprompt, res1, res2;
oldprompt := interface(':-prompt'=``);
try
printf(sprintf("Is the equation you want to use %a? (y/n)\n",
candidate));
res1 := readline(-1);
if member(res1,{"y;","y","yes;","yes"}) then
result := true;
assign(resultvar,candidate);
elif member(res1,{"n;","n","no;","no"}) then
result := false;
printf("Enter your equation.\n");
res2 := readline(-1);
assign(resultvar,parse(res2));
else
printf("Response not recognized\n");
result := FAIL;
end if;
catch:
finally
interface(':-prompt'=oldprompt);
end try;
return result;
end proc:
Now we can test it out.
p(x=f(X0), 'ans');
ans;
We could also use the call to p inside an if statement. Eg,
if p(x=f(X0), 'ans') then
"accepted";
else
"new choice made";
end if;
ans;
Here, answering "n" to the first query will make the conditional test see a false value, but the named argument ans will get assigned to as a side-effect.
Now I build a template for our invoice printer.
But I do really not know, why it crashes without any error.
My goal is to separate the String ItemName at the ';' and print each part into a new line to a Memo1.
procedure DetailBeforeGenerate;
var
s1: String;
s2: String;
wordcount: Integer;
notelength: Integer;
begin
s1 := plPrintInvLine['ItemName'];
notelength := Length(s1);
while notelength > 0 do
begin
notelength := Length(s1);
wordcount := Pos(';' , s1);
s2 := Copy(s1, 0, wordcount-1);
Memo1.Lines.Add(s2);
Delete(s1, 0, wordcount);
end;
end;
See comments below regarding accessing index[0] in a string and thanks to David Heffernen and Ken White. BUT:
Looks like you've got an infinite loop in your code:
notelength := Length(s1);
while notelength > 0 do
begin
notelength := Length(s1);
wordcount := Pos(';' , s1);
s2 := Copy(s1, 0, wordcount-1);
Memo1.Lines.Add(s2);
Delete(s1, 0, wordcount);
end;
Delete(s1, 0, wordcount); Has no effect! Try it in Delphi debugger. Result? notelength is never decremented so you'll loop forever. ' Delete(s1, 0, wordcount);' does not blow up but neither does it delete. Use Delete(s1,1, wordcount) instead.
Index[0] in Delphi strings does not contain your character data - it's 'not accessible' according to the compiler, if you try compiling myString[0];
Also: the way your code is written, you MUST terminate with ';' or a string such as this:
s1 := 'mikey;was;here;a'; will loop infinitely on the last string after ';' ('a')
I also use ReportBuilder templates, etc: In Delphi itself you will not be able to compile MyString[0], but the copy and delete methods are protected from this error, (as David explained) however it appears from what I saw in the debugger that 'Delete(s1, 0, wordcount)' will not throw an exception but fails to delete. So I would not expect RBuilder to be any better, and perhaps worse - copy() may also be failing on string[0] in RAP.
RAP is NOT Delphi - it is a Runtime scripting environment that runs in your template, based on Object Pascal, but it does not support everything, and you cannot always expect it to behave exactly like Delphi.
BTW - ReportBuilder is now up to version 14.0X - if possible you should upgrade - there have been a lot of improvements in the RAP environment. In a later version your code might work OK or you'll get back an error message from RAP.
Also: If you want to debug in RAP it's not so easy. But to give you a clue as to where the error might be occurring, put a text label on your report and after each line of your code add
mylabel.caption:='statementxxx ran';
or
mylabel.caption:= myVariable.value;
Etc. That will give you a little ad hoc tracer - maybe show you where/why you failed, etc.
For all searching people: I found the solution with the excellent help of this community!
The working code looks like this:
procedure DetailBeforeGenerate;
var
S1: String;
S2: String;
wordcount: Integer;
notelength: Integer;
begin
S1 := plPrintInvLine['ArtName'];
notelength := Length(S1);
while (notelength > 0) do
begin
wordcount := Pos(';',S1);
S2 := Copy(S1, 1, wordcount-1);
if ( Pos(' ',S2) = 1 ) then Delete(S2, 1, 1);
Memo1.Lines.Add(S2);
Delete(S1, 1, wordcount);
notelength := notelength - wordcount;
end;
end;