Beispiel #1
0
def test_nested_remove_nones():
    expected_payload = {
        'configuration_slot': 1,
        'connection_data': {
            'ocpp_version': 'OCPP20',
            'ocpp_transport': 'JSON',
            'ocpp_csms_url': 'wss://localhost:9000',
            'message_timeout': 60,
            'security_profile': 1,
            'ocpp_interface': 'Wired0'
        }
    }

    connection_data = NetworkConnectionProfileType(
        ocpp_version=OCPPVersionType.ocpp20,
        ocpp_transport=OCPPTransportType.json,
        ocpp_csms_url='wss://localhost:9000',
        message_timeout=60,
        security_profile=1,
        ocpp_interface=OCPPInterfaceType.wired0,
        vpn=None,
        apn=None)

    payload = SetNetworkProfilePayload(configuration_slot=1,
                                       connection_data=connection_data)
    payload = asdict(payload)

    assert expected_payload == remove_nones(payload)
Beispiel #2
0
    async def call(self, *, message: Any, context: RouterContext):
        ocpp_version = subprotocol_to_ocpp_version(self.subprotocol)

        camel_case_payload = snake_to_camel_case(asdict(message))

        call = Call(
            unique_id=str(self._unique_id_generator()),
            action=message.__class__.__name__[:-7],
            payload=remove_nones(camel_case_payload),
        )

        validate_payload(call, ocpp_version)

        await self._send(message=call.to_json(),
                         is_response=False,
                         context=context)
        self.subscriptions[call.unique_id] = context.queue
        try:
            response = await asyncio.wait_for(context.queue.get(),
                                              self._response_timeout)
        except asyncio.TimeoutError:
            del self.subscriptions[call.unique_id]
            raise

        if response.message_type_id == MessageType.CallError:
            log.warning("Received a CALLError: %s'", response)
            raise response.to_exception()
        else:
            response.action = call.action
            validate_payload(response, ocpp_version)

        snake_case_payload = camel_to_snake_case(response.payload)
        call_result = context.ocpp_adapter.call_result
        cls = getattr(call_result, message.__class__.__name__)
        return cls(**snake_case_payload)
Beispiel #3
0
def payload_to_message(payload: Any) -> str:
    camel_case_payload = snake_to_camel_case(asdict(payload))
    call = Call(
        unique_id=str(uuid4()),
        action=payload.__class__.__name__[:-7],
        payload=remove_nones(camel_case_payload),
    )
    msg = call.to_json()
    return msg
Beispiel #4
0
def payload_to_ocppj_message(payload, *, class_type, unique_id: str) -> str:
    # Helper to convert ocpp payload class to ocpp-j message string
    camel_case_payload = snake_to_camel_case(asdict(payload))
    call = class_type(
        unique_id=unique_id,
        action=payload.__class__.__name__[:-7],
        payload=remove_nones(camel_case_payload),
    )
    message = call.to_json()
    return message
Beispiel #5
0
def test_remove_nones():
    expected_payload = {
        'charge_point_model': 'foo',
        'charge_point_vendor': 'bar'
    }

    payload = BootNotificationPayload(charge_point_model='foo',
                                      charge_point_vendor='bar',
                                      charge_box_serial_number=None)
    payload = asdict(payload)

    assert expected_payload == remove_nones(payload)
Beispiel #6
0
def test_remove_nones_with_list_of_strings():
    """ Make sure that `remove_nones()` doesn't crash when it encounters an
    iterable other than a list or dict. See
    https://github.com/mobilityhouse/ocpp/issues/289.
    """
    payload = asdict(
        GetConfigurationPayload(
            key=["ClockAlignedDataInterval", "ConnectionTimeOut"]))

    assert remove_nones(payload) == {
        'key': ['ClockAlignedDataInterval', 'ConnectionTimeOut']
    }
Beispiel #7
0
def test_nested_list_remove_nones():
    expected_payload = {
        'connector_id':
        3,
        'meter_value': [{
            'timestamp':
            '2017-08-17T07:08:06.186748+00:00',
            'sampled_value': [{
                'value': '10',
                'context': 'Sample.Periodic',
                'measurand': 'Power.Active.Import',
                'unit': 'W'
            }, {
                'value': '50000',
                'context': 'Sample.Periodic',
                'measurand': 'Power.Active.Import',
                'phase': 'L1',
                'unit': 'W'
            }]
        }, {
            'timestamp':
            '2017-08-17T07:07:07.186748+00:00',
            'sampled_value': [{
                'value': '10',
                'context': 'Sample.Periodic',
                'measurand': 'Power.Active.Import',
                'unit': 'W'
            }, {
                'value': '50000',
                'context': 'Sample.Periodic',
                'measurand': 'Power.Active.Import',
                'phase': 'L1',
                'unit': 'W'
            }]
        }],
        'transaction_id':
        5
    }

    payload = MeterValuesPayload(
        connector_id=3,
        meter_value=[
            MeterValue(timestamp='2017-08-17T07:08:06.186748+00:00',
                       sampled_value=[
                           SampledValue(value='10',
                                        context='Sample.Periodic',
                                        format=None,
                                        measurand='Power.Active.Import',
                                        phase=None,
                                        location=None,
                                        unit='W'),
                           SampledValue(value='50000',
                                        context='Sample.Periodic',
                                        format=None,
                                        measurand='Power.Active.Import',
                                        phase='L1',
                                        location=None,
                                        unit='W')
                       ]),
            MeterValue(timestamp='2017-08-17T07:07:07.186748+00:00',
                       sampled_value=[
                           SampledValue(value='10',
                                        context='Sample.Periodic',
                                        format=None,
                                        measurand='Power.Active.Import',
                                        phase=None,
                                        location=None,
                                        unit='W'),
                           SampledValue(value='50000',
                                        context='Sample.Periodic',
                                        format=None,
                                        measurand='Power.Active.Import',
                                        phase='L1',
                                        location=None,
                                        unit='W')
                       ])
        ],
        transaction_id=5)

    payload = asdict(payload)
    assert expected_payload == remove_nones(payload)
