import grpc import os import sys import socket import logging import time sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'generated')) import App_pb2 import App_pb2_grpc from Definitions_pb2 import Empty import RiaVersionInfo from rips.Commands import Commands from rips.Project import Project class Instance: """The ResInsight Instance class. Use to launch or find existing ResInsight instances Attributes: launched (bool): Tells us whether the application was launched as a new process. If the application was launched we may need to close it when exiting the script. commands (Commands): Command executor. Set when creating an instance. project (Project): Current project in ResInsight. Set when creating an instance and updated when opening/closing projects. """ @staticmethod def __is_port_in_use(port): with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.settimeout(0.2) return s.connect_ex(('localhost', port)) == 0 @staticmethod def launch(resinsight_executable = '', console = False, launch_port = -1, command_line_parameters=[]): """ Launch a new Instance of ResInsight. This requires the environment variable RESINSIGHT_EXECUTABLE to be set or the parameter resinsight_executable to be provided. The RESINSIGHT_GRPC_PORT environment variable can be set to an alternative port number. Args: resinsight_executable (str): Path to a valid ResInsight executable. If set will take precedence over what is provided in the RESINSIGHT_EXECUTABLE environment variable. console (bool): If True, launch as console application, without GUI. launch_port(int): If -1 will use the default port of 50051 or look for RESINSIGHT_GRPC_PORT if anything else, ResInsight will try to launch with this port command_line_parameters(list): Additional command line parameters as string entries in the list. Returns: Instance: an instance object if it worked. None if not. """ port = 50051 port_env = os.environ.get('RESINSIGHT_GRPC_PORT') if port_env: port = int(port_env) if launch_port is not -1: port = launch_port if not resinsight_executable: resinsight_executable = os.environ.get('RESINSIGHT_EXECUTABLE') if not resinsight_executable: print('ERROR: Could not launch ResInsight because the environment variable' ' RESINSIGHT_EXECUTABLE is not set') return None while Instance.__is_port_in_use(port): port += 1 print('Port ' + str(port)) print('Trying to launch', resinsight_executable) if isinstance(command_line_parameters, str): command_line_parameters = [str] parameters = ["ResInsight", "--server", str(port)] + command_line_parameters if console: print("Launching as console app") parameters.append("--console") # Stringify all parameters for i in range(0, len(parameters)): parameters[i] = str(parameters[i]) pid = os.spawnv(os.P_NOWAIT, resinsight_executable, parameters) if pid: instance = Instance(port=port, launched=True) return instance return None @staticmethod def find(start_port = 50051, end_port = 50071): """ Search for an existing Instance of ResInsight by testing ports. By default we search from port 50051 to 50071 or if the environment variable RESINSIGHT_GRPC_PORT is set we search RESINSIGHT_GRPC_PORT to RESINSIGHT_GRPC_PORT+20 Args: start_port (int): start searching from this port end_port (int): search up to but not including this port """ port_env = os.environ.get('RESINSIGHT_GRPC_PORT') if port_env: start_port = int(port_env) end_port = start_port + 20 for try_port in range(start_port, end_port): if Instance.__is_port_in_use(try_port): return Instance(port=try_port) print('Error: Could not find any ResInsight instances responding between ports ' + str(start_port) + ' and ' + str(end_port)) return None def __check_version(self): try: major_version_ok = self.major_version() == int(RiaVersionInfo.RESINSIGHT_MAJOR_VERSION) minor_version_ok = self.minor_version() == int(RiaVersionInfo.RESINSIGHT_MINOR_VERSION) return True, major_version_ok and minor_version_ok except: return False, False def __init__(self, port = 50051, launched = False): """ Attempts to connect to ResInsight at aa specific port on localhost Args: port(int): port number """ logging.basicConfig() location = "localhost:" + str(port) self.channel = grpc.insecure_channel(location, options=[('grpc.enable_http_proxy', False)]) self.launched = launched # Main version check package self.app = self.app = App_pb2_grpc.AppStub(self.channel) connection_ok = False version_ok = False if self.launched: for i in range(0, 10): connection_ok, version_ok = self.__check_version() if connection_ok: break time.sleep(1.0) else: connection_ok, version_ok = self.__check_version() if not connection_ok: if self.launched: raise Exception('Error: Could not connect to resinsight at ', location, ' after trying 10 times with 1 second apart') else: raise Exception('Error: Could not connect to resinsight at ', location) exit(1) if not version_ok: raise Exception('Error: Wrong Version of ResInsight at ', location) # Service packages self.commands = Commands(self.channel) self.project = Project(self.channel) path = os.getcwd() self.commands.set_start_dir(path=path) def __version_message(self): return self.app.GetVersion(Empty()) def major_version(self): """Get an integer with the major version number""" return self.__version_message().major_version def minor_version(self): """Get an integer with the minor version number""" return self.__version_message().minor_version def patch_version(self): """Get an integer with the patch version number""" return self.__version_message().patch_version def version_string(self): """Get a full version string, i.e. 2019.04.01""" return str(self.major_version()) + "." + str(self.minor_version()) + "." + str(self.patch_version()) def exit(self): """Tell ResInsight instance to quit""" print("Telling ResInsight to Exit") return self.app.Exit(Empty()) def is_console(self): """Returns true if the connected ResInsight instance is a console app""" return self.app.GetRuntimeInfo(Empty()).app_type == App_pb2.ApplicationTypeEnum.Value('CONSOLE_APPLICATION') def is_gui(self): """Returns true if the connected ResInsight instance is a GUI app""" return self.app.GetRuntimeInfo(Empty()).app_type == App_pb2.ApplicationTypeEnum.Value('GUI_APPLICATION')