private DockingStationAction ExamineErrors(DockingStationEvent dsEvent, InetUploader inet) { DockingStationErrorLevel highestError = DockingStationErrorLevel.None; DockingStationAction dsAction = null; foreach (DockingStationError error in dsEvent.Errors) { highestError = (DockingStationErrorLevel)Math.Max((int)error.ErrorLevel, (int)highestError); } if (dsAction != null) { return(dsAction); } if (highestError >= DockingStationErrorLevel.Error) { dsAction = new UnavailableAction(); Log.Debug(string.Format("{0}Returning {1}", Name, dsAction.Name)); return(dsAction); } return(null); }
public DockingStationAction ReportFlowFailedError(GasEndPoint gasEndPoint) { Log.Debug(string.Format("Empty Cylinder was reported on position {0} during gas operation.", gasEndPoint.Position)); using (InetUploader inetUploader = new InetUploader()) { DockingStationAction dsAction = ProcessEmptyGasEndPoint(gasEndPoint, inetUploader); return(dsAction); } }
internal void ReportQueuedErrors() { lock ( _errorsQueue ) { if (_errorsQueue.Count == 0) { return; } using (InetUploader localUploader = new InetUploader()) { ReportQueuedErrors(localUploader); } } }
private void ReportQueuedErrors(InetUploader inetUploader) { lock ( _errorsQueue ) { while (_errorsQueue.Count > 0) { Log.Debug("ReportQueuedErrors: uploading error"); DockingStationError dsError = _errorsQueue.Peek(); Log.Debug(dsError.ToString()); inetUploader.UploadError(dsError, Configuration.DockingStation.TimeZoneInfo); _errorsQueue.Dequeue(); } _errorsQueue.TrimExcess(); } }
/// <summary> /// Executes an instrument set server ip address settings operation. /// </summary> /// <returns>Docking station event</returns> public DockingStationEvent Execute() { // First connect to iNet by doing an ExchangeStatus call. The intention is that we // get back an account number needed to subsequently upload the DockingStation info // (this will probably be a manufacturing account). Also, we need to set the clock // to the proper time in order get a proper SetupDate. // NOTE: IF THIS ExchangeStatus FAILS (THE WEB SERVICE RETURNS NULL), A LIKELY // CULPRIT IS THAT THE SERVER HAS NOT BEEN CORRECTLY CONFIGURED TO KNOW WHAT IT'S // MANUFACTURING ACCOUNT IS!!! InetStatus inetStatus = null; using (InetDownloader inet = new InetDownloader(DockingStation, Configuration.Schema)) { Log.Info("Calling ExchangeStatus for new S/N " + DockingStation.SerialNumber); inetStatus = inet.ExchangeStatus(Name, string.Empty, null, null, true); // TODO - what should we do with the error? if (inetStatus.Error != string.Empty) { throw new ApplicationException(inetStatus.Error); } Log.Info(string.Format("ExchangeStatus successful for S/N {0}!", DockingStation.SerialNumber)); // TODO - will the following ever happen? We need the current time in order // to properly set the SetupDate. if (inetStatus.CurrentTime == DomainModelConstant.NullDateTime) { throw new ApplicationException("No current time returned by iNet."); // TODO } // TODO - will this ever happen? if (inetStatus.Schema.AccountNum == string.Empty) { throw new ApplicationException("No account number returned by iNet"); } // TODO - what about the time zone? CurrentTime will be in UTC. We probably // need to make sure that we set the time in the context of Eastern so that // SetupDate is in Eastern. SystemTime.SetSystemTime(inetStatus.CurrentTime); } // Upload the docking station's info to Inet. Log.Info(string.Format("Uploading new serialization info to iNet (S/N {0})", DockingStation.SerialNumber)); using (InetUploader inet = new InetUploader(DockingStation, Configuration.Schema)) { string uploadError = inet.UploadDockingStation(DockingStation, DateTime.UtcNow, inetStatus.Schema.AccountNum, Configuration.DockingStation.TimeZoneInfo); if (uploadError != string.Empty) { throw new ApplicationException(uploadError); // should we handle failure in a better way? } } Log.Info(string.Format("Upload successful!", DockingStation.SerialNumber)); // If we make it to here, then we must have successfully uploaded the serialization // info to iNet and we assume that iNet now has knowledge of this docking station. // We now actually save the serialization info. if (ShouldSave == true) // SGF 29-Apr-2011 INS-3563 Added if-statement { Configuration.Serialize(DockingStation); } return(null); }
/// <summary> /// For certain 'actions' that the VDS decides to do, iNet needs to be /// notified of. e.g. failed Leak check, Unavailable Gas, etc. /// </summary> /// <param name="dsAction"></param> /// <param name="inetUploader">May be null. /// If null is passed, the method will instantiate an Inet object to use.</param> private void ProcessNotificationAction(DockingStationAction dsAction, InetUploader inetUploader) { // _lastNotificationError is the last CONSECUTIVE NotificationAction. // This the passed-in action is not a NotificationAction, then that // breaks the current series of consecutive NotificationActions. // So, we set it to null to denote that. if (!(dsAction is INotificationAction)) { _lastNotificationError = null; return; } const string funcMsg = "ProcessNotificationAction: "; StringBuilder errMsg = new StringBuilder(dsAction.Name); foreach (string m in dsAction.Messages) { errMsg.AppendFormat("\r\n{0}", m); } if (dsAction is UnsupportedCylinderAction) { errMsg.AppendFormat("\r\non Port {0}", ((UnsupportedCylinderAction)dsAction).GasEndPoint.Position); } // If it's an UnavailableAction, then the content of the error should // be the Exception's error (if there is an Exception). if ((dsAction is UnavailableAction) && (((UnavailableAction)dsAction).Exception != null)) { errMsg.AppendFormat("\r\n{0}", ((UnavailableAction)dsAction).Exception.ToString()); } DockingStationError dsError; //Suresh 02-Feb-2012 INS-2392 if (Master.SwitchService.Instrument == null) { dsError = new DockingStationError(errMsg.ToString()); } else { dsError = new DockingStationError(errMsg.ToString(), Master.SwitchService.Instrument.SerialNumber); } // If this NotificationError's detail is the exact same as the last NotificationError's // detail, then we assume it's a duplicate. We don't want to upload duplicates. if (_lastNotificationError != null && _lastNotificationError.Description == dsError.Description) { Log.Debug(string.Format("{0}Ignoring duplicate: {1}", funcMsg, dsAction.ToString())); return; } _lastNotificationError = dsError; // We upload the error immediately (don't just queue it to our "Errors" List). if (inetUploader != null) { Log.Debug(string.Format("{0}Uploading ", funcMsg, dsAction.Name)); inetUploader.UploadError(dsError, Configuration.DockingStation.TimeZoneInfo); } else { // if an uploader wasn't passed in to us to be re-used, then we need to create our own local one. using (InetUploader localUploader = new InetUploader()) { Log.Debug(string.Format("{0}Uploading {1}", funcMsg, dsAction.Name)); localUploader.UploadError(dsError, Configuration.DockingStation.TimeZoneInfo); } } }
/// <summary> /// </summary> /// <remarks> /// 1) Updates the cylinder's pressure in the database (change it from Full to Low, or Low to Empty). /// 2) Update cached cylinder (in Configuration.DockingStation.InstalledCylinders) with the pressure /// change. /// 3) Also forces an upload of a SettingsReadEvent in order to notify iNet of the pressure change. /// </remarks> /// <param name="emptyCylinderError"></param> /// <param name="inetUploader"></param> /// <returns></returns> private DockingStationAction ProcessEmptyGasEndPoint(GasEndPoint emptyEndPoint, InetUploader inetUploader) { Log.Debug(string.Format("Empty Cylinder was reported on position {0}", emptyEndPoint.Position)); GasEndPoint gasEndPoint = null; using (DataAccessTransaction trx = new DataAccessTransaction()) { GasEndPointDataAccess gepDataAccess = new GasEndPointDataAccess(); gasEndPoint = gepDataAccess.FindByPosition(emptyEndPoint.Position, trx); if (gasEndPoint == null) { return(null); // should we display an error? } if (gasEndPoint.Cylinder.Pressure == PressureLevel.Full) { gasEndPoint.Cylinder.Pressure = PressureLevel.Low; Log.Warning("Low pressure warning, position " + emptyEndPoint.Position); } else { gasEndPoint.Cylinder.Pressure = PressureLevel.Empty; Log.Warning("Empty pressure warning, position " + emptyEndPoint.Position); } Log.Debug(string.Format("Changing pressure to \"{0}\" for cylinder on position {1}", gasEndPoint.Cylinder.Pressure.ToString(), gasEndPoint.Position)); Log.Debug(string.Format("...PartNumber={0}, FactoryId={1}", gasEndPoint.Cylinder.PartNumber, gasEndPoint.Cylinder.FactoryId)); gepDataAccess.UpdatePressureLevel(gasEndPoint, trx); trx.Commit(); } // After updating the database, we need to update the cached copy of the // installed cylinder, too, which is kept in the global DockingStation. GasEndPoint cachedGasEndPoint = Configuration.DockingStation.GasEndPoints.Find(g => g.Position == gasEndPoint.Position); if (cachedGasEndPoint != null) { cachedGasEndPoint.Cylinder.Pressure = gasEndPoint.Cylinder.Pressure; } // We need to inform iNet whenever a cylinder's pressure changes. We can only do this // by uploading a full docking station with the cylinder information. Which means we // need to immediately do a SettingsRead to get the full docking station info, and then // upload that info along with the currently installed cylinder info. SettingsReadOperation settingsReadOperation = new SettingsReadOperation(); // Explicitly set the ChangedSmartCards to all falses so that no smart cards are read. settingsReadOperation.ChangedSmartCards = new bool[Configuration.DockingStation.NumGasPorts]; SettingsReadEvent settingsReadEvent = (SettingsReadEvent)settingsReadOperation.Execute(); // Copy currently installed cylinder info to the docking station object we're going to upload. settingsReadEvent.DockingStation.GasEndPoints.Clear(); settingsReadEvent.DockingStation.ChangedGasEndPoints.Clear(); foreach (GasEndPoint gep in Configuration.DockingStation.GasEndPoints) { settingsReadEvent.DockingStation.GasEndPoints.Add((GasEndPoint)gep.Clone()); } inetUploader.UploadEvent(settingsReadEvent, Configuration.DockingStation.TimeZoneInfo); // BEGIN INS-8630 RHP v7.5 List <string> consoleMessages = new List <string>(); consoleMessages.Add(gasEndPoint.Cylinder.Pressure.ToString()); if (!string.IsNullOrEmpty(gasEndPoint.Cylinder.PartNumber)) // For ISC Pass the Cylinder Part Number { consoleMessages.Add(gasEndPoint.Cylinder.PartNumber); } // For Non ISC Pass the gas code. But I believe that both ISC and Non-ISC cylinders have their own PArt numbers. // So below condition may not be required ? else if (gasEndPoint.Cylinder.GasConcentrations.Count > 0) { consoleMessages.Add(gasEndPoint.Cylinder.GasConcentrations[0].Type.Symbol); } Log.Info("Sending IDS ReplaceCylinderAction"); DockingStationAction dsAction = new ResourceUnavailableAction(new List <string>() { gasEndPoint.Cylinder.Pressure.ToString() }, consoleMessages); // END INS-8630 return(dsAction); }
/// <summary> /// Reports a docking station event to the server. /// </summary> /// <param name="dockingStationEvent"> /// The event to be reported /// </param> /// <returns> /// A docking station action indicating the next task /// the docking station should perform. /// </returns> public DockingStationAction ReportEvent(DockingStationEvent dsEvent) { if (dsEvent == null) { return(null); } if (dsEvent.DockingStation.SerialNumber == string.Empty) { return(null); } LogEventDetails(dsEvent); DockingStationAction dsAction = null; // We don't yet instantiate the Uploader instance. We wait until we know for sure // we're going to use it. Note that it's Disposed of in the finally block below. InetUploader inetUploader = null; try { try { // It's now finally safe to log the event. _eventProcessor.Save(dsEvent, Master.Instance.SwitchService.DockedTime); // These upload calls won't actually try and upload if we're not associated with any account. // or even if we DO have an account number, it won't upload if we're not activated on iNet. // Yet it WILL upload if we're in Service mode, regardless if activated or not. // Confusing, eh? inetUploader = new InetUploader(); inetUploader.UploadEvent(dsEvent, Configuration.DockingStation.TimeZoneInfo); if (dsEvent is InstrumentGasResponseEvent) { // Print automated bumps and cals new PrintManager().Print((InstrumentGasResponseEvent)dsEvent); } // Only save event to USB drive when in Cal Station mode and not Service mode if (!Configuration.Schema.Activated && !Configuration.ServiceMode) { if (dsEvent is InstrumentDatalogDownloadEvent) { // if the full datalog can not be saved to the USB drive an exception will // be thrown to make the docking station go unavailable; this is to prevent // the DS from erasing the datalog when it wasn't able to save it new CsvFileManager().Save((InstrumentDatalogDownloadEvent)dsEvent); } else if (dsEvent is InstrumentGasResponseEvent) { // only bumps and cals are saved new CsvFileManager().Save((InstrumentGasResponseEvent)dsEvent); } } ReportQueuedErrors(inetUploader); // See if docking station needs to take any special action for any of the event's errors. dsAction = ExamineErrors(dsEvent, inetUploader); if (dsAction != null) { Log.Debug(string.Format("{0}: ExamineErrors returned {1}", Name, dsAction.Name)); return(dsAction); } } catch (Exception e) { Log.Error(Name, e); dsAction = new UnavailableAction(e); // DO NOT report the error. If we have an error uploading, then it makes // no sense to try and upload and error notifying of a problem trying to upload. //ProcessNotificationAction( dsAction, inet ); return(dsAction); } try { // Determine if the event that just transpired requires a followed up RebootAction. dsAction = CheckRebootableEvent(dsEvent); if (dsAction == null) { // Before determining our next action, make sure we have the most up-to-date // schedules and eventjournals, etc., from iNet. ExchangeInetStatus(dsEvent); // Find out what we're supposed to do next. dsAction = Master.Scheduler.GetNextAction(dsEvent); if (dsAction is InstrumentAction) { InstrumentAction instAction = (InstrumentAction)dsAction; if (instAction is InstrumentGasAction) { // Get the resources from the resource manager. StringBuilder explanation = new StringBuilder(); List <string> consoleMessages = new List <string>(); // SGF 20-Feb-2013 INS-3821 List <string> errorCodes = new List <string>(); // SGF 20-Feb-2013 INS-3821 InstrumentGasAction gasAction = instAction as InstrumentGasAction; string eventCode = null; if (gasAction is InstrumentBumpTestAction) { eventCode = EventCode.BumpTest; } else if (gasAction is InstrumentCalibrationAction) { eventCode = EventCode.Calibration; } else { throw new ArgumentOutOfRangeException("Unrecognized GasAction: " + gasAction.GetType().ToString()); } // SGF 20-Feb-2013 INS-3821 // SGF 03-Nov-2010 Single Sensor Cal and Bump gasAction.GasEndPoints = Master.Instance.ResourceService.GetGasEndPoints(eventCode, gasAction, explanation, consoleMessages, errorCodes); if (gasAction.GasEndPoints.Count == 0) { Log.Warning(string.Format("No gases for {0}. {1}", eventCode, explanation.ToString())); // Maintain the empty cylinder error state resulting from forced actions by re-forcing the action Master.Instance.Scheduler.ReForceEvent(instAction); dsAction = new ResourceUnavailableAction(errorCodes, consoleMessages); // SGF 20-Feb-2013 INS-3821 } } // For instrument firmware upgrades, we don't want to allow the upgrade to take place // if there's no gas to both calibrate and bump the instrument. This is because at the // end of the upgrade, the VDS will automatically calibrate then bump test the instrument. if (instAction is InstrumentFirmwareUpgradeAction) { StringBuilder explanation = new StringBuilder(); List <string> consoleMessages = new List <string>(); // SGF 20-Feb-2013 INS-3821 List <string> errorCodes = new List <string>(); // SGF 20-Feb-2013 INS-3821 // SGF 20-Feb-2013 INS-3821 // SGF 03-Nov-2010 Single Sensor Cal and Bump if (Master.Instance.ResourceService.GetGasEndPoints(EventCode.Calibration, instAction, explanation, consoleMessages, errorCodes).Count == 0) { Log.Warning(string.Format("No gases for firmware upgrade {0}. {1}", EventCode.Calibration, explanation.ToString())); dsAction = new ResourceUnavailableAction(errorCodes, consoleMessages); // SGF 20-Feb-2013 INS-3821 } else { explanation = new StringBuilder(); consoleMessages.Clear(); // SGF 20-Feb-2013 INS-3821 errorCodes.Clear(); // SGF 20-Feb-2013 INS-3821 // SGF 20-Feb-2013 INS-3821 // SGF 03-Nov-2010 Single Sensor Cal and Bump if (Master.Instance.ResourceService.GetGasEndPoints(EventCode.BumpTest, instAction, explanation, consoleMessages, errorCodes).Count == 0) { Log.Warning(string.Format("No gases for firmware upgrade {0}. {1}", EventCode.BumpTest, explanation.ToString())); dsAction = new ResourceUnavailableAction(errorCodes, consoleMessages); // SGF 20-Feb-2013 INS-3821 } } } } } } // INS-8228 RHP v7.6 Log and report InstrumentSystemAlarmException thrown from Scheduler catch (InstrumentSystemAlarmException e) { Log.Error(Name, e); Master.ConsoleService.UpdateState(ConsoleState.InstrumentSystemAlarm); Master.ExecuterService.ReportExceptionError(e); } catch (Exception e) { Log.Error(Name, e); dsAction = new UnavailableAction(e); } // If the scheduler says there's currently nothing to do, then check // if we have an illegal cylinder or not. Report an unsupported cylinder if so. if (dsAction is NothingAction) { DockingStationAction cylinderAction = ExamineGasEndPoints(); if (cylinderAction != null) { dsAction = cylinderAction; } } // If the action we're about to return is telling the VDS to display an error message, // then we should tell iNet the error too. We pass in our Uploader instance to re-use the // socket which should give a couple seconds performance benefit. ProcessNotificationAction(dsAction, inetUploader); } finally { if (inetUploader != null) { inetUploader.Dispose(); } } return(dsAction); }
/// <summary> /// This method implements the thread start for this service. /// </summary> protected override void Run() { // For logging, keep track of number of items found on queue, which of // those successfully uploaded, and which that were ignored/skipped. int queuedCount = 0, uploadedCount = 0, ignoredCount = 0; InetUploader inetUploader = null; try { PersistedQueue inetQueue = PersistedQueue.CreateInetInstance(); while (true) { if (!Runnable()) // Keep sending until the message queue is empty or we're stopped/paused. { break; } try { Log.Trace(">INET: Checking for non-empty queue."); bool isEmpty = inetQueue.IsEmpty(); if (isEmpty) { Log.Trace(">INET: No data in upload queue."); PendingUploads = false; break; } Log.Debug(">INET: Retrieving queued item...."); // Get the oldest object on the queue (the object is not removed). object queObject = inetQueue.Peek(); if (queObject == null) { Log.Debug(">INET: No data in upload queue."); PendingUploads = false; break; } PendingUploads = true; string deviceLocation = string.Empty; object wsParmObject = null; queuedCount++; QueueData queueData = null; try { queueData = (QueueData)queObject; double kB = (double)queueData.WebServiceParameterSize / 1024.0d; // convert bytes to kilobytes Log.Debug(string.Format(">INET: {0} ({1},\"{2}\",{3}), {4} KB)...", queueData, queueData.Id, Log.DateTimeToString(queueData.Timestamp), queueData.InetAccountNum, kB.ToString("f1"))); wsParmObject = queueData.WebServiceParameter; } catch (Exception e) { // If this failed, then something was on the queue which // was not the right type. Could have been old data. // Just ignore it. ignoredCount++; Log.Debug(">INET: " + e.ToString()); // If debug build, DO NOT delete poison data. We need to keep // it so that we can investigate what's wrong with it. #if DEBUG Log.Debug(">INET: Found non-conforming data on queue: "); Log.Debug(">INET: " + queObject.ToString()); break; #else Log.Debug(">INET: Found non-conforming data on queue. Purging it:"); Log.Debug(">INET: " + queObject.ToString()); //_msmqTransaction.Commit(); inetQueue.Delete(queueData.Id); continue; #endif } // We don't instantiate iNet until we know we need it And, if we make it // to here, then that's now. We then will continue to re-use it for the duration // that we remain in the loop. if (inetUploader == null) { inetUploader = new InetUploader(); } string errorCode = inetUploader.Upload(wsParmObject, queueData.Label, queueData.InetAccountNum); // On any error, just break out and we'll retry on next iteration of Run(). // The intent is that if there's some network issue, or we're just offline, // then we watn to let some time pass for the issue to clear up or to go back online. if (errorCode != string.Empty) { break; } // Now that we've successfully uploaded it to iNet, we can remove it from the queue Log.Debug(string.Format(">INET: Deleting uploaded {0} from queue.", queueData)); inetQueue.Delete(queueData.Id); uploadedCount++; } // catch catch (Exception ex1) { Log.Debug(">INET: Exception caught, - " + ex1.ToString()); } } // end-while !empty queue } finally { if (inetUploader != null) { inetUploader.Dispose(); } } if (queuedCount > 0) { Log.Debug(">INET: Finished. " + queuedCount + " found in queue, " + uploadedCount + " uploaded, " + ignoredCount + " ignored"); } }