|
|
@@ -2,21 +2,16 @@
|
|
2
|
2
|
using System.Collections.Generic;
|
|
3
|
3
|
using System.ComponentModel;
|
|
4
|
4
|
using System.Data.SQLite;
|
|
5
|
|
-using System.Diagnostics;
|
|
6
|
5
|
using System.Drawing;
|
|
7
|
6
|
using System.Globalization;
|
|
8
|
|
-using System.Threading;
|
|
|
7
|
+using System.Security.Permissions;
|
|
9
|
8
|
using System.Windows.Forms;
|
|
10
|
|
-using Serilog;
|
|
11
|
9
|
|
|
12
|
10
|
namespace BulkPrinting
|
|
13
|
11
|
{
|
|
14
|
12
|
public partial class BatchForm : ObservedForm
|
|
15
|
13
|
{
|
|
16
|
|
- Thread LoadingThread;
|
|
17
|
|
- readonly object LoadingThreadLock = new object();
|
|
18
|
|
- bool CheckForNewBatches;
|
|
19
|
|
- bool RetryDownloads;
|
|
|
14
|
+ private BatchDownloader BatchDownloader;
|
|
20
|
15
|
|
|
21
|
16
|
public BatchForm()
|
|
22
|
17
|
{
|
|
|
@@ -258,254 +253,36 @@ namespace BulkPrinting
|
|
258
|
253
|
dtpFilterStartDate.Value = DateTime.Now.AddMonths(-3);
|
|
259
|
254
|
Globals.OpenBatches = new List<int>();
|
|
260
|
255
|
|
|
261
|
|
- RetryDownloads = true;
|
|
262
|
|
- CheckForNewBatches = true;
|
|
|
256
|
+ var hwnd = new System.Runtime.InteropServices.HandleRef(this, Handle);
|
|
|
257
|
+ BatchDownloader = new BatchDownloader(Globals.DB, () =>
|
|
|
258
|
+ {
|
|
|
259
|
+ // DO NOT use Invoke as this can cause a deadlock if we are attempting to join the BatchDownloader after a cancel.
|
|
|
260
|
+ // Also, use PostMessage rather than SendMessage as PostMessage doesn't wait for the message to be processed and
|
|
|
261
|
+ // therefore won't block.
|
|
|
262
|
+ Utility.PostMessage(hwnd, Utility.WM_USER, IntPtr.Zero, IntPtr.Zero);
|
|
|
263
|
+ });
|
|
|
264
|
+ BatchDownloader.Start();
|
|
263
|
265
|
|
|
264
|
|
- LoadingThread = new Thread(LoadingThreadWorker);
|
|
265
|
|
- LoadingThread.Start();
|
|
266
|
266
|
Utility.InitialiseUserLimits(Globals.DB);
|
|
267
|
|
- ReSyncTimer.Enabled = true;
|
|
|
267
|
+
|
|
|
268
|
+ PopulateGrid();
|
|
268
|
269
|
}
|
|
269
|
270
|
|
|
270
|
|
- private void LoadingThreadWorker()
|
|
|
271
|
+ [SecurityPermissionAttribute(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
|
|
|
272
|
+ [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
|
|
|
273
|
+ protected override void WndProc(ref Message m)
|
|
271
|
274
|
{
|
|
272
|
|
- const int voucherPageSize = 5000;
|
|
273
|
|
- var db = Globals.DB;
|
|
274
|
|
- //using (var db = Utility.OpenDBConnection())
|
|
|
275
|
+ if (m.Msg == Utility.WM_USER)
|
|
275
|
276
|
{
|
|
276
|
|
- Invoke(new Action(() =>
|
|
277
|
|
- {
|
|
278
|
|
- PopulateGrid();
|
|
279
|
|
- }));
|
|
280
|
|
-
|
|
281
|
|
- var lastSyncedBatchId = Utility.GetSavedParameterAsInt(db, "LastSyncedBatchId");
|
|
282
|
|
-
|
|
283
|
|
- bool skipNewBatchCheck = false;
|
|
284
|
|
- while (true)
|
|
285
|
|
- {
|
|
286
|
|
- Log.Debug("Download thread sleeping");
|
|
287
|
|
-
|
|
288
|
|
- bool forceNewBatchCheck;
|
|
289
|
|
- lock (LoadingThreadLock)
|
|
290
|
|
- {
|
|
291
|
|
- while (!RetryDownloads)
|
|
292
|
|
- {
|
|
293
|
|
- Monitor.Wait(LoadingThreadLock);
|
|
294
|
|
- }
|
|
295
|
|
- forceNewBatchCheck = CheckForNewBatches;
|
|
296
|
|
- RetryDownloads = false;
|
|
297
|
|
- CheckForNewBatches = false;
|
|
298
|
|
- }
|
|
299
|
|
-
|
|
300
|
|
- Log.Debug("Download thread woke up");
|
|
301
|
|
-
|
|
302
|
|
- if (Globals.SessionMode == SessionModes.Online)
|
|
303
|
|
- {
|
|
304
|
|
- Log.Debug("Checking for downloads");
|
|
305
|
|
-
|
|
306
|
|
- if (forceNewBatchCheck)
|
|
307
|
|
- {
|
|
308
|
|
- skipNewBatchCheck = false;
|
|
309
|
|
- }
|
|
310
|
|
-
|
|
311
|
|
- // Check for new batches if required.
|
|
312
|
|
- var refreshGrid = false;
|
|
313
|
|
- try
|
|
314
|
|
- {
|
|
315
|
|
- while (!skipNewBatchCheck)
|
|
316
|
|
- {
|
|
317
|
|
- Log.Debug("Querying batches");
|
|
318
|
|
-
|
|
319
|
|
- Page<Batch> BatchPage = new Page<Batch>();
|
|
320
|
|
- if (Utility.RESTRequest(ref BatchPage, String.Format("/api/batches/?minBatchId={0}", lastSyncedBatchId + 1)))
|
|
321
|
|
- {
|
|
322
|
|
- if (BatchPage.Items.Count == 0)
|
|
323
|
|
- {
|
|
324
|
|
- // No more new batches, so stop checking until an order is placed.
|
|
325
|
|
- skipNewBatchCheck = true;
|
|
326
|
|
- }
|
|
327
|
|
- else
|
|
328
|
|
- {
|
|
329
|
|
- foreach (var batch in BatchPage.Items)
|
|
330
|
|
- {
|
|
331
|
|
- refreshGrid = true;
|
|
332
|
|
- Utility.SaveBatch(db, batch);
|
|
333
|
|
- lastSyncedBatchId = batch.Id;
|
|
334
|
|
- if (!Utility.UpdateSavedParameter(db, "LastSyncedBatchId", lastSyncedBatchId))
|
|
335
|
|
- {
|
|
336
|
|
- // Shouldn't happen.
|
|
337
|
|
- throw new Exception("Failed to update LastSyncedBatchId");
|
|
338
|
|
- }
|
|
339
|
|
- }
|
|
340
|
|
- }
|
|
341
|
|
- }
|
|
342
|
|
- else
|
|
343
|
|
- {
|
|
344
|
|
- // Error, so leave skipNewBatchCheck unchanged.
|
|
345
|
|
- break;
|
|
346
|
|
- }
|
|
347
|
|
- }
|
|
348
|
|
- }
|
|
349
|
|
- catch (Exception e)
|
|
350
|
|
- {
|
|
351
|
|
- Log.Error(e, "Error while downloading batch list");
|
|
352
|
|
- }
|
|
353
|
|
-
|
|
354
|
|
- if (refreshGrid)
|
|
355
|
|
- {
|
|
356
|
|
- Invoke(new Action(() =>
|
|
357
|
|
- {
|
|
358
|
|
- PopulateGrid();
|
|
359
|
|
- }));
|
|
360
|
|
- }
|
|
361
|
|
-
|
|
362
|
|
- // Continue downloading incomplete batches if any.
|
|
363
|
|
- try
|
|
364
|
|
- {
|
|
365
|
|
- using (var command = db.CreateCommand("SELECT Id FROM Batch WHERE Downloaded=0 ORDER BY Downloaded, Id"))
|
|
366
|
|
- {
|
|
367
|
|
- using (SQLiteDataReader row = command.ExecuteReader())
|
|
368
|
|
- {
|
|
369
|
|
- while (row.Read())
|
|
370
|
|
- {
|
|
371
|
|
- refreshGrid = false;
|
|
372
|
|
-
|
|
373
|
|
- int batchId = (int)row["Id"];
|
|
374
|
|
- try
|
|
375
|
|
- {
|
|
376
|
|
- Batch batch = new Batch();
|
|
377
|
|
- if (Utility.RESTRequest(ref batch, String.Format("/api/batches/{0}", batchId)))
|
|
378
|
|
- {
|
|
379
|
|
- refreshGrid = true;
|
|
380
|
|
- Utility.SaveBatch(db, batch);
|
|
381
|
|
-
|
|
382
|
|
- if (batch.ReadyForDownload)
|
|
383
|
|
- {
|
|
384
|
|
- long result = (long)db.ExecuteScalar(
|
|
385
|
|
- "SELECT COUNT(*) FROM Voucher WHERE BatchId=@BatchId",
|
|
386
|
|
- new SQLiteParameter("@BatchId", batchId));
|
|
387
|
|
- int voucherCount = (int)result;
|
|
388
|
|
-
|
|
389
|
|
- while (voucherCount < batch.DeliveredQuantity)
|
|
390
|
|
- {
|
|
391
|
|
- Log.Debug("Downloading vouchers for batch {0} ({1} so far)", batchId, voucherCount);
|
|
392
|
|
-
|
|
393
|
|
- int page = voucherCount / voucherPageSize + 1;
|
|
394
|
|
- int offset = voucherCount % voucherPageSize;
|
|
395
|
|
-
|
|
396
|
|
- Page<Voucher> voucherPage = new Page<Voucher>();
|
|
397
|
|
- if (!Utility.RESTRequest(ref voucherPage, String.Format("/api/batches/{0}/vouchers/?page={1}&pageSize={2}", batchId, page, voucherPageSize)))
|
|
398
|
|
- {
|
|
399
|
|
- break;
|
|
400
|
|
- }
|
|
401
|
|
- if (voucherPage.Items.Count == 0)
|
|
402
|
|
- {
|
|
403
|
|
- throw new Exception(String.Format("Too few vouchers were returned for batch {0}", batchId));
|
|
404
|
|
- }
|
|
405
|
|
-
|
|
406
|
|
- Log.Debug("Downloaded vouchers for batch {0} ({1} so far)", batchId, voucherCount);
|
|
407
|
|
-
|
|
408
|
|
- lock (db.WriteLock)
|
|
409
|
|
- {
|
|
410
|
|
- using (var trans = db.BeginTransaction())
|
|
411
|
|
- {
|
|
412
|
|
- using (var insert = db.CreateCommand(
|
|
413
|
|
- "INSERT INTO Voucher (Id, SequenceNumber, ExpiryDate, Serial, EncryptedPIN, BatchId)" +
|
|
414
|
|
- "VALUES (@Id,@SequenceNumber,@ExpiryDate,@Serial,@EncryptedPin,@BatchId)",
|
|
415
|
|
- trans))
|
|
416
|
|
- {
|
|
417
|
|
- for (var i = offset; i < voucherPage.Items.Count; i++)
|
|
418
|
|
- {
|
|
419
|
|
- var voucher = voucherPage.Items[i];
|
|
420
|
|
- if (voucher.SequenceNumber != voucherCount + 1)
|
|
421
|
|
- {
|
|
422
|
|
- throw new Exception(String.Format("Vouchers for batch {0} are not sequential: expecting {1}, got {2}", batchId, voucherCount + 1, voucher.SequenceNumber));
|
|
423
|
|
- }
|
|
424
|
|
- insert.Parameters.Clear();
|
|
425
|
|
- insert.Parameters.AddWithValue("@Id", voucher.Id);
|
|
426
|
|
- insert.Parameters.AddWithValue("@SequenceNumber", voucher.SequenceNumber);
|
|
427
|
|
- insert.Parameters.AddWithValue("@ExpiryDate", voucher.ExpiryDate.Date);
|
|
428
|
|
- insert.Parameters.AddWithValue("@Serial", voucher.Serial);
|
|
429
|
|
- insert.Parameters.AddWithValue("@EncryptedPIN", voucher.EncryptedPIN);
|
|
430
|
|
- insert.Parameters.AddWithValue("@BatchId", batchId);
|
|
431
|
|
- insert.ExecuteNonQuery();
|
|
432
|
|
- voucherCount++;
|
|
433
|
|
- }
|
|
434
|
|
- }
|
|
435
|
|
- trans.Commit();
|
|
436
|
|
- }
|
|
437
|
|
- }
|
|
438
|
|
-
|
|
439
|
|
- Log.Debug("Inserted vouchers for batch {0}", batchId);
|
|
440
|
|
- }
|
|
441
|
|
-
|
|
442
|
|
- if (voucherCount > batch.DeliveredQuantity)
|
|
443
|
|
- {
|
|
444
|
|
- throw new Exception("Batch contains more than the specified number of vouchers");
|
|
445
|
|
- }
|
|
446
|
|
- else
|
|
447
|
|
- {
|
|
448
|
|
- Log.Debug("Batch {0} complete", batchId);
|
|
449
|
|
-
|
|
450
|
|
- db.ExecuteNonQuery(
|
|
451
|
|
- "UPDATE Batch SET Downloaded=1 WHERE Id=@BatchId",
|
|
452
|
|
- new SQLiteParameter("@BatchId", batchId));
|
|
453
|
|
- }
|
|
454
|
|
- }
|
|
455
|
|
- }
|
|
456
|
|
- else if (batch == null)
|
|
457
|
|
- {
|
|
458
|
|
- // Batch will only be null if we got a 404.
|
|
459
|
|
- Log.Debug("Removing deleted batch {0}", batchId);
|
|
460
|
|
-
|
|
461
|
|
- db.ExecuteNonQuery(
|
|
462
|
|
- "DELETE FROM Voucher WHERE BatchId=@BatchId",
|
|
463
|
|
- new SQLiteParameter("@BatchId", batchId));
|
|
464
|
|
-
|
|
465
|
|
- db.ExecuteNonQuery(
|
|
466
|
|
- "DELETE FROM Batch WHERE Id=@BatchId",
|
|
467
|
|
- new SQLiteParameter("@BatchId", batchId));
|
|
468
|
|
-
|
|
469
|
|
- refreshGrid = true;
|
|
470
|
|
- }
|
|
471
|
|
- }
|
|
472
|
|
- catch (Exception e)
|
|
473
|
|
- {
|
|
474
|
|
- Log.Error(e, "Error while downloading vouchers for batch {0}", batchId);
|
|
475
|
|
- }
|
|
476
|
|
-
|
|
477
|
|
- if (refreshGrid)
|
|
478
|
|
- {
|
|
479
|
|
- Invoke(new Action(() =>
|
|
480
|
|
- {
|
|
481
|
|
- PopulateGrid();
|
|
482
|
|
- }));
|
|
483
|
|
- }
|
|
484
|
|
- }
|
|
485
|
|
- }
|
|
486
|
|
- }
|
|
487
|
|
- }
|
|
488
|
|
- catch (Exception e)
|
|
489
|
|
- {
|
|
490
|
|
- Log.Error(e, "Error while downloading vouchers");
|
|
491
|
|
- }
|
|
492
|
|
- }
|
|
493
|
|
- else
|
|
494
|
|
- {
|
|
495
|
|
- Log.Debug("Offline, skipping download check");
|
|
496
|
|
- }
|
|
497
|
|
- }
|
|
|
277
|
+ PopulateGrid();
|
|
|
278
|
+ return;
|
|
498
|
279
|
}
|
|
|
280
|
+ base.WndProc(ref m);
|
|
499
|
281
|
}
|
|
500
|
282
|
|
|
501
|
283
|
public void NewBatchAvailable()
|
|
502
|
284
|
{
|
|
503
|
|
- lock (LoadingThreadLock)
|
|
504
|
|
- {
|
|
505
|
|
- RetryDownloads = true;
|
|
506
|
|
- CheckForNewBatches = true;
|
|
507
|
|
- Monitor.Pulse(LoadingThreadLock);
|
|
508
|
|
- }
|
|
|
285
|
+ BatchDownloader.CheckForNewBatches();
|
|
509
|
286
|
}
|
|
510
|
287
|
|
|
511
|
288
|
private void btnOrder_Click(object sender, EventArgs e)
|
|
|
@@ -588,11 +365,12 @@ namespace BulkPrinting
|
|
588
|
365
|
return;
|
|
589
|
366
|
}
|
|
590
|
367
|
|
|
591
|
|
- if ((LoadingThread != null) && (LoadingThread.IsAlive))
|
|
|
368
|
+ if (BatchDownloader != null)
|
|
592
|
369
|
{
|
|
593
|
|
- LoadingThread.Abort();
|
|
594
|
|
- LoadingThread.Join();
|
|
|
370
|
+ BatchDownloader.Cancel();
|
|
|
371
|
+ BatchDownloader.Join();
|
|
595
|
372
|
}
|
|
|
373
|
+
|
|
596
|
374
|
Utility.Logout();
|
|
597
|
375
|
}
|
|
598
|
376
|
|
|
|
@@ -774,14 +552,5 @@ namespace BulkPrinting
|
|
774
|
552
|
e.Graphics.DrawImage(b.BackgroundImage, b.ClientRectangle);
|
|
775
|
553
|
}
|
|
776
|
554
|
}
|
|
777
|
|
-
|
|
778
|
|
- private void ReSyncTimer_Tick(object sender, EventArgs e)
|
|
779
|
|
- {
|
|
780
|
|
- lock (LoadingThreadLock)
|
|
781
|
|
- {
|
|
782
|
|
- RetryDownloads = true;
|
|
783
|
|
- Monitor.Pulse(LoadingThreadLock);
|
|
784
|
|
- }
|
|
785
|
|
- }
|
|
786
|
555
|
}
|
|
787
|
556
|
}
|