ScannerViewModel.cs 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. using Comal.Classes;
  2. using CommunityToolkit.Mvvm.ComponentModel;
  3. using CommunityToolkit.Mvvm.Input;
  4. using InABox.Avalonia;
  5. using InABox.Avalonia.Dialogs;
  6. using InABox.Avalonia.Platform.Barcodes;
  7. using InABox.Core;
  8. using Microsoft.Maui.Devices;
  9. using PRS.Avalonia.Modules;
  10. using System;
  11. using System.Collections.Generic;
  12. using System.Linq;
  13. using System.Threading.Tasks;
  14. namespace PRS.Avalonia.Components;
  15. [Flags]
  16. public enum ScannerMode
  17. {
  18. None = 0,
  19. Equipment = 1,
  20. Delivery = 2,
  21. Setout = 4,
  22. ManufacturingPacket = 8,
  23. Product = 16,
  24. All = ScannerMode.Equipment | ScannerMode.Delivery | ScannerMode.Setout | ScannerMode.ManufacturingPacket | ScannerMode.Product
  25. }
  26. public partial class ScannerViewModel : ModuleViewModel
  27. {
  28. public override string Title => "Scanner";
  29. [ObservableProperty]
  30. private bool _cameraEnabled;
  31. [ObservableProperty]
  32. private bool _processing;
  33. [ObservableProperty]
  34. private Func<IReadOnlySet<BarcodeResult>, Task<bool>>? _itemScanned;
  35. [ObservableProperty]
  36. private ScannerMode _mode;
  37. private (ScannerMode Mode, Func<BarcodeResult, Task<IScanResult?>> Func)[] Modes;
  38. [ObservableProperty]
  39. private bool _scannerVisible = true;
  40. public ScannerViewModel()
  41. {
  42. Modes = [
  43. (ScannerMode.Setout, ScanSetout),
  44. (ScannerMode.ManufacturingPacket, ScanManufacturingPacket),
  45. (ScannerMode.Product, ScanProduct),
  46. (ScannerMode.Equipment, ScanEquipment),
  47. (ScannerMode.Delivery, ScanDelivery)
  48. ];
  49. }
  50. protected override Task OnActivated()
  51. {
  52. CameraEnabled = true;
  53. return base.OnActivated();
  54. }
  55. protected override Task OnDeactivated()
  56. {
  57. CameraEnabled = false;
  58. return base.OnDeactivated();
  59. }
  60. private interface IScanResult
  61. {
  62. public string Name { get; }
  63. public Task Continue();
  64. }
  65. private class ScanResult<T>(string name, T obj, Func<T, Task> continuation) : IScanResult
  66. {
  67. public string Name { get; set; } = name;
  68. public T Object { get; set; } = obj;
  69. public Func<T, Task> Continuation { get; set; } = continuation;
  70. public async Task Continue()
  71. {
  72. await Continuation(Object);
  73. }
  74. }
  75. private ScanResult<T> Result<T>(string name, T obj, Func<T, Task> continuation) => new ScanResult<T>(name, obj, continuation);
  76. [RelayCommand]
  77. private async Task DetectionFinished(IReadOnlySet<BarcodeResult> result)
  78. {
  79. if(result.Count > 0)
  80. {
  81. if (Processing || !CameraEnabled) return;
  82. Processing = true;
  83. var scanned = false;
  84. if(ItemScanned != null)
  85. {
  86. if(await ItemScanned.Invoke(result))
  87. {
  88. scanned = true;
  89. CameraEnabled = false;
  90. }
  91. }
  92. if (!scanned && Mode != ScannerMode.None)
  93. {
  94. var results = new List<IScanResult>();
  95. var modes = Modes.Where(x => Mode.HasFlag(x.Mode)).ToArray();
  96. foreach(var barcode in result.ToArray())
  97. {
  98. var tasks = modes.ToArray(x => x.Func(barcode));
  99. await Task.WhenAll(tasks);
  100. results.AddRange(tasks.Select(x => x.Result).NotNull());
  101. }
  102. if(results.Count == 1)
  103. {
  104. Vibration.Vibrate();
  105. await results[0].Continue();
  106. }
  107. else if(results.Count > 1)
  108. {
  109. Vibration.Vibrate();
  110. ScannerVisible = false;
  111. var choice = await OptionDialog.Execute("Choose Item:", results.ToArray(x => x.Name));
  112. ScannerVisible = true;
  113. var selected = choice is not null ? results.FirstOrDefault(x => x.Name == choice) : null;
  114. if(selected is not null)
  115. {
  116. await selected.Continue();
  117. }
  118. }
  119. else
  120. {
  121. Vibration.Vibrate();
  122. await Task.Delay(200);
  123. Vibration.Vibrate();
  124. await Task.Delay(1000);
  125. }
  126. }
  127. Processing = false;
  128. }
  129. }
  130. private async Task<IScanResult?> ScanSetout(BarcodeResult barcode)
  131. {
  132. var filter = Guid.TryParse(barcode.RawValue, out var id)
  133. ? new Filter<DeliveryItem>(x => x.ID).IsEqualTo(id)
  134. : new Filter<DeliveryItem>(x => x.Barcode).IsEqualTo(barcode.RawValue);
  135. var model = new DeliveryItemModel(
  136. DataAccess,
  137. () => filter,
  138. () => DefaultCacheFileName<DeliveryItemShell>());
  139. await model.RefreshAsync(true);
  140. var item = model.FirstOrDefault();
  141. if(item is null || item.SetoutID == Guid.Empty)
  142. {
  143. return null;
  144. }
  145. var setoutModel = new SetoutModel(
  146. DataAccess,
  147. () => new Filter<Setout>(x => x.ID).IsEqualTo(item.SetoutID),
  148. () => DefaultCacheFileName<SetoutShell>(item.SetoutID));
  149. await setoutModel.RefreshAsync(true);
  150. var setout = setoutModel.FirstOrDefault();
  151. if(setout is null)
  152. {
  153. return null;
  154. }
  155. return Result($"Design {setout.Number}", setout, OpenSetout);
  156. }
  157. private Task OpenSetout(SetoutShell shell)
  158. {
  159. Navigation.Navigate<ManufacturingDesignDetailsViewModel>(model =>
  160. {
  161. model.Shell = shell;
  162. });
  163. return Task.CompletedTask;
  164. }
  165. private async Task<IScanResult?> ScanManufacturingPacket(BarcodeResult barcode)
  166. {
  167. if (!Mode.HasFlag(ScannerMode.ManufacturingPacket)) return null;
  168. if(!Guid.TryParse(barcode.RawValue, out var id))
  169. {
  170. return null;
  171. }
  172. var model = new ManufacturingPacketModel(
  173. DataAccess,
  174. () => new Filter<ManufacturingPacket>(x => x.ID).IsEqualTo(id),
  175. () => DefaultCacheFileName<ManufacturingPacketShell>());
  176. await model.RefreshAsync(true);
  177. var packet = model.FirstOrDefault();
  178. return packet is not null
  179. ? Result($"Manufacturing packet {packet.SetoutNumber}", packet, OpenManufacturingPacket)
  180. : null;
  181. }
  182. private Task OpenManufacturingPacket(ManufacturingPacketShell shell)
  183. {
  184. Navigation.Navigate<ManufacturingPacketDetailsViewModel>(model =>
  185. {
  186. model.Shell = shell;
  187. });
  188. return Task.CompletedTask;
  189. }
  190. private Task<IScanResult?> ScanProduct(BarcodeResult barcode)
  191. {
  192. return Task.FromResult<IScanResult?>(null);
  193. }
  194. private async Task<IScanResult?> ScanDelivery(BarcodeResult barcode)
  195. {
  196. if(!Guid.TryParse(barcode.RawValue, out var id))
  197. {
  198. return null;
  199. }
  200. var model = new DeliveryModel(
  201. DataAccess,
  202. () => new Filter<Delivery>(x => x.ID).IsEqualTo(id),
  203. () => DefaultCacheFileName<DeliveryShell>());
  204. await model.RefreshAsync(true);
  205. var delivery = model.FirstOrDefault();
  206. return delivery is not null
  207. ? Result($"Delivery {delivery.Number} {delivery.TypeDescription}", delivery, OpenDelivery)
  208. : null;
  209. }
  210. private Task OpenDelivery(DeliveryShell shell)
  211. {
  212. Navigation.Navigate<DeliveryViewModel>(model =>
  213. {
  214. model.Delivery = shell;
  215. });
  216. return Task.CompletedTask;
  217. }
  218. private async Task<IScanResult?> ScanEquipment(BarcodeResult barcode)
  219. {
  220. var filter = Guid.TryParse(barcode.RawValue, out var id)
  221. ? new Filter<Equipment>(x => x.ID).IsEqualTo(id)
  222. : new Filter<Equipment>(x => x.Code).IsEqualTo(barcode.RawValue);
  223. var model = new EquipmentModel(
  224. DataAccess,
  225. () => filter,
  226. () => DefaultCacheFileName<EquipmentShell>());
  227. await model.RefreshAsync(true);
  228. var equipment = model.FirstOrDefault();
  229. if(equipment is null)
  230. {
  231. return null;
  232. }
  233. else
  234. {
  235. return Result($"Equipment {equipment.Code}", equipment, OpenEquipment);
  236. }
  237. }
  238. private Task OpenEquipment(EquipmentShell shell)
  239. {
  240. Navigation.Navigate<EquipmentDetailsViewModel>(model =>
  241. {
  242. model.Shell = shell;
  243. });
  244. return Task.CompletedTask;
  245. }
  246. }