ShippingPanel.xaml.cs 16 KB

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