How to break a MATLAB system call after a specified number of seconds - matlab

I am using Windows MATLAB to run SSH commands, but every once in a while the SSH command hangs indefinitely, and then so does my MATLAB script (sometimes when running overnight). How can I have a command time-out after a certain amount of waiting time? For example, suppose I don't want to wait more than 3 seconds for a SSH command to finish execution before breaking it and moving on:
% placeholder for a command that sometimes hangs
[status,result] = system('ssh some-user#0.1.2.3 sleep 10')
% placeholder for something I still want to run if the above command hangs
[status,result] = system('ssh some-user#0.1.2.3 ls')
I'd like to make a function sys_with_timeout that can be used like this:
timeoutDuration = 3;
[status,result] = sys_with_timeout('ssh some-user#0.1.2.3 sleep 10', timeoutDuration)
[status,result] = sys_with_timeout('ssh some-user#0.1.2.3 ls', timeoutDuration)
I've tried the timeout function from FEX but it doesn't seem to work for a system/SSH command.

I don't think the system command is very flexible, and I don't see a way to do what you want using it.
But we can use the Java functionality built into MATLAB for this, according to this answer by André Caron.
This is how you'd wait for the process to finish with a timeout:
runtime = java.lang.Runtime.getRuntime();
process = runtime.exec('sleep 20');
process.waitFor(10, java.util.concurrent.TimeUnit.SECONDS);
if process.isAlive()
disp("Done waiting for this...")
process.destroyForcibly();
end
process.exitValue()
In the example, we run the sleep 20 shell command, then waitFor() waits until the program finishes, but for a maximum of 10 seconds. We poll to see if the process is still running, and kill it if it is. exitValue() returns the status, if you need it.
Running sleep 5 I see:
ans =
0
Running sleep 20 I see:
Done waiting for this...
ans =
137

