]> git.basschouten.com Git - openhab-addons.git/blob
1847e55d5b86a2af0725f0b001b75b3bbee1cacd
[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.List;
18 import java.util.Map;
19 import java.util.Set;
20 import java.util.concurrent.atomic.AtomicReference;
21
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.openhab.binding.plclogo.internal.PLCLogoClient;
24 import org.openhab.binding.plclogo.internal.config.PLCMemoryConfiguration;
25 import org.openhab.core.config.core.Configuration;
26 import org.openhab.core.library.types.DecimalType;
27 import org.openhab.core.library.types.OnOffType;
28 import org.openhab.core.thing.Bridge;
29 import org.openhab.core.thing.Channel;
30 import org.openhab.core.thing.ChannelUID;
31 import org.openhab.core.thing.Thing;
32 import org.openhab.core.thing.ThingStatus;
33 import org.openhab.core.thing.ThingTypeUID;
34 import org.openhab.core.thing.binding.builder.ChannelBuilder;
35 import org.openhab.core.thing.binding.builder.ThingBuilder;
36 import org.openhab.core.thing.type.ChannelTypeUID;
37 import org.openhab.core.types.Command;
38 import org.openhab.core.types.RefreshType;
39 import org.openhab.core.types.State;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 import Moka7.S7;
44 import Moka7.S7Client;
45
46 /**
47  * The {@link PLCMemoryHandler} is responsible for handling commands, which are
48  * sent to one of the channels.
49  *
50  * @author Alexander Falkenstern - Initial contribution
51  */
52 @NonNullByDefault
53 public class PLCMemoryHandler extends PLCCommonHandler {
54
55     public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_MEMORY);
56
57     private final Logger logger = LoggerFactory.getLogger(PLCMemoryHandler.class);
58     private AtomicReference<PLCMemoryConfiguration> config = new AtomicReference<>();
59
60     /**
61      * Constructor.
62      */
63     public PLCMemoryHandler(Thing thing) {
64         super(thing);
65     }
66
67     @Override
68     public void handleCommand(ChannelUID channelUID, Command command) {
69         if (!isThingOnline()) {
70             return;
71         }
72
73         Channel channel = getThing().getChannel(channelUID.getId());
74         String name = getBlockFromChannel(channel);
75         if (!isValid(name) || (channel == null)) {
76             logger.debug("Can not update channel {}, block {}.", channelUID, name);
77             return;
78         }
79
80         int address = getAddress(name);
81         PLCLogoClient client = getLogoClient();
82         if ((address != INVALID) && (client != null)) {
83             String kind = getBlockKind();
84             String type = channel.getAcceptedItemType();
85             if (command instanceof RefreshType) {
86                 byte[] buffer = new byte[getBufferLength()];
87                 int result = client.readDBArea(1, 0, buffer.length, S7Client.S7WLByte, buffer);
88                 if (result == 0) {
89                     if (DIGITAL_OUTPUT_ITEM.equalsIgnoreCase(type) && MEMORY_BYTE.equalsIgnoreCase(kind)) {
90                         boolean value = S7.GetBitAt(buffer, address, getBit(name));
91                         updateState(channelUID, OnOffType.from(value));
92                         logger.debug("Channel {} accepting {} was set to {}.", channelUID, type, value);
93                     } else if (ANALOG_ITEM.equalsIgnoreCase(type) && MEMORY_BYTE.equalsIgnoreCase(kind)) {
94                         int value = buffer[address];
95                         updateState(channelUID, new DecimalType(value));
96                         logger.debug("Channel {} accepting {} was set to {}.", channelUID, type, value);
97                     } else if (ANALOG_ITEM.equalsIgnoreCase(type) && MEMORY_WORD.equalsIgnoreCase(kind)) {
98                         int value = S7.GetShortAt(buffer, address);
99                         updateState(channelUID, new DecimalType(value));
100                         logger.debug("Channel {} accepting {} was set to {}.", channelUID, type, value);
101                     } else if (ANALOG_ITEM.equalsIgnoreCase(type) && MEMORY_DWORD.equalsIgnoreCase(kind)) {
102                         int value = S7.GetDIntAt(buffer, address);
103                         updateState(channelUID, new DecimalType(value));
104                         logger.debug("Channel {} accepting {} was set to {}.", channelUID, type, value);
105                     } else {
106                         logger.debug("Channel {} will not accept {} items.", channelUID, type);
107                     }
108                 } else {
109                     logger.debug("Can not read data from LOGO!: {}.", S7Client.ErrorText(result));
110                 }
111             } else if (command instanceof DecimalType decimalCommand) {
112                 int length = MEMORY_BYTE.equalsIgnoreCase(kind) ? 1 : 2;
113                 byte[] buffer = new byte[MEMORY_DWORD.equalsIgnoreCase(kind) ? 4 : length];
114                 if (ANALOG_ITEM.equalsIgnoreCase(type) && MEMORY_BYTE.equalsIgnoreCase(kind)) {
115                     buffer[0] = decimalCommand.byteValue();
116                 } else if (ANALOG_ITEM.equalsIgnoreCase(type) && MEMORY_WORD.equalsIgnoreCase(kind)) {
117                     S7.SetShortAt(buffer, 0, decimalCommand.intValue());
118                 } else if (ANALOG_ITEM.equalsIgnoreCase(type) && MEMORY_DWORD.equalsIgnoreCase(kind)) {
119                     S7.SetDIntAt(buffer, 0, decimalCommand.intValue());
120                 } else {
121                     logger.debug("Channel {} will not accept {} items.", channelUID, type);
122                 }
123                 int result = client.writeDBArea(1, address, buffer.length, S7Client.S7WLByte, buffer);
124                 if (result != 0) {
125                     logger.debug("Can not write data to LOGO!: {}.", S7Client.ErrorText(result));
126                 }
127             } else if (command instanceof OnOffType onOffCommand) {
128                 byte[] buffer = new byte[1];
129                 if (DIGITAL_OUTPUT_ITEM.equalsIgnoreCase(type) && MEMORY_BYTE.equalsIgnoreCase(kind)) {
130                     S7.SetBitAt(buffer, 0, 0, onOffCommand == OnOffType.ON);
131                 } else {
132                     logger.debug("Channel {} will not accept {} items.", channelUID, type);
133                 }
134                 int result = client.writeDBArea(1, 8 * address + getBit(name), buffer.length, S7Client.S7WLBit, buffer);
135                 if (result != 0) {
136                     logger.debug("Can not write data to LOGO!: {}.", S7Client.ErrorText(result));
137                 }
138             } else {
139                 logger.debug("Channel {} received not supported command {}.", channelUID, command);
140             }
141         } else {
142             logger.info("Invalid channel {} or client {} found.", channelUID, client);
143         }
144     }
145
146     @Override
147     public void setData(final byte[] data) {
148         if (!isThingOnline()) {
149             return;
150         }
151
152         if (data.length != getBufferLength()) {
153             logger.info("Received and configured data sizes does not match.");
154             return;
155         }
156
157         List<Channel> channels = thing.getChannels();
158         if (channels.size() != getNumberOfChannels()) {
159             logger.info("Received and configured channel sizes does not match.");
160             return;
161         }
162
163         for (Channel channel : channels) {
164             ChannelUID channelUID = channel.getUID();
165             String name = getBlockFromChannel(channel);
166
167             int address = getAddress(name);
168             if (address != INVALID) {
169                 String kind = getBlockKind();
170                 String type = channel.getAcceptedItemType();
171                 Boolean force = config.get().isUpdateForced();
172
173                 if (DIGITAL_OUTPUT_ITEM.equalsIgnoreCase(type) && kind.equalsIgnoreCase(MEMORY_BYTE)) {
174                     OnOffType state = (OnOffType) getOldValue(name);
175                     OnOffType value = OnOffType.from(S7.GetBitAt(data, address, getBit(name)));
176                     if ((state == null) || (value != state) || force) {
177                         updateState(channelUID, value);
178                         logger.debug("Channel {} accepting {} was set to {}.", channelUID, type, value);
179                     }
180                     if (logger.isTraceEnabled()) {
181                         int buffer = (data[address] & 0xFF) + 0x100;
182                         logger.trace("Channel {} received [{}].", channelUID,
183                                 Integer.toBinaryString(buffer).substring(1));
184                     }
185                 } else if (ANALOG_ITEM.equalsIgnoreCase(type) && MEMORY_BYTE.equalsIgnoreCase(kind)) {
186                     Integer threshold = config.get().getThreshold();
187                     DecimalType state = (DecimalType) getOldValue(name);
188                     int value = data[address];
189                     if ((state == null) || (Math.abs(value - state.intValue()) > threshold) || force) {
190                         updateState(channelUID, new DecimalType(value));
191                         logger.debug("Channel {} accepting {} was set to {}.", channelUID, type, value);
192                     }
193                     if (logger.isTraceEnabled()) {
194                         logger.trace("Channel {} received [{}].", channelUID, data[address]);
195                     }
196                 } else if (ANALOG_ITEM.equalsIgnoreCase(type) && MEMORY_WORD.equalsIgnoreCase(kind)) {
197                     Integer threshold = config.get().getThreshold();
198                     DecimalType state = (DecimalType) getOldValue(name);
199                     int value = S7.GetShortAt(data, address);
200                     if ((state == null) || (Math.abs(value - state.intValue()) > threshold) || force) {
201                         updateState(channelUID, new DecimalType(value));
202                         logger.debug("Channel {} accepting {} was set to {}.", channelUID, type, value);
203                     }
204                     if (logger.isTraceEnabled()) {
205                         logger.trace("Channel {} received [{}, {}].", channelUID, data[address], data[address + 1]);
206                     }
207                 } else if (ANALOG_ITEM.equalsIgnoreCase(type) && MEMORY_DWORD.equalsIgnoreCase(kind)) {
208                     Integer threshold = config.get().getThreshold();
209                     DecimalType state = (DecimalType) getOldValue(name);
210                     int value = S7.GetDIntAt(data, address);
211                     if ((state == null) || (Math.abs(value - state.intValue()) > threshold) || force) {
212                         updateState(channelUID, new DecimalType(value));
213                         logger.debug("Channel {} accepting {} was set to {}.", channelUID, type, value);
214                     }
215                     if (logger.isTraceEnabled()) {
216                         logger.trace("Channel {} received [{}, {}, {}, {}].", channelUID, data[address],
217                                 data[address + 1], data[address + 2], data[address + 3]);
218                     }
219                 } else {
220                     logger.debug("Channel {} will not accept {} items.", channelUID, type);
221                 }
222             } else {
223                 logger.info("Invalid channel {} found.", channelUID);
224             }
225         }
226     }
227
228     @Override
229     protected void updateState(ChannelUID channelUID, State state) {
230         super.updateState(channelUID, state);
231
232         Channel channel = thing.getChannel(channelUID.getId());
233         setOldValue(getBlockFromChannel(channel), state);
234     }
235
236     @Override
237     protected void updateConfiguration(Configuration configuration) {
238         super.updateConfiguration(configuration);
239         config.set(getConfigAs(PLCMemoryConfiguration.class));
240     }
241
242     @Override
243     protected boolean isValid(final String name) {
244         if (3 <= name.length() && (name.length() <= 7)) {
245             String kind = getBlockKind();
246             if (Character.isDigit(name.charAt(2))) {
247                 boolean valid = MEMORY_BYTE.equalsIgnoreCase(kind) || MEMORY_WORD.equalsIgnoreCase(kind);
248                 return name.startsWith(kind) && (valid || MEMORY_DWORD.equalsIgnoreCase(kind));
249             }
250         }
251         return false;
252     }
253
254     @Override
255     protected String getBlockKind() {
256         return config.get().getBlockKind();
257     }
258
259     @Override
260     protected int getNumberOfChannels() {
261         return 1;
262     }
263
264     @Override
265     protected void doInitialization() {
266         Thing thing = getThing();
267         logger.debug("Initialize LOGO! memory handler.");
268
269         config.set(getConfigAs(PLCMemoryConfiguration.class));
270
271         super.doInitialization();
272         if (ThingStatus.OFFLINE != thing.getStatus()) {
273             String kind = getBlockKind();
274             String name = config.get().getBlockName();
275             boolean isDigital = MEMORY_BYTE.equalsIgnoreCase(kind) && (getBit(name) != INVALID);
276             String text = isDigital ? "Digital" : "Analog";
277
278             ThingBuilder tBuilder = editThing();
279
280             String label = thing.getLabel();
281             if (label == null) {
282                 Bridge bridge = getBridge();
283                 label = (bridge == null) || (bridge.getLabel() == null) ? "Siemens Logo!" : bridge.getLabel();
284                 label += (": " + text.toLowerCase() + " in/output");
285             }
286             tBuilder.withLabel(label);
287
288             String type = config.get().getChannelType();
289             ChannelUID uid = new ChannelUID(thing.getUID(), isDigital ? STATE_CHANNEL : VALUE_CHANNEL);
290             ChannelBuilder cBuilder = ChannelBuilder.create(uid, type);
291             cBuilder.withType(new ChannelTypeUID(BINDING_ID, type.toLowerCase()));
292             cBuilder.withLabel(name);
293             cBuilder.withDescription(text + " in/output block " + name);
294             cBuilder.withProperties(Map.of(BLOCK_PROPERTY, name));
295             tBuilder.withChannel(cBuilder.build());
296             setOldValue(name, null);
297
298             updateThing(tBuilder.build());
299             updateStatus(ThingStatus.ONLINE);
300         }
301     }
302
303     /**
304      * Calculate bit within address for block with given name.
305      *
306      * @param name Name of the LOGO! block
307      * @return Calculated bit
308      */
309     private int getBit(final String name) {
310         int bit = INVALID;
311
312         logger.debug("Get bit of {} LOGO! for block {} .", getLogoFamily(), name);
313
314         if (isValid(name) && (getAddress(name) != INVALID)) {
315             String[] parts = name.trim().split("\\.");
316             if (parts.length > 1) {
317                 bit = Integer.parseInt(parts[1]);
318             }
319         } else {
320             logger.info("Wrong configurated LOGO! block {} found.", name);
321         }
322
323         return bit;
324     }
325 }