1 #pragma once
2 
3 #include "NVMeSensor.hpp"
4 
5 #include <boost/asio/io_context.hpp>
6 #include <boost/asio/steady_timer.hpp>
7 
8 #include <memory>
9 #include <stdexcept>
10 
11 class NVMeContext : public std::enable_shared_from_this<NVMeContext>
12 {
13   public:
NVMeContext(boost::asio::io_context & io,int rootBus)14     NVMeContext(boost::asio::io_context& io, int rootBus) :
15         scanTimer(io), rootBus(rootBus), pollCursor(sensors.end())
16     {
17         if (rootBus < 0)
18         {
19             throw std::invalid_argument(
20                 "Invalid root bus: Bus ID must not be negative");
21         }
22     }
23 
~NVMeContext()24     virtual ~NVMeContext()
25     {
26         scanTimer.cancel();
27     }
28 
addSensor(const std::shared_ptr<NVMeSensor> & sensor)29     void addSensor(const std::shared_ptr<NVMeSensor>& sensor)
30     {
31         sensors.emplace_back(sensor);
32     }
33 
34     std::optional<std::shared_ptr<NVMeSensor>>
getSensorAtPath(const std::string & path)35         getSensorAtPath(const std::string& path)
36     {
37         for (auto& sensor : sensors)
38         {
39             if (sensor->configurationPath == path)
40             {
41                 return sensor;
42             }
43         }
44 
45         return std::nullopt;
46     }
47 
48     // Post-condition: The sensor list does not contain the provided sensor
49     // Post-condition: pollCursor is a valid iterator for the sensor list
removeSensor(const std::shared_ptr<NVMeSensor> & sensor)50     void removeSensor(const std::shared_ptr<NVMeSensor>& sensor)
51     {
52         // Locate the sensor that we're removing in the sensor list
53         auto found = std::find(sensors.begin(), sensors.end(), sensor);
54 
55         // If we failed to find the sensor in the list the post-condition is
56         // already satisfied
57         if (found == sensors.end())
58         {
59             return;
60         }
61 
62         // We've found the sensor in the list
63 
64         // If we're not actively polling the sensor list, then remove the sensor
65         if (pollCursor == sensors.end())
66         {
67             sensors.erase(found);
68             return;
69         }
70 
71         // We're actively polling the sensor list
72 
73         // If we're not polling the specific sensor that has been removed, then
74         // remove the sensor
75         if (*pollCursor != *found)
76         {
77             sensors.erase(found);
78             return;
79         }
80 
81         // We're polling the sensor that is being removed
82 
83         // Remove the sensor and update the poll cursor so the cursor remains
84         // valid
85         pollCursor = sensors.erase(found);
86     }
87 
close()88     virtual void close()
89     {
90         scanTimer.cancel();
91     }
92 
93     virtual void pollNVMeDevices() = 0;
94 
95     virtual void readAndProcessNVMeSensor() = 0;
96 
97     virtual void processResponse(std::shared_ptr<NVMeSensor>& sensor, void* msg,
98                                  size_t len) = 0;
99 
100   protected:
101     boost::asio::steady_timer scanTimer;
102     int rootBus; // Root bus for this drive
103     std::list<std::shared_ptr<NVMeSensor>> sensors;
104     std::list<std::shared_ptr<NVMeSensor>>::iterator pollCursor;
105 };
106 
107 using NVMEMap = boost::container::flat_map<int, std::shared_ptr<NVMeContext>>;
108 
109 NVMEMap& getNVMEMap();
110