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