package models import ( "database/sql/driver" "errors" "gorm.io/gorm" "strconv" "strings" "time" ) type SensorType uint const ( Temperature SensorType = iota + 1 // to allow gorm: not null Humidity Pressure Gas CO2 ) func (s SensorType) IsValid() bool { switch s { case Temperature, Humidity, Pressure, Gas, CO2: return true default: return false } } type Position struct { X int `json:"x"` Y int `json:"y"` NotNull bool `json:"-"` // to allow gorm: not null with x/y = 0 } type Sensor struct { ID uint `json:"id" gorm:"primaryKey"` CreatedAt time.Time `json:"-"` UpdatedAt time.Time `json:"-"` DeletedAt gorm.DeletedAt `json:"-" gorm:"index"` Name string `json:"name" gorm:"uniqueIndex;not null;default:null"` SensorType SensorType `json:"sensor_type" gorm:"not null;default:null"` Position Position `json:"position" gorm:"not null;default:null"` } type Measurement struct { ID uint `json:"id" gorm:"primaryKey"` CreatedAt time.Time `json:"time"` // TODO: change to datetime2 UpdatedAt time.Time `json:"-"` // TODO: remove DeletedAt gorm.DeletedAt `json:"-" gorm:"index"` // TODO: remove SensorID uint `json:"sensor_id" gorm:"<-:create;not null;default:null"` Sensor Sensor `json:"-"` Value float64 `json:"value" gorm:"<-:create;default:null"` // TODO on migrate set column NOT NULL } func (s Sensor) IsValid() bool { return s.SensorType.IsValid() } func (p Position) Value() (driver.Value, error) { return strconv.FormatInt(int64(p.X), 10) + "." + strconv.FormatInt(int64(p.Y), 10), nil } func (p *Position) Scan(value interface{}) error { p.X = 0 p.Y = 0 p.NotNull = true positionString, isString := value.(string) if !isString { return errors.New("invalid scan value") } positionArray := strings.Split(positionString, ".") if len(positionArray) != 2 { return errors.New("invalid string value") } var err error var position int64 position, err = strconv.ParseInt(positionArray[0], 10, 32) if err != nil { return errors.New("invalid x value") } p.X = int(position) position, err = strconv.ParseInt(positionArray[1], 10, 32) if err != nil { return errors.New("invalid y value") } p.Y = int(position) return nil } // http -v POST localhost:8080/sensors name="sensor_$RANDOM" sensor_type:=1 position:='{"x":1, "y":2}' // http -v POST localhost:8080/sensors name="sensor_$RANDOM" sensor_type:=2 func (m Model) AddSensor(sensor Sensor) (uint, error) { sensor.ID = 0 sensor.Position.NotNull = true if !sensor.SensorType.IsValid() { return 0, errors.New("invalid sensor type") } result := m.app.Db().Create(&sensor) return sensor.ID, result.Error } func (m Model) UpdateSensor(sensor Sensor, txSensor Sensor, tx *gorm.DB) (uint, error) { sensor.ID = txSensor.ID sensor.Position.NotNull = true if !sensor.SensorType.IsValid() { return 0, errors.New("invalid sensor type") } result := tx.Updates(&sensor) return sensor.ID, result.Error } // http -v POST localhost:8080/measurements sensor_id:=3 value:=32 // http -v GET localhost:8080/measurements/3 func (m Model) AddMeasurement(measurement Measurement) (uint, error) { measurement.ID = 0 err := m.app.Db().First(&Sensor{}, measurement.SensorID).Error if err != nil { return 0, err } result := m.app.Db().Create(&measurement) return measurement.ID, result.Error }