Global variable doesn't update until function finishes? - autohotkey

I'm trying to prevent a function (^F1) from being run twice at the same time.
To do this, I'm trying to use a global lock variable; the function needs to release the lock to enable another function call.
Now, this below code would work in Java, but it's not working in AHK. The problem is that, in AHK, the global "is_locked" does not update until ^F1 has finished. This defeats the purpose of using a global lock variable to prevent simultaneous function calls.
How can I fix this?
is_locked := False
set_lock()
{
global is_locked
is_locked := True
}
remove_lock()
{
global is_locked
is_locked := False
}
^F1::
global is_locked
if(is_locked)
{
; doesn't print until after 10 seconds, even if I am spamming ^F1
MsgBox, "locked"
return
}
set_lock()
Sleep 10000
return

Take note that is_locked is a super-global variable.
global is_locked := false
set_lock()
{
is_locked := true
}
remove_lock()
{
is_locked := false
}
^F1::
if (is_locked)
return
set_lock()
MsgBox, "locked"
Sleep 10000
remove_lock()
return

global is_locked
toggle_lock() {
is_locked := !is_locked
OutputDebug, % is_locked
}
^F1::
toggle_lock()
if is_locked
ToolTip, locked
Else
ToolTip, NOT locked
return

Related

FB_FileOpen stays busy, Statemachine not working - TwinCat3

i am trying to get into the beckhoff/twincat universe, therefore is was following along with some twincat tutorials. While programming a simple event-logger I encountered the following problem:
After executing FB_FileOpen, it´s bBusy variable stays True - therefore my statemachine won´t execute any further and is stuck in FILE_OPEN. Any idea, what I did wrong? Here is my code:
VAR
FileOpen : FB_FileOpen := (sPathName := 'C:\Events-log.txt', nMode := FOPEN_MODEAPPEND OR FOPEN_MODETEXT);
FileClose :FB_FileClose;
FilePuts : FB_FilePuts;
stEventWrittenToFile : ST_Event;
CsvString : T_MaxString;
eWriteState :(TRIGGER_FILE_OPEN, FILE_OPEN, WAIT_FOR_EVENT,TRIGGER_WRITE_EVENT, WRITE_EVENT, FILE_CLOSE, ERROR);
END_VAR
CASE eWriteState OF
TRIGGER_FILE_OPEN :
FileOpen(bExecute := TRUE);
eWriteState := FILE_OPEN;
FILE_OPEN :
FileOpen(bExecute := FALSE);
IF FileOpen.bError THEN
eWriteState := ERROR;
ELSIF NOT FileOpen.bBusy AND FileOpen.hFile <> 0 THEN
eWriteState := WAIT_FOR_EVENT;
END_IF
WAIT_FOR_EVENT :
//Do nothing, triggered externally by method
TRIGGER_WRITE_EVENT :
CsvString := ConvertStructureToString(stEvent := stEventWrittenToFile);
FilePuts( sLine:= CsvString,
hFile := FileOpen.hFile,
bExecute := TRUE,);
eWriteState := WRITE_EVENT;
WRITE_EVENT :
FilePuts(bExecute := FALSE);
IF FilePuts.bError THEN
eWriteState := ERROR;
ELSIF NOT FilePuts.bBusy THEN
eWriteState := FILE_CLOSE;
END_IF
FILE_CLOSE :
FileClose( hFile := FileOpen.hFile,
bExecute := TRUE);
IF FileClose.bError = TRUE THEN
FileClose.bExecute := FALSE;
eWriteState := ERROR;
ELSIF NOT FileClose.bBusy THEN
FileClose.bExecute := FALSE;
eWriteState := TRIGGER_FILE_OPEN;
END_IF
ERROR : // Do nothing
END_CASE
The issue probably lies in how you call the function block. You need to make sure to call the function block with the input bExecute := FALSE and only after that calling it with bExecute := TRUE will trigger the function block execution. Caliing the fb with its "exectue" input to false after it has had the input triggered, will always work so just invert your order of TRUE and FALSE executes for all your states.
TRIGGER_FILE_OPEN:
fileOpen(bExecute := FALSE);
eWriteState := FILE_OPEN;
FILE_OPEN:
fileOpen(bExecute := TRUE);
...
You could also follow the Beckhoff example provided on their website, not a fan of this, but calling the function block twice, back to back in a single PLC cycle :
(* Open source file *)
fbFileOpen( bExecute := FALSE );
fbFileOpen( sNetId := sSrcNetId,
sPathName := sSrcPathName,
nMode := FOPEN_MODEREAD OR FOPEN_MODEBINARY,
ePath := PATH_GENERIC,
tTimeout := tTimeOut,
bExecute := TRUE );
Full example can be found here : https://infosys.beckhoff.com/english.php?content=../content/1033/tcplclib_tc2_system/30977547.html&id=
I found the error.
My mistake was, that I started the state machine with a positive edge from a start variable. Since I am running the task in a 1ms cycle, the whole thing would´ve needed to complete within 1ms then.