Building on #Cris Luengo's answer, here is the sys_with_timeout() function. I didn't use the process.waitFor() function because I'd rather wait in a while loop and display output as it comes in. The while loop breaks once the command finishes or it times out, whichever comes first.
function [status,cmdout] = sys_with_timeout(command,timeoutSeconds,streamOutput,errorOnTimeout)
arguments
command char
timeoutSeconds {mustBeNonnegative} = Inf
streamOutput logical = true % display output as it comes in
errorOnTimeout logical = false % if false, display warning only
end
% launch command as java process (does not wait for output)
process = java.lang.Runtime.getRuntime().exec(command);
% start the timeout timer!
timeoutTimer = tic();
% Output reader (from https://www.mathworks.com/matlabcentral/answers/257278)
outputReader = java.io.BufferedReader(java.io.InputStreamReader(process.getInputStream()));
errorReader = java.io.BufferedReader(java.io.InputStreamReader(process.getErrorStream()));
% initialize output char array
cmdout = '';
while true
% If any lines are ready to read, append them to output
% and display them if streamOutput is true
if outputReader.ready() || errorReader.ready()
if outputReader.ready()
nextLine = char(outputReader.readLine());
elseif errorReader.ready()
nextLine = char(errorReader.readLine());
end
cmdout = [cmdout,nextLine,newline()];
if streamOutput == true
disp(nextLine);
end
continue
else
% if there are no lines ready in the reader, and the
% process is no longer running, then we are done
if ~process.isAlive()
break
end
% Check for timeout. If timeout is reached, destroy
% the process and break
if toc(timeoutTimer) > timeoutSeconds
timeoutMessage = ['sys_with_timeout(''',command, ''',', num2str(timeoutSeconds), ')',...
' failed after timeout of ',num2str(toc(timeoutTimer)),' seconds'];
if errorOnTimeout == true
error(timeoutMessage)
else
warning(timeoutMessage)
end
process.destroyForcibly();
break
end
end
end
if ~isempty(cmdout)
cmdout(end) = []; % remove trailing newline of command output
end
status = process.exitValue(); % return
end
Replacing ssh some-user#0.1.2.3 with wsl (requires WSL of course) for simplicity, here is an example of a function that times out:
>> [status,cmdout] = sys_with_timeout('wsl echo start! && sleep 2 && echo finished!',1)
start!
Warning: sys_with_timeout('wsl echo start! && sleep 2 && echo finished!',1) failed after timeout of 1.0002 seconds
> In sys_with_timeout (line 41)
status =
1
cmdout =
'start!'
and here is an example of a function that doesn't time out:
>> [status,cmdout] = sys_with_timeout('wsl echo start! && sleep 2 && echo finished!',3)
start!
finished!
status =
0
cmdout =
'start!
finished!'

Related

Save job output from SDSF into a PDS and using ISPF functions in REXX

We periodically runs jobs and we need to save the output into a PDS and then parse the output to extract parts of it to save into another member. It needs to be done by issuing a REXX command using the percent sign and the REXX member name as an SDSF command line. I've attempted to code a REXX to do this, but it is getting an error when trying to invoke an ISPF service, saying the ISPF environment has not been established. But, this is SDSF running under ISPF.
My code has this in it (copied from several sources and modified):
parse arg PSDSFPARMS "(" PUSERPARMS
parse var PSDSFPARMS PCURRPNL PPRIMPNL PROWTOKEN PPRIMCMD .
PRIMCMD=x2c(PPRIMCMD)
RC = isfquery()
if RC <> 0 then
do
Say "** SDSF environment does not exist, exec ending."
exit 20
end
RC = isfcalls("ON")
Address SDSF "ISFGET" PPRIMPNL "TOKEN('"PROWTOKEN"')" ,
" (" VERBOSE ")"
LRC = RC
if LRC > 0 then
call msgrtn "ISFGET"
if LRC <> 0 then
Exit 20
JOBNAME = value(JNAME.1)
JOBNBR = value(JOBID.1)
SMPDSN = "SMPE.*.OUTPUT.LISTINGS"
LISTC. = ''
SMPODSNS. = ''
SMPODSNS.0 = 0
$ = outtrap('LISTC.')
MSGVAL = msg('ON')
address TSO "LISTC LVL('"SMPDSN"') ALL"
MSGVAL = msg(MSGVAL)
$ = outtrap('OFF')
do LISTCi = 1 to LISTC.0
if word(LISTC.LISTCi,1) = 'NONVSAM' then
do
parse var LISTC.LISTCi . . DSN
SMPODSNS.0 = SMPODSNS.0 + 1
i = SMPODSNS.0
SMPODSNS.i = DSN
end
IX = pos('ENTRY',LISTC.LISTCi)
if IX <> 0 then
do
IX = pos('NOT FOUND',LISTC.LISTCi,IX + 8)
if IX <> 0 then
do
address ISPEXEC "SETMSG MSG(IPLL403E)"
EXITRC = 16
leave
end
end
end
LISTC. = ''
if EXITRC = 16 then
exit 0
address ISPEXEC "TBCREATE SMPDSNS NOWRITE" ,
"NAMES(TSEL TSMPDSN)"
I execute this code by typing %SMPSAVE next to the spool output line on the "H" SDSF panel and it runs fine until it gets to this point in the REXX:
114 *-* address ISPEXEC "TBCREATE SMPDSNS NOWRITE" ,
"NAMES(TSEL TSMPDSN)"
>>> "TBCREATE SMPDSNS NOWRITE NAMES(TSEL TSMPDSN)"
ISPS118S SERVICE NOT INVOKED. A VALID ISPF ENVIRONMENT DOES NOT EXIST.
+++ RC(20) +++
Does anyone know why it says I don't have a valid ISPF environment and how I can get around this?
I've done quite a bit in the past with REXX, including writing REXX code to handle line commands, but this is the first time I've tried to use ISPEXEC commands within this code.
Thank you,
Alan

interrupt a TCP-IP callback function in matlab

Recently wrote code that establishes a connection between two instances of matlab. I can send messages through the TCP-IP connection which will execute code. Now I'm trying to setup the code to be interruptible as I would like to start/stop a function through TCP-IP. Problem though is that sending a second command does nothing until the function is completed. Is there a way to interrupt a TCP-IP callback function?
code:
classdef connectcompstogether<handle
properties
serverIP
clientIP
tcpipServer
tcpipClient
Port = 4000;
bsize = 8;
earlystop
end
methods
function gh = connectcompstogether(~)
% gh.serverIP = '127.0.0.1';
gh.serverIP = 'localhost';
gh.clientIP = '0.0.0.0';
end
function SetupServer(gh)
gh.tcpipServer = tcpip(gh.clientIP,gh.Port,'NetworkRole','Server');
set(gh.tcpipServer,'OutputBufferSize',gh.bsize);
fopen(gh.tcpipServer);
display('Established Connection')
end
function SetupClient(gh)
gh.tcpipClient = tcpip(gh.serverIP,gh.Port,'NetworkRole','Client');
set(gh.tcpipClient, 'InputBufferSize',gh.bsize);
set(gh.tcpipClient, 'BytesAvailableFcnCount',8);
set(gh.tcpipClient, 'BytesAvailableFcnMode','byte');
set(gh.tcpipClient, 'BytesAvailableFcn', #(h,e)gh.recmessage(h,e));
fopen(gh.tcpipClient);
display('Established Connection')
end
function CloseClient(gh)
fclose(gh.tcpipClient);
gh.tcpipClient = [];
end
end
methods
function sendmessage(gh,message)
fwrite(gh.tcpipServer,message,'double');
end
function recmessage(gh,h,e)
Message = fread(gh.tcpipClient,gh.bsize/8,'double');
if Message == 444
gh.Funwithnumbers();
elseif Message == 777
gh.earlystop = 1;
end
end
function Funwithnumbers(gh)
x=1;
while true
if x > 5000, break;end
if gh.earlystop == 1,break;end
x = x+1;
display(x)
end
end
end
end
for ease to understand code.
server
Ser = connectcompstogether;
ser.SetupServer();
ser.sendmessage(333);
Client
cli = connectcompstogether;
cli.SetupClient();
Update:
So after going through the web, I have found out based on this post that the tcpip callback cannot be interrupt. The post was in 2017 which means my 2016a version definitely cannot interrupt a callback.
So An update to my question, Is it possible to start a subprocess in matlab to run the function. I just want to use the callback to start code. If I can start a subprocess from the callback. Than I should be able to free up the main process and use tcpip to start/stop a function on a different computer.
Update 2:
So I tried to utilize parallel processing using the 'spmd' command but the problem still persisted.
function recmessage(gh,h,e)
Message = fread(gh.tcpipClient,gh.bsize/8,'double');
spmd
switch labindex
case 1
if Message == 444
gh.Funwithnumbers();
elseif Message == 777
gh.earlystop = 1;
end
end
end
end
You may use a timer object, which is convenient to delay the execution of some function.
t=timer('ExecutionMode','singleShot', 'StartDelay',0, 'TimerFcn',#myCallback);
start(t);
In this case, the StartDelay is 0, so myCallback will be almost immediately added to the queue of tasks to be processed by Matlab. The execution however will start only after the callback to the tcpip object has been completed. It will block the queue once started, however.
You may try something like:
properties
t=timer('ExecutionMode','singleShot', 'StartDelay',0, 'TimerFcn',#myCallback);
end
function tcpipCallback(gh,tcpObj,~)
message=fread(tcpObj,1,'double');
if message==444
if strcmp(get(t,'Running'),'on')
error('The function is running already');
else
set(gh.t,'UserData',false);
start(gh.t);
end
elseif message==777
set(gh.t,'UserData',true);
end
function myCallback(tObj,~)
ii=0;
while ii<5000
if get(tObj,'UserData'),break,end
ii=ii+1;
pause(.0001); %Pause to interrupt the callback; drawnnow might work too; or perhaps this is not needed at all.
end
end

Simpy subway simulation: how to fix interrupt failure of class train while queueing for a resource?

I am working on a train simulation in simpy and have had success so far with a single train entity following the code below.
The trains processes are sections followed by platforms. Each section and platform has a resource of 1 to ensure that only one train utilises at a time.
However I can't find a way to get around the error below:
When I add in a second train to the simulation there is occasionally the situation where one train waits for an unavailable resource and then a failure occurs on that train while it is waiting.
I end up with an Interrupt: Interrupt() error.
Is there a way around these failing queues for resources?
Any help is much appreciated.
import random
import simpy
import numpy
# Configure parameters for the model
RANDOM_SEED = random.seed() # makes results repeatable
T_MEAN_SECTION = 200.0 # journey time (seconds)
DWELL_TIME = 30.0 # dwell time mean (seconds)
DWELL_TIME_EXPO = 1/DWELL_TIME # for exponential distribution
MTTF = 600.0 # mean time to failure (seconds)
TTF_MEAN = 1/MTTF # for exponential distribution
REPAIR_TIME = 120.0 # mean repair time for when failure occurs (seconds)
REPAIR_TIME_EXPO = 1/REPAIR_TIME # for exponential distribution
NUM_TRAINS = 2 # number of trains to simulate
SIM_TIME_HOURS = 1 # sim time in hours
SIM_TIME_DAYS = SIM_TIME_HOURS/18.0 # number of days to simulate
SIM_TIME = 3600 * 18 * SIM_TIME_DAYS # sim time in seconds (this is used in the code below)
# Defining the times for processes
def Section(): # returns processing time for platform 7 Waterloo to 26 Bank
return T_MEAN_SECTION
def Dwell(): # returns processing time for platform 25 Bank to platform 7 Waterloo
return random.expovariate(DWELL_TIME_EXPO)
def time_to_failure(): # returns time until next failure
return random.expovariate(TTF_MEAN)
# Defining the train
class Train(object):
def __init__(self, env, name, repair):
self.env = env
self.name = name
self.trips_complete = 0
self.num_saf = 0
self.sum_saf = 0
self.broken = False
# Start "running" and "downtime_train" processes for the train
self.process = env.process(self.running(repair))
env.process(self.downtime_train())
def running(self, repair):
while True:
# request section A
request_SA = sectionA.request()
########## SIM ERROR IF FAILURE OCCURS HERE ###########
yield request_SA
done_in_SA = Section()
while done_in_SA:
try:
# going on the trip
start = self.env.now
print('%s leaving platform at time %d') % (self.name, env.now)
# processing time
yield self.env.timeout(done_in_SA)
# releasing the section resource
sectionA.release(request_SA)
done_in_SA = 0 # Set to 0 to exit while loop
except simpy.Interrupt:
self.broken = True
delay = random.expovariate(REPAIR_TIME_EXPO)
print('Oh no! Something has caused a delay of %d seconds to %s at time %d') % (delay, self.name, env.now)
done_in_SA -= self.env.now - start # How much time left?
with repair.request(priority = 1) as request_D_SA:
yield request_D_SA
yield self.env.timeout(delay)
self.broken = False
print('Okay all good now, failure fixed on %s at time %d') % (self.name, env.now)
self.num_saf += 1
self.sum_saf += delay
# request platform A
request_PA = platformA.request()
########## SIM ERROR IF FAILURE OCCURS HERE ###########
yield request_PA
done_in_PA = Dwell()
while done_in_PA:
try:
# platform process
start = self.env.now
print('%s arriving to platform A and opening doors at time %d') % (self.name, env.now)
yield self.env.timeout(done_in_PA)
print('%s closing doors, ready to depart platform A at %d\n') % (self.name, env.now)
# releasing the platform resource
platformA.release(request_PA)
done_in_PA = 0 # Set to 0 to exit while loop
except simpy.Interrupt:
self.broken = True
delay = random.expovariate(REPAIR_TIME_EXPO)
print('Oh no! Something has caused a delay of %d seconds to %s at time %d') % (delay, self.name, env.now)
done_in_PA -= self.env.now - start # How much time left?
with repair.request(priority = 1) as request_D_PA:
yield request_D_PA
yield self.env.timeout(delay)
self.broken = False
print('Okay all good now, failure fixed on %s at time %d') % (self.name, env.now)
self.num_saf += 1
self.sum_saf += delay
# Round trip is finished
self.trips_complete += 1
# Defining the failure event
def downtime_train(self):
while True:
yield self.env.timeout(time_to_failure())
if not self.broken:
# Only break the train if it is currently working
self.process.interrupt()
# Setup and start the simulation
print('Train trip simulator')
random.seed(RANDOM_SEED) # Helps with reproduction
# Create an environment and start setup process
env = simpy.Environment()
# Defining resources
platformA = simpy.Resource(env, capacity = 1)
sectionA = simpy.Resource(env, capacity = 1)
repair = simpy.PreemptiveResource(env, capacity = 10)
trains = [Train(env, 'Train %d' % i, repair)
for i in range(NUM_TRAINS)]
# Execute
env.run(until = SIM_TIME)
Your processes request a resource and never release it. That’s why the second trains waits forever for its request to succeed. While it is waiting, the failure process seems to interrupt the process. That’s why you get an error. Please read the guide to resources to understand how SimPy’s resources work and, especially, how to release a resource when you are done.

Using a for loop to access Command Prompt

I want to call a function Christian.exe to the command line to act of a series of files that are indexed as "reentrant_008.sif" (8 is an example number).
"Christian.exe reentrant_00" & num & ".sif reentrant_00" & num & ".pgm" 0 2000 is the text that needs to be fed into the command prompt for the program to execute (num is an arbitrary number)
There are approximately 400 files, so I want to create a vbs code that calls the command prompt for each file until all the files have been accessed so far this is my code:
For
Dim cmdpath
num = CStr(i)
Set wshShell = WScript.CreateObject ("WSCript.shell")
If i < 10 Then
cmdpath = "Christian.exe reentrant_00" & num & ".sif reentrant_00" & num & ".pgm" 0 2000
Else
If i < 100 Then
cmdpath = "Christian.exe reentrant_0" & num & ".sif reentrant_0" & num & ".pgm" 0 2000
Else
cmdpath = "Christian.exe reentrant_" & num & ".sif reentrant_" & num & ".pgm" 0 2000
End If
End If
wshshell.run cmdpath
Next
Problem is that a new command prompt is being called for each file, which is slowing down my computer. How do I ensure that only one command window that addresses all my files is called?
If you look at the documentation for Run you will see two option arguments [intWindowStyle], [bWaitOnReturn]. If you want your EXE to wait before proceeding on the script change your call to this
wshshell.run cmdpath, 0, True
Where 0 will hide the window and True will wait for the program to finish before proceeding in the script. Depending on your needs you could change the number or remove it.
wshshell.run cmdpath,, True
Since you tagged your question with both vbscript and powershell I'm adding a PowerShell solution:
foreach ($i in 1..400) {
$num = "{0:d3}" -f $i
& Christian.exe "reentrant_${num}.sif" "reentrant_${num}.pgm" 0 2000
}
& is the call operator. I recommend using it whenever you run external commands in PowerShell, because otherwise you'll be in for a surprise when you try to run a command from a variable for the first time:
$christian = "Christian.exe"
$christian ... # <-- throws an error, because $christian contains a
# string, which is NOT automagically interpreted
# as a command by PowerShell
& $christian ... # <-- works
-f is the formatting operator, that allows you to create formatted string output. Since your command lines only differ by the zero-padding of the input and output files it's better to build the file names with pre-padded number strings.
I recommend doing the pre-padding in VBScript as well:
For i = 1 To 400
num = Right("000" & i, 3)
cmdpath = "Christian.exe reentrant_" & num & ".sif reentrant_" & num & _
".pgm" 0 2000
wshshell.run cmdpath, 0, True
Next

Execute Process and Redirect Output

I run a process using VBScript. The process usually takes 5-10 minutes to finish and if i run the process standalone then it gives intermittent output while running.
I want to acheive the same logic while running the process using VBScript. Can some one please tell me how to do that?
Set Process = objSWbemServices.Get("Win32_Process")
result = Process.Create(command, , , intProcessID)
waitTillProcessFinishes objSWbemServices,intProcessID
REM How to get the output when the process has finished or while it is running
You don't have access to the output of processes started via WMI. What you could do is redirect the output to a file:
result = Process.Create("cmd /c " & command & " >C:\out.txt", , , intProcessID)
and read the file periodically:
Set fso = CreateObject("Scripting.FileSystemObject")
linesRead = 0
qry = "SELECT * FROM Win32_Process WHERE ProcessID=" & intProcessID
Do While objSWbemServices.ExecQuery(qry).Count > 0
Set f = fso.OpenTextFile("C:\out.txt")
Do Until f.AtEndOfStream
For i = 1 To linesRead : f.SkipLine : Next
WScript.Echo f.ReadLine
linesRead = linesRead + 1
Loop
f.Close
WScript.Sleep 100
Loop
The above is assuming the process is running on the local host. If it's running on a remote host you need to read the file from a UNC path.
For local processes the Exec method would be an alternative, since it gives you access to the process' StdOut:
Set sh = CreateObject("WScript.Shell")
Set p = sh.Exec(command)
Do While p.Status = 0
Do Until p.StdOut.AtEndOfStream
WScript.Echo p.StdOut.ReadLine
Loop
WScript.Sleep 100
Loop
WScript.Echo p.StdOut.ReadAll