liuxiulin před 2 roky
revize
64341cc3af
12 změnil soubory, kde provedl 589 přidání a 0 odebrání
  1. 8 0
      .idea/.gitignore
  2. 8 0
      .idea/modules.xml
  3. 9 0
      .idea/pt100-gateway.iml
  4. binární
      bin/linux_amd64/pt100
  5. 12 0
      config/config.toml
  6. 20 0
      go.mod
  7. 107 0
      go.sum
  8. 70 0
      main.go
  9. 13 0
      protocol/protocol.go
  10. 195 0
      server/client.go
  11. 68 0
      server/crc.go
  12. 79 0
      server/server.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

+ 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/pt100-gateway.iml" filepath="$PROJECT_DIR$/.idea/pt100-gateway.iml" />
+    </modules>
+  </component>
+</project>

+ 9 - 0
.idea/pt100-gateway.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>

binární
bin/linux_amd64/pt100


+ 12 - 0
config/config.toml

@@ -0,0 +1,12 @@
+[Server]
+    Addr = "0.0.0.0"
+    Port = 8996
+    RunMode = "debug"
+[Sparrow]
+    Server = "http://192.168.0.224:18100"
+    ProductKey = "dd5214279131aa7772087d286d838d595dac175886c652fabe6bc1b625041e54c978a9de8e35d982f91311e809266524"
+    DeviceCode = "PT100-4G"
+    Debug = true
+
+
+

+ 20 - 0
go.mod

@@ -0,0 +1,20 @@
+module pt100-gateway
+
+go 1.16
+
+require (
+	github.com/gogf/gf v1.16.9
+	sparrow-sdk v1.0.0
+)
+
+require (
+	github.com/BurntSushi/toml v1.1.0 // indirect
+	github.com/fatih/color v1.13.0 // indirect
+	github.com/fsnotify/fsnotify v1.5.4 // indirect
+	github.com/gorilla/websocket v1.5.0 // indirect
+	go.opentelemetry.io/otel v1.7.0 // indirect
+	golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
+	golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 // indirect
+)
+
+replace sparrow-sdk v1.0.0 => gogs.yehaoji.cn/yongxu/sparrow-sdk.git v1.1.4

+ 107 - 0
go.sum

@@ -0,0 +1,107 @@
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
+github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
+github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 h1:LdXxtjzvZYhhUaonAaAKArG3pyC67kGL3YY+6hGG8G4=
+github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/eclipse/paho.mqtt.golang v1.3.5 h1:sWtmgNxYM9P2sP+xEItMozsR3w0cqZFlqnNN1bdl41Y=
+github.com/eclipse/paho.mqtt.golang v1.3.5/go.mod h1:eTzb4gxwwyWpqBUHGQZ4ABAV7+Jgm1PklsYT/eo8Hcc=
+github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
+github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
+github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
+github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
+github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
+github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
+github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
+github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/gogf/gf v1.16.6/go.mod h1:4LoHfEBl2jbVmZpVx+qk2La3zWr1V315FtF2PVZuyQ8=
+github.com/gogf/gf v1.16.9 h1:Q803UmmRo59+Ws08sMVFOcd8oNpkSWL9vS33hlo/Cyk=
+github.com/gogf/gf v1.16.9/go.mod h1:8Q/kw05nlVRp+4vv7XASBsMe9L1tsVKiGoeP2AHnlkk=
+github.com/gomodule/redigo v1.8.5 h1:nRAxCa+SVsyjSBrtZmG/cqb6VbTmuRzpg/PoTFlpumc=
+github.com/gomodule/redigo v1.8.5/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
+github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
+github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
+github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78=
+github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0=
+github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78=
+github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
+github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
+github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
+github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
+github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
+github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
+github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
+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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+go.opentelemetry.io/otel v1.0.0-RC2/go.mod h1:w1thVQ7qbAy8MHb0IFj8a5Q2QU0l2ksf8u/CN8m3NOM=
+go.opentelemetry.io/otel v1.0.0/go.mod h1:AjRVh9A5/5DE7S+mZtTR6t8vpKKryam+0lREnfmS4cg=
+go.opentelemetry.io/otel v1.7.0 h1:Z2lA3Tdch0iDcrhJXDIlC94XE+bxok1F9B+4Lz/lGsM=
+go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk=
+go.opentelemetry.io/otel/oteltest v1.0.0-RC2/go.mod h1:kiQ4tw5tAL4JLTbcOYwK1CWI1HkT5aiLzHovgOVnz/A=
+go.opentelemetry.io/otel/trace v1.0.0-RC2/go.mod h1:JPQ+z6nNw9mqEGT8o3eoPTdnNI+Aj5JcxEsVGREIAy4=
+go.opentelemetry.io/otel/trace v1.0.0/go.mod h1:PXTWqayeFUlJV1YDNhsJYB184+IvAH814St6o6ajzIs=
+go.opentelemetry.io/otel/trace v1.7.0 h1:O37Iogk1lEkMRXewVtZ1BBTVn5JEp8GrJvP92bJqC6o=
+go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU=
+gogs.yehaoji.cn/yongxu/sparrow-sdk.git v1.1.4 h1:7DEfCFizL5wTNe/0cD8SMNR0W2TFEGVTyeUProeaaEc=
+gogs.yehaoji.cn/yongxu/sparrow-sdk.git v1.1.4/go.mod h1:hWw7D5hrW8f8cOKKdhtlt8HQbdfD2o6PllWMhs0BdQs=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
+golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 h1:GLw7MR8AfAG2GmGcmVgObFOHXYypgGjnGno25RDwn3Y=
+golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+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.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

