How can I run a command from inside another stack command? - function

I have a callback handler in a stack that looks for telemetry data. When it gets some, I parse it and then want to save it. However, to save it requires other functions and commands in the same stack.
I could put it on the card, but where? I use the openCard end openCard and that's about it in the card.
The stack has all the functions and commands I need. There's no button to press to run the save code - I need it to run automatically.
How can I put the chunk of code on the card and then have the stack 'call it'?
I know how to call commands from the card, but not from the stack.

Generically, you simply call either a command handler or a function handler in-line:
on mouseUp -- a "main" handler
doSomething -- a command handler
dosomethingElse -- another command handler
put doYetAnotherThing(paramList) into field 1 -- a function handler
end mouseUp
on doSomething
well, do something
end doSomething
on doSomethingElse
you get the picture
...
Try making a simple main handler that does silly trivial things for each of the three "subRoutine" calls above. You will be an expert in a matter of hours.
The placement of the three has to be managed. Generally, they reside in the script where the main handler lives. But they can be anywhere in LC.

If you want to call a handler in a card (or any other control) from another script, you can use one of the following commands:
dispatch "command" to control with param1, param2, …
send "command" to control [in time]
put value(command, control) into tResult
Dispatch happily continues even if the command isn't handled by the control. You can check that of course.
Send has the advantage that you can schedule the sending forwards in time, but is a bit harder if you want to also send some parameters.
Value is good candidate if you call a function and want the result back.

Note that ""openCard" and "preOpenCard" messages can be trapped and worked in the stack script, as long as there are no such handlers in the card script. And even if there are, you can "pass" each message after the card script handler is done with it";

Try creating a command on the stack that is called every X times while the user is on that card. This command must be called to itself and to the other manipulators that you use to obtain the data. This same manipulator will be in charge of saving the data.
# Code on the card
local sTelemetryData
on openCard
// If the card belongs to the pile where all the telemetry is or if this pile is a library.
getTelemetryData
// otherwise you will have to call the getTelemetryData command. You can use send, disparsh, or call.
// call "getTelemetryData" to stack "stack name"
end openCard
# Code on the stack
constant kTime = 100
local sPendingMessageID
on getTelemetryData
if the short name of this card is not "TelemetryData"
then exit getTelemetryData
if sPendingMessageID is a number
then cancel sPendingMessageID
// call the commands and functions that look up the telemetry data.
// The data must be stored in the sTelemetryData variable to save it and at once use this variable as a flag
if sTelemetryData is not empty then
// The data is sent to be saved
end if
put empty into sTelemetryData
send "getTelemetryData" to me in kTime milliseconds
put the result into sPendingMessageID
end getTelemetryData

Related

how to catch event in codes after linking events in an array of string inside “events” properties of form object variable in dynamic 4D form?

It's easy to catch events from 4D object's methods of 4D binary form (traditional 4D form) but didn't find any clue to do this in the json dynamic form.
I already gave a try with some 4D commands (CALL FORM, CALL WORKER or POST OUTSIDE CALL) as well to install a project method for a form side by side in a new process inside a loop where I handled events inside that project method. But couldn't get around it. Also I couldn't find any solution/example for this in kb or 4D blog or anywhere.
So any example or database template would be more helpful.
Ravi,
Simply string them in an array named "events".
The docs are your friend for things like this: 4D Manual/Dynamic Forms#Events
The "events" property accepts a JSON array (collection) of strings or
numbers. To call an event, enter the event's name or value (see form
event constant values). For exemple, "events":["onLoad"]) or
"events":[1]
I don't know if you can mix the literal and numeric references. I would expect so but haven't actually tested that.
Edit:
Ravi, if by "catch events in code" you mean have the form you've dynamically created respond to them then you will need to include the name of a project method in the "method" tag. You can't just write some code into an object when you build it dynamically, like you can in regular 4D, but you can call a project method. In that method you can use a Case of statement to test the Form event function to determine which event fired and respond appropriately.
You can't pass parameters to this method. But you can use Object get name or Object get pointer commands to determine the particular object that called it.
For example, let's say I include myMethod as the method. The code for myMethod might look like this:
Case of
:(Form event=On Clicked) // on Clicked is a 4D constant
// do something
:(Form event=on Data Change)
// do something else
End case
Or
Case of
:(Object get name(Object current)="myButton")
Case of
:(Form event=on Clicked)
...
End case
:(Object get name(Object current)="anotherName")
Case of
:(Form event=on Clicked)
...
End case
End case
This illustrates two approaches: 1) you write a separate method for each object or 2) write a single method and determine which object called it. I prefer #2 but that's strictly my opinion.

