通过 UDP 连接的交互式处理草图
Interactive Processing Sketch over UDP Connection
我正在编写交互式处理草图,通过 UDP 向 Ether Dream DAC 发送消息来控制激光投影仪。我已经成功地编译并 运行 了 Ether Dream creator 提供的 C 驱动程序,并研究了这里描述的协议:
http://ether-dream.com/protocol.html
我正在使用 UDP 库进行处理,并已成功读取 DAC 每秒广播的状态信号。我似乎可以使用 udp.send() 将消息(在 DAC 协议中描述)发送到 DAC,但我永远不会收到确认消息。
udp = new UDP( this ,7654 );
当我以这种方式初始化我的 UDP 对象时,我能够读取广播消息。
但是...
udp = new UDP( this ,7654, "192.169.0.101");
当我以这种方式初始化它时,我收到消息:
opening socket failed!
> address:192.169.0.101, port:7654 [group:null]
> Can't assign requested address
我也试过7765端口,文档中也有提到,结果一样。
UDP 库打开这样的连接:
// open a new socket to the specified port/address
// and join the group if the multicast socket is required
try {
InetAddress addr = InetAddress.getByName(ip);
InetAddress host = (ip==null) ? (InetAddress)null: addr;
if ( !addr.isMulticastAddress() ) {
ucSocket = new DatagramSocket( port, host ); // as broadcast
log( "bound socket to host:"+address()+", port: "+port() );
}
Ether Dream 创建者提供的 C 驱动程序 运行s 成功打开了与 DAC 的连接,如下所示:
struct etherdream_conn *conn = &d->conn;
memset(conn, 0, sizeof *conn);
// Open socket
conn->dc_sock = socket(AF_INET, SOCK_STREAM, 0);
if (conn->dc_sock < 0) {
log_socket_error(d, "socket");
return -1;
}
unsigned long nonblocking = 1;
ioctl(conn->dc_sock, FIONBIO, &nonblocking);
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_addr.s_addr = d->addr.s_addr, .sin_port = htons(7765)
};
// Because the socket is nonblocking, this will always error...
connect(conn->dc_sock, (struct sockaddr *)&addr, (int)sizeof addr);
if (errno != EINPROGRESS) {
log_socket_error(d, "connect");
goto bail;
}
可以找到此驱动程序的存储库 here。
为什么我无法通过Processing UDP库与DAC建立连接?我如何修改UDP库的Java实现,使其匹配可以成功连接到DAC的C驱动程序?
只有来自 DAC 的广播发生在 7654 上的 UDP 上
与 DAC 的通信通过端口 7765 上的 TCP 进行
以下java代码将使用UDP定位Etherdream DAC并与Etherdream DAC建立TCP通信。
import java.io.*;
import java.net.*;
import java.util.Arrays;
public class Etherdream implements Runnable {
public Etherdream() {
Thread thread = new Thread(this);
thread.start();
}
public static void main(String[] args) {
Etherdream laser = new Etherdream();
while (true) {
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
enum State {
GET_BROADCAST, KEEP_ALIVE_PING,
}
// https://ether-dream.com/protocol.html
enum Command {
PREPARE_STREAM(0x70), BEGIN_PLAYBACK(0x62), QUEUE_RATE_CHANGE(0x74), WRITE_DATA(0x64), STOP(0x73),
EMERGENCY_STOP(0x00), EMERGENCY_STOP_ALTERNATIVE(0xFF), CLEAR_EMERGENCY_STOP(0x63), PING(0x3F),
ACK_RESPONSE(0x61), NAK_FULL_RESPONSE(0x46), NAK_INVALID_RESPONSE(0x49), NAK_STOPCONDITION_RESPONSE(0x21);
final byte command;
private Command(int cmd) {
command = (byte) (cmd & 0xFF);
}
public byte[] bytes() {
return new byte[] { command };
}
public byte getCommand() {
return command;
}
}
enum DACStatus {
IDLE(0x01), PREPARED(0x02), PLAYING(0x03);
final byte status;
private DACStatus(int cmd) {
status = (byte) (cmd & 0xFF);
}
public byte[] bytes() {
return new byte[] { status };
}
}
@Override
public void run() {
State state = State.GET_BROADCAST;
InetAddress etherdreamAddress = null;
while (true) {
System.out.println("state " + state);
try {
switch (state) {
case GET_BROADCAST: {
// Wait and get broadcast using UDP
try (DatagramSocket inSocket = new DatagramSocket(7654)) {
// get broadcast
byte[] buffer = new byte[512];
DatagramPacket response = new DatagramPacket(buffer, buffer.length);
inSocket.receive(response);
/*
* struct j4cDAC_broadcast {
* uint8_t mac_address[6];
* uint16_t hw_revision;
* uint16_t sw_revision;
* uint16_t buffer_capacity;
* uint32_t max_point_rate;
* struct dac_status status;
* };
*/
byte[] broadcast = Arrays.copyOfRange(buffer, 0, response.getLength());
etherdreamAddress = response.getAddress();
if (etherdreamAddress != null) {
state = State.KEEP_ALIVE_PING;
}
}
break;
}
case KEEP_ALIVE_PING: {
// Send ping using TCP port 7765
try (Socket socket = new Socket(etherdreamAddress, 7765)) {
Thread.sleep(500);
Command cmd = Command.PING;
OutputStream output = socket.getOutputStream();
InputStream input = socket.getInputStream();
output.write(cmd.bytes());
/*
* struct dac_response {
* uint8_t response;
* uint8_t command;
* struct status dac_status;
* };
*/
byte[] dac_response = input.readNBytes(3);
// make sure we got an ACK
if(dac_response[0]!=Command.ACK_RESPONSE.command){
state = State.GET_BROADCAST;
break;
}
// make sure we got the response form the PING command
if(dac_response[1]!=cmd.command){
state = State.GET_BROADCAST;
break;
}
/*
* struct dac_status {
* uint8_t protocol;
* uint8_t light_engine_state;
* uint8_t playback_state;
* uint8_t source;
* uint16_t light_engine_flags;
* uint16_t playback_flags;
* uint16_t source_flags;
* uint16_t buffer_fullness;
* uint32_t point_rate;
* uint32_t point_count;
* };
*/
byte[] dac_status = input.readNBytes(20);
}
break;
}
default:
state = State.GET_BROADCAST;
}
} catch (Exception e) {
/* If any IO error occour
* for any reason such as
* network cable disconnect
* then try locate the Etherdream DAC again
*/
state = State.GET_BROADCAST;
System.out.println(e);
}
}
}
}
我正在编写交互式处理草图,通过 UDP 向 Ether Dream DAC 发送消息来控制激光投影仪。我已经成功地编译并 运行 了 Ether Dream creator 提供的 C 驱动程序,并研究了这里描述的协议: http://ether-dream.com/protocol.html
我正在使用 UDP 库进行处理,并已成功读取 DAC 每秒广播的状态信号。我似乎可以使用 udp.send() 将消息(在 DAC 协议中描述)发送到 DAC,但我永远不会收到确认消息。
udp = new UDP( this ,7654 );
当我以这种方式初始化我的 UDP 对象时,我能够读取广播消息。
但是...
udp = new UDP( this ,7654, "192.169.0.101");
当我以这种方式初始化它时,我收到消息:
opening socket failed!
> address:192.169.0.101, port:7654 [group:null]
> Can't assign requested address
我也试过7765端口,文档中也有提到,结果一样。
UDP 库打开这样的连接:
// open a new socket to the specified port/address
// and join the group if the multicast socket is required
try {
InetAddress addr = InetAddress.getByName(ip);
InetAddress host = (ip==null) ? (InetAddress)null: addr;
if ( !addr.isMulticastAddress() ) {
ucSocket = new DatagramSocket( port, host ); // as broadcast
log( "bound socket to host:"+address()+", port: "+port() );
}
Ether Dream 创建者提供的 C 驱动程序 运行s 成功打开了与 DAC 的连接,如下所示:
struct etherdream_conn *conn = &d->conn;
memset(conn, 0, sizeof *conn);
// Open socket
conn->dc_sock = socket(AF_INET, SOCK_STREAM, 0);
if (conn->dc_sock < 0) {
log_socket_error(d, "socket");
return -1;
}
unsigned long nonblocking = 1;
ioctl(conn->dc_sock, FIONBIO, &nonblocking);
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_addr.s_addr = d->addr.s_addr, .sin_port = htons(7765)
};
// Because the socket is nonblocking, this will always error...
connect(conn->dc_sock, (struct sockaddr *)&addr, (int)sizeof addr);
if (errno != EINPROGRESS) {
log_socket_error(d, "connect");
goto bail;
}
可以找到此驱动程序的存储库 here。
为什么我无法通过Processing UDP库与DAC建立连接?我如何修改UDP库的Java实现,使其匹配可以成功连接到DAC的C驱动程序?
只有来自 DAC 的广播发生在 7654 上的 UDP 上
与 DAC 的通信通过端口 7765 上的 TCP 进行
以下java代码将使用UDP定位Etherdream DAC并与Etherdream DAC建立TCP通信。
import java.io.*;
import java.net.*;
import java.util.Arrays;
public class Etherdream implements Runnable {
public Etherdream() {
Thread thread = new Thread(this);
thread.start();
}
public static void main(String[] args) {
Etherdream laser = new Etherdream();
while (true) {
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
enum State {
GET_BROADCAST, KEEP_ALIVE_PING,
}
// https://ether-dream.com/protocol.html
enum Command {
PREPARE_STREAM(0x70), BEGIN_PLAYBACK(0x62), QUEUE_RATE_CHANGE(0x74), WRITE_DATA(0x64), STOP(0x73),
EMERGENCY_STOP(0x00), EMERGENCY_STOP_ALTERNATIVE(0xFF), CLEAR_EMERGENCY_STOP(0x63), PING(0x3F),
ACK_RESPONSE(0x61), NAK_FULL_RESPONSE(0x46), NAK_INVALID_RESPONSE(0x49), NAK_STOPCONDITION_RESPONSE(0x21);
final byte command;
private Command(int cmd) {
command = (byte) (cmd & 0xFF);
}
public byte[] bytes() {
return new byte[] { command };
}
public byte getCommand() {
return command;
}
}
enum DACStatus {
IDLE(0x01), PREPARED(0x02), PLAYING(0x03);
final byte status;
private DACStatus(int cmd) {
status = (byte) (cmd & 0xFF);
}
public byte[] bytes() {
return new byte[] { status };
}
}
@Override
public void run() {
State state = State.GET_BROADCAST;
InetAddress etherdreamAddress = null;
while (true) {
System.out.println("state " + state);
try {
switch (state) {
case GET_BROADCAST: {
// Wait and get broadcast using UDP
try (DatagramSocket inSocket = new DatagramSocket(7654)) {
// get broadcast
byte[] buffer = new byte[512];
DatagramPacket response = new DatagramPacket(buffer, buffer.length);
inSocket.receive(response);
/*
* struct j4cDAC_broadcast {
* uint8_t mac_address[6];
* uint16_t hw_revision;
* uint16_t sw_revision;
* uint16_t buffer_capacity;
* uint32_t max_point_rate;
* struct dac_status status;
* };
*/
byte[] broadcast = Arrays.copyOfRange(buffer, 0, response.getLength());
etherdreamAddress = response.getAddress();
if (etherdreamAddress != null) {
state = State.KEEP_ALIVE_PING;
}
}
break;
}
case KEEP_ALIVE_PING: {
// Send ping using TCP port 7765
try (Socket socket = new Socket(etherdreamAddress, 7765)) {
Thread.sleep(500);
Command cmd = Command.PING;
OutputStream output = socket.getOutputStream();
InputStream input = socket.getInputStream();
output.write(cmd.bytes());
/*
* struct dac_response {
* uint8_t response;
* uint8_t command;
* struct status dac_status;
* };
*/
byte[] dac_response = input.readNBytes(3);
// make sure we got an ACK
if(dac_response[0]!=Command.ACK_RESPONSE.command){
state = State.GET_BROADCAST;
break;
}
// make sure we got the response form the PING command
if(dac_response[1]!=cmd.command){
state = State.GET_BROADCAST;
break;
}
/*
* struct dac_status {
* uint8_t protocol;
* uint8_t light_engine_state;
* uint8_t playback_state;
* uint8_t source;
* uint16_t light_engine_flags;
* uint16_t playback_flags;
* uint16_t source_flags;
* uint16_t buffer_fullness;
* uint32_t point_rate;
* uint32_t point_count;
* };
*/
byte[] dac_status = input.readNBytes(20);
}
break;
}
default:
state = State.GET_BROADCAST;
}
} catch (Exception e) {
/* If any IO error occour
* for any reason such as
* network cable disconnect
* then try locate the Etherdream DAC again
*/
state = State.GET_BROADCAST;
System.out.println(e);
}
}
}
}