/// <summary> /// Returns config for schedule next campaign job by default campaign fire period. /// Uses to local time convertion to include daylight offset and reverts next fire time to UTC. /// </summary> /// <param name="schema">Campaign schema. /// Instance of <see cref="Terrasoft.Core.Campaign.CampaignSchema"/>.</param> /// <param name="scheduledTimeParameters">Parameters for schedule new job for campaign.</param> /// <param name="previousStepCalculatedTime">Instance of <see cref="CampaignFireTimeConfig"/> that contains /// information for schedule next campaign job by previous calculation step.</param> /// <returns>Instance of <see cref="CampaignFireTimeConfig"/> that contains /// all needed information for schedule next campaign job by current step.</returns> internal override CampaignFireTimeConfig Calculate(CoreCampaignSchema schema, CampaignScheduledTimeParameters scheduledTimeParameters, CampaignFireTimeConfig previousStepCalculatedTime) { var fireTimeConfig = new CampaignFireTimeConfig { Time = DateTime.MaxValue, ExecutionStrategy = CampaignSchemaExecutionStrategy.DefaultPeriod }; if (scheduledTimeParameters.CampaignStatusId != CampaignConsts.RunCampaignStatusId) { fireTimeConfig.ScheduledAction = CampaignScheduledAction.Start; fireTimeConfig.Time = scheduledTimeParameters.CurrentTime; return(fireTimeConfig); } var localStartDate = TimeZoneInfo.ConvertTimeFromUtc(scheduledTimeParameters.StartDate, schema.TimeZoneOffset); var localBenchmarkTime = TimeZoneInfo.ConvertTimeFromUtc(scheduledTimeParameters.BenchmarkTime, schema.TimeZoneOffset); var campaignLifeTime = localBenchmarkTime - localStartDate; var toPreviousDefault = (int)Math.Round(campaignLifeTime.TotalMinutes) % schema.DefaultCampaignFirePeriod; var nextLocalDefaultTime = toPreviousDefault == 0 && !scheduledTimeParameters.PreviousFireTime.HasValue ? localBenchmarkTime : localBenchmarkTime .AddMinutes(schema.DefaultCampaignFirePeriod - toPreviousDefault); fireTimeConfig.Time = ConvertToUtcByCampaignTimeZone(schema, nextLocalDefaultTime); return(CallNextStep(schema, scheduledTimeParameters, fireTimeConfig)); }
/// <summary> /// Returns config <see cref="CampaignExecutionLatenessConfig"/>, which describes lateness parameters. /// Lateness calculates relatively <paramref name="scheduledTime"/>. /// </summary> /// <param name="schema">Campaign's schema instance of /// <see cref="Terrasoft.Core.Campaign.CampaignSchema"/></param> /// <param name="scheduledTime">Time relatively this need calculate lateness config.</param> /// <returns>Returns instance <see cref="CampaignExecutionLatenessConfig"/>.</returns> public CampaignExecutionLatenessConfig GetLatenessConfig(CoreCampaignSchema schema, DateTime scheduledTime) { schema.CampaignConfiguration["ScheduledUtcFireTime"] = RoundToSeconds(scheduledTime); var latenessTime = RoundToMinutes(CurrentTime - scheduledTime); var criticalLateness = schema.CriticalExecutionLateness; var latenessConfig = new CampaignExecutionLatenessConfig { MisfiredTimeConditionElements = new List <CampaignSchemaElement>(), LatenessTime = latenessTime, Lateness = CampaignExecutionLateness.NoMisfire }; if (latenessTime.TotalMinutes <= 0) { return(latenessConfig); } var misfiredTimedElements = GetMisfiredTimedElements(schema, scheduledTime, CurrentTime); latenessConfig.MisfiredTimeConditionElements = misfiredTimedElements; if (latenessTime.TotalMinutes <= criticalLateness) { latenessConfig.Lateness = !misfiredTimedElements.Any() ? CampaignExecutionLateness.NoMisfire : CampaignExecutionLateness.MisfiredTimeConditionElements; } else { latenessConfig.Lateness = !misfiredTimedElements.Any() ? CampaignExecutionLateness.Critical : CampaignExecutionLateness.CriticalAndMisfiredTimeConditionElements; } return(latenessConfig); }
/// <summary> /// Converts local date and time to UTC based on current campaign time zone. /// If date is not valid (for example 26.03.2017 03:00-04:00 for Kiev GMT+02:00) /// Shifts it to the future by campaign default period. /// </summary> /// <param name="schema">Campaign schema. /// Instance of <see cref="Terrasoft.Core.Campaign.CampaignSchema"/>.</param> /// <param name="localTime">Local <see cref="System.DateTime"/> date and time to convert to UTC.</param> /// <returns>UTC <see cref="System.DateTime"/> with daylight offset.</returns> private DateTime ConvertToUtcByCampaignTimeZone(CoreCampaignSchema schema, DateTime localTime) { if (localTime.Kind == DateTimeKind.Utc) { return(localTime); } var defaultPeriodAddingCount = 100; if (schema.TimeZoneOffset.IsInvalidTime(localTime)) { for (int i = 0; i < defaultPeriodAddingCount; i++) { localTime = localTime.AddMinutes(schema.DefaultCampaignFirePeriod); if (!schema.TimeZoneOffset.IsInvalidTime(localTime)) { break; } } if (schema.TimeZoneOffset.IsInvalidTime(localTime)) { var message = CampaignHelper .GetLczStringValue(nameof(CampaignTimeScheduler), "IsInvalidTimeOutOfRangeIterationError"); throw new Exception(message); } } return(TimeZoneInfo.ConvertTimeToUtc(localTime, schema.TimeZoneOffset)); }
/// <summary> /// Reschedule job for the specified campaign. /// </summary> /// <param name="schema">The <see cref="Terrasoft.Core.Campaign.CampaignSchema"/> object for wich Job plans.</param> /// <param name="fireTimeConfig">Fire time config for job.</param> public void RescheduleJob(CoreCampaignSchema schema, CampaignFireTimeConfig fireTimeConfig) { lock (_rescheduleLockObject) { RemoveJobs(schema.EntityId); ScheduleJob(schema, fireTimeConfig); } }
/// <summary> /// Schedule next job for the specified campaign. /// </summary> /// <param name="schema">The <see cref="Terrasoft.Core.Campaign.CampaignSchema"/> object for wich Job plans.</param> /// <param name="fireTimeConfig">Fire time config for job.</param> public void ScheduleJob(CoreCampaignSchema schema, CampaignFireTimeConfig fireTimeConfig) { try { schema.CheckArgumentNull(nameof(schema)); fireTimeConfig.CheckArgumentNull(nameof(fireTimeConfig)); Guid campaignId = schema.EntityId; var campaignJobName = GetCampaignJobName(campaignId, fireTimeConfig.Time); var sysUserConnection = _userConnection.AppConnection.SystemUserConnection; var parameters = new Dictionary <string, object> { { "CampaignSchemaUId", schema.UId }, { "ScheduledUtcFireTime", fireTimeConfig.Time }, { "SchemaGeneratorStrategy", fireTimeConfig.ExecutionStrategy }, { "ScheduledAction", (int)fireTimeConfig.ScheduledAction } }; IJobDetail job = AppScheduler.CreateClassJob <CampaignJobExecutor>(campaignJobName, CampaignConsts.CampaignJobGroupName, sysUserConnection, parameters); ITrigger trigger = new SimpleTriggerImpl(campaignJobName + "Trigger", CampaignConsts.CampaignJobGroupName, fireTimeConfig.Time) { MisfireInstruction = MisfireInstruction.IgnoreMisfirePolicy }; AppScheduler.Instance.ScheduleJob(job, trigger); LogScheduledJob(schema.EntityId, fireTimeConfig); } catch (Exception ex) { string message = CampaignHelper.GetLczStringValue(nameof(CampaignJobDispatcher), "ScheduleException"); Logger.ErrorFormat(message, ex, schema == null ? Guid.Empty : schema.EntityId); throw; } }
/// <summary> /// Calls next step in chain and returns result from this step. /// </summary> /// <param name="schema">Campaign schema. /// Instance of <see cref="Terrasoft.Core.Campaign.CampaignSchema"/></param> /// <param name="scheduledTimeParameters">Parameters which describes campaign state /// and includes parameters for calculates next fire time.</param> /// <param name="previousStepCalculatedTime"><see cref="CampaignFireTimeConfig"/> containes /// current step calculation result.</param> /// <returns>Returns <paramref name="previousStepCalculatedTime"/> when <see cref="NextCalculator"/> /// is not defined. And result of calculation from next step in otherwise.</returns> protected CampaignFireTimeConfig CallNextStep(CoreCampaignSchema schema, CampaignScheduledTimeParameters scheduledTimeParameters, CampaignFireTimeConfig previousStepCalculatedTime) { return(NextCalculator == null ? previousStepCalculatedTime : NextCalculator.Calculate(schema, scheduledTimeParameters, previousStepCalculatedTime)); }
private IEnumerable <CampaignSchemaElement> GetMisfiredTimedElements(CoreCampaignSchema schema, DateTime startTime, DateTime endTime) { var transitionElements = schema.FlowElements.OfType <ConditionalSequenceFlowElement>(); var timerElements = schema.FlowElements.OfType <CampaignTimerElement>(); return(GetMisfiredTimedElements(transitionElements.Union <CampaignSchemaElement>(timerElements), startTime, endTime)); }
internal override CampaignFireTimeConfig Calculate(CoreCampaignSchema schema, CampaignScheduledTimeParameters scheduledTimeParameters, CampaignFireTimeConfig previousStepCalculatedTime) { var timerElements = schema.FlowElements.OfType <CampaignTimerElement>(); CampaignFireTimeConfig fireTimeConfig = GetTimedElementsNextFireTime(timerElements, scheduledTimeParameters); return(CallNextStepWithMinValue(schema, scheduledTimeParameters, previousStepCalculatedTime, fireTimeConfig)); }
/// <summary> /// Returns next fire time for campaign's schema. /// </summary> /// <param name="schema">Campaign schema. /// Instance of <see cref="Terrasoft.Core.Campaign.CampaignSchema"/></param> /// <param name="previousScheduledFireTime">Time of the previous campaign run. /// It can be null when need calculate next fire time relatively current time.</param> /// <returns>Returns instance of <see cref="CampaignFireTimeConfig"/> that contains /// all needed information for schedule next campaign job.</returns> public CampaignFireTimeConfig GetNextFireTime(CoreCampaignSchema schema, DateTime?previousScheduledFireTime) { var scheduledParameters = GetScheduledParameters(schema.EntityId); DefinesTimeParameters(schema, previousScheduledFireTime, scheduledParameters); var resultConfig = TimeCalculator.Calculate(schema, scheduledParameters, new CampaignFireTimeConfig()); resultConfig.Time = DateTime.SpecifyKind(RoundToSeconds(resultConfig.Time), DateTimeKind.Utc); return(resultConfig); }
private void DefinesTimeParameters(CoreCampaignSchema schema, DateTime?previousScheduledFireTime, CampaignScheduledTimeParameters scheduledParameters) { scheduledParameters.PreviousFireTime = previousScheduledFireTime ?? (scheduledParameters.PrevExecutedOn != default(DateTime) ? scheduledParameters.PrevExecutedOn : (DateTime?)null); scheduledParameters.CurrentTime = CurrentTime; scheduledParameters.BenchmarkTime = scheduledParameters.PreviousFireTime ?? scheduledParameters.CurrentTime; schema.CampaignConfiguration["ScheduledUtcFireTime"] = RoundToSeconds(scheduledParameters.BenchmarkTime); }
internal override CampaignFireTimeConfig Calculate(CoreCampaignSchema schema, CampaignScheduledTimeParameters scheduledTimeParameters, CampaignFireTimeConfig previousStepCalculatedTime) { var elements = schema.FlowElements .Where(x => x.ElementType.HasFlag(CampaignSchemaElementType.Transition)); var fireTimeConfig = GetTimedElementsNextFireTime(elements, scheduledTimeParameters); return(CallNextStepWithMinValue(schema, scheduledTimeParameters, previousStepCalculatedTime, fireTimeConfig)); }
/// <summary> /// Calls next step in chain with min value between <paramref name="previousStepCalculatedTime"/> and /// <paramref name="comparedCalculatedTime"/> and returns result from calculates next step. /// </summary> /// <param name="schema">Campaign schema. /// Instance of <see cref="Terrasoft.Core.Campaign.CampaignSchema"/></param> /// <param name="scheduledTimeParameters">Parameters which describes campaign state /// and includes parameters for calculates next fire time.</param> /// <param name="previousStepCalculatedTime"><see cref="CampaignFireTimeConfig"/> containes /// current step calculation result.</param> /// <param name="comparedCalculatedTime">This config should compare with /// <paramref name="previousStepCalculatedTime"/> and with minimum of this calls /// <see cref="CallNextStep(CoreCampaignSchema, CampaignScheduledTimeParameters, CampaignFireTimeConfig)"/> /// method.</param> /// <returns>Returns <paramref name="previousStepCalculatedTime"/> when <see cref="NextCalculator"/> /// is not defined. And result of calculation from next step in otherwise.</returns> protected CampaignFireTimeConfig CallNextStepWithMinValue(CoreCampaignSchema schema, CampaignScheduledTimeParameters scheduledTimeParameters, CampaignFireTimeConfig previousStepCalculatedTime, CampaignFireTimeConfig comparedCalculatedTime) { var minFireTime = comparedCalculatedTime.Time < previousStepCalculatedTime.Time && comparedCalculatedTime.Time != default(DateTime) ? comparedCalculatedTime : previousStepCalculatedTime; return(CallNextStep(schema, scheduledTimeParameters, minFireTime)); }
/// <summary> /// Schedule next job for the specified campaign. /// </summary> /// <param name="campaignSchemaUId">The unique identifier of campaign schema instance.</param> /// <param name="fireTimeConfig">Fire time config for job.</param> public void ScheduleJob(Guid campaignSchemaUId, CampaignFireTimeConfig fireTimeConfig) { try { var schemaManager = (CampaignSchemaManager)_userConnection.GetSchemaManager("CampaignSchemaManager"); CoreCampaignSchema campaignSchema = schemaManager.GetSchemaInstance(campaignSchemaUId); ScheduleJob(campaignSchema, fireTimeConfig); } catch (Exception ex) { string message = CampaignHelper.GetLczStringValue(nameof(CampaignJobDispatcher), "ScheduleBySchemaUIdException"); Logger.ErrorFormat(message, ex, campaignSchemaUId); throw; } }
/// <summary> /// Returns next fire time for campaign. /// When <paramref name="previousScheduledFireTime"/> is null then used /// <see cref="DateTime.UtcNow"/> as benchmark point for calculation. /// </summary> /// <param name="campaignId">Campaign identifier.</param> /// <param name="useCurrentUserTimezone">Flag that indicates add current user /// timezone offset to next campaign scheduled time(in UTC) or not.</param> /// <param name="previousScheduledFireTime">Time of the previous campaign run. /// It can be null when need calculate next fire time relatively current time.</param> /// <returns>Returns campaign's next run time.</returns> public DateTime GetCampaignNextFireTime(Guid campaignId, bool useCurrentUserTimezone, DateTime?previousScheduledFireTime = null) { var schemaUid = GetCampaignSchemaUid(campaignId); var benchmarkTime = previousScheduledFireTime ?? new DateTime(CurrentTime.Year, CurrentTime.Month, CurrentTime.Day, CurrentTime.Hour, CurrentTime.Minute, 0, DateTimeKind.Utc); var schemaManager = (CampaignSchemaManager)_userConnection.GetSchemaManager("CampaignSchemaManager"); CoreCampaignSchema campaignSchema = schemaManager.GetSchemaInstance(schemaUid); var scheduledUtcFireTime = GetNextFireTime(campaignSchema, benchmarkTime); if (useCurrentUserTimezone) { return(scheduledUtcFireTime.Time.AddMinutes(UserTimeZoneOffset.TotalMinutes)); } return(scheduledUtcFireTime.Time); }
internal override CampaignFireTimeConfig Calculate(CoreCampaignSchema schema, CampaignScheduledTimeParameters scheduledTimeParameters, CampaignFireTimeConfig previousStepCalculatedTime) { var fireTimeConfig = previousStepCalculatedTime; if (scheduledTimeParameters.ScheduledStopModeId == CampaignConsts.CampaingSpecifiedTimeModeId && previousStepCalculatedTime.Time >= scheduledTimeParameters.ScheduledStopDate) { fireTimeConfig = new CampaignFireTimeConfig { Time = new DateTime(scheduledTimeParameters.ScheduledStopDate.Ticks, DateTimeKind.Utc), ExecutionStrategy = CampaignSchemaExecutionStrategy.DefaultPeriod, ScheduledAction = CampaignScheduledAction.ScheduledStop }; } return(CallNextStep(schema, scheduledTimeParameters, fireTimeConfig)); }
internal override CampaignFireTimeConfig Calculate(CoreCampaignSchema schema, CampaignScheduledTimeParameters scheduledTimeParameters, CampaignFireTimeConfig previousStepCalculatedTime) { if (scheduledTimeParameters.CampaignStatusId == CampaignConsts.StoppingCampaignStatusId) { return(new CampaignFireTimeConfig { Time = scheduledTimeParameters.CurrentTime, ScheduledAction = CampaignScheduledAction.Stop, ExecutionStrategy = CampaignSchemaExecutionStrategy.DefaultPeriod }); } if (scheduledTimeParameters.ScheduledStopModeId == CampaignConsts.CampaingSpecifiedTimeModeId && scheduledTimeParameters.CurrentTime >= scheduledTimeParameters.ScheduledStopDate) { return(new CampaignFireTimeConfig { Time = scheduledTimeParameters.ScheduledStopDate, ScheduledAction = CampaignScheduledAction.ScheduledStop, ExecutionStrategy = CampaignSchemaExecutionStrategy.DefaultPeriod }); } return(CallNextStep(schema, scheduledTimeParameters, previousStepCalculatedTime)); }
internal virtual CampaignFireTimeConfig Calculate(CoreCampaignSchema schema, CampaignScheduledTimeParameters scheduledTimeParameters, CampaignFireTimeConfig previousStepCalculatedTime) { return(new CampaignFireTimeConfig()); }