4
$\begingroup$

I have a package that initiates some periodic tasks via the ScheduledTasks functionality. The tasks in question also require the use of parallel tools and the launching of external commands. Everything works if I load the package interactively either from Mathematica or MathKernel. However, I need a way to script this procedure, so that Mathematica is started and the package is loaded automatically.

So far, I have tried loading the core package file into the kernel (i.e. MathKernel <MyPackageSource.m), but the kernel exits as soon as the script file is loaded, whereas I need it to stay open to continue to service the ScheduledTasks. I also tried putting the package into the Autoload directory and starting up MathKernel to load it, but encountered a problem because my package uses Import to import a text file, and apparently the import infrastructure is not yet ready for use at the time my package is autoloaded.

So, my question is two-fold: 1. Is there a way to use Import[..., "Text"] inside an autoloaded package?, and 2. Is there an altogether better way to load a package into a Mathematica session in the context of a batch process?

$\endgroup$
3
  • $\begingroup$ See this and linked topics: mathematica.stackexchange.com/q/131856/5478 $\endgroup$
    – Kuba
    Commented May 2, 2017 at 19:05
  • $\begingroup$ I will try the suggestions there in the context of my autoload solution, but what I'm really looking for is a way to do this without using Autoload. Is there a way to start mathematica and load an arbitrary package, from the command line? $\endgroup$
    – tavr
    Commented May 2, 2017 at 19:11
  • $\begingroup$ I see, ok, let's wait for someone with more experience in this area. $\endgroup$
    – Kuba
    Commented May 2, 2017 at 21:07

2 Answers 2

2
$\begingroup$

I have tried loading the core package file into the kernel, but the kernel exits as soon as the script file is loaded, whereas I need it to stay open to continue to service the ScheduledTasks.

I think the kernel exiting is the problem here. You just need the main "thread" to stay in an idle loop so the background can run.

Here's a script file that starts a scheduled task and then stays looping while the task runs:

Print["Watching for file changes in ", Directory[]]
files = FileNames[];
checkForChangedFiles[] := 
(
    latestfiles = FileNames[];
    newfiles = Complement[latestfiles, files];
    If[newfiles =!= {}, Print["Found new files: ", newfiles]];
    removed = Complement[files, latestfiles];
    If[removed =!= {}, Print["Files removed: ", removed];];
    files = latestfiles;
)

task = RunScheduledTask[checkForChangedFiles[], 2];

While[True,
    Pause[1]
]

Call this file watcher.m, and invoke it from command line:

$ math -script watcher.m

You can test it by adding or removing files from a separate process.

In your case, you need to load a package. You can add that code in the beginning, where I have a function definition and before the RunScheduledTask call.

$\endgroup$
4
  • $\begingroup$ I can confirm that the infinite loop keeps the kernel open when you load the package file as a script, and allows the scheduled tasks to be serviced. I was hoping for a more robust approach, but this does work. $\endgroup$
    – tavr
    Commented May 19, 2017 at 20:36
  • $\begingroup$ What's not robust about it? $\endgroup$
    – Joel Klein
    Commented May 19, 2017 at 23:01
  • $\begingroup$ Pause is keeping the master kernel open, but at the cost of blocking all further evaluations. This isn't much of a concern in batch mode, but it means that I cannot simply load my package in a batch procedure, but must nest it inside a script that keeps the master kernel spinning. Again, not a big deal, but not ideal either. I think what's needed is an option to keep math executable open indefinitely, or else math should respect pending ScheduledTask's and not exit at end of script. $\endgroup$
    – tavr
    Commented May 20, 2017 at 0:37
  • $\begingroup$ "at the cost of blocking all further evaluations" -- what do you mean? $\endgroup$
    – Joel Klein
    Commented Jun 6, 2017 at 21:39
0
$\begingroup$

I made an automatic water misting fan. The temperature monitor software checks temperature at certain interval and decides if it should turn on/off the water misting fan. I think what the temperature monitor software does might meet your needs.

Please note that you have to add JLink.jar to this java project.

