作为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使用情况的规范,以了解是否有任何更改...