How to implement FB GetLocalAmsNetId?

this is my first question here on stackoverflow and im hoping someone might be able to help me out.
I'm trying to get the local AmsNetId of my TwinCat PLC system. The Code is running on the TwinCat System locally.
The function is working properly, no problems compiling. But the functionblock FB_GetLocalAmsNetId never seems to return the Ams Net Id. fbGetAmsNetId.bBusy is always busy.
I dont know what i'm doing wrong.
Variables:
FUNCTION_BLOCK FB_GetAmsNetId
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
fbGetAmsNetId : FB_GetLocalAmsNetId;
bRequestStarted : BOOL := FALSE;
sAmsNetId : T_AmsNetId;
END_VAR
Programcode:
IF(bRequestStarted = FALSE) THEN
fbGetAmsNetId(bExecute := TRUE, tTimeOut := T#2S);
bRequestStarted := TRUE;
ELSE
IF(NOT fbGetAmsNetId.bBusy) THEN
sAmsNetId := fbGetAmsNetId.AddrString;
fbGetAmsNetId.bExecute := FALSE;
bRequestStarted := FALSE;
END_IF
END_IF
You need to cyclically call fbGetAmsNetId in your code, otherwise FB_GetLocalAmsNetId will not be able to finish it's internal operations beeing executed for only one plc cycle.
For example:
fbGetAmsNetId();
IF(bRequestStarted = FALSE) THEN
fbGetAmsNetId(bExecute := TRUE, tTimeOut := T#2S);
bRequestStarted := TRUE;
ELSE
IF(NOT fbGetAmsNetId.bBusy) THEN
sAmsNetId := fbGetAmsNetId.AddrString;
fbGetAmsNetId.bExecute := FALSE;
bRequestStarted := FALSE;
END_IF
END_IF

Is there a timer function or variable in Codesys as in arduino millis()?

Is there a timer function or variable in Codesys as in arduino millis() ?
If not, how can I create a timer?
Thanks!
In CoDeSys function TIME() return time in milliseconds from PLC start. If you want to start the count on the event you can use triggers to create a time point.
VAR
tStarted, tElapsed : TIME;
END_VAR
fbR_TRIG(CLK := xStart);
IF (fbR_TRIG.Q) THEN
tStarted := TIME();
END_IF;
tElapsed := TIME() - tStarted;
And rest follows like reset the timer, pause counting, etc.
You can build one yourself.
Here an example:
Declaration part:
FUNCTION_BLOCK FB_Millis
VAR_INPUT
timer : TON := (IN:=TRUE,PT:=maxTime);
END_VAR
VAR_OUTPUT
tElapsedTime : TIME;
END_VAR
VAR
maxTime : TIME := UDINT_TO_TIME(4294967295);
//timer : TON := (IN:=TRUE,PT:=maxTime);
END_VAR
Implementation part:
timer();
tElapsedTime := timer.ET;
You call it cyclically like this:
fbMillis();
And retrieve the result like this:
tElapasedTime := fbMillis.tElapsedTime;
FB_Millis overflows after 49days 17hours 2minutes 47seconds and 295ms.
If you want to compare the elapsed time from fbMillis.tElapsedTime with another variable you do like this:
IF fbMillis.tElapsedTime < tAnotherTimeVar
THEN
; //Do something
ELSE
; //Do something else
END_IF
If you instead just want a simple timer you need the TON Function Block:
Declaration part:
//2 seconds timer
mySimpleTimer : TON := (PT:=T#2s);
Implementation part:
mySimpleTimer();
// your code here
//Start timer
mySimpleTimer.IN := TRUE;
//Check if timer has reached desired time
IF mySimpleTime.Q
THEN
//Do something here
mySimpleTimer.IN := FALSE;
END_IF

How to make `ifWinActive` and `WinActivateBottom` to use only the current desktop?

I have an autohotkey script that search if specific window exists, if so, it will activate it.
I want that it will search only in the current desktop (I'm using Windows 10).
Do you have a suggestion how to do it?
My Script:
#c::
IfWinExist ,ahk_class ConsoleWindowClass
{
ifWinActive
WinActivatebottom ,ahk_class ConsoleWindowClass
else
WinActivate
return
}
#c:: WinActivateBottomOnCurrentVirtualDesktop("ConsoleWindowClass")
WinActivateBottomOnCurrentVirtualDesktop(Class){
IfWinExist, ahk_class %Class%
{
list := ""
LastWin := ""
; get a list of those windows on the current desktop
WinGet, id, list, ahk_class %Class%
Loop, %id%
{
this_ID := id%A_Index%
If IsWindowOnCurrentVirtualDesktop(this_ID)
LastWin := this_ID ; retrieves the bottommost matching window ID
}
WinActivate, ahk_id %LastWin%
}
}
; https://autohotkey.com/boards/viewtopic.php?p=64295#p64295
; Indicates whether the provided window is on the currently active virtual desktop:
IsWindowOnCurrentVirtualDesktop(hWnd) {
onCurrentDesktop := ""
CLSID := "{aa509086-5ca9-4c25-8f95-589d3c07b48a}"
IID := "{a5cd92ff-29be-454c-8d04-d82879fb3f1b}"
IVirtualDesktopManager := ComObjCreate(CLSID, IID)
Error := DllCall(NumGet(NumGet(IVirtualDesktopManager+0), 3*A_PtrSize), "Ptr", IVirtualDesktopManager, "Ptr", hWnd, "IntP", onCurrentDesktop)
ObjRelease(IVirtualDesktopManager)
if !(Error=0)
return false, ErrorLevel := true
return onCurrentDesktop, ErrorLevel := false
}

autohotkey flags keeps skipping my function

can you guys help me, i have added a flag, however it skips teleport() and keeps walksouth() any idea why? I'm kinda newbie here i hope you guys help me
walk1 := 0
loop {
teleport()
}
teleport()
{
if (walk1 => 1) ;this never worked even i added flag walk1:=1 :( please help
send, {f9}
sleep, 500
walk1 := 0
return
} else if (walk1 <= 0){
walksouth()
return
}
}
walksouth() { ;this keeps running and skipping teleport()
send, {f5}
sleep, 500
walk1 := 1 ;added flag 1 to run the teleport, but still skipping
return
}
This is your corrected and refactored code:
walk := false
loop {
teleport()
}
teleport()
{
global walk
if (walk) {
send {f9}
walk := false
} else {
send {f5}
walk := true
}
sleep 500
}
Bugs in your code:
Variables in functions have local scope unless they are declare using global. So the walk1 variables in your functions and the global walk1 are all different variables
You are missing an open brace { after if (walk1 => 1)