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