mission_script_runtime_device.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. #include <array>
  2. #include <cstdio>
  3. #include <span>
  4. #include "mission_script_runtime_internal.h"
  5. namespace sunrise::server::activity::mission {
  6. namespace {
  7. /**
  8. * Retains a device under its complete authored slot identity.
  9. * @param instance Bound mission instance.
  10. * @param key Exact accepted Sense source.
  11. * @return The matching or newly allocated row, or null at capacity.
  12. */
  13. [[nodiscard]] DeviceObservation*
  14. find_device_observation(RuntimeInstance& instance, const host::SenseObservationKey& key) noexcept {
  15. DeviceObservation* spare = nullptr;
  16. for (auto& row : instance.deviceObservations) {
  17. if (row.used && row.registryKey == key.registryKey && row.objectTag == key.objectTag
  18. && row.slotIndex == key.slotIndex) {
  19. return &row;
  20. }
  21. if (!row.used && spare == nullptr) {
  22. spare = &row;
  23. }
  24. }
  25. if (spare != nullptr) {
  26. spare->registryKey = key.registryKey;
  27. spare->objectTag = key.objectTag;
  28. spare->slotIndex = key.slotIndex;
  29. }
  30. return spare;
  31. }
  32. } // namespace
  33. /**
  34. * Publishes current device levels only when accepted client reports change them.
  35. * @param instance Bound mission instance retaining previous device levels.
  36. * @param sense Accepted observations for its current client generation.
  37. */
  38. void push_device_edges(RuntimeInstance& instance,
  39. const host::SenseObservationSnapshot& sense) noexcept {
  40. if (sense.sourceGeneration != instance.view.activityClientGeneration
  41. || sense.observationCount > sense.observations.size()
  42. || sense.valueCount > sense.values.size()) {
  43. return;
  44. }
  45. for (std::size_t index = 0; index < sense.observationCount; ++index) {
  46. const auto& observation = sense.observations[index];
  47. if (observation.sourceGeneration != sense.sourceGeneration
  48. || observation.key.slotType != kDeviceSenseSlotType
  49. || observation.key.senseSchema != kDeviceSenseSchema
  50. || observation.firstValue > sense.valueCount
  51. || observation.valueCount > sense.valueCount - observation.firstValue) {
  52. continue;
  53. }
  54. auto* const row = find_device_observation(instance, observation.key);
  55. if (row == nullptr) {
  56. log_line(core::log::Level::warn, &instance, "device_state", "watch_capacity");
  57. continue;
  58. }
  59. const bool first = !row->level.observed;
  60. bool reset = false;
  61. const auto body =
  62. std::span(sense.values).subspan(observation.firstValue, observation.valueCount);
  63. if (!update_device_level(row->level, body, observation.key.schemaRow, reset)) {
  64. continue;
  65. }
  66. row->used = true;
  67. host::Event event{};
  68. event.kind = host::EventKind::deviceState;
  69. event.binding = instance.view.binding;
  70. event.sequence = observation.sequence;
  71. event.tick = observation.tick;
  72. event.sourceGeneration = instance.view.activityClientGeneration;
  73. event.missionSequence = instance.lastMissionSequence;
  74. event.firstRegistryKey = observation.key.registryKey;
  75. event.firstSlotIndex = observation.key.slotIndex;
  76. event.firstSlotType = observation.key.slotType;
  77. event.slotObjectTag = observation.key.objectTag;
  78. event.slotSenseSchema = observation.key.senseSchema;
  79. event.deviceFirstReport = first;
  80. event.deviceReset = reset;
  81. for (std::size_t channel = 0; channel < row->level.channels.size(); ++channel) {
  82. const auto& level = row->level.channels[channel];
  83. event.deviceValues[channel] = level.value;
  84. event.deviceSequences[channel] = level.sequence;
  85. if (level.valueKnown) {
  86. event.deviceValueMask |= static_cast<std::uint8_t>(1U << channel);
  87. }
  88. if (level.sequenceKnown) {
  89. event.deviceSequenceMask |= static_cast<std::uint8_t>(1U << channel);
  90. }
  91. }
  92. std::array<char, 128> details{};
  93. const int written = std::snprintf(details.data(),
  94. details.size(),
  95. "registry=%08X slot=%u position=%.4f sequence=%d "
  96. "known=%u reset=%u",
  97. observation.key.registryKey,
  98. static_cast<unsigned>(observation.key.slotIndex),
  99. static_cast<double>(event.deviceValues[0]),
  100. event.deviceSequences[0],
  101. static_cast<unsigned>(event.deviceValueMask),
  102. reset ? 1U : 0U);
  103. if (written > 0 && static_cast<std::size_t>(written) < details.size()) {
  104. log_line(core::log::Level::debug,
  105. &instance,
  106. "device_state",
  107. "changed",
  108. {details.data(), static_cast<std::size_t>(written)});
  109. }
  110. push_script_event(instance, event);
  111. }
  112. }
  113. } // namespace sunrise::server::activity::mission