Read all input in event handler?

Is the readable file event triggered by a channel becoming readable, or by it being readable? In a file event handler, do I have to read all available input to get a new event triggered in the future? Or can I read only 1 character and return, and if there was more input, my handler will automatically be called again directly? Or not directly, but rather put at the end of the event queue, so this would be the preferred way to do it (maybe not just 1 character but for example 1 line) to prevent one stream from blocking the entire program?
It is triggered by a channel being readable, so you can read only a part of input (or read nothing occasionally, being sure that the handler will be called again). However, reading all available input is better for performance (not necessarily all available bytes: if you read line by line, do gets until it returns -1 for fblocked condition).
The sign of input being exhausted is the true value of [fblocked $channel]. That's why the use case with gets and incomplete line of input is possible: even though the channel is technically readable, just not getsable, the event doesn't fire again until new data arrive.

MATLAB Updating a global variable from one function does not get reflected in the other function

I have a function that uses a global variable and I want to change its value from another function. Even though I tried in many ways, the function that uses the value seems not getting updated with the new value of the global variable. Here's the code I'm using.
calculate.m
function calculateTest()
global isStop;
global value;
value=0;
while ~isStop
pause(1);
value = value+1
end
end
start.m
function start()
global isStop;
isStop = 0;
calculateTest();
end
stop.m
function stop()
global isStop;
isStop = 1;
end
When I call start() the value starts getting printed. But even if I call stop(), it never stops. It keeps on printing. Do you have any idea of what I am missing?
(I have tried while isStop==0 as well. But the result was the same.
I think what you need is a background thread that would do the calculateTest while leaving you the possibility to run stop from a matlab script/command line. This functionality is not supported by MATLAB in the pure sense. You can sometimes implement similar things using the timer functionality. Essentially, you tell MATLAB to run a function repeatedly after some time has passed. However, MATLAB is running the timer function in the foreground. And while it is doing that you can not run your stop script. So you can not implement a long loop in the timer function. timer is only good to schedule some tasks to be executed by MATLAB every now and then, but does not implement threading.
You could implement your own background thread using a MEX function. You could then call the MEX function to pass 'start'/'stop' commands to your thread. But the MEX thread would have to do the data processing inside. You can not e.g. call some matlab script to do the job.
Another thing. start and stop are MATLAB functions that manage the timer. Do not use those identifiers as names of your own functions - that is allowed, but considered a bad practice.
You haven't actually called the stop function anywhere in your code, so there is no opportunity for it to update the global variable.
You could, for example, modify calculateTest() by adding a conditional test which calls the stop function when "value" reaches a certain number, such as 5:-
function calculateTest()
global isStop;
global value;
value=0;
while ~isStop
pause(1);
value = value+1
if value == 5
stop;
end
end
end
You will find that this stops it perfectly well. If you added the stop command into start instead, after CalculateTest, that will not work because the flow of control never reaches that line - it remains on CalculateTest until that function is terminated.

How to check for functions dependency in powershell scripts. Avoid running the same function multiple times

In my PowerShell script - one function's output is another function's input. For Eg: Function CreateReport($x) cannot run until unless the Function ParseXml($x) runs. What if a user directly runs the 2nd function before running the 1st. How can I check if 1st function is already run to continue with 2nd, i.e, first run the 1st function (generate the txt file) then run the 2nd? if first func is already run do not re-run it.
For Eg: Suppose I have a TestFunc.ps1 file having 2 functions as below
$X = "C:\XmlPath\file1.xml"
Function ParseXml($X)
{
#Read xml and output contents in a txt file
}
#This function should execute only after the function Parsexml($X) and if Pasrsexml() has run before and generated the output, it shouldnot be allowed to re-run here
Function CreateReport($T)
{
#from the txtfile Create csv
}
According to this and your other question How to alias a parameterized function as a flag in powershell script? you are trying to implement a so called build script. Instead of inventing a wheel (implementing task dependencies, watching tasks to be run once, etc.) take a look at some already implemented tools like psake or Invoke-Build. They are designed for PowerShell and they do exactly what you want (run specified task sets, maintain task dependencies, run tasks once, etc.). These tools require a little bit of learning, of course, but in a long run they are worth to be learned.
If ParseXml function output a file, you can, in the CreateReport function, test for the existence of this file with Test-Path cmdlet:
if exists continue with CreateReport function else call the ParseXml function before continue.
Use a flag. Set the flag in ParseXml function and check it in the CreateReport function. If the flag isn't set, print an error and exit, othervise run the reporting code. Remember to clear the flag when the process is complete.
You can use a flag variable. For more persistent flags, consider using flag files or setting the flag in a database.

'Invalid Handle object' when using a timer inside a function in MatLab

I am using a script in MatLab that works perfectly fine by itself, but I need to make a function out of it.
The script read a .csv file, extract all values, start a timer, and at each tick displays the corresponding coordinates extracted from the .csv, resulting in a 3D animation of my graph.
What I would like is to give it the location of the .csv, so that it starts displaying the graphs for this csv.
Here is what I have come up with:
function handFig(fileLoc)
csv=csvread(fileLoc,1,0);
both = csv(:,2:19);
ax=axes;
set(ax,'NextPlot','replacechildren');
Dt=0.1; %sampling period in secs
k=1;
hp1=text(both(k,1),both(k,2),both(k,3),'thumb'); %get handle to dot object
hold on;
hp2=text(both(k,4),both(k,5),both(k,6),'index');
hp3=text(both(k,7),both(k,8),both(k,9),'middle');
hp4=text(both(k,10),both(k,11),both(k,12),'ring');
hp5=text(both(k,13),both(k,14),both(k,15),'pinky');
hp6=text(both(k,16),both(k,17),both(k,18),'HAND');
L1=plot3([both(k,1),both(k,16)],[both(k,2),both(k,17)],[both(k,3),both(k,18)]);
L2=plot3([both(k,4),both(k,16)],[both(k,5),both(k,17)],[both(k,6),both(k,18)]);
L3=plot3([both(k,7),both(k,16)],[both(k,8),both(k,17)],[both(k,9),both(k,18)]);
L4=plot3([both(k,10),both(k,16)],[both(k,11),both(k,17)],[both(k,12),both(k,18)]);
L5=plot3([both(k,13),both(k,16)],[both(k,14),both(k,17)],[both(k,15),both(k,18)]);
hold off;
t1=timer('TimerFcn','k=doPlot(hp1,hp2,hp3,hp4,hp5,hp6,L1,L2,L3,L4,L5,both,t1,k)','Period', Dt,'ExecutionMode','fixedRate');
start(t1);
end
And the doplot function used:
function k=doPlot(hp1,hp2,hp3,hp4,hp5,hp6,L1,L2,L3,L4,L5,pos,t1,k)
k=k+1;
if k<5000%length(pos)
set(hp1,'pos',[pos(k,1),pos(k,2),pos(k,3)]);
axis([0 255 0 255 0 255]);
set(hp2,'pos',[pos(k,4),pos(k,5),pos(k,6)]);
set(hp3,'pos',[pos(k,7),pos(k,8),pos(k,9)]);
set(hp4,'pos',[pos(k,10),pos(k,11),pos(k,12)]);
set(hp5,'pos',[pos(k,13),pos(k,14),pos(k,15)]);
set(hp6,'pos',[pos(k,16),pos(k,17),pos(k,18)]);
set(L1,'XData',[pos(k,1),pos(k,16)],'YData',[pos(k,2),pos(k,17)],'ZData',[pos(k,3),pos(k,18)]);
set(L2,'XData',[pos(k,4),pos(k,16)],'YData',[pos(k,5),pos(k,17)],'ZData',[pos(k,6),pos(k,18)]);
set(L3,'XData',[pos(k,7),pos(k,16)],'YData',[pos(k,8),pos(k,17)],'ZData',[pos(k,9),pos(k,18)]);
set(L4,'XData',[pos(k,10),pos(k,16)],'YData',[pos(k,11),pos(k,17)],'ZData',[pos(k,12),pos(k,18)]);
set(L5,'XData',[pos(k,13),pos(k,16)],'YData',[pos(k,14),pos(k,17)],'ZData',[pos(k,15),pos(k,18)]);
else
k=1;
set(hp1,'pos',[pos(k,1),pos(k,2),pos(k,3)]);
axis([0 255 0 255 0 255]);
set(hp2,'pos',[pos(k,4),pos(k,5),pos(k,6)]);
set(hp3,'pos',[pos(k,7),pos(k,8),pos(k,9)]);
set(hp4,'pos',[pos(k,10),pos(k,11),pos(k,12)]);
set(hp5,'pos',[pos(k,13),pos(k,14),pos(k,15)]);
set(hp6,'pos',[pos(k,16),pos(k,17),pos(k,18)]);
end
However, when I run handFig('fileName.csv'), I obtain the same error everytime:
??? Error while evaluating TimerFcn for timer 'timer-7'
Invalid handle object.
I figured that it might come from the function trying to create a new 'csv' and 'both' everytime, so I tried removing them, and feeding the function the data directly, without results.
What is exactly the problem? Is there a solution?
Thanks a lot!
I think it's because when you call doPlot in the timer for the first time, you pass in t1 as an argument, and it might not exist the first time.
Does doPlot need t1 at all? I'd suggest modifying it so it's not used, and then your call to:
t1=timer('TimerFcn','k=doPlot(hp1,hp2,hp3,hp4,hp5,hp6,L1,L2,L3,L4,L5,both,k)','Period', Dt,'ExecutionMode','fixedRate');
Note the missing t1 in the doPlot call.
Either that, or initialise your t1 before you create the timer so it has some value to pass in.
Update (as an aside, can you use pause(Dct) in a loop instead? seems easier)
Actually, now I think it's a problem of scope.
It took a bit of digging to get to this, but looking at the Matlab documentation for function callbacks, it says:
When MATLAB evaluates function handles, the same variables are in scope as when the function handle was created. (In contrast, callbacks specified as strings are evaluated in the base workspace.)
You currently give your TimerFcn argument as a string, so k=doPlot(...) is evaluated in the base workspace. If you were to go to the matlab prompt, run handFig, and then type h1, you'd get an error because h1 is not available in the global workspace -- it's hidden inside handFig.
That's the problem you're running into.
However, the workaround is to specify your function as a function handle rather than a string (it says function handles are evaluated in the scope in which they are created, ie within handFig).
Function handles to TimerFcn have to have two arguments obj and event (see Creating Callback Functions). Also, that help file says you have to put doPlot in its own m-file to have it not evaluate in the base Matlab workspace.
In addition to these two required input arguments, your callback
function can accept application-specific arguments. To receive these
input arguments, you must use a cell array when specifying the name of
the function as the value of a callback property. For more
information, see Specifying the Value of Callback Function Properties.
It goes through an example of what you have to do to get this working. Something like:
% create timer
t = timer('Period', Dt,'ExecutionMode','fixedRate');
% attach `k` to t so it can be accessed within doPlot
set(t,'UserData',k);
% specify TimerFcn and its extra arguments:
t.TimerFcn = { #doPlot, hp1, hp2, hp3, ...., both };
start(t)
Note -- the reason k is set in UserData is because it needs to be somehow saved and modified between calls to doPlot.
Then modify your doPlot to have two arguments at the beginning (which aren't used), and not accept the k argument. To extract k you do get(timer_obj,'UserData') from within doPlot:
function k=doPlot(timer_obj, event, hp1,hp2,hp3,.....)
k = get(timer_obj,'UserData');
.... % rest of code here.
% save back k so it's changed for next time!
set(timer_obj,'UserData',k);
I think that's on the right track - play around with it. I'd highly recommend the mathworks forums for this sort of thing too, those people are whizzes.
This thread from the mathworks forum was what got me started and might prove helpful to you.
Good luck!