Onvif - 解析来自 WS-BaseNotification 的事件通知

问题描述 投票:0回答:1

我目前正在为 Onvif 实现一个事件处理程序。但我对 WS-BaseNotification 的解组过程完全迷失了。

  1. 我如何理解/解析NotificationMessageHolderType到onvif事件实现?

  2. 我想 ws-basenotification 有一种方法可以将“通用”消息映射到自定义实现。要使用的模式的链接/信息在哪里?

我的测试代码:端点订阅者

private W3CEndpointReference subscribe(final FilterType filterType) {
    try {
        CreatePullPointSubscription parameters = new CreatePullPointSubscription();
        parameters.setFilter(filterType);
        CreatePullPointSubscriptionResponse pullPointSubscription = onvifDevice.getEventPort().createPullPointSubscription(parameters);
        return pullPointSubscription.getSubscriptionReference();

    } catch (TopicNotSupportedFault | InvalidMessageContentExpressionFault | InvalidProducerPropertiesExpressionFault | InvalidTopicExpressionFault | TopicExpressionDialectUnknownFault | UnacceptableInitialTerminationTimeFault | NotifyMessageNotSupportedFault | ResourceUnknownFault | UnsupportedPolicyRequestFault | InvalidFilterFault | SubscribeCreationFailedFault | UnrecognizedPolicyRequestFault topicNotSupportedFault) {
        topicNotSupportedFault.printStackTrace();
        return null;
    }
}

我可以接收通知并将它们解组到 org.onvif.ver10.schema.Message.class :

收到通知

PullMessagesResponse pullMessagesResponse = pullPointSubscription.pullMessages(pullMessages);
List<NotificationMessageHolderType> notificationMessage = pullMessagesResponse.getNotificationMessage();

// Some test to have understandable data.
TopicExpressionType topic = notificationMessage.get(0).getTopic();
JAXBContext context = JAXBContext.newInstance(org.onvif.ver10.schema.Message.class, AccessPointState.class);
Unmarshaller um = context.createUnmarshaller();
Object response = (Object)um.unmarshal((Node)node);

示例

设备发送的PullMessagesResponse(wireshark捕获):

<tev:PullMessagesResponse>
    <tev:CurrentTime>2020-04-29T11:41:52Z</tev:CurrentTime>
    <tev:TerminationTime>2020-04-29T11:46:51Z</tev:TerminationTime>
    <wsnt:NotificationMessage>
        <wsnt:Topic Dialect="http://docs.oasis-open.org/wsn/t-1/TopicExpression/Simple">tns1:AccessPoint/State/Enabled</wsnt:Topic>
        <wsnt:ProducerReference>
            <wsa5:Address>uri://5581ad80-95b0-11e0-b883-accc8e251272/ProducerReference</wsa5:Address>
        </wsnt:ProducerReference>
        <wsnt:Message>
            <tt:Message UtcTime="2020-04-23T15:21:26.924984Z" PropertyOperation="Initialized">
                <tt:Source>
                    <tt:SimpleItem Name="AccessPointToken" Value="Axis-accc8e251272:1584710973.159018000"></tt:SimpleItem>
                    <tt:SimpleItem Name="Device Source" Value="5581ad80-95b0-11e0-b883-accc8e251272"></tt:SimpleItem>
                </tt:Source>
                <tt:Key></tt:Key>
                <tt:Data>
                    <tt:SimpleItem Name="State" Value="1"></tt:SimpleItem>
                </tt:Data>
            </tt:Message>
        </wsnt:Message>
    </wsnt:NotificationMessage>
</tev:PullMessagesResponse>

然后将解组结果发送到 onvif.message :

附件

Onvif 核心规范手册 说(第 106 页):

A notification answers the following questions: 
• When did it happen?
• Who produced the event? 
• What happened? 

 ... text ommited 

The “what” question is answered in two steps. First, the Topic element of the NotificationMessage is used to categorize the Event. Second, items are added to the Data element of the Message container in order to describe the details of the Event.

所以我想我之前的示例“tns1:AccessPoint/State/Enabled”的主题必须映射到一个类型?但我找不到这个结构的描述,其次我希望这个映射能够在 wsdl 文件生成过程中自动完成。

profile C - onvif 文档描述了我的设备的所有现有主题,但没有对此通知内容的描述...

已更新 显然,请求:GetEventPropertiesResponse 返回每个主题中存在的属性。所以我希望找到一种方法从这个描述中生成“对象”。

感谢您的帮助

java wsdl cxf unmarshalling onvif
1个回答
0
投票

我找到了一个可行的(如果不是优雅的)解决方案

请谨慎使用,这是 3 年前的 Onvif WSDL 文件的实现。

获取消息:

private List<NotificationMessageHolderType> fetchNotifications(final PullPointSubscription pullPointSubscription) {
    try {
        return pullPointSubscription.pullMessages(pullMessages).getNotificationMessage();
    } catch (PullMessagesFaultResponse_Exception e) {
        // TODO your error handling...
        return Lists.newArrayList();
    }
}

然后我使用这个EventParser:

import ch.swissdotnet.onvif.events.entities.OnvifEvent;
import org.oasis_open.docs.wsn.b_2.NotificationMessageHolderType;
import org.onvif.ver10.schema.Message;
import org.reflections.Reflections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Node;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Optional;
import java.util.Set;

public class EventParser {

    public static final Logger LOG = LoggerFactory.getLogger(EventParser.class);

    private final Unmarshaller um2;
    private final Set<Class<? extends OnvifEvent>> events;

    public EventParser(final String eventPackage) throws JAXBException {
        Reflections reflections = new Reflections(eventPackage);
        this.events = reflections.getSubTypesOf(OnvifEvent.class);
        this.um2 = JAXBContext.newInstance(Message.class).createUnmarshaller();
    }

    public Optional<? extends OnvifEvent> parse(final NotificationMessageHolderType notificationMessageHolderType) {
        try {
            String topic = ((String) notificationMessageHolderType.getTopic().getContent().get(0));
            Message message = unmarshallToMessage(notificationMessageHolderType);
            return getEvent(message, topic);

        } catch (Exception e) {
            LOG.error("Receive not understandable event case [{}] message [{}]", e.getCause(), e.getMessage(), e);
            return Optional.empty();
        }
    }

    private Optional<? extends OnvifEvent> getEvent(final Message message, final String topic) throws Exception {
        for (Class<? extends OnvifEvent> event : events) {

            EventTopic annotatedTopic = event.getDeclaredAnnotation(EventTopic.class);
            if (topic.equals(annotatedTopic.topic())) {
                try {
                    Constructor<? extends OnvifEvent> constructor = event.getConstructor(Message.class);
                    return Optional.of(constructor.newInstance(message));
                } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
                    throw new Exception("Invalid Onvif event", e);
                }
            }
        }

        return Optional.empty();
    }

    private Message unmarshallToMessage(NotificationMessageHolderType notification) throws JAXBException {
        Node node = (Node) notification.getMessage().getAny();
        return (org.onvif.ver10.schema.Message) um2.unmarshal(node);
    }
}

现在,丑陋的事情来了,用实例检查每种事件(这只是 DoorAlarm 的演示)

public void onEvent(final OnvifEvent event) {
    if (event instanceof DoorAlarm) {
        var alarmEvent = (DoorAlarm) event;
        List<DoorEventCallback> listeners = mapListenersByDoor(alarmEvent.getSourceDoorAlarm().getDoorToken());
        listeners.forEach(listener -> listener.onAlarm(alarmEvent));
        return;
    }

...

© www.soinside.com 2019 - 2024. All rights reserved.