소스 검색

军信后端

xu 1 년 전
커밋
fb95072a52
32개의 변경된 파일2439개의 추가작업 그리고 0개의 파일을 삭제
  1. 8 0
      .idea/.gitignore
  2. 1 0
      .idea/.name
  3. 9 0
      .idea/jx_ld_server.iml
  4. 8 0
      .idea/modules.xml
  5. 6 0
      .idea/vcs.xml
  6. 90 0
      api/device.go
  7. 2 0
      build.bat
  8. 9 0
      dao/connection.go
  9. 16 0
      dao/device.go
  10. 12 0
      dao/device_loop.go
  11. 8 0
      dao/region.go
  12. 40 0
      go.mod
  13. 94 0
      go.sum
  14. 200 0
      initialize/myData.go
  15. 19 0
      main.go
  16. 74 0
      modbus/instruction.go
  17. 34 0
      modbus/operate.go
  18. 156 0
      modbus/queue.go
  19. 28 0
      model/common.go
  20. 63 0
      model/response.go
  21. 24 0
      router/router.go
  22. 172 0
      service/cron.go
  23. 84 0
      service/device.go
  24. 197 0
      static/data.json
  25. 12 0
      static/devicePath.json
  26. 35 0
      test1/ceshi_test.go
  27. 45 0
      utils/crc.go
  28. 157 0
      utils/myTool.go
  29. 90 0
      utils/mytime.go
  30. 279 0
      utils/now.go
  31. 447 0
      utils/sunrisesunset.go
  32. 20 0
      work/MyJob.go

+ 8 - 0
.idea/.gitignore

@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml

+ 1 - 0
.idea/.name

@@ -0,0 +1 @@
+jx_ld_server

+ 9 - 0
.idea/jx_ld_server.iml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="WEB_MODULE" version="4">
+  <component name="Go" enabled="true" />
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$" />
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>

+ 8 - 0
.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/jx_ld_server.iml" filepath="$PROJECT_DIR$/.idea/jx_ld_server.iml" />
+    </modules>
+  </component>
+</project>

+ 6 - 0
.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$" vcs="Git" />
+  </component>
+</project>

+ 90 - 0
api/device.go

