artnet optimiert, fehlerkorrigiert

senden von Werten nur, wenn was geändert wurde.
This commit is contained in:
Lucas Pleß 2012-03-03 18:29:31 +01:00
parent d8817c2e46
commit ddea2d1e69
38 changed files with 326 additions and 1830 deletions

View File

@ -0,0 +1,5 @@
XDOCLETBUILDERACTIVE=true
XDOCLETHOME=
XDOCLETUSEGLOBAL=true
XDOCLETVERSION=1.2.1
eclipse.preferences.version=1

View File

@ -0,0 +1,2 @@
eclipse.preferences.version=1
org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false

View File

@ -0,0 +1,2 @@
eclipse.preferences.version=1
org.eclipse.wst.ws.service.policy.projectEnabled=false

View File

@ -1,146 +0,0 @@
/*
* This file is part of artnet4j.
*
* Copyright 2009 Karsten Schmidt (PostSpectacular Ltd.)
*
* artnet4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* artnet4j is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with artnet4j. If not, see <http://www.gnu.org/licenses/>.
*/
package artnet4j;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.logging.Level;
import java.util.logging.Logger;
import artnet4j.packets.ArtNetPacket;
public class ArtNet {
public static final Logger logger = Logger.getLogger(ArtNet.class.getClass().getName());
protected static final long ARTPOLL_REPLY_TIMEOUT = 3000;
protected static final String VERSION = "0001-20091119";
protected ArtNetServer server;
// protected ArtNetNodeDiscovery discovery;
// public ArtNet() {
// logger.info("Art-Net v" + VERSION);
// }
// public void addServerListener(ArtNetServerListener l) {
// server.addListener(l);
// }
//
// public void broadcastPacket(ArtNetPacket packet) {
// server.broadcastPacket(packet);
// }
// public ArtNetNodeDiscovery getNodeDiscovery() {
// if (discovery == null) {
// discovery = new ArtNetNodeDiscovery(this);
// }
// return discovery;
// }
public void init() {
server = new ArtNetServer();
// server.addListener(new ArtNetServerEventAdapter() {
// @Override
// public void artNetPacketReceived(ArtNetPacket packet) {
// logger.fine("packet received: " + packet.getType());
// if (discovery != null && packet.getType() == PacketType.ART_POLL_REPLY) {
// discovery.discoverNode((ArtPollReplyPacket) packet);
// }
// }
//
// @Override
// public void artNetServerStarted(ArtNetServer artNetServer) {
// logger.fine("server started callback");
// }
//
// @Override
// public void artNetServerStopped(ArtNetServer artNetServer) {
// logger.info("server stopped");
// }
// });
}
// public void removeServerListener(ArtNetServerListener l) {
// server.removeListener(l);
// }
//
// public void setBroadCastAddress(String ip) {
// server.setBroadcastAddress(ip);
// }
public void start() throws SocketException, ArtNetException {
if (server == null) {
init();
}
server.start();
}
// public void startNodeDiscovery() throws ArtNetException {
// getNodeDiscovery().start();
// }
public void stop() {
// if (discovery != null) {
// discovery.stop();
// }
if (server != null) {
server.stop();
}
}
// /**
// * Sends the given packet to the specified Art-Net node.
// *
// * @param packet
// * @param node
// */
// public void unicastPacket(ArtNetPacket packet, ArtNetNode node) {
// server.unicastPacket(packet, node.getIPAddress());
// }
//
// /**
// * Sends the given packet to the specified IP address.
// *
// * @param packet
// * @param adr
// */
// public void unicastPacket(ArtNetPacket packet, InetAddress adr) {
// server.unicastPacket(packet, adr);
// }
/**
* Sends the given packet to the specified IP address.
*
* @param packet
* @param adr
*/
public void unicastPacket(ArtNetPacket packet, String adr) {
InetAddress targetAdress;
try {
targetAdress = InetAddress.getByName(adr);
server.unicastPacket(packet, targetAdress);
} catch (UnknownHostException e) {
logger.log(Level.WARNING, e.getMessage(), e);
}
}
}

View File

