mirror of
https://github.com/ClusterCockpit/cc-units.git
synced 2024-12-25 04:59:05 +01:00
Use maps to store all unit related data
This commit is contained in:
parent
ddf955ff23
commit
ddcfc07745
@ -1,9 +1,8 @@
|
||||
# ccUnits - A unit system for ClusterCockpit
|
||||
# cc-units - A unit system for ClusterCockpit
|
||||
|
||||
When working with metrics, the problem comes up that they may use different unit name but have the same unit in fact. There are a lot of real world examples like 'kB' and 'Kbyte'. In CC Metric Collector, the Collectors read data from different sources which may use different units or the programmer specifies a unit for a metric by hand. The ccUnits system is not comparable with the SI unit system. If you are looking for a package for the SI units, see [here](https://pkg.go.dev/github.com/gurre/si).
|
||||
When working with metrics, the problem comes up that they may use different unit name but have the same unit in fact. There are a lot of real world examples like 'kB' and 'Kbyte'. In [cc-metric-collector](https://github.com/ClusterCockpit/cc-metric-collector), the collectors read data from different sources which may use different units or the programmer specifies a unit for a metric by hand. The cc-units system is not comparable with the SI unit system. If you are looking for a package for the SI units, see [here](https://pkg.go.dev/github.com/gurre/si).
|
||||
|
||||
In order to enable unit comparison and conversion, the ccUnits package provides some helpers:
|
||||
There are basically two important functions:
|
||||
```go
|
||||
NewUnit(unit string) Unit // create a new unit from some string like 'GHz', 'Mbyte' or 'kevents/s'
|
||||
func GetUnitUnitFactor(in Unit, out Unit) (func(value float64) float64, error) // Get conversion function between two units
|
||||
|
248
ccUnitMeasure.go
248
ccUnitMeasure.go
@ -12,7 +12,7 @@ const (
|
||||
TemperatureC
|
||||
TemperatureF
|
||||
Rotation
|
||||
Hertz
|
||||
Frequency
|
||||
Time
|
||||
Watt
|
||||
Joule
|
||||
@ -22,171 +22,113 @@ const (
|
||||
Events
|
||||
)
|
||||
|
||||
type MeasureData struct {
|
||||
Long string
|
||||
Short string
|
||||
Regex string
|
||||
}
|
||||
|
||||
// Different names and regex used for input and output
|
||||
var InvalidMeasureLong string = "Invalid"
|
||||
var InvalidMeasureShort string = "inval"
|
||||
var MeasuresMap map[Measure]MeasureData = map[Measure]MeasureData{
|
||||
Bytes: {
|
||||
Long: "byte",
|
||||
Short: "B",
|
||||
Regex: "^([bB][yY]?[tT]?[eE]?[sS]?)",
|
||||
},
|
||||
Flops: {
|
||||
Long: "Flops",
|
||||
Short: "Flops",
|
||||
Regex: "^([fF][lL]?[oO]?[pP]?[sS]?)",
|
||||
},
|
||||
Percentage: {
|
||||
Long: "Percent",
|
||||
Short: "%",
|
||||
Regex: "^(%|[pP]ercent)",
|
||||
},
|
||||
TemperatureC: {
|
||||
Long: "DegreeC",
|
||||
Short: "degC",
|
||||
Regex: "^(deg[Cc]|°[cC])",
|
||||
},
|
||||
TemperatureF: {
|
||||
Long: "DegreeF",
|
||||
Short: "degF",
|
||||
Regex: "^(deg[fF]|°[fF])",
|
||||
},
|
||||
Rotation: {
|
||||
Long: "RPM",
|
||||
Short: "RPM",
|
||||
Regex: "^([rR][pP][mM])",
|
||||
},
|
||||
Frequency: {
|
||||
Long: "Hertz",
|
||||
Short: "Hz",
|
||||
Regex: "^([hH][eE]?[rR]?[tT]?[zZ])",
|
||||
},
|
||||
Time: {
|
||||
Long: "Seconds",
|
||||
Short: "s",
|
||||
Regex: "^([sS][eE]?[cC]?[oO]?[nN]?[dD]?[sS]?)",
|
||||
},
|
||||
Cycles: {
|
||||
Long: "Cycles",
|
||||
Short: "cyc",
|
||||
Regex: "^([cC][yY][cC]?[lL]?[eE]?[sS]?)",
|
||||
},
|
||||
Watt: {
|
||||
Long: "Watts",
|
||||
Short: "W",
|
||||
Regex: "^([wW][aA]?[tT]?[tT]?[sS]?)",
|
||||
},
|
||||
Joule: {
|
||||
Long: "Joules",
|
||||
Short: "J",
|
||||
Regex: "^([jJ][oO]?[uU]?[lL]?[eE]?[sS]?)",
|
||||
},
|
||||
Requests: {
|
||||
Long: "Requests",
|
||||
Short: "requests",
|
||||
Regex: "^([rR][eE][qQ][uU]?[eE]?[sS]?[tT]?[sS]?)",
|
||||
},
|
||||
Packets: {
|
||||
Long: "Packets",
|
||||
Short: "packets",
|
||||
Regex: "^([pP][aA]?[cC]?[kK][eE]?[tT][sS]?)",
|
||||
},
|
||||
Events: {
|
||||
Long: "Events",
|
||||
Short: "events",
|
||||
Regex: "^([eE][vV]?[eE]?[nN][tT][sS]?)",
|
||||
},
|
||||
}
|
||||
|
||||
// String returns the long string for the measure like 'Percent' or 'Seconds'
|
||||
func (m *Measure) String() string {
|
||||
switch *m {
|
||||
case Bytes:
|
||||
return "byte"
|
||||
case Flops:
|
||||
return "Flops"
|
||||
case Percentage:
|
||||
return "Percent"
|
||||
case TemperatureC:
|
||||
return "DegreeC"
|
||||
case TemperatureF:
|
||||
return "DegreeF"
|
||||
case Rotation:
|
||||
return "RPM"
|
||||
case Hertz:
|
||||
return "Hertz"
|
||||
case Time:
|
||||
return "Seconds"
|
||||
case Watt:
|
||||
return "Watts"
|
||||
case Joule:
|
||||
return "Joules"
|
||||
case Cycles:
|
||||
return "Cycles"
|
||||
case Requests:
|
||||
return "Requests"
|
||||
case Packets:
|
||||
return "Packets"
|
||||
case Events:
|
||||
return "Events"
|
||||
case InvalidMeasure:
|
||||
return "Invalid"
|
||||
default:
|
||||
return "Unknown"
|
||||
if data, ok := MeasuresMap[*m]; ok {
|
||||
return data.Long
|
||||
}
|
||||
return InvalidMeasureLong
|
||||
}
|
||||
|
||||
// Short returns the short string for the measure like 'B' (Bytes), 's' (Time) or 'W' (Watt). Is is recommened to use Short() over String().
|
||||
func (m *Measure) Short() string {
|
||||
switch *m {
|
||||
case Bytes:
|
||||
return "B"
|
||||
case Flops:
|
||||
return "Flops"
|
||||
case Percentage:
|
||||
return "Percent"
|
||||
case TemperatureC:
|
||||
return "degC"
|
||||
case TemperatureF:
|
||||
return "degF"
|
||||
case Rotation:
|
||||
return "RPM"
|
||||
case Hertz:
|
||||
return "Hz"
|
||||
case Time:
|
||||
return "s"
|
||||
case Watt:
|
||||
return "W"
|
||||
case Joule:
|
||||
return "J"
|
||||
case Cycles:
|
||||
return "cyc"
|
||||
case Requests:
|
||||
return "requests"
|
||||
case Packets:
|
||||
return "packets"
|
||||
case Events:
|
||||
return "events"
|
||||
case InvalidMeasure:
|
||||
return "Invalid"
|
||||
default:
|
||||
return "Unknown"
|
||||
if data, ok := MeasuresMap[*m]; ok {
|
||||
return data.Short
|
||||
}
|
||||
return InvalidMeasureShort
|
||||
}
|
||||
|
||||
const bytesRegexStr = `^([bB][yY]?[tT]?[eE]?[sS]?)`
|
||||
const flopsRegexStr = `^([fF][lL]?[oO]?[pP]?[sS]?)`
|
||||
const percentRegexStr = `^(%|[pP]ercent)`
|
||||
const degreeCRegexStr = `^(deg[Cc]|°[cC])`
|
||||
const degreeFRegexStr = `^(deg[fF]|°[fF])`
|
||||
const rpmRegexStr = `^([rR][pP][mM])`
|
||||
const hertzRegexStr = `^([hH][eE]?[rR]?[tT]?[zZ])`
|
||||
const timeRegexStr = `^([sS][eE]?[cC]?[oO]?[nN]?[dD]?[sS]?)`
|
||||
const wattRegexStr = `^([wW][aA]?[tT]?[tT]?[sS]?)`
|
||||
const jouleRegexStr = `^([jJ][oO]?[uU]?[lL]?[eE]?[sS]?)`
|
||||
const cyclesRegexStr = `^([cC][yY][cC]?[lL]?[eE]?[sS]?)`
|
||||
const requestsRegexStr = `^([rR][eE][qQ][uU]?[eE]?[sS]?[tT]?[sS]?)`
|
||||
const packetsRegexStr = `^([pP][aA]?[cC]?[kK][eE]?[tT][sS]?)`
|
||||
const eventsRegexStr = `^([eE][vV]?[eE]?[nN][tT][sS]?)`
|
||||
|
||||
var bytesRegex = regexp.MustCompile(bytesRegexStr)
|
||||
var flopsRegex = regexp.MustCompile(flopsRegexStr)
|
||||
var percentRegex = regexp.MustCompile(percentRegexStr)
|
||||
var degreeCRegex = regexp.MustCompile(degreeCRegexStr)
|
||||
var degreeFRegex = regexp.MustCompile(degreeFRegexStr)
|
||||
var rpmRegex = regexp.MustCompile(rpmRegexStr)
|
||||
var hertzRegex = regexp.MustCompile(hertzRegexStr)
|
||||
var timeRegex = regexp.MustCompile(timeRegexStr)
|
||||
var wattRegex = regexp.MustCompile(wattRegexStr)
|
||||
var jouleRegex = regexp.MustCompile(jouleRegexStr)
|
||||
var cyclesRegex = regexp.MustCompile(cyclesRegexStr)
|
||||
var requestsRegex = regexp.MustCompile(requestsRegexStr)
|
||||
var packetsRegex = regexp.MustCompile(packetsRegexStr)
|
||||
var eventsRegex = regexp.MustCompile(eventsRegexStr)
|
||||
|
||||
// NewMeasure creates a new measure out of a string representing a measure like 'Bytes', 'Flops' and 'precent'.
|
||||
// It uses regular expressions for matching.
|
||||
func NewMeasure(unit string) Measure {
|
||||
var match []string
|
||||
match = bytesRegex.FindStringSubmatch(unit)
|
||||
if match != nil {
|
||||
return Bytes
|
||||
}
|
||||
match = flopsRegex.FindStringSubmatch(unit)
|
||||
if match != nil {
|
||||
return Flops
|
||||
}
|
||||
match = percentRegex.FindStringSubmatch(unit)
|
||||
if match != nil {
|
||||
return Percentage
|
||||
}
|
||||
match = degreeCRegex.FindStringSubmatch(unit)
|
||||
if match != nil {
|
||||
return TemperatureC
|
||||
}
|
||||
match = degreeFRegex.FindStringSubmatch(unit)
|
||||
if match != nil {
|
||||
return TemperatureF
|
||||
}
|
||||
match = rpmRegex.FindStringSubmatch(unit)
|
||||
if match != nil {
|
||||
return Rotation
|
||||
}
|
||||
match = hertzRegex.FindStringSubmatch(unit)
|
||||
if match != nil {
|
||||
return Hertz
|
||||
}
|
||||
match = timeRegex.FindStringSubmatch(unit)
|
||||
if match != nil {
|
||||
return Time
|
||||
}
|
||||
match = cyclesRegex.FindStringSubmatch(unit)
|
||||
if match != nil {
|
||||
return Cycles
|
||||
}
|
||||
match = wattRegex.FindStringSubmatch(unit)
|
||||
if match != nil {
|
||||
return Watt
|
||||
}
|
||||
match = jouleRegex.FindStringSubmatch(unit)
|
||||
if match != nil {
|
||||
return Joule
|
||||
}
|
||||
match = requestsRegex.FindStringSubmatch(unit)
|
||||
if match != nil {
|
||||
return Requests
|
||||
}
|
||||
match = packetsRegex.FindStringSubmatch(unit)
|
||||
if match != nil {
|
||||
return Packets
|
||||
}
|
||||
match = eventsRegex.FindStringSubmatch(unit)
|
||||
if match != nil {
|
||||
return Events
|
||||
for m, data := range MeasuresMap {
|
||||
regex := regexp.MustCompile(data.Regex)
|
||||
match := regex.FindStringSubmatch(unit)
|
||||
if match != nil {
|
||||
return m
|
||||
}
|
||||
}
|
||||
return InvalidMeasure
|
||||
}
|
||||
|
264
ccUnitPrefix.go
264
ccUnitPrefix.go
@ -1,6 +1,8 @@
|
||||
package ccunits
|
||||
|
||||
import "regexp"
|
||||
import (
|
||||
"regexp"
|
||||
)
|
||||
|
||||
type Prefix float64
|
||||
|
||||
@ -27,148 +29,146 @@ const (
|
||||
Zebi = 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024
|
||||
Yobi = 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024
|
||||
)
|
||||
const prefixRegexStr = `^([kKmMgGtTpP]?[i]?)(.*)`
|
||||
const PrefixUnitSplitRegexStr = `^([kKmMgGtTpPeEzZyY]?[i]?)(.*)`
|
||||
|
||||
var prefixRegex = regexp.MustCompile(prefixRegexStr)
|
||||
var prefixUnitSplitRegex = regexp.MustCompile(PrefixUnitSplitRegexStr)
|
||||
|
||||
type PrefixData struct {
|
||||
Long string
|
||||
Short string
|
||||
Regex string
|
||||
}
|
||||
|
||||
// Different names and regex used for input and output
|
||||
var InvalidPrefixLong string = "Invalid"
|
||||
var InvalidPrefixShort string = "inval"
|
||||
var PrefixDataMap map[Prefix]PrefixData = map[Prefix]PrefixData{
|
||||
Base: {
|
||||
Long: "",
|
||||
Short: "",
|
||||
Regex: "^$",
|
||||
},
|
||||
Kilo: {
|
||||
Long: "Kilo",
|
||||
Short: "K",
|
||||
Regex: "^[kK]$",
|
||||
},
|
||||
Mega: {
|
||||
Long: "Mega",
|
||||
Short: "M",
|
||||
Regex: "^[M]$",
|
||||
},
|
||||
Giga: {
|
||||
Long: "Giga",
|
||||
Short: "G",
|
||||
Regex: "^[gG]$",
|
||||
},
|
||||
Tera: {
|
||||
Long: "Tera",
|
||||
Short: "T",
|
||||
Regex: "^[tT]$",
|
||||
},
|
||||
Peta: {
|
||||
Long: "Peta",
|
||||
Short: "P",
|
||||
Regex: "^[pP]$",
|
||||
},
|
||||
Exa: {
|
||||
Long: "Exa",
|
||||
Short: "E",
|
||||
Regex: "^[eE]$",
|
||||
},
|
||||
Zetta: {
|
||||
Long: "Zetta",
|
||||
Short: "Z",
|
||||
Regex: "^[zZ]$",
|
||||
},
|
||||
Yotta: {
|
||||
Long: "Yotta",
|
||||
Short: "Y",
|
||||
Regex: "^[yY]$",
|
||||
},
|
||||
Milli: {
|
||||
Long: "Milli",
|
||||
Short: "m",
|
||||
Regex: "^[m]$",
|
||||
},
|
||||
Micro: {
|
||||
Long: "Micro",
|
||||
Short: "u",
|
||||
Regex: "^[u]$",
|
||||
},
|
||||
Nano: {
|
||||
Long: "Nano",
|
||||
Short: "n",
|
||||
Regex: "^[n]$",
|
||||
},
|
||||
Kibi: {
|
||||
Long: "Kibi",
|
||||
Short: "Ki",
|
||||
Regex: "^[kK][i]$",
|
||||
},
|
||||
Mebi: {
|
||||
Long: "Mebi",
|
||||
Short: "Mi",
|
||||
Regex: "^[M][i]$",
|
||||
},
|
||||
Gibi: {
|
||||
Long: "Gibi",
|
||||
Short: "Gi",
|
||||
Regex: "^[gG][i]$",
|
||||
},
|
||||
Tebi: {
|
||||
Long: "Tebi",
|
||||
Short: "Ti",
|
||||
Regex: "^[tT][i]$",
|
||||
},
|
||||
Pebi: {
|
||||
Long: "Pebi",
|
||||
Short: "Pi",
|
||||
Regex: "^[pP][i]$",
|
||||
},
|
||||
Exbi: {
|
||||
Long: "Exbi",
|
||||
Short: "Ei",
|
||||
Regex: "^[eE][i]$",
|
||||
},
|
||||
Zebi: {
|
||||
Long: "Zebi",
|
||||
Short: "Zi",
|
||||
Regex: "^[zZ][i]$",
|
||||
},
|
||||
Yobi: {
|
||||
Long: "Yobi",
|
||||
Short: "Yi",
|
||||
Regex: "^[yY][i]$",
|
||||
},
|
||||
}
|
||||
|
||||
// String returns the long string for the prefix like 'Kilo' or 'Mega'
|
||||
func (s *Prefix) String() string {
|
||||
switch *s {
|
||||
case InvalidPrefix:
|
||||
return "Inval"
|
||||
case Base:
|
||||
return ""
|
||||
case Kilo:
|
||||
return "Kilo"
|
||||
case Mega:
|
||||
return "Mega"
|
||||
case Giga:
|
||||
return "Giga"
|
||||
case Tera:
|
||||
return "Tera"
|
||||
case Peta:
|
||||
return "Peta"
|
||||
case Exa:
|
||||
return "Exa"
|
||||
case Zetta:
|
||||
return "Zetta"
|
||||
case Yotta:
|
||||
return "Yotta"
|
||||
case Milli:
|
||||
return "Milli"
|
||||
case Micro:
|
||||
return "Micro"
|
||||
case Nano:
|
||||
return "Nano"
|
||||
case Kibi:
|
||||
return "Kibi"
|
||||
case Mebi:
|
||||
return "Mebi"
|
||||
case Gibi:
|
||||
return "Gibi"
|
||||
case Tebi:
|
||||
return "Tebi"
|
||||
default:
|
||||
return "Unkn"
|
||||
func (p *Prefix) String() string {
|
||||
if data, ok := PrefixDataMap[*p]; ok {
|
||||
return data.Long
|
||||
}
|
||||
return InvalidMeasureLong
|
||||
}
|
||||
|
||||
// Prefix returns the short string for the prefix like 'K', 'M' or 'G'. Is is recommened to use Prefix() over String().
|
||||
func (s *Prefix) Prefix() string {
|
||||
switch *s {
|
||||
case InvalidPrefix:
|
||||
return "<inval>"
|
||||
case Base:
|
||||
return ""
|
||||
case Kilo:
|
||||
return "K"
|
||||
case Mega:
|
||||
return "M"
|
||||
case Giga:
|
||||
return "G"
|
||||
case Tera:
|
||||
return "T"
|
||||
case Peta:
|
||||
return "P"
|
||||
case Exa:
|
||||
return "E"
|
||||
case Zetta:
|
||||
return "Z"
|
||||
case Yotta:
|
||||
return "Y"
|
||||
case Milli:
|
||||
return "m"
|
||||
case Micro:
|
||||
return "u"
|
||||
case Nano:
|
||||
return "n"
|
||||
case Kibi:
|
||||
return "Ki"
|
||||
case Mebi:
|
||||
return "Mi"
|
||||
case Gibi:
|
||||
return "Gi"
|
||||
case Tebi:
|
||||
return "Ti"
|
||||
default:
|
||||
return "<unkn>"
|
||||
func (p *Prefix) Prefix() string {
|
||||
if data, ok := PrefixDataMap[*p]; ok {
|
||||
return data.Short
|
||||
}
|
||||
return InvalidMeasureShort
|
||||
}
|
||||
|
||||
// NewPrefix creates a new prefix out of a string representing a unit like 'k', 'K', 'M' or 'G'.
|
||||
func NewPrefix(prefix string) Prefix {
|
||||
switch prefix {
|
||||
case "k":
|
||||
return Kilo
|
||||
case "K":
|
||||
return Kilo
|
||||
case "m":
|
||||
return Milli
|
||||
case "M":
|
||||
return Mega
|
||||
case "g":
|
||||
return Giga
|
||||
case "G":
|
||||
return Giga
|
||||
case "t":
|
||||
return Tera
|
||||
case "T":
|
||||
return Tera
|
||||
case "p":
|
||||
return Peta
|
||||
case "P":
|
||||
return Peta
|
||||
case "e":
|
||||
return Exa
|
||||
case "E":
|
||||
return Exa
|
||||
case "z":
|
||||
return Zetta
|
||||
case "Z":
|
||||
return Zetta
|
||||
case "y":
|
||||
return Yotta
|
||||
case "Y":
|
||||
return Yotta
|
||||
case "u":
|
||||
return Micro
|
||||
case "n":
|
||||
return Nano
|
||||
case "ki":
|
||||
return Kibi
|
||||
case "Ki":
|
||||
return Kibi
|
||||
case "Mi":
|
||||
return Mebi
|
||||
case "gi":
|
||||
return Gibi
|
||||
case "Gi":
|
||||
return Gibi
|
||||
case "Ti":
|
||||
return Tebi
|
||||
case "":
|
||||
return Base
|
||||
default:
|
||||
return InvalidPrefix
|
||||
for p, data := range PrefixDataMap {
|
||||
regex := regexp.MustCompile(data.Regex)
|
||||
match := regex.FindStringSubmatch(prefix)
|
||||
if match != nil {
|
||||
return p
|
||||
}
|
||||
}
|
||||
return InvalidPrefix
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
// Unit system for cluster monitoring metrics like bytes, flops and events
|
||||
package ccunits
|
||||
|
||||
import (
|
||||
@ -204,7 +205,7 @@ func NewUnit(unitStr string) Unit {
|
||||
measure: InvalidMeasure,
|
||||
divMeasure: InvalidMeasure,
|
||||
}
|
||||
matches := prefixRegex.FindStringSubmatch(unitStr)
|
||||
matches := prefixUnitSplitRegex.FindStringSubmatch(unitStr)
|
||||
if len(matches) > 2 {
|
||||
pre := NewPrefix(matches[1])
|
||||
measures := strings.Split(matches[2], "/")
|
||||
|
Loading…
Reference in New Issue
Block a user