Как стать автором
Обновить

Комментарии 37

А не скажете, бывают ли такие устройства с wifi?
И еще, можно ли писать на C++ или VB.Net?
Конечно, на моем образце есть wifi. Мы организовали wifi сеть на складе с повторителями, таким образом все сканеры постоянно в режиме online, в одной сети (примерно 15 сканеров и 20 точек доступа). А приложение позволяет напрямую подключаться и считывать данные с базы данных (через веб-сервис).
Язык, на котором писать — особой роли не играет. EMDK так же есть отдельно для C++.
Кстати, с помощью Symbol.WirelessLAN можно управлять WiFi приемником почти с такой же легкостью, как и сканером штрихкодов.
А это все уже устарело или только Symbol? Продаются такие железки сейчас и если да то сколько стоят?
Да, продают, и очень активно. В среднем от 15 000 до 180 000 рублей, в зависимости от характеристик. Что касается устаревания Symbol, я думаю, что это как-то связано с тем, событием, когда Google купила Motorola. Все новые терминалы сбора данных от Motorola выходят на андройде.
Motorola Solutions никак не связана с той Motorola, которую купил Google. Общего — только слово «Motorola».

А по поводу Андроида — ну а на чём их выпускать? На морально устаревших Windows CE/Mobile 6.5 и т.п.? Windows Phone Embedded ещё только грозятся выпустить. iOS не существует вне устройств Apple. А больше операционок-то и нет.

Мы купили Motorola TC55 на Android — отличная вещь.

И вообще, очень многие производители сейчас выпускают ТСД именно на Андроиде.
Девайс выглядит очень аппетитно. Удобно ли вводить данные в перчатках? В спецификации указано, что экран емкостной, но понимает, когда на него нажимают в перчатках.

Обязательно постараюсь себе такой достать.
Да, отлично понимает нажатия в перчатках, причём этот режим экрана можно отключать.
Какие преимущества у Motorola TC55 перед обычным защищенным смартфоном на андроиде с нормальной камерой?
как будет обновляться ПО в ТСД, в предложенной реализации?
Для начала стоит ответить на вопрос: «Для чего может потребоваться обновление?»

При работе с проектами, после завершения пилотного этапа, обновления, если и будут — то минимальны. Потому что подобные решения проектируются уже под бизнес-логику конкретного клиента, и если понадобится когда нибудь изменить сам порядок работы, то фактически придется прорабатывать проект заново, а потом уже вносить доработки в ПО. А все мелкие недочеты, оформление и «хотелки» решаются на пилотном этапе. Потому есть смысл ограничиться лишь запросом актуальности установленной версии, и блокировки работы, до последующего обновления вручную системным администратором.

В рабочих проектах вкладывать ресурсы на разработку самообновляющихся приложений имеет смысл только в случае работы над типовыми тиражируемыми продуктами. В этом случае, обычно, создается некий онлайн сервис, с помощью которого можно реализовать автозагрузку и установку новой версии приложения. Статей, о том как на C# реализовать подобное очень много.
В каком-то идеальном проекте и идеальной организации может обновлять и не потребуется. Занимаюсь складской логистикой уже два года и изменения в бизнесс-процессах могут быть достаточно серьёзными (а несерьёзные изменения — частыми). А когда в организации несколько тысяч ТСД (разных моделей) разбросанных по разным складам, то их обновление очень нетривиальная задача. Никаких самообновляющихся приложений не используем. Веб сервисы и всё через браузер работает прекрасно.
Наш клиент написан на C# и эволюционно мы пришли к следующей схеме:
Клиент очень тонкий, не содержит бизнес-логики.
Клиент хранит состояние (произвольная строка, обычно XML, клиент эту строку не парсит).
После каждого действия пользователя (сканирование, нажатие клавиши) состояние отправляется на сервер вместе с действием пользователя, а приходит ответ — новое состояние и что нужно показать в UI.
UI включает несколько типовых форм (два Memo, или Grid+Memo, или MessageBox), полностью настраиваемых — можно передать текст футера, хидера, цвет и содержимое некоторых элементов, в т.к. сложных типа Grid, если операция подразумевает показ мини-отчёта.

