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.optf.cmso.optimizer.availability.policies.model.AllowedPeriodicTime;
41 import org.onap.optf.cmso.optimizer.availability.policies.model.TimeLimitAndVerticalTopology;
42 import org.onap.optf.cmso.optimizer.availability.policies.model.TimeRange;
43 import org.onap.optf.cmso.optimizer.common.LogMessages;
44 import org.onap.optf.cmso.optimizer.observations.Observation;
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.setStartTime("00:00:00+00:00");
131 range.setEndTime("24:00:00+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.getStartTime(), cwStartInstant, rdata, cwEndInstant, -1);
152 List<DateTime> endList = getRecurringList(range.getEndTime(), cwStartInstant,
153 rdata, cwEndInstant, startList.size());
154 // Pair them up to make change windows
155 // Everything should be UTC time
156 for (int i = 0; i < startList.size(); i++) {
157 DateTime startDt = startList.get(i);
158 if (i < endList.size()) {
159 DateTime endDt = endList.get(i);
160 if (endDt.isAfter(startDt)) {
161 ChangeWindow cw = new ChangeWindow();
162 cw.setStartTime(startDt.toDate());
163 cw.setEndTime(endDt.toDate());
164 availableList.add(cw);
173 private static List<DateTime> getRecurringList(String rangeTime, Instant cwStartInstant, StringBuilder rdata,
174 Instant cwEndInstant, int endingSize) throws ParseException {
176 Instant startInstant = getInstanceFromTime(rangeTime, cwStartInstant);
177 DateTime start = new DateTime(startInstant.toEpochMilli());
178 DateTimeIterator recur =
179 DateTimeIteratorFactory.createDateTimeIterator(rdata.toString(), start, DateTimeZone.UTC, true);
180 List<DateTime> list = new ArrayList<>();
181 while (recur.hasNext()) {
182 DateTime next = recur.next();
183 // System.out.println(next.toString());
184 if (endingSize == -1) {
185 if (next.isAfter(cwEndInstant.toEpochMilli())) {
190 if (list.size() == endingSize) {
201 // The policies with 'Day' enumeration only have time with no day so we add the
202 // date portion of the change window to the dtstart
204 private static Instant getInstanceFromTime(String timeIn, Instant cwStartInstant) {
205 Instant instant = null;
206 Instant date = cwStartInstant.truncatedTo(ChronoUnit.DAYS);
207 // Handle ending midnight
208 if (timeIn.startsWith("24:00:00")) {
209 timeIn = "00:00:00+00:00";
210 date = date.plus(1, ChronoUnit.DAYS);
212 LocalDate epoch = LocalDate.ofEpochDay(0);
214 OffsetTime offset = OffsetTime.parse(timeIn);
215 OffsetDateTime odt = offset.atDate(epoch);
216 ZonedDateTime startTime = odt.atZoneSameInstant(ZoneOffset.UTC.normalized());
217 instant = Instant.from(startTime);
218 } catch (Exception e) {
219 LocalTime local = LocalTime.parse(timeIn);
220 LocalDateTime ldt = local.atDate(epoch);
221 ZonedDateTime startTime = ldt.atZone(ZoneOffset.UTC.normalized());
222 instant = Instant.from(startTime);
224 return instant.plus(date.toEpochMilli(), ChronoUnit.MILLIS);
228 * Gets the recurring list for change window.
230 * @param window the window
231 * @param durationInSeconds the duration in seconds
232 * @return the recurring list for change window
233 * @throws ParseException the parse exception
235 public static DateTimeIterator getRecurringListForChangeWindow(ChangeWindow window, Long durationInSeconds)
236 throws ParseException {
238 String rdata = "RRULE:FREQ=MINUTELY;INTERVAL=" + durationInSeconds / 60;
239 DateTime start = new DateTime(window.getStartTime().toInstant().toEpochMilli());
240 DateTimeIterator recur =
241 DateTimeIteratorFactory.createDateTimeIterator(rdata, start, DateTimeZone.UTC, true);