Read all input in event handler? - tcl

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.

Related

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

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

WSO2CEP : Event Receiver does not accept any JSON parameter with name having dot in it

My json custom formatted events are from a log file which contains parameters names with dots like id.orig_h etc. Sample event is:
{"ts":"2016-05-08 08:59:47.363764Z","uid":"CLuCgz3HHzG7LpLwH9","id.orig_h":"172.30.26.119","id.orig_p":51976,"id.resp_h":"172.30.26.160","id.resp_p":22,"version":2,"client":"SSH-2.0-OpenSSH_5.0","server":"SSH-2.0-OpenSSH_6.6.1p1 Ubuntu-2ubuntu2.6","cipher_alg":"arcfour256","mac_alg":"hmac-md5","compression_alg":"none","kex_alg":"diffie-hellman-group-exchange-sha1","host_key_alg":"ssh-rsa","host_key":"8d:df:71:ac:29:1f:67:6f:f3:dd:c3:e5:2e:5f:3e:b4"}
But event receiver does not take such events and gives mapping errors saying:
Could not find any matches for the incoming event with JSONPath : com.jayway.jsonpath.JsonPath#543abe49 ,hence dropping the event
If I cant change my log file, How can I make receiver to accept such parameters?
Also unless my events are not segregated with *****, receiver does not bother any further coming events. Why is so? How can I avoid it?
I simply have modified my log files before sending it via any client. I was using sample 0002, So I changed my message to comply with the receiver. However I still dont know why receiver does not accept parameters with dots in it. This sample also considered events to be separated by asterixLine i.e. *****. On removing a couple of sample lines, I made it work.

Reading byte array the second time causes error?

I am using the following code to read an error message from a byte array and it works fine the first time but if I try to access it the second time it throws an error:
errorData = process.standardError.readUTFBytes(process.standardError.bytesAvailable);
StandardError is of type InboundPipe?
The error is:
Error: Error #3212: Cannot perform operation on a NativeProcess that is not running.
even though the process is running (process.running is true). It's on the second call to readUTFBytes that seems to be the cause.
Update:
Here is the code calling the same call one after another. The error happens on the next line and process.running has not changed from true. Happens on the second call.
errorData = process.standardError.readUTFBytes(process.standardError.bytesAvailable);
errorData = process.standardError.readUTFBytes(process.standardError.bytesAvailable);
I also found out the standardError is a InboundPipe instance and implements IDataInput.
Update 2:
Thanks for all the help. I found this documentation when viewing the bytesAvailable property.
[Read Only] Returns the number of bytes of data available for reading
in the input buffer. User code must call bytesAvailable to ensure that
sufficient data is available before trying to read it with one of the
read methods.
When I call readUTFBytes() it resets the bytes available to 0. So when I read it a second time and there are no bytes available it causes the error. The error is or may be incorrect in my opinion or the native process.running flag is incorrect.
I looked into seeing if it has a position property and it does not, at least not in this instance.
Could you try to set position to zero before reading process, especially before repetitive access:
Moves, or returns the current position, in bytes, of the file pointer into the ByteArray object. This is the point at which the next call to a read method starts reading or a write method starts writing.
//ByteArray example
var source: String = "Some data";
var data: ByteArray = new ByteArray();
data.writeUTFBytes(source);
data.position = 0;
trace(data.readUTFBytes(data.bytesAvailable));
data.position = 0;
trace(data.readUTFBytes(data.bytesAvailable));
This was a tricky problem since the object was not a byte array although it looks and acts like one (same methods and almost same properties). It is an InboundPipe that also implements IDataInput.
I found this documentation when viewing the bytesAvailable property.
[Read Only] Returns the number of bytes of data available for reading
in the input buffer. User code must call bytesAvailable to ensure that
sufficient data is available before trying to read it with one of the
read methods.
When I call readUTFBytes() it resets the bytes available to 0. So when I call it a second time and there are no bytes available it causes the error. The error is or may be incorrect in my opinion or the native process.running flag is incorrect although I have reason to believe it's the former.
The solution is to check bytesAvailable before calling read operations and store the value if it needs to be accessed later.
if (process.standardError.bytesAvailable) {
errorData = process.standardError.readUTFBytes(process.standardError.bytesAvailable);
errorDataArray.push(errorData);
}
I looked into seeing if it has a position property and it does not, at least not in this instance.

'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!

MIDI Program Change event

Please take a look at Midi Event Commands, where it's explained that some events like Program Change take only one parameter.
My question is then what do you send along with this event's 2nd parameter's byte, is it 0x00? or you just entirely omit it from the event which doesn't really make sense?
Not all MIDI messages are 3 bytes long. Some are only two, and program change is one of them. For these messages, there is no second data byte.