2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021 Nordix Foundation.
4 * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
5 * ================================================================================
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 * SPDX-License-Identifier: Apache-2.0
19 * ============LICENSE_END=========================================================
22 package org.onap.policy.clamp.controlloop.participant.intermediary.handler;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.LinkedHashMap;
27 import java.util.List;
29 import java.util.UUID;
30 import java.util.stream.Collectors;
32 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ClElementStatistics;
33 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoop;
34 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElement;
35 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElementAck;
36 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElementDefinition;
37 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopOrderedState;
38 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopState;
39 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoops;
40 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantUpdates;
41 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantUtils;
42 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ControlLoopAck;
43 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ControlLoopStateChange;
44 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ControlLoopUpdate;
45 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantMessageType;
46 import org.onap.policy.clamp.controlloop.participant.intermediary.api.ControlLoopElementListener;
47 import org.onap.policy.clamp.controlloop.participant.intermediary.comm.ParticipantMessagePublisher;
48 import org.onap.policy.clamp.controlloop.participant.intermediary.parameters.ParticipantParameters;
49 import org.onap.policy.models.base.PfModelException;
50 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
51 import org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate;
52 import org.onap.policy.models.tosca.authorative.concepts.ToscaProperty;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55 import org.springframework.stereotype.Component;
58 * This class is responsible for managing the state of all control loops in the participant.
61 public class ControlLoopHandler {
62 private static final Logger LOGGER = LoggerFactory.getLogger(ControlLoopHandler.class);
64 private final ToscaConceptIdentifier participantType;
65 private final ToscaConceptIdentifier participantId;
66 private final ParticipantMessagePublisher publisher;
69 private final Map<ToscaConceptIdentifier, ControlLoop> controlLoopMap = new LinkedHashMap<>();
72 private final Map<UUID, ControlLoopElement> elementsOnThisParticipant = new LinkedHashMap<>();
75 private List<ControlLoopElementListener> listeners = new ArrayList<>();
78 * Constructor, set the participant ID and messageSender.
80 * @param parameters the parameters of the participant
81 * @param publisher the ParticipantMessage Publisher
83 public ControlLoopHandler(ParticipantParameters parameters, ParticipantMessagePublisher publisher) {
84 this.participantType = parameters.getIntermediaryParameters().getParticipantType();
85 this.participantId = parameters.getIntermediaryParameters().getParticipantId();
86 this.publisher = publisher;
89 public void registerControlLoopElementListener(ControlLoopElementListener listener) {
90 listeners.add(listener);
94 * Handle a control loop element state change message.
96 * @param id controlloop element id
97 * @param orderedState the current state
98 * @param newState the ordered state
99 * @return controlLoopElement the updated controlloop element
101 public ControlLoopElement updateControlLoopElementState(ToscaConceptIdentifier controlLoopId, UUID id,
102 ControlLoopOrderedState orderedState, ControlLoopState newState) {
105 LOGGER.warn("Cannot update Control loop element state, id is null");
109 // Update states of ControlLoopElement in controlLoopMap
110 for (var controlLoop : controlLoopMap.values()) {
111 var element = controlLoop.getElements().get(id);
112 if (element != null) {
113 element.setOrderedState(orderedState);
114 element.setState(newState);
116 var checkOpt = controlLoop.getElements().values().stream()
117 .filter(clElement -> !newState.equals(clElement.getState())).findAny();
118 if (checkOpt.isEmpty()) {
119 controlLoop.setState(newState);
120 controlLoop.setOrderedState(orderedState);
124 // Update states of ControlLoopElement in elementsOnThisParticipant
125 var clElement = elementsOnThisParticipant.get(id);
126 if (clElement != null) {
127 var controlLoopStateChangeAck = new ControlLoopAck(ParticipantMessageType.CONTROLLOOP_STATECHANGE_ACK);
128 controlLoopStateChangeAck.setParticipantId(participantId);
129 controlLoopStateChangeAck.setParticipantType(participantType);
130 controlLoopStateChangeAck.setControlLoopId(controlLoopId);
131 clElement.setOrderedState(orderedState);
132 clElement.setState(newState);
133 controlLoopStateChangeAck.getControlLoopResultMap().put(clElement.getId(), new ControlLoopElementAck(
134 newState, true, "Control loop element {} state changed to {}\", id, newState)"));
135 LOGGER.debug("Control loop element {} state changed to {}", id, newState);
136 controlLoopStateChangeAck.setMessage("ControlLoopElement state changed to {} " + newState);
137 controlLoopStateChangeAck.setResult(true);
138 publisher.sendControlLoopAck(controlLoopStateChangeAck);
145 * Handle a control loop element statistics.
147 * @param id controlloop element id
148 * @param elementStatistics control loop element Statistics
150 public void updateControlLoopElementStatistics(UUID id, ClElementStatistics elementStatistics) {
151 var clElement = elementsOnThisParticipant.get(id);
152 if (clElement != null) {
153 elementStatistics.setParticipantId(participantId);
154 elementStatistics.setId(id);
155 clElement.setClElementStatistics(elementStatistics);
160 * Handle a control loop state change message.
162 * @param stateChangeMsg the state change message
164 public void handleControlLoopStateChange(ControlLoopStateChange stateChangeMsg) {
165 if (stateChangeMsg.getControlLoopId() == null) {
169 var controlLoop = controlLoopMap.get(stateChangeMsg.getControlLoopId());
171 if (controlLoop == null) {
172 var controlLoopAck = new ControlLoopAck(ParticipantMessageType.CONTROL_LOOP_STATE_CHANGE);
173 controlLoopAck.setParticipantId(participantId);
174 controlLoopAck.setParticipantType(participantType);
175 controlLoopAck.setMessage("Control loop " + stateChangeMsg.getControlLoopId()
176 + " does not use this participant " + participantId);
177 controlLoopAck.setResult(false);
178 controlLoopAck.setResponseTo(stateChangeMsg.getMessageId());
179 controlLoopAck.setControlLoopId(stateChangeMsg.getControlLoopId());
180 publisher.sendControlLoopAck(controlLoopAck);
181 LOGGER.debug("Control loop {} does not use this participant", stateChangeMsg.getControlLoopId());
185 handleState(controlLoop, stateChangeMsg.getOrderedState());
189 * Method to handle state changes.
191 * @param controlLoop participant response
192 * @param orderedState controlloop ordered state
194 private void handleState(final ControlLoop controlLoop, ControlLoopOrderedState orderedState) {
195 switch (orderedState) {
197 handleUninitialisedState(controlLoop, orderedState);
200 handlePassiveState(controlLoop, orderedState);
203 handleRunningState(controlLoop, orderedState);
206 LOGGER.debug("StateChange message has no state, state is null {}", controlLoop.getDefinition());
212 * Handle a control loop update message.
214 * @param updateMsg the update message
216 public void handleControlLoopUpdate(ControlLoopUpdate updateMsg,
217 List<ControlLoopElementDefinition> clElementDefinitions) {
219 if (!updateMsg.appliesTo(participantType, participantId)) {
223 if (0 == updateMsg.getStartPhase()) {
224 handleClUpdatePhase0(updateMsg, clElementDefinitions);
226 handleClUpdatePhaseN(updateMsg, clElementDefinitions);
230 private void handleClUpdatePhase0(ControlLoopUpdate updateMsg,
231 List<ControlLoopElementDefinition> clElementDefinitions) {
232 var controlLoop = controlLoopMap.get(updateMsg.getControlLoopId());
234 // TODO: Updates to existing ControlLoops are not supported yet (Addition/Removal of ControlLoop
235 // elements to existing ControlLoop has to be supported).
236 if (controlLoop != null) {
237 var controlLoopUpdateAck = new ControlLoopAck(ParticipantMessageType.CONTROLLOOP_UPDATE_ACK);
238 controlLoopUpdateAck.setParticipantId(participantId);
239 controlLoopUpdateAck.setParticipantType(participantType);
241 controlLoopUpdateAck.setMessage("Control loop " + updateMsg.getControlLoopId()
242 + " already defined on participant " + participantId);
243 controlLoopUpdateAck.setResult(false);
244 controlLoopUpdateAck.setResponseTo(updateMsg.getMessageId());
245 controlLoopUpdateAck.setControlLoopId(updateMsg.getControlLoopId());
246 publisher.sendControlLoopAck(controlLoopUpdateAck);
250 if (updateMsg.getParticipantUpdatesList().isEmpty()) {
251 LOGGER.warn("No ControlLoopElement updates in message {}", updateMsg.getControlLoopId());
255 var clElements = storeElementsOnThisParticipant(updateMsg.getParticipantUpdatesList());
257 var clElementMap = prepareClElementMap(clElements);
258 controlLoop = new ControlLoop();
259 controlLoop.setDefinition(updateMsg.getControlLoopId());
260 controlLoop.setElements(clElementMap);
261 controlLoopMap.put(updateMsg.getControlLoopId(), controlLoop);
263 handleControlLoopElementUpdate(clElements, clElementDefinitions, updateMsg.getStartPhase(),
264 updateMsg.getControlLoopId());
267 private void handleClUpdatePhaseN(ControlLoopUpdate updateMsg,
268 List<ControlLoopElementDefinition> clElementDefinitions) {
270 var clElementList = updateMsg.getParticipantUpdatesList().stream()
271 .flatMap(participantUpdate -> participantUpdate.getControlLoopElementList().stream())
272 .filter(element -> participantType.equals(element.getParticipantType())).collect(Collectors.toList());
274 handleControlLoopElementUpdate(clElementList, clElementDefinitions, updateMsg.getStartPhase(),
275 updateMsg.getControlLoopId());
278 private void handleControlLoopElementUpdate(List<ControlLoopElement> clElements,
279 List<ControlLoopElementDefinition> clElementDefinitions, Integer startPhaseMsg,
280 ToscaConceptIdentifier controlLoopId) {
282 for (var element : clElements) {
283 var clElementNodeTemplate = getClElementNodeTemplate(clElementDefinitions, element.getDefinition());
284 if (clElementNodeTemplate != null) {
285 int startPhase = ParticipantUtils.findStartPhase(clElementNodeTemplate.getProperties());
286 if (startPhaseMsg.equals(startPhase)) {
287 for (var clElementListener : listeners) {
288 clElementListener.controlLoopElementUpdate(controlLoopId, element, clElementNodeTemplate);
293 } catch (PfModelException e) {
294 LOGGER.debug("Control loop element update failed {}", controlLoopId);
299 private ToscaNodeTemplate getClElementNodeTemplate(List<ControlLoopElementDefinition> clElementDefinitions,
300 ToscaConceptIdentifier clElementDefId) {
302 for (var clElementDefinition : clElementDefinitions) {
303 if (clElementDefId.getName().contains(
304 clElementDefinition.getClElementDefinitionId().getName())) {
305 return clElementDefinition.getControlLoopElementToscaNodeTemplate();
311 private List<ControlLoopElement> storeElementsOnThisParticipant(List<ParticipantUpdates> participantUpdates) {
312 var clElementList = participantUpdates.stream()
313 .flatMap(participantUpdate -> participantUpdate.getControlLoopElementList().stream())
314 .filter(element -> participantType.equals(element.getParticipantType())).collect(Collectors.toList());
316 for (var element : clElementList) {
317 elementsOnThisParticipant.put(element.getId(), element);
319 return clElementList;
322 private Map<UUID, ControlLoopElement> prepareClElementMap(List<ControlLoopElement> clElements) {
323 Map<UUID, ControlLoopElement> clElementMap = new LinkedHashMap<>();
324 for (var element : clElements) {
325 clElementMap.put(element.getId(), element);
331 * Method to handle when the new state from participant is UNINITIALISED state.
333 * @param controlLoop participant response
334 * @param orderedState orderedState
336 private void handleUninitialisedState(final ControlLoop controlLoop, final ControlLoopOrderedState orderedState) {
337 handleStateChange(controlLoop, orderedState);
338 controlLoopMap.remove(controlLoop.getDefinition());
339 controlLoop.getElements().values().forEach(element -> elementsOnThisParticipant.remove(element.getId()));
343 * Method to handle when the new state from participant is PASSIVE state.
345 * @param controlLoop participant response
346 * @param orderedState orderedState
348 private void handlePassiveState(final ControlLoop controlLoop, final ControlLoopOrderedState orderedState) {
349 handleStateChange(controlLoop, orderedState);
353 * Method to handle when the new state from participant is RUNNING state.
355 * @param controlLoop participant response
356 * @param orderedState orderedState
358 private void handleRunningState(final ControlLoop controlLoop, final ControlLoopOrderedState orderedState) {
359 handleStateChange(controlLoop, orderedState);
363 * Method to update the state of control loop elements.
365 * @param controlLoop participant status in memory
366 * @param orderedState orderedState the new ordered state the participant should have
368 private void handleStateChange(ControlLoop controlLoop, final ControlLoopOrderedState orderedState) {
370 if (orderedState.equals(controlLoop.getOrderedState())) {
371 var controlLoopAck = new ControlLoopAck(ParticipantMessageType.CONTROL_LOOP_STATE_CHANGE);
372 controlLoopAck.setParticipantId(participantId);
373 controlLoopAck.setParticipantType(participantType);
374 controlLoopAck.setMessage("Control loop is already in state " + orderedState);
375 controlLoopAck.setResult(false);
376 controlLoopAck.setControlLoopId(controlLoop.getDefinition());
377 publisher.sendControlLoopAck(controlLoopAck);
381 controlLoop.getElements().values().stream().forEach(clElement -> {
382 for (var clElementListener : listeners) {
384 clElementListener.controlLoopElementStateChange(controlLoop.getDefinition(), clElement.getId(),
385 clElement.getState(), orderedState);
386 } catch (PfModelException e) {
387 LOGGER.debug("Control loop element update failed {}", controlLoop.getDefinition());
394 * Get control loops as a {@link ConrolLoops} class.
396 * @return the control loops
398 public ControlLoops getControlLoops() {
399 var controlLoops = new ControlLoops();
400 controlLoops.setControlLoopList(new ArrayList<>(controlLoopMap.values()));
405 * Get properties of a controlloopelement.
407 * @param id the control loop element id
408 * @return the instance properties
410 public Map<String, ToscaProperty> getClElementInstanceProperties(UUID id) {
411 Map<String, ToscaProperty> propertiesMap = new HashMap<>();
412 for (var controlLoop : controlLoopMap.values()) {
413 var element = controlLoop.getElements().get(id);
414 if (element != null) {
415 propertiesMap.putAll(element.getPropertiesMap());
418 return propertiesMap;