Просмотр исходного кода

Add auto-build-clean-release-tools.

prc 1 год назад
Родитель
Сommit
4d002e13b1

+ 65 - 0
build_all.py

@@ -0,0 +1,65 @@
+import os
+import sys
+
+from winpty import PtyProcess
+
+
+class BuildAll:
+    def __init__(self):
+        self.current_dir = os.path.dirname(os.path.abspath(__file__))
+
+    @staticmethod
+    def __get_python_env_name_from_args():
+        for arg in sys.argv[1:]:
+            if arg.startswith("-python_env_name="):
+                return arg.split("=")[1]
+        return None
+
+    def build_all(self):
+        python_env_name = self.__get_python_env_name_from_args()
+        if not python_env_name:
+            python_env_name = input("请输入conda python环境的名称(例如:base): ")
+        # confirm_remove = input("是否删除输出目录及其所有内容? (y/N) ").strip().lower()
+        confirm_remove = "y"
+
+        pyinstaller_confirm_flag = "-y" if confirm_remove == 'y' else ""
+
+        commands = [
+            (f"conda activate {python_env_name} && Pyinstaller {pyinstaller_confirm_flag} -D launcher.py",
+             self.current_dir),
+            (f"conda activate {python_env_name} && Pyinstaller {pyinstaller_confirm_flag} -D server.py",
+             os.path.join(self.current_dir, "server")),
+            ("npm run build:win", os.path.join(self.current_dir, "client"))
+        ]
+
+        for cmd, cwd in commands:
+            if not self.__pre_execute_check(cmd, cwd):
+                return
+            self.__execute_cmd_with_pty(cmd, cwd)
+
+    @staticmethod
+    def __pre_execute_check(cmd, cwd):
+        if "Pyinstaller" in cmd:
+            script_name = cmd.split(" ")[-1]
+            absolute_path = os.path.join(cwd, script_name)
+            if not os.path.exists(absolute_path):
+                print(f"Error: Python脚本 '{absolute_path}' 不存在!")
+                return False
+        elif cmd.startswith("npm "):
+            pass
+        return True
+
+    @staticmethod
+    def __execute_cmd_with_pty(cmd, cwd=None):
+        process = PtyProcess.spawn(["cmd", "/c", cmd], cwd=cwd)
+        while process.isalive():
+            try:
+                output = process.read()
+                print(output, end='')
+            except EOFError:
+                break  # 当伪终端关闭时跳出循环
+
+
+if __name__ == "__main__":
+    builder = BuildAll()
+    builder.build_all()

+ 39 - 0
build_and_release.py

@@ -0,0 +1,39 @@
+from winpty import PtyProcess
+
+
+class BuildAndRelease:
+
+    def __init__(self):
+        self.commands = [
+            "python clean_build_all.py",
+            "python build_all.py",
+            "python make_release.py",
+            "python clean_build_all.py"
+        ]
+        self.user_input = input("请输入conda python环境的名称(例如:base): ")
+
+    def __run_command(self, command):
+        try:
+            # 如果是"python build_all.py"命令,先获取用户输入
+            if "python build_all.py" == command:
+                command += " "
+                command += f"-python_env_name={self.user_input}"
+            # 使用PtyProcess来运行命令
+            proc = PtyProcess.spawn(["powershell", "-Command", command])
+            # 读取输出并打印到当前终端
+            while True:
+                data = proc.read()
+                if not data:
+                    break
+                print(data, end='')
+        except Exception as e:
+            print(f"Error executing command: {command}. Error: {e}")
+
+    def build_and_release(self):
+        for command in self.commands:
+            self.__run_command(command)
+
+
+if __name__ == "__main__":
+    bar = BuildAndRelease()
+    bar.build_and_release()

+ 41 - 0
clean_build_all.py

@@ -0,0 +1,41 @@
+import glob
+import os
+import shutil
+
+
+class CleanBuildAll:
+    def __init__(self, base_path=None):
+        # 如果没有提供基准路径,使用当前文件的路径
+        self.base_path = base_path if base_path else os.path.dirname(os.path.abspath(__file__))
+
+    def clean_build_all(self):
+        self.__remove_files(["./*.spec", "./server/*.spec"])
+        self.__remove_directories([
+            "./dist",
+            "./build",
+            "./server/dist",
+            "./server/build",
+            "./client/dist",
+            "./client/release"
+        ])
+
+    def __remove_files(self, file_patterns):
+        for pattern in file_patterns:
+            abs_pattern = os.path.join(self.base_path, pattern)
+            for file_path in glob.glob(abs_pattern):
+                if os.path.exists(file_path):
+                    os.remove(file_path)
+                    print(f"Deleted file: {file_path}")
+
+    def __remove_directories(self, directory_list):
+        for directory in directory_list:
+            abs_dir_path = os.path.join(self.base_path, directory)
+            if os.path.exists(abs_dir_path):
+                shutil.rmtree(abs_dir_path)
+                print(f"Deleted directory: {abs_dir_path}")
+
+
+if __name__ == "__main__":
+    cleaner = CleanBuildAll()
+    cleaner.clean_build_all()
+

