ShippingPanel.xaml.cs 16 KB

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