Refactor network handling with SmartPackage abstraction
Introduced the SmartPackage struct to simplify and centralize logic for handling various payloads, including JSON and alarms. Moved specific handlers into dedicated functions and utilized maps for dynamic process function assignment. Improved error handling and modularity for better scalability and maintainability.
This commit is contained in:
parent
16da4affa8
commit
102c9bb36a
@ -1,10 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"gitea.unprism.ru/KRBL/n9m"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -27,147 +30,139 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
func handle(conn net.Conn) {
|
||||
var err error
|
||||
var rn int
|
||||
func handleSpecialPackages(_ *n9m.SmartPackage, pack n9m.Package) error {
|
||||
switch pack.SSRC {
|
||||
case n9m.SpecialPayloadTypeGPS:
|
||||
fmt.Printf("%+v\n", pack.GPS)
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("unhandled special operation: %d", pack.SSRC)
|
||||
}
|
||||
}
|
||||
|
||||
var serial string
|
||||
func handleCertificateConnect(sPack *n9m.SmartPackage, pack n9m.Package) (err error) {
|
||||
var params n9m.CertificateConnectRequest
|
||||
|
||||
packS := n9m.Package{}
|
||||
tmp := make([]byte, 1024)
|
||||
if err = pack.GetParametersAs(¶ms); err != nil {
|
||||
return fmt.Errorf("failed to get parameters: %w", err)
|
||||
}
|
||||
|
||||
for {
|
||||
rn, err = conn.Read(tmp)
|
||||
var response = n9m.CertificateConnectResponse{
|
||||
ErrorCode: 0,
|
||||
CommandMask: n9m.CommandMaskAll,
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
pack.SetResponse(response)
|
||||
|
||||
if _, err = sPack.Write(pack.PackPackage()); err != nil {
|
||||
return fmt.Errorf("failed to write package: %w", err)
|
||||
}
|
||||
|
||||
fmt.Println("Connected:", params.SerialNumber)
|
||||
sPack.Storage["serial"] = params.SerialNumber
|
||||
|
||||
var request n9m.ConfigModelGetRequest
|
||||
request.MDVR = "?"
|
||||
|
||||
pack.Payload.Module = "CONFIGMODEL"
|
||||
pack.Payload.Operation = "GET"
|
||||
pack.SetParameters(request)
|
||||
|
||||
sPack.Write(pack.PackPackage())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func handleKeepAlive(sPack *n9m.SmartPackage, pack n9m.Package) (err error) {
|
||||
serial := sPack.Storage["serial"]
|
||||
fmt.Println(serial, "still alive!")
|
||||
|
||||
pack.SetResponse(nil)
|
||||
sPack.Write(pack.PackPackage())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func handleGetConfig(sPack *n9m.SmartPackage, pack n9m.Package) (err error) {
|
||||
serial := sPack.Storage["serial"]
|
||||
|
||||
os.WriteFile(fmt.Sprintf("./%s.json", serial), pack.RawPayload, 0644)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func handleUselessAlarms(sPack *n9m.SmartPackage, pack n9m.Package, response n9m.SendAlarmInfoResponse) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleVideoLossAlarm(sPack *n9m.SmartPackage, pack n9m.Package, response n9m.SendAlarmInfoResponse) (err error) {
|
||||
fmt.Println("Video loss alarm!")
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleCameraCoveredAlarm(sPack *n9m.SmartPackage, pack n9m.Package, response n9m.SendAlarmInfoResponse) (err error) {
|
||||
fmt.Println("Camera covered alarm!")
|
||||
return nil
|
||||
}
|
||||
|
||||
func createSmartPackage(conn net.Conn) (pack *n9m.SmartPackage) {
|
||||
pack = n9m.NewSmartPackage(conn)
|
||||
|
||||
pack.AddPayloadHandler(n9m.PayloadTypeSpecial, handleSpecialPackages)
|
||||
|
||||
pack.AddJSONHandler("CERTIFICATE", "CONNECT", handleCertificateConnect)
|
||||
pack.AddJSONHandler("CERTIFICATE", "KEEPALIVE", handleKeepAlive)
|
||||
pack.AddJSONHandler("CONFIGMODEL", "GET", handleGetConfig)
|
||||
|
||||
pack.AddAlarmHandler(n9m.AlarmTypeMotionDetection, handleUselessAlarms)
|
||||
|
||||
pack.AddAlarmHandler(n9m.AlarmTypeVideoLoss, handleVideoLossAlarm)
|
||||
pack.AddAlarmHandler(n9m.AlarmTypeCameraCovered, handleCameraCoveredAlarm)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
go func() {
|
||||
pack := packS
|
||||
pack.Payload.Module = "EVEM"
|
||||
pack.Payload.Operation = "GALARMING"
|
||||
|
||||
ticker := time.NewTicker(5 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for range ticker.C {
|
||||
fmt.Println("Sent!")
|
||||
if _, err := conn.Write(pack.PackPackage()); err != nil {
|
||||
fmt.Println("Failed to send GALARMING:", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
*/
|
||||
|
||||
packS.AddToAccum(tmp[:rn])
|
||||
func isNetConnClosedErr(err error) bool {
|
||||
switch {
|
||||
case
|
||||
errors.Is(err, net.ErrClosed),
|
||||
errors.Is(err, io.EOF),
|
||||
errors.Is(err, syscall.EPIPE):
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for packS.ReadPackage() {
|
||||
switch packS.PayloadType {
|
||||
case n9m.PayloadTypeData:
|
||||
combined := packS.Payload.Module + ":" + packS.Payload.Operation
|
||||
func handle(conn net.Conn) {
|
||||
pack := createSmartPackage(conn)
|
||||
|
||||
switch combined {
|
||||
case "CERTIFICATE:CONNECT":
|
||||
var params n9m.CertificateConnectRequest
|
||||
var err error
|
||||
for {
|
||||
if err = pack.Handle(); err != nil {
|
||||
fmt.Println("Error:", err)
|
||||
|
||||
if err = packS.GetParametersAs(¶ms); err != nil {
|
||||
fmt.Println(combined, err)
|
||||
return
|
||||
}
|
||||
|
||||
var response = n9m.CertificateConnectResponse{
|
||||
ErrorCode: 0,
|
||||
CommandMask: n9m.CommandMaskAll,
|
||||
}
|
||||
|
||||
packS.SetResponse(response)
|
||||
|
||||
if _, err = conn.Write(packS.PackPackage()); err != nil {
|
||||
fmt.Println(combined, err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("Connected:", params.SerialNumber)
|
||||
serial = params.SerialNumber
|
||||
|
||||
var request n9m.ConfigModelGetRequest
|
||||
request.MDVR = "?"
|
||||
packS.Payload.Module = "CONFIGMODEL"
|
||||
packS.Payload.Operation = "GET"
|
||||
packS.SetParameters(request)
|
||||
|
||||
conn.Write(packS.PackPackage())
|
||||
|
||||
/*
|
||||
go func() {
|
||||
pack := packS
|
||||
pack.Payload.Module = "EVEM"
|
||||
pack.Payload.Operation = "GALARMING"
|
||||
|
||||
ticker := time.NewTicker(5 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for range ticker.C {
|
||||
fmt.Println("Sent!")
|
||||
if _, err := conn.Write(pack.PackPackage()); err != nil {
|
||||
fmt.Println("Failed to send GALARMING:", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
*/
|
||||
case "CERTIFICATE:KEEPALIVE":
|
||||
packS.SetResponse(nil)
|
||||
if _, err = conn.Write(packS.PackPackage()); err != nil {
|
||||
fmt.Println(combined, err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("Connection is still alive!")
|
||||
case "EVEM:GGALARMING":
|
||||
fmt.Println(string(packS.RawPayload))
|
||||
|
||||
var response n9m.EventModelGetAlarmingResponse
|
||||
if err = packS.GetResponseAs(&response); err != nil {
|
||||
fmt.Println(combined, err)
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf("%+v\n", response)
|
||||
case "CONFIGMODEL:GET":
|
||||
os.WriteFile(fmt.Sprintf("./%s.json", serial), packS.RawPayload, 0644)
|
||||
case "EVEM:SENDALARMINFO":
|
||||
var params n9m.SendAlarmInfoParameters
|
||||
var response n9m.SendAlarmInfoResponse
|
||||
|
||||
if err = packS.GetParametersAs(¶ms); err != nil {
|
||||
fmt.Printf("Error: %s\nData: %s", err, packS.RawPayload)
|
||||
continue
|
||||
}
|
||||
|
||||
response.ErrorCode = 0
|
||||
response.AlarmType = params.AlarmType
|
||||
response.CommandType = params.CommandType
|
||||
response.AlarmUID = params.AlarmUID
|
||||
response.NumberOfRestarts = params.NumberOfRestarts
|
||||
response.InstructionSerial = params.InstructionSerial
|
||||
|
||||
switch params.AlarmType {
|
||||
case n9m.AlarmTypeMotionDetection:
|
||||
break
|
||||
case n9m.AlarmTypeVideoLoss, n9m.AlarmTypeCameraCovered:
|
||||
var cameraParams n9m.SendAlarmInfoCameraParameters
|
||||
|
||||
if err = packS.GetParametersAs(&cameraParams); err != nil {
|
||||
fmt.Printf("Error: %s\nData: %s", err, packS.RawPayload)
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf("%+v\n", cameraParams)
|
||||
|
||||
packS.SetResponse(response)
|
||||
conn.Write(packS.PackPackage())
|
||||
default:
|
||||
fmt.Println("Unknown alarm type:", params.AlarmType)
|
||||
}
|
||||
default:
|
||||
fmt.Println("Strange operation:", combined)
|
||||
}
|
||||
|
||||
case n9m.PayloadTypeSpecial:
|
||||
switch packS.SSRC {
|
||||
case n9m.SpecialPayloadTypeGPS:
|
||||
fmt.Printf("%+v\n", packS.GPS)
|
||||
default:
|
||||
fmt.Println("Unhandled special operation:", packS.SSRC)
|
||||
}
|
||||
default:
|
||||
fmt.Println("Unhandled operation:", packS.PayloadType)
|
||||
if isNetConnClosedErr(err) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
16
scheme.go
16
scheme.go
@ -3,6 +3,7 @@ package n9m
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
@ -137,3 +138,18 @@ type Package struct {
|
||||
|
||||
Accum []byte
|
||||
}
|
||||
|
||||
type ProcessFunc func(*SmartPackage, Package) error
|
||||
type AlarmProcessFunc func(*SmartPackage, Package, SendAlarmInfoResponse) error
|
||||
|
||||
type SmartPackage struct {
|
||||
pack Package
|
||||
|
||||
conn net.Conn
|
||||
buff []byte
|
||||
|
||||
payloadProcess map[PayloadType]ProcessFunc
|
||||
jsonProcess map[string]ProcessFunc
|
||||
alarmProcess map[AlarmType]AlarmProcessFunc
|
||||
Storage map[string]interface{}
|
||||
}
|
||||
|
124
smart.go
Normal file
124
smart.go
Normal file
@ -0,0 +1,124 @@
|
||||
package n9m
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
func NewSmartPackage(conn net.Conn) *SmartPackage {
|
||||
return &SmartPackage{
|
||||
pack: Package{},
|
||||
conn: conn,
|
||||
buff: make([]byte, 1024),
|
||||
payloadProcess: make(map[PayloadType]ProcessFunc),
|
||||
jsonProcess: make(map[string]ProcessFunc),
|
||||
alarmProcess: make(map[AlarmType]AlarmProcessFunc),
|
||||
Storage: make(map[string]interface{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (pack *SmartPackage) AddPayloadHandler(payloadType PayloadType, processFunc ProcessFunc) {
|
||||
pack.payloadProcess[payloadType] = processFunc
|
||||
}
|
||||
|
||||
func (pack *SmartPackage) AddJSONHandler(module, operation string, processFunc ProcessFunc) {
|
||||
pack.jsonProcess[fmt.Sprintf("%s:%s", module, operation)] = processFunc
|
||||
}
|
||||
|
||||
func (pack *SmartPackage) AddAlarmHandler(alarmType AlarmType, processFunc AlarmProcessFunc) {
|
||||
pack.alarmProcess[alarmType] = processFunc
|
||||
}
|
||||
|
||||
func (pack *SmartPackage) handleAlarm() (err error) {
|
||||
if !(pack.pack.PayloadType == PayloadTypeData && pack.pack.Payload.Module == "EVEM" && pack.pack.Payload.Operation == "SENDALARMINFO") {
|
||||
return fmt.Errorf("invalid payload type or operation for alarm handling")
|
||||
}
|
||||
|
||||
var params SendAlarmInfoParameters
|
||||
if err = pack.pack.GetParametersAs(¶ms); err != nil {
|
||||
return fmt.Errorf("invalid payload")
|
||||
}
|
||||
|
||||
var processFunc AlarmProcessFunc
|
||||
var ok bool
|
||||
if processFunc, ok = pack.alarmProcess[params.AlarmType]; !ok {
|
||||
return fmt.Errorf("unhanled alarm")
|
||||
}
|
||||
|
||||
var response SendAlarmInfoResponse
|
||||
|
||||
response.ErrorCode = 0
|
||||
response.AlarmType = params.AlarmType
|
||||
response.CommandType = params.CommandType
|
||||
response.AlarmUID = params.AlarmUID
|
||||
response.NumberOfRestarts = params.NumberOfRestarts
|
||||
response.InstructionSerial = params.InstructionSerial
|
||||
|
||||
return processFunc(pack, pack.pack, response)
|
||||
}
|
||||
|
||||
func (pack *SmartPackage) handleJson() (err error) {
|
||||
if pack.pack.PayloadType != PayloadTypeData {
|
||||
return fmt.Errorf("invalid json payload type")
|
||||
}
|
||||
|
||||
if err = pack.handleAlarm(); err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var processFunc ProcessFunc
|
||||
var ok bool
|
||||
if processFunc, ok = pack.jsonProcess[fmt.Sprintf("%s:%s", pack.pack.Payload.Module, pack.pack.Payload.Operation)]; !ok {
|
||||
return fmt.Errorf("unhanled operation")
|
||||
}
|
||||
|
||||
return processFunc(pack, pack.pack)
|
||||
}
|
||||
|
||||
func (pack *SmartPackage) handle() (err error) {
|
||||
if err = pack.handleJson(); err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var processFunc ProcessFunc
|
||||
var ok bool
|
||||
if processFunc, ok = pack.payloadProcess[pack.pack.PayloadType]; !ok {
|
||||
return fmt.Errorf("unhanled payload type")
|
||||
}
|
||||
|
||||
return processFunc(pack, pack.pack)
|
||||
}
|
||||
|
||||
func (pack *SmartPackage) handleLoop() (err error) {
|
||||
for pack.pack.ReadPackage() {
|
||||
if err = pack.handle(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (pack *SmartPackage) Handle() (err error) {
|
||||
if err = pack.handleLoop(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var rn int
|
||||
|
||||
if rn, err = pack.conn.Read(pack.buff); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
pack.pack.AddToAccum(pack.buff[:rn])
|
||||
|
||||
return pack.handleLoop()
|
||||
}
|
||||
|
||||
func (pack *SmartPackage) GetPackage() Package {
|
||||
return pack.pack
|
||||
}
|
||||
|
||||
func (pack *SmartPackage) Write(data []byte) (int, error) {
|
||||
return pack.conn.Write(data)
|
||||
}
|
Loading…
Reference in New Issue
Block a user