Beispiel #8
0
    async def _handle_call(self, msg):
        """
        Execute all hooks installed for based on the Action of the message.

        First the '_on_action' hook is executed and its response is returned to
        the client. If there is no '_on_action' hook for Action in the message
        a CallError with a NotImplemtendError is returned.

        Next the '_after_action' hook is executed.

        """
        try:
            handlers = self.route_map[msg.action]
        except KeyError:
            raise NotImplementedError(f"No handler for '{msg.action}' "
                                      "registered.")

        if not handlers.get("_skip_schema_validation", False):
            validate_payload(msg, self._ocpp_version)

        # OCPP uses camelCase for the keys in the payload. It's more pythonic
        # to use snake_case for keyword arguments. Therefore the keys must be
        # 'translated'. Some examples:
        #
        # * chargePointVendor becomes charge_point_vendor
        # * firmwareVersion becomes firmwareVersion
        snake_case_payload = camel_to_snake_case(msg.payload)

        try:
            handler = handlers["_on_action"]
        except KeyError:
            raise NotImplementedError(f"No handler for '{msg.action}' "
                                      "registered.")

        try:
            response = handler(**snake_case_payload)
            if inspect.isawaitable(response):
                response = await response
        except Exception as e:
            LOGGER.exception("Error while handling request '%s'", msg)
            response = msg.create_call_error(e).to_json()
            await self._send(response)

            return

        temp_response_payload = asdict(response)

        # Remove nones ensures that we strip out optional arguments
        # which were not set and have a default value of None
        response_payload = remove_nones(temp_response_payload)

        # The response payload must be 'translated' from snake_case to
        # camelCase. So:
        #
        # * charge_point_vendor becomes chargePointVendor
        # * firmware_version becomes firmwareVersion
        camel_case_payload = snake_to_camel_case(response_payload)

        response = msg.create_call_result(camel_case_payload)

        if not handlers.get("_skip_schema_validation", False):
            validate_payload(response, self._ocpp_version)

        response = await self._response_strategy.run(response.to_json())

        try:
            handler = handlers["_after_action"]
            await self._after_hook_strategy.run(handler, snake_case_payload)
        except KeyError:
            # '_on_after' hooks are not required. Therefore ignore exception
            # when no '_on_after' hook is installed.
            pass
        return response
Beispiel #9
0
    async def _handle_call(self, msg, *, context: RouterContext = None):
        """
        Execute all hooks installed for based on the Action of the message.

        First the '_on_action' hook is executed and its response is returned to
        the client. If there is no '_on_action' hook for Action in the message
        a CallError with a NotImplemtendError is returned.

        Next the '_after_action' hook is executed.

        """
        ocpp_version = subprotocol_to_ocpp_version(self.subprotocol)

        try:
            handlers = self._route_map[msg.action]
        except KeyError:
            raise NotImplementedError(f"No handler for '{msg.action}' "
                                      "registered.")

        if not handlers.get("_skip_schema_validation", False):
            validate_payload(msg, ocpp_version)

        # OCPP uses camelCase for the keys in the payload. It's more pythonic
        # to use snake_case for keyword arguments. Therefore the keys must be
        # 'translated'. Some examples:
        #
        # * chargePointVendor becomes charge_point_vendor
        # * firmwareVersion becomes firmwareVersion
        snake_case_payload = camel_to_snake_case(msg.payload)

        try:
            handler = handlers["_on_action"]
        except KeyError:
            raise NotImplementedError(f"No handler for '{msg.action}' "
                                      "registered.")

        handler_context = HandlerContext(
            charging_station_id=context.charging_station_id,
            _router_context=context,
            _router=self,
        )
        # Convert message to correct Call instance
        class_ = getattr(context.ocpp_adapter.call, f"{msg.action}Payload")
        payload = class_(**snake_case_payload)
        try:
            response = handler(payload=payload, context=handler_context)
            if inspect.isawaitable(response):
                response = await response
        except Exception as e:
            log.exception("Error while handling request '%s'", msg)
            response = msg.create_call_error(e).to_json()
            await self._send(message=response,
                             is_response=True,
                             context=context)

        temp_response_payload = asdict(response)

        # Remove nones ensures that we strip out optional arguments
        # which were not set and have a default value of None
        response_payload = remove_nones(temp_response_payload)

        # The response payload must be 'translated' from snake_case to
        # camelCase. So:
        #
        # * charge_point_vendor becomes chargePointVendor
        # * firmware_version becomes firmwareVersion
        camel_case_payload = snake_to_camel_case(response_payload)

        response = msg.create_call_result(camel_case_payload)

        if not handlers.get("_skip_schema_validation", False):
            validate_payload(response, ocpp_version)

        await self._send(message=response.to_json(),
                         is_response=True,
                         context=context)

        try:
            handler = handlers["_after_action"]
            response = handler(payload=payload, context=handler_context)
            if inspect.isawaitable(response):
                if self._create_task:
                    # Create task to avoid blocking when making a call
                    # inside the after handler
                    asyncio.ensure_future(response)
                else:
                    await response
        except KeyError:
            # '_on_after' hooks are not required. Therefore ignore exception
            # when no '_on_after' hook is installed.
            pass