Setup Factory覆盖安装并且安装完成之后设置立即运行选项

本文详细阐述了程序在第二次安装时如何通过覆盖安装或保留原有安装目录的策略,包括注册表操作、检测已安装状态及自动跳转界面的设置。同时介绍了安装完成后的立即运行选项,以及如何自定义安装流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.覆盖安装

    当程序第二次安装时,这是程序就覆盖安装原来的程序,还是原来的目录。

    1.首先要在电脑里写入注册表,记录程序安装目录

          在程序安装之后,On Preload里

          设置注册表安装路径
          Registry.SetValue(HKEY_LOCAL_MACHINE, "SOFTWARE\\JMElectInstallInfo", "JMElectInstallLocation", SessionVar.Expand ("%AppFolder%"), REG_SZ); 

     2.在启动时,检测注册表里是否存在安装路径键值

          local IsJMElectInstall = Registry.DoesKeyExist(HKEY_LOCAL_MACHINE, "SOFTWARE\\JMElectInstallInfo");
  if(IsJMElectInstall == false)then
         Registry.CreateKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\JMElectInstallInfo");  --注册键
  end

  local JEtInstallLocation = Registry.GetValue(HKEY_LOCAL_MACHINE, "SOFTWARE\\JMElectInstallInfo", "JMElectInstallLocation",true);--获取项目安装路径

  if(JEtInstallLocation ~= "")then  --判断键值是否存在
         local JMElectProductName = SessionVar.Expand ("%ProductName%");
  Qid = Dialog.Message(JMElectProductName, String.Concat("检测到已安装该软件",",如果重装则不能改变安装目录。若要改变目录,请先卸载后再安装。确定要重           装吗?"), MB_OKCANCEL, MB_ICONQUESTION, MB_DEFBUTTON1);
   if(Qid == IDCANCEL) then  -- Qid为全局变量,其他窗体可用。
          Application.Exit();                                         -- 如果不重装,则退出
   else
              local resultAppFolder = Registry.GetValue(HKEY_LOCAL_MACHINE, "SOFTWARE\\JMElectInstallInfo", "JMElectInstallLocation", true);  -- 获取原来的安装目录。
              SessionVar.Set("%AppFolder%", resultAppFolder);   -- 将安装目录设置成原来的安装目录
         end

         end

     3.设置某些界面自动跳转

        在"开始安装"和"选择安装文件夹"On Preload里

       if Qid==1 then             -- 如果已有安装系统,跳过设定安装目录
          Screen.Next(); 
       end

        但好像"许可协议"不能设置跳转,知道的朋友可以告诉我。

      以上操作基本上设置完成了。


二.安装完成设置立即运行选项

     将安装之后的界面删掉,自己做一个界面

     1.添加一个检测框,只留一个复选框,其它的移除掉。编辑信息,最终如下图

      

    2.写代码实现效果

       在编辑On Next里写如下代码(JMElect.exe是你应用程序名称)

      Screen.Next();
      if (check01) then
           Shell.Execute(SessionVar.Expand("%AppFolder%\\JMElect.exe"), "open", "", "%AppFolder%", SW_SHOWNORMAL);
      end

 最后发布构件即可。


