Can I use Structured Text in Codesys to initialize a Global Variable List - codesys

I'm trying to get my head into the Codesys world and since I'm not a Menu-click type I'm wondering if there is a way to stick to the keyboard and for instance add a PersistentVarsList and Global Vars List via Structured Text in the Editor.

If by add PersistentVarsList/Global Vars list you mean to crate a new list inside the project from Structured Text code, then no. If you just have some variable list (non constant!) inside your project that you want to initialize in runtime (perhaps their values need to be calculated first), then you can have some code that only executes at the begining of the runtime:
PROGRAM POU_1
VAR
init: BOOL := FALSE;
END_VAR
IF (NOT init) THEN
myGVL.someVar := CALLCULATE_VAR();
IF (myPersist.positiveVar < 0) THEN
myPersist.positiveVar := -myPersist.positiveVar;
END_IF
// other initializations
init := TRUE;
END_IF

Related

How to declare local variable (Allen Bradley studio 5000)

How to declare local variable in structured Text in Allen Bradley studio 5000.
I have tried the following which did not work. mnemonic/syntax is not recognized?
VAR
x: INT;
END_VAR
global variables do work as, TEST_INPUT and TEST_INPUT_BOOL are globally declared.
IF TEST_INPUT.0 THEN
TEST_INPUT_BOOL := 1;
ELSE
TEST_INPUT_BOOL := 0;
END_IF;
In Allen-Bradley RSLogix 5000 / Studio 5000, local tags (variables) are not declared inline in structured text routines. Instead, tags are defined in the Tag Browser. "Controller Tags" holds the controller-scoped (global) tags, and each Program has its own "Parameters and Local Tags" section where the program-scoped tags are defined. Tags shown in the Local Tags section of a program can be used in any routine in that program.
Once you add your program-scoped tag to the appropriate Tag Browser, you can use the tag in your structured text routine. If you try to use a tag that is not declared as either a program-scoped or controller-scoped tag, you will get a verify error.

How to close (in code) a Form which the IDE has open, without closing its associated .Pas file

The code below is simplified from something I'm doing in a design-time .BPL for D7.
Update: Since posting this, I've found one way to do what I'm after, namely just send the form a WM_Close message, but I'd still be interested to know whether there's a more "official" way to do it, because using WM_Close seems like it has the potential for wrong-footing the IDE.
All I'm trying to do in this code that's causing me the problem is to close all files open in the IDE,
and then open a specific .Pas file that happens to have an associated .Dfm file. I don't want the form defined in the .Dfm to be open on-screen, so I'm trying to close it, without closing the .Pas file too - I just want the IDE Form designer and this form out of the way.
Eventually, I found out how to get at the form via OTA + NTA services in my .BPL's code and, naively but for want of any other obvious way to do it, I've tried calling .Close on it, by this snippet.
AForm := TForm(INTAComp.GetComponent);
AForm.Close;
However, the form does not close. I've traced into TCustomForm.Close from the CPU window
and evidently the reason it doesn't close is that its Visible property is already False. This is what evaluating Visible prior to AForm.Close returns, too.
Evaluation various other of its properties prior to AForm.Close tells me that
- its Owner is Nil BUT
- it has an apparently valid window handle // Arrghh! [sound of penny dropping... see update above]
I dare say that this is something to do with how the IDE's form designer works.
My question is simply: What do I need to do in my code to get the form to close,
as it does when I simply click its [x] button on its frame?
Btw, I've confirmed that the instance of the form I'm getting via AForm := [...] is the
instance that's on-screen, by changing on-screen instance's caption in the OI.
procedure TOTAForm.CloseAForm;
var
IServices : IOTAServices;
IActionServices : IOTAActionServices;
IModuleServices : IOTAModuleServices;
IEditorServices : IOTAEditorServices60;
IModule : IOTAModule;
i : Integer;
IEditor : IOTAEditor;
ISourceEditor : IOTASourceEditor;
IFormEditor : IOTAFormEditor;
IComponent : IOTAComponent;
INTAComp : INTAComponent;
AForm : TForm;
begin
IServices := BorlandIDEServices as IOTAServices;
IServices.QueryInterface(IOTAACtionServices, IActionServices);
if IActionServices <> Nil then begin
IServices.QueryInterface(IOTAModuleServices, IModuleServices);
IModuleServices.CloseAll;
if IActionServices.OpenFile(EditorFileName) then begin
IModule := IModuleServices.Modules[0];
ISourceEditor := Nil;
for i := 0 to IModule.ModuleFileCount - 1 do begin
IEditor := IModule.ModuleFileEditors[i];
IEditor.QueryInterface(IOTAFormEditor, IFormEditor);
if IFormEditor <> Nil then begin
IComponent := IFormEditor.GetRootComponent;
IComponent.QueryInterface(INTAComponent, INTAComp);
AForm := TForm(INTAComp.GetComponent);
AForm.Close;
end;
end;
end;
end;
end;
All it needed was:
AForm := TForm(INTAComp.GetComponent);
SendMessage(AForm.Handle, WM_Close, 0, 0);
//AForm.Close;
But I'd still be interested in knowing whether there is an official way to do this, because
my "solution" feels like it's doing an end-run around OTA and NTA services. Otoh, the user can always close the form manually, on-screen, so maybe I'm worrying about nothing

