作为Go中的第一个非教程程序,我编写了以下实用程序。

该实用程序的目的是


连接到扭矩/力通过UDP的传感器(又称称重传感器);
发送初始化命令;和
以记录测量结果。

由于我正在计划进一步的功能(通过串行设置无刷电机控制器的占空比),该功能将记录到同一文件中,因此已将文件写入与通过通道的UDP数据包日志记录分开。

由于这是一个用于单一目的的实用程序,因此错误处理最少。有关代码的一般样式和惯用性以及文件夹结构(将与称重传感器相关的文件分离到称重传感器子目录中的单独子包中)的反馈。 on:


loadcell.go中的go version go1.4.2 darwin/amd64中可能存在错误(请参阅最后一个for循环之前的注释)
loadCellPacket.go中的FromNetworkBytes函数。我真的很想做一个类方法,例如packet := loadCellPacket.FromNetworkBytes(b),但是据我发现,Go选项是:packet := loadCellPacketFromNetworkBytes(b)(一个简单的函数)或var packet loadCellBytes; packet.FromNetworkBytes(b)。我选择了后者,因为我可以重用packet变量,但是我希望得到反馈。

代码托管在这里,为长寿这个问题,我重复下面的文件。为了简洁起见,我省略了导入语句。

./main.go

package main

import (
    ...
    "github.com/mikehamer/ati-torque-force-logger/loadcell"
)

func main() {
    // Parse flags
    loadCellAddress := flag.String("address", "192.168.1.200:49152", "The address of the loadcell")
    logFileName := flag.String("logfile", fmt.Sprintf("%s_loadcell.log", time.Now().Format("2006-01-02_15-04-05")), "The name of the logfile")
    flag.Parse()

    //open CSV log
    logfile, err := os.Create(*logFileName)
    if err != nil {
        log.Fatal(err)
    }
    defer logfile.Close()

    // Setup communication channels
    receivedMeasurements := make(chan loadcell.Measurement)

    //connect and stream from loadcell
    go loadcell.ReceiveLoadCellStream(*loadCellAddress, receivedMeasurements)

    //loop and write logs
    fmt.Println("Saving output to", logfile.Name())
    logfile.WriteString("t, Fx, Fy, Fz, Tx, Ty, Tz\n")
    for {
        select {
        case measurement := <-receivedMeasurements:
            logfile.WriteString(measurement.String())
        }
    }
}



./loadcell/loadcell.go

package loadcell

// NETWORK CONSTANTS
var loadCellStartStreamCommand = loadCellCommand{0x1234, 0x0002, 0} // the command to send to enable realtime stream

// ReceiveLoadCellStream opens a network stream to the loadcell, sends a configuration packet and then relays received measurements back through the supplied channel
func ReceiveLoadCellStream(loadCellAddress string, receivedPackets chan<- Measurement) error {
    // calculate loadcell address
    remoteAddr, err := net.ResolveUDPAddr("udp", loadCellAddress)
    if err != nil {
        log.Fatal(err)
    }

    //open connection to loadcell
    conn, err := net.DialUDP("udp", nil, remoteAddr)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("UDP Server: Local", conn.LocalAddr(), "-> Remote", conn.RemoteAddr())
    defer conn.Close()

    // send the command instructing the loadcell to begin a realtime data stream
    conn.Write(loadCellStartStreamCommand.NetworkBytes())

    // begin receiving packets from the network connection and sending them on the outgoing channel
    startTime := time.Now()
    buf := make([]byte, 36) //BUG? This causes ReadFromUDP to block = GOOD
    //var buf []byte        //     While this causes ReadFromUDP to continuously return 0,nil,nil
    for {
        var packet loadCellPacket
        n, remoteAddr, err := conn.ReadFromUDP(buf)
        switch {

        //packet of the correct size is received
        case uintptr(n) == unsafe.Sizeof(packet):
            if err := packet.FromNetworkBytes(buf); err != nil {
                log.Fatal(err)
            } //decode it from network stream

            receivedPackets <- packet.ParseMeasurement()

        //packet is received but with incorrect size
        case n != 0:
            log.Print("From", remoteAddr, "got unexpected bytes", buf[:n])

        //an error occurs
        case err != nil:
            log.Fatal(err)
        }
    }
}



