1 /* Copyright 2016, Ableton AG, Berlin. All rights reserved.
2  *
3  *  This program is free software: you can redistribute it and/or modify
4  *  it under the terms of the GNU General Public License as published by
5  *  the Free Software Foundation, either version 2 of the License, or
6  *  (at your option) any later version.
7  *
8  *  This program is distributed in the hope that it will be useful,
9  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  *  GNU General Public License for more details.
12  *
13  *  You should have received a copy of the GNU General Public License
14  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
15  *
16  *  If you would like to incorporate Link into a proprietary software application,
17  *  please contact <link-devs@ableton.com>.
18  */
19 
20 #pragma once
21 
22 #include <ableton/platforms/asio/AsioWrapper.hpp>
23 #include <ableton/util/Injected.hpp>
24 #include <chrono>
25 #include <vector>
26 
27 namespace ableton
28 {
29 namespace discovery
30 {
31 
32 // Callback takes a range of asio::ip:address which is
33 // guaranteed to be sorted and unique
34 template <typename Callback, typename IoContext>
35 class InterfaceScanner
36 {
37 public:
38   using Timer = typename util::Injected<IoContext>::type::Timer;
39 
InterfaceScanner(const std::chrono::seconds period,util::Injected<Callback> callback,util::Injected<IoContext> io)40   InterfaceScanner(const std::chrono::seconds period,
41     util::Injected<Callback> callback,
42     util::Injected<IoContext> io)
43     : mPeriod(period)
44     , mCallback(std::move(callback))
45     , mIo(std::move(io))
46     , mTimer(mIo->makeTimer())
47   {
48   }
49 
enable(const bool bEnable)50   void enable(const bool bEnable)
51   {
52     if (bEnable)
53     {
54       scan();
55     }
56     else
57     {
58       mTimer.cancel();
59     }
60   }
61 
scan()62   void scan()
63   {
64     using namespace std;
65     debug(mIo->log()) << "Scanning network interfaces";
66     // Rescan the hardware for available network interface addresses
67     vector<asio::ip::address> addrs = mIo->scanNetworkInterfaces();
68     // Sort and unique them to guarantee consistent comparison
69     sort(begin(addrs), end(addrs));
70     addrs.erase(unique(begin(addrs), end(addrs)), end(addrs));
71     // Pass them to the callback
72     (*mCallback)(std::move(addrs));
73     // setup the next scanning
74     mTimer.expires_from_now(mPeriod);
75     using ErrorCode = typename Timer::ErrorCode;
76     mTimer.async_wait([this](const ErrorCode e) {
77       if (!e)
78       {
79         scan();
80       }
81     });
82   }
83 
84 private:
85   const std::chrono::seconds mPeriod;
86   util::Injected<Callback> mCallback;
87   util::Injected<IoContext> mIo;
88   Timer mTimer;
89 };
90 
91 // Factory function
92 template <typename Callback, typename IoContext>
makeInterfaceScanner(const std::chrono::seconds period,util::Injected<Callback> callback,util::Injected<IoContext> io)93 InterfaceScanner<Callback, IoContext> makeInterfaceScanner(
94   const std::chrono::seconds period,
95   util::Injected<Callback> callback,
96   util::Injected<IoContext> io)
97 {
98   using namespace std;
99   return {period, move(callback), move(io)};
100 }
101 
102 } // namespace discovery
103 } // namespace ableton
104