+ 70 - 0
main.go

@@ -0,0 +1,70 @@
+package main
+
+import (
+	"context"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/os/glog"
+	"github.com/gogf/gf/os/gproc"
+	"os"
+	"pt100-gateway/server"
+	"sparrow-sdk/config"
+	gatewayV2 "sparrow-sdk/v2"
+)
+
+func main() {
+	ctx := context.Background()
+	err := glog.SetLevelStr(g.Cfg().GetString("Server.RunMode"))
+	if err != nil {
+		panic(err)
+	}
+	gw := gatewayV2.NewGateway(&config.Config{
+		SparrowServer: g.Cfg().GetString("Sparrow.Server"),
+		ProductKey:    g.Cfg().GetString("Sparrow.ProductKey"),
+		Protocol:      "mqtt",
+		DeviceCode:    g.Cfg().GetString("Sparrow.DeviceCode"),
+		Version:       "1.0.0",
+		Debug:         g.Cfg().GetBool("Sparrow.Debug"),
+	})
+	if _, err = gw.Register(); err != nil {
+		panic(err)
+	}
+	if _, err = gw.Authentication(); err != nil {
+		panic(err)
+	}
+	// 通用指令回调
+	gw.SetReportCommandCallback(func(deviceCode, subId string) error {
+		return nil
+	})
+	go gw.Connect()
+
+	srv := server.NewServer(
+		ctx,
+		g.Cfg().GetString("Server.Addr"),
+		g.Cfg().GetInt("Server.Port"),
+		gw,
+	)
+	go func() {
+		if err := srv.Start(); err != nil {
+			panic(err)
+		}
+	}()
+	closeReportChan := make(chan struct{})
+	go func() {
+		for {
+			select {
+			case msg := <-gw.RecvCommand():
+				if msg.Data.Cmd == "SetOffset" {
+
+				}
+			case <-closeReportChan:
+				return
+			}
+		}
+	}()
+
+	gproc.AddSigHandlerShutdown(func(sig os.Signal) {
+		gw.Close()
+		srv.Stop()
+	})
+	gproc.Listen()
+}

+ 13 - 0
protocol/protocol.go

@@ -0,0 +1,13 @@
+package protocol
+
+type Data struct {
+	Address string  `json:"address"` // 从机地址
+	Tem1    float32 `json:"tem1"`    // 1路温度
+	Tem2    float32 `json:"tem2"`    // 2路温度
+	Tem3    float32 `json:"tem3"`    // 3路温度
+	Tem4    float32 `json:"tem4"`    // 4路温度
+	Tem5    float32 `json:"tem5"`    // 5路温度
+	Tem6    float32 `json:"tem6"`    // 6路温度
+	Tem7    float32 `json:"tem7"`    // 7路温度
+	Tem8    float32 `json:"tem8"`    // 8路温度
+}

+ 195 - 0
server/client.go