+ 5 - 0
instrument_setting.ini

@@ -0,0 +1,5 @@
+[PLC_SIM_SERVER]
+DIGITAL_MULTIMETER = DM3068
+DIGITAL_OSCILLOSCOPE = DHO1204
+WAVEFORM_GENERATOR = DG5072
+ANALOG_ELECTRONIC_LOAD = DL3021

+ 14 - 1
launcher.py

@@ -3,6 +3,7 @@ import subprocess
 import re
 import time
 import sys
+import configparser
 
 
 class Launcher(object):
@@ -14,8 +15,20 @@ class Launcher(object):
         print(f"[Launcher] {message}")
 
     def run(self):
+        config = configparser.ConfigParser()
+        config.read('instrument_setting.ini')
+        server_env = os.environ.copy()
+        variables_to_set = {
+            "PLC_SIM_SERVER_DIGITAL_MULTIMETER": config['PLC_SIM_SERVER']['DIGITAL_MULTIMETER'],
+            "PLC_SIM_SERVER_DIGITAL_OSCILLOSCOPE": config['PLC_SIM_SERVER']['DIGITAL_OSCILLOSCOPE'],
+            "PLC_SIM_SERVER_WAVEFORM_GENERATOR": config['PLC_SIM_SERVER']['WAVEFORM_GENERATOR'],
+            "PLC_SIM_SERVER_ANALOG_ELECTRONIC_LOAD": config['PLC_SIM_SERVER']['ANALOG_ELECTRONIC_LOAD']
+        }
+        for var, value in variables_to_set.items():
+            server_env[var] = value
+            print(f"Using config: {var} = {value}")
         server_process = subprocess.Popen(["./server/server.exe"], stdout=subprocess.PIPE,
-                                          stderr=subprocess.STDOUT, text=True, bufsize=1)
+                                          stderr=subprocess.STDOUT, text=True, bufsize=1, env=server_env)
         server_ready = False
         port: int = 1024
         for line in iter(server_process.stdout.readline, ""):

+ 77 - 0
make_release.py

@@ -0,0 +1,77 @@
+import os
+import shutil
+import zipfile
+
+
+class ReleaseMaker:
+    def __init__(self):
+        self.script_dir = os.path.dirname(os.path.abspath(__file__))
+        self.base_dir = os.path.join(self.script_dir, "client", "release")
+        self.__ensure_directory_exists(self.base_dir)
+        self.version = self.__find_latest_version()
+        self.release_dir = None
+
+    def __find_latest_version(self):
+        versions = [dir_name for dir_name in os.listdir(self.base_dir) if
+                    os.path.isdir(os.path.join(self.base_dir, dir_name))]
+        return max(versions, key=lambda v: list(map(int, v.split('.'))))
+
+    @staticmethod
+    def __ensure_directory_exists(dir_path):
+        if not os.path.exists(dir_path):
+            os.makedirs(dir_path)
+
+    def __unzip_files(self):
+        zip_dir = os.path.join(self.base_dir, self.version)
+        self.__ensure_directory_exists(zip_dir)
+        for item in os.listdir(zip_dir):
+            if item.endswith('.zip'):
+                with zipfile.ZipFile(os.path.join(zip_dir, item), 'r') as zip_ref:
+                    zip_ref.extractall(self.release_dir)
+
+    def __copy_files(self):
+        # Copy ./server/dist/server to ./release/PLC_SIM v{version}/server
+        server_source = os.path.join(self.script_dir, "server", "dist", "server")
+        server_dest = os.path.join(self.release_dir, "server")
+        self.__ensure_directory_exists(server_dest)
+        shutil.copytree(server_source, server_dest, dirs_exist_ok=True)
+
+        # Copy ./source to ./release/PLC_SIM v{version}/server/source
+        source_source = os.path.join(self.script_dir, "source")
+        source_dest = os.path.join(self.release_dir, "server", "source")
+        self.__ensure_directory_exists(source_dest)
+        shutil.copytree(source_source, source_dest, dirs_exist_ok=True)
+
+        # Copy README.md
+        shutil.copy2(os.path.join(self.script_dir, "README.md"), self.release_dir)
+
+        # Copy ./instrument_setting.ini
+        shutil.copy2(os.path.join(self.script_dir, "instrument_setting.ini"), self.release_dir)
+
+        # Copy everything from ./dist/launcher/ to ./release/PLC_SIM v{version}/
+        launcher_source = os.path.join(self.script_dir, "dist", "launcher")
+        for item in os.listdir(launcher_source):
+            item_path = os.path.join(launcher_source, item)
+            if os.path.isdir(item_path):
+                shutil.copytree(item_path, os.path.join(self.release_dir, item), dirs_exist_ok=True)
+            else:
+                shutil.copy2(item_path, self.release_dir)
+
+    def make_release(self):
+        self.release_dir = os.path.join(self.script_dir, f"release/PLC_SIM v{self.version}")
+        self.__ensure_directory_exists(self.release_dir)
+
+        self.__unzip_files()
+        self.__copy_files()
+
+        # Create export directory
+        export_dir = os.path.join(self.release_dir, "server", "export")
+        self.__ensure_directory_exists(export_dir)
+
+        print(f"Release for PLC_SIM v{self.version} has been successfully created!")
+
+
+if __name__ == "__main__":
+    # Create an instance and run the release process
+    maker = ReleaseMaker()
+    maker.make_release()

