Compare commits
	
		
			5 Commits
		
	
	
		
			v2.0.0-alp
			...
			v2.0.5-alp
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						9d2db3672c
	
				 | 
					
					
						|||
| 
						
						
							
						
						519ad39c0f
	
				 | 
					
					
						|||
| 
						
						
							
						
						7c348629d6
	
				 | 
					
					
						|||
| 
						
						
							
						
						58b1c67b97
	
				 | 
					
					
						|||
| 
						
						
							
						
						102c9bb36a
	
				 | 
					
					
						
@@ -1,10 +1,13 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"gitea.unprism.ru/KRBL/n9m"
 | 
			
		||||
	"gitea.unprism.ru/KRBL/n9m/v2"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net"
 | 
			
		||||
	"os"
 | 
			
		||||
	"syscall"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
@@ -27,37 +30,21 @@ func main() {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func handle(conn net.Conn) {
 | 
			
		||||
	var err error
 | 
			
		||||
	var rn int
 | 
			
		||||
 | 
			
		||||
	var serial string
 | 
			
		||||
 | 
			
		||||
	packS := n9m.Package{}
 | 
			
		||||
	tmp := make([]byte, 1024)
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		rn, err = conn.Read(tmp)
 | 
			
		||||
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Println(err)
 | 
			
		||||
			return
 | 
			
		||||
func handleSpecialPackages(_ *n9m.SmartPackage, pack n9m.Package) error {
 | 
			
		||||
	switch pack.SSRC {
 | 
			
		||||
	case n9m.SpecialPayloadTypeGPS:
 | 
			
		||||
		fmt.Printf("%+v\n", pack.GPS)
 | 
			
		||||
		return nil
 | 
			
		||||
	default:
 | 
			
		||||
		return fmt.Errorf("unhandled special operation: %d", pack.SSRC)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		packS.AddToAccum(tmp[:rn])
 | 
			
		||||
 | 
			
		||||
		for packS.ReadPackage() {
 | 
			
		||||
			switch packS.PayloadType {
 | 
			
		||||
			case n9m.PayloadTypeData:
 | 
			
		||||
				combined := packS.Payload.Module + ":" + packS.Payload.Operation
 | 
			
		||||
 | 
			
		||||
				switch combined {
 | 
			
		||||
				case "CERTIFICATE:CONNECT":
 | 
			
		||||
func handleCertificateConnect(sPack *n9m.SmartPackage, pack n9m.Package) (err error) {
 | 
			
		||||
	var params n9m.CertificateConnectRequest
 | 
			
		||||
 | 
			
		||||
					if err = packS.GetParametersAs(¶ms); err != nil {
 | 
			
		||||
						fmt.Println(combined, err)
 | 
			
		||||
						return
 | 
			
		||||
	if err = pack.GetParametersAs(¶ms); err != nil {
 | 
			
		||||
		return fmt.Errorf("failed to get parameters: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var response = n9m.CertificateConnectResponse{
 | 
			
		||||
@@ -65,23 +52,95 @@ func handle(conn net.Conn) {
 | 
			
		||||
		CommandMask: n9m.CommandMaskAll,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
					packS.SetResponse(response)
 | 
			
		||||
	pack.SetResponse(response)
 | 
			
		||||
 | 
			
		||||
					if _, err = conn.Write(packS.PackPackage()); err != nil {
 | 
			
		||||
						fmt.Println(combined, err)
 | 
			
		||||
						return
 | 
			
		||||
	if _, err = sPack.Write(pack.PackPackage()); err != nil {
 | 
			
		||||
		return fmt.Errorf("failed to write package: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fmt.Println("Connected:", params.SerialNumber)
 | 
			
		||||
					serial = params.SerialNumber
 | 
			
		||||
	sPack.Storage["serial"] = params.SerialNumber
 | 
			
		||||
 | 
			
		||||
	var request n9m.ConfigModelGetRequest
 | 
			
		||||
	request.MDVR = "?"
 | 
			
		||||
					packS.Payload.Module = "CONFIGMODEL"
 | 
			
		||||
					packS.Payload.Operation = "GET"
 | 
			
		||||
					packS.SetParameters(request)
 | 
			
		||||
 | 
			
		||||
					conn.Write(packS.PackPackage())
 | 
			
		||||
	pack.Payload.Module = "CONFIGMODEL"
 | 
			
		||||
	pack.Payload.Operation = "GET"
 | 
			
		||||
	pack.SetParameters(request)
 | 
			
		||||
 | 
			
		||||
	sPack.Write(pack.PackPackage())
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func handleKeepAlive(sPack *n9m.SmartPackage, pack n9m.Package) (err error) {
 | 
			
		||||
	serial := sPack.Storage["serial"]
 | 
			
		||||
	fmt.Println(serial, "still alive!")
 | 
			
		||||
 | 
			
		||||
	pack.SetResponse(nil)
 | 
			
		||||
	sPack.Write(pack.PackPackage())
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func handleGetConfig(sPack *n9m.SmartPackage, pack n9m.Package) (err error) {
 | 
			
		||||
	serial := sPack.Storage["serial"]
 | 
			
		||||
 | 
			
		||||
	os.WriteFile(fmt.Sprintf("./%s.json", serial), pack.RawPayload, 0644)
 | 
			
		||||
 | 
			
		||||
	var request n9m.ConfigModelSetRequest
 | 
			
		||||
 | 
			
		||||
	if err = pack.GetParametersAs(&request); err != nil {
 | 
			
		||||
		fmt.Println(err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func handleUselessAlarms(sPack *n9m.SmartPackage, pack n9m.Package, response n9m.SendAlarmInfoResponse) (err error) {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func handleVideoLossAlarm(sPack *n9m.SmartPackage, pack n9m.Package, response n9m.SendAlarmInfoResponse) (err error) {
 | 
			
		||||
	fmt.Println("Video loss alarm!")
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func handleCameraCoveredAlarm(sPack *n9m.SmartPackage, pack n9m.Package, response n9m.SendAlarmInfoResponse) (err error) {
 | 
			
		||||
	fmt.Println("Camera covered alarm!")
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func handleSPI(_ *n9m.SmartPackage, pack n9m.Package) (err error) {
 | 
			
		||||
	var params n9m.SpiParameters
 | 
			
		||||
 | 
			
		||||
	if err = pack.GetParametersAs(¶ms); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fmt.Printf("%+v\n", params)
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func createSmartPackage(conn net.Conn) (pack *n9m.SmartPackage) {
 | 
			
		||||
	pack = n9m.NewSmartPackage(conn)
 | 
			
		||||
 | 
			
		||||
	pack.AddPayloadHandler(n9m.PayloadTypeSpecial, handleSpecialPackages)
 | 
			
		||||
 | 
			
		||||
	pack.AddJSONHandler("CERTIFICATE", "CONNECT", handleCertificateConnect)
 | 
			
		||||
	pack.AddJSONHandler("CERTIFICATE", "KEEPALIVE", handleKeepAlive)
 | 
			
		||||
	pack.AddJSONHandler("CONFIGMODEL", "GET", handleGetConfig)
 | 
			
		||||
	pack.AddJSONHandler("DEVEMM", "SPI", handleSPI)
 | 
			
		||||
 | 
			
		||||
	pack.AddAlarmHandler(n9m.AlarmTypeMotionDetection, handleUselessAlarms)
 | 
			
		||||
 | 
			
		||||
	pack.AddAlarmHandler(n9m.AlarmTypeVideoLoss, handleVideoLossAlarm)
 | 
			
		||||
	pack.AddAlarmHandler(n9m.AlarmTypeCameraCovered, handleCameraCoveredAlarm)
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
	go func() {
 | 
			
		||||
@@ -101,74 +160,30 @@ func handle(conn net.Conn) {
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
*/
 | 
			
		||||
				case "CERTIFICATE:KEEPALIVE":
 | 
			
		||||
					packS.SetResponse(nil)
 | 
			
		||||
					if _, err = conn.Write(packS.PackPackage()); err != nil {
 | 
			
		||||
						fmt.Println(combined, err)
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
					fmt.Println("Connection is still alive!")
 | 
			
		||||
				case "EVEM:GGALARMING":
 | 
			
		||||
					fmt.Println(string(packS.RawPayload))
 | 
			
		||||
 | 
			
		||||
					var response n9m.EventModelGetAlarmingResponse
 | 
			
		||||
					if err = packS.GetResponseAs(&response); err != nil {
 | 
			
		||||
						fmt.Println(combined, err)
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					fmt.Printf("%+v\n", response)
 | 
			
		||||
				case "CONFIGMODEL:GET":
 | 
			
		||||
					os.WriteFile(fmt.Sprintf("./%s.json", serial), packS.RawPayload, 0644)
 | 
			
		||||
				case "EVEM:SENDALARMINFO":
 | 
			
		||||
					var params n9m.SendAlarmInfoParameters
 | 
			
		||||
					var response n9m.SendAlarmInfoResponse
 | 
			
		||||
 | 
			
		||||
					if err = packS.GetParametersAs(¶ms); err != nil {
 | 
			
		||||
						fmt.Printf("Error: %s\nData: %s", err, packS.RawPayload)
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					response.ErrorCode = 0
 | 
			
		||||
					response.AlarmType = params.AlarmType
 | 
			
		||||
					response.CommandType = params.CommandType
 | 
			
		||||
					response.AlarmUID = params.AlarmUID
 | 
			
		||||
					response.NumberOfRestarts = params.NumberOfRestarts
 | 
			
		||||
					response.InstructionSerial = params.InstructionSerial
 | 
			
		||||
 | 
			
		||||
					switch params.AlarmType {
 | 
			
		||||
					case n9m.AlarmTypeMotionDetection:
 | 
			
		||||
						break
 | 
			
		||||
					case n9m.AlarmTypeVideoLoss, n9m.AlarmTypeCameraCovered:
 | 
			
		||||
						var cameraParams n9m.SendAlarmInfoCameraParameters
 | 
			
		||||
 | 
			
		||||
						if err = packS.GetParametersAs(&cameraParams); err != nil {
 | 
			
		||||
							fmt.Printf("Error: %s\nData: %s", err, packS.RawPayload)
 | 
			
		||||
							continue
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						fmt.Printf("%+v\n", cameraParams)
 | 
			
		||||
 | 
			
		||||
						packS.SetResponse(response)
 | 
			
		||||
						conn.Write(packS.PackPackage())
 | 
			
		||||
					default:
 | 
			
		||||
						fmt.Println("Unknown alarm type:", params.AlarmType)
 | 
			
		||||
					}
 | 
			
		||||
				default:
 | 
			
		||||
					fmt.Println("Strange operation:", combined)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			case n9m.PayloadTypeSpecial:
 | 
			
		||||
				switch packS.SSRC {
 | 
			
		||||
				case n9m.SpecialPayloadTypeGPS:
 | 
			
		||||
					fmt.Printf("%+v\n", packS.GPS)
 | 
			
		||||
				default:
 | 
			
		||||
					fmt.Println("Unhandled special operation:", packS.SSRC)
 | 
			
		||||
				}
 | 
			
		||||
			default:
 | 
			
		||||
				fmt.Println("Unhandled operation:", packS.PayloadType)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										176
									
								
								devemm.go
									
									
									
									
									
								
							
							
						
						
									
										176
									
								
								devemm.go
									
									
									
									
									
								
							@@ -1,5 +1,12 @@
 | 
			
		||||
package n9m
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
// main server util
 | 
			
		||||
@@ -36,3 +43,172 @@ func (e *Package) ResponseGeolocation(errorCode int, errorCause string, serial i
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
// 3.4.5.28
 | 
			
		||||
type SpiParameters struct {
 | 
			
		||||
	DriveFlag    uint            `json:"T"`
 | 
			
		||||
	DataMask     uint            `json:"M"`
 | 
			
		||||
	Position     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"`
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
									
									
									
									
								
							@@ -1,4 +1,4 @@
 | 
			
		||||
module gitea.unprism.ru/KRBL/n9m
 | 
			
		||||
module gitea.unprism.ru/KRBL/n9m/v2
 | 
			
		||||
 | 
			
		||||
go 1.21.3
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -331,5 +331,5 @@ type Setting struct {
 | 
			
		||||
	PSI       PSI       `json:"PSI,omitempty"`
 | 
			
		||||
 | 
			
		||||
	SWUS SWUS `json:"SWUS,omitempty"`
 | 
			
		||||
	DSM  DSM  `json:"DSM,omitempty"`
 | 
			
		||||
	// DSM  DSM  `json:"DSM,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										16
									
								
								scheme.go
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								scheme.go
									
									
									
									
									
								
							@@ -3,6 +3,7 @@ package n9m
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
@@ -137,3 +138,18 @@ type Package struct {
 | 
			
		||||
 | 
			
		||||
	Accum []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ProcessFunc func(*SmartPackage, Package) error
 | 
			
		||||
type AlarmProcessFunc func(*SmartPackage, Package, SendAlarmInfoResponse) error
 | 
			
		||||
 | 
			
		||||
type SmartPackage struct {
 | 
			
		||||
	pack Package
 | 
			
		||||
 | 
			
		||||
	conn net.Conn
 | 
			
		||||
	buff []byte
 | 
			
		||||
 | 
			
		||||
	payloadProcess map[PayloadType]ProcessFunc
 | 
			
		||||
	jsonProcess    map[string]ProcessFunc
 | 
			
		||||
	alarmProcess   map[AlarmType]AlarmProcessFunc
 | 
			
		||||
	Storage        map[string]interface{}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										142
									
								
								smart.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								smart.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,142 @@
 | 
			
		||||
package n9m
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"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:           Package{},
 | 
			
		||||
		conn:           conn,
 | 
			
		||||
		buff:           make([]byte, 1024),
 | 
			
		||||
		payloadProcess: make(map[PayloadType]ProcessFunc),
 | 
			
		||||
		jsonProcess:    make(map[string]ProcessFunc),
 | 
			
		||||
		alarmProcess:   make(map[AlarmType]AlarmProcessFunc),
 | 
			
		||||
		Storage:        make(map[string]interface{}),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (pack *SmartPackage) AddPayloadHandler(payloadType PayloadType, processFunc ProcessFunc) {
 | 
			
		||||
	pack.payloadProcess[payloadType] = processFunc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (pack *SmartPackage) AddJSONHandler(module, operation string, processFunc ProcessFunc) {
 | 
			
		||||
	pack.jsonProcess[fmt.Sprintf("%s:%s", module, operation)] = processFunc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (pack *SmartPackage) AddAlarmHandler(alarmType AlarmType, processFunc AlarmProcessFunc) {
 | 
			
		||||
	pack.alarmProcess[alarmType] = processFunc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (pack *SmartPackage) handleAlarm() (err error) {
 | 
			
		||||
	if !(pack.pack.PayloadType == PayloadTypeData && pack.pack.Payload.Module == "EVEM" && pack.pack.Payload.Operation == "SENDALARMINFO") {
 | 
			
		||||
		return fmt.Errorf("invalid payload type or operation for alarm handling")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var params SendAlarmInfoParameters
 | 
			
		||||
	if err = pack.pack.GetParametersAs(¶ms); err != nil {
 | 
			
		||||
		return fmt.Errorf("invalid payload")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var processFunc AlarmProcessFunc
 | 
			
		||||
	var ok bool
 | 
			
		||||
	if processFunc, ok = pack.alarmProcess[params.AlarmType]; !ok {
 | 
			
		||||
		return ¬FoundError{
 | 
			
		||||
			message: fmt.Sprintf("alarm %d", params.AlarmType),
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var response SendAlarmInfoResponse
 | 
			
		||||
 | 
			
		||||
	response.ErrorCode = 0
 | 
			
		||||
	response.AlarmType = params.AlarmType
 | 
			
		||||
	response.CommandType = params.CommandType
 | 
			
		||||
	response.AlarmUID = params.AlarmUID
 | 
			
		||||
	response.NumberOfRestarts = params.NumberOfRestarts
 | 
			
		||||
	response.InstructionSerial = params.InstructionSerial
 | 
			
		||||
 | 
			
		||||
	return processFunc(pack, pack.pack, response)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (pack *SmartPackage) handleJson() (err error) {
 | 
			
		||||
	if pack.pack.PayloadType != PayloadTypeData {
 | 
			
		||||
		return fmt.Errorf("invalid json payload type")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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() Package {
 | 
			
		||||
	return pack.pack
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (pack *SmartPackage) Write(data []byte) (int, error) {
 | 
			
		||||
	return pack.conn.Write(data)
 | 
			
		||||
}
 | 
			
		||||
@@ -2,7 +2,7 @@ package test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"gitea.unprism.ru/KRBL/n9m"
 | 
			
		||||
	"gitea.unprism.ru/KRBL/n9m/v2"
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user