@@ -0,0 +1,195 @@
+package server
+
+import (
+	"encoding/binary"
+	"errors"
+	"fmt"
+	"github.com/gogf/gf/encoding/gbinary"
+	"github.com/gogf/gf/net/gtcp"
+	"github.com/gogf/gf/os/glog"
+	"github.com/gogf/gf/util/gconv"
+	"io"
+	"net"
+	"pt100-gateway/protocol"
+	"strconv"
+	"strings"
+	"syscall"
+	"time"
+)
+
+type Client struct {
+	Id           string
+	srv          *Server
+	conn         *gtcp.Conn
+	sendChan     chan []byte
+	closeChan    chan struct{}
+	closeHandler func(id string, c *Client)
+	regHandler   func(id string, c *Client)
+	isReg        bool
+}
+
+func NewClient(s *Server, conn *gtcp.Conn) *Client {
+	return &Client{
+		srv:       s,
+		conn:      conn,
+		sendChan:  make(chan []byte),
+		closeChan: make(chan struct{}),
+	}
+}
+
+func (c *Client) SetId(id string) {
+	c.Id = id
+}
+
+func (c *Client) SendLoop() {
+	for {
+		select {
+		case buf := <-c.sendChan:
+			err := c.send(buf)
+			if err != nil {
+				glog.Errorf("指令发送失败:%s", err.Error())
+				continue
+			}
+			timer := time.NewTimer(5 * time.Second)
+			for {
+				select {
+				case <-c.closeChan:
+					return
+				case <-timer.C:
+					glog.Error("接收指令超时")
+					break
+				default:
+					receiveBuf, err := c.conn.Recv(-1)
+					if err != nil {
+						c.readError(err)
+						break
+					}
+					if !c.isReg {
+						id := gbinary.DecodeToString(receiveBuf)
+						glog.Debugf("收到注册包!id:%s", id)
+						c.SetId(id)
+						c.isReg = true
+						if c.regHandler != nil {
+							c.regHandler(c.Id, c)
+						}
+						continue
+					}
+					glog.Debugf("收到数据:%2X", receiveBuf)
+					if err := c.decodeAndReport(receiveBuf); err != nil {
+						glog.Debugf("处理数据失败:%s", err.Error())
+						break
+					}
+				}
+				break
+			}
+		}
+	}
+}
+
+// decodeAndReport 收到数据:01 03 10 01 05 01 09 FF FF 01 06 FF FF 01 03 FF FF FF FF E7 EB
+func (c *Client) decodeAndReport(buf []byte) error {
+	length := len(buf)
+	var crc crc
+	crc.reset().pushBytes(buf[0 : length-2])
+	checksum := uint16(buf[length-1])<<8 | uint16(buf[length-2])
+	if checksum != crc.value() {
+		return errors.New(fmt.Sprintf("modbus: response crc '%v' does not match expected '%v'", checksum, crc.value()))
+	}
+	if buf[1] == 0x03 {
+		data := &protocol.Data{}
+		data.Address = gconv.String(gbinary.BeDecodeToInt(buf[0:1]))
+		data.Tem1 = decodeData(buf[3:5])
+		data.Tem2 = decodeData(buf[5:7])
+		data.Tem3 = decodeData(buf[7:9])
+		data.Tem4 = decodeData(buf[9:11])
+		data.Tem5 = decodeData(buf[11:13])
+		data.Tem6 = decodeData(buf[13:15])
+		data.Tem7 = decodeData(buf[15:17])
+		data.Tem8 = decodeData(buf[17:19])
+
+		if err := c.srv.ReportStatus(c.Id, data, "status"); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func decodeData(buf []byte) float32 {
+	if gbinary.BeDecodeToUint16(buf) == 0xFFFF {
+		return 0
+	}
+	value := gconv.Float32(gbinary.BeDecodeToUint16(buf)&0x7FFF) / 10
+
+	if gbinary.BeDecodeToUint16(buf)&0x8000 == 0x8000 {
+		value = -value
+	}
+	return value
+}
+
+func (c *Client) readError(err error) {
+	defer c.closeConnection()
+	if err == io.EOF || isErrConnReset(err) {
+		return
+	}
+	glog.Errorf("读取数据发生错误:%s", err.Error())
+}
+func (c *Client) closeConnection() {
+	_ = c.conn.Close()
+	c.conn = nil
+	close(c.closeChan)
+	c.isReg = false
+	if c.closeHandler != nil {
+		c.closeHandler(c.Id, c)
+	}
+}
+
+// 计算温度值, 处理零下的情况
+func caleTemperature(data []byte) int {
+	var ym uint16
+	var isBlowZero bool
+	var result int
+	bm := binary.BigEndian.Uint16(data)
+	var bitNum = len(data) * 8
+	f := "%." + strconv.Itoa(bitNum) + "b"
+	bmStr := fmt.Sprintf(f, bm)
+	if string(bmStr[0]) == "1" { // blow zero
+		ym = ^bm + 1
+		isBlowZero = true
+	} else {
+		ym = bm
+	}
+	result = int(ym)
+	if isBlowZero {
+		result = int(ym) * -1
+	}
+	return result
+}
+
+// isErrConnReset read: connection reset by peer
+func isErrConnReset(err error) bool {
+	if ne, ok := err.(*net.OpError); ok {
+		return strings.Contains(ne.Err.Error(), syscall.ECONNRESET.Error())
+	}
+	return false
+}
+
+func (c *Client) GetSendByte() {
+	for {
+		c.sendChan <- []byte{0x01, 0x03, 0x00, 0x00, 0x00, 0x08, 0x44, 0x0C}
+		time.Sleep(10 * time.Second)
+	}
+}
+
+func (c *Client) send(buf []byte) error {
+	if c.conn == nil {
+		return nil
+	}
+	glog.Debugf("----->%2X", buf)
+	err := c.conn.Send(buf)
+	if err != nil {
+		glog.Error(c.srv.ctx, err)
+		c.closeConnection()
+		return err
+	}
+	return nil
+}

+ 68 - 0
server/crc.go

@@ -0,0 +1,68 @@
+package server
+
+// Table of CRC values for high–order byte
+var crcHighBytes = []byte{
+	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
+	0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+	0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
+	0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
+	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
+	0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+	0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
+	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
+	0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
+	0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+	0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
+}
+
+// Table of CRC values for low-order byte
+var crcLowBytes = []byte{
+	0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04,
+	0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8,
+	0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC,
+	0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10,
+	0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
+	0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38,
+	0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C,
+	0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0,
+	0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4,
+	0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
+	0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C,
+	0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0,
+	0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54,
+	0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98,
+	0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
+	0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40,
+}
+
+// Cyclical Redundancy Checking
+type crc struct {
+	high byte
+	low  byte
+}
+
+func (crc *crc) reset() *crc {
+	crc.high = 0xFF
+	crc.low = 0xFF
+	return crc
+}
+
+func (crc *crc) pushBytes(bs []byte) *crc {
+	var idx, b byte
+
+	for _, b = range bs {
+		idx = crc.low ^ b
+		crc.low = crc.high ^ crcHighBytes[idx]
+		crc.high = crcLowBytes[idx]
+	}
+	return crc
+}
+
+func (crc *crc) value() uint16 {
+	return uint16(crc.high)<<8 | uint16(crc.low)
+}

+ 79 - 0
server/server.go

@@ -0,0 +1,79 @@
+package server
+
+import (
+	"context"
+	"fmt"
+	"github.com/gogf/gf/container/gmap"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/net/gtcp"
+	"github.com/gogf/gf/os/glog"
+	gatewayV2 "sparrow-sdk/v2"
+	"time"
+)
+
+type Server struct {
+	closeChan chan struct{}
+	srv       *gtcp.Server
+	ctx       context.Context
+	addr      string
+	port      int
+	gateWay   *gatewayV2.Gateway
+	clients   *gmap.Map
+}
+
+func NewServer(ctx context.Context, addr string, port int, gw *gatewayV2.Gateway) *Server {
+	return &Server{
+		closeChan: make(chan struct{}),
+		ctx:       ctx,
+		addr:      addr,
+		port:      port,
+		gateWay:   gw,
+		clients:   gmap.New(false),
+	}
+}
+
+func (s *Server) Start() error {
+	glog.Printf("服务端启动[%s:%d]", s.addr, s.port)
+	srv := gtcp.NewServer(fmt.Sprintf("%s:%d", s.addr, s.port), s.onClientConnect)
+	s.srv = srv
+	return s.srv.Run()
+}
+
+func (s *Server) Stop() {
+	s.clients.Iterator(func(k interface{}, v interface{}) bool {
+		client := v.(*Client)
+		close(client.closeChan)
+		return true
+	})
+	_ = s.srv.Close()
+}
+
+func (s *Server) onClientConnect(conn *gtcp.Conn) {
+	client := NewClient(s, conn)
+	client.closeHandler = func(id string, c *Client) {
+		glog.Debugf("客户端断开:%s", id)
+		if id != "" {
+			_ = s.gateWay.SubDeviceLogout(g.Cfg().GetString("sparrow.DeviceCode"), id)
+			s.clients.Remove(id)
+		}
+	}
+	client.regHandler = func(id string, c *Client) {
+		_ = s.gateWay.SubDeviceLogin(g.Cfg().GetString("Sparrow.DeviceCode"), id)
+		s.clients.Set(id, c)
+	}
+	go client.SendLoop()
+	time.Sleep(10 * time.Second)
+	go client.GetSendByte()
+}
+
+func (s *Server) ReportStatus(subId string, data interface{}, cmd string) error {
+	return s.gateWay.ReportStatus(subId, cmd, data)
+}
+
+func (s *Server) GetClient(subId string) *Client {
+	client := s.clients.Get(subId)
+	if client != nil {
+		return client.(*Client)
+	}
+	return nil
+}