import asyncio import websockets import json import struct import datetime import logging import traceback import mysql.connector from mysql.connector import pooling from mysql.connector import errorcode from typing import Dict, List, Optional # ====================== # 配置参数 # ====================== CONFIG = { "AUTH_ENABLED": False, # 是否启用认证 "APP_SECRET": "2d0c7a6f35a74721bdae2b0077735938", # AppSecretKey "FACTORY": "ztzbStdTag", # 工厂标识 "VALID_TOKEN": { # 有效Token "appSecret": "BG7DpcHE4oBjsD5x6lkI8KQpfRSWDF6M", "fullCode": "ztzbStdTagJsonPush" }, "WEBSOCKET_HOST": "0.0.0.0", "WEBSOCKET_PORT": 8765, "MAX_CONNECTIONS": 100, "DB_WRITE_BATCH_SIZE": 50, "DB_WRITE_TIMEOUT": 5.0, # MySQL数据库配置 "MYSQL": { "HOST": "192.168.191.11", "PORT": 3306, "USER": "root", "PASSWORD": "Adu@123.", "DATABASE": "CF_HIDB", "POOL_SIZE": 5, "POOL_NAME": "sensor_pool", "POOL_RESET_SESSION": True } } # ====================== # 极简日志设置 # ====================== def setup_logging(): """配置极简日志系统""" logger = logging.getLogger("SensorServer") logger.setLevel(logging.INFO) # 控制台处理器 console_handler = logging.StreamHandler() console_handler.setFormatter(logging.Formatter( '%(asctime)s - %(levelname)s - %(message)s' )) logger.addHandler(console_handler) return logger # 全局日志器 logger = setup_logging() # ====================== # MySQL数据库管理 # ====================== class MySQLDatabaseManager: def __init__(self): self.data_buffer = [] self.last_write_time = datetime.datetime.now() self.connection_pool = self._create_connection_pool() self._init_db() def _create_connection_pool(self) -> pooling.MySQLConnectionPool: """创建MySQL连接池""" try: return pooling.MySQLConnectionPool( pool_name=CONFIG["MYSQL"]["POOL_NAME"], pool_size=CONFIG["MYSQL"]["POOL_SIZE"], pool_reset_session=CONFIG["MYSQL"]["POOL_RESET_SESSION"], host=CONFIG["MYSQL"]["HOST"], port=CONFIG["MYSQL"]["PORT"], user=CONFIG["MYSQL"]["USER"], password=CONFIG["MYSQL"]["PASSWORD"], database=CONFIG["MYSQL"]["DATABASE"] ) except mysql.connector.Error as err: logger.error(f"MySQL连接池创建失败: {err}") raise def _init_db(self) -> None: """初始化数据库表结构""" try: conn = self.connection_pool.get_connection() cursor = conn.cursor() # 创建设备表 cursor.execute(''' CREATE TABLE IF NOT EXISTS devices ( id INT AUTO_INCREMENT PRIMARY KEY, device_id VARCHAR(50) NOT NULL UNIQUE, first_seen TIMESTAMP DEFAULT CURRENT_TIMESTAMP, last_seen TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ''') # 创建传感器数据表 cursor.execute(''' CREATE TABLE IF NOT EXISTS sensor_data ( id BIGINT AUTO_INCREMENT PRIMARY KEY, device_id INT NOT NULL, timestamp TIMESTAMP NOT NULL, sensor_values JSON NOT NULL, received_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX idx_device_id (device_id), INDEX idx_timestamp (timestamp), FOREIGN KEY (device_id) REFERENCES devices(id) ON DELETE CASCADE ) ''') conn.commit() conn.close() except mysql.connector.Error as err: logger.error(f"数据库初始化失败: {err}") def _get_or_create_device(self, conn, device_id: str) -> int: """获取或创建设备记录""" cursor = conn.cursor() try: # 尝试获取设备ID cursor.execute( "SELECT id FROM devices WHERE device_id = %s", (device_id,) ) device = cursor.fetchone() if device: device_id = device[0] # 更新最后出现时间 cursor.execute( "UPDATE devices SET last_seen = CURRENT_TIMESTAMP WHERE id = %s", (device_id,) ) return device_id else: # 创建新设备 cursor.execute( "INSERT INTO devices (device_id) VALUES (%s)", (device_id,) ) return cursor.lastrowid except mysql.connector.Error as err: logger.error(f"设备操作失败: {err}") raise def insert_sensor_data(self, device_id: str, timestamp: str, sensor_values: List[float]) -> None: """将数据添加到缓冲区""" # 保留小数点后两位 rounded_values = [round(value, 2) for value in sensor_values] self.data_buffer.append({ 'device_id': device_id, 'timestamp': timestamp, 'sensor_values': rounded_values }) # 检查是否满足批量写入条件 now = datetime.datetime.now() buffer_full = len(self.data_buffer) >= CONFIG["DB_WRITE_BATCH_SIZE"] timeout_reached = (now - self.last_write_time).total_seconds() >= CONFIG["DB_WRITE_TIMEOUT"] if buffer_full or timeout_reached: self.flush_buffer() def flush_buffer(self) -> None: """将缓冲区数据写入数据库""" if not self.data_buffer: return try: conn = self.connection_pool.get_connection() cursor = conn.cursor() # 批量写入设备数据 device_ids = [] for data in self.data_buffer: # 获取或创建设备ID device_db_id = self._get_or_create_device( conn, data['device_id'] ) device_ids.append(device_db_id) # 准备批量插入传感器数据 sensor_data = [] for i, data in enumerate(self.data_buffer): sensor_data.append(( device_ids[i], data['timestamp'], json.dumps(data['sensor_values']) )) # 批量插入传感器数据 cursor.executemany( "INSERT INTO sensor_data (device_id, timestamp, sensor_values) " "VALUES (%s, %s, %s)", sensor_data ) conn.commit() logger.info(f"写入 {len(self.data_buffer)} 条数据") # 清空缓冲区 self.data_buffer.clear() self.last_write_time = datetime.datetime.now() except mysql.connector.Error as err: if err.errno == errorcode.ER_TRUNCATED_WRONG_VALUE: # 日期时间错误,尝试修正 logger.warning("检测到日期时间格式错误,尝试修正...") for data in self.data_buffer: try: # 尝试使用当前时间 data['timestamp'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") except: pass # 重试写入 self.flush_buffer() else: logger.error(f"数据库写入失败: {err}") except Exception as e: logger.error(f"数据库操作异常: {e}") finally: if 'conn' in locals() and conn.is_connected(): conn.close() # ====================== # 数据包解析 # ====================== def parse_timestamp(timestamp_bytes: bytes) -> str: """解析时间戳字节数据为字符串""" try: timestamp_str = timestamp_bytes.decode('ascii').strip() # 提取日期时间部分 (YYYY-MM-DD HH:MM:SS) return timestamp_str[:19] except: # 使用当前时间作为默认值 return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") def parse_sensor_values(data: bytes, start_index: int) -> List[float]: """解析传感器值列表""" values = [] index = start_index max_count = (len(data) - index) // 4 for _ in range(max_count): if index + 4 > len(data): break value_bytes = data[index:index + 4] index += 4 try: # 尝试小端序解析 value = struct.unpack('<f', value_bytes)[0] values.append(value) except: try: # 尝试大端序解析 value = struct.unpack('>f', value_bytes)[0] values.append(value) except: # 解析失败,跳过此值 continue return values def parse_binary_packet(raw_data: bytes) -> Optional[Dict]: """解析二进制数据包""" try: # 1. 验证包头 HEADER = b'$\x00\x08\x00\x00\x00' if not raw_data.startswith(HEADER): return None # 2. 解析设备ID DEVICE_ID_START = len(HEADER) DEVICE_ID_END = DEVICE_ID_START + 8 device_id_bytes = raw_data[DEVICE_ID_START:DEVICE_ID_END] # 3. 解析时间戳 TIMESTAMP_START = DEVICE_ID_END TIMESTAMP_END = TIMESTAMP_START + 19 timestamp_bytes = raw_data[TIMESTAMP_START:TIMESTAMP_END] # 4. 解析传感器值 SENSOR_DATA_START = TIMESTAMP_END sensor_values = parse_sensor_values(raw_data, SENSOR_DATA_START) # 5. 返回解析结果 return { 'device_id': device_id_bytes.decode('ascii', errors='ignore').strip(), 'timestamp': parse_timestamp(timestamp_bytes), 'sensor_values': sensor_values } except Exception: return None # ====================== # WebSocket 服务器 (带认证) # ====================== class SensorWebSocketServer: def __init__(self, host: str, port: int, db_manager: MySQLDatabaseManager): self.host = host self.port = port self.db_manager = db_manager self.connections = set() self.server = None self.authenticated_clients = set() # 已认证的客户端集合 async def handler(self, websocket, path: str) -> None: """处理WebSocket连接""" self.connections.add(websocket) client_ip = websocket.remote_address[0] if websocket.remote_address else "unknown" logger.info(f"客户端连接: {client_ip}") try: # 如果启用认证,先进行认证 if CONFIG["AUTH_ENABLED"]: # 等待认证消息 auth_message = await websocket.recv() try: auth_data = json.loads(auth_message) # 验证Token if (auth_data.get("appSecret") == CONFIG["VALID_TOKEN"]["appSecret"] and auth_data.get("fullCode") == CONFIG["VALID_TOKEN"]["fullCode"]): self.authenticated_clients.add(websocket) await websocket.send("AuthSuccess") logger.info(f"客户端认证成功: {client_ip}") else: await websocket.send("AuthFailed: Invalid token") logger.warning(f"客户端认证失败: {client_ip}") return except: await websocket.send("AuthFailed: Invalid format") logger.warning(f"无效的认证消息: {client_ip}") return # 处理数据消息 async for message in websocket: if not isinstance(message, bytes): continue # 如果启用认证但客户端未认证,跳过处理 if CONFIG["AUTH_ENABLED"] and websocket not in self.authenticated_clients: continue parsed_data = parse_binary_packet(message) if parsed_data: # 存储到数据库 self.db_manager.insert_sensor_data( parsed_data['device_id'], parsed_data['timestamp'], parsed_data['sensor_values'] ) except websockets.exceptions.ConnectionClosed: logger.info(f"客户端断开: {client_ip}") except Exception as e: logger.error(f"处理客户端时出错: {e}") finally: if websocket in self.connections: self.connections.remove(websocket) if websocket in self.authenticated_clients: self.authenticated_clients.remove(websocket) async def start(self) -> None: """启动WebSocket服务器""" self.server = await websockets.serve( self.handler, self.host, self.port, max_size=2 ** 20, # 1MB ping_interval=60, ping_timeout=30, close_timeout=10, max_queue=CONFIG["MAX_CONNECTIONS"] ) logger.info(f"服务器启动: ws://{self.host}:{self.port}") logger.info(f"认证状态: {'启用' if CONFIG['AUTH_ENABLED'] else '禁用'}") async def stop(self) -> None: """停止WebSocket服务器""" if self.server: self.server.close() await self.server.wait_closed() logger.info("服务器已停止") # ====================== # 主程序 # ====================== async def main(): # 初始化MySQL数据库管理器 db_manager = MySQLDatabaseManager() # 创建WebSocket服务器 server = SensorWebSocketServer( CONFIG["WEBSOCKET_HOST"], CONFIG["WEBSOCKET_PORT"], db_manager ) # 启动服务器 await server.start() try: # 运行主循环 while True: await asyncio.sleep(5) # 定期刷新数据库缓冲区 db_manager.flush_buffer() except asyncio.CancelledError: pass except KeyboardInterrupt: logger.info("收到停止信号") finally: # 确保所有缓冲数据都写入数据库 db_manager.flush_buffer() await server.stop() # 启动程序 if __name__ == "__main__": logger.info("=" * 40) logger.info("传感器数据采集服务器启动") logger.info(f"时间: {datetime.datetime.now()}") logger.info(f"MySQL主机: {CONFIG['MYSQL']['HOST']}:{CONFIG['MYSQL']['PORT']}") logger.info(f"数据库: {CONFIG['MYSQL']['DATABASE']}") logger.info("=" * 40) try: asyncio.run(main()) except Exception as e: logger.error(f"服务器异常停止: {e}") finally: logger.info("=" * 40) logger.info("服务器已停止运行") logger.info("=" * 40) 部署到NAS的docker里
最新发布
06-21
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值