From 0f46871b4cc45f2c3bd5bdb0aa0f7615795a2c6d Mon Sep 17 00:00:00 2001 From: Ondrej Fabry Date: Fri, 23 Oct 2020 11:40:18 +0200 Subject: Fix encoding for float64 and generate conversion for Timestamp - 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 --- binapi/vpe_types/vpe_types.ba.go | 28 ++++++++++++++++++++ binapigen/gen_helpers.go | 47 ++++++++++++++++++++++++++++----- binapigen/gen_helpers_test.go | 27 +++++++++++++++++++ binapigen/generate.go | 2 ++ codec/codec.go | 4 +-- examples/binapi-types/binapi_types.go | 43 ++++++++++++++++++------------ examples/simple-client/simple_client.go | 17 ++++++++++++ 7 files changed, 142 insertions(+), 26 deletions(-) diff --git a/binapi/vpe_types/vpe_types.ba.go b/binapi/vpe_types/vpe_types.ba.go index 473880d..b4d324f 100644 --- a/binapi/vpe_types/vpe_types.ba.go +++ b/binapi/vpe_types/vpe_types.ba.go @@ -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"` diff --git a/binapigen/gen_helpers.go b/binapigen/gen_helpers.go index 5eafc76..944bfe2 100644 --- a/binapigen/gen_helpers.go +++ b/binapigen/gen_helpers.go @@ -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() +} diff --git a/binapigen/gen_helpers_test.go b/binapigen/gen_helpers_test.go index 371fd6c..075dd8e 100644 --- a/binapigen/gen_helpers_test.go +++ b/binapigen/gen_helpers_test.go @@ -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())) + }) + } +} diff --git a/binapigen/generate.go b/binapigen/generate.go index 834c989..bf6df81 100644 --- a/binapigen/generate.go +++ b/binapigen/generate.go @@ -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) } } diff --git a/codec/codec.go b/codec/codec.go index 3ae578b..21354a1 100644 --- a/codec/codec.go +++ b/codec/codec.go @@ -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 } diff --git a/examples/binapi-types/binapi_types.go b/examples/binapi-types/binapi_types.go index 849ad1b..2dbaa3e 100644 --- a/examples/binapi-types/binapi_types.go +++ b/examples/binapi-types/binapi_types.go @@ -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) +} diff --git a/examples/simple-client/simple_client.go b/examples/simple-client/simple_client.go index 0898c0a..10a0ea6 100644 --- a/examples/simple-client/simple_client.go +++ b/examples/simple-client/simple_client.go @@ -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..") -- cgit 1.2.3-korg