private static void GetVersionLoop() { while (loopFlag) { // sleep for timeout Thread.Sleep(timeout); // don't want two concurrent updates if (checkingFlag || updatingFlag) { continue; } try { // locking is overkill here because only one thread that wakes up every <timeout> checkingFlag = true; WebServiceHelper.GetCurrentSoftwareVersion( VersionUrl, new WebServiceHelper.AccountDelegate((version) => { if (string.IsNullOrEmpty(version)) { TraceLog.TraceError("Could not download version file"); checkingFlag = false; return; } try { // compare versions var newVersion = new Version(version); if (newVersion > CurrentVersion) { TraceLog.TraceInfo(string.Format( "New version {0} more recent than current version {1}; downloading new version", newVersion, CurrentVersion)); updatingFlag = true; // download and install new software WebServiceHelper.DownloadCurrentSoftware( DownloadUrl, new WebServiceHelper.AccountDelegate((filename) => { // install the new software TraceLog.TraceInfo(string.Format("Download successful; installing {0}", filename)); Install(filename); }), new WebServiceHelper.NetOpDelegate((inProgress, status) => { // cleanup the checking flag based on network operation status (typically failures) if (inProgress == false) { updatingFlag = false; } })); } checkingFlag = false; } catch (Exception ex) { TraceLog.TraceException("Version check failed", ex); checkingFlag = false; updatingFlag = false; } }), new WebServiceHelper.NetOpDelegate((inProgress, status) => { // cleanup the checking flag based on network operation status (typically failures) if (inProgress == false) { checkingFlag = false; } })); } catch (Exception ex) { TraceLog.TraceException("GetVersionLoop failed", ex); checkingFlag = false; updatingFlag = false; } } }
private static void Send() { try { // snap the record list var recordList = CollectorClient.GetRecords(); // if no records, may need to recover capture service if (recordList.Count == 0) { if (recovering) { // this is the second timeout period that we didn't see records. // attempt to restart the collector CollectorClient.Start(); recovering = false; } else { // this will trigger recovery if the next run through also has zero records recovering = true; } // nothing to do this time (no records to process) return; } else { // reset recovery mode recovering = false; } // compact the record list var uniqueHosts = recordList.Select(r => r.HostMacAddress).Distinct(); foreach (var host in uniqueHosts) { var uniqueDests = recordList.Where(r => r.HostMacAddress == host).Select(r => r.WebsiteName).Distinct(); foreach (var dest in uniqueDests) { var records = recordList.Where(r => r.HostMacAddress == host && r.WebsiteName == dest).OrderBy(r => r.Timestamp); // only keep the first and last record so that the duration calculated on the server will be > 0 var firstRecord = records.FirstOrDefault(); var lastRecord = records.LastOrDefault(); // if the last record is a second or more later, add the duration to the first record TimeSpan duration; if (lastRecord != null && lastRecord != firstRecord && (duration = Convert.ToDateTime(lastRecord.Timestamp) - Convert.ToDateTime(firstRecord.Timestamp)) >= TimeSpan.FromSeconds(1.0)) { firstRecord.Duration = (int)duration.TotalSeconds; } lock (sendQueue) { var existingRecord = sendQueue.FirstOrDefault(r => r.HostMacAddress == host && r.WebsiteName == dest); if (existingRecord == null || Convert.ToDateTime(firstRecord.Timestamp) - (Convert.ToDateTime(existingRecord.Timestamp) + TimeSpan.FromSeconds(existingRecord.Duration)) >= TimeSpan.FromSeconds(300.0)) // more than 5 minutes between records { // add the record to the send queue sendQueue.Add(firstRecord); } else { // augment existing record with new duration duration = Convert.ToDateTime(firstRecord.Timestamp) - Convert.ToDateTime(existingRecord.Timestamp) + TimeSpan.FromSeconds(firstRecord.Duration); existingRecord.Duration = (int)duration.TotalSeconds; } } } } if (sendQueue.Count > 0) { // snap the send queue var recordBuffer = new List <Record>(); lock (sendQueue) { // clear this buffer if collection is suspended if (collectionStatus == ControlMessage.SuspendCollection) { sendQueue.Clear(); TraceLog.TraceInfo("Probing for new collection status"); } else { recordBuffer.AddRange(sendQueue); TraceLog.TraceInfo(String.Format("Sending records: {0}", JsonConvert.SerializeObject(sendQueue))); } } var sendList = new RecordList() { DeviceId = deviceId, DeviceName = deviceName, SoftwareVersion = UpdateClient.CurrentVersion.ToString(), Records = recordBuffer }; // send the queue to the web service WebServiceHelper.PostRecords( credentials, sendList, new WebServiceHelper.PostRecordsDelegate((response) => { // a callback means the service processed the records successfully; we can free them now. // note a slight race condition - if the callback takes longer than the sleep interval, we // could lose a buffer. since the sleep interval is 60sec, this isn't an important issue // to address. lock (sendQueue) { sendQueue.Clear(); } var msg = (ControlMessage)response.controlMessage; if (collectionStatus == msg) { return; } // process the control message lock (configLock) { switch (msg) { case ControlMessage.Normal: TraceLog.TraceInfo("Changing collection status to Normal"); collectionStatus = msg; ConfigClient.Write(ConfigClient.Disabled, Convert.ToString(false)); break; case ControlMessage.SuspendCollection: TraceLog.TraceInfo("Changing collection status to Suspended"); collectionStatus = msg; ConfigClient.Write(ConfigClient.Disabled, Convert.ToString(true)); break; case ControlMessage.DisableDevice: TraceLog.TraceInfo("Disabling the device by wiping the config"); collectionStatus = msg; deviceId = null; deviceName = null; // wipe the config ConfigClient.Clear(); break; default: // new message we don't understand yet TraceLog.TraceInfo(string.Format("Received unknown control message {0}", msg)); break; } } }), new WebServiceHelper.NetOpDelegate((inProgress, status) => { if (status == OperationStatus.Retry) { // no need to do anything: next iteration will send original credentials } // no failure state to clean up })); } } catch (Exception ex) { TraceLog.TraceException("Upload processing failed", ex); } }