通过 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);
            }

        }
    }
}