preflight.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. # SPDX-License-Identifier: Apache-2.0
  2. # Copyright 2020 Contributors to OpenLEADR
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. # http://www.apache.org/licenses/LICENSE-2.0
  7. # Unless required by applicable law or agreed to in writing, software
  8. # distributed under the License is distributed on an "AS IS" BASIS,
  9. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. # See the License for the specific language governing permissions and
  11. # limitations under the License.
  12. from datetime import datetime, timedelta, timezone
  13. import warnings
  14. def preflight_message(message_type, message_payload):
  15. """
  16. Tests message contents before sending them. It will correct benign errors
  17. and warn you about them. Uncorrectable errors will raise an Exception. It
  18. changes the message_payload dict in-place.
  19. :param message_type string: The type of message you are sending
  20. :param message_payload dict: The contents of the message
  21. """
  22. if f'_preflight_{message_type}' in globals():
  23. globals()[f'_preflight_{message_type}'](message_payload)
  24. return message_type, message_payload
  25. def _preflight_oadrDistributeEvent(message_payload):
  26. if 'parse_duration' not in globals():
  27. from .utils import parse_duration
  28. # Check that the total event_duration matches the sum of the interval durations (rule 8)
  29. for event in message_payload['events']:
  30. active_period_duration = event['active_period']['duration']
  31. signal_durations = []
  32. for signal in event['event_signals']:
  33. signal_durations.append(sum([parse_duration(i['duration']) for i in signal['intervals']], timedelta(seconds=0)))
  34. if not all([d==active_period_duration for d in signal_durations]):
  35. if not all([d==signal_durations[0] for d in signal_durations]):
  36. raise ValueError("The different EventSignals have different total durations. Please correct this.")
  37. else:
  38. warnings.warn(f"The active_period duration for event {event['event_descriptor']['event_id']} ({active_period_duration})"
  39. f" was different from the sum of the interval's durations ({signal_durations[0]})."
  40. f" The active_period duration has been adjusted to ({signal_durations[0]}).")
  41. event['active_period']['duration'] = signal_durations[0]
  42. # Check that payload values with signal name SIMPLE are constricted (rule 9)
  43. for event in message_payload['events']:
  44. for event_signal in event['event_signals']:
  45. if event_signal['signal_name'] == "SIMPLE":
  46. for interval in event_signal['intervals']:
  47. if interval['signal_payload'] not in (0, 1, 2, 3):
  48. raise ValueError("Payload Values used with Signal Name SIMPLE must be one of"
  49. "0, 1, 2 or 3")
  50. # Check that the current_value is 0 for SIMPLE events that are not yet active (rule 14)
  51. for event in message_payload['events']:
  52. for event_signal in event['event_signals']:
  53. if 'current_value' in event_signal and event_signal['current_value'] != 0:
  54. if event_signal['signal_name'] == "SIMPLE" and event['event_descriptor']['event_status'] != "ACTIVE":
  55. warnings.warn("The current_value for a SIMPLE event that is not yet active must be 0. This will be corrected.")
  56. event_signal['current_value'] = 0
  57. # Check that there is a valid oadrResponseRequired value for each Event
  58. for event in message_payload['events']:
  59. if 'response_required' not in event:
  60. event['response_required'] = 'always'
  61. elif event['response_required'] not in ('never', 'always'):
  62. warnings.warn(f"The response_required property in an Event should be 'never' or 'always', not {event['response_required']}. Changing to 'always'.")
  63. event['response_required'] = 'always'
  64. # Check that there is a valid oadrResponseRequired value for each Event
  65. for event in message_payload['events']:
  66. if 'created_date_time' not in event['event_descriptor'] or not event['event_descriptor']['created_date_time']:
  67. print("ADDING CREATED DATE TIME")
  68. event['event_descriptor']['created_date_time'] = datetime.now(timezone.utc)