func (row *TCPRow) AnnotateServer(local *api.Annotations) error {
	if row.Server == nil {
		row.Server = &ServerInfo{IP: row.SockID.SrcIP, Port: row.SockID.SPort}
	}
	if local == nil {
		return nil
	}
	s := v2as.ConvertAnnotationsToServerAnnotations(local)
	row.ServerX.Geo = s.Geo
	row.ServerX.Network = s.Network
	row.Server.Geo = local.Geo
	if local.Network == nil {
		return nil
	}
	row.Server.Network = local.Network
	asn, err := local.Network.BestASN()
	if err != nil {
		metrics.AnnotationMissingCount.WithLabelValues("BestASN failed").Inc()
		return nil
	}
	row.ServerASN = uint32(asn)
	return nil
}
AnnotateServer adds the server annotations. See parser.Annotatable
local must not be nil
package schema
import (
	"time"
	"cloud.google.com/go/bigquery"
	"github.com/m-lab/annotation-service/api"
	v2as "github.com/m-lab/annotation-service/api/v2"
	"github.com/m-lab/go/cloud/bqx"
	"github.com/m-lab/go/rtx"
	"github.com/m-lab/tcp-info/inetdiag"
	"github.com/m-lab/tcp-info/snapshot"
	"github.com/m-lab/uuid-annotator/annotator"
	"github.com/m-lab/etl/metrics"
)
type ServerInfo struct {
	IP   string
	Port uint16
	IATA string
	Geo     *api.GeolocationIP
	Network *api.ASData  
}
type ClientInfo struct {
	IP   string
	Port uint16
	Geo     *api.GeolocationIP
	Network *api.ASData  
}
type ParseInfoV0 struct {
	TaskFileName  string  
	ParseTime     time.Time
	ParserVersion string
	Filename      string
}
type TCPRow struct {
	UUID     string     
	TestTime time.Time  
	ClientASN uint32  
	ServerASN uint32  
	ParseInfo *ParseInfoV0
	SockID inetdiag.SockID
	Server *ServerInfo
	Client *ClientInfo
	FinalSnapshot *snapshot.Snapshot
	Snapshots []*snapshot.Snapshot
	ServerX annotator.ServerAnnotations
	ClientX annotator.ClientAnnotations
}
func (row *TCPRow) CopySocketInfo() {
	row.Server = &ServerInfo{IP: row.SockID.SrcIP, Port: row.SockID.SPort}
	row.Client = &ClientInfo{IP: row.SockID.DstIP, Port: row.SockID.DPort}
}
func assertTCPRowIsValueSaver(r *TCPRow) {
	func(bigquery.ValueSaver) {}(r)
}
func init() {
	var err error
	tcpSchema, err = (&TCPRow{}).Schema()
	rtx.Must(err, "Error generating tcp schema")
}
var tcpSchema bigquery.Schema
func (row *TCPRow) Save() (map[string]bigquery.Value, string, error) {
	ss := bigquery.StructSaver{Schema: tcpSchema, InsertID: row.UUID, Struct: row}
	return ss.Save()
}
func (row *TCPRow) Schema() (bigquery.Schema, error) {
	sch, err := bigquery.InferSchema(row)
	if err != nil {
		return bigquery.Schema{}, err
	}
	docs := FindSchemaDocsFor(row)
	for _, doc := range docs {
		bqx.UpdateSchemaDescription(sch, doc)
	}
	rr := bqx.RemoveRequired(sch)
	return rr, nil
}
func (row *TCPRow) GetLogTime() time.Time {
	return row.TestTime
}
func (row *TCPRow) GetClientIPs() []string {
	return []string{row.SockID.DstIP}
}
func (row *TCPRow) GetServerIP() string {
	return row.SockID.SrcIP
}
func (row *TCPRow) AnnotateClients(annMap map[string]*api.Annotations) error {
	ip := row.SockID.DstIP
	if row.Client == nil {
		row.Client = &ClientInfo{IP: ip, Port: row.SockID.DPort}
	}
	ann, ok := annMap[ip]
	if !ok {
		metrics.AnnotationMissingCount.WithLabelValues("No annotation for IP").Inc()
		return nil
	}
	c := v2as.ConvertAnnotationsToClientAnnotations(ann)
	row.ClientX.Geo = c.Geo
	row.ClientX.Network = c.Network
	if ann.Geo == nil {
		metrics.AnnotationMissingCount.WithLabelValues("Empty ann.Geo").Inc()
	} else {
		row.Client.Geo = ann.Geo
	}
	if ann.Network == nil {
		metrics.AnnotationMissingCount.WithLabelValues("Empty ann.Network").Inc()
		return nil
	}
	row.Client.Network = ann.Network
	asn, err := ann.Network.BestASN()
	if err != nil {
		metrics.AnnotationMissingCount.WithLabelValues("BestASN failed").Inc()
		return nil
	}
	row.ClientASN = uint32(asn)
	return nil
}