I want to use the system() function to start a simulator app written in nodejs. The simulator is launched via a control script. The script is added as manager in the WinCC OA Console. When I start the control script via the WinCC OA Console, the simulator is running, but when I stop the control script, the simulator keeps running. Is it possible to stop the task initiated by system() on stopping the control script?
An extra question: when running the simulator outside of WinCC OA, the output is dumped to the command line and is instantly visible. Using system() it is possible to catch stdout in a string variable, but this is only visible or accessible after ending the simulator and returning from the system() call. When running the simulator via WinCC OA, nothing of the simulator is visible. Is it possible to show a command line screen or a GUI on starting with system()?
main()
{
string path = getPath(MSG_REL_PATH);
dyn_string splittedPath = strsplit(path, "/");
string mainProjectPath;
// get path of main Project
for (int i = 1; i <= (dynlen(splittedPath) - 2); i++)
{
mainProjectPath += (splittedPath[i] + "/");
}
//assemble path to simulator
string sPath = mainProjectPath + "simulator";
DebugN("simulatorPath " + sPath);
string stdOut, stdErr;
mapping options = makeMapping("program", "D:\\Programs\\nodejs\\npm.cmd", "arguments", makeDynString("start"), "workingDir", sPath);
int pid = system(options, stdOut, stdErr);
}
Is it possible to stop the task initiated by system() on stopping the control script?
As the
systemcall simply passes the desired command to the operating system, the operating system will decide whether you specified something that has to be wrapped and executed in a shell (like a batch file) or can be launched as process directly (e.g. when you specify an executable). In your case you have a windows operating system and you have specified a npm.cmd file (where cmd files are more or less the same as batch (.bat) files).In order to execute the commands within the specified file, a command processor has to be hosted which is usually
cmd.exe. So a new console process will be started, hosting the command processor which will execute the commands within the npm.cmd script.So how can we now stop this launched process when we want to?
There are several possible ways to achieve this (all are of varying complexity and required more or less workload):
Approach 1:
Find the process id of the launched command processor and terminate it whenever you want to.
The first one can be done with a command called
tasklist.According to the documentation there are several filter possibilities specifiable by a command line argument called /fi like IMAGENAME, WINDOWTITLE and so on in form of a string. With a command line argument called /fo the format of the output can be specified. So as an example
tasklist /V /FI "IMAGENAME eq cmd.exe" /FO CSVwould return all running command processors with their process id and their window title. Usually the window title includes the command that is executed, in the example above it would be exactly C:\WINDOWS\system32\cmd.exe - tasklist /V /FI "IMAGENAME eq cmd.exe" /FO CSVOnce the process id is retrieved a command called
takskillcan be used to kill the desired process.Sidenote:
Searching for the process id itself can be omitted by directly using the filter capabilities of the
taskkillcommand (see taskkill - parameters for details).Disadvantages of this approach:
Approach 2:
Using the possibilities of the
systemcommand to retrieve the process id of the launched process. According to the documentation ofsystemTherefore all that is needed is to validate the return value of the
systemfunction call and use the return value as process id. Whenever required, a call totaskkillas shown in Approach 1 can be executed to terminate the process.Your code seem to already partially take advantage of this approach as you already assign the return value of the call to the
systemfunction to a variable calledpid. What's missing is adding a value of -1 fortimeoutto the mapping.Approach 3:
Using the pmon for the dirty work. As stated in the documentation of the pmon it supports a rather simple command protocol. It might be easier to use the supported commands in order to start and stop the manager (simulator) at will. Instead of manually implementing that functionality I would suggest using functionality provided via
scripts\libs\pmon.ctlwhich is located in the WinCC OA version directory. The library includes a method calledpmonStopManagerwhich stops a manager. The correct usage for the functions of thepmon.ctlcan be obtained by inspecting code in thepanels\projAdmin\console.pnlpanel.When running the simulator via WinCC OA, nothing of the simulator is visible. Is it possible to show a command line screen or a GUI on starting with system()?
When a function call to the
systemfunction is executed where arguments forstdoutandstderrare specified, the underlaying operating system process is launched with redirected output- and error streams. Thus all information that is outputted by the launched process will be redirected by the operating system so the hosting process (in your example the control manager) can access this information.One way to solve this issue might be to force the launched process to write it's output into specified files which can be polled for changes. This can be done by specifying filenames for stderr and stdout in the options mapping for the
systemfunction call.Another way could be to intentionally launch the process in a way that a new console window opens like starting a command shell that hosts the executable you are starting. On a windows operating system (depending on the operating system version you are using) you can achieve this with hacks like using the
startcommand. I am not sure if this will work in your case but it should look something like the following code snippet BUT be advised, that in this case you will not get the process id of your process as a return value but the id of the command shell that was used (and has already terminated) to launch your process.