package client import ( "fmt" "net" bin "gordenko.dev/dima/bin/little" "gordenko.dev/dima/qb" "gordenko.dev/dima/qb/bufreader" "gordenko.dev/dima/qb/proto" ) const ( metricKeySize = 4 ) type Error struct { Code uint16 Message string } func (s Error) Error() string { return fmt.Sprintf("%d: %s", s.Code, s.Message) } type Connection struct { conn net.Conn src *bufreader.BufferedReader } func Connect(address string) (*Connection, error) { conn, err := net.Dial("tcp", address) if err != nil { return nil, err } return &Connection{ conn: conn, src: bufreader.New(conn, 1500), }, nil } func (s *Connection) String() string { return s.conn.LocalAddr().String() } func (s *Connection) Close() { s.conn.Close() } func (s *Connection) mustSuccess(reader *bufreader.BufferedReader) (err error) { code, err := reader.ReadByte() if err != nil { return } switch code { case proto.RespSuccess: fmt.Println("SUCCESS") return nil // ok case proto.RespError: return s.onError() default: return fmt.Errorf("unknown reponse code %d", code) } } func (s *Connection) AddMetric(req proto.AddMetricReq) (err error) { arr := []byte{ proto.TypeAddMetric, 0, 0, 0, 0, // byte(req.MetricType), req.FracDigits, } bin.PutUint32(arr[1:], req.MetricID) fmt.Println("arr", arr) // req if _, err = s.conn.Write(arr); err != nil { return } return s.mustSuccess(s.src) } // // func (s *Connection) UpdateMetric(req Metric) error { // arr := []byte{ // proto.TypeUpdateMetric, // 0, 0, 0, 0, // // req.FracDigits, // } // bin.PutUint32(arr[1:], req.MetricID) // if _, err := s.conn.Write(arr); err != nil { // return err // } // return s.mustSuccess(s.src) // } func (s *Connection) GetMetric(metricID uint32) (_ *proto.Metric, err error) { arr := []byte{ proto.TypeGetMetric, 0, 0, 0, 0, } bin.PutUint32(arr[1:], metricID) // req if _, err = s.conn.Write(arr); err != nil { return } // answer code, err := s.src.ReadByte() if err != nil { return } switch code { case proto.RespValue: var ( metric proto.Metric metricType byte ) metric.MetricID, err = bin.ReadUint32(s.src) if err != nil { return } metricType, err = bin.ReadByte(s.src) if err != nil { return } metric.MetricType = qb.MetricType(metricType) metric.FracDigits, err = bin.ReadByteAsInt(s.src) if err != nil { return } return &metric, nil case proto.RespError: return nil, s.onMaybeError() default: return nil, fmt.Errorf("unknown reponse code %d", code) } } func (s *Connection) DeleteMetric(metricID uint32) (err error) { arr := []byte{ proto.TypeDeleteMetric, 0, 0, 0, 0, // } bin.PutUint32(arr[1:], metricID) // req if _, err = s.conn.Write(arr); err != nil { return } return s.mustSuccess(s.src) } // func (s *Connection) AppendMeasure(req proto.AppendMeasureReq) (err error) { // arr := []byte{ // proto.TypeAppendMeasure, // 0, 0, 0, 0, // metricID // 0, 0, 0, 0, // timestamp // 0, 0, 0, 0, 0, 0, 0, 0, // value // } // bin.PutUint32(arr[1:], req.MetricID) // bin.PutUint32(arr[5:], req.Timestamp) // bin.PutFloat64(arr[9:], req.Value) // // req // if _, err = s.conn.Write(arr); err != nil { // return // } // return s.mustSuccess(s.src) // } type AppendMeasuresResult struct { WrittenCount int ErrorCode byte } func (s *Connection) AppendMeasures(req proto.AppendMeasuresReq) (result AppendMeasuresResult, err error) { if len(req.Measures) > 65535 { err = fmt.Errorf("wrong measures qty: %d", len(req.Measures)) return } var ( prefixSize = 7 recordSize = 12 arr = make([]byte, prefixSize+len(req.Measures)*recordSize) ) arr[0] = proto.TypeAppendMeasures bin.PutUint32(arr[1:], req.MetricID) bin.PutUint16(arr[5:], uint16(len(req.Measures))) pos := prefixSize for _, measure := range req.Measures { bin.PutUint32(arr[pos:], measure.Timestamp) bin.PutFloat64(arr[pos+4:], measure.Value) pos += recordSize } // req if _, err = s.conn.Write(arr); err != nil { return } // answer code, err := s.src.ReadByte() if err != nil { return } switch code { case proto.RespValue: result.WrittenCount, err = bin.ReadUint16AsInt(s.src) if err != nil { return } result.ErrorCode, err = bin.ReadByte(s.src) if err != nil { return } default: err = fmt.Errorf("unknown reponse code %d", code) return } return } // type AppendMeasurePerMetricReq struct { // MetricID uint32 // Measures []Measure // } // func (s *Connection) AppendMeasurePerMetric(list []proto.MetricMeasure) (_ []proto.AppendError, err error) { // if len(list) > 65535 { // return nil, fmt.Errorf("wrong measures qty: %d", len(list)) // } // var ( // // 3 bytes: 1b message type + 2b records qty // fixedSize = 3 // recordSize = 16 // arr = make([]byte, fixedSize+len(list)*recordSize) // ) // arr[0] = proto.TypeAppendMeasures // bin.PutUint16(arr[1:], uint16(len(list))) // pos := fixedSize // for _, item := range list { // bin.PutUint32(arr[pos:], item.MetricID) // bin.PutUint32(arr[pos+4:], item.Timestamp) // bin.PutFloat64(arr[pos+8:], item.Value) // pos += recordSize // } // // req // if _, err = s.conn.Write(arr); err != nil { // return // } // // answer // code, err := s.src.ReadByte() // if err != nil { // return // } // switch code { // case proto.RespValue: // var ( // count int // appendErrors []proto.AppendError // ) // count, err = bin.ReadUint16AsInt(s.src) // if err != nil { // return // } // for range count { // var ae proto.AppendError // ae.MetricID, err = bin.ReadUint32(s.src) // if err != nil { // return // } // ae.ErrorCode, err = bin.ReadUint16(s.src) // if err != nil { // return // } // appendErrors = append(appendErrors, ae) // } // return appendErrors, nil // default: // return nil, fmt.Errorf("unknown reponse code %d", code) // } // } func (s *Connection) ListAllInstantMeasures(metricID uint32) (_ []proto.InstantMeasure, err error) { arr := []byte{ proto.TypeListAllInstantMeasures, 0, 0, 0, 0, // metricID } bin.PutUint32(arr[1:], metricID) // req if _, err = s.conn.Write(arr); err != nil { return } // answer return s.readInstantMeasures() } func (s *Connection) ListInstantMeasures(req proto.ListInstantMeasuresReq) (_ []proto.InstantMeasure, err error) { arr := []byte{ proto.TypeListInstantMeasures, 0, 0, 0, 0, // metricID 0, 0, 0, 0, // since 0, 0, 0, 0, // until } bin.PutUint32(arr[1:], req.MetricID) bin.PutUint32(arr[5:], req.Since) bin.PutUint32(arr[9:], req.Until) // req if _, err = s.conn.Write(arr); err != nil { return } // answer return s.readInstantMeasures() } func (s *Connection) readInstantMeasures() (_ []proto.InstantMeasure, err error) { var result []proto.InstantMeasure for { var code byte code, err = bin.ReadByte(s.src) if err != nil { return nil, fmt.Errorf("read response code: %s", err) } switch code { case proto.RespPartOfValue: var count int count, err = bin.ReadUint32AsInt(s.src) if err != nil { return nil, fmt.Errorf("read records qty: %s", err) } for range count { var measure proto.InstantMeasure measure.Timestamp, err = bin.ReadUint32(s.src) if err != nil { return } measure.Value, err = bin.ReadFloat64(s.src) if err != nil { return } result = append(result, measure) } case proto.RespEndOfValue: return result, nil case proto.RespError: return nil, s.onError() default: return nil, fmt.Errorf("unknown reponse code %d", code) } } } func (s *Connection) ListAllCumulativeMeasures(metricID uint32) (_ []proto.CumulativeMeasure, err error) { arr := []byte{ proto.TypeListAllCumulativeMeasures, 0, 0, 0, 0, // metricID } bin.PutUint32(arr[1:], metricID) // req if _, err = s.conn.Write(arr); err != nil { return } // answer return s.readCumulativeMeasures() } func (s *Connection) ListCumulativeMeasures(req proto.ListCumulativeMeasuresReq) (_ []proto.CumulativeMeasure, err error) { arr := []byte{ proto.TypeListCumulativeMeasures, 0, 0, 0, 0, // metricID 0, 0, 0, 0, // since 0, 0, 0, 0, // until } bin.PutUint32(arr[1:], req.MetricID) bin.PutUint32(arr[5:], req.Since) bin.PutUint32(arr[9:], req.Until) // req if _, err = s.conn.Write(arr); err != nil { return } // answer return s.readCumulativeMeasures() } func (s *Connection) readCumulativeMeasures() (_ []proto.CumulativeMeasure, err error) { var result []proto.CumulativeMeasure for { var code byte code, err = bin.ReadByte(s.src) if err != nil { return nil, fmt.Errorf("read response code: %s", err) } fmt.Println("code", code) switch code { case proto.RespPartOfValue: fmt.Println("RespPartOfValue") var count int count, err = bin.ReadUint32AsInt(s.src) if err != nil { return nil, fmt.Errorf("read records qty: %s", err) } for range count { var measure proto.CumulativeMeasure measure.Timestamp, err = bin.ReadUint32(s.src) if err != nil { return } measure.Value, err = bin.ReadFloat64(s.src) if err != nil { return } measure.Total, err = bin.ReadFloat64(s.src) if err != nil { return } result = append(result, measure) } case proto.RespEndOfValue: fmt.Println("RespEndOfValue") return result, nil case proto.RespError: fmt.Println("RespError") return nil, s.onError() default: return nil, fmt.Errorf("unknown reponse code %d", code) } } } func (s *Connection) ListInstantPeriods(req proto.ListInstantPeriodsReq) (_ []proto.InstantPeriod, err error) { arr := []byte{ proto.TypeListInstantPeriods, 0, 0, 0, 0, // metricID 0, 0, 0, 0, // since 0, 0, 0, 0, // until byte(req.GroupBy), req.AggregateFuncs, byte(req.FirstHourOfDay), } bin.PutUint32(arr[1:], req.MetricID) bin.PutUint16(arr[5:], uint16(req.Since.Year)) arr[7] = byte(req.Since.Month) arr[8] = byte(req.Since.Day) bin.PutUint16(arr[9:], uint16(req.Until.Year)) arr[11] = byte(req.Until.Month) arr[12] = byte(req.Until.Day) // req if _, err = s.conn.Write(arr); err != nil { return } // answer var result []proto.InstantPeriod for { var code byte code, err = s.src.ReadByte() if err != nil { return } switch code { case proto.RespPartOfValue: var count int count, err = bin.ReadUint32AsInt(s.src) if err != nil { return } for range count { var p proto.InstantPeriod p.Period, err = bin.ReadUint32(s.src) if err != nil { return } p.Since, err = bin.ReadUint32(s.src) if err != nil { return } p.Until, err = bin.ReadUint32(s.src) if err != nil { return } if (req.AggregateFuncs & qb.AggregateMin) == qb.AggregateMin { p.Min, err = bin.ReadFloat64(s.src) if err != nil { return } } if (req.AggregateFuncs & qb.AggregateMax) == qb.AggregateMax { p.Max, err = bin.ReadFloat64(s.src) if err != nil { return } } if (req.AggregateFuncs & qb.AggregateAvg) == qb.AggregateAvg { p.Avg, err = bin.ReadFloat64(s.src) if err != nil { return } } result = append(result, p) } case proto.RespEndOfValue: return result, nil case proto.RespError: return nil, s.onError() default: return nil, fmt.Errorf("unknown reponse code %d", code) } } } func (s *Connection) ListCumulativePeriods(req proto.ListCumulativePeriodsReq) (_ []proto.CumulativePeriod, err error) { arr := []byte{ proto.TypeListCumulativePeriods, 0, 0, 0, 0, // metricID 0, 0, 0, 0, // since 0, 0, 0, 0, // until byte(req.GroupBy), byte(req.FirstHourOfDay), } bin.PutUint32(arr[1:], req.MetricID) bin.PutUint16(arr[5:], uint16(req.Since.Year)) arr[7] = byte(req.Since.Month) arr[8] = byte(req.Since.Day) bin.PutUint16(arr[9:], uint16(req.Until.Year)) arr[11] = byte(req.Until.Month) arr[12] = byte(req.Until.Day) // req if _, err = s.conn.Write(arr); err != nil { return } // answer var result []proto.CumulativePeriod for { var code byte code, err = s.src.ReadByte() if err != nil { return } switch code { case proto.RespPartOfValue: var count int count, err = bin.ReadUint32AsInt(s.src) if err != nil { return } for range count { var p proto.CumulativePeriod p.Period, err = bin.ReadUint32(s.src) if err != nil { return } p.Since, err = bin.ReadUint32(s.src) if err != nil { return } p.Until, err = bin.ReadUint32(s.src) if err != nil { return } p.EndValue, err = bin.ReadFloat64(s.src) if err != nil { return } p.Total, err = bin.ReadFloat64(s.src) if err != nil { return } result = append(result, p) } case proto.RespEndOfValue: return result, nil case proto.RespError: return nil, s.onError() default: return nil, fmt.Errorf("unknown reponse code %d", code) } } } func (s *Connection) ListCurrentValues(metricIDs []uint32) (_ []proto.CurrentValue, err error) { arr := make([]byte, 3+metricKeySize*len(metricIDs)) arr[0] = proto.TypeListCurrentValues bin.PutUint16(arr[1:], uint16(len(metricIDs))) off := 3 for _, metricID := range metricIDs { bin.PutUint32(arr[off:], metricID) off += metricKeySize } // req if _, err = s.conn.Write(arr); err != nil { return } // answer var result []proto.CurrentValue for { var code byte code, err = s.src.ReadByte() if err != nil { return } switch code { case proto.RespPartOfValue: var count int count, err = bin.ReadUint32AsInt(s.src) if err != nil { return } for range count { var m proto.CurrentValue m.MetricID, err = bin.ReadUint32(s.src) if err != nil { return } m.Timestamp, err = bin.ReadUint32(s.src) if err != nil { return } m.Value, err = bin.ReadFloat64(s.src) if err != nil { return } result = append(result, m) } case proto.RespEndOfValue: return result, nil case proto.RespError: return nil, s.onError() default: return nil, fmt.Errorf("unknown reponse code %d", code) } } } func (s *Connection) DeleteMeasures(req proto.DeleteMeasuresReq) (err error) { arr := []byte{ proto.TypeDeleteMeasures, 0, 0, 0, 0, // metricID 0, 0, 0, 0, // since } bin.PutUint32(arr[1:], req.MetricID) bin.PutUint32(arr[5:], req.Since) // req if _, err := s.conn.Write(arr); err != nil { return err } return s.mustSuccess(s.src) } func (s *Connection) onError() error { errorCode, err := bin.ReadUint16(s.src) if err != nil { return fmt.Errorf("read error code: %s", err) } return Error{ Code: errorCode, Message: proto.ErrorCodeToText(errorCode), } } func (s *Connection) onMaybeError() error { errorCode, err := bin.ReadUint16(s.src) if err != nil { return fmt.Errorf("read error code: %s", err) } if errorCode == proto.ErrNoMetric { return nil } return Error{ Code: errorCode, Message: proto.ErrorCodeToText(errorCode), } }