Fix encoding for float64 and generate conversion for Timestamp 05/29605/2
authorOndrej Fabry <ofabry@cisco.com>
Fri, 23 Oct 2020 09:40:18 +0000 (11:40 +0200)
committerOndrej Fabry <ofabry@cisco.com>
Fri, 30 Oct 2020 10:00:00 +0000 (10:00 +0000)
- fixes encoding/decoding of float64 - uses little endian (contrary to all other types)
- generates helper methods for vpe_types.Timestamp type
- adds usage code to simple-client and binapi-types examples

Change-Id: I2e83eee0629eb67964049406c50c7ee0a692ccaf
Signed-off-by: Ondrej Fabry <ofabry@cisco.com>
binapi/vpe_types/vpe_types.ba.go
binapigen/gen_helpers.go
binapigen/gen_helpers_test.go
binapigen/generate.go
codec/codec.go
examples/binapi-types/binapi_types.go
examples/simple-client/simple_client.go

index 473880d..b4d324f 100644 (file)
@@ -15,6 +15,7 @@ package vpe_types
 
 import (
        "strconv"
+       "time"
 
        api "git.fd.io/govpp.git/api"
 )
@@ -79,6 +80,33 @@ type Timedelta float64
 // Timestamp defines alias 'timestamp'.
 type Timestamp float64
 
