Coverage Report

Created: 2021-08-28 18:14

D:\git\skunkworks\herald-for-cpp\herald\include\herald\engine\coordinator.h
Line
Count
Source (jump to first uncovered line)
1
//  Copyright 2021 Herald Project Contributors
2
//  SPDX-License-Identifier: Apache-2.0
3
//
4
5
#ifndef HERALD_COORDINATOR_H
6
#define HERALD_COORDINATOR_H
7
8
#include "../context.h"
9
#include "activities.h"
10
#include "../data/sensor_logger.h"
11
12
#include <map>
13
#include <vector>
14
#include <algorithm>
15
#include <iterator>
16
#include <optional>
17
18
namespace herald {
19
20
/// \brief Engine classes provide for task scheduling, including complex inter-dependent tasks.
21
namespace engine {
22
23
///
24
/// \brief Coordinates all connection and activities used across all sensors within Herald
25
/// 
26
/// Responsible for:-
27
///
28
/// - Determining Sensor capabilities and requirements around connections and Activity instances
29
/// 
30
/// Manages:- 
31
///
32
/// - Nothing, but coordinates activities throughout Herald Sensor networks on behalf of SensorArray
33
/// 
34
/// Is managed by:-
35
///
36
/// - SensorArray
37
///
38
template <typename ContextT>
39
class Coordinator {
40
public:
41
  /// Default constructor. Receives a configured platform-specific context instance.
42
  Coordinator(ContextT& ctx)
43
  : context(ctx),
44
    providers(),
45
    running(false)
46
    HLOGGERINIT(ctx,"engine","coordinator")
47
1
  {}
48
49
1
  ~Coordinator() = default;
50
51
  /// Introspect and include in iteration planning
52
  template <typename SensorT>
53
1
  void add(SensorT& sensor) {
54
1
    HTDBG("Adding sensor");
55
1
    auto prov = sensor.coordinationProvider();
56
1
    if (prov.has_value()) {
57
1
      HTDBG("Sensor has Provider implementation");
58
1
      providers.push_back(prov.value());
59
1
    }
60
1
  }
61
  /// Remove from iteration planning
62
  template <typename SensorT>
63
  void remove(SensorT& sensor)
64
  {
65
    // TODO support remove
66
  }
67
68
  /// Prepares for iterations to be called (may pre-emptively make calls)
69
1
  void start() {
70
1
    HTDBG("Start called");
71
1
    // Clear feature providers
72
1
    featureProviders.clear();
73
1
    // Fetch feature providers
74
1
    for (auto prov: providers) {
75
1
      auto myFeatures = prov.get().connectionsProvided();
76
1
      for (auto feature : myFeatures) {
77
1
        featureProviders.emplace(feature,prov);
78
1
      }
79
1
    }
80
1
    running = true;
81
1
    HTDBG("Start returning");
82
1
  }
83
84
  /// Execute an iteration of activity, according to settings
85
4
  void iteration() {
86
4
    if (!running) {
87
0
      HTDBG("Coordinator not running. Returning from iteration having done nothing.");
88
0
      return;
89
0
    }
90
4
    HTDBG("################# ITERATION #################");
91
4
    // HTDBG("Entered iteration");
92
4
    // Create empty list of required prereqs per provider
93
4
    std::map<std::reference_wrapper<CoordinationProvider>,std::vector<PrioritisedPrerequisite>> assignPrereqs;
94
4
    for (auto& prov : providers) {
95
4
      assignPrereqs.emplace(prov,std::vector<PrioritisedPrerequisite>());
96
4
    }
97
4
    // HTDBG("Completed initialisation of provider prerequisities containers");
98
4
    // HTDBG(" - Provider count: {}", providers.size());
99
4
    
100
4
    std::vector<PrioritisedPrerequisite> connsRequired;
101
4
    // Loop over providers and ask for feature pre-requisites
102
4
    for (auto& prov : providers) {
103
4
      auto myConns = prov.get().requiredConnections();
104
4
      std::copy(myConns.begin(),myConns.end(),
105
4
        std::back_insert_iterator<std::vector<PrioritisedPrerequisite>>(connsRequired));
106
4
    }
107
4
    // HTDBG(std::to_string(connsRequired.size()));
108
4
    // HTDBG("Retrieved providers' current prerequisites");
109
4
    // TODO de-duplicate pre-reqs
110
4
    // Now link required prereqs to each provider
111
4
    for (auto& p : connsRequired) {
112
2
      auto el = featureProviders.find(std::get<0>(p)); // find provider for given prereq by feature tag
113
2
      if (featureProviders.end() != el) {
114
2
        assignPrereqs[el->second].push_back(p);
115
2
      }
116
2
    }
117
4
    // HTDBG("Linked pre-reqs to their providers");
118
4
119
4
    // // Some debug checks here
120
4
    // int cnt = 0;
121
4
    // for (auto& ass : assignPrereqs) {
122
4
    //   // HTDBG("assign prereqs number {} has this many prereqs to fill {}", cnt, ass.second.size());
123
4
    //   cnt++;
124
4
    // }
125
4
    
126
4
    // Communicate with relevant feature providers and request features for targets (in descending priority order)
127
4
    //  - Includes removal of previous features no longer needed
128
4
    std::vector<PrioritisedPrerequisite> provisioned;
129
4
    for (auto& prov : assignPrereqs) {
130
4
      // TODO sort by descending priority before passing on
131
4
132
4
      // FOR PLATFORMS WITH STD::FUTURE AND STD::ASYNC
133
4
      // std::future<void> fut = std::async(std::launch::async,
134
4
      //     &CoordinationProvider::provision, prov.first,
135
4
      // //prov.first->provision(
136
4
      //     prov.second,[&provisioned] (
137
4
      //   const std::vector<PrioritisedPrerequisite> myProvisioned) -> void {
138
4
      //   std::copy(myProvisioned.begin(),myProvisioned.end(),
139
4
      //     std::back_insert_iterator<std::vector<PrioritisedPrerequisite>>(provisioned));
140
4
      // });
141
4
      // fut.get(); // waits for callback // TODO wait with timeout
142
4
143
4
      // FOR OTHER PLATFORMS (E.g. ZEPHYR):-
144
4
      std::vector<PrioritisedPrerequisite> myProvisioned = prov.first.get().provision(prov.second);
145
4
      std::copy(myProvisioned.begin(),myProvisioned.end(),
146
4
        std::back_insert_iterator<std::vector<PrioritisedPrerequisite>>(provisioned));
147
4
    }
148
4
    // HTDBG("All pre-requisities requests sent and responses received");
149
4
    // TODO do the above asynchronously and await callback or timeout for all
150
4
151
4
    // For each which are now present, ask for activities (in descending priority order)
152
4
    for (auto& prov : providers) {
153
4
      auto maxActs = prov.get().requiredActivities();
154
4
      // TODO sort by descending priority before actioning
155
4
      for (auto& act : maxActs) {
156
2
        std::string san("Activity ");
157
2
        san += act.name;
158
2
        HTDBG(san);
159
2
        // HTDBG("Checking next desired activity for prereqs being satisfied");
160
2
        // Filter requested by provisioned
161
2
        bool allFound = true;
162
2
        for (auto& pre : act.prerequisites) {
163
2
          bool myFound = false;
164
2
          for (auto& exists : provisioned) {
165
2
            if (std::get<0>(pre) == std::get<0>(exists) &&
166
2
                std::get<1>(pre) == std::get<2>(exists)) {
167
2
              myFound = true;
168
2
            }
169
2
          }
170
2
          allFound = allFound & myFound;
171
2
          if (myFound) {
172
2
            HTDBG(" - Prereq satisfied");
173
2
          } else {
174
0
            HTDBG(" - Prereq NOT SATISFIED");
175
0
          }
176
2
        }
177
2
        // Carry out activities with completion callbacks passed in
178
2
        if (allFound) {
179
2
          HTDBG("All satisfied, calling activity");
180
2
          // do activity
181
2
182
2
          // FOR PLATFORMS WITH STD::ASYNC
183
2
          // act.executor(act,[this] (Activity act, std::optional<Activity> followOn) -> void {
184
2
          //   // TODO handle result
185
2
          //   // TODO Carry out any follow up activities
186
2
          //   HTDBG("Activity completion callback called");
187
2
          // });
188
2
189
2
          // FOR PLATFORMS WITHOUT
190
2
          std::optional<Activity> followOn = act.executor(act);
191
2
          // TODO carry out follow on activity until no more follow ons (or max follow on number hit)
192
2
        }
193
2
      }
194
4
    }
195
4
    // HTDBG("Leaving iteration");
196
4
    HTDBG("#################    END    #################");
197
4
  }
198
  /// Closes out any existing connections/activities
199
1
  void stop() {
200
1
    running = false;
201
1
  }
202
203
private:
204
  ContextT& context;
205
206
  std::vector<std::reference_wrapper<CoordinationProvider>> providers;
207
  std::map<FeatureTag,std::reference_wrapper<CoordinationProvider>> featureProviders;
208
209
  bool running;
210
211
  HLOGGER(ContextT);
212
};
213
214
// /** Comparator for less than (use in maps) **/
215
bool operator<(const std::reference_wrapper<CoordinationProvider> first, const std::reference_wrapper<CoordinationProvider> second);
216
217
}
218
}
219
220
#endif