diff --git a/docs/pos-scale-2026-10-11/REPORT.md b/docs/pos-scale-2026-10-11/REPORT.md
index 04260cd..730abd2 100644
--- a/docs/pos-scale-2026-10-11/REPORT.md
+++ b/docs/pos-scale-2026-10-11/REPORT.md
@@ -88,6 +88,50 @@ Linux-сборку не ломали (System.IO.Ports/System.Management общи
тогда уточним смысл флага и control-байта (сейчас они логируются, но не
интерпретируются).
+## Исправление бага автопоиска (Bluetooth) — по реальному запуску на кассе 11.10
+
+**Симптом (sysadmin):** на ноутбуке магазина порты COM7/COM8 (Bluetooth, «The write
+timed out»), COM15–17 (неопознанные), **COM22 = USB-SERIAL CH340 (там весы)**.
+«Найти весы» → «Весы не найдены ни на одном порту», хотя ручной выбор
+COM22/57600/Масса-К-кадр даёт «0,000 кг, стабильно». Причина: автопоиск спотыкался
+о Bluetooth-порты (Open/Write на них висят) и до COM22 не доходил; по умолчанию в
+списке стоял COM7 (Bluetooth).
+
+**Исправлено (и в кассе, и в HwCheck):**
+- Порты классифицируются по типу (WMI, без открытия): **USB-serial** (CH340/
+ Prolific/FTDI/CP210x), **Bluetooth** (BTHENUM/«по соединению Bluetooth»),
+ неопознанные. Порядок автопоиска: **USB-serial → неопознанные → Bluetooth**.
+- На каждый порт — **жёсткий таймаут** (Open обёрнут в задачу с гонкой против
+ Task.Delay; при зависании транспорт принудительно закрывается). Порты
+ перебираются **параллельно (до 4)**, общий бюджет ~15 с. Любая ошибка/таймаут
+ на порту → следующий порт, перебор не останавливается.
+- **Лог по каждому порту** в окне: «COM22 (USB-SERIAL CH340): ENQ→ACK, вес 0.000 кг»,
+ «COM7: пропущен Bluetooth-порт», «COM15: нет ответа». Листинг портов тоже не
+ открывает Bluetooth (помечает «не опрашивается»). По умолчанию выбирается
+ НЕ Bluetooth-порт.
+- Из zip hardware-check **убран ярлык «(с тестовой печатью).bat»** (печать — только
+ отдельно, по слову владельца).
+- Юнит-тесты: «зависающий» порт (Open 30 с) **не задерживает поиск дольше таймаута**,
+ живой порт вторым — **найден**; USB-serial проверяется раньше Bluetooth
+ (BT не трогаем). Всего по оборудованию 53/53 зелёных.
+
+**Проверка на Windows 11 (VM):** «Проверка оборудования» запущена с автопоиском
+(`--autofind`) — перебрала COM1, записала в журнал «COM1: нет ответа» и **завершилась
+быстро, без зависания** (на VM нет весов и нет Bluetooth-порта). См.
+`vm-hwcheck-autofind.png`.
+
+**НОВЫЙ zip hardware-check (с фиксом):**
+`~/pos-release/allmarket-pos-win-x64-20261011-0906-hardware-check.zip`
+SHA256 `ac5cedef0f5dd63e98f0f3044ef453666f71d8c18280107983b1023cbd573623`
+(один ярлык «Проверка оборудования.bat», без ярлыка печати).
+
+**Полная проверка с эмулятором весов + «зависающим» виртуальным портом (п. 7) —
+по-прежнему требует com0com на VM**, установка драйвера которого упирается в доверие
+издателю (см. выше) — ждёт решения владельца. Поведение «не виснуть на зависшем
+порту» доказано юнит-тестом детерминированно; на VM показано, что автопоиск логирует
+по портам и быстро завершается.
+
## Файлы
- `vm-hwcheck-launch.png` — окно «Проверка оборудования» на Windows 11.
-- `index.html` — та же сводка со скриншотом.
+- `vm-hwcheck-autofind.png` — автопоиск с журналом по портам (COM1), без зависания.
+- `index.html` — сводка со скриншотами.
diff --git a/docs/pos-scale-2026-10-11/index.html b/docs/pos-scale-2026-10-11/index.html
index 002768b..5aafeb0 100644
--- a/docs/pos-scale-2026-10-11/index.html
+++ b/docs/pos-scale-2026-10-11/index.html
@@ -17,6 +17,8 @@ code{background:#eef2f7;padding:1px 5px;border-radius:4px;font-size:13px}
✓ Контрольная цифра EAN-13/EAN-8; сканер vs ручной ввод
✓ Юнит-тесты оборудования: 50/50 зелёных
✓ «Проверка оборудования» запускается на реальной Windows 11 (скриншот ниже)
+
✓ Фикс автопоиска: USB-serial первыми, Bluetooth последними, жёсткий таймаут на порт, параллельно, лог по портам (баг на кассе 11.10 — виснул на Bluetooth, не доходил до CH340)
+
✓ Тест: «зависающий» порт (Open 30 с) не задерживает поиск; живой порт вторым — найден
✗ Живое чтение веса через виртуальную пару COM на VM — НЕ выполнено (драйвер com0com требует доверия издателю; нужно решение владельца)
✗ Реальные весы с грузом (гирей) в магазине — НЕ проверялись (нужен человек)
@@ -26,5 +28,9 @@ code{background:#eef2f7;padding:1px 5px;border-radius:4px;font-size:13px}
«ALL MARKET — Проверка оборудования» на Win11 (VM): без входа; определены COM1 и принтер; тестовая печать выключена; поле сканера активно.
+
+Фикс автопоиска на Win11: перебрал COM1, записал в журнал «COM1: нет ответа», завершился быстро без зависания (на VM нет весов/Bluetooth).
+
+
Подробности, SHA256 сборок и список «нужно решение владельца» — в REPORT.md.
diff --git a/docs/pos-scale-2026-10-11/vm-hwcheck-autofind.png b/docs/pos-scale-2026-10-11/vm-hwcheck-autofind.png
new file mode 100644
index 0000000..8e5db16
Binary files /dev/null and b/docs/pos-scale-2026-10-11/vm-hwcheck-autofind.png differ
diff --git a/src/food-market.pos.hwcheck/MainWindow.cs b/src/food-market.pos.hwcheck/MainWindow.cs
index 83f0b4d..68a1055 100644
--- a/src/food-market.pos.hwcheck/MainWindow.cs
+++ b/src/food-market.pos.hwcheck/MainWindow.cs
@@ -66,6 +66,9 @@ public MainWindow(bool allowTestPrint)
_timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(350) };
_timer.Tick += (_, _) => LiveTick();
+
+ if (Program.AutoFind)
+ Dispatcher.UIThread.Post(() => DiscoverScale(), DispatcherPriority.Background);
}
private Control BuildLayout()
diff --git a/src/food-market.pos.hwcheck/Program.cs b/src/food-market.pos.hwcheck/Program.cs
index 41a9554..6f041c7 100644
--- a/src/food-market.pos.hwcheck/Program.cs
+++ b/src/food-market.pos.hwcheck/Program.cs
@@ -8,15 +8,42 @@ internal static class Program
/// (ключ даёт владелец). Без него кнопка «Тестовая печать» выключена и на принтер
/// ничего не отправляется.
public static bool AllowTestPrint { get; private set; }
+ /// Запустить автопоиск весов сразу при открытии (для скриншотов/проверки).
+ public static bool AutoFind { get; private set; }
[STAThread]
public static int Main(string[] args)
{
AllowTestPrint = args.Contains("--allow-test-print");
+ AutoFind = args.Contains("--autofind");
+ // Headless-режим для скриптов/интеграционного теста: одно чтение веса и выход.
+ // --read-once [BAUD] [MassaKFrame|MassaKAscii|Cas]
+ if (args.Contains("--read-once")) return ReadOnceCli(args);
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
return 0;
}
+ private static int ReadOnceCli(string[] args)
+ {
+ int i = Array.IndexOf(args, "--read-once");
+ string port = (i + 1 < args.Length) ? args[i + 1] : "";
+ int baud = (i + 2 < args.Length && int.TryParse(args[i + 2], out var b)) ? b : 57600;
+ var proto = (i + 3 < args.Length && Enum.TryParse(args[i + 3], out var p))
+ ? p : Core.Hardware.ScaleProto.MassaKFrame;
+ if (string.IsNullOrEmpty(port)) { Console.Error.WriteLine("usage: --read-once [BAUD] [PROTO]"); return 2; }
+ try
+ {
+ using var t = new Core.Hardware.SystemSerialTransport(port, baud);
+ t.Open();
+ var (r, hex, err) = Core.Hardware.SerialScaleReader.ReadDetailed(t, proto);
+ if (r is null) { Console.Error.WriteLine($"FAIL: {err} hex=[{hex}]"); return 1; }
+ Console.WriteLine($"OK kg={r.Value.Kg:0.000} unit={r.Value.Unit} flag='{r.Value.Flag}' " +
+ $"control={(r.Value.Control is null ? "-" : "0x" + r.Value.Control.Value.ToString("X2"))} hex=[{r.Value.RawHex}]");
+ return 0;
+ }
+ catch (Exception ex) { Console.Error.WriteLine("ERR: " + ex.Message); return 3; }
+ }
+
public static AppBuilder BuildAvaloniaApp()
{
var b = AppBuilder.Configure().UsePlatformDetect();