BIN
server/__pycache__/instrument_controller.cpython-311.pyc


+ 29 - 6
server/instrument_controller.py

@@ -28,10 +28,34 @@ class ScpiInstrument(object):
 
 
 class InstrumentResponseMark(object):
-    digital_multimeter: str = "DM3068"
-    digital_oscilloscope: str = "DHO1204"
-    waveform_generator: str = "DG5072"
-    analog_electronic_load: str = "DL3021"
+    def __init__(self):
+        self.digital_multimeter: str = "DM3068"
+        self.digital_oscilloscope: str = "DHO1204"
+        self.waveform_generator: str = "DG5072"
+        self.analog_electronic_load: str = "DL3021"
+        self.__check_env_value()
+
+    def __check_env_value(self):
+        config_digital_multimeter = self.__get_env("PLC_SIM_SERVER_DIGITAL_MULTIMETER")
+        config_digital_oscilloscope = self.__get_env("PLC_SIM_SERVER_DIGITAL_OSCILLOSCOPE")
+        config_waveform_generator = self.__get_env("PLC_SIM_SERVER_WAVEFORM_GENERATOR")
+        config_analog_electronic_load = self.__get_env("PLC_SIM_SERVER_ANALOG_ELECTRONIC_LOAD")
+        print(f"[Instrument Controller] Using config: PLC_SIM_SERVER_DIGITAL_MULTIMETER = {config_digital_multimeter}")
+        print(f"[Instrument Controller] Using config: PLC_SIM_SERVER_DIGITAL_OSCILLOSCOPE = {config_digital_oscilloscope}")
+        print(f"[Instrument Controller] Using config: PLC_SIM_SERVER_WAVEFORM_GENERATOR = {config_waveform_generator}")
+        print(f"[Instrument Controller] Using config: PLC_SIM_SERVER_ANALOG_ELECTRONIC_LOAD = {config_analog_electronic_load}")
+        if config_digital_multimeter is not None:
+            self.digital_multimeter = config_digital_multimeter
+        if config_digital_oscilloscope is not None:
+            self.digital_oscilloscope = config_digital_oscilloscope
+        if config_waveform_generator is not None:
+            self.waveform_generator = config_waveform_generator
+        if config_analog_electronic_load is not None:
+            self.analog_electronic_load = config_analog_electronic_load
+
+    @staticmethod
+    def __get_env(name: str) -> Optional[str]:
+        return os.environ.get(name, None)
 
 
 class InstrumentControllerConfig(object):
@@ -137,7 +161,6 @@ class InstrumentController(object):
         return None, None
 
 
-
 class FloatServer:
     def __init__(self, name: Optional[str] = None):
         self.name: Optional[str] = name
@@ -348,7 +371,7 @@ class DigitalOscilloscopeService(object):
         self.__stream_url: Optional[str] = None
         self.__screenshot_frame = None
         self.__img_empty = np.zeros((500, 500, 3), dtype=np.uint8)
-        self.__img_empty [:, :] = [255, 255, 255]  # BGR
+        self.__img_empty[:, :] = [255, 255, 255]  # BGR
 
     def keep_listening(self, realtime_terminal_output: bool = False,
                        stream_port: Optional[int] = None, stream_host: str = "0.0.0.0"):