Search in sources :

Example 1 with FedFileConfig

use of org.lflang.federated.FedFileConfig in project lingua-franca by lf-lang.

the class FedCLauncher method compileCommandForFederate.

/**
 * Return the compile command for a federate.
 *
 * @param federate The federate to compile.
 * @throws IOException
 */
@Override
protected String compileCommandForFederate(FederateInstance federate) {
    FedFileConfig fedFileConfig = null;
    TargetConfig localTargetConfig = targetConfig;
    try {
        fedFileConfig = new FedFileConfig(fileConfig, federate.name);
    } catch (IOException e) {
        errorReporter.reportError("Failed to create file config for federate " + federate.name);
        return "";
    }
    String commandToReturn = "";
    // FIXME: Hack to add platform support only for linux systems.
    // We need to fix the CMake build command for remote federates.
    String linuxPlatformSupport = "core" + File.separator + "platform" + File.separator + "lf_linux_support.c";
    if (!localTargetConfig.compileAdditionalSources.contains(linuxPlatformSupport)) {
        localTargetConfig.compileAdditionalSources.add(linuxPlatformSupport);
    }
    CCompiler cCompiler = new CCompiler(localTargetConfig, fedFileConfig, errorReporter);
    commandToReturn = String.join(" ", cCompiler.compileCCommand(fileConfig.name + "_" + federate.name, false).toString());
    return commandToReturn;
}
Also used : CCompiler(org.lflang.generator.c.CCompiler) IOException(java.io.IOException) FedFileConfig(org.lflang.federated.FedFileConfig) TargetConfig(org.lflang.TargetConfig)

Example 2 with FedFileConfig

use of org.lflang.federated.FedFileConfig in project lingua-franca by lf-lang.

the class FedLauncher method createLauncher.

/**
 * Create the launcher shell scripts. This will create one or two files
 * in the output path (bin directory). The first has name equal to
 * the filename of the source file without the ".lf" extension.
 * This will be a shell script that launches the
 * RTI and the federates.  If, in addition, either the RTI or any
 * federate is mapped to a particular machine (anything other than
 * the default "localhost" or "0.0.0.0"), then this will generate
 * a shell script in the bin directory with name filename_distribute.sh
 * that copies the relevant source files to the remote host and compiles
 * them so that they are ready to execute using the launcher.
 *
 * A precondition for this to work is that the user invoking this
 * code generator can log into the remote host without supplying
 * a password. Specifically, you have to have installed your
 * public key (typically found in ~/.ssh/id_rsa.pub) in
 * ~/.ssh/authorized_keys on the remote host. In addition, the
 * remote host must be running an ssh service.
 * On an Arch Linux system using systemd, for example, this means
 * running:
 *
 *     sudo systemctl <start|enable> ssh.service
 *
 * Enable means to always start the service at startup, whereas
 * start means to just start it this once.
 *
 * On MacOS, open System Preferences from the Apple menu and
 * click on the "Sharing" preference panel. Select the checkbox
 * next to "Remote Login" to enable it.
 *
 * In addition, every host must have OpenSSL installed, with at least
 * version 1.1.1a.  You can check the version with
 *
 *     openssl version
 *
 * @param coreFiles The files from the core directory that must be
 *  copied to the remote machines.
 * @param federates A list of federate instances in the federation
 * @param federationRTIProperties Contains relevant properties of the RTI.
 *  Can have values for 'host', 'dir', and 'user'
 */
