ShippingPanel.xaml.cs 16 KB

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