/Applications/Mathematica.app/Contents/SystemFiles/Links/JLink/JLink.jar

If you use Linux or Windows, you have to modify the path of MathKernel to create KernelLink.

/Applications/Mathematica.app/Contents/MacOS/MathKernel

Java (JDK 8):

import com.wolfram.jlink.KernelLink;
import com.wolfram.jlink.MathLinkException;
import com.wolfram.jlink.MathLinkFactory;

import java.util.Optional;

public class ScheduledTask {
    public static void main(String[] argv) throws MathLinkException {
        createKernelLink()
            .map(link -> loadPackage(link))
            .map(link -> checkTemperature(link));
    }

    private static Optional<KernelLink> createKernelLink() {
        String[] options = {"-linkmode", "launch", "-linkname",
            "/Applications/Mathematica.app/Contents/MacOS/MathKernel"};
        try {
            KernelLink link = MathLinkFactory.createKernelLink(options);
            return Optional.of(link);
        } catch (MathLinkException e) {
            System.out.println("Fatal error opening link: " + e.getMessage());
            return Optional.empty();
        }
    }

    private static KernelLink loadPackage(KernelLink kernelLink) {
        String packagePath = System.getProperty("user.dir") + "/ac.m";
        String loadPackage = "Get[\"" + packagePath + "\"]";
        evaluate(kernelLink, loadPackage);
        return kernelLink;
    }

    private static void evaluate(KernelLink kernelLink, String loadPackage) {
        try {
            kernelLink.discardAnswer();
            kernelLink.evaluate(loadPackage);
            kernelLink.discardAnswer();
        } catch (MathLinkException e) {
            e.printStackTrace();
        }
    }

    private static Void checkTemperature(KernelLink link) {
        int interval = 2000;
        String expr  = "checkTemperature[]";
        return runScheduledTask(link, interval, expr);
    }

    private static Void runScheduledTask(KernelLink link, int interval, String expr) {
        Runnable task = () -> {
            try {
                link.evaluate(expr);
                link.discardAnswer();
                System.out.print(".");
            } catch (MathLinkException e) {
                e.printStackTrace();
            }
        };

        while(true) {
            try{
                task.run();
                Thread.sleep(interval);
            }
            catch(Exception e){
                link.close();
            }
        }
    }
}

Wolfram:

(* wemo switch https://github.com/iancmcc/ouimeaux *)
(* istats https://github.com/Chris911/iStats *)

cpuTemperature[] := Module[{command, xs, cs, position},
  command = {"/usr/local/bin/istats", "extra"};
  xs = RunProcess[command]["StandardOutput"] // ImportString[#, "CSV"] & // First;
  cs = Map[Select[PrintableASCIIQ], Characters@xs] // Flatten;
  position = FirstPosition[cs, "["] // First;
  Take[cs, position-4] // StringJoin // StringCases[#, x__ ~~ ":" ~~ y__->y]&//First//ToExpression]

temperatureHighQ[highTemperature_] := Module[{temperature},
  temperature = cpuTemperature[];
  If[temperature > highTemperature, (Pause[1];
  cpuTemperature[] > temperature), False]]

temperatureLowQ[lowTemperature_] := Module[{temperature},
  temperature = cpuTemperature[];
  If[temperature < lowTemperature, (Pause[1];
  cpuTemperature[] < temperature), False]]

wemoSwitch[x_] := Module[{command},
  If[x, Print["High"], Print["Low"]];
  command =  {"/usr/local/bin/wemo", "switch", "aircondition",
    If[x, "on", "off"]};
  RunProcess[command]];

checkTemperature[] := Module[{high, low},
  high = 50;
  low  = 46;
  Which[
    temperatureHighQ[high], wemoSwitch[True] ,
    temperatureLowQ[low], wemoSwitch[False]]]

aircondition[] := Module[{task, interval},
  interval = 5;
  task = RunScheduledTask[checkTemperature[], interval];
  While[True, Pause[2]]]

wemoSwitch[False]
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.