Преглед изворни кода

Add log summarisation functionality so that we can avoid so many queries on the entire Logs table.
Speed up PopulateGrid() dramatically by making use of summarised log information.
Add SIMULATE_HDD_SERIAL conditional compilation blocks.
Fix 'Retry' log field not being uploaded to server.
Better HTTP error handling.

Brett Credo пре 8 година
родитељ
комит
41ff233365

+ 22 - 9
BulkPrinting/BulkPrinting/BatchDownloader.cs

@@ -12,12 +12,14 @@ namespace BulkPrinting
12 12
 
13 13
         private DBHelper _db;
14 14
         private Action _refreshAction;
15
+        private Action _downloadLogSummarisationAction;
15 16
         private bool _checkForNewBatches;
16 17
 
17
-        public BatchDownloader(DBHelper db, Action refreshAction)
18
+        public BatchDownloader(DBHelper db, Action refreshAction, Action downloadedLogSummarisationAction)
18 19
         {
19 20
             _db = db;
20 21
             _refreshAction = refreshAction;
22
+            _downloadLogSummarisationAction = downloadedLogSummarisationAction;
21 23
         }
22 24
 
23 25
         public void CheckForNewBatches()
@@ -39,6 +41,8 @@ namespace BulkPrinting
39 41
             {
40 42
                 if (Globals.SessionMode == SessionModes.Online)
41 43
                 {
44
+                    var gotErrors = false;
45
+
42 46
                     Log.Debug("Checking for downloads");
43 47
 
44 48
                     // Check for new batches if required.
@@ -76,6 +80,7 @@ namespace BulkPrinting
76 80
                             else
77 81
                             {
78 82
                                 // Error, so leave skipNewBatchCheck unchanged.
83
+                                gotErrors = true;
79 84
                                 break;
80 85
                             }
81 86
 
@@ -87,10 +92,11 @@ namespace BulkPrinting
87 92
                     }
88 93
                     catch (Exception e)
89 94
                     {
95
+                        gotErrors = true;
90 96
                         Log.Error(e, "Error while downloading batch list");
91 97
                     }
92 98
 
93
-                    // TODO: throttle or detect changes better
99
+                    // TODO: throttle
94 100
                     if (!isCancelled && doRefresh)
95 101
                     {
96 102
                         doRefresh = false;
@@ -218,20 +224,23 @@ namespace BulkPrinting
218 224
 
219 225
                                             doRefresh = true;
220 226
                                         }
221
-                                        // else retry later
227
+                                        else
228
+                                        {
229
+                                            gotErrors = true;
230
+                                        }
222 231
                                     }
223 232
                                     catch (Exception e)
224 233
                                     {
234
+                                        gotErrors = true;
225 235
                                         Log.Error(e, "Error while downloading vouchers for batch {0}", batchId);
226 236
                                     }
227 237
 
228
-                                    /* TODO: throttle or detect changes better
238
+                                    // TODO: throttle
229 239
                                     if (!isCancelled && doRefresh)
230 240
                                     {
231 241
                                         doRefresh = false;
232 242
                                         _refreshAction();
233 243
                                     }
234
-                                    */
235 244
 
236 245
                                     lock(_lock)
237 246
                                     {
@@ -243,14 +252,18 @@ namespace BulkPrinting
243 252
                     }
244 253
                     catch (Exception e)
245 254
                     {
255
+                        gotErrors = true;
246 256
                         Log.Error(e, "Error while downloading vouchers");
247 257
                     }
248 258
 
249
-                    // TODO: throttle or detect changes better
250
-                    if (!isCancelled && doRefresh)
259
+                    if (! isCancelled && ! gotErrors)
251 260
                     {
252
-                        doRefresh = false;
253
-                        _refreshAction();
261
+                        Log.Debug("Up to date");
262
+
263
+                        // If we weren't cancelled and didn't get any errors then we should be synchronised with the server,
264
+                        // so it is now safe to summarise any downloaded logs as the corresponding batches and vouchers should
265
+                        // all be present.
266
+                        _downloadLogSummarisationAction();
254 267
                     }
255 268
                 }
256 269
                 else

+ 69 - 53
BulkPrinting/BulkPrinting/BatchForm.cs

@@ -12,6 +12,7 @@ namespace BulkPrinting
12 12
     public partial class BatchForm : ObservedForm
