From fa3b6f35f504a65558a24ca660f081b0d1fb1b5c Mon Sep 17 00:00:00 2001 From: Ziver Koc Date: Tue, 16 May 2017 17:21:34 +0200 Subject: [PATCH] Initial implementation of ZWave plugin --- Hal.iml | 17 ++ hal.conf.example | 2 + .../zwave/lib}/netty-all-4.0.46.Final.jar | Bin {lib => plugins/zwave/lib}/wzwave-0.0.3.jar | Bin .../AbstractNettyChannelInitializer.java | 72 +++++ .../netty/NettyJSCChannelInitializer.java | 44 +++ .../netty/NettyRxtxChannelInitializer.java | 45 ++++ .../netty/NettyZWaveController.java | 251 ++++++++++++++++++ .../channel/jsc/DefaultJSCChannelConfig.java | 231 ++++++++++++++++ .../netty/channel/jsc/JSCChannelConfig.java | 236 ++++++++++++++++ .../netty/channel/jsc/JSCChannelOption.java | 40 +++ .../netty/channel/jsc/JSCDeviceAddress.java | 45 ++++ .../netty/channel/jsc/JSerialCommChannel.java | 181 +++++++++++++ .../io/netty/channel/jsc/package-info.java | 20 ++ .../hal/plugin/zwave/HalZWaveController.java | 92 +++++++ .../src}/se/hal/plugin/zwave/plugin.json | 0 16 files changed, 1276 insertions(+) rename {lib => plugins/zwave/lib}/netty-all-4.0.46.Final.jar (100%) rename {lib => plugins/zwave/lib}/wzwave-0.0.3.jar (100%) create mode 100755 plugins/zwave/src/com/whizzosoftware/wzwave/controller/netty/AbstractNettyChannelInitializer.java create mode 100755 plugins/zwave/src/com/whizzosoftware/wzwave/controller/netty/NettyJSCChannelInitializer.java create mode 100755 plugins/zwave/src/com/whizzosoftware/wzwave/controller/netty/NettyRxtxChannelInitializer.java create mode 100755 plugins/zwave/src/com/whizzosoftware/wzwave/controller/netty/NettyZWaveController.java create mode 100755 plugins/zwave/src/io/netty/channel/jsc/DefaultJSCChannelConfig.java create mode 100755 plugins/zwave/src/io/netty/channel/jsc/JSCChannelConfig.java create mode 100755 plugins/zwave/src/io/netty/channel/jsc/JSCChannelOption.java create mode 100755 plugins/zwave/src/io/netty/channel/jsc/JSCDeviceAddress.java create mode 100755 plugins/zwave/src/io/netty/channel/jsc/JSerialCommChannel.java create mode 100755 plugins/zwave/src/io/netty/channel/jsc/package-info.java create mode 100755 plugins/zwave/src/se/hal/plugin/zwave/HalZWaveController.java rename {src => plugins/zwave/src}/se/hal/plugin/zwave/plugin.json (100%) diff --git a/Hal.iml b/Hal.iml index 934527c6..60b046ec 100755 --- a/Hal.iml +++ b/Hal.iml @@ -6,6 +6,7 @@ + @@ -21,6 +22,9 @@ + + + @@ -36,5 +40,18 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/hal.conf.example b/hal.conf.example index d372f82e..13d94547 100755 --- a/hal.conf.example +++ b/hal.conf.example @@ -15,3 +15,5 @@ sync_port=6666 # Network scanning should probably be disabled in some networks (default on) #netscan.ipscan=false +## ZWave plugin +#zwave.com_port=COM4 diff --git a/lib/netty-all-4.0.46.Final.jar b/plugins/zwave/lib/netty-all-4.0.46.Final.jar similarity index 100% rename from lib/netty-all-4.0.46.Final.jar rename to plugins/zwave/lib/netty-all-4.0.46.Final.jar diff --git a/lib/wzwave-0.0.3.jar b/plugins/zwave/lib/wzwave-0.0.3.jar similarity index 100% rename from lib/wzwave-0.0.3.jar rename to plugins/zwave/lib/wzwave-0.0.3.jar diff --git a/plugins/zwave/src/com/whizzosoftware/wzwave/controller/netty/AbstractNettyChannelInitializer.java b/plugins/zwave/src/com/whizzosoftware/wzwave/controller/netty/AbstractNettyChannelInitializer.java new file mode 100755 index 00000000..7eafe4e2 --- /dev/null +++ b/plugins/zwave/src/com/whizzosoftware/wzwave/controller/netty/AbstractNettyChannelInitializer.java @@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright (c) 2013 Whizzo Software, LLC. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + *******************************************************************************/ +package com.whizzosoftware.wzwave.controller.netty; + +import com.whizzosoftware.wzwave.channel.*; +import com.whizzosoftware.wzwave.codec.ZWaveFrameDecoder; +import com.whizzosoftware.wzwave.codec.ZWaveFrameEncoder; +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.jsc.JSerialCommChannel; +import io.netty.channel.oio.OioEventLoopGroup; + +import java.net.SocketAddress; + +/** + * A abstract initializer + * + * @author Ziver Koc + */ +abstract class AbstractNettyChannelInitializer extends ChannelInitializer { + + protected String serialPort; + private ZWaveChannelInboundHandler inboundHandler; + + protected Bootstrap bootstrap; + protected Channel channel; + + + public AbstractNettyChannelInitializer(String serialPort, ZWaveChannelListener listener) { + this.serialPort = serialPort; + this.inboundHandler = new ZWaveChannelInboundHandler(listener); + + bootstrap = new Bootstrap(); + bootstrap.group(new OioEventLoopGroup()); + bootstrap.handler(this); + } + + + + @Override + protected void initChannel(T channel) throws Exception { + this.channel = channel; + doInitChannel(channel); + + // Setup general channel handlers and coders + channel.pipeline().addLast("decoder", new ZWaveFrameDecoder()); + channel.pipeline().addLast("ack", new AcknowledgementInboundHandler()); + channel.pipeline().addLast("transaction", new ZWaveDataFrameTransactionInboundHandler()); + channel.pipeline().addLast("handler", inboundHandler); + channel.pipeline().addLast("encoder", new ZWaveFrameEncoder()); + channel.pipeline().addLast("writeQueue", new ZWaveQueuedOutboundHandler()); + } + + + public Bootstrap getBootstrap() { + return bootstrap; + } + public Channel getChannel() { + return channel; + } + + + public abstract SocketAddress getSocketAddress(); + + protected abstract void doInitChannel(T channel) throws Exception; +} diff --git a/plugins/zwave/src/com/whizzosoftware/wzwave/controller/netty/NettyJSCChannelInitializer.java b/plugins/zwave/src/com/whizzosoftware/wzwave/controller/netty/NettyJSCChannelInitializer.java new file mode 100755 index 00000000..cbdb768f --- /dev/null +++ b/plugins/zwave/src/com/whizzosoftware/wzwave/controller/netty/NettyJSCChannelInitializer.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2013 Whizzo Software, LLC. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + *******************************************************************************/ +package com.whizzosoftware.wzwave.controller.netty; + +import com.whizzosoftware.wzwave.channel.ZWaveChannelListener; +import io.netty.channel.jsc.JSCChannelConfig; +import io.netty.channel.jsc.JSCDeviceAddress; +import io.netty.channel.jsc.JSerialCommChannel; + + +/** + * A jSerialComm serial port initializer + * + * @author Ziver Koc + */ +public class NettyJSCChannelInitializer extends AbstractNettyChannelInitializer { + + + public NettyJSCChannelInitializer(String serialPort, ZWaveChannelListener listener) { + super(serialPort, listener); + + bootstrap.channel(JSerialCommChannel.class); + } + + @Override + protected void doInitChannel(JSerialCommChannel channel) throws Exception { + this.channel = channel; + channel.config().setBaudrate(115200); + channel.config().setDatabits(8); + channel.config().setParitybit(JSCChannelConfig.Paritybit.NONE); + channel.config().setStopbits(JSCChannelConfig.Stopbits.STOPBITS_1); + } + + + @Override + public JSCDeviceAddress getSocketAddress() { + return new JSCDeviceAddress(serialPort); + } +} diff --git a/plugins/zwave/src/com/whizzosoftware/wzwave/controller/netty/NettyRxtxChannelInitializer.java b/plugins/zwave/src/com/whizzosoftware/wzwave/controller/netty/NettyRxtxChannelInitializer.java new file mode 100755 index 00000000..77959339 --- /dev/null +++ b/plugins/zwave/src/com/whizzosoftware/wzwave/controller/netty/NettyRxtxChannelInitializer.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2013 Whizzo Software, LLC. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + *******************************************************************************/ +package com.whizzosoftware.wzwave.controller.netty; + +import com.whizzosoftware.wzwave.channel.ZWaveChannelListener; +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.Channel; +import io.netty.channel.oio.OioEventLoopGroup; +import io.netty.channel.rxtx.RxtxChannel; +import io.netty.channel.rxtx.RxtxChannelConfig; +import io.netty.channel.rxtx.RxtxDeviceAddress; + +/** + * A RxTx serial port initializer + * + * @author Ziver Koc + */ +public class NettyRxtxChannelInitializer extends AbstractNettyChannelInitializer { + + + public NettyRxtxChannelInitializer(String serialPort, ZWaveChannelListener listener) { + super(serialPort, listener); + + bootstrap.channel(RxtxChannel.class); + } + + @Override + protected void doInitChannel(RxtxChannel channel) throws Exception { + channel.config().setBaudrate(115200); + channel.config().setDatabits(RxtxChannelConfig.Databits.DATABITS_8); + channel.config().setParitybit(RxtxChannelConfig.Paritybit.NONE); + channel.config().setStopbits(RxtxChannelConfig.Stopbits.STOPBITS_1); + } + + + @Override + public RxtxDeviceAddress getSocketAddress() { + return new RxtxDeviceAddress(serialPort); + } +} diff --git a/plugins/zwave/src/com/whizzosoftware/wzwave/controller/netty/NettyZWaveController.java b/plugins/zwave/src/com/whizzosoftware/wzwave/controller/netty/NettyZWaveController.java new file mode 100755 index 00000000..66a96f1d --- /dev/null +++ b/plugins/zwave/src/com/whizzosoftware/wzwave/controller/netty/NettyZWaveController.java @@ -0,0 +1,251 @@ +/******************************************************************************* + * Copyright (c) 2013 Whizzo Software, LLC. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + *******************************************************************************/ +package com.whizzosoftware.wzwave.controller.netty; + +import com.whizzosoftware.wzwave.channel.*; +import com.whizzosoftware.wzwave.codec.ZWaveFrameDecoder; +import com.whizzosoftware.wzwave.codec.ZWaveFrameEncoder; +import com.whizzosoftware.wzwave.controller.ZWaveController; +import com.whizzosoftware.wzwave.controller.ZWaveControllerContext; +import com.whizzosoftware.wzwave.controller.ZWaveControllerListener; +import com.whizzosoftware.wzwave.frame.*; +import com.whizzosoftware.wzwave.node.*; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; + +/** + * A Netty implementation of a ZWaveController. + * + * @author Dan Noguerol + */ +public class NettyZWaveController implements ZWaveController, ZWaveControllerContext, ZWaveControllerListener, ZWaveChannelListener, NodeListener { + private static final Logger logger = LoggerFactory.getLogger(NettyZWaveController.class); + + private AbstractNettyChannelInitializer serial; + private String libraryVersion; + private int homeId; + private byte nodeId; + private ZWaveControllerListener listener; + private final List nodes = new ArrayList(); + private final Map nodeMap = new HashMap(); + + public NettyZWaveController(String serialPort) { + // Choose a available library + try{ + Class.forName("gnu.io.SerialPort"); // check if RxTx is available + logger.info("RxTx is available, using it as Serial port Com port library"); + serial = new NettyRxtxChannelInitializer(serialPort, this); + } catch (ClassNotFoundException e) { + try { + Class.forName("com.fazecast.jSerialComm.SerialPort"); // check if jSerialComm is available + logger.info("jSerialComm is available, using it as Serial port library"); + serial = new NettyJSCChannelInitializer(serialPort, this); + } catch (ClassNotFoundException e1) { + throw new RuntimeException("Unable to find Rxtx or jSerialComm lib in classpath", e); + } + } + } + + /* + * ZWaveController methods + */ + + @Override + public void setListener(ZWaveControllerListener listener) { + this.listener = listener; + } + + public void start() { + serial.getBootstrap().connect(serial.getSocketAddress()).addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) throws Exception { + if (future.isSuccess()) { + serial.getChannel().write(new Version()); + serial.getChannel().write(new MemoryGetId()); + serial.getChannel().write(new InitData()); + } else { + listener.onZWaveConnectionFailure(future.cause()); + } + } + }); + } + + @Override + public void stop() { + + } + + @Override + public int getHomeId() { + return homeId; + } + + @Override + public byte getNodeId() { + return nodeId; + } + + @Override + public String getLibraryVersion() { + return libraryVersion; + } + + @Override + public Collection getNodes() { + return nodes; + } + + @Override + public void sendDataFrame(DataFrame dataFrame) { + serial.getChannel().write(dataFrame); + } + + /* + * ZWaveControllerListener methods + */ + + @Override + public void onZWaveNodeAdded(ZWaveEndpoint node) { + if (listener != null) { + listener.onZWaveNodeAdded(node); + } + } + + @Override + public void onZWaveNodeUpdated(ZWaveEndpoint node) { + if (listener != null) { + listener.onZWaveNodeUpdated(node); + } + } + + @Override + public void onZWaveConnectionFailure(Throwable t) { + if (listener != null) { + listener.onZWaveConnectionFailure(t); + } + } + + /* + * ZWaveChannelListener methods + */ + + @Override + public void onLibraryInfo(String libraryVersion) { + this.libraryVersion = libraryVersion; + } + + @Override + public void onControllerInfo(int homeId, byte nodeId) { + this.homeId = homeId; + this.nodeId = nodeId; + } + + @Override + public void onNodeProtocolInfo(byte nodeId, NodeProtocolInfo nodeProtocolInfo) { + try { + logger.trace("Received protocol info for node " + nodeId); + ZWaveNode node = ZWaveNodeFactory.createNode(this, nodeId, nodeProtocolInfo, this); + logger.trace("Created node [" + node.getNodeId() + "]: " + node); + nodes.add(node); + nodeMap.put(node.getNodeId(), node); + } catch (NodeCreationException e) { + logger.error("Unable to create node", e); + } + } + + @Override + public void onSendData(SendData sendData) { + byte nodeId = sendData.getNodeId(); + ZWaveNode node = nodeMap.get(nodeId); + if (node != null) { + node.onDataFrameReceived(this, sendData); + } else { + logger.error("Unable to find node " + nodeId); + } + } + + @Override + public void onApplicationCommand(ApplicationCommand applicationCommand) { + ZWaveNode node = nodeMap.get(applicationCommand.getNodeId()); + if (node != null) { + node.onDataFrameReceived(this, applicationCommand); + if (listener != null && node.isStarted()) { + listener.onZWaveNodeUpdated(node); + } + } else { + logger.error("Unable to find node " + applicationCommand.getNodeId()); + } + } + + @Override + public void onApplicationUpdate(ApplicationUpdate applicationUpdate) { + Byte nodeId = applicationUpdate.getNodeId(); + + if (applicationUpdate.didInfoRequestFail()) { + logger.trace("UPDATE_STATE_NODE_INFO_REQ_FAILED received"); + } + + if (nodeId != null) { + ZWaveNode node = nodeMap.get(nodeId); + if (node != null) { + node.onDataFrameReceived(this, applicationUpdate); + if (listener != null && node.isStarted()) { + listener.onZWaveNodeUpdated(node); + } + } else { + logger.error("Unable to find node " + applicationUpdate.getNodeId()); + } + } else { + logger.error("Unable to determine node to route ApplicationUpdate to"); + } + } + + /* + * NodeListener methods + */ + + @Override + public void onNodeStarted(ZWaveNode node) { + // when a node moves to the "started" state, alert listeners that it's ready to be added + if (listener != null) { + listener.onZWaveNodeAdded(node); + } + } + + /* + * Test main + */ + + public static void main(String[] args) throws Exception { + NettyZWaveController c = new NettyZWaveController("/dev/cu.SLAB_USBtoUART"); + ZWaveControllerListener listener = new ZWaveControllerListener() { + @Override + public void onZWaveNodeAdded(ZWaveEndpoint node) { + System.out.println("onZWaveNodeAdded: " + node); + } + + @Override + public void onZWaveNodeUpdated(ZWaveEndpoint node) { + System.out.println("onZWaveNodeUpdated: " + node); + } + + @Override + public void onZWaveConnectionFailure(Throwable t) { + System.out.println("A connection error occurred: " + t.getLocalizedMessage()); + } + }; + c.setListener(listener); + c.start(); + + Thread.sleep(10000); + } +} diff --git a/plugins/zwave/src/io/netty/channel/jsc/DefaultJSCChannelConfig.java b/plugins/zwave/src/io/netty/channel/jsc/DefaultJSCChannelConfig.java new file mode 100755 index 00000000..8f3680b8 --- /dev/null +++ b/plugins/zwave/src/io/netty/channel/jsc/DefaultJSCChannelConfig.java @@ -0,0 +1,231 @@ +/* + * Copyright 2017 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel.jsc; + +import io.netty.buffer.ByteBufAllocator; +import io.netty.channel.ChannelOption; +import io.netty.channel.DefaultChannelConfig; +import io.netty.channel.MessageSizeEstimator; +import io.netty.channel.RecvByteBufAllocator; + +import java.util.Map; + +import static io.netty.channel.jsc.JSCChannelOption.*; +import static io.netty.channel.rxtx.RxtxChannelOption.WAIT_TIME; + +/** + * Default configuration class for jSerialComm device connections. + */ +final class DefaultJSCChannelConfig extends DefaultChannelConfig implements JSCChannelConfig { + + private volatile int baudrate = 115200; + private volatile boolean dtr; + private volatile boolean rts; + private volatile Stopbits stopbits = Stopbits.STOPBITS_1; + private volatile int databits = 8; + private volatile Paritybit paritybit = Paritybit.NONE; + private volatile int waitTime; + private volatile int readTimeout = 1000; + + DefaultJSCChannelConfig(JSerialCommChannel channel) { + super(channel); + } + + @Override + public Map, Object> getOptions() { + return getOptions(super.getOptions(), BAUD_RATE, DTR, RTS, STOP_BITS, DATA_BITS, PARITY_BIT, WAIT_TIME); + } + + @SuppressWarnings("unchecked") + @Override + public T getOption(ChannelOption option) { + if (option == BAUD_RATE) { + return (T) Integer.valueOf(getBaudrate()); + } + if (option == STOP_BITS) { + return (T) getStopbits(); + } + if (option == DATA_BITS) { + return (T) Integer.valueOf(getDatabits()); + } + if (option == PARITY_BIT) { + return (T) getParitybit(); + } + if (option == WAIT_TIME) { + return (T) Integer.valueOf(getWaitTimeMillis()); + } + if (option == READ_TIMEOUT) { + return (T) Integer.valueOf(getReadTimeout()); + } + return super.getOption(option); + } + + @Override + public boolean setOption(ChannelOption option, T value) { + validate(option, value); + + if (option == BAUD_RATE) { + setBaudrate((Integer) value); + } else if (option == STOP_BITS) { + setStopbits((Stopbits) value); + } else if (option == DATA_BITS) { + setDatabits((Integer)value); + } else if (option == PARITY_BIT) { + setParitybit((Paritybit) value); + } else if (option == WAIT_TIME) { + setWaitTimeMillis((Integer) value); + } else if (option == READ_TIMEOUT) { + setReadTimeout((Integer) value); + } else { + return super.setOption(option, value); + } + return true; + } + + @Override + public JSCChannelConfig setBaudrate(final int baudrate) { + this.baudrate = baudrate; + return this; + } + + @Override + public JSCChannelConfig setStopbits(final Stopbits stopbits) { + this.stopbits = stopbits; + return this; + } + + @Override + public JSCChannelConfig setDatabits(final int databits) { + this.databits = databits; + return this; + } + + @Override + public JSCChannelConfig setParitybit(final Paritybit paritybit) { + this.paritybit = paritybit; + return this; + } + + @Override + public int getBaudrate() { + return baudrate; + } + + @Override + public Stopbits getStopbits() { + return stopbits; + } + + @Override + public int getDatabits() { + return databits; + } + + @Override + public Paritybit getParitybit() { + return paritybit; + } + + + @Override + public int getWaitTimeMillis() { + return waitTime; + } + + @Override + public JSCChannelConfig setWaitTimeMillis(final int waitTimeMillis) { + if (waitTimeMillis < 0) { + throw new IllegalArgumentException("Wait time must be >= 0"); + } + waitTime = waitTimeMillis; + return this; + } + + @Override + public JSCChannelConfig setReadTimeout(int readTimeout) { + if (readTimeout < 0) { + throw new IllegalArgumentException("readTime must be >= 0"); + } + this.readTimeout = readTimeout; + return this; + } + + @Override + public int getReadTimeout() { + return readTimeout; + } + + @Override + public JSCChannelConfig setConnectTimeoutMillis(int connectTimeoutMillis) { + super.setConnectTimeoutMillis(connectTimeoutMillis); + return this; + } + + @Override + @Deprecated + public JSCChannelConfig setMaxMessagesPerRead(int maxMessagesPerRead) { + super.setMaxMessagesPerRead(maxMessagesPerRead); + return this; + } + + @Override + public JSCChannelConfig setWriteSpinCount(int writeSpinCount) { + super.setWriteSpinCount(writeSpinCount); + return this; + } + + @Override + public JSCChannelConfig setAllocator(ByteBufAllocator allocator) { + super.setAllocator(allocator); + return this; + } + + @Override + public JSCChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator) { + super.setRecvByteBufAllocator(allocator); + return this; + } + + @Override + public JSCChannelConfig setAutoRead(boolean autoRead) { + super.setAutoRead(autoRead); + return this; + } + + @Override + public JSCChannelConfig setAutoClose(boolean autoClose) { + super.setAutoClose(autoClose); + return this; + } + + @Override + public JSCChannelConfig setWriteBufferHighWaterMark(int writeBufferHighWaterMark) { + super.setWriteBufferHighWaterMark(writeBufferHighWaterMark); + return this; + } + + @Override + public JSCChannelConfig setWriteBufferLowWaterMark(int writeBufferLowWaterMark) { + super.setWriteBufferLowWaterMark(writeBufferLowWaterMark); + return this; + } + + @Override + public JSCChannelConfig setMessageSizeEstimator(MessageSizeEstimator estimator) { + super.setMessageSizeEstimator(estimator); + return this; + } +} diff --git a/plugins/zwave/src/io/netty/channel/jsc/JSCChannelConfig.java b/plugins/zwave/src/io/netty/channel/jsc/JSCChannelConfig.java new file mode 100755 index 00000000..e8d84a70 --- /dev/null +++ b/plugins/zwave/src/io/netty/channel/jsc/JSCChannelConfig.java @@ -0,0 +1,236 @@ +/* + * Copyright 2017 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel.jsc; + +import com.fazecast.jSerialComm.SerialPort; +import io.netty.buffer.ByteBufAllocator; +import io.netty.channel.ChannelConfig; +import io.netty.channel.MessageSizeEstimator; +import io.netty.channel.RecvByteBufAllocator; + +/** + * A configuration class for JSerialComm device connections. + * + *

