diff --git a/python/Bag2.py b/python/Bag2.py index 513ed8d..2ffab22 100644 --- a/python/Bag2.py +++ b/python/Bag2.py @@ -730,6 +730,130 @@ def laser_push_loop( pass +def run_laser_continuous_print( + root: tk.Tk, + laser_conn_ref: list, + laser_thread_ref: list, + laser_stop_ref: list, + ip: str, + port: int, + item_id: Optional[int], + stock_in_line_id: Optional[int], + item_code: str, + item_name: str, + set_status_message: Callable[[str, bool], None], +) -> None: + """ + Laser continuous send (連續 C) in a background thread. + User can stop via Stop button or keyboard: `Esc` / `Q`. + """ + # Stop any previous continuous run. + if laser_stop_ref[0] is not None: + try: + laser_stop_ref[0].set() + except Exception: + pass + + stop_event = threading.Event() + laser_stop_ref[0] = stop_event + + win = tk.Toplevel(root) + win.title("激光機 連續列印") + win.geometry("360x170") + win.transient(root) + win.configure(bg=BG_TOP) + + ttk.Label( + win, + text="連續送出中,按「停止」或 Esc / Q 結束", + font=get_font(FONT_SIZE), + background=BG_TOP, + ).pack(pady=(14, 8)) + + count_lbl = tk.Label(win, text="已送出:0 次", font=get_font(FONT_SIZE), bg=BG_TOP) + count_lbl.pack(pady=6) + + # Ensure stop keys work even if focus is on the main window. + def _unbind_stop_keys() -> None: + try: + root.unbind_all("") + root.unbind_all("") + root.unbind_all("") + except Exception: + pass + + def on_stop() -> None: + if stop_event.is_set(): + return + stop_event.set() + _unbind_stop_keys() + try: + win.destroy() + except Exception: + pass + + def _key_stop(_e: tk.Event) -> str: + on_stop() + return "break" + + root.bind_all("", _key_stop) + root.bind_all("", _key_stop) + root.bind_all("", _key_stop) + + ttk.Button(win, text="停止", command=on_stop, width=12).pack(pady=10) + win.protocol("WM_DELETE_WINDOW", on_stop) + + def worker() -> None: + sent = 0 + try: + while not stop_event.is_set(): + ok, msg = send_job_to_laser_with_retry( + laser_conn_ref, + ip, + port, + item_id, + stock_in_line_id, + item_code, + item_name, + ) + if not ok: + root.after(0, lambda m=msg: set_status_message(f"連續送出失敗:{m}", is_error=True)) + break + + sent += 1 + root.after(0, lambda s=sent: count_lbl.configure(text=f"已送出:{s} 次")) + + # Small delay between sends; check stop frequently. + for _ in range(4): # 4 * 0.05 = 0.20 sec + if stop_event.is_set(): + break + time.sleep(0.05) + except Exception as e: + root.after(0, lambda err=str(e): set_status_message(f"連續送出意外錯誤:{err}", is_error=True)) + finally: + _unbind_stop_keys() + if not stop_event.is_set(): + # Failure path ended the worker; keep window close. + try: + win.destroy() + except Exception: + pass + else: + # Stopped intentionally. + root.after(0, lambda s=sent: set_status_message(f"已停止激光機連續送出:{s} 次", is_error=False)) + + laser_stop_ref[0] = None + laser_thread_ref[0] = None + try: + win.destroy() + except Exception: + pass + + t = threading.Thread(target=worker, daemon=True) + laser_thread_ref[0] = t + t.start() + + def send_job_to_laser( conn_ref: list, ip: str, @@ -740,16 +864,23 @@ def send_job_to_laser( item_name: str, ) -> tuple[bool, str]: """ - Send to laser. Standard format: {"itemId": xxx, "stockInLineId": xxx}. + Send to laser using `;` separated 3 params: + {"itemID": itemId, "stockInLineId": stockInLineId} ; itemCode ; itemName ;; conn_ref: [socket or None] - reused across calls; closed only when switching printer. - When both item_id and stock_in_line_id present, sends JSON; else fallback: 0;item_code;item_name;; + When both item_id and stock_in_line_id present, sends JSON first param; else fallback: 0;item_code;item_name;; Returns (success, message). """ + code_str = (item_code or "").strip().replace(";", ",") + name_str = (item_name or "").strip().replace(";", ",") + if item_id is not None and stock_in_line_id is not None: - reply = json.dumps({"itemId": item_id, "stockInLineId": stock_in_line_id}) + # Use compact JSON so device-side parser doesn't get spaces. + json_part = json.dumps( + {"itemID": item_id, "stockInLineId": stock_in_line_id}, + separators=(",", ":"), + ) + reply = f"{json_part};{code_str};{name_str};;" else: - code_str = (item_code or "").strip().replace(";", ",") - name_str = (item_name or "").strip().replace(";", ",") reply = f"0;{code_str};{name_str};;" conn = conn_ref[0] try: @@ -1487,23 +1618,38 @@ def main() -> None: stock_in_line_id = j.get("stockInLineId") item_code_val = j.get("itemCode") or "" item_name_val = j.get("itemName") or "" - n = 100 if count == -1 else count - sent = 0 - for i in range(n): - ok, msg = send_job_to_laser_with_retry( - laser_conn_ref, ip, port, - item_id, stock_in_line_id, - item_code_val, item_name_val, + if count == -1: + run_laser_continuous_print( + root=root, + laser_conn_ref=laser_conn_ref, + laser_thread_ref=laser_thread_ref, + laser_stop_ref=laser_stop_ref, + ip=ip, + port=port, + item_id=item_id, + stock_in_line_id=stock_in_line_id, + item_code=item_code_val, + item_name=item_name_val, + set_status_message=set_status_message, ) - if ok: - sent += 1 - else: - set_status_message(f"已送出 {sent} 次,第 {sent + 1} 次失敗:{msg}", is_error=True) - break - if i < n - 1: - time.sleep(0.2) - if sent == n: - set_status_message(f"已送出激光機:{sent} 次", is_error=False) + else: + n = count + sent = 0 + for i in range(n): + ok, msg = send_job_to_laser_with_retry( + laser_conn_ref, ip, port, + item_id, stock_in_line_id, + item_code_val, item_name_val, + ) + if ok: + sent += 1 + else: + set_status_message(f"已送出 {sent} 次,第 {sent + 1} 次失敗:{msg}", is_error=True) + break + if i < n - 1: + time.sleep(0.2) + if sent == n: + set_status_message(f"已送出激光機:{sent} 次", is_error=False) for w in (row, left, batch_lbl, code_lbl, name_lbl): w.bind("", _on_click) diff --git a/python/__pycache__/Bag2.cpython-313.pyc b/python/__pycache__/Bag2.cpython-313.pyc new file mode 100644 index 0000000..64e5842 Binary files /dev/null and b/python/__pycache__/Bag2.cpython-313.pyc differ diff --git a/python/bag2_settings.json b/python/bag2_settings.json new file mode 100644 index 0000000..1f6996e --- /dev/null +++ b/python/bag2_settings.json @@ -0,0 +1,9 @@ +{ + "api_ip": "10.10.0.81", + "api_port": "8090", + "dabag_ip": "192.168.18.27", + "dabag_port": "3008", + "laser_ip": "192.168.18.77", + "laser_port": "45678", + "label_com": "TSC TTP-246M Pro" +} \ No newline at end of file