Compare commits
	
		
			21 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 0a00780468 | |||
| 91cb2927e3 | |||
| 1f5248ac87 | |||
| ec06ee84de | |||
| 92e59cf650 | |||
| 5c5496fa7f | |||
| 2737e23369 | |||
| b4ec470e2f | |||
| 6012bccf64 | |||
| 4c631db418 | |||
| 26f354d82d | |||
| edc86fb706 | |||
| eaaa634558 | |||
| 51308a2395 | |||
| 9d2db3672c | |||
| 519ad39c0f | |||
| 7c348629d6 | |||
| 58b1c67b97 | |||
| 102c9bb36a | |||
| 16da4affa8 | |||
| f3083820dc | 
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | .idea | ||||||
| @@ -1,41 +0,0 @@ | |||||||
| package n9m |  | ||||||
|  |  | ||||||
| func (e *Package) RequestConnect(session string, serial string, numOfCams int) { |  | ||||||
| 	e.Payload = map[string]any{ |  | ||||||
| 		"MODULE":    "CERTIFICATE", |  | ||||||
| 		"OPERATION": "CONNECT", |  | ||||||
| 		"PARAMETER": map[string]any{ |  | ||||||
| 			"DSNO":    serial, |  | ||||||
| 			"CHANNEL": numOfCams, |  | ||||||
| 		}, |  | ||||||
| 		"SESSION": session, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // video server util |  | ||||||
| func (e *Package) ResponseConnect(Sid string, streamName string) { |  | ||||||
| 	e.Payload = map[string]any{ |  | ||||||
| 		"MODULE":    "CERTIFICATE", |  | ||||||
| 		"OPERATION": "CREATESTREAM", |  | ||||||
| 		"RESPONSE": map[string]any{ |  | ||||||
| 			"ERRORCODE":  0, |  | ||||||
| 			"STREAMNAME": streamName, |  | ||||||
| 		}, |  | ||||||
| 		"SESSION": Sid, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // main server util |  | ||||||
| func (e *Package) ResponseCertificateConnect(Sid string) { |  | ||||||
| 	e.Payload = map[string]any{ |  | ||||||
| 		"MODULE":    "CERTIFICATE", |  | ||||||
| 		"OPERATION": "CONNECT", |  | ||||||
| 		"RESPONSE": map[string]any{ |  | ||||||
| 			"ERRORCAUSE": "", |  | ||||||
| 			"ERRORCODE":  0, |  | ||||||
| 			"MASKCMD":    5, |  | ||||||
| 			"PRO":        "1.0.5", |  | ||||||
| 		}, |  | ||||||
| 		"SESSION": Sid, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										256
									
								
								cmd/dev-client/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										256
									
								
								cmd/dev-client/main.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,256 @@ | |||||||
|  | package main | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"gitea.unprism.ru/KRBL/n9m/v2/pkg/models" | ||||||
|  | 	"gitea.unprism.ru/KRBL/n9m/v2/pkg/protocol" | ||||||
|  | 	"gitea.unprism.ru/KRBL/n9m/v2/pkg/smart" | ||||||
|  | 	"gitea.unprism.ru/KRBL/n9m/v2/pkg/utils" | ||||||
|  | 	"io" | ||||||
|  | 	"net" | ||||||
|  | 	"os" | ||||||
|  | 	"syscall" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var videoPack *smart.SmartChannelPackage | ||||||
|  |  | ||||||
|  | func main() { | ||||||
|  | 	conn, err := net.Dial("tcp", "10.100.100.99:9006") | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		panic(err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var pack = protocol.Package{} | ||||||
|  |  | ||||||
|  | 	pack.Payload.Module = "CERTIFICATE" | ||||||
|  | 	pack.Payload.Operation = "CONNECT" | ||||||
|  | 	pack.SetParameters(models.CertificateConnectClientRequest{}) | ||||||
|  | 	conn.Write(pack.PackPackage()) | ||||||
|  |  | ||||||
|  | 	handle(conn) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func handleSpecialPackages(_ *smart.SmartPackage, pack protocol.Package) error { | ||||||
|  | 	switch pack.SSRC { | ||||||
|  | 	case protocol.SpecialPayloadTypeGPS: | ||||||
|  | 		fmt.Printf("%+v\n", pack.GPS) | ||||||
|  | 		return nil | ||||||
|  | 	default: | ||||||
|  | 		return fmt.Errorf("unhandled special operation: %d", pack.SSRC) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func handleLivePackages(_ *smart.SmartPackage, pack protocol.Package) error { | ||||||
|  | 	fmt.Printf("%+v\n", pack) | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func handleCertificateConnect(sPack *smart.SmartPackage, pack protocol.Package) (err error) { | ||||||
|  | 	var params models.CertificateConnectClientResponse | ||||||
|  |  | ||||||
|  | 	if err = pack.GetResponseAs(¶ms); err != nil { | ||||||
|  | 		return fmt.Errorf("failed to get response: %w", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var response = models.CertificateVerificationRequest{ | ||||||
|  | 		S0: utils.GenerateVerifyKey(params.S0), | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	pack.Payload.Operation = "VERIFY" | ||||||
|  | 	pack.SetParameters(response) | ||||||
|  |  | ||||||
|  | 	sPack.Write(pack.PackPackage()) | ||||||
|  |  | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func handleVerify(sPack *smart.SmartPackage, pack protocol.Package) (err error) { | ||||||
|  | 	var params models.CertificateVerificationResponse | ||||||
|  |  | ||||||
|  | 	if err = pack.GetResponseAs(¶ms); err != nil { | ||||||
|  | 		return fmt.Errorf("failed to get response: %w", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if params.ErrorCode == 0 { | ||||||
|  | 		fmt.Println("ШАЛОСТЬ УДАЛАСЬ!") | ||||||
|  | 	} else { | ||||||
|  | 		fmt.Println("шалость НЕ удалась(((") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var request = models.CertificateLoginRequest{ | ||||||
|  | 		ClientID: 0, | ||||||
|  | 		MAC:      "", | ||||||
|  | 		User:     "admin", | ||||||
|  | 		Password: "", | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	pack.Payload.Operation = "LOGIN" | ||||||
|  | 	pack.SetParameters(request) | ||||||
|  |  | ||||||
|  | 	sPack.Write(pack.PackPackage()) | ||||||
|  |  | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func handleLogin(sPack *smart.SmartPackage, pack protocol.Package) (err error) { | ||||||
|  | 	var params models.CertificateLoginResponse | ||||||
|  |  | ||||||
|  | 	if err = pack.GetResponseAs(¶ms); err != nil { | ||||||
|  | 		return fmt.Errorf("failed to get response: %w", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	conn, err := net.Dial("tcp", "10.100.100.99:9006") | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		panic(err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	videoPack, err = smart.NewSmartChannelPackage(conn, sPack) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		panic(err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	go videoPack.Run() | ||||||
|  |  | ||||||
|  | 	videoPack.AddLiveSource(2, func(data []byte) error { | ||||||
|  | 		fmt.Println("Есть контакт!") | ||||||
|  | 		return nil | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	var request = models.MediaStreamModelRequestLiveVideoRequest{ | ||||||
|  | 		StreamName: videoPack.GetChannelName(), | ||||||
|  | 		StreamType: models.StreamTypeMain, | ||||||
|  | 		Channel:    4, | ||||||
|  | 		AudioValid: 4, | ||||||
|  | 		FrameMode:  0, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	pack.Payload.Module = "MEDIASTREAMMODEL" | ||||||
|  | 	pack.Payload.Operation = "REQUESTALIVEVIDEO" | ||||||
|  | 	pack.SetParameters(request) | ||||||
|  |  | ||||||
|  | 	sPack.Write(pack.PackPackage()) | ||||||
|  |  | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func handleRequestLiveVideo(sPack *smart.SmartPackage, pack protocol.Package) (err error) { | ||||||
|  | 	var params models.MediaStreamModelRequestLiveVideoResponse | ||||||
|  |  | ||||||
|  | 	if err = pack.GetResponseAs(¶ms); err != nil { | ||||||
|  | 		return fmt.Errorf("failed to get response: %w", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if params.ErrorCode != 0 { | ||||||
|  | 		fmt.Println("Request live stream error:", params.ErrorCode, params.ErrorCause) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	fmt.Printf("%+v\n", params) | ||||||
|  |  | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func handleKeepAlive(sPack *smart.SmartPackage, pack protocol.Package) (err error) { | ||||||
|  | 	serial := sPack.Storage["serial"] | ||||||
|  | 	fmt.Println(serial, "still alive!") | ||||||
|  |  | ||||||
|  | 	pack.SetResponse(nil) | ||||||
|  | 	sPack.Write(pack.PackPackage()) | ||||||
|  |  | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func handleGetConfig(sPack *smart.SmartPackage, pack protocol.Package) (err error) { | ||||||
|  | 	serial := sPack.Storage["serial"] | ||||||
|  |  | ||||||
|  | 	os.WriteFile(fmt.Sprintf("./%s.json", serial), pack.RawPayload, 0644) | ||||||
|  |  | ||||||
|  | 	var request models.ConfigModelSetRequest | ||||||
|  |  | ||||||
|  | 	if err = pack.GetParametersAs(&request); err != nil { | ||||||
|  | 		fmt.Println(err) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func handleUselessAlarms(sPack *smart.SmartPackage, pack protocol.Package, response models.SendAlarmInfoResponse) (err error) { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func handleVideoLossAlarm(sPack *smart.SmartPackage, pack protocol.Package, response models.SendAlarmInfoResponse) (err error) { | ||||||
|  | 	fmt.Println("Video loss alarm!") | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func handleCameraCoveredAlarm(sPack *smart.SmartPackage, pack protocol.Package, response models.SendAlarmInfoResponse) (err error) { | ||||||
|  | 	fmt.Println("Camera covered alarm!") | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func handleSPI(_ *smart.SmartPackage, pack protocol.Package) (err error) { | ||||||
|  | 	var params models.SpiParameters | ||||||
|  |  | ||||||
|  | 	if err = pack.GetParametersAs(¶ms); err != nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	fmt.Printf("%+v\n", params) | ||||||
|  |  | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func createSmartPackage(conn net.Conn) (pack *smart.SmartPackage) { | ||||||
|  | 	pack = smart.NewSmartPackage(conn) | ||||||
|  |  | ||||||
|  | 	pack.AddPayloadHandler(protocol.PayloadTypeLive, handleLivePackages) | ||||||
|  | 	pack.AddPayloadHandler(protocol.PayloadTypeSpecial, handleSpecialPackages) | ||||||
|  |  | ||||||
|  | 	pack.AddJSONHandler("CERTIFICATE", "CONNECT", handleCertificateConnect) | ||||||
|  | 	pack.AddJSONHandler("CERTIFICATE", "VERIFY", handleVerify) | ||||||
|  | 	pack.AddJSONHandler("CERTIFICATE", "LOGIN", handleLogin) | ||||||
|  | 	pack.AddJSONHandler("CERTIFICATE", "KEEPALIVE", handleKeepAlive) | ||||||
|  | 	pack.AddJSONHandler("MEDIASTREAMMODEL", "REQUESTALIVEVIDEO", handleRequestLiveVideo) | ||||||
|  | 	pack.AddJSONHandler("CONFIGMODEL", "GET", handleGetConfig) | ||||||
|  | 	pack.AddJSONHandler("DEVEMM", "SPI", handleSPI) | ||||||
|  |  | ||||||
|  | 	pack.AddAlarmHandler(protocol.AlarmTypeMotionDetection, handleUselessAlarms) | ||||||
|  |  | ||||||
|  | 	pack.AddAlarmHandler(protocol.AlarmTypeVideoLoss, handleVideoLossAlarm) | ||||||
|  | 	pack.AddAlarmHandler(protocol.AlarmTypeCameraCovered, handleCameraCoveredAlarm) | ||||||
|  |  | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | 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 | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func handle(conn net.Conn) { | ||||||
|  | 	pack := createSmartPackage(conn) | ||||||
|  |  | ||||||
|  | 	var err error | ||||||
|  | 	for { | ||||||
|  | 		if err = pack.Handle(); err != nil { | ||||||
|  | 			fmt.Println("Error:", err) | ||||||
|  |  | ||||||
|  | 			if isNetConnClosedErr(err) { | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										191
									
								
								cmd/dev-server/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								cmd/dev-server/main.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,191 @@ | |||||||
|  | package main | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"gitea.unprism.ru/KRBL/n9m/v2/pkg/models" | ||||||
|  | 	"gitea.unprism.ru/KRBL/n9m/v2/pkg/protocol" | ||||||
|  | 	"gitea.unprism.ru/KRBL/n9m/v2/pkg/smart" | ||||||
|  | 	"io" | ||||||
|  | 	"net" | ||||||
|  | 	"os" | ||||||
|  | 	"syscall" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func main() { | ||||||
|  | 	ln, err := net.Listen("tcp", "0.0.0.0:5556") | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		panic(err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for { | ||||||
|  | 		var conn net.Conn | ||||||
|  |  | ||||||
|  | 		conn, err = ln.Accept() | ||||||
|  |  | ||||||
|  | 		if err != nil { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		go handle(conn) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func handleSpecialPackages(_ *smart.SmartPackage, pack protocol.Package) error { | ||||||
|  | 	switch pack.SSRC { | ||||||
|  | 	case protocol.SpecialPayloadTypeGPS: | ||||||
|  | 		fmt.Printf("%+v\n", pack.GPS) | ||||||
|  | 		return nil | ||||||
|  | 	default: | ||||||
|  | 		return fmt.Errorf("unhandled special operation: %d", pack.SSRC) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func handleCertificateConnect(sPack *smart.SmartPackage, pack protocol.Package) (err error) { | ||||||
|  | 	var params models.CertificateConnectRequest | ||||||
|  |  | ||||||
|  | 	if err = pack.GetParametersAs(¶ms); err != nil { | ||||||
|  | 		return fmt.Errorf("failed to get parameters: %w", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var response = models.CertificateConnectResponse{ | ||||||
|  | 		ErrorCode:   0, | ||||||
|  | 		CommandMask: models.CommandMaskAll, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	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 models.ConfigModelGetRequest | ||||||
|  | 	request.MDVR = "?" | ||||||
|  |  | ||||||
|  | 	pack.Payload.Module = "CONFIGMODEL" | ||||||
|  | 	pack.Payload.Operation = "GET" | ||||||
|  | 	pack.SetParameters(request) | ||||||
|  |  | ||||||
|  | 	sPack.Write(pack.PackPackage()) | ||||||
|  |  | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func handleKeepAlive(sPack *smart.SmartPackage, pack protocol.Package) (err error) { | ||||||
|  | 	serial := sPack.Storage["serial"] | ||||||
|  | 	fmt.Println(serial, "still alive!") | ||||||
|  |  | ||||||
|  | 	pack.SetResponse(nil) | ||||||
|  | 	sPack.Write(pack.PackPackage()) | ||||||
|  |  | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func handleGetConfig(sPack *smart.SmartPackage, pack protocol.Package) (err error) { | ||||||
|  | 	serial := sPack.Storage["serial"] | ||||||
|  |  | ||||||
|  | 	os.WriteFile(fmt.Sprintf("./%s.json", serial), pack.RawPayload, 0644) | ||||||
|  |  | ||||||
|  | 	var request models.ConfigModelSetRequest | ||||||
|  |  | ||||||
|  | 	if err = pack.GetParametersAs(&request); err != nil { | ||||||
|  | 		fmt.Println(err) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func handleUselessAlarms(sPack *smart.SmartPackage, pack protocol.Package, response models.SendAlarmInfoResponse) (err error) { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func handleVideoLossAlarm(sPack *smart.SmartPackage, pack protocol.Package, response models.SendAlarmInfoResponse) (err error) { | ||||||
|  | 	fmt.Println("Video loss alarm!") | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func handleCameraCoveredAlarm(sPack *smart.SmartPackage, pack protocol.Package, response models.SendAlarmInfoResponse) (err error) { | ||||||
|  | 	fmt.Println("Camera covered alarm!") | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func handleSPI(_ *smart.SmartPackage, pack protocol.Package) (err error) { | ||||||
|  | 	var params models.SpiParameters | ||||||
|  |  | ||||||
|  | 	if err = pack.GetParametersAs(¶ms); err != nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	fmt.Printf("%+v\n", params) | ||||||
|  |  | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func createSmartPackage(conn net.Conn) (pack *smart.SmartPackage) { | ||||||
|  | 	pack = smart.NewSmartPackage(conn) | ||||||
|  |  | ||||||
|  | 	pack.AddPayloadHandler(protocol.PayloadTypeSpecial, handleSpecialPackages) | ||||||
|  |  | ||||||
|  | 	pack.AddJSONHandler("CERTIFICATE", "CONNECT", handleCertificateConnect) | ||||||
|  | 	pack.AddJSONHandler("CERTIFICATE", "KEEPALIVE", handleKeepAlive) | ||||||
|  | 	pack.AddJSONHandler("CONFIGMODEL", "GET", handleGetConfig) | ||||||
|  | 	pack.AddJSONHandler("DEVEMM", "SPI", handleSPI) | ||||||
|  |  | ||||||
|  | 	pack.AddAlarmHandler(protocol.AlarmTypeMotionDetection, handleUselessAlarms) | ||||||
|  |  | ||||||
|  | 	pack.AddAlarmHandler(protocol.AlarmTypeVideoLoss, handleVideoLossAlarm) | ||||||
|  | 	pack.AddAlarmHandler(protocol.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 | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | 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 | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func handle(conn net.Conn) { | ||||||
|  | 	pack := createSmartPackage(conn) | ||||||
|  |  | ||||||
|  | 	var err error | ||||||
|  | 	for { | ||||||
|  | 		if err = pack.Handle(); err != nil { | ||||||
|  | 			fmt.Println("Error:", err) | ||||||
|  |  | ||||||
|  | 			if isNetConnClosedErr(err) { | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										36
									
								
								devemm.go
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								devemm.go
									
									
									
									
									
								
							| @@ -1,36 +0,0 @@ | |||||||
| package n9m |  | ||||||
|  |  | ||||||
| import "fmt" |  | ||||||
|  |  | ||||||
| // main server util |  | ||||||
| func (e *Package) RequestGeolocation(serial int, Sid string) { |  | ||||||
| 	e.Payload = map[string]any{ |  | ||||||
| 		"MODULE":    "DEVEMM", |  | ||||||
| 		"OPERATION": "GETPOS", |  | ||||||
| 		"PARAMETER": map[string]any{ |  | ||||||
| 			"SERIAL": serial, |  | ||||||
| 		}, |  | ||||||
| 		"SESSION": Sid, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (e *Package) ResponseGeolocation(errorCode int, errorCause string, serial int, longitude float32, latitude float32, altitude float32, speed int, course int, time string) { |  | ||||||
| 	e.Payload = map[string]any{ |  | ||||||
| 		"MODULE":    "DEVEMM", |  | ||||||
| 		"OPERATION": "GETPOS", |  | ||||||
| 		"RESPONSE": map[string]any{ |  | ||||||
| 			"ERRORCODE":  errorCode, |  | ||||||
| 			"ERRORCAUSE": errorCause, |  | ||||||
| 			"SERIAL":     serial, |  | ||||||
| 			"P": map[string]any{ |  | ||||||
| 				"V": errorCode == 0, |  | ||||||
| 				"J": fmt.Sprintf("%4.6v", longitude), |  | ||||||
| 				"W": fmt.Sprintf("%4.6v", latitude), |  | ||||||
| 				"H": fmt.Sprintf("%4.6v", altitude), |  | ||||||
| 				"S": speed,  // unit - 0.01 km/h |  | ||||||
| 				"C": course, // direction (angle from north) |  | ||||||
| 				"T": time,   // yyyymmddhhmmss |  | ||||||
| 			}, |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										18
									
								
								evem.go
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								evem.go
									
									
									
									
									
								
							| @@ -1,18 +0,0 @@ | |||||||
| package n9m |  | ||||||
|  |  | ||||||
| // main server util |  | ||||||
| func (e *Package) ResponseAlarm(alarmType int64, alarmUID int64, cmdno int64, cmdtype int64, run int64, serial string, Sid string) { |  | ||||||
| 	e.Payload = map[string]any{ |  | ||||||
| 		"MODULE":    "EVEM", |  | ||||||
| 		"OPERATION": "SENDALARMINFO", |  | ||||||
| 		"RESPONSE": map[string]any{ |  | ||||||
| 			"ALARMTYPE": alarmType, |  | ||||||
| 			"ALARMUID":  alarmUID, |  | ||||||
| 			"CMDNO":     cmdno, |  | ||||||
| 			"CMDTYPE":   cmdtype, |  | ||||||
| 			"ERRORCODE": 0, |  | ||||||
| 			"RUN":       run, |  | ||||||
| 		}, |  | ||||||
| 		"SESSION": Sid, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										12
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								go.mod
									
									
									
									
									
								
							| @@ -1,13 +1,5 @@ | |||||||
| module gitea.unprism.ru/KRBL/n9m | module gitea.unprism.ru/KRBL/n9m/v2 | ||||||
|  |  | ||||||
| go 1.21.3 | go 1.21.3 | ||||||
|  |  | ||||||
| require ( | require github.com/icza/bitio v1.1.0 | ||||||
| 	github.com/icza/bitio v1.1.0 |  | ||||||
| 	github.com/tidwall/gjson v1.17.0 |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| require ( |  | ||||||
| 	github.com/tidwall/match v1.1.1 // indirect |  | ||||||
| 	github.com/tidwall/pretty v1.2.1 // indirect |  | ||||||
| ) |  | ||||||
|   | |||||||
							
								
								
									
										7
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								go.sum
									
									
									
									
									
								
							| @@ -2,10 +2,3 @@ github.com/icza/bitio v1.1.0 h1:ysX4vtldjdi3Ygai5m1cWy4oLkhWTAi+SyO6HC8L9T0= | |||||||
| github.com/icza/bitio v1.1.0/go.mod h1:0jGnlLAx8MKMr9VGnn/4YrvZiprkvBelsVIbA9Jjr9A= | github.com/icza/bitio v1.1.0/go.mod h1:0jGnlLAx8MKMr9VGnn/4YrvZiprkvBelsVIbA9Jjr9A= | ||||||
| github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6 h1:8UsGZ2rr2ksmEru6lToqnXgA8Mz1DP11X4zSJ159C3k= | github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6 h1:8UsGZ2rr2ksmEru6lToqnXgA8Mz1DP11X4zSJ159C3k= | ||||||
| github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6/go.mod h1:xQig96I1VNBDIWGCdTt54nHt6EeI639SmHycLYL7FkA= | github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6/go.mod h1:xQig96I1VNBDIWGCdTt54nHt6EeI639SmHycLYL7FkA= | ||||||
| github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM= |  | ||||||
| github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= |  | ||||||
| github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= |  | ||||||
| github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= |  | ||||||
| github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= |  | ||||||
| github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= |  | ||||||
| github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= |  | ||||||
|   | |||||||
							
								
								
									
										175
									
								
								io.go
									
									
									
									
									
								
							
							
						
						
									
										175
									
								
								io.go
									
									
									
									
									
								
							| @@ -1,175 +0,0 @@ | |||||||
| package n9m |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"bytes" |  | ||||||
| 	"encoding/json" |  | ||||||
| 	"fmt" |  | ||||||
| 	"log" |  | ||||||
|  |  | ||||||
| 	"github.com/icza/bitio" |  | ||||||
| 	"github.com/tidwall/gjson" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // Extract fields from JSON |  | ||||||
| func (e *Package) SaveJsonFields() { |  | ||||||
| 	if value, exist := e.Payload["MODULE"]; exist { |  | ||||||
| 		e.Json.Module = value.(string) |  | ||||||
| 	} else { |  | ||||||
| 		e.Json.Module = "" |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if value, exist := e.Payload["KEY"]; exist { |  | ||||||
| 		e.Json.Key = value.(string) |  | ||||||
| 	} else { |  | ||||||
| 		e.Json.Key = "" |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if value, exist := e.Payload["OPERATION"]; exist { |  | ||||||
| 		e.Json.Operation = value.(string) |  | ||||||
| 	} else { |  | ||||||
| 		e.Json.Operation = "" |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if value, exist := e.Payload["RESPONSE"]; exist { |  | ||||||
| 		e.Json.Response = value.(map[string]interface{}) |  | ||||||
| 	} else { |  | ||||||
| 		e.Json.Response = make(map[string]interface{}) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if value, exist := e.Payload["PARAMETER"]; exist { |  | ||||||
| 		e.Json.Parameters = value.(map[string]interface{}) |  | ||||||
| 	} else if value, exist := e.Payload["PARAMETERS"]; exist { |  | ||||||
| 		e.Json.Parameters = value.(map[string]interface{}) |  | ||||||
| 	} else { |  | ||||||
| 		e.Json.Parameters = make(map[string]interface{}) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Read package |  | ||||||
| func (e *Package) ReadPackage() bool { |  | ||||||
| 	if len(e.Accum) < 12 { |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	r := bitio.NewReader(bytes.NewBuffer(e.Accum)) |  | ||||||
| 	e.Version = r.TryReadBits(2) |  | ||||||
| 	e.Encription = r.TryReadBits(1) |  | ||||||
| 	e.Mark = r.TryReadBits(1) |  | ||||||
| 	e.CC = r.TryReadBits(4) |  | ||||||
| 	e.PayloadType = r.TryReadBits(8) |  | ||||||
| 	e.SSRC = r.TryReadBits(16) |  | ||||||
|  |  | ||||||
| 	// log.Println(e.PayloadType) |  | ||||||
|  |  | ||||||
| 	is_special := e.Encription == 1 && e.Mark == 1 |  | ||||||
| 	if is_special { |  | ||||||
| 		r.TryReadBits(8 * 4) |  | ||||||
| 		e.PayloadLen = r.TryReadBits(8) |  | ||||||
| 		r.TryReadBits(3 * 8) |  | ||||||
|  |  | ||||||
| 		if uint64(len(e.Accum)) < e.PayloadLen+12 { |  | ||||||
| 			return false |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		e.PayloadLen = r.TryReadBits(32) |  | ||||||
| 		// WTF: e.CC is useless |  | ||||||
| 		for i := uint64(0); i < 1; i++ { |  | ||||||
| 			e.CSRC[i] = r.TryReadBits(32) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	numOfBytes := 0 |  | ||||||
| 	rawbytes := []byte{} |  | ||||||
| 	if e.PayloadLen != 0 { |  | ||||||
| 		if e.PayloadLen > 1000000 { |  | ||||||
| 			log.Printf("%v\n", e) |  | ||||||
| 			log.Panicln("CORRUPTED PACKAGE") |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		rawbytes = make([]byte, e.PayloadLen) |  | ||||||
| 		numOfBytes, _ = r.Read(rawbytes) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if numOfBytes != int(e.PayloadLen) { |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	e.Raw = e.Accum[:12+e.PayloadLen] |  | ||||||
| 	e.Accum = e.Accum[12+e.PayloadLen:] |  | ||||||
| 	e.RawPayload = rawbytes |  | ||||||
|  |  | ||||||
| 	var ok bool |  | ||||||
| 	e.GPayload = gjson.Parse(string(rawbytes)) |  | ||||||
| 	e.Payload, ok = e.GPayload.Value().(map[string]interface{}) |  | ||||||
| 	if !ok { |  | ||||||
| 		e.Payload = gjson.Parse("{}").Value().(map[string]interface{}) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	e.SaveJsonFields() |  | ||||||
|  |  | ||||||
| 	return e.PayloadLen > 0 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (e *Package) PackPackage() []byte { |  | ||||||
| 	e.SaveJsonFields() |  | ||||||
|  |  | ||||||
| 	b := &bytes.Buffer{} |  | ||||||
| 	w := bitio.NewWriter(b) |  | ||||||
|  |  | ||||||
| 	w.TryWriteBits(e.Version, 2) |  | ||||||
| 	w.TryWriteBits(e.Encription, 1) |  | ||||||
| 	w.TryWriteBits(e.Mark, 1) |  | ||||||
| 	w.TryWriteBits(e.CC, 4) |  | ||||||
| 	w.TryWriteBits(e.PayloadType, 8) |  | ||||||
| 	w.TryWriteBits(e.SSRC, 16) |  | ||||||
| 	conv, err := json.Marshal(e.Payload) |  | ||||||
|  |  | ||||||
| 	if err != nil { |  | ||||||
| 		fmt.Println(err) |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	e.PayloadLen = uint64(len(conv)) |  | ||||||
| 	if e.PayloadLen != 0 { |  | ||||||
| 		e.PayloadLen++ |  | ||||||
| 	} |  | ||||||
| 	w.TryWriteBits(e.PayloadLen, 32) |  | ||||||
|  |  | ||||||
| 	// WTF: e.CC is useless |  | ||||||
| 	for i := uint64(0); i < 1; i++ { |  | ||||||
| 		w.TryWriteBits(e.CSRC[i], 32) |  | ||||||
| 	} |  | ||||||
| 	if e.PayloadLen != 0 { |  | ||||||
| 		w.Write(conv) |  | ||||||
| 		w.Write([]byte{0}) |  | ||||||
| 	} |  | ||||||
| 	w.Close() |  | ||||||
|  |  | ||||||
| 	return b.Bytes() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (e *Package) PackRawPackage() []byte { |  | ||||||
| 	b := &bytes.Buffer{} |  | ||||||
| 	w := bitio.NewWriter(b) |  | ||||||
|  |  | ||||||
| 	w.TryWriteBits(e.Version, 2) |  | ||||||
| 	w.TryWriteBits(e.Encription, 1) |  | ||||||
| 	w.TryWriteBits(e.Mark, 1) |  | ||||||
| 	w.TryWriteBits(e.CC, 4) |  | ||||||
| 	w.TryWriteBits(e.PayloadType, 8) |  | ||||||
| 	w.TryWriteBits(e.SSRC, 16) |  | ||||||
| 	e.PayloadLen = uint64(len(e.RawPayload) + 1) |  | ||||||
| 	w.TryWriteBits(e.PayloadLen, 32) |  | ||||||
|  |  | ||||||
| 	// WTF: e.CC is useless |  | ||||||
| 	for i := uint64(0); i < 1; i++ { |  | ||||||
| 		w.TryWriteBits(e.CSRC[i], 32) |  | ||||||
| 	} |  | ||||||
| 	if e.PayloadLen != 0 { |  | ||||||
| 		w.Write(e.RawPayload) |  | ||||||
| 		w.Write([]byte{0}) |  | ||||||
| 	} |  | ||||||
| 	w.Close() |  | ||||||
|  |  | ||||||
| 	return b.Bytes() |  | ||||||
| } |  | ||||||
| @@ -1,110 +0,0 @@ | |||||||
| package n9m |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"os" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| var ip string = os.Getenv("SERVER_IP") |  | ||||||
|  |  | ||||||
| func (e *Package) MediaRequestDownloadVideo(token int, serial string, session string, camNo int, date string, begin_time string, end_time string, recordID string, serverId int) { |  | ||||||
| 	e.Payload = map[string]any{ |  | ||||||
| 		"MODULE":    "MEDIASTREAMMODEL", |  | ||||||
| 		"OPERATION": "REQUESTDOWNLOADVIDEO", |  | ||||||
| 		"PARAMETER": map[string]any{ |  | ||||||
| 			"PT":         3, |  | ||||||
| 			"SSRC":       1, |  | ||||||
| 			"STREAMNAME": "DOWNLOAD" + "_" + serial + "_" + fmt.Sprint(camNo) + "_" + fmt.Sprint(serverId), |  | ||||||
| 			"STREAMTYPE": 1, // main stream |  | ||||||
| 			"RECORDID":   recordID, |  | ||||||
| 			"CHANNEL":    1 << (camNo - 1), |  | ||||||
| 			"STARTTIME":  date + begin_time, |  | ||||||
| 			"ENDTIME":    date + end_time, |  | ||||||
| 			"OFFSETFLAG": 1, |  | ||||||
| 			"OFFSET":     0, |  | ||||||
| 			"IPANDPORT":  ip + ":12092", |  | ||||||
| 			"SERIAL":     token, |  | ||||||
| 			"DT":         1, // high speed download |  | ||||||
| 		}, |  | ||||||
| 		"SESSION": session, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // main server util |  | ||||||
| func (e *Package) MediaRequestAliveVideo(token int, camNo int64, Sid string, serial string, quality int64) { |  | ||||||
|  |  | ||||||
| 	channel := 0 |  | ||||||
|  |  | ||||||
| 	if camNo == 1 { |  | ||||||
| 		channel = 1 |  | ||||||
| 	} else { |  | ||||||
| 		channel = 1 << (camNo - 1) |  | ||||||
| 	} |  | ||||||
| 	e.Payload = map[string]any{ |  | ||||||
| 		"MODULE":    "MEDIASTREAMMODEL", |  | ||||||
| 		"OPERATION": "REQUESTALIVEVIDEO", |  | ||||||
| 		"PARAMETER": map[string]any{ |  | ||||||
| 			"AUDIOVALID": 1, |  | ||||||
| 			"CHANNEL":    channel, |  | ||||||
| 			"FRAMEMODE":  0, |  | ||||||
| 			"IPANDPORT":  ip + ":12092", |  | ||||||
| 			"STREAMNAME": "LIVE" + "_" + serial + "_" + fmt.Sprint(camNo), |  | ||||||
| 			"STREAMTYPE": quality, |  | ||||||
| 			"SERIAL":     token, |  | ||||||
| 		}, |  | ||||||
| 		"SESSION": Sid, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // main server util |  | ||||||
| func (e *Package) MediaRequestRemotePlayback(token int, serial string, session string, camNo int, date string, begin_time string, end_time string, serverId int) { |  | ||||||
| 	if end_time == "" { |  | ||||||
| 		e.Payload = map[string]any{ |  | ||||||
| 			"MODULE":    "MEDIASTREAMMODEL", |  | ||||||
| 			"OPERATION": "REQUESTREMOTEPLAYBACK", |  | ||||||
| 			"PARAMETER": map[string]any{ |  | ||||||
| 				"STREAMNAME": "PLAYBACK" + "_" + fmt.Sprint(serial) + "_" + fmt.Sprint(camNo) + "_" + fmt.Sprint(serverId), |  | ||||||
| 				"STREAMTYPE": 1, // main stream |  | ||||||
| 				"VIDEOTYPE":  2, // common files |  | ||||||
| 				"CHANNEL":    1 << (camNo - 1), |  | ||||||
| 				"STARTTIME":  date + begin_time, |  | ||||||
| 				"IPANDPORT":  ip + ":12092", |  | ||||||
| 				"SERIAL":     token, |  | ||||||
| 				"PBST":       0, |  | ||||||
| 			}, |  | ||||||
| 			"SESSION": session, |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		e.Payload = map[string]any{ |  | ||||||
| 			"MODULE":    "MEDIASTREAMMODEL", |  | ||||||
| 			"OPERATION": "REQUESTREMOTEPLAYBACK", |  | ||||||
| 			"PARAMETER": map[string]any{ |  | ||||||
| 				"STREAMNAME": "PLAYBACK" + "_" + fmt.Sprint(serial) + "_" + fmt.Sprint(camNo) + "_" + fmt.Sprint(serverId), |  | ||||||
| 				"STREAMTYPE": 1, // main stream |  | ||||||
| 				"VIDEOTYPE":  2, // common files |  | ||||||
| 				"CHANNEL":    1 << (camNo - 1), |  | ||||||
| 				"STARTTIME":  date + begin_time, |  | ||||||
| 				"ENDTIME":    date + end_time, |  | ||||||
| 				"IPANDPORT":  ip + ":12092", |  | ||||||
| 				"SERIAL":     token, |  | ||||||
| 				"PBST":       0, |  | ||||||
| 			}, |  | ||||||
| 			"SESSION": session, |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // main server util |  | ||||||
| func (e *Package) ControlRemotePlayback(token int, serial string, session string, camNo int, date string, begin_time string, end_time string, serverId int) { |  | ||||||
| 	e.Payload = map[string]any{ |  | ||||||
| 		"MODULE":    "MEDIASTREAMMODEL", |  | ||||||
| 		"OPERATION": "CONTROLREMOTEPLAYBACK", |  | ||||||
| 		"PARAMETER": map[string]any{ |  | ||||||
| 			"STREAMNAME":  fmt.Sprint(serial) + "_" + fmt.Sprint(camNo) + "_" + fmt.Sprint(serverId), |  | ||||||
| 			"SERIAL":      token, |  | ||||||
| 			"PALYBACKCMD": 5,         // main stream |  | ||||||
| 			"CHANNEL":     268435455, // common files |  | ||||||
| 		}, |  | ||||||
| 		"SESSION": session, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										168
									
								
								pkg/models/certificate.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								pkg/models/certificate.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,168 @@ | |||||||
|  | package models | ||||||
|  |  | ||||||
|  | type NetConnectionType uint | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	NetConnectionWired NetConnectionType = iota | ||||||
|  | 	NetConnectionWireless | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type TimeShiftSupportFlag uint8 | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	TimeShiftNotSupported TimeShiftSupportFlag = iota | ||||||
|  | 	TimeShiftSupported | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type FileSystemVersionNumber uint8 | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	FileSystemVersion4 FileSystemVersionNumber = iota | ||||||
|  | 	FileSystemVersion5 | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type CertificateConnectRequest struct { | ||||||
|  | 	Net               NetConnectionType       `json:"NET"` | ||||||
|  | 	SerialNumber      string                  `json:"DSNO"` | ||||||
|  | 	DeviceName        string                  `json:"DEVNAME"` | ||||||
|  | 	ChannelsNumber    uint                    `json:"CHANNEL"` | ||||||
|  | 	LicensePlate      string                  `json:"CARNUM"` | ||||||
|  | 	DeviceNumber      string                  `json:"AUTONO"` | ||||||
|  | 	VehicleNumber     string                  `json:"AUTOCAR"` | ||||||
|  | 	TimeShiftSupport  TimeShiftSupportFlag    `json:"TSE"` | ||||||
|  | 	FileSystemVersion FileSystemVersionNumber `json:"FSV"` | ||||||
|  | 	ICCID             string                  `json:"ICCID"` | ||||||
|  | 	EvidenceSupport   string                  `json:"EV"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type CertificateConnectClientRequest struct { | ||||||
|  | 	UK string `json:"UK"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type CertificateConnectResponse struct { | ||||||
|  | 	ErrorCode   uint   `json:"ERRORCODE"` | ||||||
|  | 	ErrorCause  string `json:"ERRORCAUSE"` | ||||||
|  | 	CommandMask uint   `json:"MASKCMD"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type CertificateConnectClientResponse struct { | ||||||
|  | 	S0 string `json:"S0"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type CertificateVerificationRequest struct { | ||||||
|  | 	S0 string `json:"S0"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type CertificateVerificationResponse struct { | ||||||
|  | 	ErrorCode  uint   `json:"ERRORCODE"` | ||||||
|  | 	ErrorCause string `json:"ERRORCAUSE"` | ||||||
|  | 	ReturnFlag bool   `json:"RETURN"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type CertificateLoginRequest struct { | ||||||
|  | 	ClientID  uint   `json:"CID,omitempty"` | ||||||
|  | 	MAC       string `json:"MAC,omitempty"` | ||||||
|  | 	User      string `json:"USER"` | ||||||
|  | 	Password  string `json:"PASSWD"` | ||||||
|  | 	PlayDevID uint   `json:"PLAYDEVID"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type CertificateLoginResponse struct { | ||||||
|  | 	SerialNumber        string          `json:"DSNO"` | ||||||
|  | 	DeviceName          string          `json:"DEVNAME"` | ||||||
|  | 	ChannelsNumber      uint            `json:"CHANNEL"` | ||||||
|  | 	UID                 string          `json:"UID"` | ||||||
|  | 	AlarmInputNumber    uint            `json:"ALARMIN"` | ||||||
|  | 	AlarmOutputNumber   uint            `json:"ALARMOUT"` | ||||||
|  | 	DeviceType          string          `json:"TYPE"` | ||||||
|  | 	DeviceClass         DeviceType      `json:"DEVCLASS"` | ||||||
|  | 	CurrentVersion      string          `json:"PRO"` | ||||||
|  | 	LicensePlate        string          `json:"CARNUM"` | ||||||
|  | 	UserLever           UserAccessLevel `json:"LEVEL"` | ||||||
|  | 	CompanyName         string          `json:"CPN"` | ||||||
|  | 	CustomerName        string          `json:"CNAME"` | ||||||
|  | 	AudioChannelsNumber uint            `json:"ACHN"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type CertificateCreateStreamRequest struct { | ||||||
|  | 	StreamName string `json:"STREAMNAME"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type CertificateCreateStreamResponse struct { | ||||||
|  | 	ErrorCode  uint   `json:"ERRORCODE"` | ||||||
|  | 	ErrorCause string `json:"ERRORCAUSE"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type CommandMaskParameters uint | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	CommandMaskAlarm = 1 << iota | ||||||
|  | 	CommandMaskScanningGun | ||||||
|  | 	CommandMaskPassengerFlow | ||||||
|  | 	CommandMaskFaceContrast | ||||||
|  | 	CommandMaskCard | ||||||
|  | 	CommandMaskShutdownReport | ||||||
|  | 	CommandMaskGPSReport | ||||||
|  | 	CommandMaskAll = 0b1111111 | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type DeviceType uint | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	DeviceTypeDVR DeviceType = 1 + iota | ||||||
|  | 	DeviceTypeIPC | ||||||
|  | 	DeviceTypeNVR | ||||||
|  | 	DeviceTypeMIPC | ||||||
|  | 	DeviceTypeMDVR | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type UserAccessLevel uint | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	UserAccessLevelSuperAdmin UserAccessLevel = iota | ||||||
|  | 	UserAccessLevelAdministrator | ||||||
|  | 	UserAccessLeverUser | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | func (e *Package) RequestConnect(session string, serial string, numOfCams int) { | ||||||
|  | 	e.Payload = map[string]any{ | ||||||
|  | 		"MODULE":    "CERTIFICATE", | ||||||
|  | 		"OPERATION": "CONNECT", | ||||||
|  | 		"PARAMETER": map[string]any{ | ||||||
|  | 			"DSNO":    serial, | ||||||
|  | 			"CHANNEL": numOfCams, | ||||||
|  | 		}, | ||||||
|  | 		"SESSION": session, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // video server util | ||||||
|  | func (e *Package) ResponseConnect(Sid string, streamName string) { | ||||||
|  | 	e.Payload = map[string]any{ | ||||||
|  | 		"MODULE":    "CERTIFICATE", | ||||||
|  | 		"OPERATION": "CREATESTREAM", | ||||||
|  | 		"RESPONSE": map[string]any{ | ||||||
|  | 			"ERRORCODE":  0, | ||||||
|  | 			"STREAMNAME": streamName, | ||||||
|  | 		}, | ||||||
|  | 		"SESSION": Sid, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // main server util | ||||||
|  | func (e *Package) ResponseCertificateConnect(Sid string) { | ||||||
|  | 	e.Payload = map[string]any{ | ||||||
|  | 		"MODULE":    "CERTIFICATE", | ||||||
|  | 		"OPERATION": "CONNECT", | ||||||
|  | 		"RESPONSE": map[string]any{ | ||||||
|  | 			"ERRORCAUSE": "", | ||||||
|  | 			"ERRORCODE":  0, | ||||||
|  | 			"MASKCMD":    5, | ||||||
|  | 			"PRO":        "1.0.5", | ||||||
|  | 		}, | ||||||
|  | 		"SESSION": Sid, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | */ | ||||||
| @@ -1,4 +1,10 @@ | |||||||
| package n9m | package models | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"gitea.unprism.ru/KRBL/n9m/v2/pkg/parameters" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | /* | ||||||
| 
 | 
 | ||||||
| // request reqistration parameters (directly to register) | // request reqistration parameters (directly to register) | ||||||
| func (e *Package) RequestParameters(params map[string]any, serial int, session string) { | func (e *Package) RequestParameters(params map[string]any, serial int, session string) { | ||||||
| @@ -26,37 +32,41 @@ func (e *Package) SetParameters(params map[string]any, serial int, session strin | |||||||
| 	} | 	} | ||||||
| 	// log.Println(e.Payload) | 	// log.Println(e.Payload) | ||||||
| } // end of 'SetParameters' function | } // end of 'SetParameters' function | ||||||
|  | */ | ||||||
| 
 | 
 | ||||||
| // todo al1 | type ConfigModelSetRequest struct { | ||||||
| func (e *Package) ConfigeModel(Sid string) { | 	MDVR parameters.Setting `json:"MDVR"` | ||||||
| 	e.Payload = map[string]any{ | } | ||||||
| 		"MODULE":    "CONFIGMODEL", | 
 | ||||||
| 		"OPERATION": "SET", | type ConfigModelGetRequest struct { | ||||||
| 		"PARAMETER": map[string]any{ | 	MDVR interface{} `json:"MDVR"` | ||||||
| 			"MDVR": map[string]any{ | } | ||||||
| 				"KEYS": map[string]any{ // KEY parameters | 
 | ||||||
| 					"GV": 1, // GPS version | type ConfigModelSetResponse struct { | ||||||
|  | 	MDVR parameters.Setting `json:"MDVR"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func InitialConfig() ConfigModelSetRequest { | ||||||
|  | 	return ConfigModelSetRequest{ | ||||||
|  | 		MDVR: parameters.Setting{ | ||||||
|  | 			KEYS: parameters.KEYS{ | ||||||
|  | 				GV: 1, // GPS version | ||||||
| 			}, | 			}, | ||||||
| 				"PGDSM": map[string]any{ // Network monitoring status parameters | 			PGDSM: parameters.PGDSM{ | ||||||
| 					"PGPS": map[string]any{ // GPS position | 				PGPS: parameters.PGPS{ | ||||||
| 						"EN": 1,  // Real-time position monitoring | 					EN:   1,    // Real-time position monitoring | ||||||
| 						"TM": 10, // Time interval | 					MODE: 0b10, // Enable timer | ||||||
|  | 					TM:   10,   // Time interval | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 				"PSI": map[string]any{ // Platform basic information | 			SUBSTRNET: parameters.SUBSTRNET{ | ||||||
| 					"CG": map[string]any{ // Call information | 				SM: 1, | ||||||
| 						"AS": 0, // Automatic answer |  | ||||||
| 					}, |  | ||||||
| 				}, |  | ||||||
| 				"SUBSTRNET": map[string]any{ |  | ||||||
| 					"SM": 1, // 0-Smooth .. 4-Clear |  | ||||||
| 				}, |  | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // main server util | /* | ||||||
| func (e *Package) ResponseConfigModelSet(Sid string) { | func (e *Package) ResponseConfigModelSet(Sid string) { | ||||||
| 	e.Payload = map[string]any{ | 	e.Payload = map[string]any{ | ||||||
| 		"MODULE":    "CONFIGMODUL", // it's not error | 		"MODULE":    "CONFIGMODUL", // it's not error | ||||||
| @@ -69,3 +79,5 @@ func (e *Package) ResponseConfigModelSet(Sid string) { | |||||||
| 		"SESSION": Sid, | 		"SESSION": Sid, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | */ | ||||||
							
								
								
									
										215
									
								
								pkg/models/devemm.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										215
									
								
								pkg/models/devemm.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,215 @@ | |||||||
|  | package models | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"gitea.unprism.ru/KRBL/n9m/v2/pkg/protocol" | ||||||
|  | 	"strconv" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  | ||||||
|  | // main server util | ||||||
|  | func (e *Package) RequestGeolocation(serial int, Sid string) { | ||||||
|  | 	e.Payload = map[string]any{ | ||||||
|  | 		"MODULE":    "DEVEMM", | ||||||
|  | 		"OPERATION": "GETPOS", | ||||||
|  | 		"PARAMETER": map[string]any{ | ||||||
|  | 			"SERIAL": serial, | ||||||
|  | 		}, | ||||||
|  | 		"SESSION": Sid, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *Package) ResponseGeolocation(errorCode int, errorCause string, serial int, longitude float32, latitude float32, altitude float32, speed int, course int, time string) { | ||||||
|  | 	e.Payload = map[string]any{ | ||||||
|  | 		"MODULE":    "DEVEMM", | ||||||
|  | 		"OPERATION": "GETPOS", | ||||||
|  | 		"RESPONSE": map[string]any{ | ||||||
|  | 			"ERRORCODE":  errorCode, | ||||||
|  | 			"ERRORCAUSE": errorCause, | ||||||
|  | 			"SERIAL":     serial, | ||||||
|  | 			"P": map[string]any{ | ||||||
|  | 				"V": errorCode == 0, | ||||||
|  | 				"J": fmt.Sprintf("%4.6v", longitude), | ||||||
|  | 				"W": fmt.Sprintf("%4.6v", latitude), | ||||||
|  | 				"H": fmt.Sprintf("%4.6v", altitude), | ||||||
|  | 				"S": speed,  // unit - 0.01 km/h | ||||||
|  | 				"C": course, // direction (angle from north) | ||||||
|  | 				"T": time,   // yyyymmddhhmmss | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | // 3.4.5.28 | ||||||
|  | type SpiParameters struct { | ||||||
|  | 	DriveFlag    uint             `json:"T"` | ||||||
|  | 	DataMask     uint             `json:"M"` | ||||||
|  | 	Position     protocol.GPSData `json:"P"` | ||||||
|  | 	DeviceStatus SpiDeviceStatus  `json:"S"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 3.4.5.28.1 | ||||||
|  | type SpiDeviceStatus struct { | ||||||
|  | 	Status3G            uint                `json:"G3"` | ||||||
|  | 	Status3GStrength    uint                `json:"G3S"` | ||||||
|  | 	Status4G            uint                `json:"G4"` | ||||||
|  | 	Status4GStrength    uint                `json:"G4S"` | ||||||
|  | 	WIFIStatus          uint                `json:"W"` | ||||||
|  | 	WIFIStrength        uint                `json:"WS"` | ||||||
|  | 	Voltage             float64             `json:"V"` | ||||||
|  | 	DeviceTemperature   float64             `json:"DT"` | ||||||
|  | 	IndoorTemperature   float64             `json:"TC"` | ||||||
|  | 	Speed               float64             `json:"S"` | ||||||
|  | 	KeyIgnitionState    uint                `json:"SW"` | ||||||
|  | 	RecordStatus        []uint              `json:"RE"` | ||||||
|  | 	Time                time.Time           `json:"T"` | ||||||
|  | 	StorageDeviceNumber uint                `json:"STC"` | ||||||
|  | 	StorageDeviceInfo   []StorageDeviceInfo `json:"SINFO"` | ||||||
|  | 	VideoLossStatus     []uint              `json:"VS"` | ||||||
|  | 	Humidity            float64             `json:"H"` | ||||||
|  | 	TotalMileage        float64             `json:"TM"` | ||||||
|  | 	HardDriveHeating    uint                `json:"HTR"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (g *SpiDeviceStatus) MarshalJSON() ([]byte, error) { | ||||||
|  | 	var alias struct { | ||||||
|  | 		Status3G            uint                `json:"G3"` | ||||||
|  | 		Status3GStrength    uint                `json:"G3S"` | ||||||
|  | 		Status4G            uint                `json:"G4"` | ||||||
|  | 		Status4GStrength    uint                `json:"G4S"` | ||||||
|  | 		WIFIStatus          uint                `json:"W"` | ||||||
|  | 		WIFIStrength        uint                `json:"WS"` | ||||||
|  | 		Voltage             uint                `json:"V"` | ||||||
|  | 		DeviceTemperature   uint                `json:"DT"` | ||||||
|  | 		IndoorTemperature   uint                `json:"TC"` | ||||||
|  | 		Speed               uint                `json:"S"` | ||||||
|  | 		SpeedUnits          uint                `json:"SU"` | ||||||
|  | 		KeyIgnitionState    uint                `json:"SW"` | ||||||
|  | 		RecordStatus        []uint              `json:"RE"` | ||||||
|  | 		Time                string              `json:"T"` | ||||||
|  | 		StorageDeviceNumber uint                `json:"STC"` | ||||||
|  | 		StorageDeviceInfo   []StorageDeviceInfo `json:"SINFO"` | ||||||
|  | 		VideoLossStatus     []uint              `json:"VS"` | ||||||
|  | 		Humidity            uint                `json:"H"` | ||||||
|  | 		TotalMileage        string              `json:"TM"` | ||||||
|  | 		HardDriveHeating    uint                `json:"HTR"` | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	convert := func(v float64) uint { | ||||||
|  | 		if v < 0 { | ||||||
|  | 			return uint(-v * 100) | ||||||
|  | 		} else { | ||||||
|  | 			return uint((v + 100) * 100) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	alias.Status3G = g.Status3G | ||||||
|  | 	alias.Status3GStrength = g.Status3GStrength | ||||||
|  | 	alias.Status4G = g.Status4G | ||||||
|  | 	alias.Status4GStrength = g.Status4GStrength | ||||||
|  | 	alias.WIFIStatus = g.WIFIStatus | ||||||
|  | 	alias.WIFIStrength = g.WIFIStrength | ||||||
|  | 	alias.Voltage = uint(g.Voltage * 100) | ||||||
|  | 	alias.DeviceTemperature = convert(g.DeviceTemperature) | ||||||
|  | 	alias.IndoorTemperature = convert(g.IndoorTemperature) | ||||||
|  | 	alias.Speed = uint(g.Speed * 100) | ||||||
|  | 	alias.SpeedUnits = 0 | ||||||
|  | 	alias.KeyIgnitionState = g.KeyIgnitionState | ||||||
|  | 	alias.RecordStatus = g.RecordStatus | ||||||
|  | 	alias.Time = g.Time.Format("20060102150405") | ||||||
|  | 	alias.StorageDeviceNumber = g.StorageDeviceNumber | ||||||
|  | 	alias.StorageDeviceInfo = g.StorageDeviceInfo | ||||||
|  | 	alias.VideoLossStatus = g.VideoLossStatus | ||||||
|  | 	alias.Humidity = uint(g.Humidity * 10000) | ||||||
|  | 	alias.TotalMileage = fmt.Sprintf("%.6f", g.TotalMileage) | ||||||
|  | 	alias.HardDriveHeating = g.HardDriveHeating | ||||||
|  |  | ||||||
|  | 	return json.Marshal(alias) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (g *SpiDeviceStatus) UnmarshalJSON(data []byte) (err error) { | ||||||
|  | 	var alias struct { | ||||||
|  | 		Status3G            uint                `json:"G3"` | ||||||
|  | 		Status3GStrength    uint                `json:"G3S"` | ||||||
|  | 		Status4G            uint                `json:"G4"` | ||||||
|  | 		Status4GStrength    uint                `json:"G4S"` | ||||||
|  | 		WIFIStatus          uint                `json:"W"` | ||||||
|  | 		WIFIStrength        uint                `json:"WS"` | ||||||
|  | 		Voltage             uint                `json:"V"` | ||||||
|  | 		DeviceTemperature   uint                `json:"TD"` | ||||||
|  | 		IndoorTemperature   uint                `json:"TC"` | ||||||
|  | 		Speed               uint                `json:"S"` | ||||||
|  | 		SpeedUnits          uint                `json:"SU"` | ||||||
|  | 		KeyIgnitionState    uint                `json:"SW"` | ||||||
|  | 		RecordStatus        []uint              `json:"RE"` | ||||||
|  | 		Time                string              `json:"T"` | ||||||
|  | 		StorageDeviceNumber uint                `json:"STC"` | ||||||
|  | 		StorageDeviceInfo   []StorageDeviceInfo `json:"SINFO"` | ||||||
|  | 		VideoLossStatus     []uint              `json:"VS"` | ||||||
|  | 		Humidity            uint                `json:"H"` | ||||||
|  | 		TotalMileage        string              `json:"TM"` | ||||||
|  | 		HardDriveHeating    uint                `json:"HTR"` | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err = json.Unmarshal(data, &alias); err != nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	convert := func(v uint) float64 { | ||||||
|  | 		if v < 10000 { | ||||||
|  | 			return -float64(v) / 100 | ||||||
|  | 		} else { | ||||||
|  | 			return float64(v-10000) / 100 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	g.Status3G = alias.Status3G | ||||||
|  | 	g.Status3GStrength = alias.Status3GStrength | ||||||
|  | 	g.Status4G = alias.Status4G | ||||||
|  | 	g.Status4GStrength = alias.Status4GStrength | ||||||
|  | 	g.WIFIStatus = alias.WIFIStatus | ||||||
|  | 	g.WIFIStrength = alias.WIFIStrength | ||||||
|  | 	g.Voltage = float64(alias.Voltage) / 100 | ||||||
|  | 	g.DeviceTemperature = convert(alias.DeviceTemperature) | ||||||
|  | 	g.IndoorTemperature = convert(alias.IndoorTemperature) | ||||||
|  |  | ||||||
|  | 	g.Speed = float64(alias.Speed) / 100.0 | ||||||
|  | 	switch alias.SpeedUnits { | ||||||
|  | 	case 0: | ||||||
|  | 		break | ||||||
|  | 	case 1: | ||||||
|  | 		g.Speed *= 1.609 | ||||||
|  | 	default: | ||||||
|  | 		return fmt.Errorf("Strange speed units") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	g.KeyIgnitionState = alias.KeyIgnitionState | ||||||
|  | 	g.RecordStatus = alias.RecordStatus | ||||||
|  | 	g.Time, _ = time.Parse("20060102150405", alias.Time) | ||||||
|  | 	g.StorageDeviceNumber = alias.StorageDeviceNumber | ||||||
|  | 	g.StorageDeviceInfo = alias.StorageDeviceInfo | ||||||
|  | 	g.VideoLossStatus = alias.VideoLossStatus | ||||||
|  | 	g.Humidity = float64(alias.Humidity) / 10000 | ||||||
|  |  | ||||||
|  | 	if g.TotalMileage, err = strconv.ParseFloat(alias.TotalMileage, 64); err != nil { | ||||||
|  | 		return fmt.Errorf("invalid longitude: %w", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	g.HardDriveHeating = alias.HardDriveHeating | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 3.4.5.28.3 | ||||||
|  | type StorageDeviceInfo struct { | ||||||
|  | 	Type         uint `json:"T"` | ||||||
|  | 	MediaTime    uint `json:"O"` | ||||||
|  | 	Status       uint `json:"S"` | ||||||
|  | 	Capacity     uint `json:"TS"` | ||||||
|  | 	FreeCapacity uint `json:"LS"` | ||||||
|  | } | ||||||
							
								
								
									
										135
									
								
								pkg/models/evem.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								pkg/models/evem.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,135 @@ | |||||||
|  | package models | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"gitea.unprism.ru/KRBL/n9m/v2/pkg/protocol" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // 3.4.1.3 | ||||||
|  | type EventModelGetAlarmStatusInfoResponse struct { | ||||||
|  | 	ErrorCode            uint                        `json:"ERRORCODE"` | ||||||
|  | 	ErrorCause           string                      `json:"ERRORCAUSE"` | ||||||
|  | 	StorageErrors        []StorageErrorStatus        `json:"ST"` | ||||||
|  | 	AnotherStorageErrors []AnotherStorageErrorStatus `json:"VS"` | ||||||
|  | 	VideoLossErrors      []VideoLossErrorStatus      `json:"VL"` | ||||||
|  | 	GPSError             GPSErrorStatus              `json:"GFA"` | ||||||
|  | 	GPSAntennaError      GPSAntennaErrorStatus       `json:"GPSS"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 3.4.1.4.21 | ||||||
|  | type StorageErrorStatus struct { | ||||||
|  | 	CameraCovered uint `json:"ISA"` | ||||||
|  | 	ChannelBind   uint `json:"LHC"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 3.4.1.4.4 | ||||||
|  | type AnotherStorageErrorStatus struct { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 3.4.1.4.5 | ||||||
|  | type VideoLossErrorStatus struct { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 3.4.1.4.44 | ||||||
|  | type GPSErrorStatus struct { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 3.4.1.4.46 | ||||||
|  | type GPSAntennaErrorStatus struct { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 3.4.1.5 | ||||||
|  | // Alarm upload | ||||||
|  | type SendAlarmInfoParameters struct { | ||||||
|  | 	AlarmType         protocol.AlarmType   `json:"ALARMTYPE"` | ||||||
|  | 	CommandType       uint                 `json:"CMDTYPE"` | ||||||
|  | 	AlarmUID          uint                 `json:"ALARMUID"` | ||||||
|  | 	NumberOfRestarts  uint                 `json:"RUN"` | ||||||
|  | 	AlarmLevel        protocol.AlarmLevel  `json:"ALARMAS"` | ||||||
|  | 	AlarmCount        uint                 `json:"ALARMCOUNT"` | ||||||
|  | 	TriggerType       protocol.TriggerType `json:"TRIGGERTYPE"` | ||||||
|  | 	ContinueTime      uint                 `json:"CONTINUETIME"` | ||||||
|  | 	CurrentTime       uint                 `json:"CURRENTTIME"` | ||||||
|  | 	Language          protocol.Language    `json:"L"` | ||||||
|  | 	GPSData           protocol.GPSData     `json:"P"` | ||||||
|  | 	RealTimeUpload    uint                 `json:"REAL"` | ||||||
|  | 	InstructionSerial uint                 `json:"CMDNO"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 3.4.1.5 | ||||||
|  | // Alarm upload | ||||||
|  | type SendAlarmInfoResponse struct { | ||||||
|  | 	ErrorCode         uint               `json:"ERRORCODE"` | ||||||
|  | 	AlarmType         protocol.AlarmType `json:"ALARMTYPE"` | ||||||
|  | 	ErrorCause        string             `json:"ERRORCAUSE"` | ||||||
|  | 	CommandType       uint               `json:"CMDTYPE"` | ||||||
|  | 	AlarmUID          uint               `json:"ALARMUID"` | ||||||
|  | 	NumberOfRestarts  uint               `json:"RUN"` | ||||||
|  | 	InstructionSerial uint               `json:"CMDNO"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 3.4.1.5.1 | ||||||
|  | type SendAlarmInfoCameraParameters struct { | ||||||
|  | 	SendAlarmInfoParameters | ||||||
|  |  | ||||||
|  | 	Channel           uint   `json:"CHANNEL"` | ||||||
|  | 	ChannelMask       uint   `json:"CHANNELMASK"` | ||||||
|  | 	LCH               []uint `json:"LCH"` | ||||||
|  | 	Push              uint   `json:"PUSH"` | ||||||
|  | 	AlarmName         string `json:"ALARMNAME"` | ||||||
|  | 	AlarmAbbreviation string `json:"SER"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type EventModelGetAlarmingResponse struct { | ||||||
|  | 	CameraCoveredChannelMask uint `json:"VS_CH"` | ||||||
|  | 	CameraCoveredAlarmMask   uint `json:"VS_AT"` | ||||||
|  | 	CameraCoveredStatusMask  uint `json:"VS_AS"` | ||||||
|  |  | ||||||
|  | 	VideoLossChannelMask uint `json:"VL_CH"` | ||||||
|  | 	VideoLossAlarmMask   uint `json:"VL_AT"` | ||||||
|  | 	VideoLossStatusMask  uint `json:"VL_AS"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 3.4.1.3 | ||||||
|  | type EventModelGetAlarmInfoStatusResponse struct { | ||||||
|  | 	ErrorCode            uint                         `json:"ERRORCODE"` | ||||||
|  | 	ErrorCause           string                       `json:"ERRORCAUSE"` | ||||||
|  | 	MotionDetectionAlarm []MotionDetectionAlarmStatus `json:"MD"` | ||||||
|  | 	IOAlarm              []IOAlarmStatus              `json:"IO"` | ||||||
|  | 	StorageAlarm         []StorageAlarmStatus         `json:"ST"` | ||||||
|  | 	CameraCoveredAlarm   []CameraCoveredAlarmStatus   `json:"VS"` | ||||||
|  | 	VideoLossAlarm       []VideoLossAlarmStatus       `json:"VL"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 3.4.1.4.1 | ||||||
|  | type MotionDetectionAlarmStatus struct { | ||||||
|  | 	Status uint `json:"ISA"` | ||||||
|  | 	Mask   uint `json:"LCH"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 3.4.1.4.2 | ||||||
|  | type IOAlarmStatus struct { | ||||||
|  | 	Status           uint   `json:"ISA"` | ||||||
|  | 	IONumber         uint   `json:"IONO"` | ||||||
|  | 	Mask             uint   `json:"LCH"` | ||||||
|  | 	Name             string `json:"NAME"` | ||||||
|  | 	NameAbbreviation string `json:"NSER"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 3.4.1.4.4 | ||||||
|  | type CameraCoveredAlarmStatus struct { | ||||||
|  | 	Status uint `json:"ISA"` | ||||||
|  | 	Mask   uint `json:"LCH"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 3.4.1.4.4 | ||||||
|  | type VideoLossAlarmStatus struct { | ||||||
|  | 	Status uint `json:"ISA"` | ||||||
|  | 	Mask   uint `json:"LCH"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 3.4.1.4.21 | ||||||
|  | type StorageAlarmStatus struct { | ||||||
|  | 	SID   any  `json:"SID"` | ||||||
|  | 	Type  uint `json:"ST"` | ||||||
|  | 	Error uint `json:"E"` | ||||||
|  | } | ||||||
							
								
								
									
										53
									
								
								pkg/models/mediastreammodel.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								pkg/models/mediastreammodel.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | |||||||
|  | package models | ||||||
|  |  | ||||||
|  | import "gitea.unprism.ru/KRBL/n9m/v2/pkg/protocol" | ||||||
|  |  | ||||||
|  | type StreamType uint | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	StreamTypeSub StreamType = iota | ||||||
|  | 	StreamTypeMain | ||||||
|  | 	StreamTypeMobile | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type MediaStreamModelRequestLiveVideoRequest struct { | ||||||
|  | 	SSRC        uint       `json:"SSRC,omitempty"` | ||||||
|  | 	StreamName  string     `json:"STREAMNAME"` | ||||||
|  | 	StreamType  StreamType `json:"STREAMTYPE"` | ||||||
|  | 	Channel     uint       `json:"CHANNEL"` | ||||||
|  | 	AudioValid  uint       `json:"AUDIOVALID"` | ||||||
|  | 	Destination string     `json:"IPANDPORT,omitempty"` | ||||||
|  | 	FrameCount  uint       `json:"FRAMECOUNT,omitempty"` | ||||||
|  | 	FrameMode   uint       `json:"FRAMEMODE"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type MediaStreamModelRequestLiveVideoResponse struct { | ||||||
|  | 	SSRC       uint       `json:"SSRC"` | ||||||
|  | 	StreamName string     `json:"STREAMNAME"` | ||||||
|  | 	StreamType StreamType `json:"STREAMTYPE"` | ||||||
|  | 	ErrorCode  uint       `json:"ERRORCODE"` | ||||||
|  | 	ErrorCause string     `json:"ERRORCAUSE"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type MediaStreamCommand uint | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	MediaStreamCommandStop MediaStreamCommand = iota | ||||||
|  | 	MediaStreamCommandResume | ||||||
|  | 	MediaStreamCommandPause | ||||||
|  | 	MediaStreamCommandSwitchVideoStream | ||||||
|  | 	MediaStreamAudioManagement | ||||||
|  | 	MediaStreamFrameRate | ||||||
|  | 	MediaStreamSendingMode | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type MediaStreamModelControlStreamRequest struct { | ||||||
|  | 	PayloadType protocol.PayloadType `json:"PT"` | ||||||
|  | 	SSRC        uint16               `json:"SSRC"` | ||||||
|  | 	StreamName  string               `json:"STREAMNAME"` | ||||||
|  | 	Command     MediaStreamCommand   `json:"CMD"` | ||||||
|  |  | ||||||
|  | 	StreamType StreamType `json:"STREAMTYPE,omitempty"` | ||||||
|  | 	AudioValid uint       `json:"AUDIOVALID,omitempty"` | ||||||
|  | 	FrameMode  uint       `json:"FRAMEMODE,omitempty"` | ||||||
|  | } | ||||||
| @@ -1,4 +1,6 @@ | |||||||
| package n9m | package models | ||||||
|  | 
 | ||||||
|  | /* | ||||||
| 
 | 
 | ||||||
| func (e *Package) ResponseCalendar(errorCode int, errorCause string, serial int, dates []string) { | func (e *Package) ResponseCalendar(errorCode int, errorCause string, serial int, dates []string) { | ||||||
| 	e.Payload = map[string]any{ | 	e.Payload = map[string]any{ | ||||||
| @@ -78,3 +80,5 @@ func (e *Package) RequestFileList(queryTime string, serial int, session string, | |||||||
| 		"SESSION": session, | 		"SESSION": session, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | */ | ||||||
| @@ -1,5 +1,6 @@ | |||||||
| package n9m | package parameters | ||||||
| 
 | 
 | ||||||
|  | // 7.2 | ||||||
| type RIP struct { | type RIP struct { | ||||||
| 	DEVID   string // ID номер устройства | 	DEVID   string // ID номер устройства | ||||||
| 	BN      string // бортовой номер | 	BN      string // бортовой номер | ||||||
| @@ -7,6 +8,9 @@ type RIP struct { | |||||||
| 	LN      string // Название линии | 	LN      string // Название линии | ||||||
| 	DID     string // номер водительского удостоверения | 	DID     string // номер водительского удостоверения | ||||||
| 	DNAME   string // ФИО водителя | 	DNAME   string // ФИО водителя | ||||||
|  | 	REC     int    // Тип записи видоепотока (0 - беск запись, 1 - запись по событиям, 2 - по расписанию) | ||||||
|  | 	PREREC  int    // Длительность предтревожной записи | ||||||
|  | 	POSTREC int    // Длительность послетревожной записи | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type VS struct { | type VS struct { | ||||||
| @@ -70,9 +74,11 @@ type NEC struct { | |||||||
| 	AEN int // звук (вкл/выкл) | 	AEN int // звук (вкл/выкл) | ||||||
| 	VEN int // видео (вкл/выкл) | 	VEN int // видео (вкл/выкл) | ||||||
| 	BR  int // битрейт | 	BR  int // битрейт | ||||||
|  | 	BRM int | ||||||
| 	ECT int // кодировка | 	ECT int // кодировка | ||||||
| 	FR  int // фреймрейт | 	FR  int // фреймрейт | ||||||
| 	FT  int // тип кадров | 	FT  int // тип кадров | ||||||
|  | 	KFI int | ||||||
| 	QLT int // качество трансляции | 	QLT int // качество трансляции | ||||||
| 	RST int // разрешение | 	RST int // разрешение | ||||||
| } | } | ||||||
| @@ -94,8 +100,10 @@ type VEC struct { | |||||||
| 	ECT int // кодировка | 	ECT int // кодировка | ||||||
| 	FR  int // фреймрейт | 	FR  int // фреймрейт | ||||||
| 	QLT int // качество записи | 	QLT int // качество записи | ||||||
|  | 	RM  int // режим работы | ||||||
| 	AEN int // звук (вкл/выкл) | 	AEN int // звук (вкл/выкл) | ||||||
| 	ALT int // качество видео при тревоге | 	ALT int // качество видео при тревоге | ||||||
|  | 	BRM int // режим битрейта | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type EOSD struct { | type EOSD struct { | ||||||
| @@ -133,6 +141,18 @@ type COSD struct { | |||||||
| 	Y  int | 	Y  int | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | type MAIN struct { | ||||||
|  | 	VEN uint // 1/0 | ||||||
|  | 	RST uint // разрешение | ||||||
|  | 	ECT uint // кодировка | ||||||
|  | 	FR  uint // фреймрейт | ||||||
|  | 	QLT uint // качество записи | ||||||
|  | 	RM  uint // режим работы (0 - всегда, 1 - по расписанию, 2 - по тревоге) | ||||||
|  | 	AEN uint // звук | ||||||
|  | 	ALT uint // качество видео при тревоге | ||||||
|  | 	BRM uint // режим битрейта | ||||||
|  | } | ||||||
|  | 
 | ||||||
| type IOP struct { | type IOP struct { | ||||||
| 	EN  int | 	EN  int | ||||||
| 	AS  int | 	AS  int | ||||||
| @@ -151,6 +171,8 @@ type SAP struct { | |||||||
| 	UN  int | 	UN  int | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // 7.24.3 | ||||||
|  | // Alarm processing parameters | ||||||
| type APR struct { | type APR struct { | ||||||
| 	AR ARR | 	AR ARR | ||||||
| 	SS SS | 	SS SS | ||||||
| @@ -166,13 +188,6 @@ type SS struct { | |||||||
| 	EN int | 	EN int | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type UAP struct { |  | ||||||
| 	EN  int |  | ||||||
| 	AS  int |  | ||||||
| 	VT  int |  | ||||||
| 	SDT int |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type PVLAS struct { | type PVLAS struct { | ||||||
| 	EN  int | 	EN  int | ||||||
| 	AS  int | 	AS  int | ||||||
| @@ -218,7 +233,8 @@ type DNS struct { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type KEYS struct { | type KEYS struct { | ||||||
| 	MAC string // MAC-адрес | 	MAC string `json:"MAC,omitempty"` // MAC-адрес | ||||||
|  | 	GV  uint   `json:"GV,omitempty"`  // GPS version | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type WIFI struct { | type WIFI struct { | ||||||
| @@ -255,8 +271,10 @@ type MCMS struct { | |||||||
| 	SP []SP // Параметры сервера | 	SP []SP // Параметры сервера | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // 7.12.1 | ||||||
|  | // Central server configuration | ||||||
| type SP struct { | type SP struct { | ||||||
| 	EN     int    // Статус сервера | 	EN     int    // Флаг включения | ||||||
| 	CP     int    // Протокол подключения | 	CP     int    // Протокол подключения | ||||||
| 	NWT    int    // Способ подключения | 	NWT    int    // Способ подключения | ||||||
| 	CS     string // Адрес сервера | 	CS     string // Адрес сервера | ||||||
| @@ -267,29 +285,120 @@ type SP struct { | |||||||
| 	MUPORT int    // UDP-порт медиасервера | 	MUPORT int    // UDP-порт медиасервера | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type Setting struct { | // 7.28 | ||||||
| 	RIP       RIP | // NVR remote device parameters | ||||||
| 	VS        VS | type REDEV struct { | ||||||
| 	GSP       GSP | 	EN      int    // Флаг включения | ||||||
| 	TIMEP     TIMEP | 	LOCK    int    // ??? | ||||||
| 	ETHERNET  ETHERNET | 	CHL     int    // Номер канала на устройстве | ||||||
| 	KEYS      KEYS | 	RECHL   int    // ??? | ||||||
| 	WIFI      WIFI | 	ONLINE  int    // Доступно ли сейчас устройство | ||||||
| 	M3G       M3G | 	RETYPE  int    // Тип устройства | ||||||
| 	MCMS      MCMS | 	PROTYPE int    // Тип протокола | ||||||
| 	ATP       ATP | 	IPTYPE  int    // Тип IP-адреса (IPv4/IPv6) | ||||||
| 	SSP       SSP | 	CONTYPE int    // ??? | ||||||
| 	SWUS      SWUS | 	SW      int    // Источник поступаемого сигнала | ||||||
| 	UMP       UMP | 	REPORT  int    // Порт камеры | ||||||
| 	SUBSTRNET SUBSTRNET | 	REIP    string // IP камеры | ||||||
| 	DOSD      DOSD | 	ID      string // Идентификатор камеры | ||||||
| 	AR        AR | 	RENAME  string // Название устройства | ||||||
| 	EOSD      []EOSD | 	USER    string // Имя пользователя | ||||||
| 	MAIN      []VEC | 	PWD     string // Пароль | ||||||
| 	IOP       []IOP | 	URL     string // ??? | ||||||
| 	SAP       SAP | } | ||||||
| 	UAP       UAP | 
 | ||||||
| 	PVLAS     PVLAS | // 7.29 | ||||||
| 	PMDAS     PMDAS | // Panel emergency alarm | ||||||
| 	DSM       DSM | type UAP struct { | ||||||
|  | 	EN  int    // Флаг включения | ||||||
|  | 	AS  int    // Уровень тревоги | ||||||
|  | 	T   int    // ??? | ||||||
|  | 	VT  int    // ??? | ||||||
|  | 	UN  string // Имя тревоги | ||||||
|  | 	UO  string // Имя тривоги на экране водителя | ||||||
|  | 	APR APR    // Параметры обработки тревоги | ||||||
|  | 	// AP  AP     // Параметры тревоги | ||||||
|  | 	SDT int // ??? | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 7.30 | ||||||
|  | // Network monitoring status parameters | ||||||
|  | type PGDSM struct { | ||||||
|  | 	PGPS PGPS `json:"PGPS,omitempty"` | ||||||
|  | 	PDSM PDSM `json:"PDSM,omitempty"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 7.30.1 | ||||||
|  | // GPS (position) status parameter | ||||||
|  | type PGPS struct { | ||||||
|  | 	EN   uint // Enable | ||||||
|  | 	MODE uint | ||||||
|  | 	SEP  uint `json:"SEP,omitempty"` | ||||||
|  | 	TM   uint `json:"TM,omitempty"` | ||||||
|  | 	NUM  uint `json:"NUM,omitempty"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 7.30.2 | ||||||
|  | // Device status parameter | ||||||
|  | type PDSM struct { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 7.39.3.9 | ||||||
|  | // Platform basic information | ||||||
|  | type PSI struct { | ||||||
|  | 	CG CG `json:"CG,omitempty"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 7.39.3.8 | ||||||
|  | // Total record information for call | ||||||
|  | type CG struct { | ||||||
|  | 	ECL  uint // The maximum calling time | ||||||
|  | 	ECML uint // The maximum calling time per month | ||||||
|  | 	AS   uint // Terminal phonecall answering strategy | ||||||
|  | 	AT   uint // Answer automatically when exceed time | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type RSI struct { | ||||||
|  | 	E int64 // End time in seconds | ||||||
|  | 	S int64 // Start time in seconds | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type RCP struct { | ||||||
|  | 	RSI [][]RSI // Recording type parameters (array for cameras number, 7 for week days) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type RP struct { | ||||||
|  | 	RCP []RCP // Single channel recording period parameter list | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type Setting struct { | ||||||
|  | 	KEYS      KEYS      `json:"KEYS,omitempty"` | ||||||
|  | 	RIP       RIP       `json:"RIP,omitempty"` | ||||||
|  | 	RP        RP        `json:"RP,omitempty"` | ||||||
|  | 	TIMEP     TIMEP     `json:"TIMEP,omitempty"` | ||||||
|  | 	ATP       ATP       `json:"ATP,omitempty"` | ||||||
|  | 	SSP       SSP       `json:"SSP,omitempty"` | ||||||
|  | 	GSP       GSP       `json:"GSP,omitempty"` | ||||||
|  | 	UMP       UMP       `json:"UMP,omitempty"` | ||||||
|  | 	ETHERNET  ETHERNET  `json:"ETHERNET,omitempty"` | ||||||
|  | 	WIFI      WIFI      `json:"WIFI,omitempty"` | ||||||
|  | 	M3G       M3G       `json:"M3G,omitempty"` | ||||||
|  | 	MCMS      MCMS      `json:"MCMS,omitempty"` | ||||||
|  | 	SUBSTRNET SUBSTRNET `json:"SUBSTRNET,omitempty"` | ||||||
|  | 	DOSD      DOSD      `json:"DOSD,omitempty"` | ||||||
|  | 	AR        AR        `json:"AR,omitempty"` | ||||||
|  | 	MAIN      []VEC     `json:"MAIN,omitempty"` | ||||||
|  | 	EOSD      []EOSD    `json:"EOSD,omitempty"` | ||||||
|  | 	IOP       []IOP     `json:"IOP,omitempty"` | ||||||
|  | 	SAP       SAP       `json:"SAP,omitempty"` | ||||||
|  | 	REDEV     []REDEV   `json:"REDEV,omitempty"` | ||||||
|  | 	UAP       UAP       `json:"UAP,omitempty"` | ||||||
|  | 	PGDSM     PGDSM     `json:"PGDSM,omitempty"` | ||||||
|  | 	PVLAS     PVLAS     `json:"PVLAS,omitempty"` | ||||||
|  | 	PMDAS     PMDAS     `json:"PMDAS,omitempty"` | ||||||
|  | 	VS        VS        `json:"VS,omitempty"` | ||||||
|  | 	PSI       PSI       `json:"PSI,omitempty"` | ||||||
|  | 
 | ||||||
|  | 	SWUS SWUS `json:"SWUS,omitempty"` | ||||||
|  | 	// DSM  DSM  `json:"DSM,omitempty"` | ||||||
| } | } | ||||||
							
								
								
									
										57
									
								
								pkg/protocol/alarms.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								pkg/protocol/alarms.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | |||||||
|  | package protocol | ||||||
|  |  | ||||||
|  | type AlarmType uint | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	AlarmTypeVideoLoss AlarmType = iota | ||||||
|  | 	AlarmTypeCameraCovered | ||||||
|  | 	AlarmTypeMotionDetection | ||||||
|  | 	AlarmTypeStorageAbnormal | ||||||
|  | 	AlarmTypeUserDefined | ||||||
|  | 	AlarmTypeSentriesInspection | ||||||
|  | 	AlarmTypeViolation | ||||||
|  | 	AlarmTypeEmergency | ||||||
|  | 	AlarmTypeSpeedAlarm | ||||||
|  | 	AlarmTypeLowVoltage | ||||||
|  | 	AlarmTypeOutOfFence = iota + 7 | ||||||
|  | 	AlarmTypeAccAlarm | ||||||
|  | 	AlarmTypePeripheralsDropped | ||||||
|  | 	AlarmTypeStopAnnouncement | ||||||
|  | 	AlarmTypeGpsAntenna | ||||||
|  | 	AlarmTypeDayNightSwitch | ||||||
|  | 	AlarmTypeProhibitDriving | ||||||
|  | 	AlarmTypeSerialAlarm = iota + 15 | ||||||
|  | 	AlarmTypeFatigueAlarm | ||||||
|  | 	AlarmTypeTakeOutParking | ||||||
|  | 	AlarmTypeGestureAlarm | ||||||
|  | 	AlarmTypeGreenDriving | ||||||
|  | 	AlarmTypeIllegalIgnition | ||||||
|  | 	AlarmTypeIllegalShutdown | ||||||
|  | 	AlarmTypeCustomExternal | ||||||
|  | 	AlarmTypeThinkingLKJ | ||||||
|  | 	AlarmTypeTAX3 | ||||||
|  | 	AlarmTypeOilAlarm | ||||||
|  | 	AlarmTypeBusLineOccupation | ||||||
|  | 	AlarmTypeForgottenAlarm | ||||||
|  | 	AlarmTypeSpecialCustomerFault | ||||||
|  | 	AlarmTypeTemperatureAbnormal | ||||||
|  | 	AlarmTypeTemperatureChangeAbnormal | ||||||
|  | 	AlarmTypeSmokeAlarm | ||||||
|  | 	AlarmTypeGBox | ||||||
|  | 	AlarmTypeLicensePlateRecognition | ||||||
|  | 	AlarmTypeAnotherSpeedAlarm | ||||||
|  | 	AlarmTypeWirelessSignalAbnormal | ||||||
|  | 	AlarmTypeArming | ||||||
|  | 	AlarmTypePhoneCall | ||||||
|  | 	AlarmTypeGPSFault | ||||||
|  | 	AlarmTypeDSMFault | ||||||
|  | 	AlarmTypeFireBox | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type AlarmLevel uint | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	AlarmLevelImportant AlarmLevel = iota | ||||||
|  | 	AlarmLevelGeneral | ||||||
|  | 	AlarmLevelEmergency | ||||||
|  | ) | ||||||
							
								
								
									
										194
									
								
								pkg/protocol/io.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										194
									
								
								pkg/protocol/io.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,194 @@ | |||||||
|  | package protocol | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"encoding/binary" | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"log" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/icza/bitio" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Read package | ||||||
|  | func (e *Package) ReadPackage() bool { | ||||||
|  | 	if len(e.Accum) < 12 { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	r := bitio.NewReader(bytes.NewBuffer(e.Accum)) | ||||||
|  | 	e.Version = uint8(r.TryReadBits(2)) | ||||||
|  | 	e.EncryptionFlag = r.TryReadBool() | ||||||
|  | 	e.CompressFlag = r.TryReadBool() | ||||||
|  | 	e.CSRCCount = uint8(r.TryReadBits(4)) | ||||||
|  | 	e.PayloadType = PayloadType(r.TryReadBits(8)) | ||||||
|  | 	e.SSRC = SpecialPayloadType((r.TryReadBits(8) | (r.TryReadBits(8) << 8))) | ||||||
|  |  | ||||||
|  | 	if e.EncryptionFlag && e.CompressFlag { | ||||||
|  | 		// TODO: get snippet, that use this code | ||||||
|  | 		r.TryReadBits(8 * 4) | ||||||
|  | 		e.payloadLen = r.TryReadBits(8) | ||||||
|  | 		r.TryReadBits(3 * 8) | ||||||
|  |  | ||||||
|  | 		if uint64(len(e.Accum)) < e.payloadLen+12 { | ||||||
|  | 			return false | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		e.payloadLen = r.TryReadBits(32) | ||||||
|  |  | ||||||
|  | 		// WTF: e.CC is useless | ||||||
|  | 		for i := uint64(0); i < 1; i++ { | ||||||
|  | 			e.CSRC[i] = r.TryReadBits(32) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if e.payloadLen > 1e6 { | ||||||
|  | 		log.Printf("%v\n", e) | ||||||
|  | 		log.Panicln("CORRUPTED PACKAGE") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	numOfBytes := 0 | ||||||
|  |  | ||||||
|  | 	if e.payloadLen != 0 { | ||||||
|  | 		e.RawPayload = make([]byte, e.payloadLen) | ||||||
|  | 		numOfBytes = r.TryRead(e.RawPayload) | ||||||
|  | 	} else { | ||||||
|  | 		e.RawPayload = []byte{} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if numOfBytes != int(e.payloadLen) { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	e.Accum = e.Accum[12+e.payloadLen:] | ||||||
|  |  | ||||||
|  | 	switch e.PayloadType { | ||||||
|  | 	case PayloadTypeData: | ||||||
|  | 		e.RawPayload = bytes.TrimRight(e.RawPayload, "\x00") | ||||||
|  | 		if err := json.Unmarshal(e.RawPayload, &e.Payload); err != nil { | ||||||
|  | 			log.Printf("Error parsing JSON payload: %v", err) | ||||||
|  | 			return false | ||||||
|  | 		} | ||||||
|  | 	case PayloadTypeSpecial: | ||||||
|  | 		switch e.SSRC { | ||||||
|  | 		case SpecialPayloadTypeGPS: | ||||||
|  | 			e.GPS.GPSStatus = e.RawPayload[0] | ||||||
|  | 			e.GPS.Expand = e.RawPayload[1] | ||||||
|  | 			e.GPS.Real = e.RawPayload[2] | ||||||
|  |  | ||||||
|  | 			e.GPS.Longitude = float64(binary.BigEndian.Uint32(e.RawPayload[4:8])) / 1e6 | ||||||
|  | 			e.GPS.Latitude = float64(binary.BigEndian.Uint32(e.RawPayload[8:12])) / 1e6 | ||||||
|  | 			e.GPS.Speed = float64(binary.BigEndian.Uint32(e.RawPayload[12:16])) / 100 | ||||||
|  | 			e.GPS.Direction = float64(binary.BigEndian.Uint32(e.RawPayload[16:20])) / 100 | ||||||
|  | 			e.GPS.Altitude = int32(binary.BigEndian.Uint32(e.RawPayload[20:24])) | ||||||
|  |  | ||||||
|  | 			var err error | ||||||
|  | 			if e.GPS.Time, err = time.Parse("20060102150405", string(e.RawPayload[24:38])); err != nil { | ||||||
|  | 				log.Printf("Error parsing time: %v", err) | ||||||
|  | 			} | ||||||
|  | 		default: | ||||||
|  | 			fmt.Println("N9M parser warning: unknown special payload type", e.SSRC) | ||||||
|  | 		} | ||||||
|  | 		// default: | ||||||
|  | 		// 	 fmt.Println("N9M parser warning: unknown payload type", e.PayloadType) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if r.TryError != nil { | ||||||
|  | 		log.Printf("TryError encountered: %v", r.TryError) | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *Package) PackPayload() (err error) { | ||||||
|  | 	e.RawPayload, err = json.Marshal(e.Payload) | ||||||
|  | 	e.payloadLen = uint64(len(e.RawPayload)) | ||||||
|  |  | ||||||
|  | 	if e.payloadLen != 0 { | ||||||
|  | 		e.RawPayload = append(e.RawPayload, 0) | ||||||
|  | 		e.payloadLen++ | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *Package) PackPackage() []byte { | ||||||
|  | 	var err error | ||||||
|  |  | ||||||
|  | 	if err = e.PackPayload(); err != nil { | ||||||
|  | 		log.Printf("Error while packing payload: %v", err) | ||||||
|  | 		return []byte{} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return e.PackRawPackage() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *Package) PackRawPackage() []byte { | ||||||
|  | 	var err error | ||||||
|  |  | ||||||
|  | 	b := &bytes.Buffer{} | ||||||
|  | 	w := bitio.NewWriter(b) | ||||||
|  |  | ||||||
|  | 	w.TryWriteBits(uint64(e.Version), 2) | ||||||
|  |  | ||||||
|  | 	w.TryWriteBool(e.EncryptionFlag) | ||||||
|  | 	w.TryWriteBool(e.CompressFlag) | ||||||
|  |  | ||||||
|  | 	w.TryWriteBits(uint64(e.CSRCCount), 4) | ||||||
|  | 	w.TryWriteBits(uint64(e.PayloadType), 8) | ||||||
|  | 	w.TryWriteBits(uint64(e.SSRC), 16) | ||||||
|  |  | ||||||
|  | 	w.TryWriteBits(e.payloadLen, 32) | ||||||
|  |  | ||||||
|  | 	// WTF: e.CC is useless | ||||||
|  | 	for i := uint64(0); i < 1; i++ { | ||||||
|  | 		w.TryWriteBits(e.CSRC[i], 32) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if e.payloadLen != 0 { | ||||||
|  | 		w.TryWrite(e.RawPayload) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err = w.Close(); err != nil { | ||||||
|  | 		log.Printf("Error while closing writer: %v", err) | ||||||
|  | 		return []byte{} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return b.Bytes() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *Package) GetParametersAs(parameters any) error { | ||||||
|  | 	marshal, err := json.Marshal(e.Payload.Parameter) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return json.Unmarshal(marshal, parameters) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *Package) SetParameters(parameters any) { | ||||||
|  | 	e.Payload.Response = nil | ||||||
|  | 	e.Payload.Parameter = parameters | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *Package) GetResponseAs(response any) error { | ||||||
|  | 	marshal, err := json.Marshal(e.Payload.Response) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return json.Unmarshal(marshal, response) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *Package) SetResponse(response any) { | ||||||
|  | 	e.Payload.Parameter = nil | ||||||
|  | 	e.Payload.Response = response | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *Package) AddToAccum(data []byte) { | ||||||
|  | 	e.Accum = append(e.Accum, data...) | ||||||
|  | } | ||||||
							
								
								
									
										22
									
								
								pkg/protocol/languages.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								pkg/protocol/languages.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | package protocol | ||||||
|  |  | ||||||
|  | type Language uint | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	LanguageSimplifiedChinese Language = iota | ||||||
|  | 	LanguageEnglish | ||||||
|  | 	LanguageKorean | ||||||
|  | 	LanguageItalian | ||||||
|  | 	LanguageGerman | ||||||
|  | 	LanguageThai | ||||||
|  | 	LanguageTurkey | ||||||
|  | 	LanguagePortugal | ||||||
|  | 	LanguageSpain | ||||||
|  | 	LanguageRomania | ||||||
|  | 	LanguageGreece | ||||||
|  | 	LanguageFrench | ||||||
|  | 	LanguageRussian | ||||||
|  | 	LanguageDutch | ||||||
|  | 	LanguageHebrew | ||||||
|  | 	LanguageChineseTraditional | ||||||
|  | ) | ||||||
							
								
								
									
										139
									
								
								pkg/protocol/scheme.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								pkg/protocol/scheme.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,139 @@ | |||||||
|  | package protocol | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"strconv" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type PayloadType uint8 | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	PayloadTypeData                  PayloadType = 0 | ||||||
|  | 	PayloadTypeLive                  PayloadType = 2 | ||||||
|  | 	PayloadTypeDownload              PayloadType = 3 | ||||||
|  | 	PayloadTypePlayback              PayloadType = 4 | ||||||
|  | 	PayloadTypeCapturedPhotos        PayloadType = 6 | ||||||
|  | 	PayloadTypeParameterImport       PayloadType = 10 | ||||||
|  | 	PayloadTypeParameterExport       PayloadType = 11 | ||||||
|  | 	PayloadTypeTransmissionSubStream PayloadType = 15 | ||||||
|  | 	PayloadTypeRecordingSubStream    PayloadType = 16 | ||||||
|  | 	PayloadTypeBlackBox              PayloadType = 17 | ||||||
|  | 	PayloadTypeSpecial               PayloadType = 22 | ||||||
|  | 	PayloadTypeMaintainData          PayloadType = 30 | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type SpecialPayloadType uint16 | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	SpecialPayloadTypeHeartbeat SpecialPayloadType = iota | ||||||
|  | 	SpecialPayloadTypeHeartbeatWithoutBody | ||||||
|  | 	SpecialPayloadTypeGPS | ||||||
|  | 	SpecialPayloadTypeMileage | ||||||
|  | 	SpecialPayloadTypeEnvironmentalQuality | ||||||
|  | 	SpecialPayloadTypeDrivingPosture | ||||||
|  | 	SpecialPayloadTypeScanningGun | ||||||
|  | 	SpecialPayloadTypeOil | ||||||
|  | 	SpecialPayloadTypeGDS | ||||||
|  | 	SpecialPayloadTypeGPSToBWS | ||||||
|  | 	SpecialPayloadTypeCANBOX | ||||||
|  | 	SpecialPayloadTypeGSenor | ||||||
|  | 	SpecialPayloadTypeAckGPS | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type Message struct { | ||||||
|  | 	Module    string      `json:"MODULE"` | ||||||
|  | 	Session   string      `json:"SESSION"` | ||||||
|  | 	Operation string      `json:"OPERATION"` | ||||||
|  | 	Parameter interface{} `json:"PARAMETER,omitempty"` | ||||||
|  | 	Response  interface{} `json:"RESPONSE,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 3.4.5.27.1 | ||||||
|  | type GPSData struct { | ||||||
|  | 	GPSStatus uint8 | ||||||
|  | 	Expand    uint8 | ||||||
|  | 	Real      uint8 | ||||||
|  | 	Longitude float64 | ||||||
|  | 	Latitude  float64 | ||||||
|  | 	Speed     float64 | ||||||
|  | 	Direction float64 | ||||||
|  | 	Altitude  int32 | ||||||
|  | 	Time      time.Time | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (g *GPSData) MarshalJSON() ([]byte, error) { | ||||||
|  | 	var alias struct { | ||||||
|  | 		GPSStatus uint8  `json:"V"` | ||||||
|  | 		Longitude string `json:"J"` | ||||||
|  | 		Latitude  string `json:"W"` | ||||||
|  | 		Speed     uint   `json:"S"` | ||||||
|  | 		Direction uint   `json:"C"` | ||||||
|  | 		Altitude  int32  `json:"H"` | ||||||
|  | 		Time      string `json:"T"` | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	alias.GPSStatus = g.GPSStatus | ||||||
|  | 	alias.Longitude = fmt.Sprintf("%.6f", g.Longitude) | ||||||
|  | 	alias.Latitude = fmt.Sprintf("%.6f", g.Latitude) | ||||||
|  | 	alias.Speed = uint(g.Speed * 100) | ||||||
|  | 	alias.Direction = uint(g.Direction * 100) | ||||||
|  | 	alias.Altitude = g.Altitude | ||||||
|  | 	alias.Time = g.Time.Format("20060102150405") | ||||||
|  |  | ||||||
|  | 	return json.Marshal(alias) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (g *GPSData) UnmarshalJSON(data []byte) (err error) { | ||||||
|  | 	var alias struct { | ||||||
|  | 		GPSStatus uint8  `json:"V"` | ||||||
|  | 		Longitude string `json:"J"` | ||||||
|  | 		Latitude  string `json:"W"` | ||||||
|  | 		Speed     uint   `json:"S"` | ||||||
|  | 		Direction uint   `json:"C"` | ||||||
|  | 		Altitude  int32  `json:"H"` | ||||||
|  | 		Time      string `json:"T"` | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err = json.Unmarshal(data, &alias); err != nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	g.GPSStatus = alias.GPSStatus | ||||||
|  |  | ||||||
|  | 	if g.Longitude, err = strconv.ParseFloat(alias.Longitude, 64); err != nil { | ||||||
|  | 		return fmt.Errorf("invalid longitude: %w", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if g.Latitude, err = strconv.ParseFloat(alias.Latitude, 64); err != nil { | ||||||
|  | 		return fmt.Errorf("invalid latitude: %w", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	g.Speed = float64(alias.Speed) / 100.0 | ||||||
|  | 	g.Direction = float64(alias.Direction) / 100.0 | ||||||
|  | 	g.Altitude = alias.Altitude | ||||||
|  | 	g.Time, _ = time.Parse("20060102150405", alias.Time) | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type Package struct { | ||||||
|  | 	Version        uint8 | ||||||
|  | 	EncryptionFlag bool | ||||||
|  | 	CompressFlag   bool | ||||||
|  | 	CSRCCount      uint8 | ||||||
|  |  | ||||||
|  | 	PayloadType PayloadType | ||||||
|  | 	SSRC        SpecialPayloadType | ||||||
|  | 	Reserved    uint64 | ||||||
|  | 	CSRC        [16]uint64 | ||||||
|  |  | ||||||
|  | 	GPS GPSData | ||||||
|  |  | ||||||
|  | 	payloadLen uint64 | ||||||
|  | 	Payload    Message | ||||||
|  | 	RawPayload []byte | ||||||
|  |  | ||||||
|  | 	Accum []byte | ||||||
|  | } | ||||||
							
								
								
									
										8
									
								
								pkg/protocol/trigger.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								pkg/protocol/trigger.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | package protocol | ||||||
|  |  | ||||||
|  | type TriggerType uint | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	TriggerTypeManual TriggerType = iota | ||||||
|  | 	TriggerTypeAutomatic | ||||||
|  | ) | ||||||
							
								
								
									
										177
									
								
								pkg/smart/channel.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								pkg/smart/channel.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,177 @@ | |||||||
|  | package smart | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"gitea.unprism.ru/KRBL/n9m/v2/pkg/models" | ||||||
|  | 	"gitea.unprism.ru/KRBL/n9m/v2/pkg/protocol" | ||||||
|  | 	"gitea.unprism.ru/KRBL/n9m/v2/pkg/utils" | ||||||
|  | 	"net" | ||||||
|  | 	"sync" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" | ||||||
|  | 	length  = 6 | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func NewAutoSmartChannelPackage(mainSmartPackage *SmartPackage) (*SmartChannelPackage, error) { | ||||||
|  | 	conn, err := net.Dial(mainSmartPackage.conn.RemoteAddr().Network(), mainSmartPackage.conn.RemoteAddr().String()) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return NewSmartChannelPackage(conn, mainSmartPackage) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewSmartChannelPackage(conn net.Conn, mainSmartPackage *SmartPackage) (*SmartChannelPackage, error) { | ||||||
|  | 	pack := NewSmartPackage(conn) | ||||||
|  | 	pack.pack.Payload.Session = mainSmartPackage.pack.Payload.Session | ||||||
|  |  | ||||||
|  | 	channelName, err := registerChannelHandles(pack) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	smartChannelPackage := &SmartChannelPackage{ | ||||||
|  | 		pack:     pack, | ||||||
|  | 		mainPack: mainSmartPackage, | ||||||
|  |  | ||||||
|  | 		channelName: channelName, | ||||||
|  |  | ||||||
|  | 		mutex: sync.RWMutex{}, | ||||||
|  | 		ssrc:  make(map[uint16][]func([]byte) error), | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	pack.AddPayloadHandler(protocol.PayloadTypeLive, smartChannelPackage.handleLiveVideo) | ||||||
|  |  | ||||||
|  | 	return smartChannelPackage, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (channelPack *SmartChannelPackage) Run() error { | ||||||
|  | 	for { | ||||||
|  | 		if err := channelPack.pack.Handle(); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (channelPack *SmartChannelPackage) GetChannelName() string { | ||||||
|  | 	return channelPack.channelName | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (channelPack *SmartChannelPackage) AddLiveSource(ssrc uint16, source func([]byte) error) { | ||||||
|  | 	channelPack.mutex.Lock() | ||||||
|  | 	defer channelPack.mutex.Unlock() | ||||||
|  |  | ||||||
|  | 	channelPack.ssrc[ssrc] = append(channelPack.ssrc[ssrc], source) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (channelPack *SmartChannelPackage) handleLiveVideo(sPack *SmartPackage, pack protocol.Package) error { | ||||||
|  | 	channelPack.mutex.RLock() | ||||||
|  |  | ||||||
|  | 	sources, ok := channelPack.ssrc[uint16(pack.SSRC)] | ||||||
|  |  | ||||||
|  | 	if !ok || len(sources) == 0 { | ||||||
|  | 		channelPack.mutex.RUnlock() | ||||||
|  |  | ||||||
|  | 		var request = models.MediaStreamModelControlStreamRequest{ | ||||||
|  | 			PayloadType: protocol.PayloadTypeLive, | ||||||
|  | 			SSRC:        uint16(pack.SSRC), | ||||||
|  | 			StreamName:  channelPack.channelName, | ||||||
|  | 			Command:     models.MediaStreamCommandStop, | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		pack.PayloadType = protocol.PayloadTypeData | ||||||
|  | 		pack.Payload.Module = "MEDIASTREAMMODEL" | ||||||
|  | 		pack.Payload.Operation = "CONTROLSTREAM" | ||||||
|  | 		pack.SetParameters(request) | ||||||
|  |  | ||||||
|  | 		if _, err := channelPack.mainPack.Write(pack.PackPackage()); err != nil { | ||||||
|  | 			panic(err) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var errorMask = map[int]bool{} | ||||||
|  |  | ||||||
|  | 	for i, source := range sources { | ||||||
|  | 		if err := source(pack.RawPayload); err != nil { | ||||||
|  | 			errorMask[i] = true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	channelPack.mutex.RUnlock() | ||||||
|  |  | ||||||
|  | 	if len(errorMask) > 0 { | ||||||
|  | 		newSources := make([]func([]byte) error, 0, len(sources)) | ||||||
|  |  | ||||||
|  | 		for i, source := range sources { | ||||||
|  | 			if !errorMask[i] { | ||||||
|  | 				newSources = append(newSources, source) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		channelPack.mutex.Lock() | ||||||
|  | 		channelPack.ssrc[uint16(pack.SSRC)] = newSources | ||||||
|  | 		channelPack.mutex.Unlock() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func registerChannelHandles(pack *SmartPackage) (string, error) { | ||||||
|  | 	res := make(chan error, 1) | ||||||
|  |  | ||||||
|  | 	pack.AddJSONHandler("CERTIFICATE", "CREATESTREAM", func(smartPackage *SmartPackage, p protocol.Package) error { | ||||||
|  | 		var params models.CertificateCreateStreamResponse | ||||||
|  |  | ||||||
|  | 		if err := p.GetResponseAs(¶ms); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if params.ErrorCode != 0 { | ||||||
|  | 			res <- errors.New(params.ErrorCause) | ||||||
|  | 		} else { | ||||||
|  | 			res <- nil | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return nil | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	channelName := utils.RandomString(length, charset) | ||||||
|  |  | ||||||
|  | 	var request = models.CertificateCreateStreamRequest{ | ||||||
|  | 		StreamName: channelName, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	pack.pack.Payload.Module = "CERTIFICATE" | ||||||
|  | 	pack.pack.Payload.Operation = "CREATESTREAM" | ||||||
|  | 	pack.pack.SetParameters(request) | ||||||
|  |  | ||||||
|  | 	pack.Write(pack.pack.PackPackage()) | ||||||
|  |  | ||||||
|  | 	for { | ||||||
|  | 		if err := pack.Handle(); err != nil { | ||||||
|  | 			return "", err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		select { | ||||||
|  | 		case err := <-res: | ||||||
|  | 			close(res) | ||||||
|  |  | ||||||
|  | 			if err != nil { | ||||||
|  | 				return "", err | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			pack.AddJSONHandler("CERTIFICATE", "CREATESTREAM", func(smartPackage *SmartPackage, p protocol.Package) error { | ||||||
|  | 				return errors.New("stream already exists") | ||||||
|  | 			}) | ||||||
|  |  | ||||||
|  | 			return channelName, nil | ||||||
|  | 		default: | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										33
									
								
								pkg/smart/scheme.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								pkg/smart/scheme.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | |||||||
|  | package smart | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"gitea.unprism.ru/KRBL/n9m/v2/pkg/models" | ||||||
|  | 	"gitea.unprism.ru/KRBL/n9m/v2/pkg/protocol" | ||||||
|  | 	"net" | ||||||
|  | 	"sync" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type ProcessFunc func(*SmartPackage, protocol.Package) error | ||||||
|  | type AlarmProcessFunc func(*SmartPackage, protocol.Package, models.SendAlarmInfoResponse) error | ||||||
|  |  | ||||||
|  | type SmartPackage struct { | ||||||
|  | 	pack protocol.Package | ||||||
|  |  | ||||||
|  | 	conn net.Conn | ||||||
|  | 	buff []byte | ||||||
|  |  | ||||||
|  | 	payloadProcess map[protocol.PayloadType]ProcessFunc | ||||||
|  | 	jsonProcess    map[string]ProcessFunc | ||||||
|  | 	alarmProcess   map[protocol.AlarmType]AlarmProcessFunc | ||||||
|  | 	Storage        map[string]interface{} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type SmartChannelPackage struct { | ||||||
|  | 	pack     *SmartPackage | ||||||
|  | 	mainPack *SmartPackage | ||||||
|  |  | ||||||
|  | 	channelName string | ||||||
|  |  | ||||||
|  | 	mutex sync.RWMutex | ||||||
|  | 	ssrc  map[uint16][]func([]byte) error | ||||||
|  | } | ||||||
							
								
								
									
										144
									
								
								pkg/smart/smart.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								pkg/smart/smart.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,144 @@ | |||||||
|  | package smart | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"gitea.unprism.ru/KRBL/n9m/v2/pkg/models" | ||||||
|  | 	"gitea.unprism.ru/KRBL/n9m/v2/pkg/protocol" | ||||||
|  | 	"net" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type notFoundError struct { | ||||||
|  | 	message string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *notFoundError) Error() string { | ||||||
|  | 	return fmt.Sprintf("not found %s", e.message) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewSmartPackage(conn net.Conn) *SmartPackage { | ||||||
|  | 	return &SmartPackage{ | ||||||
|  | 		pack:           protocol.Package{}, | ||||||
|  | 		conn:           conn, | ||||||
|  | 		buff:           make([]byte, 1024), | ||||||
|  | 		payloadProcess: make(map[protocol.PayloadType]ProcessFunc), | ||||||
|  | 		jsonProcess:    make(map[string]ProcessFunc), | ||||||
|  | 		alarmProcess:   make(map[protocol.AlarmType]AlarmProcessFunc), | ||||||
|  | 		Storage:        make(map[string]interface{}), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (pack *SmartPackage) AddPayloadHandler(payloadType protocol.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 protocol.AlarmType, processFunc AlarmProcessFunc) { | ||||||
|  | 	pack.alarmProcess[alarmType] = processFunc | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (pack *SmartPackage) handleAlarm() (err error) { | ||||||
|  | 	if !(pack.pack.PayloadType == protocol.PayloadTypeData && pack.pack.Payload.Module == "EVEM" && pack.pack.Payload.Operation == "SENDALARMINFO") { | ||||||
|  | 		return fmt.Errorf("invalid payload type or operation for alarm handling") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var params models.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 ¬FoundError{ | ||||||
|  | 			message: fmt.Sprintf("alarm %d", params.AlarmType), | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var response models.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 != protocol.PayloadTypeData { | ||||||
|  | 		return fmt.Errorf("invalid json payload type") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var nfErr *notFoundError | ||||||
|  | 	if err = pack.handleAlarm(); err == nil || errors.As(err, &nfErr) { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var processFunc ProcessFunc | ||||||
|  | 	var ok bool | ||||||
|  | 	var key = fmt.Sprintf("%s:%s", pack.pack.Payload.Module, pack.pack.Payload.Operation) | ||||||
|  | 	if processFunc, ok = pack.jsonProcess[key]; !ok { | ||||||
|  | 		return ¬FoundError{ | ||||||
|  | 			message: fmt.Sprintf("json %s", key), | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return processFunc(pack, pack.pack) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (pack *SmartPackage) handle() (err error) { | ||||||
|  | 	var nfErr *notFoundError | ||||||
|  | 	if err = pack.handleJson(); err == nil || errors.As(err, &nfErr) { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var processFunc ProcessFunc | ||||||
|  | 	var ok bool | ||||||
|  | 	if processFunc, ok = pack.payloadProcess[pack.pack.PayloadType]; !ok { | ||||||
|  | 		return ¬FoundError{ | ||||||
|  | 			message: fmt.Sprintf("payload type %d", pack.pack.PayloadType), | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	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() protocol.Package { | ||||||
|  | 	return pack.pack | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (pack *SmartPackage) Write(data []byte) (int, error) { | ||||||
|  | 	return pack.conn.Write(data) | ||||||
|  | } | ||||||
							
								
								
									
										23
									
								
								pkg/utils/crypto.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								pkg/utils/crypto.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | package utils | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"crypto/hmac" | ||||||
|  | 	"crypto/md5" | ||||||
|  | 	"encoding/hex" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func generateHMAC(key string, data string) string { | ||||||
|  | 	mac := hmac.New(md5.New, []byte(key)) | ||||||
|  |  | ||||||
|  | 	mac.Write([]byte(data)) | ||||||
|  |  | ||||||
|  | 	return hex.EncodeToString(mac.Sum(nil)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func GenerateVerifyKey(key string) string { | ||||||
|  | 	return generateHMAC(key, key) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func GeneratePasswordHash(password string) string { | ||||||
|  | 	return generateHMAC("streaming", password) | ||||||
|  | } | ||||||
							
								
								
									
										18
									
								
								pkg/utils/random.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								pkg/utils/random.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | package utils | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"math/rand" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // seededRand is a source of random numbers seeded with the current time. | ||||||
|  | var seededRand *rand.Rand = rand.New(rand.NewSource(time.Now().UnixNano())) | ||||||
|  |  | ||||||
|  | // randomString generates a random string of the specified length using the given charset. | ||||||
|  | func RandomString(length int, charset string) string { | ||||||
|  | 	b := make([]byte, length) | ||||||
|  | 	for i := range b { | ||||||
|  | 		b[i] = charset[seededRand.Intn(len(charset))] | ||||||
|  | 	} | ||||||
|  | 	return string(b) | ||||||
|  | } | ||||||
							
								
								
									
										32
									
								
								scheme.go
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								scheme.go
									
									
									
									
									
								
							| @@ -1,32 +0,0 @@ | |||||||
| package n9m |  | ||||||
|  |  | ||||||
| import "github.com/tidwall/gjson" |  | ||||||
|  |  | ||||||
| type PayloadJson struct { |  | ||||||
| 	Module     string |  | ||||||
| 	Key        string |  | ||||||
| 	Operation  string |  | ||||||
| 	Parameters map[string]interface{} |  | ||||||
| 	Response   map[string]interface{} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type Package struct { |  | ||||||
| 	Version     uint64 |  | ||||||
| 	Encription  uint64 |  | ||||||
| 	Mark        uint64 |  | ||||||
| 	CC          uint64 |  | ||||||
| 	PayloadType uint64 |  | ||||||
| 	SSRC        uint64 |  | ||||||
| 	Reserved    uint64 |  | ||||||
| 	CSRC        [16]uint64 |  | ||||||
|  |  | ||||||
| 	PayloadLen uint64 |  | ||||||
| 	GPayload   gjson.Result |  | ||||||
| 	Payload    map[string]interface{} |  | ||||||
| 	RawPayload []byte |  | ||||||
|  |  | ||||||
| 	Raw   []byte |  | ||||||
| 	Accum []byte |  | ||||||
|  |  | ||||||
| 	Json PayloadJson |  | ||||||
| } |  | ||||||
							
								
								
									
										25
									
								
								utils.go
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								utils.go
									
									
									
									
									
								
							| @@ -1,25 +0,0 @@ | |||||||
| package n9m |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"encoding/hex" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // add bytes to accum |  | ||||||
| func (e *Package) AddToAccum(data []byte) { |  | ||||||
| 	e.Accum = append(e.Accum, data...) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // why store a string and constantly change it to the same thing |  | ||||||
| // the stored string is not used |  | ||||||
| func (e *Package) GetToken() { |  | ||||||
| 	hexStream := "3876431502000010380000007b224b4559223a22434c49454e544c4f47494e222c22524553504f4e5345223a7b22434c49454e544944223a2237643531323030227d7d00" |  | ||||||
|  |  | ||||||
| 	e.RawPayload, _ = hex.DecodeString(hexStream) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // the same |  | ||||||
| func (e *Package) RequestGetTokenDop() { |  | ||||||
| 	hexStream := "3876431501000010360000007b224b4559223a224c4f47494e222c22504152414d223a7b224355534552223a2231222c22505744223a22313233343536227d7d0a00" |  | ||||||
|  |  | ||||||
| 	e.RawPayload, _ = hex.DecodeString(hexStream) |  | ||||||
| } |  | ||||||
		Reference in New Issue
	
	Block a user