+func NewTimestamp(t time.Time) Timestamp {
+       sec := int64(t.Unix())
+       nsec := int32(t.Nanosecond())
+       ns := float64(sec) + float64(nsec/1e9)
+       return Timestamp(ns)
+}
+func (x Timestamp) ToTime() time.Time {
+       ns := int64(x * 1e9)
+       sec := ns / 1e9
+       nsec := ns % 1e9
+       return time.Unix(sec, nsec)
+}
+func (x Timestamp) String() string {
+       return x.ToTime().String()
+}
+func (x *Timestamp) MarshalText() ([]byte, error) {
+       return []byte(x.ToTime().Format(time.RFC3339Nano)), nil
+}
+func (x *Timestamp) UnmarshalText(text []byte) error {
+       t, err := time.Parse(time.RFC3339Nano, string(text))
+       if err != nil {
+               return err
+       }
+       *x = NewTimestamp(t)
+       return nil
+}
+
 // Version defines type 'version'.
 type Version struct {
        Major         uint32 `binapi:"u32,name=major" json:"major,omitempty"`
index 5eafc76..944bfe2 100644 (file)
@@ -22,6 +22,7 @@ func init() {
 const (
        fmtPkg     = GoImportPath("fmt")
        netPkg     = GoImportPath("net")
+       timePkg    = GoImportPath("time")
        stringsPkg = GoImportPath("strings")
 )
 
@@ -185,13 +186,6 @@ func genIPPrefixConversion(g *GenFile, structName string, ipv int) {
        g.P("func (x ", structName, ") String() string {")
        g.P("   ip := x.Address.String()")
        g.P("   return ip + \"/\" + ", strconvPkg.Ident("Itoa"), "(int(x.Len))")
-       /*if ipv == 4 {
-               g.P("   mask := ", netPkg.Ident("CIDRMask"), "(int(x.Len), 32)")
-         } else {
-               g.P("   mask := ", netPkg.Ident("CIDRMask"), "(int(x.Len), 128)")
-         }
-         g.P(" ipnet := &", netPkg.Ident("IPNet"), "{IP: x.Address.ToIP(), Mask: mask}")
-         g.P(" return ipnet.String()")*/
        g.P("}")
 
        // MarshalText method
@@ -346,3 +340,42 @@ func genMacAddressConversion(g *GenFile, structName string) {
        g.P("}")
        g.P()
 }
+
+func genTimestampConversion(g *GenFile, structName string) {
+       // NewTimestamp method
+       g.P("func New", structName, "(t ", timePkg.Ident("Time"), ") ", structName, " {")
+       g.P("   sec := int64(t.Unix())")
+       g.P("   nsec := int32(t.Nanosecond())")
+       g.P("   ns := float64(sec) + float64(nsec / 1e9)")
+       g.P("   return ", structName, "(ns)")
+       g.P("}")
+
+       // ToTime method
+       g.P("func (x ", structName, ") ToTime() ", timePkg.Ident("Time"), " {")
+       g.P("   ns := int64(x * 1e9)")
+       g.P("   sec := ns / 1e9")
+       g.P("   nsec := ns % 1e9")
+       g.P("   return ", timePkg.Ident("Unix"), "(sec, nsec)")
+       g.P("}")
+
+       // String method
+       g.P("func (x ", structName, ") String() string {")
+       g.P("   return x.ToTime().String()")
+       g.P("}")
+
+       // MarshalText method
+       g.P("func (x *", structName, ") MarshalText() ([]byte, error) {")
+       g.P("   return []byte(x.ToTime().Format(", timePkg.Ident("RFC3339Nano"), ")), nil")
+       g.P("}")
+
+       // UnmarshalText method
+       g.P("func (x *", structName, ") UnmarshalText(text []byte) error {")
+       g.P("   t, err := ", timePkg.Ident("Parse"), "(", timePkg.Ident("RFC3339Nano"), ", string(text))")
+       g.P("   if err != nil {")
+       g.P("           return err")
+       g.P("   }")
+       g.P("   *x = New", structName, "(t)")
+       g.P("   return nil")
+       g.P("}")
+       g.P()
+}
index 371fd6c..075dd8e 100644 (file)
@@ -17,11 +17,13 @@ package binapigen
 import (
        "strings"
        "testing"
+       "time"
 
        . "github.com/onsi/gomega"
 
        "git.fd.io/govpp.git/binapi/ethernet_types"
        "git.fd.io/govpp.git/binapi/ip_types"
+       "git.fd.io/govpp.git/binapi/vpe_types"
 )
 
 func TestGeneratedParseAddress(t *testing.T) {
@@ -154,3 +156,28 @@ func TestGeneratedParseMACError(t *testing.T) {
        _, err := ethernet_types.ParseMacAddress("malformed_mac")
        Expect(err).Should(HaveOccurred())
 }
+
+func TestGeneratedParseTimestamp(t *testing.T) {
+       RegisterTestingT(t)
+
+       var data = []struct {
+               input  time.Time
+               result vpe_types.Timestamp
+       }{
+               {time.Unix(0, 0), vpe_types.Timestamp(0)},
+               {time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC),
+                       vpe_types.Timestamp(9.466848e+08)},
+       }
+
+       for _, entry := range data {
+               t.Run(entry.input.String(), func(t *testing.T) {
+                       ts := vpe_types.NewTimestamp(entry.input)
+                       Expect(ts).To(Equal(entry.result))
+
+                       Expect(entry.input.Equal(ts.ToTime())).To(BeTrue())
+
+                       originTime := ts.String()
+                       Expect(originTime).To(Equal(entry.input.Local().String()))
+               })
+       }
+}
index 834c989..bf6df81 100644 (file)
@@ -246,6 +246,8 @@ func genAlias(g *GenFile, alias *Alias) {
                genAddressWithPrefixConversion(g, alias.GoName)
        case "mac_address":
                genMacAddressConversion(g, alias.GoName)
+       case "timestamp":
+               genTimestampConversion(g, alias.GoName)
        }
 }
 
index 3ae578b..21354a1 100644 (file)
@@ -156,12 +156,12 @@ func (b *Buffer) DecodeInt64() int64 {
 }
 
 func (b *Buffer) EncodeFloat64(v float64) {
-       binary.BigEndian.PutUint64(b.buf[b.pos:b.pos+8], math.Float64bits(v))
+       binary.LittleEndian.PutUint64(b.buf[b.pos:b.pos+8], math.Float64bits(v))
        b.pos += 8
 }
 
 func (b *Buffer) DecodeFloat64() float64 {
-       v := math.Float64frombits(binary.BigEndian.Uint64(b.buf[b.pos : b.pos+8]))
+       v := math.Float64frombits(binary.LittleEndian.Uint64(b.buf[b.pos : b.pos+8]))
        b.pos += 8
        return v
 }
index 849ad1b..2dbaa3e 100644 (file)
@@ -18,36 +18,42 @@ package main
 import (
        "fmt"
        "log"
+       "time"
 
        "git.fd.io/govpp.git/binapi/ethernet_types"
        "git.fd.io/govpp.git/binapi/ip"
        "git.fd.io/govpp.git/binapi/ip_types"
+       "git.fd.io/govpp.git/binapi/vpe_types"
        "git.fd.io/govpp.git/codec"
 )
 
 func init() {
-       log.SetFlags(0)
 }
 
 func main() {
-       addressUnionExample()
-       ipAddressExample()
+       log.SetFlags(0)
+
+       usageUnion()
+       usageAddress()
 
-       // convert IP from string form into Address type containing union
-       convertIP("10.10.1.1")
-       convertIP("ff80::1")
+       // convert IP address in string form into ip_types.Address
+       convertIPAddress("10.10.1.1")
+       convertIPAddress("ff80::1")
 
-       // convert IP from string form into Prefix type
+       // convert IP address / CIDR in string form into ip_types.Prefix
        convertIPPrefix("20.10.1.1/24")
        convertIPPrefix("21.10.1.1")
        convertIPPrefix("ff90::1/64")
        convertIPPrefix("ff91::1")
 
-       // convert MAC address from string into MacAddress
+       // convert MAC address in string form into ethernet_types.MacAddress
        convertToMacAddress("00:10:ab:4f:00:01")
+
+       // convert time.Time into vpe_types.Timestamp
+       convertToTimestamp(time.Now())
 }
 
-func addressUnionExample() {
+func usageUnion() {
        var union ip_types.AddressUnion
 
        // initialize union using constructors
@@ -63,21 +69,17 @@ func addressUnionExample() {
        union.SetIP6(ip6)
 }
 
-func ipAddressExample() {
+func usageAddress() {
        // parse string into IP address
-       addrIP4, err := ip_types.ParseAddress("192.168.1.10")
+       addr, err := ip_types.ParseAddress("192.168.1.10")
        if err != nil {
                panic(err)
        }
-       /*addrIP6, err := ip_types.ParseAddress("ff:2::2")
-       if err != nil {
-               panic(err)
-       }*/
 
        var msg = ip.IPPuntRedirect{
                IsAdd: true,
                Punt: ip.PuntRedirect{
-                       Nh: addrIP4,
+                       Nh: addr,
                },
        }
 
@@ -103,7 +105,7 @@ func ipAddressExample() {
        }
 }
 
-func convertIP(ip string) {
+func convertIPAddress(ip string) {
        addr, err := ip_types.ParseAddress(ip)
        if err != nil {
                log.Printf("error converting IP to Address: %v", err)
@@ -135,3 +137,10 @@ func convertToMacAddress(mac string) {
 
        fmt.Printf("MacAddress converted back to string %#v to: %s\n", parsedMac, parsedMac)
 }
+
+func convertToTimestamp(t time.Time) {
+       timestamp := vpe_types.NewTimestamp(t)
+       fmt.Printf("converted time %v to: %#v\n", t, timestamp)
+
+       fmt.Printf("Timestamp converted back to string %#v to: %s\n", timestamp, timestamp)
+}
index 0898c0a..10a0ea6 100644 (file)
@@ -84,6 +84,7 @@ func main() {
 
        // use request/reply (channel API)
        getVppVersion(ch)
+       getSystemTime(ch)
        idx := createLoopback(ch)
        interfaceDump(ch)
        addIPAddress(ch, idx)
@@ -107,6 +108,22 @@ func getVppVersion(ch api.Channel) {
        fmt.Println()
 }
 
+func getSystemTime(ch api.Channel) {
+       fmt.Println("Retrieving system time..")
+
+       req := &vpe.ShowVpeSystemTime{}
+       reply := &vpe.ShowVpeSystemTimeReply{}
+
+       if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
+               logError(err, "retrieving system time")
+               return
+       }
+
+       fmt.Printf("system time: %v\n", reply.VpeSystemTime)
+       fmt.Println("OK")
+       fmt.Println()
+}
+
 func createLoopback(ch api.Channel) interface_types.InterfaceIndex {
        fmt.Println("Creating loopback interface..")