Compare commits

...

7 Commits
main ... v2

Author SHA1 Message Date
9d2db3672c
Add SPI data handling and serialization support
Introduced `SpiParameters` and related structs to process SPI data. Added JSON marshaling/unmarshaling logic and a handler for the "SPI" command in the server. This enables parsing and transforming SPI data effectively for device communication.
2025-02-23 15:13:27 +03:00
519ad39c0f
Removed DSM 2025-02-23 12:57:09 +03:00
7c348629d6
Add custom notFoundError type and improve error handling
Introduce the notFoundError struct to provide more detailed error messages for missing handlers like alarms, JSON operations, and payload types. Update error handling to leverage the new custom type and use errors.As for better flexibility. Additionally, update module imports to version v2 in relevant files.
2025-02-23 11:51:16 +03:00
58b1c67b97
Update module path to include version suffix
The module path was updated to align with Go versioning conventions by appending "/v2". This change ensures compatibility with Go's module versioning system and properly reflects the major version update.
2025-02-22 22:16:52 +03:00
102c9bb36a
Refactor network handling with SmartPackage abstraction
Introduced the SmartPackage struct to simplify and centralize logic for handling various payloads, including JSON and alarms. Moved specific handlers into dedicated functions and utilized maps for dynamic process function assignment. Improved error handling and modularity for better scalability and maintainability.
2025-02-22 21:15:03 +03:00
16da4affa8
Refactor and enhance handling of payloads and data structures
Added support for handling various payload types, including GPS and alarms, with new structures and constants. Introduced helper methods for JSON marshalling/unmarshalling of GPS data and modularized the handling of certificates, configurations, and alarms. Implemented foundational server code for testing and expanded several package functionalities.
2025-02-22 19:23:16 +03:00
f3083820dc
Added testing and rewrite serializer
Signed-off-by: Alexander Lazarenko <kerblif@unprism.ru>
2025-02-07 21:04:06 +03:00
15 changed files with 1150 additions and 212 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.idea

View File

@ -1,5 +1,60 @@
package n9m package n9m
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 CertificateConnectResponse struct {
ErrorCode uint `json:"ERRORCODE"`
ErrorCause string `json:"ERRORCAUSE"`
CommandMask uint `json:"MASKCMD"`
}
type CommandMaskParameters uint
const (
CommandMaskAlarm = 1 << iota
CommandMaskScanningGun
CommandMaskPassengerFlow
CommandMaskFaceContrast
CommandMaskCard
CommandMaskShutdownReport
CommandMaskGPSReport
CommandMaskAll = 0b1111111
)
/*
func (e *Package) RequestConnect(session string, serial string, numOfCams int) { func (e *Package) RequestConnect(session string, serial string, numOfCams int) {
e.Payload = map[string]any{ e.Payload = map[string]any{
"MODULE": "CERTIFICATE", "MODULE": "CERTIFICATE",
@ -39,3 +94,5 @@ func (e *Package) ResponseCertificateConnect(Sid string) {
"SESSION": Sid, "SESSION": Sid,
} }
} }
*/

189
cmd/dev-server/main.go Normal file
View File

@ -0,0 +1,189 @@
package main
import (
"errors"
"fmt"
"gitea.unprism.ru/KRBL/n9m/v2"
"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(_ *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)
}
}
func handleCertificateConnect(sPack *n9m.SmartPackage, pack n9m.Package) (err error) {
var params n9m.CertificateConnectRequest
if err = pack.GetParametersAs(&params); err != nil {
return fmt.Errorf("failed to get parameters: %w", err)
}
var response = n9m.CertificateConnectResponse{
ErrorCode: 0,
CommandMask: n9m.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 n9m.ConfigModelGetRequest
request.MDVR = "?"
pack.Payload.Module = "CONFIGMODEL"
pack.Payload.Operation = "GET"
pack.SetParameters(request)
sPack.Write(pack.PackPackage())
return
}
func handleKeepAlive(sPack *n9m.SmartPackage, pack n9m.Package) (err error) {
serial := sPack.Storage["serial"]
fmt.Println(serial, "still alive!")
pack.SetResponse(nil)
sPack.Write(pack.PackPackage())
return
}
func handleGetConfig(sPack *n9m.SmartPackage, pack n9m.Package) (err error) {
serial := sPack.Storage["serial"]
os.WriteFile(fmt.Sprintf("./%s.json", serial), pack.RawPayload, 0644)
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(&params); 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() {
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
}
}
}
}

View File

@ -1,5 +1,7 @@
package n9m package n9m
/*
// 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) {
e.Payload = map[string]any{ e.Payload = map[string]any{
@ -26,37 +28,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 Setting `json:"MDVR"`
e.Payload = map[string]any{
"MODULE": "CONFIGMODEL",
"OPERATION": "SET",
"PARAMETER": map[string]any{
"MDVR": map[string]any{
"KEYS": map[string]any{ // KEY parameters
"GV": 1, // GPS version
},
"PGDSM": map[string]any{ // Network monitoring status parameters
"PGPS": map[string]any{ // GPS position
"EN": 1, // Real-time position monitoring
"TM": 10, // Time interval
},
},
"PSI": map[string]any{ // Platform basic information
"CG": map[string]any{ // Call information
"AS": 0, // Automatic answer
},
},
"SUBSTRNET": map[string]any{
"SM": 1, // 0-Smooth .. 4-Clear
},
},
},
}
} }
// main server util type ConfigModelGetRequest struct {
MDVR interface{} `json:"MDVR"`
}
type ConfigModelSetResponse struct {
MDVR Setting `json:"MDVR"`
}
func (e *Package) InitialConfigModelSetRequest() {
e.SetParameters(ConfigModelSetRequest{
MDVR: Setting{
KEYS: KEYS{
GV: 1, // GPS version
},
PGDSM: PGDSM{
PGPS: PGPS{
EN: 1, // Real-time position monitoring
MODE: 0b10, // Enable timer
TM: 10, // Time interval
},
},
SUBSTRNET: SUBSTRNET{
SM: 1,
},
},
})
}
/*
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 +75,5 @@ func (e *Package) ResponseConfigModelSet(Sid string) {
"SESSION": Sid, "SESSION": Sid,
} }
} }
*/

180
devemm.go
View File

@ -1,6 +1,13 @@
package n9m package n9m
import "fmt" import (
"encoding/json"
"fmt"
"strconv"
"time"
)
/*
// main server util // main server util
func (e *Package) RequestGeolocation(serial int, Sid string) { func (e *Package) RequestGeolocation(serial int, Sid string) {
@ -34,3 +41,174 @@ 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"`
}

172
evem.go
View File

@ -1,5 +1,175 @@
package n9m package n9m
// 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 AlarmType `json:"ALARMTYPE"`
CommandType uint `json:"CMDTYPE"`
AlarmUID uint `json:"ALARMUID"`
NumberOfRestarts uint `json:"RUN"`
AlarmLevel AlarmLevel `json:"ALARMAS"`
AlarmCount uint `json:"ALARMCOUNT"`
TriggerType TriggerType `json:"TRIGGERTYPE"`
ContinueTime uint `json:"CONTINUETIME"`
CurrentTime uint `json:"CURRENTTIME"`
Language Language `json:"L"`
GPSData 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 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 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
)
type TriggerType uint
const (
TriggerTypeManual TriggerType = iota
TriggerTypeAutomatic
)
type Language uint
const (
LanguageSimplifiedChinese Language = iota
LanguageEnglish
LanguageKorean
LanguageItalian
LanguageGerman
LanguageThai
LanguageTurkey
LanguagePortugal
LanguageSpain
LanguageRomania
LanguageGreece
LanguageFrench
LanguageRussian
LanguageDutch
LanguageHebrew
LanguageChineseTraditional
)
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"`
}
/*
// main server util // main server util
func (e *Package) ResponseAlarm(alarmType int64, alarmUID int64, cmdno int64, cmdtype int64, run int64, serial string, Sid string) { func (e *Package) ResponseAlarm(alarmType int64, alarmUID int64, cmdno int64, cmdtype int64, run int64, serial string, Sid string) {
e.Payload = map[string]any{ e.Payload = map[string]any{
@ -16,3 +186,5 @@ func (e *Package) ResponseAlarm(alarmType int64, alarmUID int64, cmdno int64, cm
"SESSION": Sid, "SESSION": Sid,
} }
} }
*/

12
go.mod
View File

@ -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
View File

@ -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=

237
io.go
View File

@ -2,49 +2,14 @@ package n9m
import ( import (
"bytes" "bytes"
"encoding/binary"
"encoding/json" "encoding/json"
"fmt" "fmt"
"log"
"github.com/icza/bitio" "github.com/icza/bitio"
"github.com/tidwall/gjson" "log"
"time"
) )
// 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 // Read package
func (e *Package) ReadPackage() bool { func (e *Package) ReadPackage() bool {
if len(e.Accum) < 12 { if len(e.Accum) < 12 {
@ -52,124 +17,172 @@ func (e *Package) ReadPackage() bool {
} }
r := bitio.NewReader(bytes.NewBuffer(e.Accum)) r := bitio.NewReader(bytes.NewBuffer(e.Accum))
e.Version = r.TryReadBits(2) e.Version = uint8(r.TryReadBits(2))
e.Encription = r.TryReadBits(1) e.EncryptionFlag = r.TryReadBool()
e.Mark = r.TryReadBits(1) e.CompressFlag = r.TryReadBool()
e.CC = r.TryReadBits(4) e.CSRCCount = uint8(r.TryReadBits(4))
e.PayloadType = r.TryReadBits(8) e.PayloadType = PayloadType(r.TryReadBits(8))
e.SSRC = r.TryReadBits(16) e.SSRC = SpecialPayloadType((r.TryReadBits(8) | (r.TryReadBits(8) << 8)))
// log.Println(e.PayloadType) if e.EncryptionFlag && e.CompressFlag {
// TODO: get snippet, that use this code
is_special := e.Encription == 1 && e.Mark == 1
if is_special {
r.TryReadBits(8 * 4) r.TryReadBits(8 * 4)
e.PayloadLen = r.TryReadBits(8) e.payloadLen = r.TryReadBits(8)
r.TryReadBits(3 * 8) r.TryReadBits(3 * 8)
if uint64(len(e.Accum)) < e.PayloadLen+12 { if uint64(len(e.Accum)) < e.payloadLen+12 {
return false return false
} }
} else { } else {
e.PayloadLen = r.TryReadBits(32) e.payloadLen = r.TryReadBits(32)
// WTF: e.CC is useless // WTF: e.CC is useless
for i := uint64(0); i < 1; i++ { for i := uint64(0); i < 1; i++ {
e.CSRC[i] = r.TryReadBits(32) e.CSRC[i] = r.TryReadBits(32)
} }
} }
numOfBytes := 0 if e.payloadLen > 1e6 {
rawbytes := []byte{}
if e.PayloadLen != 0 {
if e.PayloadLen > 1000000 {
log.Printf("%v\n", e) log.Printf("%v\n", e)
log.Panicln("CORRUPTED PACKAGE") log.Panicln("CORRUPTED PACKAGE")
} }
rawbytes = make([]byte, e.PayloadLen) numOfBytes := 0
numOfBytes, _ = r.Read(rawbytes)
if e.payloadLen != 0 {
e.RawPayload = make([]byte, e.payloadLen)
numOfBytes = r.TryRead(e.RawPayload)
} else {
e.RawPayload = []byte{}
} }
if numOfBytes != int(e.PayloadLen) { if numOfBytes != int(e.payloadLen) {
return false return false
} }
e.Raw = e.Accum[:12+e.PayloadLen] e.Accum = e.Accum[12+e.payloadLen:]
e.Accum = e.Accum[12+e.PayloadLen:]
e.RawPayload = rawbytes
var ok bool switch e.PayloadType {
e.GPayload = gjson.Parse(string(rawbytes)) case PayloadTypeData:
e.Payload, ok = e.GPayload.Value().(map[string]interface{}) if err := json.Unmarshal(e.RawPayload, &e.Payload); err != nil {
if !ok { log.Printf("Error parsing JSON payload: %v", err)
e.Payload = gjson.Parse("{}").Value().(map[string]interface{}) 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)
} }
e.SaveJsonFields() if r.TryError != nil {
log.Printf("TryError encountered: %v", r.TryError)
return false
}
return e.PayloadLen > 0 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 { func (e *Package) PackPackage() []byte {
e.SaveJsonFields() var err error
b := &bytes.Buffer{} if err = e.PackPayload(); err != nil {
w := bitio.NewWriter(b) log.Printf("Error while packing payload: %v", err)
return []byte{}
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)) return e.PackRawPackage()
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 { func (e *Package) PackRawPackage() []byte {
var err error
b := &bytes.Buffer{} b := &bytes.Buffer{}
w := bitio.NewWriter(b) w := bitio.NewWriter(b)
w.TryWriteBits(e.Version, 2) w.TryWriteBits(uint64(e.Version), 2)
w.TryWriteBits(e.Encription, 1)
w.TryWriteBits(e.Mark, 1) w.TryWriteBool(e.EncryptionFlag)
w.TryWriteBits(e.CC, 4) w.TryWriteBool(e.CompressFlag)
w.TryWriteBits(e.PayloadType, 8)
w.TryWriteBits(e.SSRC, 16) w.TryWriteBits(uint64(e.CSRCCount), 4)
e.PayloadLen = uint64(len(e.RawPayload) + 1) w.TryWriteBits(uint64(e.PayloadType), 8)
w.TryWriteBits(e.PayloadLen, 32) w.TryWriteBits(uint64(e.SSRC), 16)
w.TryWriteBits(e.payloadLen, 32)
// WTF: e.CC is useless // WTF: e.CC is useless
for i := uint64(0); i < 1; i++ { for i := uint64(0); i < 1; i++ {
w.TryWriteBits(e.CSRC[i], 32) w.TryWriteBits(e.CSRC[i], 32)
} }
if e.PayloadLen != 0 {
w.Write(e.RawPayload) if e.payloadLen != 0 {
w.Write([]byte{0}) w.TryWrite(e.RawPayload)
}
if err = w.Close(); err != nil {
log.Printf("Error while closing writer: %v", err)
return []byte{}
} }
w.Close()
return b.Bytes() 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 = struct{}{}
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 = struct{}{}
e.Payload.Response = response
}

View File

@ -1,10 +1,6 @@
package n9m package n9m
import ( /*
"fmt"
"os"
)
var ip string = os.Getenv("SERVER_IP") 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) { func (e *Package) MediaRequestDownloadVideo(token int, serial string, session string, camNo int, date string, begin_time string, end_time string, recordID string, serverId int) {
@ -108,3 +104,5 @@ func (e *Package) ControlRemotePlayback(token int, serial string, session string
"SESSION": session, "SESSION": session,
} }
} }
*/

View File

@ -1,5 +1,6 @@
package n9m package n9m
// 7.2
type RIP struct { type RIP struct {
DEVID string // ID номер устройства DEVID string // ID номер устройства
BN string // бортовой номер BN string // бортовой номер
@ -218,7 +219,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 {
@ -267,29 +269,67 @@ type SP struct {
MUPORT int // UDP-порт медиасервера MUPORT int // UDP-порт медиасервера
} }
type Setting struct { // 7.30
RIP RIP type PGDSM struct {
VS VS PGPS PGPS `json:"PGPS,omitempty"`
GSP GSP PDSM PDSM `json:"PDSM,omitempty"`
TIMEP TIMEP }
ETHERNET ETHERNET
KEYS KEYS // 7.30.1
WIFI WIFI // GPS (position) status parameter
M3G M3G type PGPS struct {
MCMS MCMS EN uint // Enable
ATP ATP MODE uint
SSP SSP SEP uint `json:"SEP,omitempty"`
SWUS SWUS TM uint `json:"TM,omitempty"`
UMP UMP NUM uint `json:"NUM,omitempty"`
SUBSTRNET SUBSTRNET }
DOSD DOSD
AR AR // 7.30.2
EOSD []EOSD type PDSM struct {
MAIN []VEC }
IOP []IOP
SAP SAP // 7.39.3.9
UAP UAP // Platform basic information
PVLAS PVLAS type PSI struct {
PMDAS PMDAS CG CG `json:"CG,omitempty"`
DSM DSM }
// 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 Setting struct {
KEYS KEYS `json:"KEYS,omitempty"`
RIP RIP `json:"RIP,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"`
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"`
} }

161
scheme.go
View File

@ -1,32 +1,155 @@
package n9m package n9m
import "github.com/tidwall/gjson" import (
"encoding/json"
"fmt"
"net"
"strconv"
"time"
)
type PayloadJson struct { type PayloadType uint8
Module string
Key string const (
Operation string PayloadTypeData PayloadType = 0
Parameters map[string]interface{} PayloadTypeLive PayloadType = 2
Response map[string]interface{} 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 { type Package struct {
Version uint64 Version uint8
Encription uint64 EncryptionFlag bool
Mark uint64 CompressFlag bool
CC uint64 CSRCCount uint8
PayloadType uint64
SSRC uint64 PayloadType PayloadType
SSRC SpecialPayloadType
Reserved uint64 Reserved uint64
CSRC [16]uint64 CSRC [16]uint64
PayloadLen uint64 GPS GPSData
GPayload gjson.Result
Payload map[string]interface{} payloadLen uint64
Payload Message
RawPayload []byte RawPayload []byte
Raw []byte
Accum []byte Accum []byte
}
Json PayloadJson
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
View 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(&params); err != nil {
return fmt.Errorf("invalid payload")
}
var processFunc AlarmProcessFunc
var ok bool
if processFunc, ok = pack.alarmProcess[params.AlarmType]; !ok {
return &notFoundError{
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 &notFoundError{
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 &notFoundError{
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)
}

View File

@ -1,5 +1,7 @@
package n9m package n9m
/*
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{
"MODULE": "STORM", "MODULE": "STORM",
@ -78,3 +80,5 @@ func (e *Package) RequestFileList(queryTime string, serial int, session string,
"SESSION": session, "SESSION": session,
} }
} }
*/

28
test/certificate_test.go Normal file
View File

@ -0,0 +1,28 @@
package test
import (
"fmt"
"gitea.unprism.ru/KRBL/n9m/v2"
"testing"
)
func TestCertificateConnection(t *testing.T) {
dump := []byte{0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xe9, 0x52, 0x0, 0x0, 0x0, 0x7b, 0x22, 0x4d, 0x4f, 0x44, 0x55, 0x4c, 0x45, 0x22, 0x3a, 0x22, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x22, 0x2c, 0x22, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x22, 0x3a, 0x22, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x22, 0x2c, 0x22, 0x50, 0x41, 0x52, 0x41, 0x4d, 0x45, 0x54, 0x45, 0x52, 0x22, 0x3a, 0x7b, 0x22, 0x41, 0x55, 0x54, 0x4f, 0x43, 0x41, 0x52, 0x22, 0x3a, 0x22, 0x22, 0x2c, 0x22, 0x41, 0x55, 0x54, 0x4f, 0x4e, 0x4f, 0x22, 0x3a, 0x22, 0x30, 0x22, 0x2c, 0x22, 0x43, 0x41, 0x52, 0x4e, 0x55, 0x4d, 0x22, 0x3a, 0x22, 0x22, 0x2c, 0x22, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x22, 0x3a, 0x31, 0x32, 0x2c, 0x22, 0x43, 0x49, 0x44, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x43, 0x4e, 0x41, 0x4d, 0x45, 0x22, 0x3a, 0x22, 0x43, 0x55, 0x53, 0x54, 0x4f, 0x4d, 0x45, 0x52, 0x5f, 0x4e, 0x55, 0x4c, 0x4c, 0x22, 0x2c, 0x22, 0x44, 0x45, 0x56, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x22, 0x3a, 0x34, 0x2c, 0x22, 0x44, 0x45, 0x56, 0x4e, 0x41, 0x4d, 0x45, 0x22, 0x3a, 0x22, 0x4d, 0x44, 0x56, 0x52, 0x22, 0x2c, 0x22, 0x44, 0x45, 0x56, 0x54, 0x59, 0x50, 0x45, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x44, 0x4c, 0x49, 0x50, 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x4c, 0x49, 0x50, 0x22, 0x3a, 0x22, 0x30, 0x2e, 0x30, 0x2e, 0x30, 0x2e, 0x30, 0x22, 0x2c, 0x22, 0x4d, 0x54, 0x22, 0x3a, 0x22, 0x65, 0x74, 0x68, 0x30, 0x22, 0x7d, 0x5d, 0x2c, 0x22, 0x44, 0x4c, 0x50, 0x22, 0x3a, 0x5b, 0x38, 0x30, 0x2c, 0x39, 0x30, 0x30, 0x36, 0x5d, 0x2c, 0x22, 0x44, 0x53, 0x4e, 0x4f, 0x22, 0x3a, 0x22, 0x30, 0x30, 0x38, 0x38, 0x30, 0x33, 0x36, 0x31, 0x42, 0x38, 0x22, 0x2c, 0x22, 0x45, 0x49, 0x44, 0x22, 0x3a, 0x22, 0x6e, 0x6f, 0x74, 0x65, 0x62, 0x6f, 0x6f, 0x6b, 0x22, 0x2c, 0x22, 0x45, 0x56, 0x22, 0x3a, 0x22, 0x56, 0x31, 0x2e, 0x31, 0x22, 0x2c, 0x22, 0x46, 0x53, 0x56, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x4c, 0x49, 0x4e, 0x45, 0x4e, 0x4f, 0x22, 0x3a, 0x22, 0x22, 0x2c, 0x22, 0x4d, 0x41, 0x43, 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x49, 0x4d, 0x41, 0x43, 0x22, 0x3a, 0x22, 0x30, 0x30, 0x3a, 0x30, 0x30, 0x3a, 0x30, 0x30, 0x3a, 0x30, 0x30, 0x3a, 0x30, 0x30, 0x3a, 0x30, 0x30, 0x22, 0x2c, 0x22, 0x4d, 0x54, 0x22, 0x3a, 0x22, 0x65, 0x74, 0x68, 0x30, 0x22, 0x7d, 0x5d, 0x2c, 0x22, 0x4d, 0x4f, 0x44, 0x45, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x4d, 0x54, 0x59, 0x50, 0x45, 0x22, 0x3a, 0x33, 0x31, 0x2c, 0x22, 0x4e, 0x45, 0x54, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x50, 0x52, 0x4f, 0x22, 0x3a, 0x22, 0x31, 0x2e, 0x30, 0x2e, 0x35, 0x22, 0x2c, 0x22, 0x53, 0x54, 0x59, 0x50, 0x45, 0x22, 0x3a, 0x35, 0x34, 0x2c, 0x22, 0x54, 0x53, 0x45, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x55, 0x4e, 0x41, 0x4d, 0x45, 0x22, 0x3a, 0x22, 0x22, 0x2c, 0x22, 0x55, 0x4e, 0x4f, 0x22, 0x3a, 0x22, 0x22, 0x7d, 0x2c, 0x22, 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x22, 0x3a, 0x22, 0x36, 0x63, 0x65, 0x64, 0x35, 0x63, 0x66, 0x37, 0x2d, 0x61, 0x34, 0x35, 0x63, 0x2d, 0x34, 0x61, 0x63, 0x61, 0x2d, 0x62, 0x39, 0x64, 0x35, 0x2d, 0x66, 0x65, 0x39, 0x34, 0x61, 0x62, 0x36, 0x32, 0x66, 0x39, 0x65, 0x61, 0x22, 0x7d, 0xa}
pack := n9m.Package{}
pack.AddToAccum(dump)
if !pack.ReadPackage() {
t.Error("Package wasn't read!")
return
}
var connectionParams n9m.CertificateConnectRequest
if err := pack.GetParametersAs(&connectionParams); err != nil {
t.Error(err)
return
}
fmt.Println(connectionParams)
}