faithoflifedev / easy_onvif_workspace

This package works with a variety of ONVIF compatible devices allowing for IP Cameras and NVRs (network video recorders) to be integrated into Dart and Flutter applications.
32 stars 19 forks source link

Unhandled Exception: type 'Null' is not a subtype of type 'Map<String, dynamic>' in type cast #4

Closed wmhseir3 closed 3 years ago

wmhseir3 commented 3 years ago

The 0.0.9 has further exception @var profs = await onvif.media.getProfiles(); line with the following logcats: E/flutter (11010): [ERROR:flutter/lib/ui/ui_dartstate.cc(209)] Unhandled Exception: type 'Null' is not a subtype of type 'Map<String, dynamic>' in type cast E/flutter (11010): #0 $CodeFromJson (package:easy_onvif/model/code.g.dart:11:37) E/flutter (11010): #1 new Code.fromJson (package:easyonvif/model/code.dart:17:55) E/flutter (11010): #2 $CodeFromJson (package:easy_onvif/model/code.g.dart:11:12) E/flutter (11010): #3 new Code.fromJson (package:easyonvif/model/code.dart:17:55) E/flutter (11010): #4 $FaultFromJson (package:easy_onvif/model/fault.g.dart:10:12) E/flutter (11010): #5 new Fault.fromJson (package:easyonvif/model/fault.dart:32:56) E/flutter (11010): #6 $BodyFromJson (package:easy_onvif/model/body.g.dart:12:19) E/flutter (11010): #7 new Body.fromJson (package:easyonvif/model/body.dart:121:55) E/flutter (11010): #8 $EnvelopeFromJson (package:easy_onvif/model/envelope.g.dart:10:18) E/flutter (11010): #9 new Envelope.fromJson (package:easy_onvif/model/envelope.dart:19:7) E/flutter (11010): #10 Soap.send (package:easy_onvif/src/soap.dart:27:35) E/flutter (11010): E/flutter (11010): #11 Soap.retrieveEnvlope (package:easy_onvif/src/soap.dart:43:26) E/flutter (11010): E/flutter (11010): #12 Media.getProfiles (package:easy_onvif/src/media.dart:35:22)

and here is the soap response when pasing it: getProfiles.txt

hsalazarl commented 3 years ago

Getting the same but at the onvif.initialize() method call. Debugging results in this SOAP response:

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:ter="http://www.onvif.org/ver10/error" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:xs="http://www.w3.org/2000/10/XMLSchema" xmlns:wsnt="http://docs.oasis-open.org/wsn/b-2">
<soap:Body>
<soap:Fault>
<soap:Code>
<soap:Value>soap:Sender</soap:Value><soap:Subcode>
<soap:Value>wsse:InvalidSecurity</soap:Value>
</soap:Subcode>
</soap:Code>
<soap:Reason>
<soap:Text xml:lang="en">An error was discovered processing the wsse:Security header.</soap:Text>
</soap:Reason>
<soap:Node>http://www.w3.org/2003/05/soap-envelope/node/ultimateReceiver</soap:Node>
<soap:Role>http://www.w3.org/2003/05/soap-envelope/role/ultimateReceiver</soap:Role>
</soap:Fault>
</soap:Body>
</soap:Envelope>
hsalazarl commented 3 years ago

For the Media.getProfiles the error happens because some json key is not available in the response. In the OP's case, the json['Subcode'] key is not available causing that to be Null and trying to cast it to Map<String, dynamic> throws an exception. In my case, I solved the onvif.initialize() error by extending the Onvif class and overriding the initialize() method like this:

@override
  Future<void> initialize() async {
    try {
      await super.initialize();
    } catch (e) {
      await _initializeOnvif();
    }
  }

  Future<void> _initializeOnvif() async {
    // We could do something with the exception to try to determine if it was a "wsse:InvalidSecurity" error.
    timeDelta = DateTime.now().toLocal().difference(DateTime.now().toUtc());
    final envelope = await Soap.retrieveEnvlope(deviceManagement.uri, secureRequest(SoapRequest.capabilities('All')));
    if (envelope.body.capabilitiesResponse == null) throw Exception();

    var capabilities = envelope.body.capabilitiesResponse!.capabilities;

    _media = Media(onvif: this, uri: capabilities.media.xaddr);

    _ptz = Ptz(onvif: this, uri: capabilities.ptz.xaddr);
  }

