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); } }