13 13
     {
14 14
         private BatchDownloader BatchDownloader;
15
+        private DownloadedLogSummariser DownloadedLogSummariser;
15 16
 
16 17
         public BatchForm()
17 18
         {
@@ -27,39 +28,6 @@ namespace BulkPrinting
27 28
 
28 29
         public void PopulateGrid()
29 30
         {
30
-            Dictionary<int, BatchEvent> BatchEvents = new Dictionary<int, BatchEvent>();
31
-            string Sql = "Select COUNT(v.BatchId) AS Total, v.BatchId,l.EventType,l.Retry FROM Voucher v LEFT JOIN Logs l on v.Id = l.VoucherId WHERE l.EventType in (@printevent,@exportevent) GROUP BY v.BatchId,l.EventType,l.Retry ORDER BY BatchId";
32
-            using (var Command = Globals.DB.CreateCommand(Sql,
33
-                new SQLiteParameter("@printevent", VendorEvent.VendorEventType.PrintVoucher),
34
-                new SQLiteParameter("@exportevent", VendorEvent.VendorEventType.ExportVoucher)))
35
-            {
36
-                using (SQLiteDataReader read = Command.ExecuteReader())
37
-                {
38
-                    while (read.Read())
39
-                    {
40
-                        int BatchId = int.Parse(read["BatchId"].ToString());
41
-                        if (!BatchEvents.ContainsKey(BatchId))
42
-                        {
43
-                            BatchEvents.Add(BatchId, new BatchEvent());
44
-                        }
45
-                        if (read["EventType"].ToString() == "6")
46
-                        {
47
-                            if (read["Retry"].ToString() == "True")
48
-                            {
49
-                                BatchEvents[BatchId].ReprintCount += int.Parse(read["Total"].ToString());
50
-                            }
51
-                            else
52
-                            {
53
-                                BatchEvents[BatchId].PrintCount += int.Parse(read["Total"].ToString());
54
-                            }
55
-                        }
56
-                        else if (read["EventType"].ToString() == "8")
57
-                        {
58
-                            BatchEvents[BatchId].ExportCount += int.Parse(read["Total"].ToString());
59
-                        }
60
-                    }
61
-                }
62
-            }
63 31
             bool CanOrder = Utility.CheckUserAccess(Utility.UserPermissions.BulkOrder);
64 32
             bool CanExport = Utility.CheckUserAccess(Utility.UserPermissions.BulkExport);
65 33
             dgvBatches.Rows.Clear();
@@ -79,12 +47,11 @@ namespace BulkPrinting
79 47
             dgvBatches.Columns.Add("Downloaded", "Downloaded");
80 48
             dgvBatches.Columns.Add("PrintedQuantity", "Quantity Printed");
81 49
             if (CanExport) dgvBatches.Columns.Add("Exported", "Exported Batch");
82
-            Sql = "Select Id,OrderDate,OrderReference,InternalReference,NetworkName,ProductDescription,VoucherType,FaceValue,RequestedQuantity,DeliveredQuantity,ReadyForDownload,Downloaded From Batch WHERE OrderDate BETWEEN @startdate AND @enddate";
50
+            var Sql = "Select Id,OrderDate,OrderReference,InternalReference,NetworkName,ProductDescription,VoucherType,FaceValue,RequestedQuantity,DeliveredQuantity,ReadyForDownload,Downloaded,PrintCount,ReprintCount,ExportCount,ReExportCount From Batch WHERE OrderDate BETWEEN @startdate AND @enddate";
83 51
 
84 52
             //Sql = "Select Id,OrderDate,OrderReference,InternalReference,NetworkName,ProductDescription,VoucherType,FaceValue,RequestedQuantity,DeliveredQuantity,ReadyForDownload,Downloaded,(SELECT COUNT(*) FROM Voucher WHERE Voucher.BatchId=Batch.Id) AS VoucherCount From Batch WHERE OrderDate BETWEEN @startdate AND @enddate";
85 53
             //dgvBatches.Columns.Add("VoucherCount", "Voucher Count");
86 54
 
87
-
88 55
             CultureInfo IVC = CultureInfo.InvariantCulture;
89 56
             using (var Command = Globals.DB.CreateCommand(Sql,
90 57
                 new SQLiteParameter("@startdate", dtpFilterStartDate.Value.Date.ToString("yyyy-MM-dd 00:00:00", IVC)),
@@ -96,44 +63,45 @@ namespace BulkPrinting
96 63
                     while (read.Read())
97 64
                     {
98 65
                         int BatchId = int.Parse(read["Id"].ToString());
99
-                        if (!BatchEvents.ContainsKey(BatchId))
100
-                        { //For batches with no events
101
-                            BatchEvents.Add(BatchId, new BatchEvent());
102
-                        }
103 66
                         int DeliveredQuantity = int.Parse(read["DeliveredQuantity"].ToString());
67
+                        int PrintCount = (int)read["PrintCount"];
68
+                        int ReprintCount = (int)read["ReprintCount"];
69
+                        int ExportCount = (int)read["ExportCount"];
70
+                        int ReExportCount = (int)read["ReExportCount"];
71
+
104 72
 
105 73
                         //Filters to exclude rows that don't match
106 74
                         if (rdoFilterPrinted.Checked)
107 75
                         {
108
-                            if (BatchEvents[BatchId].PrintCount == 0)
76
+                            if (PrintCount == 0)
109 77
                             {
110 78
                                 continue;
111 79
                             }
112 80
                         }
113 81
                         if (rdoFilterUnprinted.Checked)
114 82
                         {
115
-                            if (BatchEvents[BatchId].PrintCount >= DeliveredQuantity)
83
+                            if (PrintCount >= DeliveredQuantity)
116 84
                             {
117 85
                                 continue;
118 86
                             }
119 87
                         }
120 88
                         if (rdoFilterReprinted.Checked)
121 89
                         {
122
-                            if (BatchEvents[BatchId].ReprintCount == 0)
90
+                            if (ReprintCount == 0)
123 91
                             {
124 92
                                 continue;
125 93
                             }
126 94
                         }
127 95
                         if (rdoFilterExported.Checked)
128 96
                         {
129
-                            if (BatchEvents[BatchId].ExportCount == 0)
97
+                            if (ExportCount == 0)
130 98
                             {
131 99
                                 continue;
132 100
                             }
133 101
                         }
134 102
                         else
135 103
                         {
136
-                            if (BatchEvents[BatchId].ExportCount > 0)
104
+                            if (ExportCount > 0)
137 105
                             {
138 106
                                 continue;
139 107
                             }
@@ -163,10 +131,10 @@ namespace BulkPrinting
163 131
                             BatchRow.Add(((bool)read["ReadyForDownload"] == true ? "Yes" : "No"));
164 132
 
165 133
                         BatchRow.Add((bool)read["Downloaded"] ? "Yes" : "No");
166
-                        BatchRow.Add((object)BatchEvents[BatchId].PrintCount);
134
+                        BatchRow.Add(PrintCount);
167 135
 
168 136
                         if (CanExport)
169
-                            BatchRow.Add((BatchEvents[BatchId].ExportCount > 0 ? "Exported" : ""));
137
+                            BatchRow.Add(ExportCount > 0 ? "Exported" : "");
170 138
 
171 139
                         //BatchRow.Add(read["VoucherCount"]);
172 140
 
@@ -254,13 +222,22 @@ namespace BulkPrinting
254 222
             Globals.OpenBatches = new List<int>();
255 223
 
256 224
             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
-            });
225
+            BatchDownloader = new BatchDownloader(
226
+                Globals.DB,
227
+                () =>
228
+                {
229
+                    // Refresh grid
230
+
231
+                    // DO NOT use Invoke as this can cause a deadlock if we are attempting to join the BatchDownloader after a cancel.
232
+                    // Also, use PostMessage rather than SendMessage as PostMessage doesn't wait for the message to be processed and
233
+                    // therefore won't block.
234
+                    Utility.PostMessage(hwnd, Utility.WM_USER, IntPtr.Zero, IntPtr.Zero);
235
+                },
236
+                () =>
237
+                {
238
+                    // Trigger download log summarisation
239
+                    Utility.PostMessage(hwnd, Utility.WM_USER + 1, IntPtr.Zero, IntPtr.Zero);
240
+                });
264 241
             BatchDownloader.Start();
265 242
 
266 243
             Utility.InitialiseUserLimits(Globals.DB);
@@ -277,9 +254,36 @@ namespace BulkPrinting
277 254
                 PopulateGrid();
278 255
                 return;
279 256
             }
257
+            else if (m.Msg == Utility.WM_USER + 1)
258
+            {
259
+                TriggerDownloadedLogSummarisation();
260
+                return;
261
+            }
280 262
             base.WndProc(ref m);
281 263
         }
282 264
 
265
+        private void TriggerDownloadedLogSummarisation()
266
+        {
267
+            if (DownloadedLogSummariser == null)
268
+            {
269
+                var hwnd = new System.Runtime.InteropServices.HandleRef(this, Handle);
270
+
271
+                DownloadedLogSummariser = new DownloadedLogSummariser(
272
+                    Globals.DB,
273
+                    () =>
274
+                    {
275
+                        // Refresh grid
276
+
277
+                        // DO NOT use Invoke as this can cause a deadlock if we are attempting to join the BatchDownloader after a cancel.
278
+                        // Also, use PostMessage rather than SendMessage as PostMessage doesn't wait for the message to be processed and
279
+                        // therefore won't block.
280
+                        Utility.PostMessage(hwnd, Utility.WM_USER, IntPtr.Zero, IntPtr.Zero);
281
+                    });
282
+
283
+                DownloadedLogSummariser.Start();
284
+            }
285
+        }
286
+
283 287
         public void NewBatchAvailable()
284 288
         {
285 289
             BatchDownloader.CheckForNewBatches();
@@ -368,8 +372,20 @@ namespace BulkPrinting
368 372
             if (BatchDownloader != null)
369 373
             {
370 374
                 BatchDownloader.Cancel();
375
+            }
376
+            if (DownloadedLogSummariser != null)
377
+            {
378
+                DownloadedLogSummariser.Cancel();
379
+            }
380
+
381
+            if (BatchDownloader != null)
382
+            {
371 383
                 BatchDownloader.Join();
372 384
             }
385
+            if (DownloadedLogSummariser != null)
386
+            {
387
+                DownloadedLogSummariser.Join();
388
+            }
373 389
 
374 390
             Utility.Logout();
375 391
         }

+ 2 - 1
BulkPrinting/BulkPrinting/BulkPrinting.csproj

@@ -79,7 +79,7 @@
79 79
   <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
80 80
     <DebugSymbols>true</DebugSymbols>
81 81
     <OutputPath>bin\x86\Debug\</OutputPath>
82
-    <DefineConstants>DEBUG;TRACE</DefineConstants>
82
+    <DefineConstants>TRACE;DEBUG</DefineConstants>
83 83
     <DebugType>full</DebugType>
84 84
     <PlatformTarget>x86</PlatformTarget>
85 85
     <ErrorReport>prompt</ErrorReport>
@@ -157,6 +157,7 @@
157 157
     <Compile Include="BatchDownloader.cs" />
158 158
     <Compile Include="Configuration.cs" />
159 159
     <Compile Include="DBHelper.cs" />
160
+    <Compile Include="DownloadedLogSummariser.cs" />
160 161
     <Compile Include="ExportForm.cs">
161 162
       <SubType>Form</SubType>
162 163
     </Compile>

+ 114 - 0
BulkPrinting/BulkPrinting/DownloadedLogSummariser.cs

@@ -0,0 +1,114 @@
1
+using System;
2
+using System.Threading;
3
+
4
+namespace BulkPrinting
5
+{
6
+    public class DownloadedLogSummariser : ThreadHelper
7
+    {
8
+        private const int _retryInterval = 30000;
9
+
10
+        private DBHelper _db;
11
+        private Action _refreshAction;
12
+
13
+        public DownloadedLogSummariser(DBHelper db, Action refreshAction)
14
+        {
15
+            _db = db;
16
+            _refreshAction = refreshAction;
17
+        }
18
+
19
+        protected override void Run()
20
+        {
21
+            bool isCancelled = false;
22
+            while (!isCancelled)
23
+            {
24
+                bool doRefresh = false;
25
+
26
+                while (!isCancelled)
27
+                {
28
+                    lock (_db.WriteLock)
29
+                    {
30
+                        using (var trans = _db.BeginTransaction())
31
+                        {
32
+                            using (var selectCommand = _db.CreateCommand(
33
+                                "SELECT l.*, v.BatchId FROM Logs l " +
34
+                                    "LEFT JOIN Voucher v ON l.VoucherId=v.Id " +
35
+                                "WHERE Summarised=0 " +
36
+                                "LIMIT 1000",
37
+                                trans))
38
+                            using (var updateLogsCommand = _db.CreateCommand(
39
+                                "UPDATE Logs SET Summarised=1 WHERE Id=@Id"))
40
+                            using (var updateVoucherCommand = Utility.CreateVoucherEventCountUpdateCommand(_db, trans))
41
+                            using (var updateBatchCommand = Utility.CreateBatchEventCountUpdateCommand(_db, trans))
42
+                            {
43
+                                updateLogsCommand.Parameters.AddWithValue("@Id", null);
44
+
45
+                                using (var reader = selectCommand.ExecuteReader())
46
+                                {
47
+                                    var rowCount = 0;
48
+                                    while (reader.Read())
49
+                                    {
50
+                                        rowCount++;
51
+                                        doRefresh = true;
52
+
53
+                                        int printCountDelta, reprintCountDelta, exportCountDelta, reExportCountDelta;
54
+                                        if ((reader["VoucherId"] != null) &&
55
+                                            Utility.CalculateEventCountDeltas(
56
+                                            (VendorEvent.VendorEventType)int.Parse((string)reader["EventType"]),
57
+                                            (bool)reader["Retry"],
58
+                                            out printCountDelta,
59
+                                            out reprintCountDelta,
60
+                                            out exportCountDelta,
61
+                                            out reExportCountDelta))
62
+                                        {
63
+                                            Utility.ExecuteEventCountUpdateQuery(updateVoucherCommand, (int)reader["VoucherId"],
64
+                                                printCountDelta, reprintCountDelta, exportCountDelta, reExportCountDelta);
65
+
66
+                                            if (reader["BatchId"] != null)
67
+                                            {
68
+                                                Utility.ExecuteEventCountUpdateQuery(updateBatchCommand, (int)reader["BatchId"],
69
+                                                    printCountDelta, reprintCountDelta, exportCountDelta, reExportCountDelta);
70
+                                            }
71
+                                        }
72
+
73
+                                        var logId = (long)reader["Id"];
74
+
75
+                                        updateLogsCommand.Parameters["@Id"].Value = (int)logId;
76
+                                        updateLogsCommand.ExecuteNonQuery();
77
+                                    }
78
+
79
+                                    if (rowCount == 0)
80
+                                    {
81
+                                        break;
82
+                                    }
83
+                                }
84
+                            }
85
+
86
+                            trans.Commit();
87
+                        }
88
+                    }
89
+
90
+                    lock (_lock)
91
+                    {
92
+                        isCancelled = _cancelled;
93
+                    }
94
+                }
95
+
96
+                if (! isCancelled && doRefresh)
97
+                {
98
+                    doRefresh = false;
99
+                    _refreshAction();
100
+                }
101
+
102
+                // We sleep and try again on the offchance that we got triggered too early.
103
+                lock (_lock)
104
+                {
105
+                    if (!_cancelled)
106
+                    {
107
+                        Monitor.Wait(_lock, _retryInterval);
108
+                    }
109
+                    isCancelled = _cancelled;
110
+                }
111
+            }
112
+        }
113
+    }
114
+}

+ 1 - 0
BulkPrinting/BulkPrinting/ExportForm.cs

@@ -198,6 +198,7 @@ namespace BulkPrinting
198 198
                                         var ExportEvent = new EventLog();
199 199
                                         ExportEvent.EventType = VendorEvent.VendorEventType.ExportVoucher;
200 200
                                         ExportEvent.VoucherId = int.Parse(read["Id"].ToString());
201
+                                        ExportEvent.BatchId = ExportBatch.Id;
201 202
                                         ExportEvent.Retry = (read["Exports"].ToString() != "0");
202 203
                                         LogEvents.Add(ExportEvent);
203 204
                                     }

+ 2 - 2
BulkPrinting/BulkPrinting/LogDownloader.cs

@@ -30,8 +30,8 @@ namespace BulkPrinting
30 30
                         using (var trans = _db.BeginTransaction())
31 31
                         {
32 32
                             using (var insertCommand = _db.CreateCommand(
33
-                                "INSERT INTO Logs (Id,  UserId,  VoucherId,  EventDate,  EventType,  Retry) " +
34
-                                          "VALUES (@id, @userid, @voucherid, @eventdate, @eventtype, @retry)",
33
+                                "INSERT INTO Logs (Id,  UserId,  VoucherId,  EventDate,  EventType,  Retry,  Summarised) " +
34
+                                          "VALUES (@id, @userid, @voucherid, @eventdate, @eventtype, @retry, 0)",
35 35
                                 trans))
36 36
                             using (var updateCommand = _db.CreateCommand(
37 37
                                 "UPDATE Parameters SET Value=@Value WHERE Key='SyncBackwardsFromLogId'", trans))

+ 2 - 1
BulkPrinting/BulkPrinting/LogUploader.cs

@@ -38,7 +38,7 @@ namespace BulkPrinting
38 38
                     List<RemoteVendorEvent> eventList = new List<RemoteVendorEvent>();
39 39
                     int counter = 0;
40 40
                     using (var Command = _db.CreateCommand(
41
-                        "Select Id,UserId,VoucherId,EventDate,EventType From Logs WHERE Id > @id",
41
+                        "Select Id,UserId,VoucherId,EventDate,EventType,Retry From Logs WHERE Id > @id",
42 42
                         new SQLiteParameter("@id", lastSyncedLogId)))
43 43
                     {
44 44
                         using (SQLiteDataReader read = Command.ExecuteReader())
@@ -60,6 +60,7 @@ namespace BulkPrinting
60 60
                                 nextEvent.EventDate = read.GetDateTime(3);
61 61
                                 nextEvent.EventType = (VendorEvent.VendorEventType)Enum.Parse(typeof(VendorEvent.VendorEventType), read.GetValue(4).ToString());
62 62
                                 nextEvent.VendorId = Globals.SessionData.Credentials.Payload.Vendor.id;
63
+                                nextEvent.Retry = read.GetBoolean(5);
63 64
                                 eventList.Add(nextEvent);
64 65
 
65 66
                                 if (counter == _uploadPageSize)

+ 71 - 8
BulkPrinting/BulkPrinting/Migrations.cs

@@ -1,9 +1,12 @@
1 1
 using System;
2
+using System.Data.SQLite;
2 3
 
3 4
 namespace BulkPrinting
4 5
 {
5 6
     class Migrations
6 7
     {
8
+        private delegate void PostMigrateDelegate(DBHelper db, SQLiteTransaction trans);
9
+
7 10
         public static void CheckMigrations(DBHelper db)
8 11
         {
9 12
             var result = db.ExecuteScalar("SELECT Value FROM Parameters WHERE Key = 'Migration'");
@@ -17,7 +20,7 @@ namespace BulkPrinting
17 20
                 throw new Exception(String.Format("Invalid migration value: {0}", result));
18 21
             }
19 22
 
20
-            if (startingMigration > 3)
23
+            if (startingMigration > 4)
21 24
             {
22 25
                 throw new Exception("Database is for a newer version of this application. Please upgrade and try again.");
23 26
             }
@@ -33,12 +36,16 @@ namespace BulkPrinting
33 36
             {
34 37
                 Migration3(db);
35 38
             }
39
+            if (startingMigration < 4)
40
+            {
41
+                Migration4(db);
42
+            }
36 43
             //Add further migration executions here - Migration = '1', '2', '3' etc
37 44
 
38 45
             return;
39 46
         }
40 47
 
41
-        private static bool ApplyMigrationQueries(DBHelper db, params string[] queries)
48
+        private static bool ApplyMigrationQueries(DBHelper db, PostMigrateDelegate postMigrate, params string[] queries)
42 49
         {
43 50
             lock (db.WriteLock)
44 51
             {
@@ -51,6 +58,7 @@ namespace BulkPrinting
51 58
                             command.ExecuteNonQuery();
52 59
                         }
53 60
                     }
61
+                    postMigrate?.Invoke(db, trans);
54 62
                     trans.Commit();
55 63
                 }
56 64
             }
@@ -60,7 +68,7 @@ namespace BulkPrinting
60 68
         public static bool InitialVersion(DBHelper db)
61 69
         {
62 70
             return ApplyMigrationQueries(
63
-                db,
71
+                db, null,
64 72
                 "CREATE TABLE Batch (" +
65 73
                     "Id INT," +
66 74
                     "OrderDate DATETIME," +
@@ -129,7 +137,7 @@ namespace BulkPrinting
129 137
         public static bool Migration1(DBHelper db)
130 138
         {
131 139
             return ApplyMigrationQueries(
132
-                db,
140
+                db, null,
133 141
                 "ALTER TABLE Orders ADD COLUMN InternalReference VARCHAR(32)",
134 142
                 "ALTER TABLE Batch ADD COLUMN InternalReference VARCHAR(32)",
135 143
 
@@ -141,7 +149,7 @@ namespace BulkPrinting
141 149
         public static bool Migration2(DBHelper db)
142 150
         {
143 151
             return ApplyMigrationQueries(
144
-                db,
152
+                db, null,
145 153
                 "ALTER TABLE Batch ADD COLUMN Downloaded BOOLEAN",
146 154
                 "CREATE UNIQUE INDEX Batch_Id ON Batch(Id)",
147 155
                 "CREATE INDEX Batch_OrderDate ON Batch(OrderDate)",
@@ -176,14 +184,69 @@ namespace BulkPrinting
176 184
         public static bool Migration3(DBHelper db)
177 185
         {
178 186
             return ApplyMigrationQueries(
179
-                db,
187
+                db, null,
180 188
 
181
-                // Forgot to update the migration value initially, hence the 'NOT EXISTS'
182
-                "CREATE INDEX IF NOT EXISTS Logs_VoucherId_EventType_Id ON Logs(VoucherId, EventType, Id)",
189
+                "CREATE INDEX Logs_VoucherId_EventType_Id ON Logs(VoucherId, EventType, Id)",
183 190
 
184 191
                 // IMPORTANT
185 192
                 "UPDATE Parameters SET Value='3' WHERE Key='Migration'"
186 193
            );
187 194
         }
195
+
196
+        public static bool Migration4(DBHelper db)
197
+        {
198
+            return ApplyMigrationQueries(
199
+                db,
200
+                (db2, trans) =>
201
+                {
202
+                    using (var selectCommand = db2.CreateCommand(
203
+                        "SELECT l.*, v.BatchId FROM Logs l " +
204
+                            "INNER JOIN Voucher v ON l.VoucherId=v.Id",
205
+                        trans))
206
+                    using (var updateVoucherCommand = Utility.CreateVoucherEventCountUpdateCommand(db2, trans))
207
+                    using (var updateBatchCommand = Utility.CreateBatchEventCountUpdateCommand(db2, trans))
208
+                    {
209
+                        using (var reader = selectCommand.ExecuteReader())
210
+                        {
211
+                            while (reader.Read())
212
+                            {
213
+                                int printCountDelta, reprintCountDelta, exportCountDelta, reExportCountDelta;
214
+                                if (Utility.CalculateEventCountDeltas(
215
+                                    (VendorEvent.VendorEventType)int.Parse((string)reader["EventType"]),
216
+                                    (bool)reader["Retry"],
217
+                                    out printCountDelta,
218
+                                    out reprintCountDelta,
219
+                                    out exportCountDelta,
220
+                                    out reExportCountDelta))
221
+                                {
222
+                                    Utility.ExecuteEventCountUpdateQuery(updateVoucherCommand, (int)reader["VoucherId"],
223
+                                        printCountDelta, reprintCountDelta, exportCountDelta, reExportCountDelta);
224
+
225
+                                    Utility.ExecuteEventCountUpdateQuery(updateBatchCommand, (int)reader["BatchId"],
226
+                                        printCountDelta, reprintCountDelta, exportCountDelta, reExportCountDelta);
227
+                                }
228
+                            }
229
+                        }
230
+                    }
231
+                },
232
+
233
+                "ALTER TABLE Logs ADD COLUMN Summarised BOOLEAN DEFAULT 0",
234
+                "CREATE INDEX Logs_Summarised_Id ON Logs(Summarised, Id)",
235
+                "UPDATE Logs SET Summarised=1",
236
+
237
+                "ALTER TABLE Batch ADD COLUMN PrintCount INT DEFAULT 0",
238
+                "ALTER TABLE Batch ADD COLUMN ReprintCount INT DEFAULT 0",
239
+                "ALTER TABLE Batch ADD COLUMN ExportCount INT DEFAULT 0",
240
+                "ALTER TABLE Batch ADD COLUMN ReExportCount INT DEFAULT 0",
241
+
242
+                "ALTER TABLE Voucher ADD COLUMN PrintCount INT DEFAULT 0",
243
+                "ALTER TABLE Voucher ADD COLUMN ReprintCount INT DEFAULT 0",
244
+                "ALTER TABLE Voucher ADD COLUMN ExportCount INT DEFAULT 0",
245
+                "ALTER TABLE Voucher ADD COLUMN ReExportCount INT DEFAULT 0",
246
+
247
+                // IMPORTANT
248
+                "UPDATE Parameters SET Value='4' WHERE Key='Migration'"
249
+           );
250
+        }
188 251
     }
189 252
 }

+ 1 - 0
BulkPrinting/BulkPrinting/Models.cs

@@ -501,6 +501,7 @@ namespace BulkPrinting
501 501
     public class EventLog {
502 502
         public VendorEvent.VendorEventType EventType { get; set; }
503 503
         public int? VoucherId { get; set; }
504
+        public int? BatchId { get; set; }
504 505
         public bool Retry { get; set; }
505 506
     }
506 507
 

+ 4 - 0
BulkPrinting/BulkPrinting/Program.cs

@@ -30,7 +30,11 @@ namespace BulkPrinting
30 30
 
31 31
             AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
32 32
 
33
+#if SIMULATE_HDD_SERIAL
34
+            Globals.HDDSerialNumber = "9VMTDFGX";
35
+#else
33 36
             Globals.HDDSerialNumber = Utility.GetHDDSerial();
37
+#endif
34 38
 
35 39
             Application.EnableVisualStyles();
36 40
             Application.SetCompatibleTextRenderingDefault(false);

+ 154 - 21
BulkPrinting/BulkPrinting/Utility.cs

@@ -103,13 +103,21 @@ namespace BulkPrinting
103 103
                 if (ex.Response != null)
104 104
                 {
105 105
                     var response = ex.Response;
106
-                    var stream = response.GetResponseStream();
107
-                    var reader = new StreamReader(stream);
108
-                    var message = reader.ReadToEnd();
109
-                    MaxException MaxError = JsonConvert.DeserializeObject<MaxException>(message);
110
-                    if (MaxError.Code != null)
106
+                    HttpStatusCode? status = (response as HttpWebResponse)?.StatusCode;
107
+                    if (status.HasValue && (status.Value == HttpStatusCode.InternalServerError))
111 108
                     {
112
-                        MessageBox.Show("Error " + MaxError.Code.ToString() + ": " + MaxError.Error, "Login Failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
109
+                        var stream = response.GetResponseStream();
110
+                        var reader = new StreamReader(stream);
111
+                        var message = reader.ReadToEnd();
112
+                        MaxException MaxError = JsonConvert.DeserializeObject<MaxException>(message);
113
+                        if (MaxError.Code != null)
114
+                        {
115
+                            MessageBox.Show("Error " + MaxError.Code.ToString() + ": " + MaxError.Error, "Login Failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
116
+                        }
117
+                        else
118
+                        {
119
+                            MessageBox.Show("Login Failed. Please try again.", "Login Failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
120
+                        }
113 121
                     }
114 122
                     else
115 123
                     {
@@ -172,7 +180,7 @@ namespace BulkPrinting
172 180
                     {
173 181
                         Result = default(R);
174 182
                     }
175
-                    else
183
+                    else if (status.HasValue && (status == HttpStatusCode.InternalServerError))
176 184
                     {
177 185
                         var stream = response.GetResponseStream();
178 186
                         var reader = new StreamReader(stream);
@@ -182,6 +190,14 @@ namespace BulkPrinting
182 190
                         {
183 191
                             MessageBox.Show("Error " + MaxError.Code.ToString() + ": " + MaxError.Error, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
184 192
                         }
193
+                        else
194
+                        {
195
+                            MessageBox.Show(MaxError.Error, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
196
+                        }
197
+                    }
198
+                    else
199
+                    {
200
+                        MessageBox.Show("The server encountered an unexpected error.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
185 201
                     }
186 202
                 }
187 203
                 return false;
@@ -472,6 +488,7 @@ namespace BulkPrinting
472 488
                                 var ExportEvent = new EventLog();
473 489
                                 ExportEvent.EventType = VendorEvent.VendorEventType.PrintVoucher;
474 490
                                 ExportEvent.VoucherId = PrintedVoucher.VoucherId;
491
+                                ExportEvent.BatchId = BatchId;
475 492
                                 ExportEvent.Retry = IsReprint;
476 493
                                 LogEvents.Add(ExportEvent);
477 494
                             }
@@ -623,15 +640,18 @@ namespace BulkPrinting
623 640
 
624 641
         public static void TriggerLogDownload()
625 642
         {
643
+#if ! SIMULATE_HDD_SERIAL
626 644
             if (Globals.LogDownloader == null)
627 645
             {
628 646
                 Globals.LogDownloader = new LogDownloader(Globals.DB);
629 647
                 Globals.LogDownloader.Start();
630 648
             }
649
+#endif
631 650
         }
632 651
 
633 652
         public static void TriggerLogUpload()
634 653
         {
654
+#if ! SIMULATE_HDD_SERIAL
635 655
             if (Globals.LogUploader == null)
636 656
             {
637 657
                 Globals.LogUploader = new LogUploader(Globals.DB);
@@ -641,27 +661,138 @@ namespace BulkPrinting
641 661
             {
642 662
                 Globals.LogUploader.TriggerUpload();
643 663
             }
664
+#endif
665
+        }
666
+
667
+        public static void CreateEventCountUpdateQueryParameters(SQLiteCommand command)
668
+        {
669
+            command.Parameters.AddWithValue("@Id", null);
670
+            command.Parameters.AddWithValue("@PrintCountDelta", null);
671
+            command.Parameters.AddWithValue("@ReprintCountDelta", null);
672
+            command.Parameters.AddWithValue("@ExportCountDelta", null);
673
+            command.Parameters.AddWithValue("@ReExportCountDelta", null);
674
+        }
675
+
676
+        public static void ExecuteEventCountUpdateQuery(SQLiteCommand command, int id,
677
+            int printCountDelta, int reprintCountDelta, int exportCountDelta, int reExportCountDelta)
678
+        {
679
+            command.Parameters["@Id"].Value = id;
680
+            command.Parameters["@PrintCountDelta"].Value = printCountDelta;
681
+            command.Parameters["@ReprintCountDelta"].Value = reprintCountDelta;
682
+            command.Parameters["@ExportCountDelta"].Value = exportCountDelta;
683
+            command.Parameters["@ReExportCountDelta"].Value = reExportCountDelta;
684
+            command.ExecuteNonQuery();
685
+        }
686
+
687
+        public static SQLiteCommand CreateVoucherEventCountUpdateCommand(DBHelper db, SQLiteTransaction trans)
688
+        {
689
+            var command = db.CreateCommand(
690
+                "UPDATE Voucher SET " +
691
+                    "PrintCount=PrintCount+@PrintCountDelta," +
692
+                    "ReprintCount=ReprintCount+@ReprintCountDelta," +
693
+                    "ExportCount=ExportCount+@ExportCountDelta," +
694
+                    "ReExportCount=ReExportCount+@ReExportCountDelta " +
695
+                "WHERE Id=@Id",
696
+                trans);
697
+            CreateEventCountUpdateQueryParameters(command);
698
+            return command;
699
+        }
700
+
701
+        public static bool CalculateEventCountDeltas(VendorEvent.VendorEventType eventType, bool retry,
702
+            out int printCountDelta, out int reprintCountDelta, out int exportCountDelta, out int reExportCountDelta)
703
+        {
704
+            printCountDelta = 0;
705
+            reprintCountDelta = 0;
706
+            exportCountDelta = 0;
707
+            reExportCountDelta = 0;
708
+
709
+            if (eventType == VendorEvent.VendorEventType.PrintVoucher)
710
+            {
711
+                if (retry)
712
+                {
713
+                    reprintCountDelta++;
714
+                }
715
+                else
716
+                {
717
+                    printCountDelta++;
718
+                }
719
+                return true;
720
+            }
721
+            else if (eventType == VendorEvent.VendorEventType.ExportVoucher)
722
+            {
723
+                if (retry)
724
+                {
725
+                    reExportCountDelta++;
726
+                }
727
+                else
728
+                {
729
+                    exportCountDelta++;
730
+                }
731
+                return true;
732
+            }
733
+            return false;
734
+        }
735
+
736
+        public static SQLiteCommand CreateBatchEventCountUpdateCommand(DBHelper db, SQLiteTransaction trans)
737
+        {
738
+            var command = db.CreateCommand(
739
+                "UPDATE Batch SET " +
740
+                    "PrintCount=PrintCount+@PrintCountDelta," +
741
+                    "ReprintCount=ReprintCount+@ReprintCountDelta," +
742
+                    "ExportCount=ExportCount+@ExportCountDelta," +
743
+                    "ReExportCount=ReExportCount+@ReExportCountDelta " +
744
+                "WHERE Id=@Id",
745
+                trans);
746
+            CreateEventCountUpdateQueryParameters(command);
747
+            return command;
644 748
         }
645 749
 
646 750
         public static void LogBulkEvents(DBHelper db, List<EventLog> EventLogs)
647 751
         {
648
-            string Sql = "INSERT INTO Logs (UserId, VoucherId, EventDate, EventType, Retry) VALUES (@userid, @voucherid, @eventdate, @eventtype, @retry)";
752
+            string Sql = "INSERT INTO Logs (UserId,  VoucherId,  EventDate,  EventType,  Retry,  Summarised) " +
753
+                                   "VALUES (@userid, @voucherid, @eventdate, @eventtype, @retry, 1)";
649 754
 
650 755
             lock (db.WriteLock)
651 756
             {
652 757
                 using (var trans = db.BeginTransaction())
653 758
                 {
654
-                    using (var Command = db.CreateCommand(Sql, trans))
759
+                    using (var insertLogsCommand = db.CreateCommand(Sql, trans))
760
+                    using (var updateVoucherCommand = CreateVoucherEventCountUpdateCommand(db, trans))
761
+                    using (var updateBatchCommand = CreateBatchEventCountUpdateCommand(db, trans))
655 762
                     {
656 763
                         foreach (var EventLog in EventLogs)
657 764
                         {
658
-                            Command.Parameters.Clear();
659
-                            Command.Parameters.AddWithValue("@userid", Globals.SessionData.Credentials.Payload.User.Id);
660
-                            Command.Parameters.AddWithValue("@voucherid", EventLog.VoucherId);
661
-                            Command.Parameters.AddWithValue("@eventdate", DateTime.UtcNow);
662
-                            Command.Parameters.AddWithValue("@eventtype", EventLog.EventType);
663
-                            Command.Parameters.AddWithValue("@retry", EventLog.Retry);
664
-                            Command.ExecuteNonQuery();
765
+                            insertLogsCommand.Parameters.Clear();
766
+                            insertLogsCommand.Parameters.AddWithValue("@userid", Globals.SessionData.Credentials.Payload.User.Id);
767
+                            insertLogsCommand.Parameters.AddWithValue("@voucherid", EventLog.VoucherId);
768
+                            insertLogsCommand.Parameters.AddWithValue("@eventdate", DateTime.UtcNow);
769
+                            insertLogsCommand.Parameters.AddWithValue("@eventtype", EventLog.EventType);
770
+                            insertLogsCommand.Parameters.AddWithValue("@retry", EventLog.Retry);
771
+                            insertLogsCommand.ExecuteNonQuery();
772
+
773
+                            if (EventLog.VoucherId.HasValue)
774
+                            {
775
+                                if (! EventLog.BatchId.HasValue)
776
+                                {
777
+                                    throw new Exception("Event log entry is missing a batch ID");
778
+                                }
779
+
780
+                                int printCountDelta, reprintCountDelta, exportCountDelta, reExportCountDelta;
781
+                                if (CalculateEventCountDeltas(
782
+                                    EventLog.EventType,
783
+                                    EventLog.Retry,
784
+                                    out printCountDelta,
785
+                                    out reprintCountDelta,
786
+                                    out exportCountDelta,
787
+                                    out reExportCountDelta))
788
+                                {
789
+                                    ExecuteEventCountUpdateQuery(updateVoucherCommand, EventLog.VoucherId.Value,
790
+                                        printCountDelta, reprintCountDelta, exportCountDelta, reExportCountDelta);
791
+
792
+                                    ExecuteEventCountUpdateQuery(updateBatchCommand, EventLog.BatchId.Value,
793
+                                        printCountDelta, reprintCountDelta, exportCountDelta, reExportCountDelta);
794
+                                }
795
+                            }
665 796
                         }
666 797
                     }
667 798
                     trans.Commit();
@@ -674,16 +805,18 @@ namespace BulkPrinting
674 805
             }
675 806
         }
676 807
 
677
-        public static void LogEvent(DBHelper db, VendorEvent.VendorEventType EventType, int? VoucherId = null, bool Retry = false)
808
+        public static void LogEvent(DBHelper db, VendorEvent.VendorEventType EventType)
678 809
         {
810
+            // Not for voucher-related events. If you add support for individual voucher events, please update print counts,
811
+            // etc., as in LogBulkEvents.
679 812
             db.ExecuteNonQuery(
680
-                "INSERT INTO Logs (UserId,  VoucherId,  EventDate,  EventType,  Retry) " +
681
-                          "VALUES (@userid, @voucherid, @eventdate, @eventtype, @retry)",
813
+                "INSERT INTO Logs (UserId,  VoucherId,  EventDate,  EventType,  Retry,  Summarised) " +
814
+                          "VALUES (@userid, @voucherid, @eventdate, @eventtype, @retry, 1)",
682 815
                 new SQLiteParameter("@userid", Globals.SessionData.Credentials.Payload.User.Id),
683
-                new SQLiteParameter("@voucherid", VoucherId),
816
+                new SQLiteParameter("@voucherid", null),
684 817
                 new SQLiteParameter("@eventdate", DateTime.UtcNow),
685 818
                 new SQLiteParameter("@eventtype", EventType),
686
-                new SQLiteParameter("@retry", Retry));
819
+                new SQLiteParameter("@retry", false));
687 820
             if (Globals.SessionMode == SessionModes.Online)
688 821
             {
689 822
                 TriggerLogUpload();