private void UpdateEnforcementActionTakenFromSqLite(SpaceStatusModel model, CustomerLogic result) { DateTime minDateCutoff = DateTime.MinValue; // Get the related space asset so we can obtain the AreaID SpaceAsset asset = GetSpaceAsset(model.MeterID, model.BayID); // CustomerLogic.CustomerManager.GetSpaceAsset(_CustomerConfig, model.MeterID, model.BayID); int areaID = 0; if (asset != null) { areaID = asset.AreaID_Internal; } if (((model.BayEnforcementState == EnforcementState.OverstayViolation) || (model.BayEnforcementState == EnforcementState.Discretionary)) && (model.CurrentOverstayOrLatestDiscretionaryVio != null)) { // For overstay condition, we only want the latest action taken if it happened after the violation started! minDateCutoff = model.CurrentOverstayOrLatestDiscretionaryVio.DateTime_StartOfOverstayViolation; // Get the most recent action taken for this space asset OverstayVioActionsDTO ActionTakenDTO = result.GetLatestVioActionForSpace(this._CustomerConfig.CustomerId, model.MeterID, areaID, model.BayID); if ((ActionTakenDTO != null) && (minDateCutoff > DateTime.MinValue)) { // If the action taken was recorded after the start of this violation, then it qualifies as being the action taken for this // violation condition. If so, retain the "Action Taken" reason if (ActionTakenDTO.EventTimestamp >= minDateCutoff) { model.EnforcementActionTaken = ActionTakenDTO.ActionTaken; } } } else { minDateCutoff = model.BayVehicleSensingTimestamp; // Get the most recent action taken for this space asset OverstayVioActionsDTO ActionTakenDTO = result.GetLatestVioActionForSpace(this._CustomerConfig.CustomerId, model.MeterID, areaID, model.BayID); if ((ActionTakenDTO != null) && (minDateCutoff >= DateTime.MinValue)) { // If the action taken was recorded after the start of this violation, then it qualifies as being the action taken for this // violation condition. If so, retain the "Action Taken" reason if (ActionTakenDTO.EventTimestamp >= minDateCutoff) { model.EnforcementActionTaken = ActionTakenDTO.ActionTaken; } } } }
public SpaceStatusModel GetOrCreateSpaceStatusModel(int meterID, int spaceID, SpaceAsset spaceAsset) { // Search our list for an existing model matching the MeterID and SpaceID SpaceStatusModel result = null; foreach (SpaceStatusModel nextModel in this.SpaceStatusModels) { if ((nextModel.MeterID == meterID) && (nextModel.BayID == spaceID)) { result = nextModel; break; } } // If no match found, create a new object if (result == null) { result = new SpaceStatusModel(); result.MeterID = meterID; result.BayID = spaceID; // If the associated space asset wasn't given to us, we will need to look it up if (spaceAsset == null) { SpaceAsset nextSpaceAsset = GetSpaceAsset(meterID, spaceID); // CustomerLogic.CustomerManager.GetSpaceAsset(_CustomerConfig, meterID, spaceID); if (nextSpaceAsset != null) { result.IsSensorOnly = nextSpaceAsset.IsSensorOnly; } } else { result.IsSensorOnly = spaceAsset.IsSensorOnly; } // Add the new object to our list this.SpaceStatusModels.Add(result); } return(result); }
private SpaceAsset GetSpaceAsset(int meterID, int spaceID) { SpaceAsset result = null; // If we have a list of cached assets, look through it, which should be faster than constantly asking the customer manager for it // since we would have a local copy and not need to worry about concurrency issues if (_cachedSpaceAssets != null) { foreach (SpaceAsset asset in _cachedSpaceAssets) { if ((asset.MeterID == meterID) && (asset.SpaceID == spaceID)) { result = asset; break; } } } else { result = CustomerLogic.CustomerManager.GetSpaceAsset(_CustomerConfig, meterID, spaceID); } return(result); }
public Object GetData(IDataReader reader) { // This is where we define the mapping between the object properties and the // data columns. The convention that should be used is that the object property // names are exactly the same as the column names. However if there is some // compelling reason for the names to be different, the mapping can be defined here. // We assume the reader has data and is already on the row that contains the data // we need. We don't need to call read. As a general rule, assume that every field must // be null checked. If a field is null then the nullvalue for that field has already // been set by the DTO constructor, we don't have to change it. if (!_isInitialized) { InitializeMapper(reader); } // Now we can load the data into the DTO object from the DB reader SpaceAsset dto = new SpaceAsset(); if (!reader.IsDBNull(_ordinal_ParkingSpaceID)) { dto.ParkingSpaceId_Internal = Convert.ToInt64(reader[_ordinal_ParkingSpaceID]); } if (!reader.IsDBNull(_ordinal_BayNumber)) { dto.SpaceID = Convert.ToInt32(reader[_ordinal_BayNumber]); } if (!reader.IsDBNull(_ordinal_AreaID)) { dto.AreaID_Internal = Convert.ToInt32(reader[_ordinal_AreaID]); } if (!reader.IsDBNull(_ordinal_LibertyArea)) { dto.AreaID_Liberty = Convert.ToInt32(reader[_ordinal_LibertyArea]); } if (!reader.IsDBNull(_ordinal_MeterID)) { dto.MeterID = Convert.ToInt32(reader[_ordinal_MeterID]); } if (!reader.IsDBNull(_ordinal_ClusterID)) { dto.PAMClusterID = Convert.ToInt32(reader[_ordinal_ClusterID]); } if (!reader.IsDBNull(_ordinal_HasSensor)) { dto.HasSensor = Convert.ToBoolean(reader[_ordinal_HasSensor]); } if (!reader.IsDBNull(_ordinal_SpaceType)) { dto.SpaceType = Convert.ToInt32(reader[_ordinal_SpaceType]); } if (!reader.IsDBNull(_ordinal_Latitude)) { dto.Latitude = Convert.ToSingle(reader[_ordinal_Latitude]); } if (!reader.IsDBNull(_ordinal_Longitude)) { dto.Longitude = Convert.ToSingle(reader[_ordinal_Longitude]); } if (!reader.IsDBNull(_ordinal_CollRouteID)) { dto.CollectionRouteID = Convert.ToInt32(reader[_ordinal_CollRouteID]); } if (!reader.IsDBNull(_ordinal_EnfRouteID)) { dto.EnforcementRouteID = Convert.ToInt32(reader[_ordinal_EnfRouteID]); } if (!reader.IsDBNull(_ordinal_MaintRouteID)) { dto.MaintRouteID = Convert.ToInt32(reader[_ordinal_MaintRouteID]); } if (!reader.IsDBNull(_ordinal_CustomGroup1)) { dto.CustomGroup1ID = Convert.ToInt32(reader[_ordinal_CustomGroup1]); } if (!reader.IsDBNull(_ordinal_CustomGroup2)) { dto.CustomGroup2ID = Convert.ToInt32(reader[_ordinal_CustomGroup2]); } if (!reader.IsDBNull(_ordinal_CustomGroup3)) { dto.CustomGroup3ID = Convert.ToInt32(reader[_ordinal_CustomGroup3]); } // We won't use an IsActive column from the database anymore. Now we are using a local XML file in the webservice to keep track of inactive spaces /* * if (!reader.IsDBNull(_ordinal_IsActive)) { dto.IsActive = Convert.ToBoolean(reader[_ordinal_IsActive]); } * * // We might as well log the fact that we loaded a space that is marked as "inactive". This might be useful info to have available when * // researching an issue in the future... * if (dto.IsActive == false) * { * Duncan.PEMS.SpaceStatus.UtilityClasses.Logging.AddTextToGenericLog(UtilityClasses.Logging.LogLevel.Debug, * string.Format("Space is flagged as inactive: ParkingSpaceID={0}, MID={1}, BAY={2} (Based on HousingMaster.IsActive)", dto.ParkingSpaceId_Internal, dto.MeterID, dto.SpaceID), * System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name + "." + System.Reflection.MethodBase.GetCurrentMethod().Name, * System.Threading.Thread.CurrentThread.ManagedThreadId); * } */ return(dto); }
protected void AnalyzeSpaceStatusModelForOverstayViolation(SpaceStatusModel model) { // There is nothing to do if this event is not for occupied status if ((model.BayOccupancyState == OccupancyState.Empty) || (model.BayOccupancyState == OccupancyState.NotAvailable) || (model.BayOccupancyState == OccupancyState.OutOfDate) || (model.BayOccupancyState == OccupancyState.Unknown)) { return; } // Find the space asset associated with this data model. If the space is "inactive" (based on the "IsActive" column of "HousingMaster" table in database), // then we will not consider the space to be in a violating state, because the sensor is effectively marked as bad/untrusted SpaceAsset spcAsset = GetSpaceAsset(model.MeterID, model.BayID); if (spcAsset != null) { // Nothing more to do if the space isn't active if (spcAsset.IsActive == false) { return; } } // Resolve the associated area for the meter int areaID = ResolveAreaIDForMeterID(model.MeterID); // Try to obtain the regulated hours applicable to this meter RegulatedHoursGroup regulatedHours = RegulatedHoursGroupRepository.Repository.GetBestGroupForMeter(this._CustomerConfig.CustomerId, areaID, model.MeterID); // If no regulated hour defintions came back, then we are unable to calculate any overstay violation, so just exit if ((regulatedHours == null) || (regulatedHours.Details == null) || (regulatedHours.Details.Count == 0)) { return; } DateTime NowAtDestination = Convert.ToDateTime(this._CustomerConfig.DestinationTimeZoneDisplayName); DateTime ruleStart = DateTime.MinValue; DateTime ruleEnd = DateTime.MinValue; TimeSlot OccupiedSegment = new TimeSlot(model.BayVehicleSensingTimestamp, NowAtDestination); // We need to check if this single occupancy event is an overstay violation for multiple rules, or even for more than one day, etc. while (OccupiedSegment.Start < NowAtDestination) { // Determine the day of week that is involved int dayOfWeek = (int)OccupiedSegment.Start.DayOfWeek; // Loop through the daily rules and see which ones overlap with our occupied period foreach (RegulatedHoursDetail detail in regulatedHours.Details) { // Skip this one if its for a different day of the week if (detail.DayOfWeek != dayOfWeek) { continue; } // Determine if the occupied timeslot overlaps with the rule's timeslot ruleStart = new DateTime(OccupiedSegment.Start.Year, OccupiedSegment.Start.Month, OccupiedSegment.Start.Day, detail.StartTime.Hour, detail.StartTime.Minute, 0); ruleEnd = new DateTime(OccupiedSegment.Start.Year, OccupiedSegment.Start.Month, OccupiedSegment.Start.Day, detail.EndTime.Hour, detail.EndTime.Minute, 59); TimeSlot RuleSegment = new TimeSlot(ruleStart, ruleEnd); // We only care about this overlapping rule if the MaxStayMinutes is greater than zero (zero or less means there is no MaxStay that is enforced), // or if it's explicitly set as a "No Parking" regulation if ((RuleSegment.OverlapsWith(OccupiedSegment) == true) && ((detail.MaxStayMinutes > 0) || (string.Compare(detail.Type, "No Parking", true) == 0)) ) { // Normally we will use the verbatim value of the max stay minutes, but if its a "No Parking", we will always take that to mean 0 minutes is the actual max int timeLimitMinutes = detail.MaxStayMinutes; if (string.Compare(detail.Type, "No Parking", true) == 0) { timeLimitMinutes = 0; } // Get the intersection of the overlaps so we know how long the vehicle has been occupied during this rule TimeSlot OccupiedIntersection = RuleSegment.GetIntersection(OccupiedSegment); // Determine if the vehicle has been occupied during this rule segment in excess of the MaxStayMinutes if (OccupiedIntersection != null) { if (OccupiedIntersection.Duration.TotalMinutes >= timeLimitMinutes) { // We will check to see if this violated regulated period matches the current regulated period. // But since it could be occupied for a long time, we must also check the current date, in addition to day of week and time of day! bool currDetailMatchesCurrentRegulatedPeriod = false; DateTime TodayAtDestination = Convert.ToDateTime(this._CustomerConfig.DestinationTimeZoneDisplayName); //UtilityClasses.TimeZoneInfo.ConvertTimeZoneToTimeZone(DateTime.Now, this._CustomerConfig.ServerTimeZone, this._CustomerConfig.CustomerTimeZone).Date; if (OccupiedSegment.Start >= TodayAtDestination) { if (model.ActiveRegulationPeriod != null) { RegulatedHoursDetailLogicalComparer comparer = new RegulatedHoursDetailLogicalComparer(); currDetailMatchesCurrentRegulatedPeriod = (comparer.Compare(model.ActiveRegulationPeriod, detail) == 0); } } // Create a new Overstay Vio Info object and add to the overall list of violations OverstayViolationInfo overstay = new OverstayViolationInfo(); overstay.IsForCurrentRegulationPeriod = currDetailMatchesCurrentRegulatedPeriod; overstay.Regulation_Date = new DateTime(OccupiedSegment.Start.Year, OccupiedSegment.Start.Month, OccupiedSegment.Start.Day); overstay.Regulation_DayOfWeek = detail.DayOfWeek; overstay.Regulation_EndTime = detail.EndTime; overstay.Regulation_MaxStayMinutes = detail.MaxStayMinutes; // Instead of using our calculated time limit, we will record the configured max stay minutes here, because it will be displayed overstay.Regulation_StartTime = detail.StartTime; overstay.Regulation_Type = detail.Type; overstay.DateTime_StartOfOverstayViolation = new DateTime(OccupiedIntersection.Start.Ticks).AddMinutes(timeLimitMinutes); overstay.DurationOfTimeBeyondStayLimits = new TimeSpan(OccupiedIntersection.Duration.Ticks).Add(new TimeSpan(0, (-1) * timeLimitMinutes, 0)); // Add this overstay info to the model's list model.AllOverstayViolations.Add(overstay); // If its also for the current regulation period, retain a reference to it if (overstay.IsForCurrentRegulationPeriod == true) { model.CurrentOverstayViolation = overstay; } // Mark as "Overstay Violation" status if we have a current overstay violation. Otherwise mark // as "Discretionary" because there is an overstay violation, but not for the current enforcement period if (model.CurrentOverstayViolation != null) { model.BayEnforcementState = EnforcementState.OverstayViolation; } else { model.BayEnforcementState = EnforcementState.Discretionary; } } } } } // Rules for current day of week have been processed. So now we will advance to beginning of next day and see if there are more violations that we will use // to add accumulated time in violation state... OccupiedSegment = new TimeSlot(new DateTime(OccupiedSegment.Start.Year, OccupiedSegment.Start.Month, OccupiedSegment.Start.Day).AddDays(1), NowAtDestination); } }