./loadcell/loadCellPacket.go

package loadcell

// loadCellPacket is the packet as received over the network
type loadCellPacket struct {
    RdtSequence uint32 // RDT sequence number of this packet.
    FtSequence  uint32 // The record’s internal sequence number
    Status      uint32 // System status code
    // Force and torque readings use counts values
    Fx int32 // X-axis force
    Fy int32 // Y-axis force
    Fz int32 // Z-axis force
    Tx int32 // X-axis torque
    Ty int32 // Y-axis torque
    Tz int32 // Z-axis torque
}

// FromNetworkBytes parses a loadCellPacket from a network (BigEndian) bytestream
func (s *loadCellPacket) FromNetworkBytes(b []byte) error {
    var packet loadCellPacket
    buf := bytes.NewReader(b)
    if err := binary.Read(buf, binary.BigEndian, &packet); err != nil {
        return err
    }
    *s = packet
    return nil
}

// ParseMeasurement creates a Measurement from the loadCellPacket
func (s *loadCellPacket) ParseMeasurement(rxTime float64) Measurement {
    return Measurement{
        rxTime,
        float32(s.Fx) / 1e6,
        float32(s.Fy) / 1e6,
        float32(s.Fz) / 1e6,
        float32(s.Tx) / 1e6,
        float32(s.Ty) / 1e6,
        float32(s.Tz) / 1e6}
}



./loadcell/LoadCellMeasurement.go

package loadcell

// Measurement is a loadCellPacket that has been converted into a useable form
type Measurement struct {
    RxTime float64 // the receive time, since the beginning of the program
    Fx     float32 // x-force in Newtons
    Fy     float32 // y-force in Newtons
    Fz     float32 // z-force in Newtons
    Tx     float32 // x-torque in Newton-meters
    Ty     float32 // y-torque in Newton-meters
    Tz     float32 // z-torque in Newton-meters
}

// Bytes returns the Measurement as a LittleEndian-encoded byte slice, ready for serialization
func (s *Measurement) Bytes() []byte {
    buf := new(bytes.Buffer)

    if err := binary.Write(buf, binary.LittleEndian, s); err != nil {
        log.Fatal(err)
    }

    return buf.Bytes()
}

// String returns the Measurement as a comma-separated string, ready for logging
func (s *Measurement) String() string {
    return fmt.Sprintf("%.6f, %v, %v, %v, %v, %v, %v\n", s.RxTime, s.Fx, s.Fy, s.Fz, s.Tx, s.Ty, s.Tz)
}



./loadcell/loadCellCommand.go

package loadcell

// loadCellCommand is a command packet sent to the loadcell
type loadCellCommand struct {
    header      uint16 // = 0x1234 Required
    command     uint16 // Command to execute
    sampleCount uint32 // Samples to output (0 = infinite)
}

// NetworkBytes returns the loadCellCommand as a BigEndian-encoded byte slice ready for network transmission
func (s *loadCellCommand) NetworkBytes() []byte {
    buf := new(bytes.Buffer)

    if err := binary.Write(buf, binary.BigEndian, s); err != nil {
        log.Fatal(err)
    }

    return buf.Bytes()
}



#1 楼

关于它的第一部分:

从阅读golang文档开始,它似乎出现了

buf := make([]byte, 36) //BUG? This causes ReadFromUDP to block = GOOD

因为特定的分配和初始化36 []字节类型正在发生,如果到达要读取的UDP数据包,则至少足以容纳某些内容。

请参阅文档:Builtin
var buf []byte        //     While this causes ReadFromUDP to continuously return 0,nil,nil


在这种情况下,您将buf用作指向字节的指针,但是未对它调用new或make。和文档似乎是最新的1.9.x。我没有发现一种简单的方法来检查网络软件包或连接/ UDP使用情况的规范,以了解是否有任何更改...