@@ -0,0 +1,90 @@
+package api
+
+import (
+	"github.com/gin-gonic/gin"
+	"server/dao"
+	"server/model"
+	"server/service"
+	"server/utils"
+)
+
+func LoadData(c *gin.Context) {
+	data, err := utils.LoadData()
+	if err != nil {
+		model.FailWithMessage(err.Error(), c)
+		return
+	}
+	model.OkWithData(data, c)
+}
+
+func SaveDevice(c *gin.Context) {
+	var regions []dao.Region
+	err := c.ShouldBindJSON(&regions)
+	if err != nil {
+		model.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = service.SaveData(regions)
+	if err != nil {
+		model.FailWithMessage(err.Error(), c)
+		return
+	}
+	model.Ok(c)
+}
+
+// DeviceLoopSwitch 指定设备回路进行开关灯操作
+func DeviceLoopSwitch(c *gin.Context) {
+	var deviceLoop dao.DeviceLoop
+	err := c.ShouldBindJSON(&deviceLoop)
+	if err != nil {
+		model.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = service.DeviceLoopSwitch(deviceLoop)
+	if err != nil {
+		model.FailWithMessage(err.Error(), c)
+		return
+	}
+	model.Ok(c)
+}
+
+// DeviceSwitch 指定设备进行开关灯操作
+func DeviceSwitch(c *gin.Context) {
+	var req model.DeviceRequest
+	err := c.ShouldBindJSON(&req)
+	if err != nil {
+		model.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = service.DeviceSwitch(req)
+	if err != nil {
+		model.FailWithMessage(err.Error(), c)
+		return
+	}
+	model.Ok(c)
+}
+
+// BatchDeviceSwitch 设备批量开关
+func BatchDeviceSwitch(c *gin.Context) {
+	var req model.DevicesRequest
+	err := c.ShouldBindJSON(&req)
+	if err != nil {
+		model.FailWithMessage(err.Error(), c)
+		return
+	}
+	err = service.DeviceBatchSwitch(req)
+	if err != nil {
+		model.FailWithMessage(err.Error(), c)
+		return
+	}
+	model.Ok(c)
+}
+
+func GetOnlineDevice(c *gin.Context) {
+	total, online := service.GetOnlineDevice()
+
+	model.OkWithDetailed(gin.H{
+		"total":  total,
+		"online": online,
+	}, "ok", c)
+}

+ 2 - 0
build.bat

@@ -0,0 +1,2 @@
+%此脚本需要在命令窗口执行,不能直接运行%
+go env -w CGO_ENABLED=0 GOOS=linux GOARCH=amd64 && go build -o build/jx_ld_server ./

+ 9 - 0
dao/connection.go

@@ -0,0 +1,9 @@
+package dao
+
+import "github.com/goburrow/modbus"
+
+type Connection struct {
+	Path string        `json:"path"`
+	ID   string        `json:"id"`
+	Tcp  modbus.Client `json:"tcp"`
+}

+ 16 - 0
dao/device.go

@@ -0,0 +1,16 @@
+package dao
+
+import "time"
+
+type Device struct {
+	ID          int          `json:"id"`
+	Sn          string       `json:"sn"`
+	RegionId    int          `json:"regionId"`
+	Name        string       `json:"name"`
+	Genre       string       `json:"genre"`
+	State       int          `json:"state"`
+	IsSun       bool         `json:"isSun"`
+	LoopNumber  int          `json:"loopNumber"`
+	OnlineTime  time.Time    `json:"onlineTime"`
+	DeviceLoops []DeviceLoop `json:"deviceLoops"`
+}

+ 12 - 0
dao/device_loop.go

@@ -0,0 +1,12 @@
+package dao
+
+type DeviceLoop struct {
+	ID                    int    `json:"id"`
+	DeviceId              string `json:"deviceId"`
+	Name                  string `json:"name"`
+	State                 int    `json:"state"`
+	TimeCondition1OnTime  string `json:"timeCondition1OnTime"`
+	TimeCondition1OffTime string `json:"timeCondition1OffTime"`
+	TimeCondition2OnTime  string `json:"timeCondition2OnTime"`
+	TimeCondition2OffTime string `json:"timeCondition2OffTime"`
+}

+ 8 - 0
dao/region.go

@@ -0,0 +1,8 @@
+package dao
+
+type Region struct {
+	ID        int      `json:"id"`
+	Name      string   `json:"name"`
+	SubRegion []Region `json:"subRegion"`
+	Devices   []Device `json:"devices"`
+}

+ 40 - 0
go.mod

@@ -0,0 +1,40 @@
+module server
+
+go 1.23.0
+
+require (
+	github.com/gin-gonic/gin v1.10.0
+	github.com/goburrow/modbus v0.1.0
+	github.com/robfig/cron v1.2.0
+)
+
+require (
+	github.com/bytedance/sonic v1.12.3 // indirect
+	github.com/bytedance/sonic/loader v0.2.1 // indirect
+	github.com/cloudwego/base64x v0.1.4 // indirect
+	github.com/cloudwego/iasm v0.2.0 // indirect
+	github.com/gabriel-vasile/mimetype v1.4.6 // indirect
+	github.com/gin-contrib/sse v0.1.0 // indirect
+	github.com/go-playground/locales v0.14.1 // indirect
+	github.com/go-playground/universal-translator v0.18.1 // indirect
+	github.com/go-playground/validator/v10 v10.22.1 // indirect
+	github.com/goburrow/serial v0.1.0 // indirect
+	github.com/goccy/go-json v0.10.3 // indirect
+	github.com/json-iterator/go v1.1.12 // indirect
+	github.com/klauspost/cpuid/v2 v2.2.8 // indirect
+	github.com/leodido/go-urn v1.4.0 // indirect
+	github.com/mattn/go-isatty v0.0.20 // indirect
+	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+	github.com/modern-go/reflect2 v1.0.2 // indirect
+	github.com/pelletier/go-toml/v2 v2.2.3 // indirect
+	github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07 // indirect
+	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
+	github.com/ugorji/go/codec v1.2.12 // indirect
+	golang.org/x/arch v0.11.0 // indirect
+	golang.org/x/crypto v0.28.0 // indirect
+	golang.org/x/net v0.30.0 // indirect
+	golang.org/x/sys v0.26.0 // indirect
+	golang.org/x/text v0.19.0 // indirect
+	google.golang.org/protobuf v1.35.1 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
+)

+ 94 - 0
go.sum

@@ -0,0 +1,94 @@
+github.com/bytedance/sonic v1.12.3 h1:W2MGa7RCU1QTeYRTPE3+88mVC0yXmsRQRChiyVocVjU=
+github.com/bytedance/sonic v1.12.3/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
+github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
+github.com/bytedance/sonic/loader v0.2.1 h1:1GgorWTqf12TA8mma4DDSbaQigE2wOgQo7iCjjJv3+E=
+github.com/bytedance/sonic/loader v0.2.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
+github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
+github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
+github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
+github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
+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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc=
+github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc=
+github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
+github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
+github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
+github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
+github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
+github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
+github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
+github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
+github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
+github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
+github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
+github.com/goburrow/modbus v0.1.0 h1:DejRZY73nEM6+bt5JSP6IsFolJ9dVcqxsYbpLbeW/ro=
+github.com/goburrow/modbus v0.1.0/go.mod h1:Kx552D5rLIS8E7TyUwQ/UdHEqvX5T8tyiGBTlzMcZBg=
+github.com/goburrow/serial v0.1.0 h1:v2T1SQa/dlUqQiYIT8+Cu7YolfqAi3K96UmhwYyuSrA=
+github.com/goburrow/serial v0.1.0/go.mod h1:sAiqG0nRVswsm1C97xsttiYCzSLBmUZ/VSlVLZJ8haA=
+github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
+github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
+github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
+github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
+github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
+github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
+github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
+github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
+github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
+github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07 h1:UyzmZLoiDWMRywV4DUYb9Fbt8uiOSooupjTq10vpvnU=
+github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
+github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
+github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
+github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
+github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
+golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4=
+golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
+golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
+golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
+golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
+golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
+golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
+golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
+google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=

+ 200 - 0
initialize/myData.go

@@ -0,0 +1,200 @@
+package initialize
+
+import (
+	"encoding/hex"
+	"fmt"
+	"log"
+	"net"
+	"runtime"
+	"runtime/debug"
+	"server/modbus"
+	"server/model"
+	"server/service"
+	"server/utils"
+	"strconv"
+	"sync"
+	"time"
+)
+
+type ModbusHandler struct {
+	queue *modbus.MlQueue
+}
+
+var _handlerOnce sync.Once
+var _handlerSingle *ModbusHandler
+
+func GetHandler() *ModbusHandler {
+	_handlerOnce.Do(func() {
+		_handlerSingle = &ModbusHandler{
+			queue: modbus.NewQueue(10000),
+		}
+	})
+	return _handlerSingle
+}
+
+func InitInductanceTCP() {
+	lis, err := net.Listen("tcp", ":60001")
+	if err != nil {
+		fmt.Println(err)
+	} else {
+		fmt.Println(lis)
+		model.InductanceTCP = lis
+		fmt.Println("inductanceTCP启动成功")
+	}
+	go StartInductanceTCP()
+}
+
+func StartInductanceTCP() {
+	fmt.Println("进入StartInductanceTCP")
+	handler := GetHandler()
+	for {
+		log.Println("进入InductanceTCP循环")
+		conn, err := model.InductanceTCP.Accept()
+		if err != nil {
+			log.Println(err)
+		}
+		remoteAddr := conn.RemoteAddr().String()
+		log.Printf("lis Accept conn = %s\n", remoteAddr)
+
+		model.Mutex.Lock()
+		if _, exists := model.ConnectionMap[remoteAddr]; exists {
+			log.Printf("Connection from %s already exists, closing new connection\n", remoteAddr)
+			conn.Close()
+			model.Mutex.Unlock()
+			continue
+		}
+		deviceId, err := GetDeviceId(conn)
+		if err != nil {
+			log.Println("Error getting device ID:", err)
+			conn.Close()
+			model.Mutex.Unlock()
+			continue
+		}
+		model.ConnectionMap[deviceId] = conn
+		model.Mutex.Unlock()
+		//用新的协程处理新的连接
+		go handler.ReadAndHandle(conn, deviceId)
+		go handler.Handler()
+	}
+}
+
+func GetDeviceId(conn net.Conn) (id string, err error) {
+	// 发送 Modbus RTU 帧
+	//FE 04 03 EE 00 08 85 B2
+	data, err := utils.WriteAndReadDevice(modbus.ReadDeviceId, conn, 9, 2)
+	if err != nil {
+		return
+	}
+	return string(data), err
+}
+
+func (o *ModbusHandler) ReadAndHandle(conn net.Conn, deviceId string) {
+	for {
+		buffer := make([]byte, 1024)
+		n, err := conn.Read(buffer)
+		if err != nil {
+			log.Println("Error reading from connection:", err)
+			return
+		}
+		queueData := model.QueueData{
+			Id:    deviceId,
+			Value: buffer[:n],
+		}
+		ok, cnt := o.queue.Put(&queueData)
+		if ok {
+			continue
+		} else {
+			fmt.Printf("HandlerData:查询队列失败,队列消息数量:%d", cnt)
+			runtime.Gosched()
+		}
+	}
+}
+
+func (o *ModbusHandler) Handler() interface{} {
+	defer func() {
+		if err := recover(); err != nil {
+			go GetHandler().Handler()
+			fmt.Printf("MqttHandler.Handler:发生异常:%s", string(debug.Stack()))
+		}
+	}()
+	for {
+		msg, ok, quantity := o.queue.Get()
+		if !ok {
+			time.Sleep(10 * time.Millisecond)
+			continue
+		} else if quantity > 1000 {
+			fmt.Printf("数据队列累积过多,请注意优化,当前队列条数:%d", quantity)
+		}
+		queueData, ok := msg.(*model.QueueData)
+		if !ok {
+			fmt.Println("Type assertion failed: msg is not of type model.QueueDat")
+			return nil
+		}
+		// 信息处理返回
+		parseData(queueData)
+		// 对数据进行修改
+
+	}
+}
+
+func parseData(data *model.QueueData) {
+	reg, dev, err := utils.GetDataByDeviceId(data.Id)
+	if err != nil {
+		fmt.Println("Error getting register and device:", err)
+		return
+	}
+	toString := hex.EncodeToString(data.Value)
+	switch toString[0:2] {
+	case "fe":
+		switch toString[4:8] { // 开关灯
+		case "0000", "0001", "0002", "0003", "0004", "0005", "0006", "0007":
+			if toString[8:12] == "0000" {
+				for i, loop := range dev.DeviceLoops {
+					loop.State = 0
+					dev.DeviceLoops[i] = loop
+				}
+			} else if toString[8:12] == "ff00" {
+				for i, loop := range dev.DeviceLoops {
+					loop.State = 1
+					dev.DeviceLoops[i] = loop
+				}
+			}
+		}
+	case "11":
+		switch toString[2:6] {
+		case "0336":
+			batteryVoltage, _ := strconv.ParseInt(toString[6:10], 16, 64)
+			//电池
+			if float64(batteryVoltage)/100 < 5 {
+				data1 := modbus.DeviceSwitch(8, 1)
+				utils.WriteDevice(data1, model.ConnectionMap[data.Id])
+			} else {
+				data1 := modbus.DeviceSwitch(8, 0)
+				utils.WriteDevice(data1, model.ConnectionMap[data.Id])
+			}
+		}
+	}
+
+	switch toString[0:4] {
+	case "4c43":
+		bytes, err := hex.DecodeString(toString[4:])
+		if err != nil {
+			fmt.Println("Error decoding bytes:", err)
+			return
+		}
+		fmt.Println(string(bytes) + time.Now().String())
+		for i, device := range reg.Devices {
+			if device.Sn == string(bytes) {
+				reg.Devices[i].State = 1
+				reg.Devices[i].OnlineTime = time.Now()
+			}
+		}
+		regions, err := utils.SaveRegionOnData(reg)
+		err = service.SaveData(regions)
+		if err != nil {
+			fmt.Println(err)
+			return
+		}
+	}
+
+}

+ 19 - 0
main.go

@@ -0,0 +1,19 @@
+package main
+
+import (
+	"fmt"
+	"server/initialize"
+	"server/router"
+	"server/work"
+)
+
+func main() {
+	initRouter := router.InitRouter()
+
+	work.MyJob()
+	initialize.InitInductanceTCP()
+	err := initRouter.Run(":8222")
+	if err != nil {
+		fmt.Println("服务器启动失败!")
+	}
+}

+ 74 - 0
modbus/instruction.go

@@ -0,0 +1,74 @@
+package modbus
+
+import "server/utils"
+
+var (
+	ReadDeviceId           = []byte{0xFE, 0x04, 0x03, 0xEE, 0x00, 0x08, 0x85, 0xB2} //读设备id
+	ReadDeviceIdAndDOAndDI = []byte{0xFE, 0x04, 0x03, 0xE8, 0x00, 0x14, 0x64, 0x7A} //读设备id、do、di
+)
+
+func DeviceLoopSwitch(loop int, state int) (data []byte) {
+	data = append(data, 0xFE)
+	data = append(data, 0x05)
+	data = append(data, 0x00)
+	data = append(data, byte(loop-1))
+	if state == 1 {
+		data = append(data, 0xFF)
+	} else {
+		data = append(data, 0x00)
+	}
+	data = append(data, 0x00)
+
+	crc16 := utils.CRC16(data)
+	data = append(data, byte(crc16&0xFF))
+	data = append(data, byte(crc16>>8))
+	return
+}
+
+func DeviceSwitch(number int, state int) (data []byte) {
+	data = append(data, 0xFE)
+	data = append(data, 0x0F)
+	data = append(data, 0x00)
+	data = append(data, 0x00)
+	data = append(data, 0x00)
+	data = append(data, byte(number))
+	data = append(data, 0x01)
+	if state == 1 {
+		data = append(data, 0xFF)
+	} else {
+		data = append(data, 0x00)
+	}
+	crc16 := utils.CRC16(data)
+	data = append(data, byte(crc16&0xFF))
+	data = append(data, byte(crc16>>8))
+	return
+}
+
+func SolarEnergyData() (data []byte) {
+	data = append(data, 0x11)
+	data = append(data, 0x03)
+	data = append(data, 0x00)
+	data = append(data, 0x04)
+	data = append(data, 0x00)
+	data = append(data, 0x1b)
+	crc16 := utils.CRC16(data)
+	data = append(data, byte(crc16&0xFF))
+	data = append(data, byte(crc16>>8))
+	return
+}
+
+func Xie() (data []byte) {
+	data = append(data, 0x01)
+	data = append(data, 0x10)
+	data = append(data, 0x00)
+	data = append(data, 0x00)
+	data = append(data, 0x00)
+	data = append(data, 0x01)
+	data = append(data, 0x02)
+	data = append(data, 0x00)
+	data = append(data, 0x11)
+	crc16 := utils.CRC16(data)
+	data = append(data, byte(crc16&0xFF))
+	data = append(data, byte(crc16>>8))
+	return
+}

+ 34 - 0
modbus/operate.go

@@ -0,0 +1,34 @@
+package modbus
+
+import (
+	"server/model"
+	"server/utils"
+	"time"
+)
+
+// GetSunPowerInfo 获取太阳能信息
+func GetSunPowerInfo() error {
+	for id, conn := range model.ConnectionMap {
+		_, dev, err := utils.GetDataByDeviceId(id)
+		if dev.IsSun {
+			err = utils.WriteDevice(SolarEnergyData(), conn)
+			if err != nil {
+				return err
+			}
+		}
+	}
+	return nil
+}
+
+// DealWithOffline 处理离线
+func DealWithOffline() {
+	regions, _ := utils.LoadData()
+	for i, region := range regions {
+		for i2, device := range region.Devices {
+			if time.Now().After(device.OnlineTime.Add(4 * time.Minute)) {
+				regions[i].Devices[i2].State = 0
+			}
+		}
+	}
+	utils.SaveData("static/data.json", regions)
+}

+ 156 - 0
modbus/queue.go

@@ -0,0 +1,156 @@
+package modbus
+
+import (
+	"fmt"
+	"runtime"
+	"sync/atomic"
+)
+
+type mlCache struct {
+	putNo uint32
+	getNo uint32
+	value interface{}
+}
+
+type MlQueue struct {
+	capacity uint32
+	capMod   uint32
+	putPos   uint32
+	getPos   uint32
+	cache    []mlCache
+}
+
+func NewQueue(capacity uint32) *MlQueue {
+	q := new(MlQueue)
+	q.capacity = minQuantity(capacity)
+	q.capMod = q.capacity - 1
+	q.putPos = 0
+	q.getPos = 0
+	q.cache = make([]mlCache, q.capacity)
+	for i := range q.cache {
+		cache := &q.cache[i]
+		cache.getNo = uint32(i)
+		cache.putNo = uint32(i)
+	}
+	cache := &q.cache[0]
+	cache.getNo = q.capacity
+	cache.putNo = q.capacity
+	return q
+}
+
+func (q *MlQueue) String() string {
+	getPos := atomic.LoadUint32(&q.getPos)
+	putPos := atomic.LoadUint32(&q.putPos)
+	return fmt.Sprintf("Queue{capacity: %v, capMod: %v, putPos: %v, getPos: %v}",
+		q.capacity, q.capMod, putPos, getPos)
+}
+
+func (q *MlQueue) Capacity() uint32 {
+	return q.capacity
+}
+
+func (q *MlQueue) Quantity() uint32 {
+	var putPos, getPos uint32
+	var quantity uint32
+	getPos = atomic.LoadUint32(&q.getPos)
+	putPos = atomic.LoadUint32(&q.putPos)
+
+	if putPos >= getPos {
+		quantity = putPos - getPos
+	} else {
+		quantity = q.capMod + (putPos - getPos)
+	}
+
+	return quantity
+}
+
+func (q *MlQueue) Put(val interface{}) (ok bool, quantity uint32) {
+	var putPos, putPosNew, getPos, posCnt uint32
+	var cache *mlCache
+	capMod := q.capMod
+
+	getPos = atomic.LoadUint32(&q.getPos)
+	putPos = atomic.LoadUint32(&q.putPos)
+
+	if putPos >= getPos {
+		posCnt = putPos - getPos
+	} else {
+		posCnt = capMod + (putPos - getPos)
+	}
+
+	if posCnt >= capMod-1 {
+		runtime.Gosched()
+		return false, posCnt
+	}
+
+	putPosNew = putPos + 1
+	if !atomic.CompareAndSwapUint32(&q.putPos, putPos, putPosNew) {
+		runtime.Gosched()
+		return false, posCnt
+	}
+
+	cache = &q.cache[putPosNew&capMod]
+
+	for {
+		getNo := atomic.LoadUint32(&cache.getNo)
+		putNo := atomic.LoadUint32(&cache.putNo)
+		if putPosNew == putNo && getNo == putNo {
+			cache.value = val
+			atomic.AddUint32(&cache.putNo, q.capacity)
+			return true, posCnt + 1
+		} else {
+			runtime.Gosched()
+		}
+	}
+}
+
+func (q *MlQueue) Get() (val interface{}, ok bool, quantity uint32) {
+	var putPos, getPos, getPosNew, posCnt uint32
+	var cache *mlCache
+	capMod := q.capMod
+
+	putPos = atomic.LoadUint32(&q.putPos)
+	getPos = atomic.LoadUint32(&q.getPos)
+
+	if putPos >= getPos {
+		posCnt = putPos - getPos
+	} else {
+		posCnt = capMod + (putPos - getPos)
+	}
+
+	if posCnt < 1 {
+		runtime.Gosched()
+		return nil, false, posCnt
+	}
+
+	getPosNew = getPos + 1
+	if !atomic.CompareAndSwapUint32(&q.getPos, getPos, getPosNew) {
+		runtime.Gosched()
+		return nil, false, posCnt
+	}
+
+	cache = &q.cache[getPosNew&capMod]
+
+	for {
+		getNo := atomic.LoadUint32(&cache.getNo)
+		putNo := atomic.LoadUint32(&cache.putNo)
+		if getPosNew == getNo && getNo == putNo-q.capacity {
+			val = cache.value
+			atomic.AddUint32(&cache.getNo, q.capacity)
+			return val, true, posCnt - 1
+		} else {
+			runtime.Gosched()
+		}
+	}
+}
+
+func minQuantity(v uint32) uint32 {
+	v--
+	v |= v >> 1
+	v |= v >> 2
+	v |= v >> 4
+	v |= v >> 8
+	v |= v >> 16
+	v++
+	return v
+}

+ 28 - 0
model/common.go

@@ -0,0 +1,28 @@
+package model
+
+import (
+	"net"
+	"server/dao"
+	"sync"
+)
+
+var (
+	InductanceTCP net.Listener
+	ConnectionMap = make(map[string]net.Conn)
+	Mutex         sync.Mutex
+)
+
+type QueueData struct {
+	Id    string `json:"id"`
+	Value []byte `json:"value"`
+}
+
+type DeviceRequest struct {
+	Device dao.Device `json:"device"`
+	State  int        `json:"state"`
+}
+
+type DevicesRequest struct {
+	Devices []dao.Device `json:"devices"`
+	State   int          `json:"state"`
+}

+ 63 - 0
model/response.go

@@ -0,0 +1,63 @@
+package model
+
+import (
+	"net/http"
+
+	"github.com/gin-gonic/gin"
+)
+
+type Response struct {
+	Code int         `json:"code"`
+	Data interface{} `json:"data"`
+	Msg  string      `json:"msg"`
+}
+
+const (
+	ERROR   = 7
+	SUCCESS = 0
+)
+
+func Result(code int, data interface{}, msg string, c *gin.Context) {
+	// 开始时间
+	c.JSON(http.StatusOK, Response{
+		code,
+		data,
+		msg,
+	})
+}
+
+func Ok(c *gin.Context) {
+	Result(SUCCESS, map[string]interface{}{}, "操作成功", c)
+}
+
+func OkWithMessage(message string, c *gin.Context) {
+	Result(SUCCESS, map[string]interface{}{}, message, c)
+}
+
+func OkWithData(data interface{}, c *gin.Context) {
+	Result(SUCCESS, data, "查询成功", c)
+}
+
+func OkWithDetailed(data interface{}, message string, c *gin.Context) {
+	Result(SUCCESS, data, message, c)
+}
+
+func Fail(c *gin.Context) {
+	Result(ERROR, map[string]interface{}{}, "操作失败", c)
+}
+
+func FailWithMessage(message string, c *gin.Context) {
+	Result(ERROR, map[string]interface{}{}, message, c)
+}
+
+func NoAuth(message string, c *gin.Context) {
+	c.JSON(http.StatusUnauthorized, Response{
+		7,
+		nil,
+		message,
+	})
+}
+
+func FailWithDetailed(data interface{}, message string, c *gin.Context) {
+	Result(ERROR, data, message, c)
+}

+ 24 - 0
router/router.go

@@ -0,0 +1,24 @@
+package router
+
+import (
+	"github.com/gin-gonic/gin"
+	"server/api"
+)
+
+// InitRouter 注册路由
+func InitRouter() *gin.Engine {
+	router := gin.Default()
+
+	//设置路由组
+	Group := router.Group("api")
+
+	{
+		Group.GET("get", api.LoadData)
+		Group.POST("saveDevice", api.SaveDevice)
+		Group.POST("deviceLoopSwitch", api.DeviceLoopSwitch)
+		Group.POST("deviceSwitch", api.DeviceSwitch)
+		Group.POST("deviceBatchSwitch", api.BatchDeviceSwitch)
+		Group.GET("getOnlineDevice", api.GetOnlineDevice)
+	}
+	return router
+}

+ 172 - 0
service/cron.go

@@ -0,0 +1,172 @@
+package service
+
+import (
+	"fmt"
+	"log"
+	"server/modbus"
+	"server/model"
+	"server/utils"
+	"time"
+)
+
+type Cron struct {
+}
+
+// RelayOnOffTimeTask 定时开关  前提是 设备在线 否则失效
+func (c Cron) RelayOnOffTimeTask() {
+	current := time.Now().Format("15:04")
+	devices, err := utils.GetOnlineDevices()
+	if err != nil {
+		_ = fmt.Errorf("LoadData err = %s", err.Error())
+		return
+	}
+	relayMap := make(map[string]map[int]int)
+	for _, d := range devices {
+		if d.State == 0 {
+			continue
+		}
+		tmp, isExist := relayMap[d.Sn]
+		if !isExist { // 回路id  回路状态
+			tmp = make(map[int]int)
+		}
+		relays := d.DeviceLoops
+		if err != nil {
+			fmt.Errorf(err.Error())
+			continue
+		}
+		rcTime, rlTime, err := utils.SunriseSunsetForChina(28.23, 113.05)
+		if err != nil {
+			log.Printf("SunriseSunsetForChina err = %s", err.Error())
+			rcTime = "06:00"
+			rlTime = "18:00"
+		}
+		for _, r := range relays {
+			//日出日落时间
+			if r.TimeCondition1OnTime == "日出" {
+				r.TimeCondition1OnTime = rcTime
+			} else if r.TimeCondition1OnTime == "日落" {
+				r.TimeCondition1OnTime = rlTime
+			}
+			if r.TimeCondition1OffTime == "日出" {
+				r.TimeCondition1OffTime = rcTime
+			} else if r.TimeCondition1OffTime == "日落" {
+				r.TimeCondition1OffTime = rlTime
+			}
+			if r.TimeCondition2OnTime == "日出" {
+				r.TimeCondition2OnTime = rcTime
+			} else if r.TimeCondition2OnTime == "日落" {
+				r.TimeCondition2OnTime = rlTime
+			}
+			if r.TimeCondition2OffTime == "日出" {
+				r.TimeCondition2OffTime = rcTime
+			} else if r.TimeCondition2OffTime == "日落" {
+				r.TimeCondition2OffTime = rlTime
+			}
+			isTiming := 0 // 用来判断该设备的回路 是否定时
+
+			//控制回路  时间判断 是否关闭 当前时间是否等与规定时间 规定时间到规定时间+10分钟
+			//因为1分钟执行一次,所以会多次执行,则新加判断 如果数据库中状态和回路状态相同则不执行下去
+
+			layout := "15:04"
+
+			rTimeCondition1OnTime, _ := time.Parse(layout, r.TimeCondition1OnTime)
+			rTimeCondition1OffTime, _ := time.Parse(layout, r.TimeCondition1OffTime)
+			rTimeCondition2OnTime, _ := time.Parse(layout, r.TimeCondition2OnTime)
+			rTimeCondition2OffTime, _ := time.Parse(layout, r.TimeCondition2OffTime)
+			rCurrent, _ := time.Parse(layout, current)
+
+			if r.TimeCondition1OnTime != "关闭" &&
+				(r.TimeCondition1OnTime == current ||
+					(rCurrent.Before(rTimeCondition1OnTime.Add(11*time.Minute)) && rCurrent.After(rTimeCondition1OnTime))) {
+
+				tmp[r.ID] = 1
+				isTiming = 1
+			}
+			if r.TimeCondition1OffTime != "关闭" && (r.TimeCondition1OffTime == current ||
+				(rCurrent.Before(rTimeCondition1OffTime.Add(11*time.Minute)) && rCurrent.After(rTimeCondition1OffTime))) {
+				tmp[r.ID] = 0
+				isTiming = 1
+			}
+			if r.TimeCondition2OnTime != "关闭" && (r.TimeCondition2OnTime == current ||
+				(rCurrent.Before(rTimeCondition2OnTime.Add(11*time.Minute)) && rCurrent.After(rTimeCondition2OnTime))) {
+				tmp[r.ID] = 1
+				isTiming = 1
+			}
+			if r.TimeCondition2OffTime != "关闭" && (r.TimeCondition2OffTime == current ||
+				(rCurrent.Before(rTimeCondition2OffTime.Add(11*time.Minute)) && rCurrent.After(rTimeCondition2OffTime))) {
+				tmp[r.ID] = 0
+				isTiming = 1
+			}
+			if isTiming == 1 {
+				relayMap[d.Sn] = tmp
+			}
+		}
+	}
+	fmt.Println(relayMap)
+	for key, value := range relayMap {
+
+		reg, dev, err := utils.GetDataByDeviceId(key)
+		if err != nil {
+			fmt.Printf("GetDataByDeviceId err = %s\n", err.Error())
+		}
+
+		for i, i2 := range value {
+			data := modbus.DeviceLoopSwitch(i, i2)
+			if model.ConnectionMap[key] == nil {
+				fmt.Errorf("设备连接丢失")
+			}
+			err := utils.WriteDevice(data, model.ConnectionMap[key])
+			time.Sleep(100 * time.Millisecond)
+			if err != nil {
+				fmt.Printf("WriteDevice err = %s\n", err.Error())
+			}
+
+			for j, loop := range dev.DeviceLoops {
+				if loop.ID == i {
+					dev.DeviceLoops[j].State = i2
+				}
+			}
+		}
+
+		for i, device := range reg.Devices {
+			if device.Sn == dev.Sn {
+				reg.Devices[i] = dev
+			}
+		}
+
+		data, err := utils.SaveRegionOnData(reg)
+		if err != nil {
+			fmt.Printf("SaveRegionOnData err = %s\n", err.Error())
+		}
+		err = SaveData(data)
+		if err != nil {
+			fmt.Printf("SaveData err = %s\n", err.Error())
+		}
+	}
+}
+
+// UpdateDeviceRelayStatus 更新设备状态
+//func (c Cron) UpdateDeviceRelayStatus() {
+//	service := DeviceService{}
+//	devices := service.GetDevicesByModule1AndModule3()
+//	for _, d := range devices {
+//		//新2路开关,因固件与8路相同 这里要特殊处理下这里
+//		if *d.Module == 3 {
+//			d.CircuitNum = 8
+//		}
+//		Query(d.DeviceSn, d.CircuitNum)
+//	}
+//}
+//
+//// StatisticsDevice 设备统计
+//func (c Cron) StatisticsDevice() {
+//	statisticsService := DeviceOnlineDailyStatisticsService{}
+//	onlines := statisticsService.GetTotalDeviceOnline()
+//	for _, online := range onlines {
+//		err := statisticsService.UpdateStatisticsDevice(online)
+//		if err != nil {
+//			logger.Logger.Errorf("StatisticsDevice err = %s", err.Error())
+//		}
+//	}
+//}
+//

+ 84 - 0
service/device.go

@@ -0,0 +1,84 @@
+package service
+
+import (
+	"fmt"
+	"server/dao"
+	"server/modbus"
+	"server/model"
+	"server/utils"
+)
+
+func GetDeviceList() {
+	fmt.Println("Get device list.")
+}
+
+func SaveData(data interface{}) error {
+	err := utils.SaveData("static/data.json", data)
+	if err != nil {
+		return err
+	}
+	return err
+}
+
+func DeviceLoopSwitch(deviceLoop dao.DeviceLoop) error {
+	data := modbus.DeviceLoopSwitch(deviceLoop.ID, deviceLoop.State)
+	if model.ConnectionMap[deviceLoop.DeviceId] == nil {
+		return fmt.Errorf("设备连接丢失")
+	}
+	_, dev, _ := utils.GetDataByDeviceId(deviceLoop.DeviceId)
+	if dev.State == 0 {
+		return fmt.Errorf("设备离线无法操作")
+	}
+
+	err := utils.WriteDevice(data, model.ConnectionMap[deviceLoop.DeviceId])
+	if err != nil {
+		fmt.Printf("Write device error: %v\n", err)
+		return err
+	}
+	return nil
+}
+func DeviceSwitch(req model.DeviceRequest) error {
+	data := modbus.DeviceSwitch(req.Device.LoopNumber, req.State)
+	if model.ConnectionMap[req.Device.Sn] == nil {
+		return fmt.Errorf("设备连接丢失")
+	}
+	err := utils.WriteDevice(data, model.ConnectionMap[req.Device.Sn])
+	if err != nil {
+		fmt.Printf("Write device error: %v\n", err)
+		return err
+	}
+	return nil
+}
+
+func DeviceBatchSwitch(req model.DevicesRequest) error {
+	for _, device := range req.Devices {
+		if device.State == 0 {
+			continue
+		}
+		data := modbus.DeviceSwitch(device.LoopNumber, req.State)
+		if model.ConnectionMap[device.Sn] == nil {
+			return fmt.Errorf("设备连接丢失")
+		}
+		err := utils.WriteDevice(data, model.ConnectionMap[device.Sn])
+		if err != nil {
+			fmt.Printf("Write device error: %v\n", err)
+			return err
+		}
+	}
+	return nil
+}
+
+// GetOnlineDevice 获取在线设备 总共设备  在线率
+func GetOnlineDevice() (total, online int) {
+	devices, err := utils.GetOnlineDevices()
+	if err != nil {
+		fmt.Printf("GetOnlineDevices err = %s\n", err.Error())
+		return
+	}
+	allDevices, err := utils.GetAllDevices()
+	if err != nil {
+		fmt.Printf("GetAllDevices err = %s\n", err.Error())
+		return
+	}
+	return len(allDevices), len(devices)
+}

+ 197 - 0
static/data.json

@@ -0,0 +1,197 @@
+[
+  {
+    "id": 1,
+    "name": "生活区",
+    "subRegion": null,
+    "devices": [
+      {
+        "id": 1,
+        "sn": "JM36g0RM0ivegAGK",
+        "regionId": 1,
+        "name": "测试",
+        "genre": "八回路控制",
+        "state": 1,
+        "isSun": false,
+        "loopNumber": 8,
+        "onlineTime": "2024-11-13T15:58:21.6440944+08:00",
+        "deviceLoops": [
+          {
+            "id": 1,
+            "deviceId": "JM36g0RM0ivegAGK",
+            "name": "回路12",
+            "state": 0,
+            "timeCondition1OnTime": "11:18",
+            "timeCondition1OffTime": "关闭",
+            "timeCondition2OnTime": "关闭",
+            "timeCondition2OffTime": "关闭"
+          },
+          {
+            "id": 2,
+            "deviceId": "JM36g0RM0ivegAGK",
+            "name": "回路2",
+            "state": 0,
+            "timeCondition1OnTime": "11:18",
+            "timeCondition1OffTime": "关闭",
+            "timeCondition2OnTime": "关闭",
+            "timeCondition2OffTime": "关闭"
+          },
+          {
+            "id": 3,
+            "deviceId": "JM36g0RM0ivegAGK",
+            "name": "回路3",
+            "state": 0,
+            "timeCondition1OnTime": "11:18",
+            "timeCondition1OffTime": "关闭",
+            "timeCondition2OnTime": "关闭",
+            "timeCondition2OffTime": "关闭"
+          },
+          {
+            "id": 4,
+            "deviceId": "JM36g0RM0ivegAGK",
+            "name": "回路4",
+            "state": 0,
+            "timeCondition1OnTime": "11:18",
+            "timeCondition1OffTime": "关闭",
+            "timeCondition2OnTime": "关闭",
+            "timeCondition2OffTime": "关闭"
+          },
+          {
+            "id": 5,
+            "deviceId": "JM36g0RM0ivegAGK",
+            "name": "回路5",
+            "state": 0,
+            "timeCondition1OnTime": "11:18",
+            "timeCondition1OffTime": "关闭",
+            "timeCondition2OnTime": "关闭",
+            "timeCondition2OffTime": "关闭"
+          },
+          {
+            "id": 6,
+            "deviceId": "JM36g0RM0ivegAGK",
+            "name": "回路6",
+            "state": 0,
+            "timeCondition1OnTime": "10:50",
+            "timeCondition1OffTime": "关闭",
+            "timeCondition2OnTime": "关闭",
+            "timeCondition2OffTime": "关闭"
+          },
+          {
+            "id": 7,
+            "deviceId": "JM36g0RM0ivegAGK",
+            "name": "回路7",
+            "state": 0,
+            "timeCondition1OnTime": "11:18",
+            "timeCondition1OffTime": "关闭",
+            "timeCondition2OnTime": "关闭",
+            "timeCondition2OffTime": "关闭"
+          },
+          {
+            "id": 8,
+            "deviceId": "JM36g0RM0ivegAGK",
+            "name": "回路8",
+            "state": 0,
+            "timeCondition1OnTime": "10:50",
+            "timeCondition1OffTime": "关闭",
+            "timeCondition2OnTime": "关闭",
+            "timeCondition2OffTime": "关闭"
+          }
+        ]
+      },
+      {
+        "id": 2,
+        "sn": "JM45zpMXCeW4SG7G",
+        "regionId": 1,
+        "name": "测试3",
+        "genre": "四回路控制",
+        "state": 1,
+        "isSun": false,
+        "loopNumber": 4,
+        "onlineTime": "2024-11-13T15:58:33.2260868+08:00",
+        "deviceLoops": [
+          {
+            "id": 1,
+            "deviceId": "JM45zpMXCeW4SG7G",
+            "name": "回路1",
+            "state": 0,
+            "timeCondition1OnTime": "关闭",
+            "timeCondition1OffTime": "关闭",
+            "timeCondition2OnTime": "关闭",
+            "timeCondition2OffTime": "关闭"
+          },
+          {
+            "id": 2,
+            "deviceId": "JM45zpMXCeW4SG7G",
+            "name": "回路2",
+            "state": 0,
+            "timeCondition1OnTime": "关闭",
+            "timeCondition1OffTime": "关闭",
+            "timeCondition2OnTime": "关闭",
+            "timeCondition2OffTime": "关闭"
+          },
+          {
+            "id": 3,
+            "deviceId": "JM45zpMXCeW4SG7G",
+            "name": "回路3",
+            "state": 0,
+            "timeCondition1OnTime": "关闭",
+            "timeCondition1OffTime": "关闭",
+            "timeCondition2OnTime": "关闭",
+            "timeCondition2OffTime": "关闭"
+          },
+          {
+            "id": 4,
+            "deviceId": "JM45zpMXCeW4SG7G",
+            "name": "回路4",
+            "state": 0,
+            "timeCondition1OnTime": "关闭",
+            "timeCondition1OffTime": "关闭",
+            "timeCondition2OnTime": "关闭",
+            "timeCondition2OffTime": "关闭"
+          }
+        ]
+      }
+    ]
+  },
+  {
+    "id": 2,
+    "name": "股份公司进场上坡",
+    "subRegion": null,
+    "devices": []
+  },
+  {
+    "id": 3,
+    "name": "地磅房至污泥厂门口",
+    "subRegion": null,
+    "devices": []
+  },
+  {
+    "id": 4,
+    "name": "三角花园至油库",
+    "subRegion": null,
+    "devices": []
+  },
+  {
+    "id": 5,
+    "name": "地磅房至搅拌站",
+    "subRegion": null,
+    "devices": []
+  },
+  {
+    "id": 6,
+    "name": "搅拌站至三岔路口",
+    "subRegion": null,
+    "devices": []
+  },
+  {
+    "id": 7,
+    "name": "垃圾堆体道路",
+    "subRegion": null,
+    "devices": []
+  },
+  {
+    "id": 8,
+    "name": "污水进场道路",
+    "subRegion": null,
+    "devices": []
+  }
+]

+ 12 - 0
static/devicePath.json

@@ -0,0 +1,12 @@
+[
+  {
+    "path": "192.168.110.212:2",
+    "id": "JM032347132tWa6g",
+    "tcp": null
+  },
+  {
+    "path": "192.168.110.214:2",
+    "id": "JM03g8PcVDVxLNHn",
+    "tcp": null
+  }
+]

+ 35 - 0
test1/ceshi_test.go

@@ -0,0 +1,35 @@
+package test1
+
+import (
+	"encoding/json"
+	"fmt"
+	"os"
+	"server/initialize"
+	"testing"
+)
+
+type Ceshi1 struct{}
+
+const dataFilePath = "../static/devicePath.json"
+
+func TestCeshi(t *testing.T) {
+	for _, connection := range initialize.ConnectionPool {
+		fmt.Println("111")
+		fmt.Println(connection)
+		results, err := connection.Tcp.ReadInputRegisters(0x03EE, 0x0008)
+		if err != nil {
+			fmt.Printf("Failed to write to Modbus device: %v\n", err)
+			return
+		}
+		fmt.Printf("Write Multiple Registers: %v\n", string(results))
+	}
+}
+
+// 保存数据到文件
+func saveData(parameter interface{}) error {
+	data, err := json.MarshalIndent(parameter, "", "  ")
+	if err != nil {
+		return err
+	}
+	return os.WriteFile(dataFilePath, data, 0644)
+}

+ 45 - 0
utils/crc.go

@@ -0,0 +1,45 @@
+package utils
+
+import (
+	"sync"
+)
+
+// Cyclical Redundancy Checking
+type crc struct {
+	once  sync.Once
+	table []uint16
+}
+
+var crcTb crc
+
+// CRC16 Calculate Cyclical Redundancy Checking
+func CRC16(bs []byte) uint16 {
+	crcTb.once.Do(crcTb.initTable)
+
+	val := uint16(0xFFFF)
+	for _, v := range bs {
+		val = (val >> 8) ^ crcTb.table[(val^uint16(v))&0x00FF]
+	}
+	return val
+}
+
+// initTable 初始化表
+func (c *crc) initTable() {
+	crcPoly16 := uint16(0xa001)
+	c.table = make([]uint16, 256)
+
+	for i := uint16(0); i < 256; i++ {
+		crc := uint16(0)
+		b := i
+
+		for j := uint16(0); j < 8; j++ {
+			if ((crc ^ b) & 0x0001) > 0 {
+				crc = (crc >> 1) ^ crcPoly16
+			} else {
+				crc = crc >> 1
+			}
+			b = b >> 1
+		}
+		c.table[i] = crc
+	}
+}

+ 157 - 0
utils/myTool.go

@@ -0,0 +1,157 @@
+package utils
+
+import (
+	"encoding/json"
+	"fmt"
+	"net"
+	"os"
+	"server/dao"
+	"time"
+)
+
+const connDataFilePath = "static/devicePath.json"
+const dataFilePath = "static/data.json"
+
+// LoadData 从文件读取数据
+func LoadData() (regions []dao.Region, err error) {
+	data, err := os.ReadFile(dataFilePath)
+	if err != nil {
+		if os.IsNotExist(err) {
+			return nil, nil
+		}
+		return nil, err
+	}
+	err = json.Unmarshal(data, &regions)
+	if err != nil {
+		return nil, err
+
+	}
+	return regions, nil
+}
+
+func GetOnlineDevices() (devices []dao.Device, err error) {
+	regions, err := LoadData()
+	if err != nil {
+		return nil, err
+	}
+
+	for _, region := range regions {
+		for _, device := range region.Devices {
+			if device.State == 0 {
+				continue
+			}
+			devices = append(devices, device)
+		}
+	}
+
+	return devices, err
+}
+
+func GetAllDevices() (devices []dao.Device, err error) {
+	regions, err := LoadData()
+	if err != nil {
+		return nil, err
+	}
+
+	for _, region := range regions {
+		for _, device := range region.Devices {
+			devices = append(devices, device)
+		}
+	}
+
+	return devices, err
+}
+
+// GetDataByDeviceId 按设备id获取信息 地区+设备
+func GetDataByDeviceId(deviceId string) (reg dao.Region, dev dao.Device, err error) {
+	regions, err := LoadData()
+	if err != nil {
+		return reg, dev, err
+	}
+
+	for _, region := range regions {
+		for _, device := range region.Devices {
+			if device.Sn == deviceId {
+				return region, device, nil
+			}
+		}
+	}
+
+	return reg, dev, fmt.Errorf("设备%s未找到", deviceId)
+}
+
+func SaveRegionOnData(data dao.Region) ([]dao.Region, error) {
+	regions, err := LoadData()
+	if err != nil {
+		return regions, err
+	}
+	for i, region := range regions {
+		if region.Name == data.Name {
+			regions[i] = data
+		}
+	}
+	return regions, nil
+}
+
+// SaveData 保存数据到文件
+func SaveData(path string, parameter interface{}) error {
+	data, err := json.MarshalIndent(parameter, "", "  ")
+	if err != nil {
+		return err
+	}
+	return os.WriteFile(path, data, 0644)
+}
+
+// CalculateCircuitSum 计算给定回路数量的总和
+func CalculateCircuitSum(circuits int) (int, error) {
+	if circuits <= 0 {
+		return 0, fmt.Errorf("回路数量必须大于0")
+	}
+
+	// 计算结果
+	result := (1 << circuits) - 1
+
+	return result, nil
+}
+
+// WriteDevice 纯写
+func WriteDevice(frame []byte, conn net.Conn) error {
+	_, err := conn.Write(frame)
+	if err != nil {
+		fmt.Println("Error writing to connection:", err)
+		return err
+	}
+	return nil
+}
+
+// ReadDevice 纯读
+func ReadDevice(conn net.Conn) (data []byte) {
+	buffer := make([]byte, 1024)
+	n, err := conn.Read(buffer)
+	if err != nil {
+		fmt.Println("Error reading from connection:", err)
+		return
+	}
+	fmt.Printf("Received %d bytes: %X\n", n, buffer[6:n])
+	return buffer
+}
+
+// WriteAndReadDevice 又写又读
+func WriteAndReadDevice(frame []byte, conn net.Conn, former, after int) (data []byte, err error) {
+	// 发送 Modbus RTU 帧
+	//FE 04 03 EE 00 08 85 B2
+	n, err := conn.Write(frame)
+	if err != nil {
+		fmt.Println("Error writing to connection:", err)
+		return
+	}
+	time.Sleep(1000 * time.Millisecond)
+	buffer := make([]byte, 1024)
+	// 读取响应
+	n, err = conn.Read(buffer)
+	if err != nil {
+		fmt.Println("Error reading from connection:", err)
+		return
+	}
+	return buffer[former : n-after], err
+}

+ 90 - 0
utils/mytime.go

@@ -0,0 +1,90 @@
+package utils
+
+import (
+	"time"
+)
+
+var bjlocation *time.Location
+
+func init() {
+	loc, err := time.LoadLocation("Asia/Shanghai")
+	if err != nil {
+		bjlocation = time.FixedZone("CST", 8*3600)
+	} else {
+		bjlocation = loc
+	}
+}
+
+func Unix2Time(sec int64) string {
+	return time.Unix(sec, 0).Format("2006-01-02 15:04:05")
+}
+
+func MlNow() time.Time {
+	return time.Now().In(bjlocation)
+}
+
+func MlParseTimeX(layout, strTime string) (time.Time, error) {
+	return time.ParseInLocation(layout, strTime, bjlocation)
+}
+
+type MLTime time.Time
+
+const (
+	timeFormart = "2006-01-02 15:04:05"
+)
+
+func (t *MLTime) UnmarshalJSON(data []byte) (err error) {
+	now, err := time.ParseInLocation(`"`+timeFormart+`"`, string(data), bjlocation)
+	*t = MLTime(now)
+	return
+}
+
+func (t MLTime) MarshalJSON() ([]byte, error) {
+	b := make([]byte, 0, len(timeFormart)+2)
+	b = append(b, '"')
+	tt := time.Time(t)
+	if !tt.IsZero() {
+		b = tt.AppendFormat(b, timeFormart)
+	}
+	b = append(b, '"')
+	return b, nil
+}
+
+func (t MLTime) String() string {
+	tt := time.Time(t)
+	if tt.IsZero() {
+		return ""
+	}
+	return tt.Format(timeFormart)
+}
+
+type MLTimeEx time.Time
+
+const (
+	timeFormartEx = "2006-01-02 15:04:05.000"
+)
+
+func (t *MLTimeEx) UnmarshalJSON(data []byte) (err error) {
+	now, err := time.ParseInLocation(`"`+timeFormartEx+`"`, string(data), bjlocation)
+	*t = MLTimeEx(now)
+	return
+}
+
+func (t MLTimeEx) MarshalJSON() ([]byte, error) {
+	b := make([]byte, 0, len(timeFormartEx)+2)
+	b = append(b, '"')
+	tt := time.Time(t)
+	if !tt.IsZero() {
+		b = tt.AppendFormat(b, timeFormartEx)
+	}
+	b = append(b, '"')
+	return b, nil
+}
+
+func (t MLTimeEx) String() string {
+	tt := time.Time(t)
+	if tt.IsZero() {
+		return ""
+	}
+	return tt.Format(timeFormartEx)
+}

+ 279 - 0
utils/now.go

@@ -0,0 +1,279 @@
+package utils
+
+import (
+	"errors"
+	"regexp"
+	"time"
+)
+
+func (now *Now) BeginningOfMinute() time.Time {
+	return now.Truncate(time.Minute)
+}
+
+func (now *Now) BeginningOfHour() time.Time {
+	y, m, d := now.Date()
+	return time.Date(y, m, d, now.Time.Hour(), 0, 0, 0, now.Time.Location())
+}
+
+func (now *Now) BeginningOfDay() time.Time {
+	y, m, d := now.Date()
+	return time.Date(y, m, d, 0, 0, 0, 0, now.Time.Location())
+}
+
+func (now *Now) BeginningOfWeek() time.Time {
+	t := now.BeginningOfDay()
+	weekday := int(t.Weekday())
+
+	if WeekStartDay != time.Sunday {
+		weekStartDayInt := int(WeekStartDay)
+
+		if weekday < weekStartDayInt {
+			weekday = weekday + 7 - weekStartDayInt
+		} else {
+			weekday = weekday - weekStartDayInt
+		}
+	}
+	return t.AddDate(0, 0, -weekday)
+}
+
+func (now *Now) BeginningOfMonth() time.Time {
+	y, m, _ := now.Date()
+	return time.Date(y, m, 1, 0, 0, 0, 0, now.Location())
+}
+
+func (now *Now) BeginningOfQuarter() time.Time {
+	month := now.BeginningOfMonth()
+	offset := (int(month.Month()) - 1) % 3
+	return month.AddDate(0, -offset, 0)
+}
+
+func (now *Now) BeginningOfYear() time.Time {
+	y, _, _ := now.Date()
+	return time.Date(y, time.January, 1, 0, 0, 0, 0, now.Location())
+}
+
+func (now *Now) EndOfMinute() time.Time {
+	return now.BeginningOfMinute().Add(time.Minute - time.Nanosecond)
+}
+
+func (now *Now) EndOfHour() time.Time {
+	return now.BeginningOfHour().Add(time.Hour - time.Nanosecond)
+}
+
+func (now *Now) EndOfDay() time.Time {
+	y, m, d := now.Date()
+	return time.Date(y, m, d, 23, 59, 59, int(time.Second-time.Nanosecond), now.Location())
+}
+
+func (now *Now) EndOfWeek() time.Time {
+	return now.BeginningOfWeek().AddDate(0, 0, 7).Add(-time.Nanosecond)
+}
+
+func (now *Now) EndOfMonth() time.Time {
+	return now.BeginningOfMonth().AddDate(0, 1, 0).Add(-time.Nanosecond)
+}
+
+func (now *Now) EndOfQuarter() time.Time {
+	return now.BeginningOfQuarter().AddDate(0, 3, 0).Add(-time.Nanosecond)
+}
+
+func (now *Now) EndOfYear() time.Time {
+	return now.BeginningOfYear().AddDate(1, 0, 0).Add(-time.Nanosecond)
+}
+
+func (now *Now) Monday() time.Time {
+	t := now.BeginningOfDay()
+	weekday := int(t.Weekday())
+	if weekday == 0 {
+		weekday = 7
+	}
+	return t.AddDate(0, 0, -weekday+1)
+}
+
+func (now *Now) Sunday() time.Time {
+	t := now.BeginningOfDay()
+	weekday := int(t.Weekday())
+	if weekday == 0 {
+		return t
+	}
+	return t.AddDate(0, 0, (7 - weekday))
+}
+
+func (now *Now) EndOfSunday() time.Time {
+	return New(now.Sunday()).EndOfDay()
+}
+
+func parseWithFormat(str string) (t time.Time, err error) {
+	for _, format := range TimeFormats {
+		t, err = time.Parse(format, str)
+		if err == nil {
+			return
+		}
+	}
+	err = errors.New("Can't parse string as time: " + str)
+	return
+}
+
+var hasTimeRegexp = regexp.MustCompile(`(\s+|^\s*)\d{1,2}((:\d{1,2})*|((:\d{1,2}){2}\.(\d{3}|\d{6}|\d{9})))\s*$`)
+var onlyTimeRegexp = regexp.MustCompile(`^\s*\d{1,2}((:\d{1,2})*|((:\d{1,2}){2}\.(\d{3}|\d{6}|\d{9})))\s*$`)
+
+func (now *Now) Parse(strs ...string) (t time.Time, err error) {
+	var (
+		setCurrentTime  bool
+		parseTime       []int
+		currentTime     = []int{now.Nanosecond(), now.Second(), now.Minute(), now.Hour(), now.Day(), int(now.Month()), now.Year()}
+		currentLocation = now.Location()
+		onlyTimeInStr   = true
+	)
+
+	for _, str := range strs {
+		hasTimeInStr := hasTimeRegexp.MatchString(str)
+		onlyTimeInStr = hasTimeInStr && onlyTimeInStr && onlyTimeRegexp.MatchString(str)
+		if t, err = parseWithFormat(str); err == nil {
+			location := t.Location()
+			if location.String() == "UTC" {
+				location = currentLocation
+			}
+
+			parseTime = []int{t.Nanosecond(), t.Second(), t.Minute(), t.Hour(), t.Day(), int(t.Month()), t.Year()}
+
+			for i, v := range parseTime {
+				if hasTimeInStr && i <= 3 {
+					continue
+				}
+
+				if v == 0 {
+					if setCurrentTime {
+						parseTime[i] = currentTime[i]
+					}
+				} else {
+					setCurrentTime = true
+				}
+
+				if onlyTimeInStr {
+					if i == 4 || i == 5 {
+						parseTime[i] = currentTime[i]
+						continue
+					}
+				}
+			}
+
+			t = time.Date(parseTime[6], time.Month(parseTime[5]), parseTime[4], parseTime[3], parseTime[2], parseTime[1], parseTime[0], location)
+			currentTime = []int{t.Nanosecond(), t.Second(), t.Minute(), t.Hour(), t.Day(), int(t.Month()), t.Year()}
+		}
+	}
+	return
+}
+
+func (now *Now) MustParse(strs ...string) (t time.Time) {
+	t, err := now.Parse(strs...)
+	if err != nil {
+		panic(err)
+	}
+	return t
+}
+
+func (now *Now) Between(begin, end string) bool {
+	beginTime := now.MustParse(begin)
+	endTime := now.MustParse(end)
+	return now.After(beginTime) && now.Before(endTime)
+}
+
+var WeekStartDay = time.Sunday
+
+var TimeFormats = []string{"1/2/2006", "1/2/2006 15:4:5", "2006", "2006-1", "2006-1-2", "2006-1-2 15", "2006-1-2 15:4", "2006-1-2 15:4:5", "1-2", "15:4:5", "15:4", "15", "15:4:5 Jan 2, 2006 MST", "2006-01-02 15:04:05.999999999 -0700 MST"}
+
+type Now struct {
+	time.Time
+}
+
+func New(t time.Time) *Now {
+	return &Now{t}
+}
+
+func BeginningOfMinute() time.Time {
+	return New(time.Now()).BeginningOfMinute()
+}
+
+func BeginningOfHour() time.Time {
+	return New(time.Now()).BeginningOfHour()
+}
+
+func BeginningOfDay() time.Time {
+	return New(time.Now()).BeginningOfDay()
+}
+
+func BeginningOfWeek() time.Time {
+	return New(time.Now()).BeginningOfWeek()
+}
+
+func BeginningOfMonth() time.Time {
+	return New(time.Now()).BeginningOfMonth()
+}
+
+func BeginningOfQuarter() time.Time {
+	return New(time.Now()).BeginningOfQuarter()
+}
+
+func BeginningOfYear() time.Time {
+	return New(time.Now()).BeginningOfYear()
+}
+
+func EndOfMinute() time.Time {
+	return New(time.Now()).EndOfMinute()
+}
+
+func EndOfHour() time.Time {
+	return New(time.Now()).EndOfHour()
+}
+
+func EndOfDay() time.Time {
+	return New(time.Now()).EndOfDay()
+}
+
+func EndOfWeek() time.Time {
+	return New(time.Now()).EndOfWeek()
+}
+
+func EndOfMonth() time.Time {
+	return New(time.Now()).EndOfMonth()
+}
+
+func EndOfQuarter() time.Time {
+	return New(time.Now()).EndOfQuarter()
+}
+
+func EndOfYear() time.Time {
+	return New(time.Now()).EndOfYear()
+}
+
+func Monday() time.Time {
+	return New(time.Now()).Monday()
+}
+
+func Sunday() time.Time {
+	return New(time.Now()).Sunday()
+}
+
+func EndOfSunday() time.Time {
+	return New(time.Now()).EndOfSunday()
+}
+
+func Parse(strs ...string) (time.Time, error) {
+	return New(time.Now()).Parse(strs...)
+}
+
+func ParseInLocation(loc *time.Location, strs ...string) (time.Time, error) {
+	return New(time.Now().In(loc)).Parse(strs...)
+}
+
+func MustParse(strs ...string) time.Time {
+	return New(time.Now()).MustParse(strs...)
+}
+
+func MustParseInLocation(loc *time.Location, strs ...string) time.Time {
+	return New(time.Now().In(loc)).MustParse(strs...)
+}
+func Between(time1, time2 string) bool {
+	return New(time.Now()).Between(time1, time2)
+}

+ 447 - 0
utils/sunrisesunset.go

@@ -0,0 +1,447 @@
+// Package sunrisesunset should be used to calculate the apparent sunrise and sunset based on the latitude, longitude, UTC offset and date.
+// All calculations (formulas) were extracted from the Solar Calculation Details of the Earth System Research Laboratory:
+// https://www.esrl.noaa.gov/gmd/grad/solcalc/calcdetails.html
+package utils
+
+import (
+	"errors"
+	"math"
+	"time"
+)
+
+// The Parameters struct can also be used to manipulate
+// the data and get the sunrise and sunset
+type Parameters struct {
+	Latitude  float64
+	Longitude float64
+	UtcOffset float64
+	Date      time.Time
+}
+
+// Just call the 'general' GetSunriseSunset function and return the results
+func (p *Parameters) GetSunriseSunset() (time.Time, time.Time, error) {
+	return GetSunriseSunset(p.Latitude, p.Longitude, p.UtcOffset, p.Date)
+}
+
+// Convert radians to degrees
+func rad2deg(radians float64) float64 {
+	return radians * (180.0 / math.Pi)
+}
+
+// Convert degrees to radians
+func deg2rad(degrees float64) float64 {
+	return degrees * (math.Pi / 180.0)
+}
+
+// Creates a vector with the seconds normalized to the range 0~1.
+// seconds - The number of seconds will be normalized to 1
+// Return A vector with the seconds normalized to 0~1
+func createSecondsNormalized(seconds int) (vector []float64) {
+	for index := 0; index < seconds; index++ {
+		temp := float64(index) / float64(seconds-1)
+		vector = append(vector, temp)
+	}
+	return
+}
+
+// Calculate Julian Day based on the formula: nDays+2415018.5+secondsNorm-UTCoff/24
+// numDays - The number of days calculated in the calculate function
+// secondsNorm - Seconds normalized calculated by the createSecondsNormalized function
+// utcOffset - UTC offset defined by the user
+// Return Julian day slice
+func calcJulianDay(numDays int64, secondsNorm []float64, utcOffset float64) (julianDay []float64) {
+	for index := 0; index < len(secondsNorm); index++ {
+		temp := float64(numDays) + 2415018.5 + secondsNorm[index] - utcOffset/24.0
+		julianDay = append(julianDay, temp)
+	}
+	return
+}
+
+// Calculate the Julian Century based on the formula: (julianDay - 2451545.0) / 36525.0
+// julianDay - Julian day vector calculated by the calcJulianDay function
+// Return Julian century slice
+func calcJulianCentury(julianDay []float64) (julianCentury []float64) {
+	for index := 0; index < len(julianDay); index++ {
+		temp := (julianDay[index] - 2451545.0) / 36525.0
+		julianCentury = append(julianCentury, temp)
+	}
+	return
+}
+
+// Calculate the Geom Mean Long Sun in degrees based on the formula: 280.46646 + julianCentury * (36000.76983 + julianCentury * 0.0003032)
+// julianCentury - Julian century calculated by the calcJulianCentury function
+// Return The Geom Mean Long Sun slice
+func calcGeomMeanLongSun(julianCentury []float64) (geomMeanLongSun []float64) {
+	for index := 0; index < len(julianCentury); index++ {
+		a := 280.46646 + julianCentury[index]*(36000.76983+julianCentury[index]*0.0003032)
+		temp := math.Mod(a, 360.0)
+		geomMeanLongSun = append(geomMeanLongSun, temp)
+	}
+	return
+}
+
+// Calculate the Geom Mean Anom Sun in degrees based on the formula: 357.52911 + julianCentury * (35999.05029 - 0.0001537 * julianCentury)
+// julianCentury - Julian century calculated by the calcJulianCentury function
+// Return The Geom Mean Anom Sun slice
+func calcGeomMeanAnomSun(julianCentury []float64) (geomMeanAnomSun []float64) {
+	for index := 0; index < len(julianCentury); index++ {
+		temp := 357.52911 + julianCentury[index]*(35999.05029-0.0001537*julianCentury[index])
+		geomMeanAnomSun = append(geomMeanAnomSun, temp)
+	}
+	return
+}
+
+// Calculate the Eccent Earth Orbit based on the formula: 0.016708634 - julianCentury * (0.000042037 + 0.0000001267 * julianCentury)
+// julianCentury - Julian century calculated by the calcJulianCentury function
+// Return The Eccent Earth Orbit slice
+func calcEccentEarthOrbit(julianCentury []float64) (eccentEarthOrbit []float64) {
+	for index := 0; index < len(julianCentury); index++ {
+		temp := 0.016708634 - julianCentury[index]*(0.000042037+0.0000001267*julianCentury[index])
+		eccentEarthOrbit = append(eccentEarthOrbit, temp)
+	}
+	return
+}
+
+// Calculate the Sun Eq Ctr based on the formula: sin(deg2rad(geomMeanAnomSun))*(1.914602-julianCentury*(0.004817+0.000014*julianCentury))+sin(deg2rad(2*geomMeanAnomSun))*(0.019993-0.000101*julianCentury)+sin(deg2rad(3*geomMeanAnomSun))*0.000289;
+// julianCentury - Julian century calculated by the calcJulianCentury function
+// geomMeanAnomSun - Geom Mean Anom Sun calculated by the calcGeomMeanAnomSun function
+// Return The Sun Eq Ctr slice
+func calcSunEqCtr(julianCentury []float64, geomMeanAnomSun []float64) (sunEqCtr []float64) {
+	if len(julianCentury) != len(geomMeanAnomSun) {
+		return
+	}
+
+	for index := 0; index < len(julianCentury); index++ {
+		temp := math.Sin(deg2rad(geomMeanAnomSun[index]))*(1.914602-julianCentury[index]*(0.004817+0.000014*julianCentury[index])) + math.Sin(deg2rad(2*geomMeanAnomSun[index]))*(0.019993-0.000101*julianCentury[index]) + math.Sin(deg2rad(3*geomMeanAnomSun[index]))*0.000289
+		sunEqCtr = append(sunEqCtr, temp)
+	}
+	return
+}
+
+// Calculate the Sun True Long in degrees based on the formula: sunEqCtr + geomMeanLongSun
+// sunEqCtr - Sun Eq Ctr calculated by the calcSunEqCtr function
+// geomMeanLongSun - Geom Mean Long Sun calculated by the calcGeomMeanLongSun function
+// Return The Sun True Long slice
+func calcSunTrueLong(sunEqCtr []float64, geomMeanLongSun []float64) (sunTrueLong []float64) {
+	if len(sunEqCtr) != len(geomMeanLongSun) {
+		return
+	}
+
+	for index := 0; index < len(sunEqCtr); index++ {
+		temp := sunEqCtr[index] + geomMeanLongSun[index]
+		sunTrueLong = append(sunTrueLong, temp)
+	}
+	return
+}
+
+// Calculate the Sun App Long in degrees based on the formula: sunTrueLong-0.00569-0.00478*sin(deg2rad(125.04-1934.136*julianCentury))
+// sunTrueLong - Sun True Long calculated by the calcSunTrueLong function
+// julianCentury - Julian century calculated by the calcJulianCentury function
+// Return The Sun App Long slice
+func calcSunAppLong(sunTrueLong []float64, julianCentury []float64) (sunAppLong []float64) {
+	if len(sunTrueLong) != len(julianCentury) {
+		return
+	}
+
+	for index := 0; index < len(sunTrueLong); index++ {
+		temp := sunTrueLong[index] - 0.00569 - 0.00478*math.Sin(deg2rad(125.04-1934.136*julianCentury[index]))
+		sunAppLong = append(sunAppLong, temp)
+	}
+	return
+}
+
+// Calculate the Mean Obliq Ecliptic in degrees based on the formula: 23+(26+((21.448-julianCentury*(46.815+julianCentury*(0.00059-julianCentury*0.001813))))/60)/60
+// julianCentury - Julian century calculated by the calcJulianCentury function
+// Return the Mean Obliq Ecliptic slice
+func calcMeanObliqEcliptic(julianCentury []float64) (meanObliqEcliptic []float64) {
+	for index := 0; index < len(julianCentury); index++ {
+		temp := 23.0 + (26.0+(21.448-julianCentury[index]*(46.815+julianCentury[index]*(0.00059-julianCentury[index]*0.001813)))/60.0)/60.0
+		meanObliqEcliptic = append(meanObliqEcliptic, temp)
+	}
+	return
+}
+
+// Calculate the Obliq Corr in degrees based on the formula: meanObliqEcliptic+0.00256*cos(deg2rad(125.04-1934.136*julianCentury))
+// meanObliqEcliptic - Mean Obliq Ecliptic calculated by the calcMeanObliqEcliptic function
+// julianCentury - Julian century calculated by the calcJulianCentury function
+// Return the Obliq Corr slice
+func calcObliqCorr(meanObliqEcliptic []float64, julianCentury []float64) (obliqCorr []float64) {
+	if len(meanObliqEcliptic) != len(julianCentury) {
+		return
+	}
+
+	for index := 0; index < len(julianCentury); index++ {
+		temp := meanObliqEcliptic[index] + 0.00256*math.Cos(deg2rad(125.04-1934.136*julianCentury[index]))
+		obliqCorr = append(obliqCorr, temp)
+	}
+	return
+}
+
+// Calculate the Sun Declination in degrees based on the formula: rad2deg(asin(sin(deg2rad(obliqCorr))*sin(deg2rad(sunAppLong))))
+// obliqCorr - Obliq Corr calculated by the calcObliqCorr function
+// sunAppLong - Sun App Long calculated by the calcSunAppLong function
+// Return the sun declination slice
+func calcSunDeclination(obliqCorr []float64, sunAppLong []float64) (sunDeclination []float64) {
+	if len(obliqCorr) != len(sunAppLong) {
+		return
+	}
+
+	for index := 0; index < len(obliqCorr); index++ {
+		temp := rad2deg(math.Asin(math.Sin(deg2rad(obliqCorr[index])) * math.Sin(deg2rad(sunAppLong[index]))))
+		sunDeclination = append(sunDeclination, temp)
+	}
+	return
+}
+
+// Calculate the equation of time (minutes) based on the formula:
+// 4*rad2deg(multiFactor*sin(2*deg2rad(geomMeanLongSun))-2*eccentEarthOrbit*sin(deg2rad(geomMeanAnomSun))+4*eccentEarthOrbit*multiFactor*sin(deg2rad(geomMeanAnomSun))*cos(2*deg2rad(geomMeanLongSun))-0.5*multiFactor*multiFactor*sin(4*deg2rad(geomMeanLongSun))-1.25*eccentEarthOrbit*eccentEarthOrbit*sin(2*deg2rad(geomMeanAnomSun)))
+// multiFactor - The Multi Factor vector calculated in the calculate function
+// geomMeanLongSun - The Geom Mean Long Sun vector calculated by the calcGeomMeanLongSun function
+// eccentEarthOrbit - The Eccent Earth vector calculated by the calcEccentEarthOrbit function
+// geomMeanAnomSun - The Geom Mean Anom Sun vector calculated by the calcGeomMeanAnomSun function
+// Return the equation of time slice
+func calcEquationOfTime(multiFactor []float64, geomMeanLongSun []float64, eccentEarthOrbit []float64, geomMeanAnomSun []float64) (equationOfTime []float64) {
+
+	if len(multiFactor) != len(geomMeanLongSun) ||
+		len(multiFactor) != len(eccentEarthOrbit) ||
+		len(multiFactor) != len(geomMeanAnomSun) {
+		return
+	}
+
+	for index := 0; index < len(multiFactor); index++ {
+		a := multiFactor[index] * math.Sin(2.0*deg2rad(geomMeanLongSun[index]))
+		b := 2.0 * eccentEarthOrbit[index] * math.Sin(deg2rad(geomMeanAnomSun[index]))
+		c := 4.0 * eccentEarthOrbit[index] * multiFactor[index] * math.Sin(deg2rad(geomMeanAnomSun[index]))
+		d := math.Cos(2.0 * deg2rad(geomMeanLongSun[index]))
+		e := 0.5 * multiFactor[index] * multiFactor[index] * math.Sin(4.0*deg2rad(geomMeanLongSun[index]))
+		f := 1.25 * eccentEarthOrbit[index] * eccentEarthOrbit[index] * math.Sin(2.0*deg2rad(geomMeanAnomSun[index]))
+		temp := 4.0 * rad2deg(a-b+c*d-e-f)
+		equationOfTime = append(equationOfTime, temp)
+	}
+	return
+}
+
+// Calculate the HaSunrise in degrees based on the formula: rad2deg(acos(cos(deg2rad(90.833))/(cos(deg2rad(latitude))*cos(deg2rad(sunDeclination)))-tan(deg2rad(latitude))*tan(deg2rad(sunDeclination))))
+// latitude - The latitude defined by the user
+// sunDeclination - The Sun Declination calculated by the calcSunDeclination function
+// Return the HaSunrise slice
+func calcHaSunrise(latitude float64, sunDeclination []float64) (haSunrise []float64) {
+	for index := 0; index < len(sunDeclination); index++ {
+		temp := rad2deg(math.Acos(math.Cos(deg2rad(90.833))/(math.Cos(deg2rad(latitude))*math.Cos(deg2rad(sunDeclination[index]))) - math.Tan(deg2rad(latitude))*math.Tan(deg2rad(sunDeclination[index]))))
+		haSunrise = append(haSunrise, temp)
+	}
+	return
+}
+
+// Calculate the Solar Noon based on the formula: (720 - 4 * longitude - equationOfTime + utcOffset * 60) * 60
+// longitude - The longitude is defined by the user
+// equationOfTime - The Equation of Time slice is calculated by the calcEquationOfTime function
+// utcOffset - The UTC offset is defined by the user
+// Return the Solar Noon slice
+func calcSolarNoon(longitude float64, equationOfTime []float64, utcOffset float64) (solarNoon []float64) {
+	for index := 0; index < len(equationOfTime); index++ {
+		temp := (720.0 - 4.0*longitude - equationOfTime[index] + utcOffset*60.0) * 60.0
+		solarNoon = append(solarNoon, temp)
+	}
+	return
+}
+
+// Check if the latitude is valid. Range: -90 - 90
+func checkLatitude(latitude float64) bool {
+	if latitude < -90.0 || latitude > 90.0 {
+		return false
+	}
+	return true
+}
+
+// Check if the longitude is valid. Range: -180 - 180
+func checkLongitude(longitude float64) bool {
+	if longitude < -180.0 || longitude > 180.0 {
+		return false
+	}
+	return true
+}
+
+// Check if the UTC offset is valid. Range: -12 - 14
+func checkUtcOffset(utcOffset float64) bool {
+	if utcOffset < -12.0 || utcOffset > 14.0 {
+		return false
+	}
+	return true
+}
+
+// Check if the date is valid.
+func checkDate(date time.Time) bool {
+	minDate := time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC)
+	maxDate := time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC)
+	if date.Before(minDate) || date.After(maxDate) {
+		return false
+	}
+	return true
+}
+
+// Compute the number of days between two dates
+func diffDays(date1, date2 time.Time) int64 {
+	return int64(date2.Sub(date1) / (24 * time.Hour))
+}
+
+// Find the index of the minimum value
+func minIndex(slice []float64) int {
+	if len(slice) == 0 {
+		return -1
+	}
+	min := slice[0]
+	minIndex := 0
+	for index := 0; index < len(slice); index++ {
+		if slice[index] < min {
+			min = slice[index]
+			minIndex = index
+		}
+	}
+	return minIndex
+}
+
+// Convert each value to the absolute value
+func abs(slice []float64) []float64 {
+	var newSlice []float64
+	for _, value := range slice {
+		if value < 0.0 {
+			value = math.Abs(value)
+		}
+		newSlice = append(newSlice, value)
+	}
+	return newSlice
+}
+
+func round(value float64) int {
+	if value < 0 {
+		return int(value - 0.5)
+	}
+	return int(value + 0.5)
+}
+
+// GetSunriseSunset function is responsible for calculate the apparent Sunrise and Sunset times.
+// If some parameter is wrong it will return an error.
+func GetSunriseSunset(latitude float64, longitude float64, utcOffset float64, date time.Time) (sunrise time.Time, sunset time.Time, err error) {
+	// Check latitude
+	if !checkLatitude(latitude) {
+		err = errors.New("Invalid latitude")
+		return
+	}
+	// Check longitude
+	if !checkLongitude(longitude) {
+		err = errors.New("Invalid longitude")
+		return
+	}
+	// Check UTC offset
+	if !checkUtcOffset(utcOffset) {
+		err = errors.New("Invalid UTC offset")
+		return
+	}
+	// Check date
+	if !checkDate(date) {
+		err = errors.New("Invalid date")
+		return
+	}
+
+	// The number of days since 30/12/1899
+	since := time.Date(1899, 12, 30, 0, 0, 0, 0, time.UTC)
+	numDays := diffDays(since, date)
+
+	// Seconds of a full day 86400
+	seconds := 24 * 60 * 60
+
+	// Creates a vector that represents each second in the range 0~1
+	secondsNorm := createSecondsNormalized(seconds)
+
+	// Calculate Julian Day
+	julianDay := calcJulianDay(numDays, secondsNorm, utcOffset)
+
+	// Calculate Julian Century
+	julianCentury := calcJulianCentury(julianDay)
+
+	// Geom Mean Long Sun (deg)
+	geomMeanLongSun := calcGeomMeanLongSun(julianCentury)
+
+	// Geom Mean Anom Sun (deg)
+	geomMeanAnomSun := calcGeomMeanAnomSun(julianCentury)
+
+	// Eccent Earth Orbit
+	eccentEarthOrbit := calcEccentEarthOrbit(julianCentury)
+
+	// Sun Eq of Ctr
+	sunEqCtr := calcSunEqCtr(julianCentury, geomMeanAnomSun)
+
+	// Sun True Long (deg)
+	sunTrueLong := calcSunTrueLong(sunEqCtr, geomMeanLongSun)
+
+	// Sun App Long (deg)
+	sunAppLong := calcSunAppLong(sunTrueLong, julianCentury)
+
+	// Mean Obliq Ecliptic (deg)
+	meanObliqEcliptic := calcMeanObliqEcliptic(julianCentury)
+
+	// Obliq Corr (deg)
+	obliqCorr := calcObliqCorr(meanObliqEcliptic, julianCentury)
+
+	// Sun Declin (deg)
+	sunDeclination := calcSunDeclination(obliqCorr, sunAppLong)
+
+	// var y
+	var multiFactor []float64
+	for index := 0; index < len(obliqCorr); index++ {
+		temp := math.Tan(deg2rad(obliqCorr[index]/2.0)) * math.Tan(deg2rad(obliqCorr[index]/2.0))
+		multiFactor = append(multiFactor, temp)
+	}
+
+	// Eq of Time (minutes)
+	equationOfTime := calcEquationOfTime(multiFactor, geomMeanLongSun, eccentEarthOrbit, geomMeanAnomSun)
+
+	// HA Sunrise (deg)
+	haSunrise := calcHaSunrise(latitude, sunDeclination)
+
+	// Solar Noon (LST)
+	solarNoon := calcSolarNoon(longitude, equationOfTime, utcOffset)
+
+	// Sunrise and Sunset Times (LST)
+	var tempSunrise []float64
+	var tempSunset []float64
+
+	for index := 0; index < len(solarNoon); index++ {
+		tempSunrise = append(tempSunrise, (solarNoon[index] - float64(round(haSunrise[index]*4.0*60.0)) - float64(seconds)*secondsNorm[index]))
+		tempSunset = append(tempSunset, (solarNoon[index] + float64(round(haSunrise[index]*4.0*60.0)) - float64(seconds)*secondsNorm[index]))
+	}
+
+	// Get the sunrise and sunset in seconds
+	sunriseSeconds := minIndex(abs(tempSunrise))
+	sunsetSeconds := minIndex(abs(tempSunset))
+
+	// Convert the seconds to time
+	defaultTime := new(time.Time)
+	sunrise = defaultTime.Add(time.Duration(sunriseSeconds) * time.Second)
+	sunset = defaultTime.Add(time.Duration(sunsetSeconds) * time.Second)
+
+	return
+}
+
+func SunriseSunsetForChina(latitude, longitude float64) (string, string, error) {
+	p := Parameters{
+		Latitude:  latitude,
+		Longitude: longitude,
+		UtcOffset: 8.0,
+		Date:      New(MlNow()).BeginningOfDay(),
+	}
+	// Get sunrise and sunset in seconds
+	sunrise, sunset, err := p.GetSunriseSunset()
+	if err != nil {
+		return "", "", err
+	}
+	if sunrise.Second() >= 30 {
+		sunrise = sunrise.Add(time.Minute)
+	}
+	if sunset.Second() >= 30 {
+		sunset = sunset.Add(time.Minute)
+	}
+	//return "16:17", "16:16", nil
+	return sunrise.Format("15:04"), sunset.Format("15:04"), nil
+}

+ 20 - 0
work/MyJob.go

@@ -0,0 +1,20 @@
+package work
+
+import (
+	"github.com/robfig/cron"
+	"server/modbus"
+	"server/service"
+)
+
+func MyJob() {
+	c := cron.New()
+
+	_ = c.AddFunc("0 */2 * * * ?", func() {
+		modbus.GetSunPowerInfo()
+	})
+	_ = c.AddFunc("0 * * * * ?", func() {
+		service.Cron{}.RelayOnOffTimeTask()
+		modbus.DealWithOffline()
+	})
+	c.Start()
+}