Обновлять такой клиент нужно очень редко, только в случаях, когда необходимой формы ещё не было (например, ввод ШК вручную). Новые формы UI делаются максимально гибкими, чтобы их можно было переиспользовать для разных сценариев.
А вариант с браузерной версией рассматривался? Почему выбрали именно такой вариант реализации, какие преимущества? Экономия трафика, требовательность к железу, скорость разработки и т.д.?
Не рассматривался. Я только из комментов к этой статье узнал, что есть возможность пользоваться сканером из js.
Теперь уже ресурсы на переписывание не выделят. Особенно сложно будет переучивать персонал.
Т.е. вы сделали браузер? :)
А как со скоростью работы? Ведь каждое сканирование обрабатывать на сервере — очень накладно и долго: пользователю надо отсканировать кучу штрихкодов, а он ждет 2-3 секунды после каждого…
Откуда такой прогноз?
Даже далёкие филиалы в Москве получают ответ за 100-200ms, если операция — просто добавить ШК в «состояние» (и проверить, существует ли ШК в базе). Зато некоторый плюс — каждый штрих проверяется в онлайне, пользователь быстрее получает отклик.
Время — всего лишь предположение. Плюсов у такого решения много, не спорю, но это накладывает жесткие ограничения на качество сети.
Теоретически можно представить сценарий, когда накопление ШК локально и отправка на сервер одним пакетом полезны.
Но на практике удобнее, чтобы каждый ШК проверялся он-лайн. Можно перепроверить наименование товара, вывести его на экран после сканирования. Особенно важно проверять перемещения и отгрузку. Если какой-то заказ не оплачен, сканер должен сразу выдать ошибку перемещения, не заставляя оператора потом искать среди 50 отсканированных позиций то, что надо вернуть.

Интересно, можно ли из браузера работать со спикером?
У нас есть разнообразная звуковая индикация ошибок и событий.
Если использовать «документную» архитектуру, а не товарную, то проблем с локальной обработкой меньше: на ТСД приходит «документ», например заказ, который необходимо собрать. Тут мы четко знаем какие шк мы ждем от пользователя.
Со спикером — не готов точно ответить сейчас, но проигрывать локальные звуковые файлы — да. Так же можно взаимодействовать с клавиатурой ТСД, файловой системой, делать вызовы внешних программ с ожиданием их ответа и еще много всего. Немаловажно, что браузер блокирует систему: из него нельзя выйти (если в своей программе не поедусмотреть это)
Расскажите поподробнее, что произойдет, если вы просканируете шк товара, которого нет в списке документа? Программа с этой архитектурой его просто не распознает?

Так же очень интересно, как вы работаете с такой архитектурой с упаковками, и весовым товаром. Ведь любому штрихкоду товара соответствует артикул и упаковка, в которой (т.е. и количество штук в упаковке) он лежит, если товар штучный, и артикул и его вес, если товар весовой.
Да, просто не распознает, а пользователю будет сообщение вроде «товара нет в документе».
С упаковкой и весом согласен, так не поработаешь. Но ведь мы выбираем решения в зависимости от целей :)
На данный момент бизнес-логика не предполагает таких операций, а если требования появятся, то в браузере тоже не составит труда сходить на сервер аяксом в любой момент
Есть у нас и «документные» терминалы, такие как этот habrahabr.ru/post/112726/
Всё же онлайн-модель удобнее и пользователям, и разработчикам.

