Skip to content

Remoting

OperationAddFrontendWin32

Bases: OperationAddRemotingWinAbstract

Add a 32-bit front-end proxy to a 32-bit Windows FMU.

Wraps the existing 32-bit DLL behind a remoting front-end for process isolation, without changing the target platform.

Attributes:

Name Type Description
bitness_from str

"win32" — the original FMU platform.

bitness_to str

"win32" — same platform (front-end only).

Source code in fmu_manipulation_toolbox/remoting.py
class OperationAddFrontendWin32(OperationAddRemotingWinAbstract):
    """Add a 32-bit front-end proxy to a 32-bit Windows FMU.

    Wraps the existing 32-bit DLL behind a remoting front-end for
    process isolation, without changing the target platform.

    Attributes:
        bitness_from (str): `"win32"` — the original FMU platform.
        bitness_to (str): `"win32"` — same platform (front-end only).
    """
    bitness_from = "win32"
    bitness_to = "win32"

OperationAddFrontendWin64

Bases: OperationAddRemotingWinAbstract

Add a 64-bit front-end proxy to a 64-bit Windows FMU.

Wraps the existing 64-bit DLL behind a remoting front-end for process isolation, without changing the target platform.

Attributes:

Name Type Description
bitness_from str

"win64" — the original FMU platform.

bitness_to str

"win64" — same platform (front-end only).

Source code in fmu_manipulation_toolbox/remoting.py
class OperationAddFrontendWin64(OperationAddRemotingWinAbstract):
    """Add a 64-bit front-end proxy to a 64-bit Windows FMU.

    Wraps the existing 64-bit DLL behind a remoting front-end for
    process isolation, without changing the target platform.

    Attributes:
        bitness_from (str): `"win64"` — the original FMU platform.
        bitness_to (str): `"win64"` — same platform (front-end only).
    """
    bitness_from = "win64"
    bitness_to = "win64"

OperationAddRemotingWin32

Bases: OperationAddRemotingWinAbstract

Add 32-bit remoting proxy to a 64-bit Windows FMU.

Copies a 32-bit client DLL and a 64-bit server executable so that a 32-bit simulation host can load and run the originally 64-bit FMU.

Attributes:

Name Type Description
bitness_from str

"win64" — the original FMU platform.

bitness_to str

"win32" — the target platform to add.

Source code in fmu_manipulation_toolbox/remoting.py
class OperationAddRemotingWin32(OperationAddRemotingWinAbstract):
    """Add 32-bit remoting proxy to a 64-bit Windows FMU.

    Copies a 32-bit client DLL and a 64-bit server executable so that
    a 32-bit simulation host can load and run the originally 64-bit FMU.

    Attributes:
        bitness_from (str): `"win64"` — the original FMU platform.
        bitness_to (str): `"win32"` — the target platform to add.
    """
    bitness_from = "win64"
    bitness_to = "win32"

OperationAddRemotingWin64

Bases: OperationAddRemotingWinAbstract

Add 64-bit remoting proxy to a 32-bit Windows FMU.

Copies a 64-bit client DLL and a 32-bit server executable so that a 64-bit simulation host can load and run the originally 32-bit FMU.

Attributes:

Name Type Description
bitness_from str

"win32" — the original FMU platform.

bitness_to str

"win64" — the target platform to add.

Source code in fmu_manipulation_toolbox/remoting.py
class OperationAddRemotingWin64(OperationAddRemotingWinAbstract):
    """Add 64-bit remoting proxy to a 32-bit Windows FMU.

    Copies a 64-bit client DLL and a 32-bit server executable so that
    a 64-bit simulation host can load and run the originally 32-bit FMU.

    Attributes:
        bitness_from (str): `"win32"` — the original FMU platform.
        bitness_to (str): `"win64"` — the target platform to add.
    """
    bitness_from = "win32"
    bitness_to = "win64"

OperationAddRemotingWinAbstract

Bases: OperationAbstract

Base class for adding Windows remoting or front-end support to an FMU.

Copies the remoting client and server binaries into the FMU so that a 32-bit FMU can be called from a 64-bit host (or vice versa), or a front-end proxy can be added for same-bitness isolation.

This operation is only supported for FMI 2.0 FMUs on Windows.

Subclasses must set bitness_from and bitness_to to define the remoting direction.

Attributes:

Name Type Description
bitness_from str | None

Source platform directory ("win32" or "win64").

bitness_to str | None

Target platform directory ("win32" or "win64").

vr dict[str, list[int]]

Value references grouped by FMI type ("Real", "Integer", "Boolean").

