]> git.basschouten.com Git - openhab-addons.git/blob
4753d2b1d7d49c9d2432595878844adbfe525fa9
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.plclogo.internal.handler;
14
15 import static org.openhab.binding.plclogo.internal.PLCLogoBindingConstants.*;
16
17 import java.util.HashMap;
18 import java.util.Map;
19
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;
36
37 /**
38  * The {@link PLCCommonHandler} is responsible for handling commands, which are
39  * sent to one of the channels.
40  *
41  * @author Alexander Falkenstern - Initial contribution
42  */
43 @NonNullByDefault
44 public abstract class PLCCommonHandler extends BaseThingHandler {
45
46     public static final int INVALID = Integer.MAX_VALUE;
47
48     private final Logger logger = LoggerFactory.getLogger(PLCCommonHandler.class);
49
50     private Map<String, @Nullable State> oldValues = new HashMap<>();
51
52     private @Nullable PLCLogoClient client;
53     private String family = NOT_SUPPORTED;
54
55     /**
56      * Constructor.
57      */
58     public PLCCommonHandler(Thing thing) {
59         super(thing);
60     }
61
62     @Override
63     public void initialize() {
64         synchronized (oldValues) {
65             oldValues.clear();
66         }
67         scheduler.execute(this::doInitialization);
68     }
69
70     @Override
71     public void dispose() {
72         logger.debug("Dispose LOGO! common block handler.");
73         super.dispose();
74
75         ThingBuilder tBuilder = editThing();
76         for (Channel channel : getThing().getChannels()) {
77             tBuilder.withoutChannel(channel.getUID());
78         }
79         updateThing(tBuilder.build());
80
81         synchronized (oldValues) {
82             oldValues.clear();
83         }
84
85         family = NOT_SUPPORTED;
86         client = null;
87     }
88
89     /**
90      * Return data buffer start address to read/write dependent on configured Logo! family.
91      *
92      * @return Start address of data buffer
93      */
94     public int getStartAddress() {
95         String kind = getBlockKind();
96         String family = getLogoFamily();
97         logger.debug("Get start address of {} LOGO! for {} blocks.", family, kind);
98
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;
102     }
103
104     /**
105      * Return data buffer length to read/write dependent on configured Logo! family.
106      *
107      * @return Length of data buffer in bytes
108      */
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);
113
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;
117     }
118
119     /**
120      * Update value channel of current thing with new data.
121      *
122      * @param data Data value to update with
123      */
124     public abstract void setData(final byte[] data);
125
126     /**
127      * Checks if block name is valid.
128      *
129      * @param name Name of the LOGO! block to check
130      * @return True, if the name is valid and false otherwise
131      */
132     protected abstract boolean isValid(final String name);
133
134     /**
135      * Returns configured block kind.
136      *
137      * @return Configured block kind
138      */
139     protected abstract String getBlockKind();
140
141     /**
142      * Return number of channels dependent on configured Logo! family.
143      *
144      * @return Number of channels
145      */
146     protected abstract int getNumberOfChannels();
147
148     /**
149      * Calculate address for the block with given name.
150      *
151      * @param name Name of the LOGO! block
152      * @return Calculated address
153      */
154     protected int getAddress(final String name) {
155         int address = INVALID;
156
157         logger.debug("Get address of {} LOGO! for block {} .", getLogoFamily(), name);
158
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));
168             }
169         } else {
170             logger.info("Wrong configurated LOGO! block {} found.", name);
171         }
172
173         return address;
174     }
175
176     /**
177      * Calculate address offset for given block name.
178      *
179      * @param name Name of the data block
180      * @return Calculated address offset
181      */
182     protected int getBase(final String name) {
183         Layout layout = null;
184         String family = getLogoFamily();
185
186         logger.debug("Get base address of {} LOGO! for block {} .", family, name);
187
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));
197             }
198         }
199
200         return layout != null ? layout.address : INVALID;
201     }
202
203     /**
204      * Checks if thing handler is valid and online.
205      *
206      * @return True, if handler is valid and false otherwise
207      */
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()));
213         }
214         return false;
215     }
216
217     protected @Nullable State getOldValue(final String name) {
218         synchronized (oldValues) {
219             return oldValues.get(name);
220         }
221     }
222
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);
227             } else {
228                 logger.info("Wrong configurated LOGO! block {} found.", name);
229             }
230         }
231     }
232
233     /**
234      * Returns configured LOGO! communication client.
235      *
236      * @return Configured LOGO! client
237      */
238     protected @Nullable PLCLogoClient getLogoClient() {
239         return client;
240     }
241
242     protected @Nullable PLCBridgeHandler getBridgeHandler() {
243         Bridge bridge = getBridge();
244         if (bridge != null) {
245             BridgeHandler handler = bridge.getHandler();
246             if (handler instanceof PLCBridgeHandler bridgeHandler) {
247                 return bridgeHandler;
248             }
249         }
250         return null;
251     }
252
253     /**
254      * Returns configured LOGO! family.
255      *
256      * @see PLCLogoBindingConstants#LOGO_0BA7
257      * @see PLCLogoBindingConstants#LOGO_0BA8
258      * @return Configured LOGO! family
259      */
260     protected String getLogoFamily() {
261         return family;
262     }
263
264     /**
265      * Perform thing initialization.
266      */
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);
275
276                 Thing thing = getThing();
277                 logger.warn("Can not initialize thing {} for LOGO! {}.", thing.getUID(), thing.getBridgeUID());
278             }
279         }
280     }
281
282     protected static String getBlockFromChannel(final @Nullable Channel channel) {
283         if (channel == null) {
284             return NOT_SUPPORTED;
285         }
286         String block = channel.getProperties().get(BLOCK_PROPERTY);
287         return block == null ? NOT_SUPPORTED : block;
288     }
289 }