But now, I am getting errors in the getProfiles() call because other json keys are not available, such as json['VideoAnalyticsConfiguration'] and json['PTZConfiguration'].

Probably the solution would be to null check for these json keys that are optional in the responses due to the device not having those capabilities or information.

Something like:

videoAnalyticsConfiguration: VideoAnalyticsConfiguration.fromJson(
          (json['VideoAnalyticsConfiguration'] ?? Map<String, dynamic>()) as Map<String, dynamic>)

But that code is autogenerated, so maybe doing it by hand or something...

faithoflifedev commented 3 years ago

@wmhseir3 , @hsalazarl , confirmed. I should have some time today to dig into this.

faithoflifedev commented 3 years ago

easy_onvif 0.0.10 has been published to pub.dev, it included the PR for #5 , let me know if it resolves the issue so I can close this.

hsalazarl commented 3 years ago

@faithoflifedev thank you for addressing these issues. It does fix the main issue with Code, Profile. But there is still problems with the I8nText classs, specifically the two properties are optional, in my case I encountered problems with both:

@JsonKey(name: 'lang')
  final String lang;

  @JsonKey(name: '\$')
  final String text;

In my tests, both could be null sometimes.

Besides that, not sure if you checked the issue with onvif.initialize() with the GetSystemDateAndTime failing to return a valid response complaining about the wsse:Security header. I made a workaround so that I calculate the timeDelta using the local time like this:

timeDelta = DateTime.now().toLocal().difference(DateTime.now().toUtc());

To be able to wrap next capabilities request in a secureRequest() call.

But I guess this is a topic for another issue. Main issue described here is fixed, but there are still some json properties that are optional. That's all. Thank you again.

wmhseir3 commented 3 years ago

The 0.0.10 build has still the same issue as following logs when making the "var profs = await onvif.media.getProfiles();", the DioError captured as followings: E/flutter (10996): [ERROR:flutter/lib/ui/ui_dartstate.cc(209)] Unhandled Exception: type 'Null' is not a subtype of type 'Map<String, dynamic>' in type cast E/flutter (10996): #0 $FaultFromJson (package:easy_onvif/model/fault.g.dart:11:38) E/flutter (10996): #1 new Fault.fromJson (package:easyonvif/model/fault.dart:32:56) E/flutter (10996): #2 $BodyFromJson (package:easy_onvif/model/body.g.dart:12:19) E/flutter (10996): #3 new Body.fromJson (package:easyonvif/model/body.dart:121:55) E/flutter (10996): #4 $EnvelopeFromJson (package:easy_onvif/model/envelope.g.dart:10:18) E/flutter (10996): #5 new Envelope.fromJson (package:easy_onvif/model/envelope.dart:19:7) E/flutter (10996): #6 Soap.send (package:easy_onvif/src/soap.dart:27:35) E/flutter (10996): E/flutter (10996): #7 Soap.retrieveEnvlope (package:easy_onvif/src/soap.dart:43:26) E/flutter (10996): E/flutter (10996): #8 Media.getProfiles (package:easy_onvif/src/media.dart:35:22)

Thanks for your prompt solution next.

faithoflifedev commented 3 years ago

@wmhseir3, it looks like in this case I did implement the code such that it matches the documented spec. The SOAP 1.2 Fault Spec which is referred to by the Onvif Core Spec (section 5.11.2 SOAP errors) says that the "Reason" field (the field that throws the exception) is required. But it appears that some devices are not supplying the field.

That said, I don't think my code changes to resolve this particular error will solve your problem since the result of the change would be to properly report the fault returned in the SOAP response. In this case the fault code is SOAP-ENV:Receiver with the sub-code is showing ter:ActionNotSupported

So it looks like there is some other issue, maybe related to the SOAP request itself.

faithoflifedev commented 3 years ago

@hsalazarl, in my latest code changes (not committed yet)it makes the Detail field of the SOAP fault response optional. I'm hoping that will at least partially resolve the issue that you mention.

faithoflifedev commented 3 years ago

@hsalazarl , I'm closing this issue, v0.0.12 resolves it. The problem was that the documented spec for a "fault" (ate least in my interpretation) does not match what is being returned by some devices. The new code does not make any assumptions about the response and will give a descriptive error when throwing an exception.

Additionally, as part of troubleshooting this I did find that there was a namespace issue with the Onvif SOAP envelope being created by the code. I've corrected this as well.