nb_input int

Number of input and parameter ports.

nb_output int

Number of output, local, and other ports.

Raises:

Type Description
OperationError

If the FMU is not FMI 2.0 or the source platform binaries are missing.

Source code in fmu_manipulation_toolbox/remoting.py
class OperationAddRemotingWinAbstract(OperationAbstract):
    """Base class for adding Windows remoting or front-end support to an FMU.

    Copies the remoting client and server binaries into the FMU so that
    a 32-bit FMU can be called from a 64-bit host (or vice versa), or
    a front-end proxy can be added for same-bitness isolation.

    This operation is only supported for FMI 2.0 FMUs on Windows.

    Subclasses must set `bitness_from` and `bitness_to` to define the
    remoting direction.

    Attributes:
        bitness_from (str | None): Source platform directory
            (`"win32"` or `"win64"`).
        bitness_to (str | None): Target platform directory
            (`"win32"` or `"win64"`).
        vr (dict[str, list[int]]): Value references grouped by FMI type
            (`"Real"`, `"Integer"`, `"Boolean"`).
        nb_input (int): Number of input and parameter ports.
        nb_output (int): Number of output, local, and other ports.

    Raises:
        OperationError: If the FMU is not FMI 2.0 or the source platform
            binaries are missing.
    """
    bitness_from = None
    bitness_to = None

    def __repr__(self):
        return f"Add '{self.bitness_to}' remoting on '{self.bitness_from}' FMU"

    def __init__(self):
        self.vr = {
            "Real": [],
            "Integer": [],
            "Boolean": []
        }
        self.nb_input = 0
        self.nb_output = 0

    def fmi_attrs(self, attrs):
        if not attrs["fmiVersion"] == "2.0":
            raise OperationError(f"Adding remoting is only available for FMI-2.0")

    def cosimulation_attrs(self, attrs):
        fmu_bin = {
            "win32": Path(self.fmu.tmp_directory) / "binaries" / "win32",
            "win64": Path(self.fmu.tmp_directory) / "binaries" / "win64",
        }

        if not fmu_bin[self.bitness_from].is_dir():
            raise OperationError(f"{self.bitness_from} interface does not exist")

        if fmu_bin[self.bitness_to].is_dir():
            logger.info(f"{self.bitness_to} already exists. Add front-end.")
            shutil.move(fmu_bin[self.bitness_to] / Path(attrs['modelIdentifier']).with_suffix(".dll"),
                        fmu_bin[self.bitness_to] / Path(attrs['modelIdentifier']).with_suffix("-remoted.dll"))
        else:
            fmu_bin[self.bitness_to].mkdir()

        to_path = Path(__file__).parent / "resources" / self.bitness_to
        try:
            shutil.copyfile(to_path / "client_sm.dll",
                            fmu_bin[self.bitness_to] / Path(attrs['modelIdentifier']).with_suffix(".dll"))
        except FileNotFoundError as e:
            logger.critical(f"Cannot add remoting client: {e}")

        from_path = Path(__file__).parent / "resources" / self.bitness_from
        try:
            shutil.copyfile(from_path / "server_sm.exe",
                            fmu_bin[self.bitness_from] / "server_sm.exe")
        except FileNotFoundError as e:
            logger.critical(f"Cannot add remoting server: {e}")

        shutil.copyfile(Path(__file__).parent / "resources" / "license.txt",
                        fmu_bin[self.bitness_to] / "license.txt")

    def port_attrs(self, fmu_port) -> int:
        vr = int(fmu_port["valueReference"])
        causality = fmu_port.get("causality", "local")
        try:
            self.vr[fmu_port.fmi_type].append(vr)
            if causality in ("input", "parameter"):
                self.nb_input += 1
            else:
                self.nb_output += 1
        except KeyError:
            logger.error(f"Type '{fmu_port.fmi_type}' is not supported by remoting.")

        return 0

    def closure(self):
        target_dir = Path(self.fmu.tmp_directory) / "resources"
        if not target_dir.is_dir():
            target_dir.mkdir()

        logger.info(f"Remoting nb input port: {self.nb_input}")
        logger.info(f"Remoting nb output port: {self.nb_output}")
        with open(target_dir/ "remoting_table.txt", "wt") as file:
            for fmi_type in ('Real', 'Integer', 'Boolean'):
                print(len(self.vr[fmi_type]), file=file)
            for fmi_type in ('Real', 'Integer', 'Boolean'):
                for vr in sorted(self.vr[fmi_type]):
                    print(vr, file=file)