Hiperion 1300g
Hiperion 1300g
Это - сканер штрих-кодов. Ну такой полосковый код на упаковке.
Внутри штука достаточно сложная. С программной точки зрения - тоже.
Подключение - простое - УСБ.
Внутри, однако, сканер может быть сконфигурирован как устройство на КОМ-порте.
В Device Manager он так и представляется - Hiperion 1300g (COM6).
Почему - не знаю - наследие.
Проблема - для использования устройства мне надо знать на каком порту оно эмулируется.
Сейчас написано так, что порт привязки читается из конфига, затем - инициализируется
устройство посылкой команды.
Сканеров на одной машине может быть несколько, пулл и обвязку Я уже написал и врезал,
но там есть моментик - фабрике сканеров нужно/дается только имя сканера, а порт -
неизвестен. Хочу чтобы при создании сканера порт находился автоматически.
Нормально было бы опросить порт, но суппорт пока не говорит как это сделать.
Вторым вариантом было бы прочесть в реестре. Там есть информация по сканеру, но
нет (или пока не нашел) информации по привязке к порту.
Еще один вариант - через ManagementObjectSearcher. Информация по УСБ-портам есть,
по привязке к КОМ-порту - нету.
В общем - непонятно где искать инфу об привязке. Ткните носом куда смотреть...
Таки сделал.
Имя порта лежит в Регистре (у меня):
HKEY_LOCAL_MACHINE\SYSTEM\ControlSet002\Enum\usbcdcacm\VID_0C2E&PID_0B0A&MI_00\1&2b53a856&0&7_294012cb_0_0000_00\Device Parameters
Имя значения: PortName
Сложность представляет получение идентификатора устройства: VID_0C2E&PID_0B0A&MI_00\1&2b53a856&0&7_294012cb_0_0000_00
При поиске по УСБ-устройствам находится подключенный к УСБ сканер, но он имеет другой идентификатор устройства. Конвертировать из одного в другое у меня не получилось.
Потому обошел это дело поиском по всем устройствам, которые соответствуют эмуляторам КОМ-портов и имеют нужное имя.
Работающая версия для Вин7:
internal class TComPortManager{const string ComEmulatorKey = "USBCDCACM";//const string DeviceName = "Hyperion 1300g";const string select_format = @"Select * From Win32_PnPEntity WHERE DeviceID LIKE '{0}%' AND Name LIKE '{1}%'";static List<DeviceInfo> GetDevices(string pDeviceName){List<DeviceInfo> devices = new List<DeviceInfo>();ManagementObjectCollection collection;string select = String.Format(select_format, ComEmulatorKey, pDeviceName);using (var searcher = new ManagementObjectSearcher(select))collection = searcher.Get();
foreach (var device in collection){try{DeviceInfo deviceInfo = new DeviceInfo(device);devices.Add(deviceInfo);}catch (Exception ex){string msg = ex.Message;}}collection.Dispose();return devices;}const string RegistryBase = @"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\";const string EntryName = @"\Device Parameters";public static string[] GetComNames(string pDeviceName){List<string> comms = new List<string>();var devices = GetDevices(pDeviceName);foreach (var device in devices){string fullKey = RegistryBase + device.DeviceID + EntryName;string portName = (string)Registry.GetValue(fullKey, "PortName", null);comms.Add(portName);}return comms.ToArray();}}
Как сделать то же самое под НТ4 и ХР - пока не смотрел. Но там - точно по другому.
И немного про мудака билли.
Есть у него набор примеров по использованию сканеров (как HID устройств).
Так этот мудень требует Вин8 / Вин8.1 минимально... ему, блин, обязательно Метро интерфейс иметь... это - для сканера... поцик, бля...
Непонятное.
Выросло из обработки приема на КОМ-порте.
Все элементарно просто - ожидаем первого вызова ScannerGetData_
дальше начинаются чудеса
- если первым обработчиком подвязан приватный метод - количество вызовов будет... 0.
- если первым обработчиком подвязан паблиц метод - количество вызовов будет... 1.
Вроде как во втором случае все правильно, но тест - провалится...
Кто-то выскажется по поводу?
Да тут как бы все просто. Кривой код.
Line 19:
worker.RunWorkerAsync(); worker.ScannerGetData += ScannerGetData_1; worker.ScannerGetData -= ScannerGetData_2;
Что делает -= во втором случае не понятно.
Подвязывание делегатов после RunWorkerAsync - race condition.
Line 36:
public void ScannerGetData_1(string data) { ++exceptions; throw new Exception("Data recived"); }
После выброса исключения в обработчике события следующие за ним оставшиеся делегаты не будут вызваны.
Как следствие private void MyWork(object sender, System.ComponentModel.DoWorkEventArgs e) вываливается с исключением, и цикл
for (int i = 0; i < 5; ++i) { System.Threading.Thread.Sleep(1000); worker.SleepCondition = false; }
работает вхолостую.
Line 33:
Assert.Fail("Exception didn't happen, but accounted " + exceptions.ToString() + " times.");
Бессмысленно ждать исключения, которое было брошено в другом потоке. Для этого существует AsyncCompletedEventArgs.Error.
Кривой код.
-----
Вот что значит делать что-то в тяпницу после обеда...
Лине 19
-----
описался и не заметил. Вопрос об вызове приватного метода снят - вроде работает.
race condition.
-----
В данном случае - без разницы, специфика имплементации.
Есть однако зависимость от внутренней реализации воркера - может и вообще не реагировать ни на какие назначения после запуска... вот треад-статиk он не понимает...
Line 36:
-----
Ожидалось не это.
По старой памяти - при выбросе исключения из потока - поток умирает.
Вот это и мешает... бо воркер - жив, хотя уже и не работает...
Ожидалось, что воркер занулится - нулл-эхцептион.
Для этого существует AsyncCompletedEventArgs.Error.
-----
Только он, увы, не доступен в рун-методе воркера.
В данном случае - без разницы, специфика имплементации.
Именно в данном случае race condition.
Как у тебя worker в реальном проекте выглядит, я не знаю.
По старой памяти - при выбросе исключения из потока - поток умирает.
BackgroundWorker - это не просто поток, но еще чуток кода. Так что советую почитать доку по нему.
Только он, увы, не доступен в рун-методе воркера.
Он доступен в другом месте. Читай https://msdn.microsoft.com/en-us/library/system.componentm...
Именно в данном случае race condition.
-----
В данном случае у меня управление вызовом - внешнее. Т.е. Я точно знаю, что назначение (+=) будет сделано ДО того как потребуется вызов.
То, что фактическая имплементация допускает возникновение расе кондитион - это уже отдельно.
Ну да это не критично - в живой системе сканеры создаются в мультитоне, там же, при создании, задается обработчик. Запуск происходит позднее.
Так что советую почитать доку по нему.
-----
Читал. Не помогает.
Недавно рассматривали решение с шарингом стрима между объектами потоков. Перерыл все до исходников - решения не нашел. Пришлось реализовывать по старинке.
Читай
-----
Видимо ты меня хотел вот сюда послать: https://msdn.microsoft.com/en-us/library/system.componentm...
Увы, у меня это не используется.
У меня либо есть данные и тогда вызывается метод обработки, либо данных нет и тогда все спит.
Руннер, как видно из примера, находится в том же воркере и поставляет наружу только корректные
данные.
Завершение работы - не предусмотрено. Совсем. Все возможные ошибки данных - обрабатываются
в вызываемом методе. Ошибки коммуникации с ком-портом - в руннере.
Хотя, скорее всего, следующим шагом будет проверка на жив ли поток и перезапуск если сдох...