ShippingPanel.xaml.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Windows;
  5. using System.Windows.Controls;
  6. using Comal.Classes;
  7. using InABox.Clients;
  8. using InABox.Core;
  9. using InABox.DynamicGrid;
  10. using InABox.WPF;
  11. using Motorola.Snapi;
  12. using Motorola.Snapi.Constants.Enums;
  13. using Motorola.Snapi.EventArguments;
  14. namespace PRSDesktop
  15. {
  16. /// Notes for Motorola Barcode Scanners
  17. /// 1) Install Scanner Driver (CoreScanner)
  18. /// 2) Ensure Scanner is set to SNAPI-NoImaging
  19. /// <summary>
  20. /// Interaction logic for ShippingPanel.xaml
  21. /// </summary>
  22. public partial class DispatchPanel : UserControl, IPanel<Shipment>
  23. {
  24. private bool _updatingItems;
  25. private readonly Dictionary<CodeStatus, BeepPattern> BeepList = new()
  26. {
  27. { CodeStatus.Initialized, BeepPattern.FastWarble },
  28. { CodeStatus.Opened, BeepPattern.ThreeHighShort },
  29. { CodeStatus.Closed, BeepPattern.ThreeLowShort },
  30. { CodeStatus.Added, BeepPattern.LowHigh },
  31. { CodeStatus.Removed, BeepPattern.HighLow },
  32. { CodeStatus.NotFound, BeepPattern.FourLowLong },
  33. { CodeStatus.Error, BeepPattern.FourLowShort }
  34. };
  35. private bool? Item;
  36. private CoreTable ItemCache;
  37. private DateTime Last = DateTime.MinValue;
  38. private bool? Product;
  39. private CoreTable ProductCache;
  40. private bool? Requi;
  41. public List<IMotorolaBarcodeScanner> Scanners = new();
  42. private bool? Shipment;
  43. //Shipment[] ShipmentList = new Shipment[] { };
  44. //private Shipment CurrentShipment = null;
  45. //private DispatcherTimer timer = new DispatcherTimer();
  46. //private DateTime lastselection = DateTime.MaxValue;
  47. //private DispatcherTimer processing = new DispatcherTimer();
  48. public DispatchPanel()
  49. {
  50. InitializeComponent();
  51. Shipments.OnSelectItem += Shipments_OnSelectItem;
  52. //timer.Interval = new TimeSpan(0, 0, 0, 0, 100);
  53. //timer.Tick += Timer_Tick;
  54. //timer.IsEnabled = true;
  55. //processing.Interval = new TimeSpan(0, 0, 1);
  56. //processing.Tick += (o, e) => WaitBeep();
  57. }
  58. public bool IsReady { get; set; }
  59. public Dictionary<string, object[]> Selected()
  60. {
  61. return new Dictionary<string, object[]>
  62. {
  63. { typeof(Shipment).EntityName(), Shipments.SelectedRows },
  64. { typeof(DeliveryItem).EntityName(), Items.SelectedRows }
  65. };
  66. }
  67. public void Setup()
  68. {
  69. SetupScanner();
  70. UpdateLayout();
  71. Shipments.Refresh(true, false);
  72. Shipments.SelectedRows = new CoreRow[] { }; //CurrentRow = -1;
  73. Items.CurrentShipmentID = Guid.Empty;
  74. Items.Refresh(true, false);
  75. RefreshCache();
  76. }
  77. public void Shutdown()
  78. {
  79. foreach (var scanner in Scanners)
  80. scanner.Actions.ToggleLed(LedMode.GreenOff);
  81. BarcodeScannerManager.Instance.DataReceived -= Instance_DataReceived;
  82. BarcodeScannerManager.Instance.Close();
  83. }
  84. public event DataModelUpdateEvent OnUpdateDataModel;
  85. public void CreateToolbarButtons(IPanelHost host)
  86. {
  87. //host.CreatePanelAction(new PanelAction() { Caption = "Mark As Delivered", OnExecute = MarkAsDelivered, Image = PRSDesktop.Resources.barcode });
  88. }
  89. //private void MarkAsDelivered(PanelAction obj)
  90. //{
  91. // Shipment shipment = new Client<Shipment>().Load(new Filter<Shipment>(x => x.ID).IsEqualTo(Items.CurrentShipmentID)).FirstOrDefault();
  92. // if (shipment == null)
  93. // {
  94. // MessageBox.Show("Please select a shipment!");
  95. // return;
  96. // }
  97. // var rc = MessageBox.Show("This will mark all the items on this rack as delivered.\n\nDo you also want to empty the rack of all items?", "Confirmation", MessageBoxButton.YesNoCancel);
  98. // if (rc != MessageBoxResult.Cancel)
  99. // {
  100. // DeliveryNotification notification = new DeliveryNotification();
  101. // notification.ShipmentLink.ID = shipment.ID;
  102. // notification.Location.Latitude = shipment.TrackerLink.Location.Latitude;
  103. // notification.Location.Longitude = shipment.TrackerLink.Location.Longitude;
  104. // notification.Location.Timestamp = shipment.TrackerLink.Location.Timestamp;
  105. // if (rc == MessageBoxResult.No)
  106. // {
  107. // foreach (CoreRow row in Items.Data.Rows)
  108. // notification.RetainedItems.Add(row.Get<DeliveryItem, Guid>(x => x.ID));
  109. // }
  110. // using (new WaitCursor())
  111. // {
  112. // new Client<DeliveryNotification>().Save(notification, "Manual Delivery Notification");
  113. // Refresh();
  114. // }
  115. // MessageBox.Show("Rack Updated");
  116. // }
  117. //}
  118. public void Refresh()
  119. {
  120. Shipments.Refresh(false, true);
  121. }
  122. public string SectionName => "Shipping";
  123. public DataModel DataModel(Selection selection)
  124. {
  125. var ids = Shipments.ExtractValues(x => x.ID, selection).ToArray();
  126. return new ShipmentDataModel(new Filter<Shipment>(x => x.ID).InList(ids));
  127. }
  128. public void Heartbeat(TimeSpan time)
  129. {
  130. }
  131. private void RefreshCache()
  132. {
  133. ClientFactory.MultiQuery(
  134. new IQueryDef[]
  135. {
  136. new QueryDef<Product>(
  137. null,
  138. new Columns<Product>(
  139. x => x.ID,
  140. x => x.Code,
  141. x => x.Name
  142. ),
  143. null
  144. ),
  145. new QueryDef<DeliveryItem>(
  146. new Filter<DeliveryItem>(x => x.DeliveredDate).IsEqualTo(DateTime.MinValue)
  147. .And(x => x.ManufacturingPacketLink).LinkValid(),
  148. Items.DataColumns(),
  149. null
  150. )
  151. },
  152. results =>
  153. {
  154. Dispatcher.Invoke(() =>
  155. {
  156. ProductCache = results[0];
  157. ItemCache = results[1];
  158. });
  159. }
  160. );
  161. }
  162. private void SetupScanner()
  163. {
  164. // From Motorola Documentation
  165. // May not be complete?
  166. var scannermodes = new Dictionary<string, string>
  167. {
  168. { "XUA-45001-1", "IBM HID" },
  169. { "XUA-45001-2", "IBM TABLETOP HID" },
  170. { "XUA-45001-3", "HID KEYBOARD" },
  171. { "XUA-45001-8", "OPOS" },
  172. { "XUA-45001-9", "SNAPI w/o Imaging" },
  173. { "XUA-45001-10", "SNAPI with Imaging" },
  174. { "XUA-45001-11", "CDC Serial Emulation" }
  175. };
  176. Scanners.Clear();
  177. BarcodeScannerManager.Instance.Open();
  178. BarcodeScannerManager.Instance.RegisterForEvents(EventType.Barcode, EventType.Pnp, EventType.Image, EventType.Other, EventType.Rmd);
  179. BarcodeScannerManager.Instance.GetDevices();
  180. foreach (var scanner in BarcodeScannerManager.Instance.GetDevices())
  181. {
  182. var mode = scanner.Info.UsbHostMode;
  183. if (string.Equals(mode, "XUA-45001-9"))
  184. {
  185. try
  186. {
  187. scanner.Actions.ToggleLed(LedMode.RedOn);
  188. Scanners.Add(scanner);
  189. }
  190. catch (Exception e)
  191. {
  192. Logger.Send(LogType.Error, "",
  193. string.Format("Exception initialising scanner #{0}: {1}\n{2}", scanner.Info.ScannerId, e.Message, e.StackTrace));
  194. }
  195. }
  196. else
  197. {
  198. var value = scannermodes.ContainsKey(mode) ? scannermodes[mode] : string.Format("Unknown ({0})", mode);
  199. MessageBox.Show(string.Format(
  200. "Scanner #{0} is set to [{1}]!\n\nPlease set it to [SNAPI w/o Imaging] by scanning the appropriate setup barcode.",
  201. scanner.Info.SerialNumber, value));
  202. }
  203. }
  204. if (Scanners.Any())
  205. {
  206. Beep(CodeStatus.Initialized);
  207. BarcodeScannerManager.Instance.DataReceived += Instance_DataReceived;
  208. }
  209. else
  210. {
  211. MessageBox.Show(
  212. "Cannot find any valid scanners!\n\nPlease make sure that the scanner is turned on, connected to the PC, and set to SNAPI (w/o Imaging).");
  213. }
  214. }
  215. private void Beep(CodeStatus status)
  216. {
  217. foreach (var scanner in Scanners)
  218. {
  219. if (status == CodeStatus.Opened)
  220. scanner.Actions.ToggleLed(LedMode.GreenOn);
  221. else if (status == CodeStatus.Closed)
  222. scanner.Actions.ToggleLed(LedMode.RedOn);
  223. scanner.Actions.SoundBeeper(BeepList[status]);
  224. }
  225. }
  226. private void WaitBeep()
  227. {
  228. foreach (var scanner in Scanners)
  229. {
  230. scanner.Actions.ToggleLed(LedMode.YellowOn);
  231. scanner.Actions.SoundBeeper(BeepPattern.OneHighShort);
  232. }
  233. }
  234. private void Instance_DataReceived(object sender, BarcodeScanEventArgs e)
  235. {
  236. var scanner = Scanners.FirstOrDefault(x => x.Info.ScannerId == (int)e.ScannerId);
  237. if (scanner != null)
  238. Dispatcher.Invoke(() => { CheckCode(scanner, e.Data); });
  239. }
  240. //WaitCursor waiting = null;
  241. //private void StartWaiting()
  242. //{
  243. // if (waiting == null)
  244. // waiting = new WaitCursor();
  245. //}
  246. //private void StopWaiting()
  247. //{
  248. // if (waiting != null)
  249. // waiting.Dispose();
  250. // waiting = null;
  251. //}
  252. private void CheckCode(IMotorolaBarcodeScanner scanner, string code)
  253. {
  254. using (new WaitCursor())
  255. {
  256. Product = null;
  257. Item = null;
  258. Shipment = null;
  259. Requi = null;
  260. var row = CheckShipmentLocalCache(code);
  261. if (row == null)
  262. row = CheckShipmentOnServer(code);
  263. if (row != null)
  264. {
  265. Shipments.ScrollIntoView(row);
  266. Shipments.SelectedRows = new[] { row };
  267. Beep(CodeStatus.Opened);
  268. LoadShipment();
  269. //lastselection = DateTime.Now;
  270. return;
  271. }
  272. if (Shipments.SelectedRows.Any())
  273. {
  274. var item = CheckItem(code);
  275. if (item != null)
  276. {
  277. var icount = Shipments.SelectedRows.First().Get<Shipment, int>(x => x.ItemCount);
  278. var itemrow = Items.Data.Rows.Where(r => r.Get<DeliveryItem, string>(c => c.Barcode) == item.Barcode).FirstOrDefault();
  279. if (itemrow != null)
  280. {
  281. item.ShipmentCode = "";
  282. item.ShipmentLink.ID = Guid.Empty;
  283. item.Delivery.ID = Guid.Empty;
  284. Items.DeleteRow(itemrow);
  285. icount--;
  286. }
  287. else
  288. {
  289. item.ShipmentLink.ID = Items.CurrentShipmentID;
  290. item.Delivery.ID = Shipments.SelectedRows.First().Get<Shipment, Guid>(x => x.Delivery.ID);
  291. item.ShipmentCode = Shipments.SelectedRows.First().Get<Shipment, string>(x => x.Code);
  292. Items.AddRow(item);
  293. icount++;
  294. }
  295. _updatingItems = true;
  296. Shipments.UpdateRow<Shipment, int>(Shipments.SelectedRows.First(), x => x.ItemCount, icount);
  297. _updatingItems = false;
  298. new Client<DeliveryItem>().Save(
  299. item,
  300. string.Format("Item {0} Rack {1}", !item.ShipmentLink.IsValid() ? "removed from" : "added to",
  301. Shipments.SelectedRows.First().Get<Shipment, string>(x => x.Code)),
  302. (o, e) => { }
  303. );
  304. Beep(itemrow != null ? CodeStatus.Removed : CodeStatus.Added);
  305. return;
  306. //StopWaiting();
  307. }
  308. }
  309. Beep(CodeStatus.Error);
  310. }
  311. }
  312. private CoreRow CheckShipmentLocalCache(string code)
  313. {
  314. var row = Shipments.MasterData.Rows.FirstOrDefault(r => r.Get<Shipment, string>(c => c.BarCode).Equals(code));
  315. if (row != null)
  316. return row;
  317. return null;
  318. }
  319. private CoreRow? CheckShipmentOnServer(string code)
  320. {
  321. var row = new Client<Shipment>().Query(new Filter<Shipment>(x => x.BarCode).IsEqualTo(code))
  322. .Rows.FirstOrDefault();
  323. if (row != null)
  324. {
  325. Shipments.AddRow(row);
  326. return row;
  327. }
  328. return null;
  329. }
  330. private DeliveryItem CheckItem(string code)
  331. {
  332. var row = ItemCache.Rows.FirstOrDefault(r => String.Equals(r.Get<DeliveryItem, string>(c => c.Barcode),code));
  333. return row != null ? row.ToObject<DeliveryItem>() : null;
  334. }
  335. //private void Timer_Tick(object sender, EventArgs e)
  336. //{
  337. // if (lastselection < DateTime.Now.AddMilliseconds(-500))
  338. // {
  339. // timer.IsEnabled = false;
  340. // try
  341. // {
  342. // lastselection = DateTime.MaxValue;
  343. // LoadShipment();
  344. // }
  345. // finally
  346. // {
  347. // timer.IsEnabled = true;
  348. // }
  349. // }
  350. //}
  351. private void LoadShipment()
  352. {
  353. RefreshCache();
  354. var row = Shipments.SelectedRows.FirstOrDefault();
  355. foreach (var scanner in Scanners)
  356. scanner.Actions.ToggleLed(row != null ? LedMode.GreenOn : LedMode.RedOn);
  357. var shipid = row != null ? row.Get<Shipment, Guid>(x => x.ID) : Guid.Empty;
  358. if (shipid != Items.CurrentShipmentID)
  359. {
  360. Items.CurrentShipmentID = shipid;
  361. Items.Refresh(false, true);
  362. }
  363. }
  364. private void Shipments_OnSelectItem(object sender, DynamicGridSelectionEventArgs e)
  365. {
  366. LoadShipment();
  367. //if (!_updatingItems && IsReady)
  368. // lastselection = DateTime.Now;
  369. }
  370. public Type DataType()
  371. {
  372. return typeof(Shipment);
  373. }
  374. public Dictionary<Type, CoreTable> DataEnvironment()
  375. {
  376. var env = new Dictionary<Type, CoreTable>();
  377. env[typeof(Shipment)] = Shipments.Data;
  378. env[typeof(DeliveryItem)] = new Client<DeliveryItem>().Query(new Filter<DeliveryItem>(x => x.ShipmentLink).LinkValid());
  379. return env;
  380. }
  381. private enum CodeStatus
  382. {
  383. Initialized,
  384. Opened,
  385. Closed,
  386. Added,
  387. Removed,
  388. Error,
  389. NotFound
  390. }
  391. }
  392. }