foooboar
This commit is contained in:
parent
0eb0a3002c
commit
a9c10c8332
15 changed files with 407 additions and 7 deletions
BIN
RA239 Product Manual.pdf
Normal file
BIN
RA239 Product Manual.pdf
Normal file
Binary file not shown.
BIN
RA239.pdf
Normal file
BIN
RA239.pdf
Normal file
Binary file not shown.
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
||||||
|
"github.com/disintegration/imaging"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -18,6 +19,20 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TakePhoto(id string) error {
|
func TakePhoto(id string) error {
|
||||||
cmd := exec.Command(gphotoPath, "--capture-image-and-download", "--filename", fmt.Sprintf("images/original/ticket-%s.jpg", id))
|
filename := fmt.Sprintf("images/original/ticket-%s.jpg", id)
|
||||||
return cmd.Run()
|
cmd := exec.Command(gphotoPath, "--capture-image-and-download", "--filename", filename)
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
img, err := imaging.Open("./" + filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rotated := imaging.Rotate180(img)
|
||||||
|
err = imaging.Save(rotated, filename, imaging.JPEGQuality(95))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,10 @@ type webConfig struct {
|
||||||
Host string
|
Host string
|
||||||
Port int
|
Port int
|
||||||
}
|
}
|
||||||
|
type radarConfig struct {
|
||||||
|
Baud int
|
||||||
|
Port string
|
||||||
|
}
|
||||||
type config struct {
|
type config struct {
|
||||||
BaseUrl string
|
BaseUrl string
|
||||||
|
|
||||||
|
@ -22,6 +26,9 @@ type config struct {
|
||||||
Web webConfig
|
Web webConfig
|
||||||
|
|
||||||
PrinterPort string
|
PrinterPort string
|
||||||
|
|
||||||
|
Radar radarConfig
|
||||||
|
SpeedsignIP string
|
||||||
}
|
}
|
||||||
|
|
||||||
var C config
|
var C config
|
||||||
|
@ -33,7 +40,10 @@ func init() {
|
||||||
flag.String("db", "db.sqlite", "Database String")
|
flag.String("db", "db.sqlite", "Database String")
|
||||||
flag.String("base-url", "http://localhost:3001", "Base URL of the frontend")
|
flag.String("base-url", "http://localhost:3001", "Base URL of the frontend")
|
||||||
flag.String("printer-port", "", "Serial port for printer")
|
flag.String("printer-port", "", "Serial port for printer")
|
||||||
|
flag.String("radar-port", "", "Radar port")
|
||||||
|
flag.Int("radar-baud", 9600, "Radar Baudrate")
|
||||||
flag.Int("mail-smtp-port", 587, "Mail Port")
|
flag.Int("mail-smtp-port", 587, "Mail Port")
|
||||||
|
flag.String("speedsign-ip", "", "192.168.1.143")
|
||||||
_ = viper.BindPFlags(flag.CommandLine)
|
_ = viper.BindPFlags(flag.CommandLine)
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
@ -46,6 +56,7 @@ func init() {
|
||||||
C = config{
|
C = config{
|
||||||
BaseUrl: viper.GetString("base-url"),
|
BaseUrl: viper.GetString("base-url"),
|
||||||
PrinterPort: viper.GetString("printer-port"),
|
PrinterPort: viper.GetString("printer-port"),
|
||||||
|
SpeedsignIP: viper.GetString("speedsign-ip"),
|
||||||
DB: dbConfig{
|
DB: dbConfig{
|
||||||
Type: viper.GetString("db-type"),
|
Type: viper.GetString("db-type"),
|
||||||
ConnectionString: viper.GetString("db"),
|
ConnectionString: viper.GetString("db"),
|
||||||
|
@ -54,5 +65,9 @@ func init() {
|
||||||
Host: viper.GetString("host"),
|
Host: viper.GetString("host"),
|
||||||
Port: viper.GetInt("port"),
|
Port: viper.GetInt("port"),
|
||||||
},
|
},
|
||||||
|
Radar: radarConfig{
|
||||||
|
Baud: viper.GetInt("radar-baud"),
|
||||||
|
Port: viper.GetString("radar-port"),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -54,6 +54,7 @@ require (
|
||||||
go.uber.org/multierr v1.9.0 // indirect
|
go.uber.org/multierr v1.9.0 // indirect
|
||||||
golang.org/x/arch v0.7.0 // indirect
|
golang.org/x/arch v0.7.0 // indirect
|
||||||
golang.org/x/crypto v0.32.0 // indirect
|
golang.org/x/crypto v0.32.0 // indirect
|
||||||
|
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 // indirect
|
||||||
golang.org/x/net v0.33.0 // indirect
|
golang.org/x/net v0.33.0 // indirect
|
||||||
golang.org/x/text v0.21.0 // indirect
|
golang.org/x/text v0.21.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.1 // indirect
|
google.golang.org/protobuf v1.36.1 // indirect
|
||||||
|
@ -65,6 +66,7 @@ require (
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/disintegration/imaging v1.6.2
|
||||||
github.com/gin-contrib/sessions v1.0.2
|
github.com/gin-contrib/sessions v1.0.2
|
||||||
github.com/hennedo/escpos v0.0.1
|
github.com/hennedo/escpos v0.0.1
|
||||||
github.com/jonmol/gphoto2 v1.0.1
|
github.com/jonmol/gphoto2 v1.0.1
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -20,6 +20,8 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||||
|
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||||
|
@ -264,6 +266,8 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y
|
||||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||||
|
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U=
|
||||||
|
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
|
1
main.go
1
main.go
|
@ -3,6 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"git.ctdo.de/henne/blitzer-v2/config"
|
"git.ctdo.de/henne/blitzer-v2/config"
|
||||||
"git.ctdo.de/henne/blitzer-v2/db"
|
"git.ctdo.de/henne/blitzer-v2/db"
|
||||||
|
_ "git.ctdo.de/henne/blitzer-v2/radar"
|
||||||
"git.ctdo.de/henne/blitzer-v2/webserver"
|
"git.ctdo.de/henne/blitzer-v2/webserver"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,67 @@
|
||||||
package radar
|
package radar
|
||||||
|
|
||||||
func init() {
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.ctdo.de/henne/blitzer-v2/camera"
|
||||||
|
"git.ctdo.de/henne/blitzer-v2/config"
|
||||||
|
"git.ctdo.de/henne/blitzer-v2/db"
|
||||||
|
"git.ctdo.de/henne/blitzer-v2/printer"
|
||||||
|
"git.ctdo.de/henne/blitzer-v2/radar_lib"
|
||||||
|
"git.ctdo.de/henne/blitzer-v2/speedsign"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
var r radar_lib.Radar
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
r = radar_lib.New(config.C.Radar.Port, config.C.Radar.Baud)
|
||||||
|
r.SetEventHandler(onEvent)
|
||||||
|
r.SetSpeedHandler(onSpeedEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func onSpeedEvent(speed int) {
|
||||||
|
speedsign.Show(speed)
|
||||||
|
}
|
||||||
|
|
||||||
|
func onEvent(speed int) {
|
||||||
|
speedingTicket := db.SpeedingTicket{
|
||||||
|
Speed: speed,
|
||||||
|
AllowedSpeed: db.GetConfig().TriggerSpeed,
|
||||||
|
}
|
||||||
|
if err := db.DB.Save(&speedingTicket).Error; err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
}
|
||||||
|
if err := camera.TakePhoto(speedingTicket.ID.String()); err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
}
|
||||||
|
speedingTicket.ImagePath = fmt.Sprintf("/images/original/ticket-%s.jpg", speedingTicket.ID)
|
||||||
|
if err := db.DB.Save(&speedingTicket).Error; err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
}
|
||||||
|
printer.PrintTicket(speedingTicket)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetConfig(height int, angle int) {
|
||||||
|
r.SetBaseConfig(height, angle, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetSpeedConfig(speed int, minDistance, maxDistance, minSpeed, maxSpeed, triggerDistance int) {
|
||||||
|
r.SetTargetSpeedConfig(radar_lib.DirectionBidirectional, minDistance*2, maxDistance*2, minSpeed, maxSpeed, speed, radar_lib.OutputLogicMostPlausible)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetBaseConfig() {
|
||||||
|
r.SetCommunicationConfig(radar_lib.PortRS485, radar_lib.Baud115200, radar_lib.OutputTypeNoOutput, radar_lib.OutputTypeNoOutput, radar_lib.OutputTypeNoOutput, 50)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
cfg.RadarAngle = form.RadarAngle
|
||||||
|
cfg.RadarHeight = form.RadarHeight
|
||||||
|
cfg.RadarMaxDistance = form.RadarMaxDistance
|
||||||
|
cfg.RadarMinDistance = form.RadarMinDistance
|
||||||
|
cfg.RadarMaxSpeed = form.RadarMaxSpeed
|
||||||
|
cfg.RadarMinSpeed = form.RadarMinSpeed
|
||||||
|
cfg.RadarWaveform = form.RadarWaveform
|
||||||
|
cfg.TriggerDistance = form.TriggerDistance
|
||||||
|
cfg.TriggerSpeed = form.TriggerSpeed
|
||||||
|
*/
|
||||||
|
|
1
radar_lib/configuration.go
Normal file
1
radar_lib/configuration.go
Normal file
|
@ -0,0 +1 @@
|
||||||
|
package radar_lib
|
199
radar_lib/main.go
Normal file
199
radar_lib/main.go
Normal file
|
@ -0,0 +1,199 @@
|
||||||
|
package radar_lib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"log"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"go.bug.st/serial"
|
||||||
|
)
|
||||||
|
|
||||||
|
type configResponse struct {
|
||||||
|
CommandCode int
|
||||||
|
Success bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type Direction int
|
||||||
|
type OutputLogic int
|
||||||
|
type Port int
|
||||||
|
type Baud int
|
||||||
|
type OutputType int
|
||||||
|
type TriggerMethod int
|
||||||
|
type OperatingMode int
|
||||||
|
|
||||||
|
const (
|
||||||
|
ControlPinRelay = iota
|
||||||
|
ControlPinCtrl1
|
||||||
|
ControlPinCtrl2
|
||||||
|
)
|
||||||
|
const (
|
||||||
|
DirectionBidirectional Direction = iota
|
||||||
|
DirectionIncoming
|
||||||
|
DirectionOutgoing
|
||||||
|
)
|
||||||
|
const (
|
||||||
|
TargetStateInvalid = iota
|
||||||
|
TargetStateMovingInside
|
||||||
|
TargetStateExit
|
||||||
|
TargetStateWithin
|
||||||
|
)
|
||||||
|
const (
|
||||||
|
OutputLogicLargestSpeed = iota
|
||||||
|
OutputLogicStrongestEngergy
|
||||||
|
OutputLogicMostPlausible
|
||||||
|
)
|
||||||
|
const (
|
||||||
|
PortTTL Port = iota
|
||||||
|
PortRS485
|
||||||
|
)
|
||||||
|
const (
|
||||||
|
Baud9600 Baud = iota
|
||||||
|
Baud19200
|
||||||
|
Baud57600
|
||||||
|
Baud115200
|
||||||
|
)
|
||||||
|
const (
|
||||||
|
OutputTypeNoOutput OutputType = iota
|
||||||
|
OutputTypePeriodicOutput
|
||||||
|
OutputTypeValidOutput
|
||||||
|
)
|
||||||
|
const (
|
||||||
|
TriggerMethodHigh TriggerMethod = iota
|
||||||
|
TriggerMethodLow
|
||||||
|
TriggerMethodPositivePulse
|
||||||
|
TriggerMethodNegativePulse
|
||||||
|
)
|
||||||
|
const (
|
||||||
|
OperatingModeNoOutput OperatingMode = iota
|
||||||
|
OperatingModeFirstProtocol
|
||||||
|
OperatingModeSecondProtocol
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
startSequence = []byte{0x43, 0x46}
|
||||||
|
endSequence = []byte{0x0D, 0x0A}
|
||||||
|
)
|
||||||
|
|
||||||
|
func New(port string, baudrate int) Radar {
|
||||||
|
s, err := serial.Open(port, &serial.Mode{
|
||||||
|
BaudRate: baudrate,
|
||||||
|
DataBits: 8,
|
||||||
|
Parity: serial.NoParity,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
r := Radar{
|
||||||
|
port: s,
|
||||||
|
baudrate: baudrate,
|
||||||
|
configResponseChannel: make(chan configResponse),
|
||||||
|
}
|
||||||
|
go r.listenSerial()
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
type Radar struct {
|
||||||
|
port serial.Port
|
||||||
|
lock sync.RWMutex
|
||||||
|
configLock sync.RWMutex
|
||||||
|
configResponseChannel chan (configResponse)
|
||||||
|
baudrate int
|
||||||
|
handler func(int)
|
||||||
|
speedHandler func(int)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Radar) write(data []byte) error {
|
||||||
|
r.lock.Lock()
|
||||||
|
defer r.lock.Unlock()
|
||||||
|
_, err := r.port.Write(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return r.port.Drain()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPinTrigger configures a pin to trigger on the radar when an object is detected in a specific range.
|
||||||
|
// controlPin defines the Pin to trigger, distance is the target distance in 0.5 meters, outputLevel is the voltage in 0.1V steps, direction defines weather to trigger on incoming / outgoing or both
|
||||||
|
func (r *Radar) SetPinTrigger(controlPin int, distance int, outputLevel int, direction Direction) error {
|
||||||
|
data := startSequence
|
||||||
|
data = append(data, 0x00, byte(controlPin), byte(distance), byte(outputLevel), byte(direction))
|
||||||
|
data = append(data, endSequence...)
|
||||||
|
return r.write(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBaseConfig configures the base configuration. height defines the mounted height in 1cm steps, angle defines the vertical angle in degrees, waveformConfig is used to differentiate 2 different radars
|
||||||
|
func (r *Radar) SetBaseConfig(height int, angle int, waveformConfig int) error {
|
||||||
|
data := startSequence
|
||||||
|
data = append(data, 0x01, byte(height), byte(angle), byte(waveformConfig), 0x00)
|
||||||
|
data = append(data, endSequence...)
|
||||||
|
return r.write(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetEventConfig can configure up to 8 events that will trigger a response from the radar. distances are in 0.5m, speeds are in km/h.
|
||||||
|
func (r *Radar) SetEventConfig(eventNumber, minDistance, maxDistance, minSpeed, maxSpeed, direction, state int) error {
|
||||||
|
if eventNumber < 1 || eventNumber > 1 {
|
||||||
|
return errors.New("eventNumber needs to be between 1 and 8")
|
||||||
|
}
|
||||||
|
data := startSequence
|
||||||
|
data = append(data, 0x02, byte(eventNumber), byte(minDistance), byte(maxDistance), byte(minSpeed), byte(maxSpeed), byte(direction), byte(state))
|
||||||
|
data = append(data, endSequence...)
|
||||||
|
return r.write(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Radar) SetTargetSpeedConfig(direction Direction, minDistance, maxDistance, minSpeed, maxSpeed, speeding int, outputLogic OutputLogic) error {
|
||||||
|
data := startSequence
|
||||||
|
data = append(data, 0x03, byte(direction), byte(minDistance), byte(maxDistance), byte(minSpeed), byte(maxSpeed), byte(speeding), byte(outputLogic))
|
||||||
|
data = append(data, endSequence...)
|
||||||
|
return r.write(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Radar) SetCommunicationConfig(port Port, baud Baud, speedOutput, targetOutput, triggerOutput OutputType, communicationPeriod int) error {
|
||||||
|
data := startSequence
|
||||||
|
outputProtocol := 0
|
||||||
|
switch speedOutput {
|
||||||
|
case OutputTypePeriodicOutput:
|
||||||
|
outputProtocol |= (1 << 4)
|
||||||
|
case OutputTypeValidOutput:
|
||||||
|
outputProtocol |= (1 << 5)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch targetOutput {
|
||||||
|
case OutputTypePeriodicOutput:
|
||||||
|
outputProtocol |= (1 << 2)
|
||||||
|
case OutputTypeValidOutput:
|
||||||
|
outputProtocol |= (1 << 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch triggerOutput {
|
||||||
|
case OutputTypePeriodicOutput:
|
||||||
|
outputProtocol |= (1 << 0)
|
||||||
|
case OutputTypeValidOutput:
|
||||||
|
outputProtocol |= (1 << 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
data = append(data, 0x03, byte(port), byte(baud), byte(outputProtocol), byte(communicationPeriod))
|
||||||
|
data = append(data, endSequence...)
|
||||||
|
return r.write(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Radar) SetControlPinConfig(controlPin int, triggerMethod TriggerMethod, outputLevel int, triggerEvent int, span int) error {
|
||||||
|
data := startSequence
|
||||||
|
data = append(data, 0x05, byte(controlPin), byte(triggerMethod), byte(outputLevel), byte(triggerEvent), byte(span))
|
||||||
|
data = append(data, endSequence...)
|
||||||
|
return r.write(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Radar) SetLampBoardOutput(operatingMode OperatingMode, luminance int, span int) error {
|
||||||
|
data := startSequence
|
||||||
|
data = append(data, 0x06, byte(operatingMode), byte(luminance), byte(span))
|
||||||
|
data = append(data, endSequence...)
|
||||||
|
return r.write(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Radar) SetEventHandler(handler func(int)) {
|
||||||
|
r.handler = handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Radar) SetSpeedHandler(handler func(int)) {
|
||||||
|
r.speedHandler = handler
|
||||||
|
}
|
77
radar_lib/reader.go
Normal file
77
radar_lib/reader.go
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
package radar_lib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (r *Radar) listenSerial() {
|
||||||
|
rcvBuf := make([]byte, 100)
|
||||||
|
lastIndex := 0
|
||||||
|
for {
|
||||||
|
buf := make([]byte, 100)
|
||||||
|
n, err := r.port.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if n == 0 {
|
||||||
|
log.Println("\nEOF")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
rcvBuf[lastIndex] = buf[i]
|
||||||
|
if i > 1 && buf[i-1] == 0x0D && buf[i] == 0x0A {
|
||||||
|
r.decodeInput(rcvBuf[0 : lastIndex+1])
|
||||||
|
lastIndex = 0
|
||||||
|
} else {
|
||||||
|
lastIndex++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Radar) decodeInput(buf []byte) {
|
||||||
|
// trigger message
|
||||||
|
if len(buf) > 2 && buf[0] == 0x56 && buf[1] == 0x50 {
|
||||||
|
log.Printf("Radar Trigger Message Event %d", buf[2])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// config response
|
||||||
|
if len(buf) > 2 && buf[0] == 0x46 && buf[1] == 0x43 {
|
||||||
|
s := "FAIL"
|
||||||
|
if buf[3] == 0 {
|
||||||
|
s = "SUCCESS"
|
||||||
|
}
|
||||||
|
log.Printf("%s response: Code: %d", s, buf[2])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// speed information
|
||||||
|
if len(buf) > 2 && buf[0] == 0x56 && buf[1] == 0x52 {
|
||||||
|
dir := "incoming"
|
||||||
|
if buf[2]&(1<<(6)) != 0 {
|
||||||
|
dir = "outgoing"
|
||||||
|
}
|
||||||
|
overspeed := "no"
|
||||||
|
if buf[2]&(1<<(3)) == 1 {
|
||||||
|
overspeed = "yes"
|
||||||
|
}
|
||||||
|
valid := "no"
|
||||||
|
if buf[2]&(1<<(0)) == 1 {
|
||||||
|
valid = "yes"
|
||||||
|
}
|
||||||
|
log.Printf("Speed: %dkm/h (%s, Over: %s, Valid: %s)", buf[3], dir, overspeed, valid)
|
||||||
|
r.speedHandler(int(buf[3]))
|
||||||
|
if overspeed == "yes" && valid == "yes" {
|
||||||
|
|
||||||
|
r.handler(int(buf[3]))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// skip this for now
|
||||||
|
if len(buf) > 2 && buf[0] == 0x56 && buf[1] == 0x51 {
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("%# x\n", buf)
|
||||||
|
}
|
18
speedsign/main.go
Normal file
18
speedsign/main.go
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package speedsign
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"git.ctdo.de/henne/blitzer-v2/config"
|
||||||
|
"git.ctdo.de/henne/blitzer-v2/db"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Show(speed int) {
|
||||||
|
if speed > db.GetConfig().TriggerSpeed {
|
||||||
|
_, _ = http.Get("http://" + config.C.SpeedsignIP + "/api/color/FF0000")
|
||||||
|
} else {
|
||||||
|
_, _ = http.Get("http://" + config.C.SpeedsignIP + "/api/color/00FF00")
|
||||||
|
}
|
||||||
|
_, _ = http.Get(fmt.Sprintf("http://%s/api/number/%d", config.C.SpeedsignIP, speed))
|
||||||
|
}
|
|
@ -1,7 +1,9 @@
|
||||||
package webserver
|
package webserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
"git.ctdo.de/henne/blitzer-v2/db"
|
"git.ctdo.de/henne/blitzer-v2/db"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
@ -9,6 +11,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func HandleDelete(ctx *gin.Context) {
|
func HandleDelete(ctx *gin.Context) {
|
||||||
|
filename := fmt.Sprintf("images/original/ticket-%s.jpg", ctx.Param("id"))
|
||||||
|
os.Remove(filename)
|
||||||
if err := db.DB.Where("id = ?", ctx.Param("id")).Delete(&db.SpeedingTicket{}).Error; err != nil {
|
if err := db.DB.Where("id = ?", ctx.Param("id")).Delete(&db.SpeedingTicket{}).Error; err != nil {
|
||||||
logrus.Error(err)
|
logrus.Error(err)
|
||||||
ctx.String(500, "internal server error")
|
ctx.String(500, "internal server error")
|
||||||
|
|
|
@ -2,6 +2,7 @@ package webserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.ctdo.de/henne/blitzer-v2/db"
|
"git.ctdo.de/henne/blitzer-v2/db"
|
||||||
|
"git.ctdo.de/henne/blitzer-v2/radar"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
@ -36,4 +37,5 @@ func HandleSetupSave(ctx *gin.Context) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Error(err)
|
logrus.Error(err)
|
||||||
}
|
}
|
||||||
|
radar.SetConfig(cfg.RadarHeight, cfg.RadarAngle)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,11 +13,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func HandleTest(ctx *gin.Context) {
|
func HandleTest(ctx *gin.Context) {
|
||||||
id := rand.Intn(500)
|
//id := rand.Intn(500)
|
||||||
speedingTicket := db.SpeedingTicket{
|
speedingTicket := db.SpeedingTicket{
|
||||||
Speed: 20 + rand.Intn(50),
|
Speed: 20 + rand.Intn(50),
|
||||||
ImagePath: fmt.Sprintf("https://picsum.photos/id/%d/300/200", id),
|
//ImagePath: fmt.Sprintf("https://picsum.photos/id/%d/300/200", id),
|
||||||
KIImagePath: fmt.Sprintf("https://picsum.photos/id/%d/300/200", id),
|
//KIImagePath: fmt.Sprintf("https://picsum.photos/id/%d/300/200", id),
|
||||||
AllowedSpeed: db.GetConfig().TriggerSpeed,
|
AllowedSpeed: db.GetConfig().TriggerSpeed,
|
||||||
}
|
}
|
||||||
if err := db.DB.Save(&speedingTicket).Error; err != nil {
|
if err := db.DB.Save(&speedingTicket).Error; err != nil {
|
||||||
|
|
Loading…
Add table
Reference in a new issue