2 * ============LICENSE_START==============================================
3 * Copyright (c) 2019 AT&T Intellectual Property.
4 * =======================================================================
5 * Licensed under the Apache License, Version 2.0 (the "License"); you may
6 * not use this file except in compliance with the License. You may obtain a
7 * copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
14 * or implied. See the License for the specific language governing
15 * permissions and limitations under the License.
16 * ============LICENSE_END=================================================
20 package org.onap.optf.cmso.optimizer.availability.timewindows;
22 import com.google.ical.compat.jodatime.DateTimeIterator;
23 import com.google.ical.compat.jodatime.DateTimeIteratorFactory;
24 import java.text.ParseException;
25 import java.time.Instant;
26 import java.time.LocalDate;
27 import java.time.LocalDateTime;
28 import java.time.LocalTime;
29 import java.time.OffsetDateTime;
30 import java.time.OffsetTime;
31 import java.time.ZoneOffset;
32 import java.time.ZonedDateTime;
33 import java.time.temporal.ChronoUnit;
34 import java.util.ArrayList;
35 import java.util.HashSet;
36 import java.util.List;
38 import org.joda.time.DateTime;
39 import org.joda.time.DateTimeZone;
40 import org.onap.observations.Observation;
41 import org.onap.optf.cmso.optimizer.availability.policies.model.AllowedPeriodicTime;
42 import org.onap.optf.cmso.optimizer.availability.policies.model.TimeLimitAndVerticalTopology;
43 import org.onap.optf.cmso.optimizer.availability.policies.model.TimeRange;
44 import org.onap.optf.cmso.optimizer.common.LogMessages;
45 import org.onap.optf.cmso.optimizer.service.rs.models.ChangeWindow;
48 * The Class RecurringWindows.
50 public class RecurringWindows {
53 * Gets the availability windows for policies.
55 * @param policies the policies
56 * @param changeWindow the change window
57 * @return the availability windows for policies
59 public static List<ChangeWindow> getAvailabilityWindowsForPolicies(List<TimeLimitAndVerticalTopology> policies,
60 ChangeWindow changeWindow) {
61 List<ChangeWindow> availableList = new ArrayList<>();
62 for (TimeLimitAndVerticalTopology policy : policies) {
63 if (policy.getTimeSchedule() != null && policy.getTimeSchedule().getAllowedPeriodicTime() != null) {
64 for (AllowedPeriodicTime available : policy.getTimeSchedule().getAllowedPeriodicTime()) {
65 getAvailableWindowsForApt(available, changeWindow, availableList);
69 // Collapse all duplicate and overlapping availabity windows into minimum
71 availableList = collapseWindows(availableList);
77 private static List<ChangeWindow> collapseWindows(List<ChangeWindow> availableList) {
78 List<ChangeWindow> collapsed = new ArrayList<>();
79 Set<ChangeWindow> consumed = new HashSet<>();
80 for (ChangeWindow win : availableList) {
81 if (!consumed.contains(win)) {
82 // Find all windows that can collapse into this one
84 boolean allUnique = false;
87 for (ChangeWindow test : availableList) {
88 // if availability windows overlap
89 if (!consumed.contains(test)) {
90 if (win.absorbIfOverlapping(test)) {
103 // "RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR",
104 private static void getAvailableWindowsForApt(AllowedPeriodicTime available, ChangeWindow changeWindow,
105 List<ChangeWindow> availableList) {
107 if (available.getDay() != null) {
108 switch (available.getDay()) {
111 getAvailableWindowsForAptDay(available, changeWindow, availableList);
117 availableList.add(changeWindow);
118 Observation.report(LogMessages.UNSUPPORTED_PERIODIC_TIME, available.toString());
122 // "RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR",
124 private static void getAvailableWindowsForAptDay(AllowedPeriodicTime available, ChangeWindow changeWindow,
125 List<ChangeWindow> availableList) {
127 List<TimeRange> ranges = available.getTimeRange();
128 if (ranges.size() == 0) {
129 TimeRange range = new TimeRange();
130 range.setStart_time("00:00:00+00:00");
131 range.setStart_time("23:59:59+00:00");
134 StringBuilder rdata = new StringBuilder();
135 rdata.append(available.getDay().getRrule()).append("\n");
136 for (TimeRange range : ranges) {
137 processRange(range, changeWindow, availableList, rdata);
139 } catch (Exception e) {
140 Observation.report(LogMessages.UNEXPECTED_EXCEPTION, e, e.getMessage());
145 private static void processRange(TimeRange range, ChangeWindow changeWindow, List<ChangeWindow> availableList,
146 StringBuilder rdata) throws ParseException {
148 Instant cwStartInstant = changeWindow.getStartTime().toInstant();
149 Instant cwEndInstant = changeWindow.getEndTime().toInstant();
151 List<DateTime> startList = getRecurringList(range.getStart_time(), cwStartInstant, rdata, cwEndInstant, -1);
152 List<DateTime> endList = getRecurringList(range.getEnd_time(), cwStartInstant, rdata, cwEndInstant, startList.size());
153 // Pair them up to make change windows
154 // Everything should be UTC time
155 for (int i = 0; i < startList.size(); i++) {
156 DateTime startDt = startList.get(i);
157 if (i < endList.size()) {
158 DateTime endDt = endList.get(i);
159 if (endDt.isAfter(startDt)) {
160 ChangeWindow cw = new ChangeWindow();
161 cw.setStartTime(startDt.toDate());
162 cw.setEndTime(endDt.toDate());
163 availableList.add(cw);
172 private static List<DateTime> getRecurringList(String rangeTime, Instant cwStartInstant, StringBuilder rdata,
173 Instant cwEndInstant, int endingSize) throws ParseException {
175 Instant startInstant = getInstanceFromTime(rangeTime, cwStartInstant);
176 DateTime start = new DateTime(startInstant.toEpochMilli());
177 DateTimeIterator recur =
178 DateTimeIteratorFactory.createDateTimeIterator(rdata.toString(), start, DateTimeZone.UTC, true);
179 List<DateTime> list = new ArrayList<>();
180 while (recur.hasNext()) {
181 DateTime next = recur.next();
182 // System.out.println(next.toString());
183 if (endingSize == -1) {
184 if (next.isAfter(cwEndInstant.toEpochMilli())) {
189 if (list.size() == endingSize) {
200 // The policies with 'Day' enumeration only have time with no day so we add the
201 // date portion of the change window to the dtstart
203 private static Instant getInstanceFromTime(String timeIn, Instant cwStartInstant) {
204 Instant instant = null;
205 Instant date = cwStartInstant.truncatedTo(ChronoUnit.DAYS);
206 LocalDate epoch = LocalDate.ofEpochDay(0);
208 OffsetTime offset = OffsetTime.parse(timeIn);
209 OffsetDateTime odt = offset.atDate(epoch);
210 ZonedDateTime startTime = odt.atZoneSameInstant(ZoneOffset.UTC.normalized());
211 instant = Instant.from(startTime);
212 } catch (Exception e) {
213 LocalTime local = LocalTime.parse(timeIn);
214 LocalDateTime ldt = local.atDate(epoch);
215 ZonedDateTime startTime = ldt.atZone(ZoneOffset.UTC.normalized());
216 instant = Instant.from(startTime);
218 return instant.plus(date.toEpochMilli(), ChronoUnit.MILLIS);
221 public static DateTimeIterator getRecurringListForChangeWindow(ChangeWindow window, Long durationInSeconds)
222 throws ParseException {
224 String rdata = "RRULE:FREQ=MINUTELY;INTERVAL=" + durationInSeconds/60;
225 DateTime start = new DateTime(window.getStartTime().toInstant().toEpochMilli());
226 DateTimeIterator recur =
227 DateTimeIteratorFactory.createDateTimeIterator(rdata, start, DateTimeZone.UTC, true);