public void createLauncher(ArrayList<String> coreFiles, List<FederateInstance> federates, LinkedHashMap<String, Object> federationRTIProperties) throws IOException {
    // NOTE: It might be good to use screen when invoking the RTI
    // or federates remotely so you can detach and the process keeps running.
    // However, I was unable to get it working properly.
    // What this means is that the shell that invokes the launcher
    // needs to remain live for the duration of the federation.
    // If that shell is killed, the federation will die.
    // Hence, it is reasonable to launch the federation on a
    // machine that participates in the federation, for example,
    // on the machine that runs the RTI.  The command I tried
    // to get screen to work looks like this:
    // ssh -t «target» cd «path»; screen -S «filename»_«federate.name» -L bin/«filename»_«federate.name» 2>&1
    // var outPath = binGenPath
    StringBuilder shCode = new StringBuilder();
    StringBuilder distCode = new StringBuilder();
    shCode.append(getSetupCode() + "\n");
    String distHeader = getDistHeader();
    Object host = federationRTIProperties.get("host");
    Object target = host;
    Object path = federationRTIProperties.get("dir");
    if (path == null)
        path = "LinguaFrancaRemote";
    Object user = federationRTIProperties.get("user");
    if (user != null) {
        target = user + "@" + host;
    }
    String RTILaunchString = getRtiCommand(federates);
    // Launch the RTI in the foreground.
    if (host.equals("localhost") || host.equals("0.0.0.0")) {
        // FIXME: the paths below will not work on Windows
        shCode.append(getLaunchCode(RTILaunchString) + "\n");
    } else {
        // Copy the source code onto the remote machine and compile it there.
        if (distCode.length() == 0)
            distCode.append(distHeader + "\n");
        String logFileName = String.format("log/%s_RTI.log", fileConfig.name);
        // Launch the RTI on the remote machine using ssh and screen.
        // The -t argument to ssh creates a virtual terminal, which is needed by screen.
        // The -S gives the session a name.
        // The -L option turns on logging. Unfortunately, the -Logfile giving the log file name
        // is not standardized in screen. Logs go to screenlog.0 (or screenlog.n).
        // FIXME: Remote errors are not reported back via ssh from screen.
        // How to get them back to the local machine?
        // Perhaps use -c and generate a screen command file to control the logfile name,
        // but screen apparently doesn't write anything to the log file!
        // 
        // The cryptic 2>&1 reroutes stderr to stdout so that both are returned.
        // The sleep at the end prevents screen from exiting before outgoing messages from
        // the federate have had time to go out to the RTI through the socket.
        RTILaunchString = getRtiCommand(federates);
        shCode.append(getRemoteLaunchCode(host, target, logFileName, RTILaunchString) + "\n");
    }
    // Index used for storing pids of federates
    int federateIndex = 0;
    for (FederateInstance federate : federates) {
        if (federate.isRemote) {
            FedFileConfig fedFileConfig = new FedFileConfig(fileConfig, federate.name);
            Path fedRelSrcGenPath = fedFileConfig.getSrcGenBasePath().relativize(fedFileConfig.getSrcGenPath());
            if (distCode.length() == 0)
                distCode.append(distHeader + "\n");
            String logFileName = String.format("log/%s_%s.log", fedFileConfig.name, federate.name);
            String compileCommand = compileCommandForFederate(federate);
            // '''«targetConfig.compiler» src-gen/«topLevelName»_«federate.name».c -o bin/«topLevelName»_«federate.name» -pthread «targetConfig.compilerFlags.join(" ")»'''
            // FIXME: Should $FEDERATION_ID be used to ensure unique directories, executables, on the remote host?
            distCode.append(getDistCode(path, federate, fedRelSrcGenPath, logFileName, fedFileConfig, compileCommand) + "\n");
            String executeCommand = executeCommandForRemoteFederate(federate);
            shCode.append(getFedRemoteLaunchCode(federate, path, logFileName, executeCommand, federateIndex++) + "\n");
        } else {
            String executeCommand = executeCommandForLocalFederate(fileConfig, federate);
            shCode.append(getFedLocalLaunchCode(federate, executeCommand, federateIndex++) + "\n");
        }
    }
    if (host.equals("localhost") || host.equals("0.0.0.0")) {
        // Local PID managements
        shCode.append("echo \"#### Bringing the RTI back to foreground so it can receive Control-C.\"" + "\n");
        shCode.append("fg %1" + "\n");
    }
    // Wait for launched processes to finish
    shCode.append(String.join("\n", "echo \"RTI has exited. Wait for federates to exit.\"", "# Wait for launched processes to finish.", "# The errors are handled separately via trap.", "for pid in \"${pids[@]}\"", "do", "    wait $pid", "done", "echo \"All done.\"") + "\n");
    // Write the launcher file.
    // Delete file previously produced, if any.
    File file = fileConfig.binPath.resolve(fileConfig.name).toFile();
    if (file.exists()) {
        file.delete();
    }
    FileOutputStream fOut = new FileOutputStream(file);
    fOut.write(shCode.toString().getBytes());
    fOut.close();
    if (!file.setExecutable(true, false)) {
        errorReporter.reportWarning("Unable to make launcher script executable.");
    }
    // Write the distributor file.
    // Delete the file even if it does not get generated.
    file = fileConfig.binPath.resolve(fileConfig.name + "_distribute.sh").toFile();
    if (file.exists()) {
        file.delete();
    }
    if (distCode.length() > 0) {
        fOut = new FileOutputStream(file);
        fOut.write(distCode.toString().getBytes());
        fOut.close();
        if (!file.setExecutable(true, false)) {
            errorReporter.reportWarning("Unable to make distributor script executable.");
        }
    }
}
Also used : Path(java.nio.file.Path) FederateInstance(org.lflang.federated.FederateInstance) FileOutputStream(java.io.FileOutputStream) FedFileConfig(org.lflang.federated.FedFileConfig) File(java.io.File)

Example 3 with FedFileConfig

use of org.lflang.federated.FedFileConfig in project lingua-franca by lf-lang.

the class PythonGenerator method doGenerate.

/**
 * Generate C code from the Lingua Franca model contained by the
 *  specified resource. This is the main entry point for code
 *  generation.
 *  @param resource The resource containing the source code.
 *  @param context Context relating to invocation of the code generator.
 */
