Conditional set in Zimpl? - scip

I want to do this in Zimpl, but I get the following error after I run zimpl test.zpl:
set A := { 1..6 };
set B := { 1..4 };
set dim[A] := <1> {1}, <2> {1}, <3> {3}, <4> {2}, <5> {1}, <6> {4};
set C := {<i, j> in A*B with j<dim[i]};
Error 800: File test.zpl Line 4 : syntax error, unexpected SETSYM
set C := { in A*B with j < dim[i]};
Basically I want these elements from A*B:
{<1,1>,<2,1>,<3,1>,<3,2>,<3,3>,<4,1>,<4,2>,<5,1>,<6,1>,<6,2>,<6,3>,<6,4>}
Any idea why this is happening and how to get around it?

You need to define dim as a parameter instead of a set. Your example should look like:
set A := { 1..6 };
set B := { 1..4 };
param dim[A] := <1> 1, <2> 1, <3> 3, <4> 2, <5> 1, <6> 4;
set C := {<i, j> in A*B with j < dim[i]};
do print C;

Related

How can I add the XML tag and data to an existing XML in PL-SQL?

The xmldom usage in the package is as follows. I specify the next line to be added. But I can't find a solution on how to convert the line to xmldom format and add it.
vc_xml := '<'||cs_KEY_TAG||'>'|| pin_xml ||'</'||cs_KEY_TAG||'>';
parser := xmlparser.newParser;
xmlparser.parseclob(parser, vc_xml);
doc := xmlparser.getDocument(parser);
nl1 := xmldom.getElementsByTagName(doc, cs_KEY_TAG);
node := xmldom.item(nl1, 0);
nl2 := XMLDOM.getchildnodes(node);
len1 := xmldom.getLength(nl2);
FOR i in 0..len1-1 LOOP
node := xmldom.item(nl2, i);
nl3 := xmldom.getchildnodes(node);
len2 := xmldom.getLength(nl3);
FOR j in 0..len2 -1 LOOP
node := xmldom.item(nl3, j);
element := xmldom.makeElement(node);
node := xmldom.getFirstChild(node);
'<device_count>' || vs_d_count || '</device_count>' ->this is the line to add to the XML

csv file is not updating while saving data from TwinCat

