internal static string FormatNextOccurrences(TimerSchedule schedule, int count, DateTime?now = null, string functionShortName = null) { if (schedule == null) { throw new ArgumentNullException("schedule"); } // If we've got a timer name, format it if (functionShortName != null) { functionShortName = $"'{functionShortName}' "; } bool isUtc = TimeZoneInfo.Local.HasSameRules(TimeZoneInfo.Utc); IEnumerable <DateTime> nextOccurrences = schedule.GetNextOccurrences(count, now); StringBuilder builder = new StringBuilder(); builder.AppendLine($"The next {count} occurrences of the {functionShortName}schedule ({schedule}) will be:"); foreach (DateTime occurrence in nextOccurrences) { if (isUtc) { builder.AppendLine(occurrence.ToUniversalTime().ToString(DateTimeFormat)); } else { TimeSpan offset = TimeZoneInfo.Local.GetUtcOffset(occurrence); builder.AppendLine($"{occurrence.ToString(DateTimeFormat)} ({occurrence.ToUniversalTime().ToString(DateTimeFormat)})"); } } return(builder.ToString()); }
public void Create_ConstantSchedule_CreatesExpectedSchedule() { TimerTriggerAttribute attribute = new TimerTriggerAttribute("00:00:15"); INameResolver nameResolver = new TestNameResolver(); ConstantSchedule schedule = (ConstantSchedule)TimerSchedule.Create(attribute, nameResolver, _logger); Assert.False(attribute.UseMonitor); var log = _loggerProvider.GetAllLogMessages().Single(); Assert.Equal("UseMonitor changed to false based on schedule frequency.", log.FormattedMessage); Assert.Equal(LogLevel.Debug, log.Level); DateTime now = new DateTime(2015, 5, 22, 9, 45, 00); DateTime nextOccurrence = schedule.GetNextOccurrence(now); Assert.Equal(new TimeSpan(0, 0, 15), nextOccurrence - now); // For schedules occuring on an interval greater than a minute, we expect // UseMonitor to be defaulted to true _loggerProvider.ClearAllLogMessages(); attribute = new TimerTriggerAttribute("01:00:00"); schedule = (ConstantSchedule)TimerSchedule.Create(attribute, nameResolver, _logger); Assert.True(attribute.UseMonitor); Assert.Empty(_loggerProvider.GetAllLogMessages()); // verify that if UseMonitor is set explicitly, it is not overridden attribute = new TimerTriggerAttribute("01:00:00"); attribute.UseMonitor = false; schedule = (ConstantSchedule)TimerSchedule.Create(attribute, nameResolver, _logger); Assert.False(attribute.UseMonitor); Assert.Empty(_loggerProvider.GetAllLogMessages()); }
internal static string FormatNextOccurrences(TimerSchedule schedule, int count, DateTime?now = null) { if (schedule == null) { throw new ArgumentNullException("schedule"); } bool isUtc = TimeZoneInfo.Local.HasSameRules(TimeZoneInfo.Utc); IEnumerable <DateTime> nextOccurrences = schedule.GetNextOccurrences(count, now); StringBuilder builder = new StringBuilder(); foreach (DateTime occurrence in nextOccurrences) { if (isUtc) { builder.AppendLine(occurrence.ToUniversalTime().ToString(DateTimeFormat)); } else { TimeSpan offset = TimeZoneInfo.Local.GetUtcOffset(occurrence); builder.AppendLine($"{occurrence.ToString(DateTimeFormat)} ({occurrence.ToUniversalTime().ToString(DateTimeFormat)})"); } } return(builder.ToString()); }
public void Create_ConstantSchedule_CreatesExpectedSchedule() { TimerTriggerAttribute attribute = new TimerTriggerAttribute("00:00:15"); INameResolver nameResolver = new TestNameResolver(); ConstantSchedule schedule = (ConstantSchedule)TimerSchedule.Create(attribute, nameResolver); Assert.False(attribute.UseMonitor); DateTime now = new DateTime(2015, 5, 22, 9, 45, 00); DateTime nextOccurrence = schedule.GetNextOccurrence(now); Assert.Equal(new TimeSpan(0, 0, 15), nextOccurrence - now); // For schedules occuring on an interval greater than a minute, we expect // UseMonitor to be defaulted to true attribute = new TimerTriggerAttribute("01:00:00"); schedule = (ConstantSchedule)TimerSchedule.Create(attribute, nameResolver); Assert.True(attribute.UseMonitor); // verify that if UseMonitor is set explicitly, it is not overridden attribute = new TimerTriggerAttribute("01:00:00"); attribute.UseMonitor = false; schedule = (ConstantSchedule)TimerSchedule.Create(attribute, nameResolver); Assert.False(attribute.UseMonitor); }
public void Create_InvalidSchedule() { TimerTriggerAttribute attribute = new TimerTriggerAttribute("invalid"); ArgumentException ex = Assert.Throws <ArgumentException>(() => { TimerSchedule.Create(attribute, new TestNameResolver()); }); Assert.Equal("The schedule expression 'invalid' was not recognized as a valid cron expression or timespan string.", ex.Message); }
public TimerListener(TimerTriggerAttribute attribute, TimerSchedule schedule, string timerName, TimersOptions options, ITriggeredFunctionExecutor executor, ILogger logger, ScheduleMonitor scheduleMonitor) { _attribute = attribute; _timerName = timerName; _options = options; _executor = executor; _logger = logger; _cancellationTokenSource = new CancellationTokenSource(); _schedule = schedule; ScheduleMonitor = _attribute.UseMonitor ? scheduleMonitor : null; }
public TimerListener(TimerTriggerAttribute attribute, string timerName, TimersConfiguration config, ITriggeredFunctionExecutor executor) { _attribute = attribute; _timerName = timerName; _config = config; _executor = executor; _cancellationTokenSource = new CancellationTokenSource(); _schedule = _attribute.Schedule; _scheduleMonitor = _config.ScheduleMonitor; }
public TimerListener(TimerTriggerAttribute attribute, string timerName, TimersConfiguration config, ITriggeredFunctionExecutor executor, TraceWriter trace) { _attribute = attribute; _timerName = timerName; _config = config; _executor = executor; _trace = trace; _cancellationTokenSource = new CancellationTokenSource(); _schedule = _attribute.Schedule; ScheduleMonitor = _attribute.UseMonitor ? _config.ScheduleMonitor : null; }
public override void Setup() { TimerSchedule = Substitute.For <TimerSchedule>(); CommonService = Substitute.For <ICommonService>(); Logger = Substitute.For <ILogger <IPersonalLearningRecordService> >(); PersonalLearningRecordService = Substitute.For <IPersonalLearningRecordService>(); var mapperConfig = new MapperConfiguration(c => c.AddMaps(typeof(Startup).Assembly)); Mapper = new AutoMapper.Mapper(mapperConfig); LearningEventsFunction = new Functions.LearnerVerificationAndLearningEvents(CommonService, PersonalLearningRecordService); }
public override void Setup() { TimerSchedule = Substitute.For <TimerSchedule>(); CommonService = Substitute.For <ICommonService>(); Logger = Substitute.For <ILogger <ILearnerService> >(); LearnerService = Substitute.For <ILearnerService>(); var mapperConfig = new MapperConfiguration(c => c.AddMaps(typeof(Startup).Assembly)); Mapper = new AutoMapper.Mapper(mapperConfig); LearnerGenderFunction = new Functions.LearnerGender(CommonService, LearnerService); }
public TimerTriggerBinding(ParameterInfo parameter, TimerTriggerAttribute attribute, TimerSchedule schedule, TimersConfiguration config, TraceWriter trace) { _attribute = attribute; _schedule = schedule; _parameter = parameter; _config = config; _trace = trace; _bindingContract = CreateBindingDataContract(); MethodInfo methodInfo = (MethodInfo)parameter.Member; _timerName = string.Format("{0}.{1}", methodInfo.DeclaringType.FullName, methodInfo.Name); }
public TimerTriggerBinding(ParameterInfo parameter, TimerTriggerAttribute attribute, TimerSchedule schedule, TimersOptions options, ILogger logger, ScheduleMonitor scheduleMonitor) { _attribute = attribute; _schedule = schedule; _parameter = parameter; _options = options; _logger = logger; _scheduleMonitor = scheduleMonitor; _bindingContract = CreateBindingDataContract(); MethodInfo methodInfo = (MethodInfo)parameter.Member; _timerName = string.Format("{0}.{1}", methodInfo.DeclaringType.FullName, methodInfo.Name); }
public void Create_CustomSchedule_CreatesExpectedSchedule() { TimerTriggerAttribute attribute = new TimerTriggerAttribute(typeof(CustomSchedule)); INameResolver nameResolver = new TestNameResolver(); CustomSchedule schedule = (CustomSchedule)TimerSchedule.Create(attribute, nameResolver); Assert.NotNull(schedule); Assert.True(attribute.UseMonitor); // verify that if UseMonitor is set explicitly, it is not overridden attribute = new TimerTriggerAttribute(typeof(CustomSchedule)); attribute.UseMonitor = false; schedule = (CustomSchedule)TimerSchedule.Create(attribute, nameResolver); Assert.False(attribute.UseMonitor); }
public void Create_UsesNameResolver() { TimerTriggerAttribute attribute = new TimerTriggerAttribute("%test_schedule%"); TestNameResolver nameResolver = new TestNameResolver(); nameResolver.Values.Add("test_schedule", "*/15 * * * * *"); CronSchedule schedule = (CronSchedule)TimerSchedule.Create(attribute, nameResolver); Assert.False(attribute.UseMonitor); DateTime now = new DateTime(2015, 5, 22, 9, 45, 00); DateTime nextOccurrence = schedule.GetNextOccurrence(now); Assert.Equal(new TimeSpan(0, 0, 15), nextOccurrence - now); }
public void ShouldDisableScheduleMonitor_ReturnsExpectedValue(string schedule, string nowTimestamp, bool expected) { DateTime now; if (!string.IsNullOrEmpty(nowTimestamp)) { now = DateTime.Parse(nowTimestamp); } else { now = DateTime.Now; } CronSchedule.TryCreate(schedule, out CronSchedule cronSchedule); Assert.Equal(expected, TimerSchedule.ShouldDisableScheduleMonitor(cronSchedule, now)); }
internal static string FormatNextOccurrences(TimerSchedule schedule, int count, DateTime?now = null) { if (schedule == null) { throw new ArgumentNullException("schedule"); } IEnumerable <DateTime> nextOccurrences = schedule.GetNextOccurrences(count, now); StringBuilder builder = new StringBuilder(); builder.AppendLine(string.Format("The next {0} occurrences of the schedule will be:", count)); foreach (DateTime occurrence in nextOccurrences) { builder.AppendLine(occurrence.ToString()); } return(builder.ToString()); }
private void VerifyConstantSchedule(string expression, TimeSpan expectedInterval) { Assert.True(TimeSpan.TryParse(expression, out TimeSpan timeSpan)); Assert.Equal(timeSpan, expectedInterval); TimerTriggerAttribute attribute = new TimerTriggerAttribute(expression); INameResolver nameResolver = new TestNameResolver(); ConstantSchedule schedule = (ConstantSchedule)TimerSchedule.Create(attribute, nameResolver, _logger); DateTime now = new DateTime(2015, 5, 22, 9, 45, 00); var occurrences = schedule.GetNextOccurrences(5, now); for (int i = 0; i < 4; i++) { var delta = occurrences.ElementAt(i + 1) - occurrences.ElementAt(i); Assert.Equal(expectedInterval, delta); } }
public void GetNextInterval_NextWithinDST_ReturnsExpectedValue(TimerSchedule schedule, TimeSpan expectedInterval) { // This only works with a DST-supported time zone, so throw a nice exception if (!TimeZoneInfo.Local.SupportsDaylightSavingTime) { throw new InvalidOperationException("This test will only pass if the time zone supports DST."); } // Running at 1:59 AM, i.e. one minute before the DST switch at 2 AM on 3/11 (Pacific Standard Time) // Note: this test uses Local time, so if you're running in a timezone where // DST doesn't transition the test might not be valid. var now = new DateTime(2018, 3, 11, 1, 59, 0, DateTimeKind.Local); var next = schedule.GetNextOccurrence(now); var interval = TimerListener.GetNextTimerInterval(next, now, schedule.AdjustForDST); Assert.Equal(expectedInterval, interval); }
private void CreateTestListener(string expression, bool useMonitor = true, Action functionAction = null) { _attribute = new TimerTriggerAttribute(expression); _schedule = TimerSchedule.Create(_attribute, new TestNameResolver()); _attribute.UseMonitor = useMonitor; _options = new TimersOptions(); _mockScheduleMonitor = new Mock <ScheduleMonitor>(MockBehavior.Strict); _mockTriggerExecutor = new Mock <ITriggeredFunctionExecutor>(MockBehavior.Strict); FunctionResult result = new FunctionResult(true); _mockTriggerExecutor.Setup(p => p.TryExecuteAsync(It.IsAny <TriggeredFunctionData>(), It.IsAny <CancellationToken>())) .Callback <TriggeredFunctionData, CancellationToken>((mockFunctionData, mockToken) => { _triggeredFunctionData = mockFunctionData; functionAction?.Invoke(); }) .Returns(Task.FromResult(result)); _logger = new TestLogger(null); _listener = new TimerListener(_attribute, _schedule, _testTimerName, _options, _mockTriggerExecutor.Object, _logger, _mockScheduleMonitor.Object); }
public void GetNextInterval_NextAfterDST_ReturnsExpectedValue(TimerSchedule schedule, TimeSpan expectedInterval) { // This only works with a DST-supported time zone, so throw a nice exception if (!TimeZoneInfo.Local.SupportsDaylightSavingTime) { throw new InvalidOperationException("This test will only pass if the time zone supports DST."); } // Running on the Friday before the DST switch at 2 AM on 3/11 (Pacific Standard Time) // Note: this test uses Local time, so if you're running in a timezone where // DST doesn't transition the test might not be valid. // The input schedules will run after DST changes. For some (Cron), they will subtract // an hour to account for the shift. For others (Constant), they will not. var now = new DateTime(2018, 3, 9, 18, 0, 0, DateTimeKind.Local); var next = schedule.GetNextOccurrence(now); var interval = TimerListener.GetNextTimerInterval(next, now, schedule.AdjustForDST); // One week is normally 168 hours, but it's 167 hours across DST Assert.Equal(interval, expectedInterval); }
public async Task BindAsync_ReturnsExpectedTriggerData() { ParameterInfo parameter = GetType().GetMethod("TestTimerJob").GetParameters()[0]; MethodInfo methodInfo = (MethodInfo)parameter.Member; string timerName = string.Format("{0}.{1}", methodInfo.DeclaringType.FullName, methodInfo.Name); Mock <ScheduleMonitor> mockScheduleMonitor = new Mock <ScheduleMonitor>(MockBehavior.Strict); ScheduleStatus status = new ScheduleStatus(); mockScheduleMonitor.Setup(p => p.GetStatusAsync(timerName)).ReturnsAsync(status); TimerTriggerAttribute attribute = parameter.GetCustomAttribute <TimerTriggerAttribute>(); INameResolver nameResolver = new TestNameResolver(); TimerSchedule schedule = TimerSchedule.Create(attribute, nameResolver); TimersConfiguration config = new TimersConfiguration(); config.ScheduleMonitor = mockScheduleMonitor.Object; ILoggerFactory loggerFactory = new LoggerFactory(); loggerFactory.AddProvider(new TestLoggerProvider()); TimerTriggerBinding binding = new TimerTriggerBinding(parameter, attribute, schedule, config, loggerFactory.CreateLogger("Test")); // when we bind to a non-TimerInfo (e.g. in a Dashboard invocation) a new // TimerInfo is created, with the ScheduleStatus populated FunctionBindingContext functionContext = new FunctionBindingContext(Guid.NewGuid(), CancellationToken.None); ValueBindingContext context = new ValueBindingContext(functionContext, CancellationToken.None); TriggerData triggerData = (TriggerData)(await binding.BindAsync(string.Empty, context)); TimerInfo timerInfo = (TimerInfo)(await triggerData.ValueProvider.GetValueAsync()); Assert.Same(status, timerInfo.ScheduleStatus); // when we pass in a TimerInfo that is used TimerInfo expected = new TimerInfo(schedule, status); triggerData = (TriggerData)(await binding.BindAsync(expected, context)); timerInfo = (TimerInfo)(await triggerData.ValueProvider.GetValueAsync()); Assert.Same(expected, timerInfo); }
private void CreateTestListener(string expression, bool useMonitor = true) { _attribute = new TimerTriggerAttribute(expression); _schedule = TimerSchedule.Create(_attribute, new TestNameResolver()); _attribute.UseMonitor = useMonitor; _config = new TimersConfiguration(); _mockScheduleMonitor = new Mock <ScheduleMonitor>(MockBehavior.Strict); _config.ScheduleMonitor = _mockScheduleMonitor.Object; _mockTriggerExecutor = new Mock <ITriggeredFunctionExecutor>(MockBehavior.Strict); FunctionResult result = new FunctionResult(true); _mockTriggerExecutor.Setup(p => p.TryExecuteAsync(It.IsAny <TriggeredFunctionData>(), It.IsAny <CancellationToken>())) .Callback <TriggeredFunctionData, CancellationToken>((mockFunctionData, mockToken) => { _triggeredFunctionData = mockFunctionData; }) .Returns(Task.FromResult(result)); JobHostConfiguration hostConfig = new JobHostConfiguration(); hostConfig.HostId = "testhostid"; _listener = new TimerListener(_attribute, _schedule, _testTimerName, _config, _mockTriggerExecutor.Object, new TestTraceWriter()); }
public Task <ITriggerBinding> TryCreateAsync(TriggerBindingProviderContext context) { if (context == null) { throw new ArgumentNullException("context"); } ParameterInfo parameter = context.Parameter; TimerTriggerAttribute timerTriggerAttribute = parameter.GetCustomAttribute <TimerTriggerAttribute>(inherit: false); if (timerTriggerAttribute == null) { return(Task.FromResult <ITriggerBinding>(null)); } if (parameter.ParameterType != typeof(TimerInfo)) { throw new InvalidOperationException(string.Format("Can't bind TimerTriggerAttribute to type '{0}'.", parameter.ParameterType)); } TimerSchedule schedule = TimerSchedule.Create(timerTriggerAttribute, _nameResolver, _logger); return(Task.FromResult <ITriggerBinding>(new TimerTriggerBinding(parameter, timerTriggerAttribute, schedule, _options, _logger, _scheduleMonitor))); }
public async Task <ResponseData <ApplianceDTO> > SetupThink([FromBody] ApplianceDTO appliance) { try { var response = new ResponseData <ApplianceDTO>(); var things = await TelitApi.ThingList(); bool isMatching = false; bool hasSerialNumber = false; Telit.ThingList.Result thing = new Telit.ThingList.Result(); if (things != null && things.things != null && things.things.success && things.things.@params != null && [email protected] != null && [email protected]() > 0) { foreach (var item in [email protected]) { if (item.key == appliance.SerialNumber) { hasSerialNumber = true; if (item.loc != null && item.loc.addr != null) { isMatching = await calculateAddressAsync(appliance.Street1.Trim(), appliance.City.Trim(), _stateService.GetStateById(appliance.StateId.Value).Name, appliance.ZipCode.Trim(), item.loc.lat, item.loc.lng); thing = item; break; } } } } if (!hasSerialNumber) { response.Message = ResponseMessage.SerialNumberInCorrect; response.Status = ResponseStatus.Success.ToString(); return(response); } if (isMatching) { var isSerialNumberExist = _applianceService.GetApplianceBySerialNumber(appliance.SerialNumber); var thingFind = await TelitApi.ThingFind(thing.key); bool thingIsOn = false; bool thingIsConnected = false; bool thingCellular = false; bool thingPower = false; bool thingWifi = false; bool thingWifiInternet = false; int? thingTrustLevel = null; Telit.ThingFind.Params _params = new Telit.ThingFind.Params(); string applianceEnvironment = string.Empty; if (thingFind != null && thingFind.Things != null && thingFind.Things.success && thingFind.Things.@params != null) { _params = thingFind.Things.@params; } if (_params != null && _params.alarms != null && _params.alarms.env != null && _params.alarms.env.state >= 0 && _params.alarms.env.state < 16) { applianceEnvironment = Convert.ToString(_params.alarms.env.state, 2).PadLeft(4, '0'); var array = !string.IsNullOrEmpty(applianceEnvironment) ? applianceEnvironment.ToArray() : new char[] { }; thingIsOn = Convert.ToBoolean(_params.alarms.on.state); thingIsConnected = _params.connected; thingCellular = array.Length == 4 ? Convert.ToBoolean(Convert.ToInt16(array[2].ToString())) : false; thingPower = array.Length == 4 ? Convert.ToBoolean(Convert.ToInt16(array[3].ToString())) : false; thingWifi = array.Length == 4 ? Convert.ToBoolean(Convert.ToInt16(array[1].ToString())) : false; thingWifiInternet = array.Length == 4 ? Convert.ToBoolean(Convert.ToInt16(array[0].ToString())) : false; thingTrustLevel = _params.alarms.trust != null ? (int?)_params.alarms.trust.state : null; } if (isSerialNumberExist == null) { var app = _applianceService.Insert(new Appliance() { AccountId = appliance.AccountId, Cellular = thingCellular, IsConnected = thingIsConnected, City = appliance.City, DeviceName = _params != null && _params.attrs != null && _params.attrs.name != null ? _params.attrs.name.value : string.Empty, OsVersion = "", GeoFenceEnabled = _params.alarms != null && _params.alarms.timerState != null && _params.alarms.timerState.state != 0 ? true : false, Lat = _params.loc != null ? _params.loc.lat : 0, Lon = _params.loc != null ? _params.loc.lng : 0, SerialNumber = appliance.SerialNumber, Power = thingPower, StateId = appliance.StateId, Street1 = appliance.Street1, Street2 = appliance.Street2, TimerEnabled = _params.alarms != null && _params.alarms.timerState != null && _params.alarms.timerState.state != 0 ? true : false, TriggerMile = Convert.ToDecimal(_appSettings.TriggerMiles), Wifi = thingWifi, WiFiInternet = thingWifiInternet, ZipCode = appliance.ZipCode, TrustLevel = thingTrustLevel != null ? thingTrustLevel : 0, IsOn = thingIsOn }); if (app != null) { var accountAppliance = _accountApplianceService.Insert(new AccountAppliance() { AccountId = app.AccountId, AirGapVersion = "", ApplianceId = app.Id, IsQRCodeScaned = false, IsVerified = false }); appliance.Id = app.Id; if (_params.attrs != null && _params.attrs.timerSchedule != null) { var timerScheduleDTO = JsonConvert.DeserializeObject <TimerScheduleFromTelit>(_params.attrs.timerSchedule.value); char[] weekDayArray = timerScheduleDTO.Weekdays.ToCharArray(); char[] weekEndArray = timerScheduleDTO.Weekends.ToCharArray(); string valueWeekDay = string.Empty; string valueWeekEnd = string.Empty; for (int i = 0; i < weekDayArray.Length; i++) { if (weekDayArray[i] == '1') { valueWeekDay += i + 1 + ","; } } for (int i = 0; i < weekEndArray.Length; i++) { if (weekEndArray[i] == '1') { valueWeekEnd += i + 1 + ","; } } if (!string.IsNullOrEmpty(valueWeekDay)) { var timerScheduleOfWeekDays = new TimerSchedule() { TimerTypeId = Configuration.Weekdays, ApplianceId = app.Id, ActiveValues = !string.IsNullOrEmpty(valueWeekDay) ? valueWeekDay.Remove(valueWeekDay.Length - 1) : string.Empty }; _timerScheduleService.Insert(timerScheduleOfWeekDays); } if (!string.IsNullOrEmpty(valueWeekEnd)) { var timerScheduleOfWeekEnd = new TimerSchedule() { TimerTypeId = Configuration.Weekends, ApplianceId = app.Id, ActiveValues = !string.IsNullOrEmpty(valueWeekEnd) ? valueWeekEnd.Remove(valueWeekEnd.Length - 1) : string.Empty }; _timerScheduleService.Insert(timerScheduleOfWeekEnd); } } } } else { if (isSerialNumberExist.AccountId == null) { isSerialNumberExist.AccountId = appliance.AccountId; isSerialNumberExist.Street1 = appliance.Street1; isSerialNumberExist.Street2 = appliance.Street2; isSerialNumberExist.City = appliance.City; isSerialNumberExist.StateId = appliance.StateId; isSerialNumberExist.ZipCode = appliance.ZipCode; isSerialNumberExist.Lat = _params.loc != null ? _params.loc.lat : 0; isSerialNumberExist.Lon = _params.loc != null ? _params.loc.lng : 0; isSerialNumberExist.Cellular = thingCellular; isSerialNumberExist.IsConnected = thingIsConnected; isSerialNumberExist.DeviceName = _params != null && _params.attrs != null && _params.attrs.name != null ? _params.attrs.name.value : string.Empty; isSerialNumberExist.OsVersion = ""; isSerialNumberExist.GeoFenceEnabled = _params.alarms != null && _params.alarms.timerState != null && _params.alarms.timerState.state != 0 ? true : false; isSerialNumberExist.Power = thingPower; isSerialNumberExist.TimerEnabled = _params.alarms != null && _params.alarms.timerState != null && _params.alarms.timerState.state != 0 ? true : false; isSerialNumberExist.Wifi = thingWifi; isSerialNumberExist.WiFiInternet = thingWifiInternet; isSerialNumberExist.ZipCode = appliance.ZipCode; isSerialNumberExist.TrustLevel = thingTrustLevel != null ? thingTrustLevel : 0; isSerialNumberExist.IsOn = thingIsOn; isSerialNumberExist.TriggerMile = Convert.ToDecimal(_appSettings.TriggerMiles); _applianceService.Update(isSerialNumberExist); var accountAppliance = _accountApplianceService.Insert(new AccountAppliance() { AccountId = appliance.AccountId, AirGapVersion = "", ApplianceId = isSerialNumberExist.Id, IsQRCodeScaned = false, IsVerified = false }); appliance.Id = isSerialNumberExist.Id; if (_params.attrs != null && _params.attrs.timerSchedule != null) { var timerScheduleDTO = JsonConvert.DeserializeObject <TimerScheduleFromTelit>(_params.attrs.timerSchedule.value); char[] weekDayArray = timerScheduleDTO.Weekdays.ToCharArray(); char[] weekEndArray = timerScheduleDTO.Weekends.ToCharArray(); string valueWeekDay = string.Empty; string valueWeekEnd = string.Empty; for (int i = 0; i < weekDayArray.Length; i++) { if (weekDayArray[i] == '1') { valueWeekDay += i + 1 + ","; } } for (int i = 0; i < weekEndArray.Length; i++) { if (weekEndArray[i] == '1') { valueWeekEnd += i + 1 + ","; } } var timerSchedule = _timerScheduleService.GetTimerScheduleByApplianceId(appliance.Id); if (timerSchedule != null && timerSchedule.Count > 0) { foreach (var item in timerSchedule) { if (item.TimerTypeId == Configuration.Weekdays) { item.ActiveValues = !string.IsNullOrEmpty(valueWeekDay) ? valueWeekDay.Remove(valueWeekDay.Length - 1) : string.Empty; } if (item.TimerTypeId == Configuration.Weekends) { item.ActiveValues = !string.IsNullOrEmpty(valueWeekEnd) ? valueWeekEnd.Remove(valueWeekEnd.Length - 1) : string.Empty; } _timerScheduleService.Update(item); } } else { if (!string.IsNullOrEmpty(valueWeekDay)) { var timerScheduleOfWeekDays = new TimerSchedule() { TimerTypeId = Configuration.Weekdays, ApplianceId = isSerialNumberExist.Id, ActiveValues = !string.IsNullOrEmpty(valueWeekDay) ? valueWeekDay.Remove(valueWeekDay.Length - 1) : string.Empty }; _timerScheduleService.Insert(timerScheduleOfWeekDays); } if (!string.IsNullOrEmpty(valueWeekEnd)) { var timerScheduleOfWeekEnd = new TimerSchedule() { TimerTypeId = Configuration.Weekends, ApplianceId = isSerialNumberExist.Id, ActiveValues = !string.IsNullOrEmpty(valueWeekEnd) ? valueWeekEnd.Remove(valueWeekEnd.Length - 1) : string.Empty }; _timerScheduleService.Insert(timerScheduleOfWeekEnd); } } } } else { response.Status = ResponseStatus.Success.ToString(); response.Message = ResponseMessage.SerialNumberIsExist; return(response); } } } appliance.IsMatching = isMatching; appliance.HasSerialNumber = hasSerialNumber; response.Data = appliance; response.Status = ResponseStatus.Success.ToString(); return(response); } catch (Exception ex) { var response = new ResponseData <ApplianceDTO>(); response.Message = ex.Message; response.Status = ResponseStatus.Error.ToString(); return(response); } }
/// <inheritdoc/> public override async Task <bool> IsPastDueAsync(string timerName, DateTime now, TimerSchedule schedule) { StatusEntry status = GetStatus(timerName); DateTime recordedNextOccurrence; if (status == null) { // If we've never recorded a status for this timer, write an initial // status entry. This ensures that for a new timer, we've captured a // status log for the next occurrence even though no occurrence has happened yet // (ensuring we don't miss an occurrence) DateTime nextOccurrence = schedule.GetNextOccurrence(now); await UpdateAsync(timerName, default(DateTime), nextOccurrence); recordedNextOccurrence = nextOccurrence; } else { // ensure that the schedule hasn't been updated since the last // time we checked, and if it has, update the status file DateTime expectedNextOccurrence = schedule.GetNextOccurrence(status.Last); if (status.Next != expectedNextOccurrence) { await UpdateAsync(timerName, status.Last, expectedNextOccurrence); } recordedNextOccurrence = status.Next; } // if now is after the last next occurrence we recorded, we know we've missed // at least one schedule instance return(now > recordedNextOccurrence); }
/// <summary> /// Returns the <see cref="TimeSpan"/> duration that the specified timer is past due. /// </summary> /// <param name="timerName">The name of the timer.</param> /// <param name="now">The current time.</param> /// <param name="schedule">The <see cref="TimerSchedule"/>.</param> /// <returns>The duration the timer is past due.</returns> protected async Task<TimeSpan> GetPastDueDuration(string timerName, DateTime now, TimerSchedule schedule) { StatusEntry status = GetStatus(timerName); DateTime recordedNextOccurrence; if (status == null) { // If we've never recorded a status for this timer, write an initial // status entry. This ensures that for a new timer, we've captured a // status log for the next occurrence even though no occurrence has happened yet // (ensuring we don't miss an occurrence) DateTime nextOccurrence = schedule.GetNextOccurrence(now); await UpdateAsync(timerName, default(DateTime), nextOccurrence); recordedNextOccurrence = nextOccurrence; } else { // ensure that the schedule hasn't been updated since the last // time we checked, and if it has, update the status file DateTime expectedNextOccurrence = schedule.GetNextOccurrence(status.Last); if (status.Next != expectedNextOccurrence) { await UpdateAsync(timerName, status.Last, expectedNextOccurrence); } recordedNextOccurrence = status.Next; } if (now > recordedNextOccurrence) { // if now is after the last next occurrence we recorded, we know we've missed // at least one schedule instance and we are past due return now - recordedNextOccurrence; } else { // not past due return TimeSpan.Zero; } }
/// <summary> /// Determines whether the schedule is currently past due. /// </summary> /// <remarks> /// On startup, all schedules are checked to see if they are past due. Any /// timers that are past due will be executed immediately by default. Subclasses can /// change this behavior by inspecting the current time and schedule to determine /// whether it should be considered past due. /// </remarks> /// <param name="timerName">The name of the timer to check</param> /// <param name="now">The time to check</param> /// <param name="schedule">The <see cref="TimerSchedule"/></param> /// <returns>True if the schedule is past due, false otherwise.</returns> public abstract Task<bool> IsPastDueAsync(string timerName, DateTime now, TimerSchedule schedule);
public static void NextOccurrences(ILogger logger, string functionName, TimerSchedule schedule, string occurrences) => _nextOccurrences(logger, functionName, schedule, Environment.NewLine + occurrences, null);
/// <summary> /// Constructs a new instances /// </summary> /// <param name="schedule">The timer trigger schedule.</param> public TimerInfo(TimerSchedule schedule) { Schedule = schedule; }
/// <summary> /// Constructs a new instance /// </summary> /// <param name="schedule">The timer trigger schedule.</param> /// <param name="status">The current schedule status.</param> /// <param name="isPastDue">True if the schedule is past due, false otherwise.</param> public TimerInfo(TimerSchedule schedule, ScheduleStatus status, bool isPastDue = false) { Schedule = schedule; ScheduleStatus = status; IsPastDue = isPastDue; }
/// <summary> /// Determines whether the schedule is currently past due. /// </summary> /// <param name="timerName">The name of the timer to check</param> /// <param name="now">The time to check</param> /// <param name="schedule">The <see cref="TimerSchedule"/></param> /// <returns>True if the schedule is past due, false otherwise.</returns> public abstract Task <bool> IsPastDueAsync(string timerName, DateTime now, TimerSchedule schedule);
public async Task <IActionResult> UpdateStatusFromTelit([FromBody] RequestTelit request) { try { Event _event = null; var appliance = _applianceService.GetApplianceBySerialNumber(request.thing); if (appliance == null) { return(Json(new { success = true, message = ResponseMessage.SerialNumberInCorrect })); } if (request.key.ToLower() == Configuration.TurnOn) { if (appliance != null) { var thingFind = await TelitApi.ThingFind(request.thing); Telit.ThingFind.Params _params = new Telit.ThingFind.Params(); bool isMatching = false; if (thingFind != null && thingFind.Things != null && thingFind.Things.success && thingFind.Things.@params != null) { _params = thingFind.Things.@params; if (_params.loc != null) { isMatching = DistanceBetweenPlaces(appliance.Lat.Value, appliance.Lon.Value, _params.loc.lat, _params.loc.lng) <= Convert.ToDouble(_appSettings.Miles) ? true : false; if (!isMatching) { appliance.StateId = _params.loc != null && _params.loc.addr != null && _stateService.GetStateByNameOrCode(_params.loc.addr.state) != null ? (long?)_stateService.GetStateByNameOrCode(_params.loc.addr.state).Id : null; appliance.Street1 = _params.loc != null && _params.loc.addr != null ? _params.loc.addr.streetNumber + " " + _params.loc.addr.street : string.Empty; appliance.Street2 = string.Empty; appliance.Lat = _params.loc != null ? _params.loc.lat : 0; appliance.Lon = _params.loc != null ? _params.loc.lng : 0; appliance.City = _params.loc != null && _params.loc.addr != null ? _params.loc.addr.city : string.Empty; appliance.ZipCode = _params.loc != null && _params.loc.addr != null ? _params.loc.addr.zipCode : string.Empty; } } } appliance.IsOn = Convert.ToBoolean(Convert.ToInt16(request.value)); if (Convert.ToInt16(request.value) == Configuration.Off) { appliance.TimerEnabled = true; appliance.GeoFenceEnabled = true; } _applianceService.Update(appliance); if (Convert.ToInt32(request.value) == (Int32)AlarmStatus.OffScheduleTimer || Convert.ToInt32(request.value) == (Int32)AlarmStatus.OnScheduleTimer) { request.msg = ResponseMessage.ScheduleTimerChange; } _event = new Event() { ApplianceId = Convert.ToInt64(appliance.Id), EventTypeId = Constant.EventType.ConnectionChange, EventDetail = Constant.ResponseMessage.ConnectionChange, Timestamp = DateTime.UtcNow, Message = request.msg }; _eventService.Insert(_event); } } else if (request.key.ToLower() == Configuration.Name) { if (appliance != null) { appliance.DeviceName = request.value; _applianceService.Update(appliance); } } else if (request.key.ToLower() == Configuration.Env) { var applianceEnvironment = Convert.ToString(Convert.ToInt16(request.value), 2).PadLeft(4, '0'); var array = !string.IsNullOrEmpty(applianceEnvironment) ? applianceEnvironment.ToArray() : new char[] { }; if (appliance != null) { bool isPowerStatusChange = appliance.Power != (array.Length == 4 ? Convert.ToBoolean(Convert.ToInt16(array[3].ToString())) : false) ? true : false; bool isWifiStatusChange = appliance.Wifi != (array.Length == 4 ? Convert.ToBoolean(Convert.ToInt16(array[1].ToString())) : false) ? true : false; bool isWiFiInternetStatusChange = appliance.WiFiInternet != (array.Length == 4 ? Convert.ToBoolean(Convert.ToInt16(array[0].ToString())) : false) ? true : false; appliance.Cellular = array.Length == 4 ? Convert.ToBoolean(Convert.ToInt16(array[2].ToString())) : false; appliance.Power = array.Length == 4 ? Convert.ToBoolean(Convert.ToInt16(array[3].ToString())) : false; appliance.Wifi = array.Length == 4 ? Convert.ToBoolean(Convert.ToInt16(array[1].ToString())) : false; appliance.WiFiInternet = array.Length == 4 ? Convert.ToBoolean(Convert.ToInt16(array[0].ToString())) : false; var thingFind = await TelitApi.ThingFind(request.thing); Telit.ThingFind.Params _params = new Telit.ThingFind.Params(); bool isMatching = false; if (thingFind != null && thingFind.Things != null && thingFind.Things.success && thingFind.Things.@params != null) { _params = thingFind.Things.@params; if (_params.loc != null) { isMatching = DistanceBetweenPlaces(appliance.Lat.Value, appliance.Lon.Value, _params.loc.lat, _params.loc.lng) <= Convert.ToDouble(_appSettings.Miles) ? true : false; if (!isMatching) { appliance.StateId = _params.loc != null && _params.loc.addr != null && _stateService.GetStateByNameOrCode(_params.loc.addr.state) != null ? (long?)_stateService.GetStateByNameOrCode(_params.loc.addr.state).Id : null; appliance.Street1 = _params.loc != null && _params.loc.addr != null ? _params.loc.addr.streetNumber + " " + _params.loc.addr.street : string.Empty; appliance.Street2 = string.Empty; appliance.Lat = _params.loc != null ? _params.loc.lat : 0; appliance.Lon = _params.loc != null ? _params.loc.lng : 0; appliance.City = _params.loc != null && _params.loc.addr != null ? _params.loc.addr.city : string.Empty; appliance.ZipCode = _params.loc != null && _params.loc.addr != null ? _params.loc.addr.zipCode : string.Empty; } } } _applianceService.Update(appliance); var lAccount = _accountApplianceService.GetAccountByApplianceId(appliance.Id, true); if (isPowerStatusChange) { _event = new Event() { ApplianceId = Convert.ToInt64(appliance.Id), EventTypeId = Constant.EventType.HomePowerLoss, EventDetail = Constant.ResponseMessage.ManualConnectedStatusChange, Timestamp = DateTime.UtcNow, Message = appliance.DeviceName + "(" + appliance.SerialNumber.Substring(appliance.SerialNumber.Length - 4) + ")" + ": " + Constant.ResponseMessage.HomePower + " " + (array.Length == 4 && Convert.ToBoolean(Convert.ToInt16(array[3].ToString())) ? Configuration.TurnOn.ToUpper() : Configuration.TurnOff.ToUpper()) }; var insEvent = _eventService.Insert(_event); if (lAccount != null && lAccount.Count > 0) { foreach (var item in lAccount) { var notificationPreference = _notificationPreferenceService.GetNotificationPreferenceByAccountId(item.Id.Value); if (notificationPreference.Any(x => x.EventTypeId == Constant.EventType.HomePowerLoss && x.ApplianceId == appliance.Id)) { var temp = notificationPreference.FirstOrDefault(x => x.EventTypeId == Constant.EventType.HomePowerLoss && x.ApplianceId == appliance.Id); var accountAppliance = _accountApplianceService.GetAccountApplianceByAccountIdAndApplianceId(temp.AccountId, temp.ApplianceId); if (accountAppliance.PhoneType == Configuration.Android) { var pushGcmotification = new AndroidGcmPushNotification(_appSettings); pushGcmotification.InitPushNotification(accountAppliance.DeviceToken, insEvent.Message); System.Threading.Thread.Sleep(1000); } else { var applePush = new ApplePushNotificationService(_appSettings); applePush.PushMessage(insEvent.Message, accountAppliance.DeviceToken, 0, null).Wait(); System.Threading.Thread.Sleep(1000); } var notification = new Notification() { AccountId = accountAppliance.AccountId, ApplianceId = accountAppliance.ApplianceId, EventId = insEvent.Id, Timestamp = DateTime.UtcNow }; _notificationService.Insert(notification); } } } } if (isWifiStatusChange) { _event = new Event() { ApplianceId = Convert.ToInt64(appliance.Id), EventTypeId = request.msg != null && request.msg == Constant.EventType.StatusChangeFromGeoFence.ToString() ? Constant.EventType.StatusChangeFromGeoFence : Constant.EventType.NetWorkStatusChange, EventDetail = Constant.ResponseMessage.Wifi, Timestamp = DateTime.UtcNow, Message = appliance.DeviceName + "(" + appliance.SerialNumber.Substring(appliance.SerialNumber.Length - 4) + ")" + ": " + Constant.ResponseMessage.Wifi + " " + (array.Length == 4 && Convert.ToBoolean(Convert.ToInt16(array[1].ToString())) ? Configuration.TurnOn.ToUpper() : Configuration.TurnOff.ToUpper()) }; var insEvent = _eventService.Insert(_event); if (lAccount != null && lAccount.Count > 0) { foreach (var item in lAccount) { var notificationPreference = _notificationPreferenceService.GetNotificationPreferenceByAccountId(item.Id.Value); if (notificationPreference.Any(x => x.EventTypeId == Constant.EventType.NetWorkStatusChange && x.ApplianceId == appliance.Id)) { var temp = notificationPreference.FirstOrDefault(x => x.EventTypeId == Constant.EventType.NetWorkStatusChange && x.ApplianceId == appliance.Id); var accountAppliance = _accountApplianceService.GetAccountApplianceByAccountIdAndApplianceId(temp.AccountId, temp.ApplianceId); if (accountAppliance.PhoneType == Configuration.Android) { var pushGcmotification = new AndroidGcmPushNotification(_appSettings); pushGcmotification.InitPushNotification(accountAppliance.DeviceToken, insEvent.Message); System.Threading.Thread.Sleep(1000); } else { var applePush = new ApplePushNotificationService(_appSettings); applePush.PushMessage(insEvent.Message, accountAppliance.DeviceToken, 0, null).Wait(); System.Threading.Thread.Sleep(1000); } var notification = new Notification() { AccountId = accountAppliance.AccountId, ApplianceId = accountAppliance.ApplianceId, EventId = insEvent.Id, Timestamp = DateTime.UtcNow }; _notificationService.Insert(notification); } } } } if (isWiFiInternetStatusChange) { _event = new Event() { ApplianceId = Convert.ToInt64(appliance.Id), EventTypeId = Constant.EventType.ISPOutage, EventDetail = Constant.ResponseMessage.ISPOutage, Timestamp = DateTime.UtcNow, Message = appliance.DeviceName + "(" + appliance.SerialNumber.Substring(appliance.SerialNumber.Length - 4) + ")" + ": " + Constant.ResponseMessage.ISPOutage + " " + (array.Length == 4 && Convert.ToBoolean(Convert.ToInt16(array[0].ToString())) ? Configuration.Connected : Configuration.Disconnected) }; var insEvent = _eventService.Insert(_event); if (lAccount != null && lAccount.Count > 0) { foreach (var item in lAccount) { var notificationPreference = _notificationPreferenceService.GetNotificationPreferenceByAccountId(item.Id.Value); if (notificationPreference.Any(x => x.EventTypeId == Constant.EventType.ISPOutage && x.ApplianceId == appliance.Id)) { var temp = notificationPreference.FirstOrDefault(x => x.EventTypeId == Constant.EventType.ISPOutage && x.ApplianceId == appliance.Id); var accountAppliance = _accountApplianceService.GetAccountApplianceByAccountIdAndApplianceId(temp.AccountId, temp.ApplianceId); if (accountAppliance.PhoneType == Configuration.Android) { var pushGcmotification = new AndroidGcmPushNotification(_appSettings); pushGcmotification.InitPushNotification(accountAppliance.DeviceToken, insEvent.Message); System.Threading.Thread.Sleep(1000); } else { var applePush = new ApplePushNotificationService(_appSettings); applePush.PushMessage(insEvent.Message, accountAppliance.DeviceToken, 0, null).Wait(); System.Threading.Thread.Sleep(1000); } var notification = new Notification() { AccountId = accountAppliance.AccountId, ApplianceId = accountAppliance.ApplianceId, EventId = insEvent.Id, Timestamp = DateTime.UtcNow }; _notificationService.Insert(notification); } } } } } } else if (request.key.ToLower() == Configuration.Trust.ToLower()) { if (appliance != null) { bool isStatusChange = appliance.TrustLevel != null && appliance.TrustLevel.Value.ToString() != request.value ? true : false; _event = new Event() { ApplianceId = Convert.ToInt64(appliance.Id), EventTypeId = Constant.EventType.TrustLevelChange, EventDetail = Constant.ResponseMessage.TrustLevel, Timestamp = DateTime.UtcNow, Message = Constant.ResponseMessage.TrustLevel + " " + request.value }; _eventService.Insert(_event); appliance.TrustLevel = Convert.ToInt16(request.value); _applianceService.Update(appliance); } } else if (request.key.ToLower() == Configuration.TimmerState) { if (appliance != null) { appliance.TimerEnabled = Convert.ToInt16(request.value) != Configuration.TimerDisable ? true : false; _applianceService.Update(appliance); } } else if (request.key.ToLower() == Configuration.TimerSchedule) { var timerScheduleDTO = JsonConvert.DeserializeObject <TimerScheduleFromTelit>(request.value); var timerScheduleOldValue = JsonConvert.DeserializeObject <TimerScheduleFromTelit>(request.prev); char[] weekDayArray = timerScheduleDTO.Weekdays.ToCharArray(); char[] weekEndArray = timerScheduleDTO.Weekends.ToCharArray(); string valueWeekDay = string.Empty; string valueWeekEnd = string.Empty; for (int i = 0; i < weekDayArray.Length; i++) { if (weekDayArray[i] == '1') { valueWeekDay += i + 1 + ","; } } for (int i = 0; i < weekEndArray.Length; i++) { if (weekEndArray[i] == '1') { valueWeekEnd += i + 1 + ","; } } var timerSchedule = _timerScheduleService.GetTimerScheduleByApplianceId(appliance.Id); if (timerSchedule != null && timerSchedule.Count > 0) { foreach (var item in timerSchedule) { if (item.TimerTypeId == Configuration.Weekdays) { item.ActiveValues = !string.IsNullOrEmpty(valueWeekDay) ? valueWeekDay.Remove(valueWeekDay.Length - 1) : string.Empty; } if (item.TimerTypeId == Configuration.Weekends) { item.ActiveValues = !string.IsNullOrEmpty(valueWeekEnd) ? valueWeekEnd.Remove(valueWeekEnd.Length - 1) : string.Empty; } _timerScheduleService.Update(item); } _event = new Event() { ApplianceId = Convert.ToInt64(appliance.Id), EventTypeId = Constant.EventType.ScheduleTimerChange, EventDetail = Constant.ResponseMessage.ScheduleTimerChange, Timestamp = DateTime.UtcNow, Message = "from Weekdays: " + timerScheduleOldValue.Weekdays + " Weekends: " + timerScheduleOldValue.Weekends + " to Weekdays: " + timerScheduleDTO.Weekdays + " Weekends: " + timerScheduleDTO.Weekends }; _eventService.Insert(_event); } else { if (!string.IsNullOrEmpty(valueWeekDay)) { var timerScheduleOfWeekDays = new TimerSchedule() { TimerTypeId = Configuration.Weekdays, ApplianceId = appliance.Id, ActiveValues = !string.IsNullOrEmpty(valueWeekDay) ? valueWeekDay.Remove(valueWeekDay.Length - 1) : string.Empty }; _timerScheduleService.Insert(timerScheduleOfWeekDays); } if (!string.IsNullOrEmpty(valueWeekEnd)) { var timerScheduleOfWeekEnd = new TimerSchedule() { TimerTypeId = Configuration.Weekends, ApplianceId = appliance.Id, ActiveValues = !string.IsNullOrEmpty(valueWeekEnd) ? valueWeekEnd.Remove(valueWeekEnd.Length - 1) : string.Empty }; _timerScheduleService.Insert(timerScheduleOfWeekEnd); } _event = new Event() { ApplianceId = Convert.ToInt64(appliance.Id), EventTypeId = Constant.EventType.ScheduleTimerChange, EventDetail = Constant.ResponseMessage.ScheduleTimerChange, Timestamp = DateTime.UtcNow, Message = "from Weekdays: " + timerScheduleOldValue.Weekdays + " Weekends: " + timerScheduleOldValue.Weekends + " to Weekdays: " + timerScheduleDTO.Weekdays + " Weekends: " + timerScheduleDTO.Weekends }; _eventService.Insert(_event); } } } catch (Exception ex) { return(Json(new { success = false, message = ex.Message })); } return(Json(new { success = true, message = request.msg })); }
/// <inheritdoc/> public override async Task<bool> IsPastDueAsync(string timerName, DateTime now, TimerSchedule schedule) { TimeSpan pastDueDuration = await GetPastDueDuration(timerName, now, schedule); return pastDueDuration != TimeSpan.Zero; }
public static void ScheduleAndTimeZone(ILogger logger, string functionName, TimerSchedule schedule, string timeZone) => _scheduleAndTimeZone(logger, functionName, schedule, timeZone, null);