Accesing Values From One Form on Another

Im trying to build in a language switch into one of my programs.
Where the user selects a language and in runtime the app gets translated. I sort of got this working in a small test project. BUT only when the forms are auto created, which i dont want.
the way the forms get created is the following:
SideNote: Most of my forms are fsMDIChild forms.
ParametersForm := TParametersForm.Create(Self); //(consider this the mainform for now)
On ParametersForm I have
procedure TParametersForm.FormCreate(Sender: TObject);
begin
ResourceStringsDM.ParametersF; //(consider this the second form)
end;
this Datamodule houses the captions for ParametersForm.
procedure TResourceStringsDM.ParametersF;
begin
with ParametersForm do
begin
bsSkinLabel1.Caption := 'Execute Nieuwefacturen';
bsSkinLabel2.Caption := 'Execute Viewfacturen';
end;
end;
I have added ResourceStringsDM to the implementation uses of ParametersForm and ParametersForm to the interface uses of ResourceStringsDM.
This all above gives me an access violation cause in the procedure where it sets the captions uses ParametersForm (var name of the form i want to translate) but at that moment this var is nil. Prolly cause it isnt done creating the form yet and hasnt filled in the form var.
The only way i got this all working was by using
Application.CreateForm(TParametersForm, ParametersForm);
But I want to avoid this after reading about it, and that you should only use this on your mainform. Also it doesnt handle passing parameters very well.
Does anyone of you fine people have any hints or tips / help to get me access to the captions of form 1 from form 2?
i probably forgot tons of info you guys need. just tell me and ill add it in.
In an "MDI Application" template (file->new-other->...>, neither MDI childs are auto created nor the child form's unit contains the global form reference. There's a reason for this, more than one instance of a child form should be able to run at the same time. When you have two instances of the same child for instance, which one will the form reference hold?
Anyway, it is of course possible to not to use this design, but then if you don't use the construct you mention in the question, you're responsible for assigning the instance to the reference yourself. So either do it (which I don't recommend):
procedure TParametersForm.FormCreate(Sender: TObject);
begin
ParametersForm := Self;
ResourceStringsDM.ParametersF; //(consider this the second form)
end;
or better, pass the instance to the function in your data module so that it can work on it:
procedure TResourceStringsDM.ParametersF(ParametersForm: TParametersForm);
begin
with ParametersForm do
begin
bsSkinLabel1.Caption := 'Execute Nieuwefacturen';
...
 
procedure TParametersForm.FormCreate(Sender: TObject);
begin
ResourceStringsDM.ParametersF(Self); //(consider this the second form)
end;

Lua Scripting Push Class Function PN.click()

I'm incorporating Lua scripting in my iPhone game implementation and it's working great!
For purely cosmetic reasons, I'd like for my functions in Lua to be in the format of PN.function(). Currently they are in the format of function().
I've tried registering the function as such:
lua_register(lua, "PN.Color", Color);
But it won't let me call it in the Lua script.
Anyone have any suggestions?
Thanks!
Answered my own question!:
lua_newtable(lua);
int pn = lua_gettop(lua);
lua_pushstring(lua, "Click");
lua_pushcfunction(lua, Click);
lua_settable(lua, pn);
lua_pushstring(lua, "Release");
lua_pushcfunction(lua, Release);
lua_settable(lua, pn);
lua_setglobal(lua, "PN");
You cannot use . as a function name in Lua. If you're trying to put all of your Lua functions in a global table called PN, then you have to actually do that.
Remember: lua_register is just a macro:
#define lua_register(L,n,f) \
(lua_pushcfunction(L, f), lua_setglobal(L, n))
There's nothing that say you couldn't do it yourself more specifically.
If you have a global table PN that you want to register Lua functions into, you do the following:
Push the PN table onto the stack, using lua_getfield(L, LUA_GLOBALSINDEX, "PN").
Push the function you want to register onto the stack, with lua_pushcfunction(L, Color).
Put the function into the proper location in the table, with lua_setfield(L, -2, "Color").
Pop the table from the stack with lua_pop(L, 1).

InnoSetup: Is it possible to open my custom Delphi form (from the DLL) instead of the standard setup wizard

I need to create a complex form with my own components (kinda OneClick installer), and use it as the replacement of the standard InnoSetup wizard. Is it possible?
My form is placed into DLL, and this DLL will be available for InnoSetup process.
This is how I tried to do that:
Delphi DLL code
library OneClickWizard;
uses
SysUtils,
Classes,
Wizard in 'Wizard.pas' {FormWizard};
{$R *.res}
exports
CreateWizardForm,
DestroyWizardForm;
begin
end.
Delphi form
unit Wizard;
interface
type
TFormWizard = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
end;
var
FormWizard: TFormWizard;
procedure CreateWizardForm(AppHandle: THandle); stdcall;
procedure DestroyWizardForm; stdcall;
implementation
{$R *.dfm}
procedure CreateWizardForm(AppHandle: THandle);
begin
Application.Handle := AppHandle;
FormWizard := TFormWizard.Create(Application);
FormWizard.Show;
FormWizard.Refresh;
end;
procedure DestroyWizardForm;
begin
FormWizard.Free;
end;
InnoSetup script (iss)
[Setup]
;Disable all of the default wizard pages
DisableDirPage=yes
DisableProgramGroupPage=yes
DisableReadyMemo=true
DisableReadyPage=true
DisableStartupPrompt=true
DisableWelcomePage=true
DisableFinishedPage=true
[Files]
Source:"OneClickWizard.dll"; Flags: dontcopy
[Code]
procedure CreateWizardForm(AppHandle: Cardinal);
external 'CreateWizardForm#files:OneClickWizard.dll stdcall';
procedure DestroyWizardForm;
external 'DestroyWizardForm#files:OneClickWizard.dll stdcall';
procedure InitializeWizard();
begin
CreateWizardForm(MainForm.Handle);
end;
The form appearing on the screen, but it doesn't react on my input. Seems it is out of main message cycle. How to do this correctly?
In my setup I do something similar.
InnoSetup code I pass the handle as StrToInt(ExpandConstant('{wizardhwnd}')) (my guess is that MainForm.Handle is zero)
in the DLL:
OldAppHandle := Application.Handle;
try
Application.Handle := hAppHandle; // hAppHandle the handle from InnoSetup
F := TfmZForm.Create(Application);
try
F.Caption := lpTitle;
F.ShowModal;
Result := F.ErrorCode;
finally
F.Free;
end;
finally
Application.Handle := OldAppHandle;
end;
I know precisely nothing about InnoSetup but surely you need to use ShowModal rather than Show here. Installation UI is invariably modal and what you want here is to wait until the user has finished iteracting with the form before you return to Inno. Otherwise, how would Inno know when to proceed? ShowModal runs a message loop to service the form so there will be no problems receiving input.
You would also change your DLL to remove DestroyWizardForm since the function that calls ShowModal can both create and destroy the form.
If you want to entirely replace the UI, it will probably be easier to create a stub application that presents the form then runs the normal setup in silent mode passing various command line parameters.
Either that or at the very least, using Inno's native form and wizard page functions/logic.