Если приложение браузерное, оно скорее всего чувствительно к сбоям сети.
Что будет, если в момент выполнения POST-запроса прервётся связь? Браузер выведет страницу с ошибкой?
В нашей архитектуре автомат остаётся в предыдущем состоянии. Оператор повторяет событие (сканирование, нажатие кнопки) и делается повторная попытка отправки.
Связь прервется — будет ошибка по таймауту. В принципе, ничего криминального — документ на сервере «отработает» в любом случае.
Да, удобнее и надежнее, соглашусь, но опять-же повторюсь — накладывает более серьезные ограничения на архитектуру сети.
Но и такая модель как я описал — вполне жизнеспособна.
А если прервётся до начала POST-запроса, будет потеря данных?
Нет, потери данных не будет в любом случае. Если запрос не ушел или не вернулся, локальные данные остаются неизмененными и могут быть переданы повторно при восстановлении связи. Вручную или автоматически.
Интересно, что пользователь видит в браузере. Например, сервер вернул 503 по каким-то причинам.
Нужно в браузере нажать back и ничего не пропадёт? Документ на сохранение хранится в local storage? Оно есть в IE на WinCE?
Все гораздо проще :)
Кнопок back/forward — нет в принципе. Если делать правильно и через Ajax запросы, то показать можно обычный алерт с фразой «ошибка сохранения, попробуйте еще раз» на любой шухер с сервера. Локального хранилища нет, к сожалению. Приходится работать по-старинке через массивы, циклы. Мельком читал, что есть версии браузеров с поддержкой работы со сканерами на WebKit — там может и есть local storage, не углублялся.
У себя используем на моторолловских ТСД их же pocket browser — расширенный ie со встроенными JavaScript библиотеками по работе с аппаратным обеспечением ТСД. По удобству разработки — сносно, но огромный плюс в обновлениях ПО на огромном парке ТСД.
В свое время запускали склад на таких ТСД. В качестве платформы использовали софт компании Клеверенс-Софт… Не сочтите за рекламу, но ребя та сделали очень грамотную и гибкую систему, где разработка бизнес-логики — одно удовольствие. Там по сути не программирование даже, а конфигурирование.
прочитал как «Пишем под ЛСД». Обрадовался
Писал года 4 назад интеграцию с софтом Клеверенс-Софт для нашего приложения.
В принципе у Клеверенс-Софт много чего есть (по организации складского учёта) и достаточно лишь вставить устройство в кредл, нажать на приложение обмен и выгрузить данные в нужном формате. Ну и тоже самое для загрузки.
Тестировал на Motoroll-e и DataLogic-е.
У Клеверенс-Софт непонятно, зачем нужно вставлять устройство в кредл, когда на дворе 2014 год, а в любом устройстве есть Wi-Fi.
Возможно у них уже есть решение и для Wi-Fi.
На тот момент клиента устраивало и такой вариант…
Я такую штуку писал под Casio IT-600, а затем ещё на несколько дейвайсов.
Обновлялку делал через .cab файлы.
Для идентификации устройства использовал:
Платформу:
public const UInt32 SPI_GETPLATFORMTYPE = 257;
[DllImport("coredll")]
public extern static Boolean SystemParametersInfo(
	UInt32 uiAction,
	UInt32 uiParam,
	StringBuilder pvParam,
	UInt32 fWinIni);

public static String GetPlatformType()
{
	StringBuilder sb = new StringBuilder(255);
	if(Environment.OSVersion.Platform == PlatformID.Win32NT)
		return "WIN32";//NativeMethods.SystemParametersInfoWin32(NativeMethods.SPI_GETPLATFORMTYPE, (UInt32)sb.Capacity, sb, 0);
	else
		NativeMethods.SystemParametersInfo(NativeMethods.SPI_GETPLATFORMTYPE, (UInt32)sb.Capacity, sb, 0);

	String platType = sb.ToString();
	switch(platType)
	{
		case "PocketPC":
			return "PPC";
		case "Windows CE":
			return "WCE";
		case "Phoenix"://Casio IT-600
			return "Phoenix";
		default:
			return platType;
	}
}


И процессор:
[DllImport("Coredll")]

public extern static Boolean QueryInstructionSet(
	UInt32 dwInstructionSet,
	out CpuInstructionSet CurrentInstructionSet
	);

