Утилита, которая декодирует и регистрирует пакеты UDP

Я написал следующую утилиту, как свою первую не обучающую программу в Go.

Назначение утилиты

  • для подключения к датчику крутящего момента /силы (aka load-cell) через UDP;
  • отправить команду инициализации; и
  • для записи результирующего потока измерений.

Поскольку я планирую дальнейшую функциональность (установив рабочий цикл бесщеточного контроллера двигателя через последовательный порт), который будет зарегистрирован в том же файле, я отделил запись файла из протокола UDP-протокола через канал.

Так как это утилита, которая служит одной цели, обработка ошибок минимальна.

Я надеюсь на обратную связь относительно общего стиля и идиоматики кода и структуры папок (разделение файлов, связанных с загрузкой, в отдельный подпакет в подкаталоге loadcell).

В частности, я был бы благодарен за ваши мысли:

  1. Возможная ошибка в go version go1.4.2 darwin/amd64, в loadcell.go (см. комментарий перед заключительным циклом for)
  2. Функция FromNetworkBytes в loadCellPacket.go. Я действительно хотел создать метод класса, например. packet := loadCellPacket.FromNetworkBytes(b), но, насколько я нашел, параметры Go: packet := loadCellPacketFromNetworkBytes(b) (простая функция), или var packet loadCellBytes; packet.FromNetworkBytes(b). Я выбрал более поздние, так как я могу повторно использовать переменную пакета, но я был бы признателен за отзывы.

Этот код размещен здесь , и для долголетия этого вопроса я повторяю файлы ниже. Я оставил инструкции импорта для краткости.

./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()
}
37 голосов | спросил Mike Hamer 16 PMpThu, 16 Apr 2015 22:13:22 +030013Thursday 2015, 22:13:22

1 ответ


1

Относительно первой части:

Из чтения документов golang появляется

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

может работать, потому что происходит определенное выделение и инициализация 36 [] байтового типа, по крайней мере, достаточно, чтобы что-то удерживать, если пакет UDP прибывает для чтения

см. Документация: встроено

, а

var buf []byte        //     While this causes ReadFromUDP to continuously return 0,nil,nil

в этом случае у вас есть buf как указатель на байт, но ни новый, ни make не были вызваны на него

Остерегайтесь, вы используете 1.4.x, и документы, похоже, соответствуют текущему 1.9.x. Я не видел простой способ проверить спецификации в сетевом пакете или использовании Connection /UDP, что изменилось ...

ответил M. Cooperman 12 TueEurope/Moscow2017-12-12T20:35:16+03:00Europe/Moscow12bEurope/MoscowTue, 12 Dec 2017 20:35:16 +0300 2017, 20:35:16

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132