2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.plclogo.internal.handler;
15 import static org.openhab.binding.plclogo.internal.PLCLogoBindingConstants.*;
17 import java.util.HashMap;
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.openhab.binding.plclogo.internal.PLCLogoBindingConstants;
23 import org.openhab.binding.plclogo.internal.PLCLogoBindingConstants.Layout;
24 import org.openhab.binding.plclogo.internal.PLCLogoClient;
25 import org.openhab.core.thing.Bridge;
26 import org.openhab.core.thing.Channel;
27 import org.openhab.core.thing.Thing;
28 import org.openhab.core.thing.ThingStatus;
29 import org.openhab.core.thing.ThingStatusDetail;
30 import org.openhab.core.thing.binding.BaseThingHandler;
31 import org.openhab.core.thing.binding.BridgeHandler;
32 import org.openhab.core.thing.binding.builder.ThingBuilder;
33 import org.openhab.core.types.State;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
38 * The {@link PLCCommonHandler} is responsible for handling commands, which are
39 * sent to one of the channels.
41 * @author Alexander Falkenstern - Initial contribution
44 public abstract class PLCCommonHandler extends BaseThingHandler {
46 public static final int INVALID = Integer.MAX_VALUE;
48 private final Logger logger = LoggerFactory.getLogger(PLCCommonHandler.class);
50 private Map<String, @Nullable State> oldValues = new HashMap<>();
52 private @Nullable PLCLogoClient client;
53 private String family = NOT_SUPPORTED;
58 public PLCCommonHandler(Thing thing) {
63 public void initialize() {
64 synchronized (oldValues) {
67 scheduler.execute(this::doInitialization);
71 public void dispose() {
72 logger.debug("Dispose LOGO! common block handler.");
75 ThingBuilder tBuilder = editThing();
76 for (Channel channel : getThing().getChannels()) {
77 tBuilder.withoutChannel(channel.getUID());
79 updateThing(tBuilder.build());
81 synchronized (oldValues) {
85 family = NOT_SUPPORTED;
90 * Return data buffer start address to read/write dependent on configured Logo! family.
92 * @return Start address of data buffer
94 public int getStartAddress() {
95 String kind = getBlockKind();
96 String family = getLogoFamily();
97 logger.debug("Get start address of {} LOGO! for {} blocks.", family, kind);
99 Map<?, Layout> memory = LOGO_MEMORY_BLOCK.get(family);
100 Layout layout = (memory != null) ? memory.get(kind) : null;
101 return layout != null ? layout.address : INVALID;
105 * Return data buffer length to read/write dependent on configured Logo! family.
107 * @return Length of data buffer in bytes
109 public int getBufferLength() {
110 String kind = getBlockKind();
111 String family = getLogoFamily();
112 logger.debug("Get data buffer length of {} LOGO! for {} blocks.", family, kind);
114 Map<?, Layout> memory = LOGO_MEMORY_BLOCK.get(family);
115 Layout layout = (memory != null) ? memory.get(kind) : null;
116 return layout != null ? layout.length : 0;
120 * Update value channel of current thing with new data.
122 * @param data Data value to update with
124 public abstract void setData(final byte[] data);
127 * Checks if block name is valid.
129 * @param name Name of the LOGO! block to check
130 * @return True, if the name is valid and false otherwise
132 protected abstract boolean isValid(final String name);
135 * Returns configured block kind.
137 * @return Configured block kind
139 protected abstract String getBlockKind();
142 * Return number of channels dependent on configured Logo! family.
144 * @return Number of channels
146 protected abstract int getNumberOfChannels();
149 * Calculate address for the block with given name.
151 * @param name Name of the LOGO! block
152 * @return Calculated address
154 protected int getAddress(final String name) {
155 int address = INVALID;
157 logger.debug("Get address of {} LOGO! for block {} .", getLogoFamily(), name);
159 int base = getBase(name);
160 if (isValid(name) && (base != INVALID)) {
161 String block = name.split("\\.")[0];
162 if (Character.isDigit(block.charAt(1))) {
163 address = Integer.parseInt(block.substring(1));
164 } else if (Character.isDigit(block.charAt(2))) {
165 address = Integer.parseInt(block.substring(2));
166 } else if (Character.isDigit(block.charAt(3))) {
167 address = Integer.parseInt(block.substring(3));
170 logger.info("Wrong configurated LOGO! block {} found.", name);
177 * Calculate address offset for given block name.
179 * @param name Name of the data block
180 * @return Calculated address offset
182 protected int getBase(final String name) {
183 Layout layout = null;
184 String family = getLogoFamily();
186 logger.debug("Get base address of {} LOGO! for block {} .", family, name);
188 String block = name.split("\\.")[0];
189 Map<?, Layout> memory = LOGO_MEMORY_BLOCK.get(family);
190 if (isValid(name) && !block.isEmpty() && (memory != null)) {
191 if (Character.isDigit(block.charAt(1))) {
192 layout = memory.get(block.substring(0, 1));
193 } else if (Character.isDigit(block.charAt(2))) {
194 layout = memory.get(block.substring(0, 2));
195 } else if (Character.isDigit(block.charAt(3))) {
196 layout = memory.get(block.substring(0, 3));
200 return layout != null ? layout.address : INVALID;
204 * Checks if thing handler is valid and online.
206 * @return True, if handler is valid and false otherwise
208 protected boolean isThingOnline() {
209 Bridge bridge = getBridge();
210 if (bridge != null) {
211 Thing thing = getThing();
212 return ((ThingStatus.ONLINE == bridge.getStatus()) && (ThingStatus.ONLINE == thing.getStatus()));
217 protected @Nullable State getOldValue(final String name) {
218 synchronized (oldValues) {
219 return oldValues.get(name);
223 protected void setOldValue(final String name, final @Nullable State value) {
224 synchronized (oldValues) {
225 if (!NOT_SUPPORTED.equalsIgnoreCase(name)) {
226 oldValues.put(name, value);
228 logger.info("Wrong configurated LOGO! block {} found.", name);
234 * Returns configured LOGO! communication client.
236 * @return Configured LOGO! client
238 protected @Nullable PLCLogoClient getLogoClient() {
242 protected @Nullable PLCBridgeHandler getBridgeHandler() {
243 Bridge bridge = getBridge();
244 if (bridge != null) {
245 BridgeHandler handler = bridge.getHandler();
246 if ((handler != null) && (handler instanceof PLCBridgeHandler)) {
247 return (PLCBridgeHandler) handler;
254 * Returns configured LOGO! family.
256 * @see PLCLogoBindingConstants#LOGO_0BA7
257 * @see PLCLogoBindingConstants#LOGO_0BA8
258 * @return Configured LOGO! family
260 protected String getLogoFamily() {
265 * Perform thing initialization.
267 protected void doInitialization() {
268 PLCBridgeHandler handler = getBridgeHandler();
269 if (handler != null) {
270 family = handler.getLogoFamily();
271 client = handler.getLogoClient();
272 if ((client == null) || NOT_SUPPORTED.equalsIgnoreCase(family)) {
273 String message = "Can not initialize LOGO! block handler.";
274 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, message);
276 Thing thing = getThing();
277 logger.warn("Can not initialize thing {} for LOGO! {}.", thing.getUID(), thing.getBridgeUID());
282 protected static String getBlockFromChannel(final @Nullable Channel channel) {
283 if (channel == null) {
284 return NOT_SUPPORTED;
286 String block = channel.getProperties().get(BLOCK_PROPERTY);
287 return block == null ? NOT_SUPPORTED : block;