@Override
public void doGenerate(Resource resource, LFGeneratorContext context) {
    // If there are federates, assign the number of threads in the CGenerator to 1
    if (isFederated) {
        targetConfig.threads = 1;
    }
    // Prevent the CGenerator from compiling the C code.
    // The PythonGenerator will compiler it.
    boolean compileStatus = targetConfig.noCompile;
    targetConfig.noCompile = true;
    // Force disable the CMake because
    targetConfig.useCmake = false;
    // it interferes with the Python target functionality
    int cGeneratedPercentProgress = (IntegratedBuilder.VALIDATED_PERCENT_PROGRESS + 100) / 2;
    super.doGenerate(resource, new SubContext(context, IntegratedBuilder.VALIDATED_PERCENT_PROGRESS, cGeneratedPercentProgress));
    SubContext compilingFederatesContext = new SubContext(context, cGeneratedPercentProgress, 100);
    targetConfig.noCompile = compileStatus;
    if (errorsOccurred()) {
        context.unsuccessfulFinish();
        return;
    }
    String baseFileName = topLevelName;
    // Keep a separate file config for each federate
    FileConfig oldFileConfig = fileConfig;
    var federateCount = 0;
    Map<Path, CodeMap> codeMaps = new HashMap<>();
    for (FederateInstance federate : federates) {
        federateCount++;
        if (isFederated) {
            topLevelName = baseFileName + '_' + federate.name;
            try {
                fileConfig = new FedFileConfig(fileConfig, federate.name);
            } catch (IOException e) {
                throw Exceptions.sneakyThrow(e);
            }
        }
        // Don't generate code if there is no main reactor
        if (this.main != null) {
            try {
                Map<Path, CodeMap> codeMapsForFederate = generatePythonFiles(federate);
                codeMaps.putAll(codeMapsForFederate);
                PyUtil.copyTargetFiles(fileConfig);
                if (!targetConfig.noCompile) {
                    compilingFederatesContext.reportProgress(String.format("Validating %d/%d sets of generated files...", federateCount, federates.size()), 100 * federateCount / federates.size());
                    // If there are no federates, compile and install the generated code
                    new PythonValidator(fileConfig, errorReporter, codeMaps, protoNames).doValidate(context);
                    if (!errorsOccurred() && !Objects.equal(context.getMode(), LFGeneratorContext.Mode.LSP_MEDIUM)) {
                        compilingFederatesContext.reportProgress(String.format("Validation complete. Compiling and installing %d/%d Python modules...", federateCount, federates.size()), 100 * federateCount / federates.size());
                        // Why is this invoked here if the current federate is not a parameter?
                        pythonCompileCode(context);
                    }
                } else {
                    System.out.println(PythonInfoGenerator.generateSetupInfo(fileConfig));
                }
            } catch (Exception e) {
                throw Exceptions.sneakyThrow(e);
            }
            if (!isFederated) {
                System.out.println(PythonInfoGenerator.generateRunInfo(fileConfig, topLevelName));
            }
        }
        fileConfig = oldFileConfig;
    }
    if (isFederated) {
        System.out.println(PythonInfoGenerator.generateFedRunInfo(fileConfig));
    }
    // Restore filename
    topLevelName = baseFileName;
    if (errorReporter.getErrorsOccurred()) {
        context.unsuccessfulFinish();
    } else if (!isFederated) {
        context.finish(GeneratorResult.Status.COMPILED, topLevelName + ".py", fileConfig.getSrcGenPath(), fileConfig, codeMaps, "python3");
    } else {
        context.finish(GeneratorResult.Status.COMPILED, fileConfig.name, fileConfig.binPath, fileConfig, codeMaps, "bash");
    }
}
Also used : Path(java.nio.file.Path) FedFileConfig(org.lflang.federated.FedFileConfig) FileConfig(org.lflang.FileConfig) HashMap(java.util.HashMap) IOException(java.io.IOException) IOException(java.io.IOException) CodeMap(org.lflang.generator.CodeMap) SubContext(org.lflang.generator.SubContext) FederateInstance(org.lflang.federated.FederateInstance) FedFileConfig(org.lflang.federated.FedFileConfig)

Aggregations

FedFileConfig (org.lflang.federated.FedFileConfig)3 IOException (java.io.IOException)2 Path (java.nio.file.Path)2 FederateInstance (org.lflang.federated.FederateInstance)2 File (java.io.File)1 FileOutputStream (java.io.FileOutputStream)1 HashMap (java.util.HashMap)1 FileConfig (org.lflang.FileConfig)1 TargetConfig (org.lflang.TargetConfig)1 CodeMap (org.lflang.generator.CodeMap)1 SubContext (org.lflang.generator.SubContext)1 CCompiler (org.lflang.generator.c.CCompiler)1