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:
Александр Лазаренко 2025-02-22 21:15:03 +03:00
parent 16da4affa8
commit 102c9bb36a
Signed by: Kerblif
GPG Key ID: 5AFAD6640F4670C3
3 changed files with 267 additions and 132 deletions

View File

@ -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(&params); 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(&params); 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(&params); 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
}
}
}

View File

@ -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
View 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(&params); 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)
}