public enum CpuInstructionSet
{
	X86 = Constant.Processor.PROCESSOR_X86_32BIT_INSTRUCTION,
	SH3 = Constant.Processor.PROCESSOR_HITACHI_SH3_INSTRUCTION,
	SH4 = Constant.Processor.PROCESSOR_HITACHI_SH4_INSTRUCTION,
	MIPSV4 = Constant.Processor.PROCESSOR_MIPS_MIPSIV_INSTRUCTION,
	MIPSV4_FP = Constant.Processor.PROCESSOR_MIPS_MIPSIVFP_INSTRUCTION,
	MIPSVII = Constant.Processor.PROCESSOR_MIPS_MIPSII_INSTRUCTION,
	MIPSVII_FP = Constant.Processor.PROCESSOR_MIPS_MIPSIIFP_INSTRUCTION,
	MIPS16 = Constant.Processor.PROCESSOR_MIPS_MIPS16_INSTRUCTION,
	ARMV4 = Constant.Processor.PROCESSOR_ARM_V4_INSTRUCTION,
	ARMV4T = Constant.Processor.PROCESSOR_ARM_V4T_INSTRUCTION,
	ARMV4I =Constant.Processor.PROCESSOR_ARM_V4I_INSTRUCTION,
	ARMV4FP = Constant.Processor.PROCESSOR_ARM_V4FP_INSTRUCTION,
	ARMV4IFP = Constant.Processor.PROCESSOR_ARM_V4IFP_INSTRUCTION,
	ARMV4TFP = Constant.Processor.PROCESSOR_ARM_V4TFP_INSTRUCTION,
}

+ функции для старых версий ОС (Возможно сейчас уже и не пригодятся)

internal struct SYSTEM_INFO
{
	public UInt16 wProcessorArchitecture;
	public UInt16 wReserved;
	public UInt32 dwPageSize;
	public Int32 lpMinimumApplicationAddress;
	public Int32 lpMaximumApplicationAddress;
	public UInt32 dwActiveProcessorMask;
	public UInt32 dwNumberOfProcessors;
	public UInt32 dwProcessorType;
	public UInt32 dwAllocationGranularity;
	public UInt16 wProcessorLevel;
	public UInt16 wProcessorRevision;
}

[DllImport("Coredll")]
public extern static void GetSystemInfo(ref SYSTEM_INFO SystemInfo);

[DllImport("Kernel32.dll", EntryPoint = "GetSystemInfo")]
public extern static void GetSystemInfoWin32(ref SYSTEM_INFO SystemInfo);


Итого:
public static String GetInstructionSet()
{
	CpuInstructionSet iset = CpuInstructionSet.X86;
	try
	{
		if(Environment.OSVersion.Platform == PlatformID.Win32NT)
			return iset.ToString();
		else
			NativeMethods.QueryInstructionSet(0, out iset);
	}
	catch(MissingMethodException)
	{
		SYSTEM_INFO si = new SYSTEM_INFO();
		if(Environment.OSVersion.Platform == PlatformID.Win32NT)
			NativeMethods.GetSystemInfoWin32(ref si);
		else
			NativeMethods.GetSystemInfo(ref si);
		switch(si.wProcessorArchitecture)
		{
			case Constant.Processor.Architecture.PROCESSOR_ARCHITECTURE_ARM:
				return CpuInstructionSet.ARMV4.ToString();
			case Constant.Processor.Architecture.PROCESSOR_ARCHITECTURE_SHX:
				return CpuInstructionSet.SH3.ToString();
			case Constant.Processor.Architecture.PROCESSOR_ARCHITECTURE_INTEL:
				return CpuInstructionSet.X86.ToString();
			case Constant.Processor.Architecture.PROCESSOR_ARCHITECTURE_MIPS:
				return CpuInstructionSet.MIPSV4.ToString();
			default:
				return "Unkown_"+si.wProcessorArchitecture.ToString();
		}
	}

	return iset.ToString();
}


А работало это всё через «завод» с использованием ScanAPI или OBR. На выбор.
Проект писался в 2009 году. Возможно с тех времён что-то и поменялось…
Но если кому интересно, могу выложить некоторые куски. Может кому-то пригодятся.
У меня сейчас аццкая поделка под название Mobile logistics. Как раз потому что оборудование на wince. Подождали бы чуть-чуть, купили бы на андроиде — писал бы на относительно нормальной 1ске.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории