mirror of
https://github.com/kerberos-io/onvif.git
synced 2026-08-23 15:08:33 +00:00
Adds a Classify function that maps ONVIF topic strings to EventKind so the agent does not need to know AXIS vs Hikvision vs Bosch topic conventions. The classifier canonicalizes topics by stripping XML-namespace prefixes from each path segment, which collapses vendor variants like 'tns1:Device/tnssamsung:DigitalInput' and 'tns1:Device/Trigger/DigitalInput' to a single matchable form. Motion coverage on day one: * tns1:VideoSource/MotionAlarm (AXIS, Bosch, Dahua, ...) * tns1:VideoAnalytics/<vendor>:MotionAlarm * tns1:RuleEngine/CellMotionDetector/Motion (ONVIF standard, Hikvision) * tns1:RuleEngine/MotionRegionDetector/Motion (AXIS region rule) * tnsaxis:CameraApplicationPlatform/ObjectAnalytics/... Also covers Tamper, DigitalInput, Relay (DigitalOutput), object analytics and audio alarms, so the same Stream can replace the agent's ad-hoc digital I/O polling without losing coverage.
54 lines
2.4 KiB
Go
54 lines
2.4 KiB
Go
package stream
|
|
|
|
import "testing"
|
|
|
|
func TestClassifyTopic(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
topic string
|
|
want EventKind
|
|
}{
|
|
// AXIS: motion alarm on video source.
|
|
{"axis_video_source_motion", "tns1:VideoSource/MotionAlarm", KindMotion},
|
|
// AXIS / Hikvision / others: ONVIF cell-motion detector rule.
|
|
{"cell_motion_detector", "tns1:RuleEngine/CellMotionDetector/Motion", KindMotion},
|
|
// AXIS region motion.
|
|
{"motion_region_detector", "tns1:RuleEngine/MotionRegionDetector/Motion", KindMotion},
|
|
// Vendor-prefixed motion (e.g. Bosch).
|
|
{"bosch_motion", "tns1:VideoAnalytics/tnsbosch:MotionAlarm", KindMotion},
|
|
// Tampering / scene change.
|
|
{"tamper_detector", "tns1:RuleEngine/TamperDetector/Tamper", KindTampering},
|
|
{"axis_scene_tamper", "tns1:VideoSource/ImageTooDark/ImagingService", KindUnknown}, // not classified
|
|
// Digital input — vendor-namespaced variant from agent code.
|
|
{"digital_input", "tns1:Device/Trigger/DigitalInput", KindDigitalInput},
|
|
{"digital_input_samsung", "tns1:Device/tns1:Trigger/tnssamsung:DigitalInput", KindDigitalInput},
|
|
// Digital output / relay.
|
|
{"relay", "tns1:Device/Trigger/Relay", KindDigitalOutput},
|
|
{"relay_avigilon", "tns1:Device/tns1:Trigger/tns1:Relay", KindDigitalOutput},
|
|
// Analytics object detection (AXIS object analytics, generic motion analytics).
|
|
{"object_detected", "tns1:RuleEngine/MyRuleDetector/ObjectsInside", KindObjectDetected},
|
|
{"axis_object_analytics", "tnsaxis:CameraApplicationPlatform/ObjectAnalytics/Device1ScenarioANY", KindObjectDetected},
|
|
// Audio.
|
|
{"audio_alarm", "tns1:AudioAnalytics/Audio/DetectedSound", KindAudioAlarm},
|
|
{"axis_audio", "tnsaxis:AudioSource/TriggerLevel", KindAudioAlarm},
|
|
// Unknown — should not be force-classified.
|
|
{"empty", "", KindUnknown},
|
|
{"unknown_topic", "tns1:UserAlarm/IVA", KindUnknown},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if got := Classify(tc.topic); got != tc.want {
|
|
t.Errorf("Classify(%q) = %v, want %v", tc.topic, got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestClassifyIsCaseSensitive(t *testing.T) {
|
|
// ONVIF topic names are case-sensitive per spec; we should not silently
|
|
// upper/lower-case. A lowercased topic must not match.
|
|
if got := Classify("tns1:videosource/motionalarm"); got != KindUnknown {
|
|
t.Errorf("Classify lowercase = %v, want KindUnknown (topics are case-sensitive)", got)
|
|
}
|
|
}
|