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 controlLoopId the controlLoop Id
97 * @param id the controlLoop UUID
98 * @param orderedState the current state
99 * @param newState the ordered state
100 * @return controlLoopElement the updated controlloop element
102 public ControlLoopElement updateControlLoopElementState(ToscaConceptIdentifier controlLoopId, UUID id,
103 ControlLoopOrderedState orderedState, ControlLoopState newState) {
106 LOGGER.warn("Cannot update Control loop element state, id is null");
110 // Update states of ControlLoopElement in controlLoopMap
111 for (var controlLoop : controlLoopMap.values()) {
112 var element = controlLoop.getElements().get(id);
113 if (element != null) {
114 element.setOrderedState(orderedState);
115 element.setState(newState);
117 var checkOpt = controlLoop.getElements().values().stream()
118 .filter(clElement -> !newState.equals(clElement.getState())).findAny();
119 if (checkOpt.isEmpty()) {
120 controlLoop.setState(newState);
121 controlLoop.setOrderedState(orderedState);
125 // Update states of ControlLoopElement in elementsOnThisParticipant
126 var clElement = elementsOnThisParticipant.get(id);
127 if (clElement != null) {
128 var controlLoopStateChangeAck = new ControlLoopAck(ParticipantMessageType.CONTROLLOOP_STATECHANGE_ACK);
129 controlLoopStateChangeAck.setParticipantId(participantId);
130 controlLoopStateChangeAck.setParticipantType(participantType);
131 controlLoopStateChangeAck.setControlLoopId(controlLoopId);
132 clElement.setOrderedState(orderedState);
133 clElement.setState(newState);
134 controlLoopStateChangeAck.getControlLoopResultMap().put(clElement.getId(), new ControlLoopElementAck(
135 newState, true, "Control loop element {} state changed to {}\", id, newState)"));
136 LOGGER.debug("Control loop element {} state changed to {}", id, newState);
137 controlLoopStateChangeAck.setMessage("ControlLoopElement state changed to {} " + newState);
138 controlLoopStateChangeAck.setResult(true);
139 publisher.sendControlLoopAck(controlLoopStateChangeAck);
146 * Handle a control loop element statistics.
148 * @param id controlloop element id
149 * @param elementStatistics control loop element Statistics
151 public void updateControlLoopElementStatistics(UUID id, ClElementStatistics elementStatistics) {
152 var clElement = elementsOnThisParticipant.get(id);
153 if (clElement != null) {
154 elementStatistics.setParticipantId(participantId);
155 elementStatistics.setId(id);
156 clElement.setClElementStatistics(elementStatistics);
161 * Handle a control loop state change message.
163 * @param stateChangeMsg the state change message
164 * @param clElementDefinitions the list of ControlLoopElementDefinition
166 public void handleControlLoopStateChange(ControlLoopStateChange stateChangeMsg,
167 List<ControlLoopElementDefinition> clElementDefinitions) {
168 if (stateChangeMsg.getControlLoopId() == null) {
172 var controlLoop = controlLoopMap.get(stateChangeMsg.getControlLoopId());
174 if (controlLoop == null) {
175 var controlLoopAck = new ControlLoopAck(ParticipantMessageType.CONTROLLOOP_STATECHANGE_ACK);
176 controlLoopAck.setParticipantId(participantId);
177 controlLoopAck.setParticipantType(participantType);
178 controlLoopAck.setMessage("Control loop " + stateChangeMsg.getControlLoopId()
179 + " does not use this participant " + participantId);
180 controlLoopAck.setResult(false);
181 controlLoopAck.setResponseTo(stateChangeMsg.getMessageId());
182 controlLoopAck.setControlLoopId(stateChangeMsg.getControlLoopId());
183 publisher.sendControlLoopAck(controlLoopAck);
184 LOGGER.debug("Control loop {} does not use this participant", stateChangeMsg.getControlLoopId());
188 handleState(controlLoop, stateChangeMsg.getOrderedState(), stateChangeMsg.getStartPhase(),
189 clElementDefinitions);
193 * Method to handle state changes.
195 * @param controlLoop participant response
196 * @param orderedState controlloop ordered state
197 * @param startPhaseMsg startPhase from message
198 * @param clElementDefinitions the list of ControlLoopElementDefinition
200 private void handleState(final ControlLoop controlLoop, ControlLoopOrderedState orderedState, Integer startPhaseMsg,
201 List<ControlLoopElementDefinition> clElementDefinitions) {
202 switch (orderedState) {
204 handleUninitialisedState(controlLoop, orderedState, startPhaseMsg, clElementDefinitions);
207 handlePassiveState(controlLoop, orderedState, startPhaseMsg, clElementDefinitions);
210 handleRunningState(controlLoop, orderedState, startPhaseMsg, clElementDefinitions);
213 LOGGER.debug("StateChange message has no state, state is null {}", controlLoop.getDefinition());
219 * Handle a control loop update message.
221 * @param updateMsg the update message
222 * @param clElementDefinitions the list of ControlLoopElementDefinition
224 public void handleControlLoopUpdate(ControlLoopUpdate updateMsg,
225 List<ControlLoopElementDefinition> clElementDefinitions) {
227 if (!updateMsg.appliesTo(participantType, participantId)) {
231 if (0 == updateMsg.getStartPhase()) {
232 handleClUpdatePhase0(updateMsg, clElementDefinitions);
234 handleClUpdatePhaseN(updateMsg, clElementDefinitions);
238 private void handleClUpdatePhase0(ControlLoopUpdate updateMsg,
239 List<ControlLoopElementDefinition> clElementDefinitions) {
240 var controlLoop = controlLoopMap.get(updateMsg.getControlLoopId());
242 // TODO: Updates to existing ControlLoops are not supported yet (Addition/Removal of ControlLoop
243 // elements to existing ControlLoop has to be supported).
244 if (controlLoop != null) {
245 var controlLoopUpdateAck = new ControlLoopAck(ParticipantMessageType.CONTROLLOOP_UPDATE_ACK);
246 controlLoopUpdateAck.setParticipantId(participantId);
247 controlLoopUpdateAck.setParticipantType(participantType);
249 controlLoopUpdateAck.setMessage("Control loop " + updateMsg.getControlLoopId()
250 + " already defined on participant " + participantId);
251 controlLoopUpdateAck.setResult(false);
252 controlLoopUpdateAck.setResponseTo(updateMsg.getMessageId());
253 controlLoopUpdateAck.setControlLoopId(updateMsg.getControlLoopId());
254 publisher.sendControlLoopAck(controlLoopUpdateAck);
258 if (updateMsg.getParticipantUpdatesList().isEmpty()) {
259 LOGGER.warn("No ControlLoopElement updates in message {}", updateMsg.getControlLoopId());
263 var clElements = storeElementsOnThisParticipant(updateMsg.getParticipantUpdatesList());
265 var clElementMap = prepareClElementMap(clElements);
266 controlLoop = new ControlLoop();
267 controlLoop.setDefinition(updateMsg.getControlLoopId());
268 controlLoop.setElements(clElementMap);
269 controlLoopMap.put(updateMsg.getControlLoopId(), controlLoop);
271 handleControlLoopElementUpdate(clElements, clElementDefinitions, updateMsg.getStartPhase(),
272 updateMsg.getControlLoopId());
275 private void handleClUpdatePhaseN(ControlLoopUpdate updateMsg,
276 List<ControlLoopElementDefinition> clElementDefinitions) {
278 var clElementList = updateMsg.getParticipantUpdatesList().stream()
279 .flatMap(participantUpdate -> participantUpdate.getControlLoopElementList().stream())
280 .filter(element -> participantType.equals(element.getParticipantType())).collect(Collectors.toList());
282 handleControlLoopElementUpdate(clElementList, clElementDefinitions, updateMsg.getStartPhase(),
283 updateMsg.getControlLoopId());
286 private void handleControlLoopElementUpdate(List<ControlLoopElement> clElements,
287 List<ControlLoopElementDefinition> clElementDefinitions, Integer startPhaseMsg,
288 ToscaConceptIdentifier controlLoopId) {
290 for (var element : clElements) {
291 var clElementNodeTemplate = getClElementNodeTemplate(clElementDefinitions, element.getDefinition());
292 if (clElementNodeTemplate != null) {
293 int startPhase = ParticipantUtils.findStartPhase(clElementNodeTemplate.getProperties());
294 if (startPhaseMsg.equals(startPhase)) {
295 for (var clElementListener : listeners) {
296 clElementListener.controlLoopElementUpdate(controlLoopId, element, clElementNodeTemplate);
301 } catch (PfModelException e) {
302 LOGGER.debug("Control loop element update failed {}", controlLoopId);
307 private ToscaNodeTemplate getClElementNodeTemplate(List<ControlLoopElementDefinition> clElementDefinitions,
308 ToscaConceptIdentifier clElementDefId) {
310 for (var clElementDefinition : clElementDefinitions) {
311 if (clElementDefId.getName().contains(clElementDefinition.getClElementDefinitionId().getName())) {
312 return clElementDefinition.getControlLoopElementToscaNodeTemplate();
318 private List<ControlLoopElement> storeElementsOnThisParticipant(List<ParticipantUpdates> participantUpdates) {
319 var clElementList = participantUpdates.stream()
320 .flatMap(participantUpdate -> participantUpdate.getControlLoopElementList().stream())
321 .filter(element -> participantType.equals(element.getParticipantType())).collect(Collectors.toList());
323 for (var element : clElementList) {
324 elementsOnThisParticipant.put(element.getId(), element);
326 return clElementList;
329 private Map<UUID, ControlLoopElement> prepareClElementMap(List<ControlLoopElement> clElements) {
330 Map<UUID, ControlLoopElement> clElementMap = new LinkedHashMap<>();
331 for (var element : clElements) {
332 clElementMap.put(element.getId(), element);
338 * Method to handle when the new state from participant is UNINITIALISED state.
340 * @param controlLoop participant response
341 * @param orderedState orderedState
342 * @param startPhaseMsg startPhase from message
343 * @param clElementDefinitions the list of ControlLoopElementDefinition
345 private void handleUninitialisedState(final ControlLoop controlLoop, final ControlLoopOrderedState orderedState,
346 Integer startPhaseMsg, List<ControlLoopElementDefinition> clElementDefinitions) {
347 handleStateChange(controlLoop, orderedState, startPhaseMsg, clElementDefinitions);
348 boolean isAllUninitialised = controlLoop.getElements().values().stream()
349 .filter(element -> !ControlLoopState.UNINITIALISED.equals(element.getState())).findAny().isEmpty();
350 if (isAllUninitialised) {
351 controlLoopMap.remove(controlLoop.getDefinition());
352 controlLoop.getElements().values().forEach(element -> elementsOnThisParticipant.remove(element.getId()));
357 * Method to handle when the new state from participant is PASSIVE state.
359 * @param controlLoop participant response
360 * @param orderedState orderedState
361 * @param startPhaseMsg startPhase from message
362 * @param clElementDefinitions the list of ControlLoopElementDefinition
364 private void handlePassiveState(final ControlLoop controlLoop, final ControlLoopOrderedState orderedState,
365 Integer startPhaseMsg, List<ControlLoopElementDefinition> clElementDefinitions) {
366 handleStateChange(controlLoop, orderedState, startPhaseMsg, clElementDefinitions);
370 * Method to handle when the new state from participant is RUNNING state.
372 * @param controlLoop participant response
373 * @param orderedState orderedState
374 * @param startPhaseMsg startPhase from message
375 * @param clElementDefinitions the list of ControlLoopElementDefinition
377 private void handleRunningState(final ControlLoop controlLoop, final ControlLoopOrderedState orderedState,
378 Integer startPhaseMsg, List<ControlLoopElementDefinition> clElementDefinitions) {
379 handleStateChange(controlLoop, orderedState, startPhaseMsg, clElementDefinitions);
383 * Method to update the state of control loop elements.
385 * @param controlLoop participant status in memory
386 * @param orderedState orderedState the new ordered state the participant should have
387 * @param startPhaseMsg startPhase from message
388 * @param clElementDefinitions the list of ControlLoopElementDefinition
390 private void handleStateChange(ControlLoop controlLoop, final ControlLoopOrderedState orderedState,
391 Integer startPhaseMsg, List<ControlLoopElementDefinition> clElementDefinitions) {
393 if (orderedState.equals(controlLoop.getOrderedState())) {
394 var controlLoopAck = new ControlLoopAck(ParticipantMessageType.CONTROLLOOP_STATECHANGE_ACK);
395 controlLoopAck.setParticipantId(participantId);
396 controlLoopAck.setParticipantType(participantType);
397 controlLoopAck.setMessage("Control loop is already in state " + orderedState);
398 controlLoopAck.setResult(false);
399 controlLoopAck.setControlLoopId(controlLoop.getDefinition());
400 publisher.sendControlLoopAck(controlLoopAck);
404 controlLoop.getElements().values().stream().forEach(clElement -> controlLoopElementStateChange(controlLoop,
405 orderedState, clElement, startPhaseMsg, clElementDefinitions));
408 private void controlLoopElementStateChange(ControlLoop controlLoop, ControlLoopOrderedState orderedState,
409 ControlLoopElement clElement, Integer startPhaseMsg,
410 List<ControlLoopElementDefinition> clElementDefinitions) {
411 var clElementNodeTemplate = getClElementNodeTemplate(clElementDefinitions, clElement.getDefinition());
412 if (clElementNodeTemplate != null) {
413 int startPhase = ParticipantUtils.findStartPhase(clElementNodeTemplate.getProperties());
414 if (startPhaseMsg.equals(startPhase)) {
415 for (var clElementListener : listeners) {
417 clElementListener.controlLoopElementStateChange(controlLoop.getDefinition(), clElement.getId(),
418 clElement.getState(), orderedState);
419 } catch (PfModelException e) {
420 LOGGER.debug("Control loop element update failed {}", controlLoop.getDefinition());
428 * Get control loops as a {@link ConrolLoops} class.
430 * @return the control loops
432 public ControlLoops getControlLoops() {
433 var controlLoops = new ControlLoops();
434 controlLoops.setControlLoopList(new ArrayList<>(controlLoopMap.values()));
439 * Get properties of a controlloopelement.
441 * @param id the control loop element id
442 * @return the instance properties
444 public Map<String, ToscaProperty> getClElementInstanceProperties(UUID id) {
445 Map<String, ToscaProperty> propertiesMap = new HashMap<>();
446 for (var controlLoop : controlLoopMap.values()) {
447 var element = controlLoop.getElements().get(id);
448 if (element != null) {
449 propertiesMap.putAll(element.getPropertiesMap());
452 return propertiesMap;