## Modules
-The PID controller can be used in openHAB's [rule engine](https://www.openhab.org/docs/configuration/rules-dsl.html). This automation provides a trigger and an action module.
+The PID controller can be used in openHAB's [rule engine](https://www.openhab.org/docs/configuration/rules-dsl.html).
+This automation provides a trigger module ("PID controller triggers").
+The return value is used to feed the Action module "Item Action" aka "send a command", which controls the actuator.
+
+To configure a rule, you need to add a Trigger ("PID controller triggers") and an Action ("Item Action").
+Select the Item you like to control in the "Item Action" and leave the command empty.
### Trigger
| `kdTimeConstant` | Decimal | D-T1: [Derivative Gain Time Constant](#derivative-time-constant-d-t1-parameter) in sec. | Y |
| `commandItem` | String | Send a String "RESET" to this item to reset the I and the D part to 0. | N |
| `loopTime` | Decimal | The interval the output value will be updated in milliseconds. Note: the output will also be updated when the input value or the setpoint changes. | Y |
-
+| `pInspector` | Item | Name of the debug Item for the current P part | N |
+| `iInspector` | Item | Name of the debug Item for the current I part | N |
+| `dInspector` | Item | Name of the debug Item for the current D part | N |
+| `eInspector` | Item | Name of the debug Item for the current regulation difference (error) | N |
The `loopTime` should be max a tenth of the system response.
E.g. the heating needs 10 min to heat up the room, the loop time should be max 1 min.
Lower values won't harm, but need more calculation resources.
-### Action
-
-This module writes the PID controller's output value into the `output` Item and provides debugging abilities.
-
-| Name | Type | Description | Required |
-|--------------|------|----------------------------------------------------------------------|----------|
-| `output` | Item | Name of the output Item (e.g. the valve actuator 0-100%) | Y |
-| `pInspector` | Item | Name of the debug Item for the current P part | N |
-| `iInspector` | Item | Name of the debug Item for the current I part | N |
-| `dInspector` | Item | Name of the debug Item for the current D part | N |
-| `eInspector` | Item | Name of the debug Item for the current regulation difference (error) | N |
-
You can view the internal P, I and D parts of the controller with the inspector Items.
These values are useful when tuning the controller.
-They are updated everytime the output is updated.
+They are updated every time the output is updated.
## Proportional (P) Gain Parameter
So, this will be described in the following.
To be able to proceed with this method, you need to visualize the input and the output value of the PID controller over time.
-It's also good to visualize the individual P, I and D parts (these are forming the output value) via the inspector Items.
-The visualization can be done by the analyze function in Main UI or by adding a persistence and use Grafana for example.
+It's also good to visualize the individual P, I and D parts (these are forming the output value) via the inspector items.
+The visualization could be done by adding a persistence and use Grafana for example.
After you added a [Rule](https://www.openhab.org/docs/configuration/rules-dsl.html) with above trigger and action module and configured those, proceed with the following steps:
public static final String I_INSPECTOR = "iInspector";
public static final String D_INSPECTOR = "dInspector";
public static final String E_INSPECTOR = "eInspector";
- public static final String OUTPUT = "output";
+ public static final String COMMAND = "command";
}
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.automation.pidcontroller.internal.handler.PIDControllerActionHandler;
import org.openhab.automation.pidcontroller.internal.handler.PIDControllerTriggerHandler;
-import org.openhab.core.automation.Action;
import org.openhab.core.automation.Module;
import org.openhab.core.automation.Trigger;
import org.openhab.core.automation.handler.BaseModuleHandlerFactory;
@Component(service = ModuleHandlerFactory.class, configurationPid = "action.pidcontroller")
@NonNullByDefault
public class PIDControllerModuleHandlerFactory extends BaseModuleHandlerFactory {
- private static final Collection<String> TYPES = Set.of(PIDControllerTriggerHandler.MODULE_TYPE_ID,
- PIDControllerActionHandler.MODULE_TYPE_ID);
+ private static final Collection<String> TYPES = Set.of(PIDControllerTriggerHandler.MODULE_TYPE_ID);
private ItemRegistry itemRegistry;
private EventPublisher eventPublisher;
private BundleContext bundleContext;
switch (module.getTypeUID()) {
case PIDControllerTriggerHandler.MODULE_TYPE_ID:
return new PIDControllerTriggerHandler((Trigger) module, itemRegistry, eventPublisher, bundleContext);
- case PIDControllerActionHandler.MODULE_TYPE_ID:
- return new PIDControllerActionHandler((Action) module, itemRegistry, eventPublisher);
}
return null;
+++ /dev/null
-/**
- * Copyright (c) 2010-2021 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.automation.pidcontroller.internal.handler;
-
-import static org.openhab.automation.pidcontroller.internal.PIDControllerConstants.AUTOMATION_NAME;
-
-import java.math.BigDecimal;
-import java.util.Map;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.core.automation.Action;
-import org.openhab.core.automation.handler.ActionHandler;
-import org.openhab.core.automation.handler.BaseModuleHandler;
-import org.openhab.core.config.core.Configuration;
-import org.openhab.core.events.EventPublisher;
-import org.openhab.core.items.ItemRegistry;
-import org.openhab.core.items.events.ItemCommandEvent;
-import org.openhab.core.items.events.ItemEventFactory;
-import org.openhab.core.library.types.DecimalType;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- *
- * @author Hilbrand Bouwkamp - Initial Contribution
- * @author Fabian Wolter - Add PID debugging items
- */
-@NonNullByDefault
-public class PIDControllerActionHandler extends BaseModuleHandler<Action> implements ActionHandler {
- public static final String MODULE_TYPE_ID = AUTOMATION_NAME + ".action";
-
- private final Logger logger = LoggerFactory.getLogger(PIDControllerActionHandler.class);
-
- private ItemRegistry itemRegistry;
- private EventPublisher eventPublisher;
-
- public PIDControllerActionHandler(Action module, ItemRegistry itemRegistry, EventPublisher eventPublisher) {
- super(module);
- this.itemRegistry = itemRegistry;
- this.eventPublisher = eventPublisher;
- }
-
- @Override
- public @Nullable Map<String, Object> execute(Map<String, Object> context) {
- final Configuration configuration = module.getConfiguration();
-
- context.forEach((k, v) -> {
- // Remove triggername from key to get raw trigger param
- String itemKey = k.substring(k.lastIndexOf('.') + 1);
- String itemName = (String) configuration.get(itemKey);
-
- if (itemName == null || itemName.isBlank()) {
- // try original key name (<triggername>.<trigger_param>)
- itemName = (String) configuration.get(k);
- if (itemName == null || itemName.isBlank()) {
- return;
- }
- }
- if (v instanceof BigDecimal) {
- final BigDecimal command = (BigDecimal) v;
- final DecimalType outputValue = new DecimalType(command);
- final ItemCommandEvent itemCommandEvent = ItemEventFactory.createCommandEvent(itemName, outputValue);
-
- eventPublisher.post(itemCommandEvent);
- } else {
- logger.warn(
- "Command was not posted because either the configuration was not correct or a service was missing: ItemName: {}, Command: {}, eventPublisher: {}, ItemRegistry: {}",
- itemName, v, eventPublisher, itemRegistry);
- }
- });
- return null;
- }
-}
import static org.openhab.automation.pidcontroller.internal.PIDControllerConstants.*;
import java.math.BigDecimal;
-import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.automation.Trigger;
import org.openhab.core.automation.handler.BaseTriggerModuleHandler;
import org.openhab.core.automation.handler.TriggerHandlerCallback;
-import org.openhab.core.common.NamedThreadFactory;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.events.Event;
import org.openhab.core.events.EventFilter;
import org.openhab.core.items.events.ItemEventFactory;
import org.openhab.core.items.events.ItemStateChangedEvent;
import org.openhab.core.items.events.ItemStateEvent;
+import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
public static final String MODULE_TYPE_ID = AUTOMATION_NAME + ".trigger";
private static final Set<String> SUBSCRIBED_EVENT_TYPES = Set.of(ItemStateEvent.TYPE, ItemStateChangedEvent.TYPE);
private final Logger logger = LoggerFactory.getLogger(PIDControllerTriggerHandler.class);
- private final ScheduledExecutorService scheduler = Executors
- .newSingleThreadScheduledExecutor(new NamedThreadFactory("automation-" + AUTOMATION_NAME, true));
private final ServiceRegistration<?> eventSubscriberRegistration;
private final PIDController controller;
private final int loopTimeMs;
- private @Nullable ScheduledFuture<?> controllerjob;
private long previousTimeMs = System.currentTimeMillis();
private Item inputItem;
private Item setpointItem;
private Optional<String> commandTopic;
private EventFilter eventFilter;
private EventPublisher eventPublisher;
+ private @Nullable String pInspector;
+ private @Nullable String iInspector;
+ private @Nullable String dInspector;
+ private @Nullable String eInspector;
public PIDControllerTriggerHandler(Trigger module, ItemRegistry itemRegistry, EventPublisher eventPublisher,
BundleContext bundleContext) {
double kiAdjuster = getDoubleFromConfig(config, CONFIG_KI_GAIN);
double kdAdjuster = getDoubleFromConfig(config, CONFIG_KD_GAIN);
double kdTimeConstant = getDoubleFromConfig(config, CONFIG_KD_TIMECONSTANT);
+ pInspector = (String) config.get(P_INSPECTOR);
+ iInspector = (String) config.get(I_INSPECTOR);
+ dInspector = (String) config.get(D_INSPECTOR);
+ eInspector = (String) config.get(E_INSPECTOR);
loopTimeMs = ((BigDecimal) requireNonNull(config.get(CONFIG_LOOP_TIME), CONFIG_LOOP_TIME + " is not set"))
.intValue();
eventFilter = event -> {
String topic = event.getTopic();
- return topic.equals("openhab/items/" + inputItemName + "/state")
- || topic.equals("openhab/items/" + inputItemName + "/statechanged")
- || topic.equals("openhab/items/" + setpointItemName + "/statechanged")
+ return ("openhab/items/" + inputItemName + "/state").equals(topic)
+ || ("openhab/items/" + inputItemName + "/statechanged").equals(topic)
+ || ("openhab/items/" + setpointItemName + "/statechanged").equals(topic)
|| commandTopic.map(t -> topic.equals(t)).orElse(false);
};
eventSubscriberRegistration = bundleContext.registerService(EventSubscriber.class.getName(), this, null);
eventPublisher.post(ItemEventFactory.createCommandEvent(inputItemName, RefreshType.REFRESH));
+ }
- controllerjob = scheduler.scheduleWithFixedDelay(this::calculate, 0, loopTimeMs, TimeUnit.MILLISECONDS);
+ @Override
+ public void setCallback(ModuleHandlerCallback callback) {
+ super.setCallback(callback);
+ getCallback().getScheduler().scheduleWithFixedDelay(this::calculate, 0, loopTimeMs, TimeUnit.MILLISECONDS);
}
private <T> T requireNonNull(T obj, String message) {
PIDOutputDTO output = controller.calculate(input, setpoint, now - previousTimeMs, loopTimeMs);
previousTimeMs = now;
- Map<String, BigDecimal> outputs = new HashMap<>();
+ updateItem(pInspector, output.getProportionalPart());
+ updateItem(iInspector, output.getIntegralPart());
+ updateItem(dInspector, output.getDerivativePart());
+ updateItem(eInspector, output.getError());
+
+ getCallback().triggered(module, Map.of(COMMAND, new DecimalType(output.getOutput())));
+ }
- putBigDecimal(outputs, OUTPUT, output.getOutput());
- putBigDecimal(outputs, P_INSPECTOR, output.getProportionalPart());
- putBigDecimal(outputs, I_INSPECTOR, output.getIntegralPart());
- putBigDecimal(outputs, D_INSPECTOR, output.getDerivativePart());
- putBigDecimal(outputs, E_INSPECTOR, output.getError());
+ private void updateItem(@Nullable String itemName, double value) {
+ if (itemName != null) {
+ eventPublisher.post(ItemEventFactory.createCommandEvent(itemName, new DecimalType(value)));
+ }
+ }
+ private TriggerHandlerCallback getCallback() {
ModuleHandlerCallback localCallback = callback;
if (localCallback != null && localCallback instanceof TriggerHandlerCallback) {
- ((TriggerHandlerCallback) localCallback).triggered(module, outputs);
- } else {
- logger.warn("No callback set");
+ return (TriggerHandlerCallback) localCallback;
}
- }
- private void putBigDecimal(Map<String, BigDecimal> map, String key, double value) {
- map.put(key, BigDecimal.valueOf(value));
+ throw new IllegalStateException("The module callback is not set");
}
private double getItemValueAsNumber(Item item) throws PIDException {
if (setpointState instanceof Number) {
double doubleValue = ((Number) setpointState).doubleValue();
- if (Double.isFinite(doubleValue)) {
+ if (Double.isFinite(doubleValue) && !Double.isNaN(doubleValue)) {
return doubleValue;
}
} else if (setpointState instanceof StringType) {
public void dispose() {
eventSubscriberRegistration.unregister();
- ScheduledFuture<?> localControllerjob = controllerjob;
- if (localControllerjob != null) {
- localControllerjob.cancel(true);
- }
-
- scheduler.shutdown();
-
super.dispose();
}
}
import java.util.Collections;
import java.util.List;
-import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.automation.pidcontroller.internal.PIDControllerConstants;
-import org.openhab.automation.pidcontroller.internal.handler.PIDControllerActionHandler;
import org.openhab.automation.pidcontroller.internal.handler.PIDControllerTriggerHandler;
-import org.openhab.automation.pidcontroller.internal.type.PIDControllerActionType;
import org.openhab.core.automation.Action;
import org.openhab.core.automation.Condition;
import org.openhab.core.automation.Trigger;
final List<Trigger> triggers = List.of(ModuleBuilder.createTrigger().withId(triggerId)
.withTypeUID(PIDControllerTriggerHandler.MODULE_TYPE_ID).withLabel("PID Controller Trigger").build());
- final Map<String, String> actionInputs = Map.of(PIDControllerActionType.INPUT,
- triggerId + "." + PIDControllerConstants.OUTPUT);
-
- final List<Action> actions = List.of(ModuleBuilder.createAction().withId(UUID.randomUUID().toString())
- .withTypeUID(PIDControllerActionHandler.MODULE_TYPE_ID).withLabel("PID Controller Action")
- .withInputs(actionInputs).build());
-
- return new PIDControllerRuleTemplate(Set.of("PID Controller"), triggers, Collections.emptyList(), actions,
- Collections.emptyList());
+ return new PIDControllerRuleTemplate(Set.of("PID Controller"), triggers, Collections.emptyList(),
+ Collections.emptyList(), Collections.emptyList());
}
public PIDControllerRuleTemplate(Set<String> tags, List<Trigger> triggers, List<Condition> conditions,
+++ /dev/null
-/**
- * Copyright (c) 2010-2021 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.automation.pidcontroller.internal.type;
-
-import static org.openhab.automation.pidcontroller.internal.PIDControllerConstants.*;
-
-import java.math.BigDecimal;
-import java.util.List;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.automation.pidcontroller.internal.handler.PIDControllerActionHandler;
-import org.openhab.core.automation.Visibility;
-import org.openhab.core.automation.type.ActionType;
-import org.openhab.core.automation.type.Input;
-import org.openhab.core.config.core.ConfigDescriptionParameter;
-import org.openhab.core.config.core.ConfigDescriptionParameter.Type;
-import org.openhab.core.config.core.ConfigDescriptionParameterBuilder;
-
-/**
- *
- * @author Hilbrand Bouwkamp - Initial Contribution
- */
-@NonNullByDefault
-public class PIDControllerActionType extends ActionType {
- public static final String INPUT = "input";
-
- public static PIDControllerActionType initialize() {
- final ConfigDescriptionParameter outputItem = ConfigDescriptionParameterBuilder.create(OUTPUT, Type.TEXT)
- .withRequired(true).withMultiple(false).withContext("item").withLabel("Output Item")
- .withDescription("Item to send output").build();
- final ConfigDescriptionParameter pInspectorItem = ConfigDescriptionParameterBuilder
- .create(P_INSPECTOR, Type.TEXT).withRequired(false).withMultiple(false).withContext("item")
- .withLabel("P Inspector Item").withDescription("Item for debugging the P part").build();
- final ConfigDescriptionParameter iInspectorItem = ConfigDescriptionParameterBuilder
- .create(I_INSPECTOR, Type.TEXT).withRequired(false).withMultiple(false).withContext("item")
- .withLabel("I Inspector Item").withDescription("Item for debugging the I part").build();
- final ConfigDescriptionParameter dInspectorItem = ConfigDescriptionParameterBuilder
- .create(D_INSPECTOR, Type.TEXT).withRequired(false).withMultiple(false).withContext("item")
- .withLabel("D Inspector Item").withDescription("Item for debugging the D part").build();
- final ConfigDescriptionParameter eInspectorItem = ConfigDescriptionParameterBuilder
- .create(E_INSPECTOR, Type.TEXT).withRequired(false).withMultiple(false).withContext("item")
- .withLabel("Error Inspector Item").withDescription("Item for debugging the error value").build();
-
- List<ConfigDescriptionParameter> config = List.of(outputItem, pInspectorItem, iInspectorItem, dInspectorItem,
- eInspectorItem);
-
- List<Input> inputs = List.of(createInput(INPUT), createInput(P_INSPECTOR), createInput(I_INSPECTOR),
- createInput(D_INSPECTOR), createInput(E_INSPECTOR));
-
- return new PIDControllerActionType(config, inputs);
- }
-
- private static Input createInput(String name) {
- return new Input(name, BigDecimal.class.getName());
- }
-
- public PIDControllerActionType(List<ConfigDescriptionParameter> configDescriptions, List<Input> inputs) {
- super(PIDControllerActionHandler.MODULE_TYPE_ID, configDescriptions, "calculate PID output", null, null,
- Visibility.VISIBLE, inputs, null);
- }
-}
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.automation.pidcontroller.internal.handler.PIDControllerActionHandler;
import org.openhab.automation.pidcontroller.internal.handler.PIDControllerTriggerHandler;
import org.openhab.core.automation.type.ModuleType;
import org.openhab.core.automation.type.ModuleTypeProvider;
@Component
@NonNullByDefault
public class PIDControllerModuleTypeProvider implements ModuleTypeProvider {
- private static final Map<String, ModuleType> PROVIDED_MODULE_TYPES = Map.of(
- PIDControllerActionHandler.MODULE_TYPE_ID, PIDControllerActionType.initialize(),
- PIDControllerTriggerHandler.MODULE_TYPE_ID, PIDControllerTriggerType.initialize());
+ private static final Map<String, ModuleType> PROVIDED_MODULE_TYPES = Map
+ .of(PIDControllerTriggerHandler.MODULE_TYPE_ID, PIDControllerTriggerType.initialize());
@SuppressWarnings("unchecked")
@Override
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.automation.pidcontroller.internal.handler.PIDControllerTriggerHandler;
/**
*
* @author Hilbrand Bouwkamp - Initial Contribution
+ * @author Fabian Wolter - Add inspector Items for debugging
*/
@NonNullByDefault
public class PIDControllerTriggerType extends TriggerType {
private static final String DEFAULT_LOOPTIME_MS = "1000";
+ private static final String ITEM = "item";
public static PIDControllerTriggerType initialize() {
List<ConfigDescriptionParameter> configDescriptions = new ArrayList<>();
configDescriptions.add(ConfigDescriptionParameterBuilder.create(CONFIG_INPUT_ITEM, Type.TEXT) //
.withRequired(true) //
.withMultiple(false) //
- .withContext("item") //
+ .withContext(ITEM) //
.withLabel("Input Item") //
.withDescription("Item to monitor") //
.build());
configDescriptions.add(ConfigDescriptionParameterBuilder.create(CONFIG_SETPOINT_ITEM, Type.TEXT) //
.withRequired(true) //
.withMultiple(false) //
- .withContext("item") //
+ .withContext(ITEM) //
.withLabel("Setpoint") //
.withDescription("Targeted setpoint") //
.build());
.withDescription("Slows the rate of change of the D part (T1) in seconds.") //
.withUnit("s") //
.build());
- configDescriptions.add(ConfigDescriptionParameterBuilder.create(CONFIG_COMMAND_ITEM, Type.TEXT) //
- .withRequired(false) //
- .withMultiple(false) //
- .withContext("item") //
- .withLabel("Command Item") //
- .withDescription("You can send String commands to this Item like \"RESET\".") //
- .build());
configDescriptions.add(ConfigDescriptionParameterBuilder.create(CONFIG_LOOP_TIME, Type.DECIMAL) //
.withRequired(true) //
.withMultiple(false) //
.withDescription("The interval the output value is updated in ms") //
.withUnit("ms") //
.build());
- Output output = new Output(OUTPUT, BigDecimal.class.getName(), "Output", "Output value of the PID Controller",
- null, null, null);
- Output pInspector = new Output(P_INSPECTOR, BigDecimal.class.getName(), "P Inspector",
- "Current P value of the pid controller", null, null, null);
- Output iInspector = new Output(I_INSPECTOR, BigDecimal.class.getName(), "I Inspector",
- "Current I value of the pid controller", null, null, null);
- Output dInspector = new Output(D_INSPECTOR, BigDecimal.class.getName(), "D Inspector",
- "Current D value of the pid controller", null, null, null);
- Output eInspector = new Output(E_INSPECTOR, BigDecimal.class.getName(), "Error Value Inspector",
- "Current error value of the pid controller", null, null, null);
+ configDescriptions.add(ConfigDescriptionParameterBuilder.create(P_INSPECTOR, Type.TEXT) //
+ .withRequired(false) //
+ .withMultiple(false) //
+ .withContext(ITEM) //
+ .withLabel("P Inspector Item") //
+ .withDescription("Item for debugging the P part") //
+ .build());
+ configDescriptions.add(ConfigDescriptionParameterBuilder.create(I_INSPECTOR, Type.TEXT) //
+ .withRequired(false) //
+ .withMultiple(false) //
+ .withContext(ITEM) //
+ .withLabel("I Inspector Item") //
+ .withDescription("Item for debugging the I part") //
+ .build());
+ configDescriptions.add(ConfigDescriptionParameterBuilder.create(D_INSPECTOR, Type.TEXT) //
+ .withRequired(false).withMultiple(false) //
+ .withContext(ITEM) //
+ .withLabel("D Inspector Item") //
+ .withDescription("Item for debugging the D part") //
+ .build());
+ configDescriptions.add(ConfigDescriptionParameterBuilder.create(E_INSPECTOR, Type.TEXT) //
+ .withRequired(false).withMultiple(false) //
+ .withContext(ITEM) //
+ .withLabel("Error Inspector Item") //
+ .withDescription("Item for debugging the error value") //
+ .build());
- List<Output> outputs = List.of(output, pInspector, iInspector, dInspector, eInspector);
+ Output output = new Output(COMMAND, BigDecimal.class.getName(), "Output", "Output value of the PID Controller",
+ Set.of("command"), null, null);
- return new PIDControllerTriggerType(configDescriptions, outputs);
+ return new PIDControllerTriggerType(configDescriptions, List.of(output));
}
public PIDControllerTriggerType(List<ConfigDescriptionParameter> configDescriptions, List<Output> outputs) {