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.LinkedHashMap;
26 import java.util.List;
28 import java.util.UUID;
29 import java.util.stream.Collectors;
31 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ClElementStatistics;
32 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoop;
33 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElement;
34 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElementAck;
35 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElementDefinition;
36 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopOrderedState;
37 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopState;
38 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoops;
39 import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantUpdates;
40 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ControlLoopAck;
41 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ControlLoopStateChange;
42 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ControlLoopUpdate;
43 import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantMessageType;
44 import org.onap.policy.clamp.controlloop.participant.intermediary.api.ControlLoopElementListener;
45 import org.onap.policy.clamp.controlloop.participant.intermediary.comm.ParticipantMessagePublisher;
46 import org.onap.policy.clamp.controlloop.participant.intermediary.parameters.ParticipantParameters;
47 import org.onap.policy.models.base.PfModelException;
48 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
49 import org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52 import org.springframework.stereotype.Component;
55 * This class is responsible for managing the state of all control loops in the participant.
58 public class ControlLoopHandler {
59 private static final Logger LOGGER = LoggerFactory.getLogger(ControlLoopHandler.class);
61 private final ToscaConceptIdentifier participantType;
62 private final ToscaConceptIdentifier participantId;
63 private final ParticipantMessagePublisher publisher;
66 private final Map<ToscaConceptIdentifier, ControlLoop> controlLoopMap = new LinkedHashMap<>();
69 private final Map<UUID, ControlLoopElement> elementsOnThisParticipant = new LinkedHashMap<>();
72 private List<ControlLoopElementListener> listeners = new ArrayList<>();
75 * Constructor, set the participant ID and messageSender.
77 * @param parameters the parameters of the participant
78 * @param publisher the ParticipantMessage Publisher
80 public ControlLoopHandler(ParticipantParameters parameters, ParticipantMessagePublisher publisher) {
81 this.participantType = parameters.getIntermediaryParameters().getParticipantType();
82 this.participantId = parameters.getIntermediaryParameters().getParticipantId();
83 this.publisher = publisher;
86 public void registerControlLoopElementListener(ControlLoopElementListener listener) {
87 listeners.add(listener);
91 * Handle a control loop element state change message.
93 * @param id controlloop element id
94 * @param orderedState the current state
95 * @param newState the ordered state
96 * @return controlLoopElement the updated controlloop element
98 public ControlLoopElement updateControlLoopElementState(ToscaConceptIdentifier controlLoopId, UUID id,
99 ControlLoopOrderedState orderedState, ControlLoopState newState) {
102 LOGGER.warn("Cannot update Control loop element state, id is null");
106 // Update states of ControlLoopElement in controlLoopMap
107 for (var controlLoop : controlLoopMap.values()) {
108 var element = controlLoop.getElements().get(id);
109 if (element != null) {
110 element.setOrderedState(orderedState);
111 element.setState(newState);
113 var checkOpt = controlLoop.getElements().values().stream()
114 .filter(clElement -> !newState.equals(clElement.getState())).findAny();
115 if (checkOpt.isEmpty()) {
116 controlLoop.setState(newState);
117 controlLoop.setOrderedState(orderedState);
121 // Update states of ControlLoopElement in elementsOnThisParticipant
122 var clElement = elementsOnThisParticipant.get(id);
123 if (clElement != null) {
124 var controlLoopStateChangeAck = new ControlLoopAck(ParticipantMessageType.CONTROLLOOP_STATECHANGE_ACK);
125 controlLoopStateChangeAck.setParticipantId(participantId);
126 controlLoopStateChangeAck.setParticipantType(participantType);
127 controlLoopStateChangeAck.setControlLoopId(controlLoopId);
128 clElement.setOrderedState(orderedState);
129 clElement.setState(newState);
130 controlLoopStateChangeAck.getControlLoopResultMap().put(clElement.getId(), new ControlLoopElementAck(
131 newState, true, "Control loop element {} state changed to {}\", id, newState)"));
132 LOGGER.debug("Control loop element {} state changed to {}", id, newState);
133 controlLoopStateChangeAck.setMessage("ControlLoopElement state changed to {} " + newState);
134 controlLoopStateChangeAck.setResult(true);
135 publisher.sendControlLoopAck(controlLoopStateChangeAck);
142 * Handle a control loop element statistics.
144 * @param id controlloop element id
145 * @param elementStatistics control loop element Statistics
147 public void updateControlLoopElementStatistics(UUID id, ClElementStatistics elementStatistics) {
148 var clElement = elementsOnThisParticipant.get(id);
149 if (clElement != null) {
150 elementStatistics.setParticipantId(participantId);
151 elementStatistics.setId(id);
152 clElement.setClElementStatistics(elementStatistics);
157 * Handle a control loop state change message.
159 * @param stateChangeMsg the state change message
161 public void handleControlLoopStateChange(ControlLoopStateChange stateChangeMsg) {
162 if (stateChangeMsg.getControlLoopId() == null) {
166 var controlLoop = controlLoopMap.get(stateChangeMsg.getControlLoopId());
168 if (controlLoop == null) {
169 var controlLoopAck = new ControlLoopAck(ParticipantMessageType.CONTROL_LOOP_STATE_CHANGE);
170 controlLoopAck.setParticipantId(participantId);
171 controlLoopAck.setParticipantType(participantType);
172 controlLoopAck.setMessage("Control loop " + stateChangeMsg.getControlLoopId()
173 + " does not use this participant " + participantId);
174 controlLoopAck.setResult(false);
175 controlLoopAck.setResponseTo(stateChangeMsg.getMessageId());
176 controlLoopAck.setControlLoopId(stateChangeMsg.getControlLoopId());
177 publisher.sendControlLoopAck(controlLoopAck);
178 LOGGER.debug("Control loop {} does not use this participant", stateChangeMsg.getControlLoopId());
182 handleState(controlLoop, stateChangeMsg.getOrderedState());
186 * Method to handle state changes.
188 * @param controlLoop participant response
189 * @param orderedState controlloop ordered state
191 private void handleState(final ControlLoop controlLoop, ControlLoopOrderedState orderedState) {
192 switch (orderedState) {
194 handleUninitialisedState(controlLoop, orderedState);
197 handlePassiveState(controlLoop, orderedState);
200 handleRunningState(controlLoop, orderedState);
203 LOGGER.debug("StateChange message has no state, state is null {}", controlLoop.getDefinition());
209 * Handle a control loop update message.
211 * @param updateMsg the update message
213 public void handleControlLoopUpdate(ControlLoopUpdate updateMsg,
214 List<ControlLoopElementDefinition> clElementDefinitions) {
216 if (!updateMsg.appliesTo(participantType, participantId)) {
220 var controlLoop = controlLoopMap.get(updateMsg.getControlLoopId());
222 // TODO: Updates to existing ControlLoops are not supported yet (Addition/Removal of ControlLoop
223 // elements to existing ControlLoop has to be supported).
224 if (controlLoop != null) {
225 var controlLoopUpdateAck = new ControlLoopAck(ParticipantMessageType.CONTROLLOOP_UPDATE_ACK);
226 controlLoopUpdateAck.setParticipantId(participantId);
227 controlLoopUpdateAck.setParticipantType(participantType);
229 controlLoopUpdateAck.setMessage("Control loop " + updateMsg.getControlLoopId()
230 + " already defined on participant " + participantId);
231 controlLoopUpdateAck.setResult(false);
232 controlLoopUpdateAck.setResponseTo(updateMsg.getMessageId());
233 controlLoopUpdateAck.setControlLoopId(updateMsg.getControlLoopId());
234 publisher.sendControlLoopAck(controlLoopUpdateAck);
238 if (updateMsg.getParticipantUpdatesList().isEmpty()) {
239 LOGGER.warn("No ControlLoopElement updates in message {}", updateMsg.getControlLoopId());
243 var clElements = storeElementsOnThisParticipant(updateMsg.getParticipantUpdatesList());
246 for (var element : clElements) {
247 var clElementNodeTemplate =
248 getClElementNodeTemplate(clElementDefinitions, element.getDefinition());
249 for (var clElementListener : listeners) {
250 clElementListener.controlLoopElementUpdate(updateMsg.getControlLoopId(), element,
251 clElementNodeTemplate);
254 } catch (PfModelException e) {
255 LOGGER.debug("Control loop element update failed {}", updateMsg.getControlLoopId());
258 var clElementMap = prepareClElementMap(clElements);
259 controlLoop = new ControlLoop();
260 controlLoop.setDefinition(updateMsg.getControlLoopId());
261 controlLoop.setElements(clElementMap);
262 controlLoopMap.put(updateMsg.getControlLoopId(), controlLoop);
265 private ToscaNodeTemplate getClElementNodeTemplate(List<ControlLoopElementDefinition> clElementDefinitions,
266 ToscaConceptIdentifier clElementDefId) {
267 for (var clElementDefinition : clElementDefinitions) {
268 if (clElementDefinition.getClElementDefinitionId().equals(clElementDefId)) {
269 return clElementDefinition.getControlLoopElementToscaNodeTemplate();
275 private List<ControlLoopElement> storeElementsOnThisParticipant(List<ParticipantUpdates> participantUpdates) {
276 var clElementMap = participantUpdates.stream()
277 .flatMap(participantUpdate -> participantUpdate.getControlLoopElementList().stream())
278 .filter(element -> participantType.equals(element.getParticipantType())).collect(Collectors.toList());
280 for (var element : clElementMap) {
281 elementsOnThisParticipant.put(element.getId(), element);
286 private Map<UUID, ControlLoopElement> prepareClElementMap(List<ControlLoopElement> clElements) {
287 Map<UUID, ControlLoopElement> clElementMap = new LinkedHashMap<>();
288 for (var element : clElements) {
289 clElementMap.put(element.getId(), element);
295 * Method to handle when the new state from participant is UNINITIALISED state.
297 * @param controlLoop participant response
298 * @param orderedState orderedState
300 private void handleUninitialisedState(final ControlLoop controlLoop, final ControlLoopOrderedState orderedState) {
301 handleStateChange(controlLoop, orderedState);
302 controlLoopMap.remove(controlLoop.getDefinition());
303 controlLoop.getElements().values().forEach(element -> elementsOnThisParticipant.remove(element.getId()));
307 * Method to handle when the new state from participant is PASSIVE state.
309 * @param controlLoop participant response
310 * @param orderedState orderedState
312 private void handlePassiveState(final ControlLoop controlLoop, final ControlLoopOrderedState orderedState) {
313 handleStateChange(controlLoop, orderedState);
317 * Method to handle when the new state from participant is RUNNING state.
319 * @param controlLoop participant response
320 * @param orderedState orderedState
322 private void handleRunningState(final ControlLoop controlLoop, final ControlLoopOrderedState orderedState) {
323 handleStateChange(controlLoop, orderedState);
327 * Method to update the state of control loop elements.
329 * @param controlLoop participant status in memory
330 * @param orderedState orderedState the new ordered state the participant should have
332 private void handleStateChange(ControlLoop controlLoop, final ControlLoopOrderedState orderedState) {
334 if (orderedState.equals(controlLoop.getOrderedState())) {
335 var controlLoopAck = new ControlLoopAck(ParticipantMessageType.CONTROL_LOOP_STATE_CHANGE);
336 controlLoopAck.setParticipantId(participantId);
337 controlLoopAck.setParticipantType(participantType);
338 controlLoopAck.setMessage("Control loop is already in state " + orderedState);
339 controlLoopAck.setResult(false);
340 controlLoopAck.setControlLoopId(controlLoop.getDefinition());
341 publisher.sendControlLoopAck(controlLoopAck);
345 controlLoop.getElements().values().stream().forEach(clElement -> {
346 for (var clElementListener : listeners) {
348 clElementListener.controlLoopElementStateChange(controlLoop.getDefinition(),
349 clElement.getId(), clElement.getState(), orderedState);
350 } catch (PfModelException e) {
351 LOGGER.debug("Control loop element update failed {}", controlLoop.getDefinition());
358 * Get control loops as a {@link ConrolLoops} class.
360 * @return the control loops
362 public ControlLoops getControlLoops() {
363 var controlLoops = new ControlLoops();
364 controlLoops.setControlLoopList(new ArrayList<>(controlLoopMap.values()));