@ -1,33 +0,0 @@
/*
* This file is part of artnet4j.
*
* Copyright 2009 Karsten Schmidt (PostSpectacular Ltd.)
*
* artnet4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* artnet4j is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with artnet4j. If not, see <http://www.gnu.org/licenses/>.
*/
package artnet4j;
public class ArtNetException extends Exception {
private static final long serialVersionUID = 1L;
public ArtNetException(String message) {
super(message);
}
public ArtNetException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -1,170 +0,0 @@
/*
* This file is part of artnet4j.
*
* Copyright 2009 Karsten Schmidt (PostSpectacular Ltd.)
*
* artnet4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* artnet4j is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with artnet4j. If not, see <http://www.gnu.org/licenses/>.
*/
package artnet4j;
import java.net.InetAddress;
import java.util.logging.Logger;
import artnet4j.packets.ArtPollReplyPacket;
import artnet4j.packets.ByteUtils;
public class ArtNetNode {
protected static final Logger logger = Logger.getLogger(ArtNetNode.class
.getClass().getName());
protected final NodeStyle nodeStyle;
private InetAddress ip;
private int subSwitch;
private int oemCode;
private int nodeStatus;
private NodeReportCode reportCode;
private String shortName;
private String longName;
private int numPorts;
private PortDescriptor[] ports;
private byte[] dmxIns;
private byte[] dmxOuts;
public ArtNetNode() {
this(NodeStyle.ST_NODE);
}
public ArtNetNode(NodeStyle style) {
nodeStyle = style;
}
public void extractConfig(ArtPollReplyPacket source) {
setIPAddress(source.getIPAddress());
subSwitch = source.getSubSwitch();
oemCode = source.getOEMCode();
nodeStatus = source.getNodeStatus();
shortName = source.getShortName();
longName = source.getLongName();
ports = source.getPorts();
numPorts = ports.length;
reportCode = source.getReportCode();
dmxIns = source.getDmxIns();
dmxOuts = source.getDmxOuts();
logger.info("updated node config");
}
/**
* @return the dmxIns
*/
public byte[] getDmxIns() {
return dmxIns;
}
/**
* @return the dmxOuts
*/
public byte[] getDmxOuts() {
return dmxOuts;
}
/**
* @return the ip
*/
public InetAddress getIPAddress() {
return ip;
}
/**
* @return the longName
*/
public String getLongName() {
return longName;
}
/**
* @return the nodeStatus
*/
public int getNodeStatus() {
return nodeStatus;
}
/**
* @return the nodeStyle
*/
public NodeStyle getNodeStyle() {
return nodeStyle;
}
/**
* @return the numPorts
*/
public int getNumPorts() {
return numPorts;
}
/**
* @return the oemCode
*/
public int getOemCode() {
return oemCode;
}
/**
* @return the ports
*/
public PortDescriptor[] getPorts() {
return ports;
}
/**
* @return the reportCode
*/
public NodeReportCode getReportCode() {
return reportCode;
}
/**
* @return the shortName
*/
public String getShortName() {
return shortName;
}
public int getSubNet() {
return subSwitch;
}
public String getSubNetAsHex() {
return ByteUtils.hex(subSwitch, 2);
}
public void setIPAddress(InetAddress ip) {
this.ip = ip;
}
@Override
public String toString() {
return "node: " + nodeStyle + " " + ip + " " + longName + ", "
+ numPorts + " ports, subswitch: "
+ ByteUtils.hex(subSwitch, 2);
}
}

View File

@ -1,132 +0,0 @@
///*
// * This file is part of artnet4j.
// *
// * Copyright 2009 Karsten Schmidt (PostSpectacular Ltd.)
// *
// * artnet4j is free software: you can redistribute it and/or modify
// * it under the terms of the GNU General Public License as published by
// * the Free Software Foundation, either version 3 of the License, or
// * (at your option) any later version.
// *
// * artnet4j is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// * GNU General Public License for more details.
// *
// * You should have received a copy of the GNU General Public License
// * along with artnet4j. If not, see <http://www.gnu.org/licenses/>.
// */
//
//package artnet4j;
//
//import java.net.InetAddress;
//import java.util.ArrayList;
//import java.util.List;
//import java.util.concurrent.ConcurrentHashMap;
//import java.util.logging.Logger;
//
//import artnet4j.events.ArtNetDiscoveryListener;
//import artnet4j.packets.ArtPollPacket;
//import artnet4j.packets.ArtPollReplyPacket;
//
//public class ArtNetNodeDiscovery implements Runnable {
//
// public static final int POLL_INTERVAL = 10000;
//
// public static final Logger logger = Logger
// .getLogger(ArtNetNodeDiscovery.class.getClass().getName());
//
// protected final ArtNet artNet;
// protected ConcurrentHashMap<InetAddress, ArtNetNode> discoveredNodes = new ConcurrentHashMap<InetAddress, ArtNetNode>();
// protected List<ArtNetNode> lastDiscovered = new ArrayList<ArtNetNode>();
// protected List<ArtNetDiscoveryListener> listeners = new ArrayList<ArtNetDiscoveryListener>();
//
// protected boolean isActive = true;
//
// protected long discoveryInterval;
//
// private Thread discoveryThread;
//
// public ArtNetNodeDiscovery(ArtNet artNet) {
// this.artNet = artNet;
// setInterval(POLL_INTERVAL);
// }
//
// public void addListener(ArtNetDiscoveryListener l) {
// synchronized (listeners) {
// listeners.add(l);
// }
// }
//
// public void discoverNode(ArtPollReplyPacket reply) {
// InetAddress nodeIP = reply.getIPAddress();
// ArtNetNode node = discoveredNodes.get(nodeIP);
// if (node == null) {
// logger.info("discovered new node: " + nodeIP);
// node = reply.getNodeStyle().createNode();
// node.extractConfig(reply);
// discoveredNodes.put(nodeIP, node);
// for (ArtNetDiscoveryListener l : listeners) {
// l.discoveredNewNode(node);
// }
// } else {
// node.extractConfig(reply);
// }
// lastDiscovered.add(node);
// }
//
// public void removeListener(ArtNetDiscoveryListener l) {
// synchronized (listeners) {
// listeners.remove(l);
// }
// }
//
// @Override
// public void run() {
// try {
// while (isActive) {
// lastDiscovered.clear();
// ArtPollPacket poll = new ArtPollPacket();
// artNet.broadcastPacket(poll);
// Thread.sleep(ArtNet.ARTPOLL_REPLY_TIMEOUT);
// if (isActive) {
// synchronized (listeners) {
// for (ArtNetNode node : discoveredNodes.values()) {
// if (!lastDiscovered.contains(node)) {
// discoveredNodes.remove(node.getIPAddress());
// for (ArtNetDiscoveryListener l : listeners) {
// l.discoveredNodeDisconnected(node);
// }
// }
// }
// for (ArtNetDiscoveryListener l : listeners) {
// l.discoveryCompleted(new ArrayList<ArtNetNode>(
// discoveredNodes.values()));
// }
// }
// Thread.sleep(discoveryInterval
// - ArtNet.ARTPOLL_REPLY_TIMEOUT);
// }
// }
// } catch (InterruptedException e) {
// logger.warning("node discovery interrupted");
// }
// }
//
// public void setInterval(int interval) {
// discoveryInterval = Math.max(interval, ArtNet.ARTPOLL_REPLY_TIMEOUT);
// }
//
// public void start() throws ArtNetException {
// if (discoveryThread == null) {
// discoveryThread = new Thread(this);
// discoveryThread.start();
// } else {
// throw new ArtNetException("discovery already started.");
// }
// }
//
// public void stop() {
// isActive = false;
// }
//}

View File

@ -1,179 +0,0 @@
/*
* This file is part of artnet4j.
*
* Copyright 2009 Karsten Schmidt (PostSpectacular Ltd.)
*
* artnet4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* artnet4j is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with artnet4j. If not, see <http://www.gnu.org/licenses/>.
*/
package artnet4j;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.logging.Level;
import artnet4j.packets.ArtNetPacket;
public class ArtNetServer extends ArtNetNode { // implements Runnable {
public static final int DEFAULT_PORT = 0x1936;
public static final String DEFAULT_BROADCAST_IP = "2.255.255.255";
protected final int port;
protected final int sendPort;
protected DatagramSocket socket;
protected InetAddress broadCastAddress;
protected Thread serverThread;
protected int receiveBufferSize;
protected boolean isRunning;
// protected final List<ArtNetServerListener> listeners;
public ArtNetServer() {
this(DEFAULT_PORT, DEFAULT_PORT);
}
public ArtNetServer(int port, int sendPort) {
super(NodeStyle.ST_SERVER);
this.port = port;
this.sendPort = sendPort;
// this.listeners = new ArrayList<ArtNetServerListener>();
setBufferSize(2048);
}
// public void addListener(ArtNetServerListener l) {
// synchronized (listeners) {
// listeners.add(l);
// }
// }
//
// public void broadcastPacket(ArtNetPacket ap) {
// try {
// DatagramPacket packet = new DatagramPacket(ap.getData(), ap
// .getLength(), broadCastAddress, sendPort);
// socket.send(packet);
// for (ArtNetServerListener l : listeners) {
// l.artNetPacketBroadcasted(ap);
// }
// } catch (IOException e) {
// logger.warning(e.getMessage());
// }
// }
//
// public void removeListener(ArtNetServerListener l) {
// synchronized (listeners) {
// listeners.remove(l);
// }
// }
// @Override
// public void run() {
// byte[] receiveBuffer = new byte[receiveBufferSize];
// DatagramPacket receivedPacket = new DatagramPacket(receiveBuffer, receiveBuffer.length);
//
// try {
// while (isRunning) {
// socket.receive(receivedPacket);
// logger.finer("received new packet");
// ArtNetPacket packet = ArtNetPacketParser.parse(receivedPacket);
// if (packet != null) {
// if (packet.getType() == PacketType.ART_POLL) {
// sendArtPollReply(receivedPacket.getAddress(), (ArtPollPacket) packet);
// }
// for (ArtNetServerListener l : listeners) {
// l.artNetPacketReceived(packet);
// }
// }
// }
// socket.close();
// logger.info("server thread terminated.");
// for (ArtNetServerListener l : listeners) {
// l.artNetServerStopped(this);
// }
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
// private void sendArtPollReply(InetAddress inetAddress, ArtPollPacket packet) {
// // TODO send reply with self description
// }
public void setBroadcastAddress(String address) {
try {
broadCastAddress = InetAddress.getByName(address);
logger.fine("broadcast IP set to: " + broadCastAddress);
} catch (UnknownHostException e) {
logger.log(Level.WARNING, e.getMessage(), e);
}
}
private void setBufferSize(int size) {
if (!isRunning) {
receiveBufferSize = size;
}
}
public void start() throws SocketException, ArtNetException {
if (broadCastAddress == null) {
setBroadcastAddress(DEFAULT_BROADCAST_IP);
}
if (socket == null) {
// socket = new DatagramSocket(port);
socket = new DatagramSocket();
// logger.info("Art-Net server started at port: " + port);
// for (ArtNetServerListener l : listeners) {
// l.artNetServerStarted(this);
// }
isRunning = true;
// serverThread = new Thread(this);
// serverThread.start();
} else {
throw new ArtNetException(
"Couldn't create server socket, server already running?");
}
}
public void stop() {
isRunning = false;
}
/**
* Sends the given packet to the specified IP address.
*
* @param ap
* @param targetAdress
*/
public void unicastPacket(ArtNetPacket ap, InetAddress targetAdress) {
try {
DatagramPacket packet = new DatagramPacket(ap.getData(), ap.getLength(), targetAdress, sendPort);
socket.send(packet);
logger.finer("sent packet to: " + targetAdress);
// for (ArtNetServerListener l : listeners) {
// l.artNetPacketUnicasted(ap);
// }
} catch (IOException e) {
logger.warning(e.getMessage());
}
}
}

View File

@ -1,122 +0,0 @@
/*
* This file is part of artnet4j.
*
* Copyright 2009 Karsten Schmidt (PostSpectacular Ltd.)
*
* artnet4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* artnet4j is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with artnet4j. If not, see <http://www.gnu.org/licenses/>.
*/
package artnet4j;
import artnet4j.packets.ArtDmxPacket;
public class DmxUniverse {
protected final DmxUniverseConfig config;
protected final byte[] frameData;
protected ArtNetNode node;
protected boolean isEnabled = true;
protected boolean isActive = true;
public DmxUniverse(ArtNetNode node, DmxUniverseConfig config) {
this.node = node;
this.config = config;
frameData = new byte[0x200];
}
public DmxUniverseConfig getConfig() {
return config;
}
public String getID() {
return config.id;
}
public ArtNetNode getNode() {
return node;
}
public int getNumChannels() {
return config.numDmxChannels;
}
public ArtDmxPacket getPacket(int sequenceID) {
ArtDmxPacket packet = new ArtDmxPacket();
packet.setSequenceID(sequenceID);
packet.setUniverse(node.getSubNet(), config.universeID);
// FIXME Art-Lynx OP has firmware issue with packet lengths < 512
// channels
// packet.setDMX(frameData, config.numDmxChannels);
packet.setDMX(frameData, config.ignoreNumChannels
? 0x200
: config.numDmxChannels);
return packet;
}
/**
* @return the isActive
*/
public boolean isActive() {
return isActive;
}
/**
* @return the isEnabled
*/
public boolean isEnabled() {
return isEnabled;
}
/**
* @param isActive
* the isActive to sunsetTime
*/
public void setActive(boolean isActive) {
this.isActive = isActive;
}
public void setChannel(int offset, int val) {
frameData[offset] = (byte) val;
}
/**
* @param isEnabled
* the isEnabled to sunsetTime
*/
public void setEnabled(boolean isEnabled) {
this.isEnabled = isEnabled;
}
/**
* @param node
* the node to sunsetTime
*/
public void setNode(ArtNetNode node) {
this.node = node;
}
public void setRGBPixel(int offset, int col) {
offset *= 3;
frameData[offset] = (byte) (col >> 16 & 0xff);
frameData[offset + 1] = (byte) (col >> 8 & 0xff);
frameData[offset + 2] = (byte) (col & 0xff);
}
@Override
public String toString() {
return node.getIPAddress() + "u: " + config.universeID + " st: "
+ isEnabled + "/" + isActive + " c: " + config.numDmxChannels;
}
}

View File

@ -1,53 +0,0 @@
/*
* This file is part of artnet4j.
*
* Copyright 2009 Karsten Schmidt (PostSpectacular Ltd.)
*
* artnet4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* artnet4j is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with artnet4j. If not, see <http://www.gnu.org/licenses/>.
*/
package artnet4j;
import java.net.InetAddress;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
public class DmxUniverseConfig {
@XmlAttribute
public String id;
@XmlAttribute
@XmlJavaTypeAdapter(InetAddressAdapter.class)
public InetAddress ip;
@XmlAttribute(name = "universe")
public int universeID;
@XmlAttribute(name = "numchannels")
public int numDmxChannels;
@XmlAttribute(name = "port")
public int serverPort;
@XmlAttribute
public boolean ignoreNumChannels;
@Override
public String toString() {
return "nodeConfig: id=" + id + ", ip=" + ip + ", uid=" + universeID
+ ", nc=" + numDmxChannels;
}
}

View File

@ -1,38 +0,0 @@
/*
* This file is part of artnet4j.
*
* Copyright 2009 Karsten Schmidt (PostSpectacular Ltd.)
*
* artnet4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* artnet4j is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with artnet4j. If not, see <http://www.gnu.org/licenses/>.
*/
package artnet4j;
import java.net.InetAddress;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class InetAddressAdapter extends XmlAdapter<String, InetAddress> {
@Override
public String marshal(InetAddress adr) throws Exception {
return adr.getHostAddress();
}
@Override
public InetAddress unmarshal(String adr) throws Exception {
return InetAddress.getByName(adr);
}
}

View File

@ -1,78 +0,0 @@
/*
* This file is part of artnet4j.
*
* Copyright 2009 Karsten Schmidt (PostSpectacular Ltd.)
*
* artnet4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* artnet4j is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with artnet4j. If not, see <http://www.gnu.org/licenses/>.
*/
package artnet4j;
public enum NodeReportCode {
RcDebug("#0000", "Booted in debug mode"),
RcPowerOk("#0001", "Power On Tests successful"),
RcPowerFail("#0002", "Hardware tests failed at Power On"),
RcSocketWr1(
"#0003",
"Last UDP from Node failed due to truncated length. Most likely caused by a collision."),
RcParseFail("#0004",
"Unable to identify last UDP transmission. Check OpCode and packet length."),
RcUdpFail("#0005", "Unable to open Udp Socket in last transmission attempt"),
RcShNameOk("#0006",
"Confirms that Short Name programming via ArtAddress, was successful."),
RcLoNameOk("#0007",
"Confirms that Long Name programming via ArtAddress, was successful."),
RcDmxError("#0008", "DMX512 receive errors detected."), RcDmxUdpFull(
"#0009", "Ran out of internal DMX transmit buffers."), RcDmxRxFull(
"#000a", "Ran out of internal DMX Rx buffers."), RcSwitchErr(
"#000b", "Rx Universe switches conflict."), RcConfigErr("#000c",
"Product configuration does not match firmware."), RcDmxShort(
"#000d", "DMX output short detected. See GoodOutput field."),
RcFirmwareFail("#000e", "Last attempt to upload new firmware failed."),
RcUserFail("#000f",
"User changed switch settings when address locked by remote.");
public static NodeReportCode getForID(String id) {
NodeReportCode code = null;
for (NodeReportCode c : values()) {
if (c.id.equalsIgnoreCase(id)) {
code = c;
break;
}
}
return code;
}
private final String id;
private final String description;
private NodeReportCode(String id, String desc) {
this.id = id;
this.description = desc;
}
/**
* @return the description
*/
public String getDescription() {
return description;
}
/**
* @return the id
*/
public String getID() {
return id;
}
}

View File

@ -1,50 +0,0 @@
/*
* This file is part of artnet4j.
*
* Copyright 2009 Karsten Schmidt (PostSpectacular Ltd.)
*
* artnet4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* artnet4j is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with artnet4j. If not, see <http://www.gnu.org/licenses/>.
*/
package artnet4j;
public enum NodeStyle {
ST_NODE(0, ArtNetNode.class), ST_SERVER(1, ArtNetServer.class), ST_MEDIA(2,
ArtNetNode.class), ST_ROUTER(3, ArtNetNode.class), ST_BACKUP(4,
ArtNetNode.class), ST_CONFIG(5, ArtNetNode.class);
private int id;
private Class<? extends ArtNetNode> nodeClass;
private NodeStyle(int id, Class<? extends ArtNetNode> nodeClass) {
this.id = id;
this.nodeClass = nodeClass;
}
public ArtNetNode createNode() {
ArtNetNode node = null;
try {
node = nodeClass.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return node;
}
public int getStyleID() {
return id;
}
}

View File

@ -1,65 +0,0 @@
/*
* This file is part of artnet4j.
*
* Copyright 2009 Karsten Schmidt (PostSpectacular Ltd.)
*
* artnet4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* artnet4j is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with artnet4j. If not, see <http://www.gnu.org/licenses/>.
*/
package artnet4j;
public class PortDescriptor {
protected boolean canOutput;
protected boolean canInput;
protected PortType type;
public PortDescriptor(int id) {
canOutput = (id & 0x80) > 0;
canInput = (id & 0x40) > 0;
id &= 0x3f;
for (PortType t : PortType.values()) {
if (id == t.getPortID()) {
type = t;
}
}
}
/**
* @return the canInput
*/
public boolean canInput() {
return canInput;
}
/**
* @return the canOutput
*/
public boolean canOutput() {
return canOutput;
}
/**
* @return the type
*/
public PortType getType() {
return type;
}
@Override
public String toString() {
return "PortDescriptor: " + type + " out: " + canOutput + " in: "
+ canInput;
}
}

View File

@ -1,33 +0,0 @@
/*
* This file is part of artnet4j.
*
* Copyright 2009 Karsten Schmidt (PostSpectacular Ltd.)
*
* artnet4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* artnet4j is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with artnet4j. If not, see <http://www.gnu.org/licenses/>.
*/
package artnet4j;
public enum PortType {
DMX512(0), MIDI(1), AVAB(2), COLORTRAN(3), ADB62_5(4), ARTNET(5);
private int id;
private PortType(int id) {
this.id = id;
}
public int getPortID() {
return id;
}
}

View File

@ -1,47 +0,0 @@
/*
* This file is part of artnet4j.
*
* Copyright 2009 Karsten Schmidt (PostSpectacular Ltd.)
*
* artnet4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* artnet4j is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with artnet4j. If not, see <http://www.gnu.org/licenses/>.
*/
package artnet4j.events;
import java.util.List;
import artnet4j.ArtNetNode;
public class ArtNetDiscoveryEventAdapter implements ArtNetDiscoveryListener {
@Override
public void discoveredNewNode(ArtNetNode node) {
}
@Override
public void discoveredNodeDisconnected(ArtNetNode node) {
}
@Override
public void discoveryCompleted(List<ArtNetNode> nodes) {
}
@Override
public void discoveryFailed(Throwable t) {
}
}

View File

@ -1,35 +0,0 @@
/*
* This file is part of artnet4j.
*
* Copyright 2009 Karsten Schmidt (PostSpectacular Ltd.)
*
* artnet4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* artnet4j is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with artnet4j. If not, see <http://www.gnu.org/licenses/>.
*/
package artnet4j.events;
import java.util.List;
import artnet4j.ArtNetNode;
public interface ArtNetDiscoveryListener {
void discoveredNewNode(ArtNetNode node);
void discoveredNodeDisconnected(ArtNetNode node);
void discoveryCompleted(List<ArtNetNode> nodes);
void discoveryFailed(Throwable t);
}

View File

@ -1,70 +0,0 @@
/*
* This file is part of artnet4j.
*
* Copyright 2009 Karsten Schmidt (PostSpectacular Ltd.)
*
* artnet4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* artnet4j is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with artnet4j. If not, see <http://www.gnu.org/licenses/>.
*/
package artnet4j.events;
/*
* This file is part of artnet4j.
*
* Copyright 2009 Karsten Schmidt (PostSpectacular Ltd.)
*
* artnet4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* artnet4j is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with artnet4j. If not, see <http://www.gnu.org/licenses/>.
*/
import artnet4j.ArtNetServer;
import artnet4j.packets.ArtNetPacket;
public class ArtNetServerEventAdapter implements ArtNetServerListener {
@Override
public void artNetPacketBroadcasted(ArtNetPacket packet) {
}
@Override
public void artNetPacketReceived(ArtNetPacket packet) {
}
@Override
public void artNetPacketUnicasted(ArtNetPacket packet) {
}
@Override
public void artNetServerStarted(ArtNetServer server) {
}
@Override
public void artNetServerStopped(ArtNetServer server) {
}
}

View File

@ -1,37 +0,0 @@
/*
* This file is part of artnet4j.
*
* Copyright 2009 Karsten Schmidt (PostSpectacular Ltd.)
*
* artnet4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* artnet4j is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with artnet4j. If not, see <http://www.gnu.org/licenses/>.
*/
package artnet4j.events;
import artnet4j.ArtNetServer;
import artnet4j.packets.ArtNetPacket;
public interface ArtNetServerListener {
void artNetPacketBroadcasted(ArtNetPacket packet);
void artNetPacketReceived(ArtNetPacket packet);
void artNetPacketUnicasted(ArtNetPacket packet);
void artNetServerStarted(ArtNetServer server);
void artNetServerStopped(ArtNetServer server);
}

View File

@ -1,69 +0,0 @@
/*
* This file is part of artnet4j.
*
* Copyright 2009 Karsten Schmidt (PostSpectacular Ltd.)
*
* artnet4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* artnet4j is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with artnet4j. If not, see <http://www.gnu.org/licenses/>.
*/
package artnet4j.packets;
import java.net.DatagramPacket;
import java.util.logging.Logger;
public class ArtNetPacketParser {
public static final Logger logger =
Logger.getLogger(ArtNetPacketParser.class.getClass().getName());
public static ArtNetPacket createPacketForOpCode(int opCode, byte[] data) {
logger.finer("creating packet instance for opcode: 0x"
+ ByteUtils.hex(opCode, 4));
ArtNetPacket packet = null;
for (PacketType type : PacketType.values()) {
if (opCode == type.getOpCode()) {
packet = type.createPacket();
if (packet != null) {
packet.parse(data);
break;
} else {
logger.fine("packet type valid, but not yet supported: "
+ type);
}
}
}
return packet;
}
private static ArtNetPacket parse(byte[] raw, int offset, int length) {
ArtNetPacket packet = null;
ByteUtils data = new ByteUtils(raw);
if (data.length > 10) {
if (data.compareBytes(ArtNetPacket.HEADER, 0, 8)) {
int opCode = data.getInt16LE(8);
packet = createPacketForOpCode(opCode, raw);
} else {
logger.warning("invalid header");
}
} else {
logger.warning("invalid packet length: " + data.length);
}
return packet;
}
public static ArtNetPacket parse(DatagramPacket receivedPacket) {
return parse(receivedPacket.getData(), receivedPacket.getOffset(),
receivedPacket.getLength());
}
}

View File

@ -1,65 +0,0 @@
/*
* This file is part of artnet4j.
*
* Copyright 2009 Karsten Schmidt (PostSpectacular Ltd.)
*
* artnet4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* artnet4j is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with artnet4j. If not, see <http://www.gnu.org/licenses/>.
*/
package artnet4j.packets;
public class ArtPollPacket extends ArtNetPacket {
private static int ARTPOLL_LENGTH = 14;
private boolean replyOnce;
private boolean replyDirect;
public ArtPollPacket() {
this(true, true);
}
public ArtPollPacket(boolean replyOnce, boolean replyDirect) {
super(PacketType.ART_POLL);
setData(new byte[ARTPOLL_LENGTH]);
setHeader();
setProtocol();
setTalkToMe(replyOnce, replyDirect);
}
@Override
public int getLength() {
return data.getLength();
}
@Override
public boolean parse(byte[] raw) {
setData(raw, ARTPOLL_LENGTH);
int talk = data.getInt8(12);
replyOnce = 0 == (talk & 0x02);
replyDirect = 1 == (talk & 0x01);
return true;
}
private void setTalkToMe(boolean replyOnce, boolean replyDirect) {
this.replyOnce = replyOnce;
this.replyDirect = replyDirect;
data.setInt8((replyOnce ? 0 : 2) | (replyDirect ? 1 : 0), 12);
}
@Override
public String toString() {
return type + ": reply once:" + replyOnce + " direct: " + replyDirect;
}
}

View File

@ -1,185 +0,0 @@
/*
* This file is part of artnet4j.
*
* Copyright 2009 Karsten Schmidt (PostSpectacular Ltd.)
*
* artnet4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* artnet4j is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with artnet4j. If not, see <http://www.gnu.org/licenses/>.
*/
package artnet4j.packets;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.logging.Level;
import artnet4j.NodeReportCode;
import artnet4j.NodeStyle;
import artnet4j.PortDescriptor;
public class ArtPollReplyPacket extends ArtNetPacket {
private InetAddress ip;
private int subSwitch;
private int oemCode;
private int nodeStatus;
private String shortName;
private String longName;
private int numPorts;
private PortDescriptor[] ports;
private NodeStyle nodeStyle;
private NodeReportCode reportCode;
private byte[] dmxIns;
private byte[] dmxOuts;
public ArtPollReplyPacket() {
super(PacketType.ART_POLL_REPLY);
}
public ArtPollReplyPacket(byte[] data) {
super(PacketType.ART_POLL_REPLY);
setData(data);
}
/**
* @return the dmxIns
*/
public byte[] getDmxIns() {
return dmxIns;
}
/**
* @return the dmxOuts
*/
public byte[] getDmxOuts() {
return dmxOuts;
}
/**
* @return the ip
*/
public InetAddress getIPAddress() {
InetAddress ipClone = null;
try {
ipClone = InetAddress.getByAddress(ip.getAddress());
} catch (UnknownHostException e) {
}
return ipClone;
}
public String getLongName() {
return longName;
}
public int getNodeStatus() {
return nodeStatus;
}
public NodeStyle getNodeStyle() {
return nodeStyle;
}
public int getOEMCode() {
return oemCode;
}
public PortDescriptor[] getPorts() {
return ports;
}
/**
* @return the reportCode
*/
public NodeReportCode getReportCode() {
return reportCode;
}
public String getShortName() {
return shortName;
}
public int getSubSwitch() {
return subSwitch;
}
@Override
public boolean parse(byte[] raw) {
setData(raw);
// System.out.println(data.toHex(256));
setIPAddress(data.getByteChunk(null, 10, 4));
subSwitch = data.getInt16(18);
oemCode = data.getInt16(20);
nodeStatus = data.getInt8(23);
shortName = new String(data.getByteChunk(null, 26, 17));
longName = new String(data.getByteChunk(null, 44, 64));
reportCode =
NodeReportCode.getForID(new String(data.getByteChunk(null, 108,
5)));
numPorts = data.getInt16(172);
ports = new PortDescriptor[numPorts];
for (int i = 0; i < numPorts; i++) {
ports[i] = new PortDescriptor(data.getInt8(174 + i));
}
dmxIns = data.getByteChunk(null, 186, 4);
dmxOuts = data.getByteChunk(null, 190, 4);
for (int i = 0; i < 4; i++) {
dmxIns[i] &= 0x0f;
dmxOuts[i] &= 0x0f;
}
int styleID = data.getInt8(200);
for (NodeStyle s : NodeStyle.values()) {
if (styleID == s.getStyleID()) {
nodeStyle = s;
}
}
return true;
}
/**
* @param dmxIns
* the dmxIns to set
*/
public void setDmxIns(byte[] dmxIns) {
this.dmxIns = dmxIns;
}
/**
* @param dmxOuts
* the dmxOuts to set
*/
public void setDmxOuts(byte[] dmxOuts) {
this.dmxOuts = dmxOuts;
}
private void setIPAddress(byte[] address) {
try {
ip = InetAddress.getByAddress(address);
logger.fine("setting ip address: " + ip);
} catch (UnknownHostException e) {
logger.log(Level.WARNING, e.getMessage(), e);
}
}
/**
* @param reportCode
* the reportCode to set
*/
public void setReportCode(NodeReportCode reportCode) {
this.reportCode = reportCode;
}
}

View File

@ -0,0 +1,10 @@
package de.ctdo.bunti.artnet;
import de.ctdo.bunti.artnet.packets.ArtNetPacket;
public interface ArtNetSocket {
public boolean unicastPacket(ArtNetPacket pack, String address);
}

View File

@ -0,0 +1,56 @@
package de.ctdo.bunti.artnet;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import de.ctdo.bunti.artnet.packets.ArtNetPacket;
@Component
public class ArtNetSocketImpl implements ArtNetSocket {
private final Logger logger = LoggerFactory.getLogger(getClass());
public static final int DEFAULT_PORT = 0x1936;
protected DatagramSocket socket;
public ArtNetSocketImpl() {
try {
socket = new DatagramSocket();
} catch (SocketException e) {
// TODO
logger.debug(e.getMessage(), e.getStackTrace());
}
}
@Override
public boolean unicastPacket(ArtNetPacket pack, String address) {
InetAddress targetAdress;
try {
targetAdress = InetAddress.getByName(address);
DatagramPacket packet = new DatagramPacket(pack.getData(), pack.getLength(), targetAdress, DEFAULT_PORT);
socket.send(packet);
//socket.send(packet, targetAdress);
} catch (UnknownHostException e) {
logger.error(e.getMessage(), e);
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
return false;
}
}

View File

@ -17,16 +17,17 @@
* along with artnet4j. If not, see <http://www.gnu.org/licenses/>. * along with artnet4j. If not, see <http://www.gnu.org/licenses/>.
*/ */
package artnet4j.packets; package de.ctdo.bunti.artnet;
public class ByteUtils { public class ByteUtils {
protected final byte[] data;
public final int length;
public ByteUtils(byte[] data) {
this.data = data;
this.length = data.length;
}
/**
* Converts the byte into an unsigned int.
*
* @param b
* @return
*/
public static final int byteToUint(byte b) { public static final int byteToUint(byte b) {
return (b < 0 ? 256 + b : b); return (b < 0 ? 256 + b : b);
} }
@ -43,15 +44,6 @@ public class ByteUtils {
return stuff; return stuff;
} }
protected final byte[] data;
public final int length;
public ByteUtils(byte[] data) {
this.data = data;
this.length = data.length;
}
public boolean compareBytes(byte[] other, int offset, int length) { public boolean compareBytes(byte[] other, int offset, int length) {
boolean isEqual = (offset + length) < data.length; boolean isEqual = (offset + length) < data.length;
for (int i = 0; i < length && isEqual; i++) { for (int i = 0; i < length && isEqual; i++) {

View File

@ -0,0 +1,9 @@
package de.ctdo.bunti.artnet;
import java.util.Map;
public interface SimpleArtNetSender {
public void sendDMXData(Map<Integer,Integer> dmxdata, String adr);
}

View File

@ -0,0 +1,41 @@
package de.ctdo.bunti.artnet;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import de.ctdo.bunti.artnet.packets.ArtDmxPacket;
@Component
public class SimpleArtNetSenderImpl implements SimpleArtNetSender {
private ArtNetSocket socket;
private int sequence = 0;
@Autowired
public void setSocket(ArtNetSocket socket) {
this.socket = socket;
}
@Override
public void sendDMXData(Map<Integer,Integer> dmxdata, String adr) {
ArtDmxPacket packet = new ArtDmxPacket();
int size = dmxdata.size();
byte[] arr = new byte[size];
for (int i = 0; i < dmxdata.size(); i++) {
arr[i] = (byte)(dmxdata.get(i) & 0xff);
}
packet.setSequenceID(sequence++);
packet.setDMX(arr, arr.length);
packet.setUniverse(0, 0);
socket.unicastPacket(packet, adr);
}
}

View File

@ -17,7 +17,8 @@
* along with artnet4j. If not, see <http://www.gnu.org/licenses/>. * along with artnet4j. If not, see <http://www.gnu.org/licenses/>.
*/ */
package artnet4j.packets; package de.ctdo.bunti.artnet.packets;
public class ArtDmxPacket extends ArtNetPacket { public class ArtDmxPacket extends ArtNetPacket {
@ -79,7 +80,6 @@ public class ArtDmxPacket extends ArtNetPacket {
} }
public void setDMX(byte[] dmxData, int numChannels) { public void setDMX(byte[] dmxData, int numChannels) {
logger.finer("setting DMX data for: " + numChannels + " channels");
this.numChannels = numChannels; this.numChannels = numChannels;
data.setByteChunk(dmxData, 18, numChannels); data.setByteChunk(dmxData, 18, numChannels);
data.setInt16((1 == numChannels % 2 ? numChannels + 1 : numChannels), data.setInt16((1 == numChannels % 2 ? numChannels + 1 : numChannels),
@ -111,9 +111,6 @@ public class ArtDmxPacket extends ArtNetPacket {
this.subnetID = subnetID & 0x0f; this.subnetID = subnetID & 0x0f;
this.universeID = universeID & 0x0f; this.universeID = universeID & 0x0f;
data.setInt16LE(subnetID << 4 | universeID, 14); data.setInt16LE(subnetID << 4 | universeID, 14);
logger.finer("universe ID set to: subnet: "
+ ByteUtils.hex(subnetID, 2) + "/"
+ ByteUtils.hex(universeID, 2));
} }
/** /**

View File

@ -1,35 +1,11 @@
/* package de.ctdo.bunti.artnet.packets;
* This file is part of artnet4j.
*
* Copyright 2009 Karsten Schmidt (PostSpectacular Ltd.)
*
* artnet4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* artnet4j is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with artnet4j. If not, see <http://www.gnu.org/licenses/>.
*/
package artnet4j.packets; import de.ctdo.bunti.artnet.ByteUtils;
import java.util.logging.Logger;
public abstract class ArtNetPacket { public abstract class ArtNetPacket {
public static final byte[] HEADER = "Art-Net\0".getBytes(); public static final byte[] HEADER = "Art-Net\0".getBytes();
public static final int PROTOCOL_VERSION = 14; public static final int PROTOCOL_VERSION = 14;
public static final Logger logger =
Logger.getLogger(ArtNetPacket.class.getClass().getName());
protected ByteUtils data; protected ByteUtils data;
protected final PacketType type; protected final PacketType type;
@ -37,27 +13,14 @@ public abstract class ArtNetPacket {
this.type = type; this.type = type;
} }
/**
* @return the data
*/
public byte[] getData() { public byte[] getData() {
return data.getBytes(); return data.getBytes();
} }
/**
* Returns the actually used length of the data buffer.
*
* @return
*/
public int getLength() { public int getLength() {
return data.length; return data.length;
} }
/**
* Returns the type of this packet.
*
* @return the type
*/
public PacketType getType() { public PacketType getType() {
return type; return type;
} }
@ -90,10 +53,6 @@ public abstract class ArtNetPacket {
setData(raw); setData(raw);
} }
/**
* Sets the header bytes of the packet consisting of {@link #HEADER} and the
* type's OpCode.
*/
protected void setHeader() { protected void setHeader() {
data.setByteChunk(HEADER, 0, 8); data.setByteChunk(HEADER, 0, 8);
data.setInt16LE(type.getOpCode(), 8); data.setInt16LE(type.getOpCode(), 8);
@ -107,4 +66,5 @@ public abstract class ArtNetPacket {
public String toString() { public String toString() {
return data.toHex(getLength()); return data.toHex(getLength());
} }
} }

View File

@ -17,21 +17,33 @@
* along with artnet4j. If not, see <http://www.gnu.org/licenses/>. * along with artnet4j. If not, see <http://www.gnu.org/licenses/>.
*/ */
package artnet4j.packets; package de.ctdo.bunti.artnet.packets;
public enum PacketType { public enum PacketType {
ART_POLL(0x2000, ArtPollPacket.class), ART_POLL_REPLY(0x2100, ART_POLL(0x2000, null),
ArtPollReplyPacket.class), ART_OUTPUT(0x5000, null), ART_ADDRESS( ART_POLL_REPLY(0x2100, null),
0x6000, null), ART_INPUT(0x7000, null), ART_TOD_REQUEST(0x8000, ART_OUTPUT(0x5000, null),
null), ART_TOD_DATA(0x8100, null), ART_TOD_CONTROL(0x8200, null), ART_ADDRESS(0x6000, null),
ART_RDM(0x8300, null), ART_RDMSUB(0x8400, null), ART_MEDIA(0x9000, null), ART_INPUT(0x7000, null),
ART_MEDIA_PATCH(0x9100, null), ART_MEDIA_CONTROL(0x9200, null), ART_TOD_REQUEST(0x8000, null),
ART_MEDIA_CONTROL_REPLY(0x9300, null), ART_VIDEO_SETUP(0xa010, null), ART_TOD_DATA(0x8100, null),
ART_VIDEO_PALETTE(0xa020, null), ART_VIDEO_DATA(0xa040, null), ART_TOD_CONTROL(0x8200, null),
ART_MAC_MASTER(0xf000, null), ART_MAC_SLAVE(0xf100, null), ART_RDM(0x8300, null),
ART_FIRMWARE_MASTER(0xf200, null), ART_FIRMWARE_REPLY(0xf300, null), ART_RDMSUB(0x8400, null),
ART_IP_PROG(0xf800, null), ART_IP_PROG_REPLY(0xf900, null); ART_MEDIA(0x9000, null),
ART_MEDIA_PATCH(0x9100, null),
ART_MEDIA_CONTROL(0x9200, null),
ART_MEDIA_CONTROL_REPLY(0x9300, null),
ART_VIDEO_SETUP(0xa010, null),
ART_VIDEO_PALETTE(0xa020, null),
ART_VIDEO_DATA(0xa040, null),
ART_MAC_MASTER(0xf000, null),
ART_MAC_SLAVE(0xf100, null),
ART_FIRMWARE_MASTER(0xf200, null),
ART_FIRMWARE_REPLY(0xf300, null),
ART_IP_PROG(0xf800, null),
ART_IP_PROG_REPLY(0xf900, null);
private final int opCode; private final int opCode;
private final Class<? extends ArtNetPacket> packetClass; private final Class<? extends ArtNetPacket> packetClass;

View File

@ -1,11 +1,8 @@
package de.ctdo.bunti.devices; package de.ctdo.bunti.devices;
import org.springframework.context.ApplicationListener;
import de.ctdo.bunti.DeviceChangedEvent; public interface DeviceMixer {
public interface DeviceMixer extends ApplicationListener<DeviceChangedEvent> {

View File

@ -1,7 +1,6 @@
package de.ctdo.bunti.devices; package de.ctdo.bunti.devices;
import org.slf4j.Logger; import org.springframework.context.ApplicationListener;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import de.ctdo.bunti.DeviceChangedEvent; import de.ctdo.bunti.DeviceChangedEvent;
@ -9,8 +8,7 @@ import de.ctdo.bunti.model.BuntiSwitchingDevice;
@Component @Component
public class DeviceMixerImpl implements DeviceMixer { public class DeviceMixerImpl implements DeviceMixer, ApplicationListener<DeviceChangedEvent> {
private Logger logger = LoggerFactory.getLogger(getClass());
@Override @Override
public void onApplicationEvent(DeviceChangedEvent arg0) { public void onApplicationEvent(DeviceChangedEvent arg0) {

View File

@ -0,0 +1 @@
/DMXMixer.java

View File

@ -1,10 +1,10 @@
package de.ctdo.bunti.dmx; package de.ctdo.bunti.dmx;
public class DMXChannel { public class DMXChannel {
int offset; private int offset;
String name; private String name;
int value; private int value;
long lastChangedTimestamp = 0; private long lastChangedTimestamp = 0;
public DMXChannel(int offset, String name) { public DMXChannel(int offset, String name) {
this.name = name; this.name = name;

View File

@ -6,8 +6,10 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
* DMXChannel container * DMXChannel container
*/ * @author jCoder
*
*/
public class DMXChannels { public class DMXChannels {
protected Map<Integer,DMXChannel> channelByNumber = new HashMap<Integer, DMXChannel>(); protected Map<Integer,DMXChannel> channelByNumber = new HashMap<Integer, DMXChannel>();
@ -33,14 +35,14 @@ public class DMXChannels {
return this.channelByName.get(name); return this.channelByName.get(name);
} }
// /** /**
// * Returns a channel by offset * Returns a channel by offset
// * @param number number * @param number number
// * @return channel or null if not found * @return channel or null if not found
// */ */
// public DMXChannel getChannelByNumber(int number) { public DMXChannel getChannelByNumber(int number) {
// return this.channelByNumber.get(number); return this.channelByNumber.get(number);
// } }
/** /**
* Adds a channel * Adds a channel
@ -53,50 +55,51 @@ public class DMXChannels {
return false; return false;
} }
// description cannot be null // description cannot be null
if (channel.name == null) { if (channel.getName() == null) {
return false; return false;
} }
// entry must not exist by offset // entry must not exist by offset
if (this.channelByNumber.containsKey(channel.offset) == true) { if (this.channelByNumber.containsKey(channel.getOffset()) == true) {
return false; return false;
} }
// entry must not exist by name // entry must not exist by name
if (this.channelByName.containsKey(channel.name) == true) { if (this.channelByName.containsKey(channel.getName()) == true) {
return false; return false;
} }
this.channelByNumber.put(channel.offset, channel); this.channelByNumber.put(channel.getOffset(), channel);
this.channelByName.put(channel.name, channel); this.channelByName.put(channel.getName(), channel);
return true; return true;
} }
// /** /**
// * Removes a channel by offset * Removes a channel by offset
// * @param offset offset * @param offset offset
// * @return removed channel or null if it does not exist * @return removed channel or null if it does not exist
// */ */
// public DMXChannel removeChannel(int offset) { public DMXChannel removeChannel(int offset) {
// DMXChannel tmpChannel = this.channelByNumber.remove(offset); DMXChannel tmpChannel = this.channelByNumber.remove(offset);
// if (tmpChannel != null) { if (tmpChannel != null) {
// this.channelByName.remove(tmpChannel.name); this.channelByName.remove(tmpChannel.getName());
// } }
// return tmpChannel; return tmpChannel;
// } }
/**
* Removes a channel by name
* @param name channel name
* @return removed channel or null if it does not exist
*/
public DMXChannel removeChannel(String name) {
if (name == null) {
return null;
}
DMXChannel tmpChannel = this.channelByName.remove(name);
if (tmpChannel != null) {
this.channelByNumber.remove(tmpChannel.getOffset());
}
return tmpChannel;
}
// /**
// * Removes a channel by name
// * @param name channel name
// * @return removed channel or null if it does not exist
// */
// public DMXChannel removeChannel(String name) {
// if (name == null) {
// return null;
// }
// DMXChannel tmpChannel = this.channelByName.remove(name);
// if (tmpChannel != null) {
// this.channelByOffset.remove(tmpChannel.offset);
// }
// return tmpChannel;
// }
/** /**
* Returns an (unmodifiable) collection of all channels * Returns an (unmodifiable) collection of all channels

View File

@ -1,112 +1,84 @@
package de.ctdo.bunti.dmx; package de.ctdo.bunti.dmx;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import de.ctdo.bunti.DeviceChangedEvent; import de.ctdo.bunti.DeviceChangedEvent;
import de.ctdo.bunti.dao.BuntiDevicesDAO; import de.ctdo.bunti.artnet.SimpleArtNetSender;
import de.ctdo.bunti.model.*; import de.ctdo.bunti.model.*;
@Component @Component
public class DMXMixerImpl implements DMXMixer { public class DMXMixerImpl implements DMXMixer, ApplicationListener<DeviceChangedEvent> {
private Logger logger = LoggerFactory.getLogger(getClass()); private final Logger logger = LoggerFactory.getLogger(getClass());
private final int TICKS_BETWEEN_DMX_SEND = 20; private final String ARTNET_DEVICE_ADDRESS = "192.168.0.90";
// private final String ARTNET_DEVICE_ADDRESS = "192.168.0.90";
private int[] dmx512databuffer = new int[DMX.DMX_CHANNELS_MAX+1]; private final Map<Integer,Integer> dmxMap = Collections.synchronizedMap(new HashMap<Integer,Integer>());
private SimpleArtNetSender artNetSender;
// private int sequenceID = 0; private boolean hasDataChanged = true;
private long ticksLastBufferFlush = 0;
private BuntiDevicesDAO devicesDAO;
@Autowired @Autowired
public void setDevicesDAO(BuntiDevicesDAO devicesDAO) { public void setArtNetSender(SimpleArtNetSender artNetSender) {
this.devicesDAO = devicesDAO; this.artNetSender = artNetSender;
} }
public void initDMXData() {
private void sendOutDMXBuffer() { for (int i = 0; i <= DMX.DMX_CHANNELS_MAX; i++) {
dmxMap.put(i, 0);
StringBuilder sb = new StringBuilder();
sb.append("DMX Data: ");
byte[] arr = new byte[dmx512databuffer.length];
for (int i = 0; i < dmx512databuffer.length; i++) {
arr[i] = (byte)dmx512databuffer[i];
sb.append(i);
sb.append("=");
sb.append(dmx512databuffer[i]);
sb.append(", ");
}
// sequenceID++;
logger.debug(sb.toString());
}
public void sendOutDMXBufferIfNeeded() {
if(ticksLastBufferFlush + TICKS_BETWEEN_DMX_SEND < System.currentTimeMillis()) {
// mergeDevicesIntoBuffer();
sendOutDMXBuffer();
ticksLastBufferFlush = System.currentTimeMillis();
} }
} }
private void updateDevice(BuntiDevice device, Map<String, Object> options) { @Scheduled(fixedDelay=100) //TODO aendern auf 10ms
public void sendOutDMXBuffer() {
if( dmxMap.size() == 0) initDMXData();
if( hasDataChanged ) {
artNetSender.sendDMXData(dmxMap, ARTNET_DEVICE_ADDRESS);
hasDataChanged = false;
}
// logger.debug(sb.toString());
}
public void updateDevice(BuntiDevice device, Map<String, Object> options) {
BuntiDMXDevice dmxDev = (BuntiDMXDevice)device; BuntiDMXDevice dmxDev = (BuntiDMXDevice)device;
boolean retval = dmxDev.setValuesFromOptions(options);
if( retval ) { if( dmxDev.setValuesFromOptions(options) ) {
dmxDev.mergeDMXData(dmx512databuffer);
sendOutDMXBufferIfNeeded(); dmxMap.putAll(dmxDev.getChannelData());
logger.info("setValuesFromOptions on " + device);
} else { } else {
logger.error("setValuesFromOptions failed"); logger.info("setValuesFromOptions on " + device + " failed");
} }
} }
// private void mergeDevicesIntoBuffer() { @Override
// public void setDMX512Channel(int channel, int value) {
// for (BuntiDMXDevice buntiDMXDevice : devicesDAO.getAllDMXDevices()) { if(!DMX.checkChannelBoundaries(channel)) return;
// long lastchanged = buntiDMXDevice.getLastChanged(); value = DMX.sanitizeDMXValue(value);
// long lastSend = buntiDMXDevice.getLastSendOut();
//
// if (lastchanged >= lastSend) {
// buntiDMXDevice.mergeDMXData(dmx512databuffer);
// logger.debug("merged " + buntiDMXDevice + " into dmx buffer");
// buntiDMXDevice.setSendOutNow();
// }
//
// }
// }
// public void setDMX512Channel(int channel, int value) { dmxMap.put(channel, value);
// if(!DMX.checkChannelBoundaries(channel)) return; hasDataChanged = true;
// value = DMX.sanitizeDMXValue(value); }
//
// dmx512databuffer[channel] = value;
//
// sendOutDMXBufferIfNeeded();
// }
@Override @Override
public void onApplicationEvent(DeviceChangedEvent arg0) { public void onApplicationEvent(DeviceChangedEvent arg0) {
if( arg0.getDevice() instanceof BuntiDMXDevice) { if( arg0.getDevice() instanceof BuntiDMXDevice) {
updateDevice(arg0.getDevice(), arg0.getOptions()); updateDevice(arg0.getDevice(), arg0.getOptions());
hasDataChanged = true;
// TODO: hier kann man z.B. auch noch direkt einmal die DMX Daten zu verschicken veranlassen
} }
} }
} }

View File

@ -1,5 +1,6 @@
package de.ctdo.bunti.model; package de.ctdo.bunti.model;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
@ -58,28 +59,66 @@ public abstract class BuntiDMXDevice extends BuntiDevice {
return 0; return 0;
} }
/** // /**
* Merge the DMX values from this device into a global DMX512 Data Array. // * Merge the DMX values from this device into a global DMX512 Data Array.
* @param dmxData DMX512 Data array to merge the local values into. // * @param dmxData DMX512 Data array to merge the local values into.
* @return True on success, false otherwise. // * @return True on success, false otherwise.
*/ // */
public boolean mergeDMXData(int[] dmxData) { // public boolean mergeDMXData(int[] dmxData) {
//
// if(dmxData == null || dmxData.length == 0) {
// return false;
// }
//
// for (DMXChannel channel : dmxChannels.getAllChannels()) {
// int index = channel.getOffset() + (startAddress - DMX.DMX_STARTADDRESS_OFFSET);
//
// if(index >= 0 && index < dmxData.length){
// dmxData[index] = channel.getValue();
// } else {
// return false;
// }
// }
//
// return true;
// }
//
// /**
// * Merge the DMX values from this device into a global DMX512 Data Array.
// * @param dmxData DMX512 Data to merge the local values into.
// * @return True on success, false otherwise.
// */
// public boolean mergeDMXData(Map<Integer,Integer> dmxData) {
//
// if(dmxData == null || dmxData.size() == 0) {
// return false;
// }
//
// for (DMXChannel channel : dmxChannels.getAllChannels()) {
// int index = channel.getOffset() + (startAddress - DMX.DMX_STARTADDRESS_OFFSET);
//
// if(index >= DMX.DMX_CHANNELS_MIN && index <= DMX.DMX_CHANNELS_MAX){
// dmxData.put(index, channel.getValue());
// } else {
// return false;
// }
// }
//
// return true;
// }
if(dmxData == null || dmxData.length == 0) { public Map<Integer,Integer> getChannelData() {
return false; Map<Integer,Integer> map = new HashMap<Integer, Integer>();
}
for (DMXChannel channel : dmxChannels.getAllChannels()) { for (DMXChannel channel : dmxChannels.getAllChannels()) {
int index = channel.getOffset() + (startAddress - DMX.DMX_STARTADDRESS_OFFSET); int index = channel.getOffset() + (startAddress - DMX.DMX_STARTADDRESS_OFFSET);
if(index >= 0 && index < dmxData.length){ if(index >= DMX.DMX_CHANNELS_MIN && index <= DMX.DMX_CHANNELS_MAX){
dmxData[index] = channel.getValue(); map.put(index, channel.getValue());
} else {
return false;
} }
} }
return true; return map;
} }
@Override @Override

View File

@ -1,10 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" <beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:context="http://www.springframework.org/schema/context"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation=" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd">
<context:component-scan base-package="de.ctdo" /> <context:component-scan base-package="de.ctdo" />
@ -12,4 +14,9 @@
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" /> <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" /> <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />
<task:annotation-driven scheduler="myScheduler"/>
<!--<task:annotation-driven executor="myExecutor" scheduler="myScheduler"/>
<task:executor id="myExecutor" pool-size="3-10" queue-capacity="50" />-->
<task:scheduler id="myScheduler" pool-size="3"/>
</beans> </beans>