Available options

+ * + * In addition to the options provided by {@link ChannelConfig}, + * {@link DefaultJSCChannelConfig} allows the following options in the option map: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
NameAssociated setter method
{@link JSCChannelOption#BAUD_RATE}{@link #setBaudrate(int)}
{@link JSCChannelOption#STOP_BITS}{@link #setStopbits(Stopbits)}
{@link JSCChannelOption#DATA_BITS}{@link #setDatabits(int)}
{@link JSCChannelOption#PARITY_BIT}{@link #setParitybit(Paritybit)}
{@link JSCChannelOption#WAIT_TIME}{@link #setWaitTimeMillis(int)}
+ */ +public interface JSCChannelConfig extends ChannelConfig { + enum Stopbits { + /** + * 1 stop bit will be sent at the end of every character + */ + STOPBITS_1(SerialPort.ONE_STOP_BIT), + /** + * 2 stop bits will be sent at the end of every character + */ + STOPBITS_2(SerialPort.TWO_STOP_BITS), + /** + * 1.5 stop bits will be sent at the end of every character + */ + STOPBITS_1_5(SerialPort.ONE_POINT_FIVE_STOP_BITS); + + private final int value; + + Stopbits(int value) { + this.value = value; + } + + public int value() { + return value; + } + + public static Stopbits valueOf(int value) { + for (Stopbits stopbit : Stopbits.values()) { + if (stopbit.value == value) { + return stopbit; + } + } + throw new IllegalArgumentException("unknown " + Stopbits.class.getSimpleName() + " value: " + value); + } + } + + enum Paritybit { + /** + * No parity bit will be sent with each data character at all + */ + NONE(SerialPort.NO_PARITY), + /** + * An odd parity bit will be sent with each data character, ie. will be set + * to 1 if the data character contains an even number of bits set to 1. + */ + ODD(SerialPort.ODD_PARITY), + /** + * An even parity bit will be sent with each data character, ie. will be set + * to 1 if the data character contains an odd number of bits set to 1. + */ + EVEN(SerialPort.EVEN_PARITY), + /** + * A mark parity bit (ie. always 1) will be sent with each data character + */ + MARK(SerialPort.MARK_PARITY), + /** + * A space parity bit (ie. always 0) will be sent with each data character + */ + SPACE(SerialPort.SPACE_PARITY); + + private final int value; + + Paritybit(int value) { + this.value = value; + } + + public int value() { + return value; + } + + public static Paritybit valueOf(int value) { + for (Paritybit paritybit : Paritybit.values()) { + if (paritybit.value == value) { + return paritybit; + } + } + throw new IllegalArgumentException("unknown " + Paritybit.class.getSimpleName() + " value: " + value); + } + } + + /** + * Sets the baud rate (ie. bits per second) for communication with the serial device. + * The baud rate will include bits for framing (in the form of stop bits and parity), + * such that the effective data rate will be lower than this value. + * + * @param baudrate The baud rate (in bits per second) + */ + JSCChannelConfig setBaudrate(int baudrate); + + /** + * Sets the number of stop bits to include at the end of every character to aid the + * serial device in synchronising with the data. + * + * @param stopbits The number of stop bits to use + */ + JSCChannelConfig setStopbits(Stopbits stopbits); + + /** + * Sets the number of data bits to use to make up each character sent to the serial + * device. + * + * @param databits The number of data bits to use + */ + JSCChannelConfig setDatabits(int databits); + + /** + * Sets the type of parity bit to be used when communicating with the serial device. + * + * @param paritybit The type of parity bit to be used + */ + JSCChannelConfig setParitybit(Paritybit paritybit); + + /** + * @return The configured baud rate, defaulting to 115200 if unset + */ + int getBaudrate(); + + /** + * @return The configured stop bits, defaulting to {@link Stopbits#STOPBITS_1} if unset + */ + Stopbits getStopbits(); + + /** + * @return The configured data bits, defaulting to 8 if unset + */ + int getDatabits(); + + /** + * @return The configured parity bit, defaulting to {@link Paritybit#NONE} if unset + */ + Paritybit getParitybit(); + + /** + * @return The number of milliseconds to wait between opening the serial port and + * initialising. + */ + int getWaitTimeMillis(); + + /** + * Sets the time to wait after opening the serial port and before sending it any + * configuration information or data. A value of 0 indicates that no waiting should + * occur. + * + * @param waitTimeMillis The number of milliseconds to wait, defaulting to 0 (no + * wait) if unset + * @throws IllegalArgumentException if the supplied value is < 0 + */ + JSCChannelConfig setWaitTimeMillis(int waitTimeMillis); + + /** + * Sets the maximal time (in ms) to block while try to read from the serial port. Default is 1000ms + */ + JSCChannelConfig setReadTimeout(int readTimeout); + + /** + * Return the maximal time (in ms) to block and wait for something to be ready to read. + */ + int getReadTimeout(); + + @Override + JSCChannelConfig setConnectTimeoutMillis(int connectTimeoutMillis); + + @Override + @Deprecated + JSCChannelConfig setMaxMessagesPerRead(int maxMessagesPerRead); + + @Override + JSCChannelConfig setWriteSpinCount(int writeSpinCount); + + @Override + JSCChannelConfig setAllocator(ByteBufAllocator allocator); + + @Override + JSCChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator); + + @Override + JSCChannelConfig setAutoRead(boolean autoRead); + + @Override + JSCChannelConfig setAutoClose(boolean autoClose); + + @Override + JSCChannelConfig setWriteBufferHighWaterMark(int writeBufferHighWaterMark); + + @Override + JSCChannelConfig setWriteBufferLowWaterMark(int writeBufferLowWaterMark); + + @Override + JSCChannelConfig setMessageSizeEstimator(MessageSizeEstimator estimator); +} diff --git a/plugins/zwave/src/io/netty/channel/jsc/JSCChannelOption.java b/plugins/zwave/src/io/netty/channel/jsc/JSCChannelOption.java new file mode 100755 index 00000000..e31f5fb6 --- /dev/null +++ b/plugins/zwave/src/io/netty/channel/jsc/JSCChannelOption.java @@ -0,0 +1,40 @@ +/* + * Copyright 2017 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel.jsc; + +import io.netty.channel.ChannelOption; +import io.netty.channel.jsc.JSCChannelConfig.Paritybit; +import io.netty.channel.jsc.JSCChannelConfig.Stopbits; + +/** + * Option for configuring a serial port connection + */ +public final class JSCChannelOption extends ChannelOption { + + public static final ChannelOption BAUD_RATE = valueOf("BAUD_RATE"); + public static final ChannelOption DTR = valueOf("DTR"); + public static final ChannelOption RTS = valueOf("RTS"); + public static final ChannelOption STOP_BITS = valueOf("STOP_BITS"); + public static final ChannelOption DATA_BITS = valueOf("DATA_BITS"); + public static final ChannelOption PARITY_BIT = valueOf("PARITY_BIT"); + public static final ChannelOption WAIT_TIME = valueOf("WITE_TIMEOUT"); + public static final ChannelOption READ_TIMEOUT = valueOf("READ_TIMEOUT"); + + @SuppressWarnings({ "unused", "deprecation" }) + private JSCChannelOption() { + super(null); + } +} diff --git a/plugins/zwave/src/io/netty/channel/jsc/JSCDeviceAddress.java b/plugins/zwave/src/io/netty/channel/jsc/JSCDeviceAddress.java new file mode 100755 index 00000000..c1589d97 --- /dev/null +++ b/plugins/zwave/src/io/netty/channel/jsc/JSCDeviceAddress.java @@ -0,0 +1,45 @@ +/* + * Copyright 2017 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel.jsc; + +import java.net.SocketAddress; + +/** + * A {@link SocketAddress} subclass to wrap the serial port address of a jSerialComm + * device (e.g. COM1, /dev/ttyUSB0). + */ +public class JSCDeviceAddress extends SocketAddress { + + private static final long serialVersionUID = -2907820090993709523L; + + private final String value; + + /** + * Creates a JSCDeviceAddress representing the address of the serial port. + * + * @param value the address of the device (e.g. COM1, /dev/ttyUSB0, ...) + */ + public JSCDeviceAddress(String value) { + this.value = value; + } + + /** + * @return The serial port address of the device (e.g. COM1, /dev/ttyUSB0, ...) + */ + public String value() { + return value; + } +} diff --git a/plugins/zwave/src/io/netty/channel/jsc/JSerialCommChannel.java b/plugins/zwave/src/io/netty/channel/jsc/JSerialCommChannel.java new file mode 100755 index 00000000..c5758c13 --- /dev/null +++ b/plugins/zwave/src/io/netty/channel/jsc/JSerialCommChannel.java @@ -0,0 +1,181 @@ +/* + * Copyright 2017 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel.jsc; + +import com.fazecast.jSerialComm.SerialPort; +import io.netty.channel.ChannelPromise; +import io.netty.channel.oio.OioByteStreamChannel; + +import java.io.IOException; +import java.net.SocketAddress; +import java.util.concurrent.TimeUnit; + +import static io.netty.channel.jsc.JSCChannelOption.*; + + +/** + * A channel to a serial device using the jSerialComm library. + */ +public class JSerialCommChannel extends OioByteStreamChannel { + + private static final JSCDeviceAddress LOCAL_ADDRESS = new JSCDeviceAddress("localhost"); + + private final JSCChannelConfig config; + + private boolean open = true; + private JSCDeviceAddress deviceAddress; + private SerialPort serialPort; + + public JSerialCommChannel() { + super(null); + + config = new DefaultJSCChannelConfig(this); + } + + @Override + public JSCChannelConfig config() { + return config; + } + + @Override + public boolean isOpen() { + return open; + } + + @Override + protected AbstractUnsafe newUnsafe() { + return new JSCUnsafe(); + } + + @Override + protected void doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception { + JSCDeviceAddress remote = (JSCDeviceAddress) remoteAddress; + SerialPort commPort = SerialPort.getCommPort(remote.value()); + if(!commPort.openPort()) + throw new IOException("Could not open port: "+remote.value()); + + commPort.setComPortTimeouts( + SerialPort.TIMEOUT_READ_BLOCKING, config().getOption(READ_TIMEOUT), 0); + + deviceAddress = remote; + serialPort = commPort; + } + + protected void doInit() throws Exception { + serialPort.setComPortParameters( + config().getOption(BAUD_RATE), + config().getOption(DATA_BITS), + config().getOption(STOP_BITS).value(), + config().getOption(PARITY_BIT).value() + ); + //serialPort.setFlowControl(config().getOption(FLOW_CTRL)); + + activate(serialPort.getInputStream(), serialPort.getOutputStream()); + } + + @Override + public JSCDeviceAddress localAddress() { + return (JSCDeviceAddress) super.localAddress(); + } + + @Override + public JSCDeviceAddress remoteAddress() { + return (JSCDeviceAddress) super.remoteAddress(); + } + + @Override + protected JSCDeviceAddress localAddress0() { + return LOCAL_ADDRESS; + } + + @Override + protected JSCDeviceAddress remoteAddress0() { + return deviceAddress; + } + + @Override + protected void doBind(SocketAddress localAddress) throws Exception { + throw new UnsupportedOperationException(); + } + + @Override + protected void doDisconnect() throws Exception { + doClose(); + } + + @Override + protected void doClose() throws Exception { + open = false; + try { + super.doClose(); + } finally { + if (serialPort != null) { + serialPort.closePort(); + serialPort = null; + } + } + } + + @Override + protected boolean isInputShutdown() { + return !open; + } + + + private final class JSCUnsafe extends AbstractUnsafe { + @Override + public void connect( + final SocketAddress remoteAddress, + final SocketAddress localAddress, final ChannelPromise promise) { + if (!promise.setUncancellable() || !isOpen()) { + return; + } + + try { + final boolean wasActive = isActive(); + doConnect(remoteAddress, localAddress); + + int waitTime = config().getOption(WAIT_TIME); + if (waitTime > 0) { + eventLoop().schedule(new Runnable() { + @Override + public void run() { + try { + doInit(); + safeSetSuccess(promise); + if (!wasActive && isActive()) { + pipeline().fireChannelActive(); + } + } catch (Throwable t) { + safeSetFailure(promise, t); + closeIfClosed(); + } + } + }, waitTime, TimeUnit.MILLISECONDS); + } else { + doInit(); + safeSetSuccess(promise); + if (!wasActive && isActive()) { + pipeline().fireChannelActive(); + } + } + } catch (Throwable t) { + safeSetFailure(promise, t); + closeIfClosed(); + } + } + } +} diff --git a/plugins/zwave/src/io/netty/channel/jsc/package-info.java b/plugins/zwave/src/io/netty/channel/jsc/package-info.java new file mode 100755 index 00000000..0f6e5642 --- /dev/null +++ b/plugins/zwave/src/io/netty/channel/jsc/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2017 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +/** + * A serial and parallel port communication transport based on RXTX. + */ +package io.netty.channel.jsc; diff --git a/plugins/zwave/src/se/hal/plugin/zwave/HalZWaveController.java b/plugins/zwave/src/se/hal/plugin/zwave/HalZWaveController.java new file mode 100755 index 00000000..fab16c43 --- /dev/null +++ b/plugins/zwave/src/se/hal/plugin/zwave/HalZWaveController.java @@ -0,0 +1,92 @@ +package se.hal.plugin.zwave; + +import com.whizzosoftware.wzwave.controller.ZWaveController; +import com.whizzosoftware.wzwave.controller.ZWaveControllerListener; +import com.whizzosoftware.wzwave.controller.netty.NettyZWaveController; +import com.whizzosoftware.wzwave.node.ZWaveEndpoint; +import se.hal.HalContext; +import se.hal.intf.*; + +/** + * + */ +public class HalZWaveController implements HalSensorController, HalEventController, HalAutoScannableController, ZWaveControllerListener { + + private ZWaveController controller; + + + @Override + public boolean isAvailable() { + return HalContext.getStringProperty("zwave.com_port") != null; + } + @Override + public void initialize() throws Exception { + controller = new NettyZWaveController(HalContext.getStringProperty("zwave.com_port")); + controller.setListener(this); + controller.start(); + } + + @Override + public void close() { + controller.stop(); + controller = null; + } + + ////////////// Z-WAVE CODE //////////////////////// + + @Override + public void onZWaveNodeAdded(ZWaveEndpoint zWaveEndpoint) { + + } + + @Override + public void onZWaveNodeUpdated(ZWaveEndpoint zWaveEndpoint) { + + } + + @Override + public void onZWaveConnectionFailure(Throwable throwable) { + + } + + + ////////////// HAL CODE //////////////////////// + + @Override + public void register(HalSensorConfig sensor) { + + } + @Override + public void register(HalEventConfig event) { + + } + + @Override + public void deregister(HalSensorConfig sensor) { + + } + @Override + public void deregister(HalEventConfig event) { + + } + + @Override + public int size() { + return 0; + } + + @Override + public void setListener(HalEventReportListener listener) { + + } + @Override + public void setListener(HalSensorReportListener listener) { + + } + + + @Override + public void send(HalEventConfig eventConfig, HalEventData eventData) { + + } +} diff --git a/src/se/hal/plugin/zwave/plugin.json b/plugins/zwave/src/se/hal/plugin/zwave/plugin.json similarity index 100% rename from src/se/hal/plugin/zwave/plugin.json rename to plugins/zwave/src/se/hal/plugin/zwave/plugin.json