diff --git a/client-file-collector/README.md b/client-file-collector/README.md index cdee350..6ff22eb 100644 --- a/client-file-collector/README.md +++ b/client-file-collector/README.md @@ -9,7 +9,7 @@ - 客户端只需录入: - 系统地址 - 轮询秒 - - 最多3行配置(`site + buNo + equipmentNo + 本地目录`) + - 最多3行配置(`site + buNo + equipmentNo + 本地目录` 必填,`备份目录` 可选) - 双击打开 EXE 后,录入并保存配置即可按轮询秒自动同步。 - 服务端收到每行配置上传的文件后,会自动写入 `D:\qms-dataCollection\equipmentNo`。 @@ -26,7 +26,9 @@ - 每轮轮询会扫描每行配置的本地目录。 - 仅同步新增或修改过的文件(避免重复上传同一文件)。 -- 单个文件上传成功后,会将该文件从本地目录转移至备份目录(源目录名 + `_bak`,例如 `D:\sbFile\DRFID02004` -> `D:\sbFile\DRFID02004_bak`),源目录中对应文件即被移除。 +- 单个文件上传成功后,会将该文件从本地目录转移至该行配置的备份目录,源目录中对应文件即被移除。 +- 备份目录留空时,会自动使用源目录同级的 `_bak` 目录(例如 `D:\sbFile\DRFID02004` -> `D:\sbFile\DRFID02004_bak`)。 +- 旧版配置若未设置备份目录,加载时也会自动补成上述默认路径。 - 若备份目录中已存在同名文件,会自动在文件名后追加时间戳避免覆盖。 - Windows 下首次启动 `QMSFileCollector.exe` 时,会自动写入当前用户自启动项(优先写入 `Startup` 启动文件夹,失败时回退注册表 `HKCU\Software\Microsoft\Windows\CurrentVersion\Run`)。 diff --git a/client-file-collector/client_file_collector.py b/client-file-collector/client_file_collector.py index 2a27d19..f4d64b1 100644 --- a/client-file-collector/client_file_collector.py +++ b/client-file-collector/client_file_collector.py @@ -127,7 +127,7 @@ class CollectorApp: def __init__(self, root): self.root = root self.root.title("文件采集客户端") - self.root.geometry("1180x720") + self.root.geometry("1280x720") self.root.resizable(True, True) self.config_path = get_runtime_dir() / "collector_config.json" @@ -148,6 +148,7 @@ class CollectorApp: "bu_no": StringVar(), "equipment_no": StringVar(), "source_path": StringVar(), + "backup_path": StringVar(), }) self._build_ui() @@ -175,22 +176,25 @@ class CollectorApp: Button(self.root, text="开始同步", command=self.start_collect).place(x=180, y=54) Button(self.root, text="停止同步", command=self.stop_collect).place(x=265, y=54) - Label(self.root, text="配置说明:最多3行,每行需填写 site + buNo + equipmentNo + 本地目录").place(x=360, y=58) + Label(self.root, text="配置说明:最多3行,每行需填写 site + buNo + equipmentNo + 本地目录;备份目录可选(为空则自动使用本地目录_bak)").place(x=360, y=58) Label(self.root, text="行").place(x=20, y=100) Label(self.root, text="Site").place(x=80, y=100) - Label(self.root, text="BuNo").place(x=230, y=100) - Label(self.root, text="EquipmentNo").place(x=380, y=100) - Label(self.root, text="本地目录").place(x=560, y=100) + Label(self.root, text="BuNo").place(x=220, y=100) + Label(self.root, text="EquipmentNo").place(x=360, y=100) + Label(self.root, text="本地目录").place(x=530, y=100) + Label(self.root, text="备份目录").place(x=890, y=100) for idx, row in enumerate(self.row_vars): y = 130 + idx * 40 Label(self.root, text=str(idx + 1)).place(x=26, y=y) Entry(self.root, textvariable=row["site"], width=16).place(x=80, y=y) - Entry(self.root, textvariable=row["bu_no"], width=16).place(x=230, y=y) - Entry(self.root, textvariable=row["equipment_no"], width=18).place(x=380, y=y) - Entry(self.root, textvariable=row["source_path"], width=56).place(x=560, y=y) - Button(self.root, text="选择目录", command=lambda x=idx: self.choose_source_dir(x)).place(x=1010, y=y - 4) + Entry(self.root, textvariable=row["bu_no"], width=16).place(x=220, y=y) + Entry(self.root, textvariable=row["equipment_no"], width=18).place(x=360, y=y) + Entry(self.root, textvariable=row["source_path"], width=40).place(x=530, y=y) + Button(self.root, text="选择目录", command=lambda x=idx: self.choose_source_dir(x)).place(x=815, y=y - 4) + Entry(self.root, textvariable=row["backup_path"], width=30).place(x=890, y=y) + Button(self.root, text="选择目录", command=lambda x=idx: self.choose_backup_dir(x)).place(x=1110, y=y - 4) self.log_text = ScrolledText(self.root, width=165, height=23) self.log_text.place(x=20, y=280) @@ -209,7 +213,22 @@ class CollectorApp: def choose_source_dir(self, row_index): path = filedialog.askdirectory(title="选择第%d行本地目录" % (row_index + 1)) if path: - self.row_vars[row_index]["source_path"].set(path) + row = self.row_vars[row_index] + row["source_path"].set(path) + if not row["backup_path"].get().strip(): + row["backup_path"].set(self._default_backup_path(path)) + + def choose_backup_dir(self, row_index): + path = filedialog.askdirectory(title="选择第%d行备份目录" % (row_index + 1)) + if path: + self.row_vars[row_index]["backup_path"].set(path) + + def _default_backup_path(self, source_path): + source_text = str(source_path).strip() + if not source_text: + return "" + source = Path(source_text) + return str(source.parent / (source.name + "_bak")) def _load_config(self): if not self.config_path.exists(): @@ -228,14 +247,21 @@ class CollectorApp: "bu_no": config.get("bu_no", ""), "equipment_no": config.get("equipment_no", ""), "source_path": config.get("source_path", ""), + "backup_path": config.get("backup_path", ""), }] for idx in range(min(3, len(rows))): item = rows[idx] if isinstance(rows[idx], dict) else {} + source_path = str(item.get("source_path", "")).strip() + backup_path = str(item.get("backup_path", "")).strip() + if source_path and not backup_path: + backup_path = self._default_backup_path(source_path) + self.row_vars[idx]["site"].set(str(item.get("site", "")).strip()) self.row_vars[idx]["bu_no"].set(str(item.get("bu_no", "")).strip()) self.row_vars[idx]["equipment_no"].set(str(item.get("equipment_no", "")).strip()) - self.row_vars[idx]["source_path"].set(str(item.get("source_path", "")).strip()) + self.row_vars[idx]["source_path"].set(source_path) + self.row_vars[idx]["backup_path"].set(backup_path) self.log("配置加载完成。") if self._can_auto_start(): @@ -295,25 +321,37 @@ class CollectorApp: bu_no = row["bu_no"].get().strip() equipment_no = row["equipment_no"].get().strip() source_path = row["source_path"].get().strip() + backup_path = row["backup_path"].get().strip() filled_count = 0 - for value in (site, bu_no, equipment_no, source_path): + for value in (site, bu_no, equipment_no, source_path, backup_path): if value: filled_count += 1 if filled_count == 0: continue - if require_complete and filled_count < 4: + required_count = 0 + for value in (site, bu_no, equipment_no, source_path): + if value: + required_count += 1 + + if require_complete and required_count < 4: raise ValueError("第%d行配置未填写完整,请补全site、buNo、equipmentNo、本地目录。" % (idx + 1)) - if filled_count == 4: + if required_count == 4: + if not backup_path: + backup_path = self._default_backup_path(source_path) + if os.path.normcase(os.path.normpath(source_path)) == os.path.normcase(os.path.normpath(backup_path)): + raise ValueError("第%d行配置错误:本地目录和备份目录不能相同。" % (idx + 1)) + rows.append({ "row_index": idx + 1, "site": site, "bu_no": bu_no, "equipment_no": equipment_no, "source_path": source_path, + "backup_path": backup_path, }) if raise_on_empty and len(rows) == 0: @@ -434,14 +472,20 @@ class CollectorApp: self.log("读取文件状态失败: %s, 原因: %s" % (file_path, e)) return None - def _get_backup_dir(self, source_path): - source = Path(source_path) - return source.parent / (source.name + "_bak") + def _get_backup_dir(self, row): + backup_path = str(row.get("backup_path", "")).strip() + if backup_path: + return Path(backup_path) + return Path(self._default_backup_path(row["source_path"])) def _backup_and_remove_file(self, row, file_path): source_path = row["source_path"] row_index = row["row_index"] - backup_dir = self._get_backup_dir(source_path) + backup_dir = self._get_backup_dir(row) + + if os.path.normcase(os.path.normpath(str(source_path))) == os.path.normcase(os.path.normpath(str(backup_dir))): + self.log("第%d行备份目录不能与本地目录相同: %s" % (row_index, backup_dir)) + return False try: backup_dir.mkdir(parents=True, exist_ok=True) diff --git a/client-file-collector/dist/QMSFileCollector.exe b/client-file-collector/dist/QMSFileCollector.exe index 4862cab..e4e740c 100644 Binary files a/client-file-collector/dist/QMSFileCollector.exe and b/client-file-collector/dist/QMSFileCollector.exe differ diff --git a/client-file-collector/dist/collector_config.json b/client-file-collector/dist/collector_config.json index bc9ce86..18e5123 100644 --- a/client-file-collector/dist/collector_config.json +++ b/client-file-collector/dist/collector_config.json @@ -1,13 +1,14 @@ { "base_url": "http://192.168.1.162:9097", - "poll_seconds": "10", + "poll_seconds": "1", "rows": [ { "row_index": 1, "site": "2", "bu_no": "03-RFID", "equipment_no": "RFID02004", - "source_path": "D:/sbFile/DRFID02004" + "source_path": "D:/sbFile/DRFID02004", + "backup_path": "D:/sbFile/DRFID02004_bak" } ] } \ No newline at end of file