I have created a plc program in TwinCat and its saving data into csv file. But variable values are not updating. Only one value is repeating in csv file again and and again. Where I am doing wrong !!! Here is my code:
PROGRAM MAIN
VAR
// Open, close and write function block
fbFileOpen: FB_FileOpen;
fbFileClose: FB_FileClose;
fbFileWrite: FB_FileWrite;
fbFormatString2: FB_FormatString2;
fbGetTime: NT_GetTime;
//file variables
nState : INT := 0;
nCounter : LREAL :=0;
hFile: UINT;
sPathName : T_MaxString;
sWriteBuffer : STRING(5000);
sBufferTemp : STRING(1000);
bBufferTemp : BOOL;
sFormat : STRING(255);
//General Variables
bFill: BOOL;
bWrite: BOOL;
rTimestamp : LREAL;
rCurrent: LREAL;
rActPos: LREAL;
nTimeMilli: INT;
i: ULINT;
END_VAR
// Input values
//------------------------------------------------------------------------------------------------------------------------
rTimestamp:= ULINT_TO_LREAL(F_GetSystemTime());
nCounter:= nCounter+1;
rCurrent:= (nCounter+1)/100;
rActPos:= (nCounter+1)/200;
IF bFill THEN
FOR i :=0 TO 10000000 BY 1 DO
GVL.arrLog[i].rTimestamp := rTimestamp;
GVL.arrLog[i].rCurrent := rCurrent;
GVL.arrLog[i].rActPos := rActPos;
END_FOR
END_IF
// Function Block for Current Date and Time
//------------------------------------------------------------------------------------------------------------------------
IF fbGetTime.START AND NOT fbGetTime.BUSY THEN // simple flip flop for quick update of time
fbGetTime.START := FALSE;
ELSE
fbGetTime.START := TRUE;
END_IF
fbGetTime(
NETID:= ,
START:= ,
TMOUT:= ,
BUSY=> ,
ERR=> ,
ERRID=> ,
TIMESTR=> ); // The TIMESTR is used to get times and dates
//Case Statements that will handle sequence of writing
//------------------------------------------------------------------------------------------------------------------------
CASE nState OF
0: //Wait for write trigger
IF bWrite THEN
nState := 10;
bWrite := FALSE;
END_IF
10: //Create file path and file using date
sPathName := CONCAT('D:\Data\', WORD_TO_STRING(fbGetTime.TIMESTR.wYear));
sPathName := CONCAT(sPathName,'_');
sPathName := CONCAT(sPathName,WORD_TO_STRING(fbGetTime.TIMESTR.wMonth));
sPathName := CONCAT(sPathName,'_');
sPathName := CONCAT(sPathName,WORD_TO_STRING(fbGetTime.TIMESTR.wDay));
sPathName := CONCAT(sPathName,'_');
sPathName := CONCAT(sPathName,WORD_TO_STRING(fbGetTime.TIMESTR.wHour));
sPathName := CONCAT(sPathName,'_');
sPathName := CONCAT(sPathName,WORD_TO_STRING(fbGetTime.TIMESTR.wMinute));
sPathName := CONCAT(sPathName,'_Datalog.csv');
nState:= 20;
fbFileOpen.bExecute := TRUE;
20: //Open and wait for file to open
fbFileOpen.bExecute := TRUE;
IF NOT fbFileOpen.bBusy AND NOT fbFileOpen.bError THEN
fbFileOpen.bExecute := FALSE;
nState := 30;
END_IF
30: // Write contents in file
sWriteBuffer := 'Name, fCurrentScaled, fActPos $n';
sFormat := '%F, %F, %F $n';
nTimeMilli := WORD_TO_INT(fbGetTime.TIMESTR.wMilliseconds);
IF WORD_TO_STRING(fbGetTime.TIMESTR.wMinute) <> INT_TO_STRING(40) THEN
FOR nTimeMilli:= 0 TO 999 BY 1 DO
fbFormatString2(
pFormatString:= ADR(sFormat),
arg1:= F_LREAL(GVL.arrLog[i].rTimestamp),
arg2:= F_LREAL(GVL.arrLog[i].rCurrent),
arg3:= F_LREAL(GVL.arrLog[i].rActPos),
pDstString:= ADR(sWriteBuffer),
nDstSize:= SIZEOF(sWriteBuffer),
bError=> ,
nErrId=> );
bBufferTemp := CONCAT2(pSrcString1 := ADR(sWriteBuffer),
pSrcString2 := ADR(sBufferTemp),
pDstString:= ADR(sWriteBuffer),
nDstSize := SIZEOF(sWriteBuffer));
END_FOR
ELSE
nState := 40;
fbFileWrite.bExecute := TRUE;
END_IF
40: // Write data in file and Wait for writing in the file
fbFileWrite.bExecute := TRUE;
IF NOT fbFileWrite.bBusy AND NOT fbFileWrite.bError THEN
fbFileWrite.bExecute := FALSE;
nState := 50;
fbFileClose.bExecute := TRUE;
END_IF
50: // close file and wait for it to close
fbFileClose.bExecute := TRUE;
IF NOT fbFileClose.bBusy AND NOT fbFileClose.bError THEN
fbFileClose.bExecute := FALSE;
nState:= 0;
END_IF
END_CASE
// FunctionBlocks for OPEN, WRITE and CLOSE
//------------------------------------------------------------------------------------------------------------------------
fbFileOpen(
sNetId:= '', //The netID does not need to be specified for local system
sPathName:= sPathName,
nMode:= FOPEN_MODEAPPEND OR FOPEN_MODEPLUS, // Open empty file for both read and write. If file exists then its content are destroyed
ePath:= PATH_GENERIC,
bExecute:= ,
tTimeout:= ,
bBusy=> ,
bError=> ,
nErrId=> ,
hFile=> hFile); // This file handle will be same for all function blocks.
fbFileClose(
sNetId:= '',
hFile:= hFile,
bExecute:= ,
tTimeout:= ,
bBusy=> ,
bError=> ,
nErrId=> );
fbFileWrite(
sNetId:= '',
hFile:= hFile,
pWriteBuff:= ADR(sWriteBuffer), // A pointer is used to get address
cbWriteLen:= SIZEOF(sWriteBuffer), // Needs to know to size of string going to be written
bExecute:= ,
tTimeout:= ,
bBusy=> ,
bError=> ,
nErrId=> ,
cbWrite=> );
I have created a counter and divided into smaller portion so that I get several values for one second. Is there update syntax I am missing?
The problem would appear to be in your step 30. It looks like you're trying to execute the file write during the 40th minute of the hour, and the rest of the time you're assembling the string to be written. But you have the line sWriteBuffer := 'Name, fCurrentScaled, fActPos $n'; at the beginning of step 30, outside of the conditional block, so it will execute every time.
When the 40th minute occurs and you move to the next step to do the file write, you're still executing that line which will overwrite whatever was put into sWriteBuffer by fbFormatString2. I think the assignment statements for sWriteBuffer and sFormat should be inside the IF statement.
Also, I don't know why you have the line nTimeMilli := WORD_TO_INT(fbGetTime.TIMESTR.wMilliseconds); when you're using nTimeMilli as the index variable in your FOR loop. Is that supposed to be i?
You don't show how bWrite is activated, which starts the state machine, but what if it happened to be triggered right on the 40th minute? Your step 30 would go immediately into the file write without assembling the values to be written.

InnoSetup timer block all page

I need to get the output of the execution of my script and integrate it into the installer, everything works well, but I need to add a "cancel" button, it will not be difficult to add a button, but during the execution of the timer, the entire installer window is blocked, but I can not minimize to tray and press any button.
function SetTimer(
Wnd: LongWord; IDEvent, Elapse: LongWord; TimerFunc: LongWord): LongWord;
external 'SetTimer#user32.dll stdcall';
function KillTimer(hWnd: LongWord; uIDEvent: LongWord): BOOL;
external 'KillTimer#user32.dll stdcall';
var
ProgressFileName: string;
function BufferToAnsi(const Buffer: string): AnsiString;
var
W: Word;
I: Integer;
begin
SetLength(Result, Length(Buffer) * 2);
for I := 1 to Length(Buffer) do
begin
W := Ord(Buffer[I]);
Result[(I * 2)] := Chr(W shr 8); { high byte }
Result[(I * 2) - 1] := Chr(Byte(W)); { low byte }
end;
end;
procedure UpdateProgress;
var
S: AnsiString;
I, L, Max: Integer;
Buffer: string;
Stream: TFileStream;
Lines: TStringList;
begin
if not FileExists(ProgressFileName) then
begin
Log(Format('Progress file %s does not exist', [ProgressFileName]));
end
else
begin
try
Stream := TFileStream.Create(ProgressFileName, fmOpenRead or fmShareDenyNone);
try
L := Stream.Size;
Max := 100*2014;
if L > Max then
begin
Stream.Position := L - Max;
L := Max;
end;
SetLength(Buffer, (L div 2) + (L mod 2));
Stream.ReadBuffer(Buffer, L);
S := BufferToAnsi(Buffer);
finally
Stream.Free;
end;
except
Log(Format('Failed to read progress from file %s - %s', [
ProgressFileName, GetExceptionMessage]));
end;
end;
if S <> '' then
begin
Log('Progress len = ' + IntToStr(Length(S)));
Lines := TStringList.Create();
Lines.Text := S;
for I := 0 to Lines.Count - 1 do
begin
if I < ProgressListBox.Items.Count then
begin
ProgressListBox.Items[I] := Lines[I];
end
else
begin
ProgressListBox.Items.Add(Lines[I]);
end
end;
ProgressListBox.ItemIndex := ProgressListBox.Items.Count - 1;
ProgressListBox.Selected[ProgressListBox.ItemIndex] := False;
Lines.Free;
end;
ProgressPage.SetProgress(0, 1);
end;
procedure UpdateProgressProc(
H: LongWord; Msg: LongWord; Event: LongWord; Time: LongWord);
begin
UpdateProgress;
end;
procedure RunInstallBatInsideProgress;
var
ResultCode: Integer;
Timer: LongWord;
AppPath: string;
AppError: string;
Command: string;
begin
ProgressPage :=
CreateOutputProgressPage(
'Installing something', 'Please wait until this finishes...');
ProgressPage.Show();
ProgressListBox := TNewListBox.Create(WizardForm);
ProgressListBox.Parent := ProgressPage.Surface;
ProgressListBox.Top := 0;
ProgressListBox.Left := 0;
ProgressListBox.Width := ProgressPage.SurfaceWidth;
ProgressListBox.Height := ProgressPage.SurfaceHeight;
ProgressPage.ProgressBar.Top := -100;
try
Timer := SetTimer(0, 0, 250, CreateCallback(#UpdateProgressProc));
AppPath := ExpandConstant('{app}\installer.py');
ProgressFileName := ExpandConstant('{app}\tmp\installer-progress.log');
Log(Format('Expecting progress in %s', [ProgressFileName]));
Command := Format('""%s" > "%s""', [AppPath, ProgressFileName]);
if not Exec(ExpandConstant('{cmd}'), '/c ' + Command + ExpandConstant(' {app}\PIPE'), '', SW_HIDE, ewWaitUntilTerminated, ResultCode) then
begin
AppError := 'Cannot start app';
end
else
if ResultCode <> 0 then
begin
AppError := Format('App failed with code %d', [ResultCode]);
end;
UpdateProgress;
finally
KillTimer(0, Timer);
ProgressPage.Hide;
DeleteFile(ProgressFileName);
ProgressPage.Free();
end;
if AppError <> '' then
begin
RaiseException(AppError);
end;
end;
procedure CurStepChanged(CurStep: TSetupStep);
begin
if CurStep=ssPostInstall then
begin
RunInstallBatInsideProgress;
end
end;
enter image description here

How to sum 1+2^2+3^3+...n^n in pascal?

I want to sum that thing but only use the 'for loop' (no power functions). I've already created a loop that generates powers:
Program powers;
Var
i, n, result : integer;
writeln('enter N'), read(n);
BEGIN
Result := 1;
for i := 1 to n do
begin
Result := Result * n;
end;
writeln('result=',result);
END.
But I neither have any idea on how to make that code generate multiple powers ( this code only generates n^n) nor how to make a loop that sums them together.
It's best to break problems down into smaller problems. In this case, you may wish to have a pow function to handle the exponentiation for you.
Hopefully the pow function is acceptable if it's not a library function.
function pow(n, exp : integer) : integer;
var
i, result : integer;
begin
result := n;
for i := 2 to exp do
result := result * n;
pow := result;
end;
Then the main portion of your program is simpler.
program powers;
var
n, i, sum : integer;
function pow(n, exp : integer) : integer;
var
i, result : integer;
begin
result := n;
for i := 2 to exp do
result := result * n;
pow := result;
end;
begin
sum := 0;
write('enter N:');
readln(n);
for i := 0 to n do
sum := sum + pow(i, i);
writeln('result=', sum);
end.
Now that I finally understand what you're asking, please try this:
Program powers;
Var
i, j, n, p, result : integer;
BEGIN
write('enter N:');
readln(n);
Result := 0;
for i := 1 to n do
begin
p := 1;
for j := 1 to i do
p := p * i;
Result := Result + p;
end;
writeln('result=', result);
END.

Does HelpNDoc Pascal Script support structures?

I am trying to create a structure:
MyTopic
TopicID : String;
HelpID : Integer;
I wanted to create an array of these structures so I could sort them.
I have tried using this type / record syntax but it is failing.
Update
I defined this type and procedure:
type
TMyTopicRecord = record
idTopic : String;
idContextHelp : integer;
End;
procedure GetSortedTopicIDs(aTopics : array of String; size : Integer);
var
aMyTopicRecords : array of TMyTopicRecord;
temp : TMyTopicRecord;
iTopic, i, j : Integer;
begin
// Init the array
SetLength(aMyTopicRecords, size);
// Fill the array with the existing topid ids.
// Get the context ids at the same time.
for iTopic := 0 to size - 1 do
aMyTopicRecords[iTopic].idTopic := aTopics[iTopic];
aMyTopicRecords[iTopic].idContextHelp := HndTopics.GetTopicHelpContext(aTopics[iTopic]);
// Sort the array on context id
for i := size-1 DownTo 1 do
for j := 2 to i do
if (aMyTopicRecords[j-1].idContextHelp > aMyTopicRecords[j].idContextHelp) Then
begin
temp := aMyTopicRecords[j-1];
aMyTopicRecords[j-1] := aMyTopicRecords[j];
aMyTopicRecords[j] := temp;
end;
// Rebuild the original array of topic ids
for iTopic := 0 to size - 1 do
aTopics[iTopic] := aMyTopicRecords[iTopic].idTopic;
end;
The procedure gets called in a loop of the parent function (code snipped):
function GetKeywordsAsHtml(): string;
var
aKeywordList: THndKeywordsInfoArray;
aAssociatedTopics: array of string;
nBlocLevel, nDif, nClose, nCurKeywordLevel, nCurKeywordChildrenCnt: Integer;
nCurKeyword, nCurKeywordTopic: Integer;
nCountAssociatedTopics: Integer;
sCurrentKeyword, sKeywordLink, sKeywordRelated: string;
sKeywordJsCaption: string;
begin
Result := '<ul>';
nBlocLevel := 0;
try
aKeywordList := HndKeywords.GetKeywordList(False);
for nCurKeyword := 0 to length(aKeywordList) - 1 do
begin
sCurrentKeyword := aKeywordList[nCurKeyword].id;
nCurKeywordLevel := HndKeywords.GetKeywordLevel(sCurrentKeyword);
nCurKeywordChildrenCnt := HndKeywords.GetKeywordDirectChildrenCount(sCurrentKeyword);
sKeywordLink := '#';
sKeywordRelated := '[]';
aAssociatedTopics := HndTopicsKeywords.GetTopicsAssociatedWithKeyword(sCurrentKeyword);
nCountAssociatedTopics := Length(aAssociatedTopics);
if nCountAssociatedTopics > 0 then
begin
GetSortedTopicIDs(aAssociatedTopics, nCountAssociatedTopics);
// Code snipped
end;
end;
finally
Result := Result + '</ul>';
end;
end;
The script compiled in the HelpNDoc internal editor with no issues. But when I go to actually build my HTML documentation I encounter a problem:
The HelpNDoc API is explained here.
Is there something wrong with my code?
I decided to go about it a different way and used a simpler technique:
procedure GetSortedTopicIDs(var aTopics : array of String; iNumTopics : Integer);
var
iTopic : Integer;
// List of output
aList: TStringList;
begin
// Init list
aList := TStringList.Create;
// Build a new array of "nnn x"
// - nnn is the help context id
// - x is the topid id
// Note: I know that the context ID values are within the range 0 - 200
for iTopic := 0 to iNumTopics - 1 do
// We pad the context id with 0. We could increase the padding width to
// make the script mre useful
aList.Add(Format('%0.3d %s', [
HndTopics.GetTopicHelpContext(aTopics[iTopic]),
aTopics[iTopic]
]));
// Now we sort the new array (which basically sorts it by context id)
aList.Sort;
// Update original array
for iTopic := 0 to iNumTopics - 1 do
// We ignore the "nnn " part of the string to get just the topic id
aTopics[iTopic] := copy(aList[iTopic],5, length(aList[iTopic])-4);
// Tidy up
aList.Free;
end;
This compiles and I get the sorted array of topic IDs at the end of it. So the pop-up help is now listed as I want.