Skip to content

Commit

Permalink
Public-Auto-release: v2.18.2
Browse files Browse the repository at this point in the history
# Added
* C++: Add clean shutdown method to mockup server
* Automatically wait for successful time synchronization

# Removed
* Python: Dropped legacy methods `set_ntp_server` and `get_ntp_server`
  • Loading branch information
blickfeld-lidar committed Mar 8, 2021
1 parent 3dd3eb4 commit 105bf6c
Show file tree
Hide file tree
Showing 12 changed files with 123 additions and 75 deletions.
9 changes: 9 additions & 0 deletions doc/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ influence the resulting point cloud.

### Removed

## [2.18.2] - 2021.03.08

### Added
* C++: Add clean shutdown method to mockup server
* Automatically wait for successful time synchronization

### Removed
* Python: Dropped legacy methods `set_ntp_server` and `get_ntp_server`

## [2.18.1] - 2021.03.02

### Changed
Expand Down
30 changes: 21 additions & 9 deletions examples/cpp/server/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,38 @@
* LICENSE.md file in the root directory of this source tree.
*/

#include <csignal>

#include <blickfeld/exception.h>
#include <blickfeld/mockup_server.h>

std::shared_ptr<blickfeld::network::mockup_server> server;

void sigint_handler(int signal) {
std::cout << "Received SIGINT. Shutting down.." << std::endl;
server = nullptr;
}

int example(int argc, char* argv[]) {
// Install a sigint handler
std::signal(SIGINT, sigint_handler);

std::string dump_fn;
if (argc > 1)
dump_fn = argv[1];
else
throw blickfeld::str_exception("Please provide filename of recording");

blickfeld::network::mockup_server server(dump_fn, [](const blickfeld::protocol::stream::Subscribe::PointCloud subscribe) {
// Handler is called for each client point cloud subscription.
// Variables can be initialized in this step.
server = std::make_shared<blickfeld::network::mockup_server>(dump_fn, [](const blickfeld::protocol::stream::Subscribe::PointCloud subscribe) {
// Handler is called for each client point cloud subscription.
// Variables can be initialized in this step.

// Return function, which as called for every frame provided to the client.
return [](blickfeld::protocol::data::Frame& frame) {
// Process & mutate client frame
};
});
return server.serve_forever();
// Return function, which as called for every frame provided to the client.
return [](blickfeld::protocol::data::Frame& frame) {
// Process & mutate client frame
};
});
return server->serve_forever();
}

int main(int argc, char* argv[]) {
Expand Down
8 changes: 3 additions & 5 deletions examples/cpp/time_synchronization/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,16 @@ int example(int argc, char* argv[]) {
std::cout << "Connected to " << scanner_ip_or_host << std::endl;

if (time_sync == "ntp") {
std::cout << "NTP synchronization selected" << std::endl;
std::cout << "NTP synchronization selected. Starting time synchronization.." << std::endl;
scanner->set_ntp_time_synchronization();
} else if (time_sync == "ptp") {
std::cout << "PTP synchronization selected" << std::endl;
std::cout << "PTP synchronization selected. Starting time synchronization.." << std::endl;
scanner->set_ptp_time_synchronization();
} else {
std::cout << "Couldn't parse second input, please provide either NTP or PTP" << std::endl;
return 0;
}
std::cout << "Synchronization status:" << std::endl;
std::cout << scanner->get_status().time_synchronization().DebugString() << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(60));

std::cout << "Synchronization status:" << std::endl;
std::cout << scanner->get_status().time_synchronization().DebugString() << std::endl;

Expand Down
15 changes: 5 additions & 10 deletions examples/python/ntp_several_lidar_synchronization.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,11 @@ def example(args):

if args.ntp_server_ip != "": # If the ntp_server_ip variable is set, set the ntp servers
for device in devices: # iterate through devices
device_ntp_server_ip = device.get_ntp_server() # Get ntp server ip address of the device
print("On device: " + device.hostname_or_ip + " current ntp server is: " + device.get_ntp_server())
if device_ntp_server_ip != args.ntp_server_ip: # It the ntp server ip address is different to the
# given ntp_server_ip set the ntp server ip address to the given ntp_server_ip
device.set_ntp_server(args.ntp_server_ip)
print("On device: " + device.hostname_or_ip + " new ntp server is: " + device.get_ntp_server())
else:
print("Requested pattern:", config)
print(devices[0].fill_scan_pattern(config))
blickfeld_scanner.scanner.sync(devices=devices, scan_pattern=config, target_frame_rate=args.frame_rate)
device.set_time_synchronization(ntp = [args.ntp_server_ip])

print("Requested pattern:", config)
print(devices[0].fill_scan_pattern(config))
blickfeld_scanner.scanner.sync(devices=devices, scan_pattern=config, target_frame_rate=args.frame_rate)

if __name__ == "__main__":
parser = argparse.ArgumentParser() # Command line argument parser
Expand Down
3 changes: 1 addition & 2 deletions examples/python/time_synchronization.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,10 @@ def example(args):

device = blickfeld_scanner.scanner(args.host) # Connect to the device

print("Starting time_synchronization..");
device.set_time_synchronization(ntp=args.ntp, ptp=args.ptp) # Set time synchronization method. A list of ntp server or a list of ptp unicast destinations can be given as arguments as well.

print(device.get_status().time_synchronization) # Print time synchronization status
time.sleep(60) # Sleep 1 minute to make sure synchronization worked and then print the resulting time synchronization status
print(device.get_status().time_synchronization)


if __name__ == "__main__":
Expand Down
9 changes: 9 additions & 0 deletions include/blickfeld/mockup_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ namespace blickfeld {
namespace network {

class simulated_connection;
class mockup_session;

/**
* @brief This server mocks the blickfeld-scanner-server, which accepts the BSL connections on the Blickfeld devices.
Expand All @@ -53,6 +54,7 @@ class mockup_server : public logged_object {

asio::io_context* io_context;
asio::basic_socket_acceptor<asio::ip::tcp, asio::executor>* acceptor;
std::vector<std::weak_ptr<mockup_session> > sessions;

void do_accept();

Expand All @@ -73,6 +75,13 @@ class mockup_server : public logged_object {
* @return int Return 0 if it has been exited normally.
*/
int serve_forever();

/**
* @brief Shutdown server
*
* This can be called asynchronously to shutdown the server.
*/
void shutdown();
};

} // namespace network
Expand Down
15 changes: 13 additions & 2 deletions include/blickfeld/scanner.h
Original file line number Diff line number Diff line change
Expand Up @@ -370,14 +370,25 @@ class BF_DLLEXPORT scanner : public logged_object {
/// @param persist: Persist advanced config on device and reload it after a power-cycle
void set_advanced_config(protocol::config::Advanced config, bool persist=false);

/// > Introduced in BSL v2.18 and firmware v1.19
///
/// Function to set time synchronization
/// The existing config will be overwritten.
///
/// @param config: Configuration, see protobuf_protocol advanced config
/// @param persist: Persist advanced config on device and reload it after a power-cycle
/// @param wait_for_sync: Wait until device is synchronized. Raises exception if device is not synchronized within max_sync_duration.
/// @param max_sync_duration: Specify maximum time in seconds for synchronization.
void set_time_synchronization(const protocol::config::Advanced::TimeSynchronization config, bool persist=true, bool wait_for_sync=true, int max_sync_duration=60);

/// > Introduced in BSL v2.18 and firmware v1.19
///
/// Function to set ntp time synchronization, a vector of servers can be provided.
/// The old servers and config will be overwritten.
///
/// @param servers: vector of servers to connect to for time synchronization
/// @param persist: Persist advanced config on device and reload it after a power-cycle
void set_ntp_time_synchronization(std::vector<std::string> servers={}, bool persist=false);
void set_ntp_time_synchronization(std::vector<std::string> servers={});

/// > Introduced in BSL v2.18 and firmware v1.19
///
Expand All @@ -388,7 +399,7 @@ class BF_DLLEXPORT scanner : public logged_object {
///
/// @param servers: vector of unicast destinations to connect to for time synchronization (if not given or vector is empty, multicast will be used)
/// @param persist: Persist advanced config on device and reload it after a power-cycle
void set_ptp_time_synchronization(std::vector<std::string> unicast_destinations={}, bool persist=false);
void set_ptp_time_synchronization(std::vector<std::string> unicast_destinations={});

/// Start self test on device
const protocol::Response::RunSelfTest run_self_test();
Expand Down
55 changes: 15 additions & 40 deletions python/blickfeld_scanner/scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from .protocol.stream import connection_pb2 as stream_connection_pb2
from .protocol import options_pb2, common_pb2
from .protocol.config import algorithm_pb2
from .protocol.status import time_synchronization_pb2
from . import stream
from .version import __version__

Expand Down Expand Up @@ -369,7 +370,7 @@ def get_device_timestamp(self):
req.hello.protocol_version = self.protocol_version
return self._connection.send_request(req).timestamp_ns / 1e9

def set_time_synchronization(self, ntp=False, ptp=False, persist=True):
def set_time_synchronization(self, ntp=False, ptp=False, persist=True, wait_for_sync=True, max_sync_duration=60):
"""> Introduced in BSL v2.18 and firmware v1.19
Set the type of time synchronization and set basic parameters
Expand All @@ -379,7 +380,9 @@ def set_time_synchronization(self, ntp=False, ptp=False, persist=True):
:param ntp: Set this field to choose NTPv4 time synchronization, this can be a list of ntp servers or a bool if the standard config should be used
:param ptp: Set this field to choose PTP time synchronization, this can be a list of ptp unicast destinations, this will also activate unicast mode and deactivate multicast mode or a bool if the standard config should be used
:param persist: Persist time synchronization config on device and reload it after a power-cycle
:return: response advanced config, see :any:`protobuf_protocol` advanced config
:param wait_for_sync: Wait until device is synchronized. Raises exception if device is not synchronized within max_sync_duration.
:param max_sync_duration: Specify maximum time in seconds for synchronization.
:return: None.
"""
cfg = self.get_advanced_config()

Expand All @@ -403,45 +406,17 @@ def set_time_synchronization(self, ntp=False, ptp=False, persist=True):
else:
cfg.time_synchronization.ptp.CopyFrom(ptp)

return self.set_advanced_config(cfg, persist=persist)

def get_ntp_server(self):
"""Attention: To use this function you need the requests library
`https://requests.readthedocs.io/en/master/`
Function to get a ntp server
:return: Returns the ntp server IP address
"""
if self._hello.library_version and LooseVersion(self._hello.library_version) >= LooseVersion("2.18.0"):
ntp = self.get_advanced_config().time_synchronization.ntp
return ntp.servers[0] if len(ntp.servers) > 0 else ""
else:
import requests
req = requests.get(self.api_path + "network/ntp")
req.raise_for_status()
return req.json()['data']['server']

def set_ntp_server(self, server):
"""Attention: To use this function you need the requests library
`https://requests.readthedocs.io/en/master/`
Function to set a ntp server
:param server: Server IP to set the ntp server to
:type server: str
"""
ret = self.set_advanced_config(cfg, persist=persist)

if self._hello.library_version and LooseVersion(self._hello.library_version) >= LooseVersion("2.18.0"):
return self.set_time_synchronization(ntp=[server])
else:
import requests
import json
cur_server = self.get_ntp_server()
if cur_server != server:
req = requests.put(self.api_path + "network/ntp/server", data=json.dumps({ "data": server }), headers={"Content-Type": "application/json"})
req.raise_for_status()
log.info("Configured NTP server to '%s'. Please power cycle the device to apply changes." % (self.get_ntp_server()))
if wait_for_sync:
while max_sync_duration > 0:
status = self.get_status()
if status.time_synchronization.state == time_synchronization_pb2.TimeSynchronization.State.SYNCED:
return
time.sleep(1)
max_sync_duration = max_sync_duration - 1

raise Exception("Device failed to synchronize. Current state is %s." % (time_synchronization_pb2.TimeSynchronization.State.Name(status.time_synchronization.state)))

def run_self_test(self):
"""> Introduced in BSL v2.10 and firmware v1.9
Expand Down
18 changes: 17 additions & 1 deletion src/mockup_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ mockup_server::mockup_server(const std::string replay_dump_fn, point_cloud_strea
}

mockup_server::~mockup_server() {
shutdown();
if (acceptor)
delete acceptor;
if (io_context)
Expand All @@ -37,7 +38,7 @@ mockup_server::~mockup_server() {
int mockup_server::serve_forever() {
log_info("started...\n");
io_context->run();
log_info("shutting down...\n");
log_info("stopped.\n");
return 0;
}

Expand All @@ -60,9 +61,24 @@ void mockup_server::do_accept() {

if (acceptor->is_open())
do_accept();

sessions.push_back(client);
});
}

void mockup_server::shutdown() {
log_info("shutting down..\n");
acceptor->close();

for (auto session : sessions) {
if (auto session_lock = session.lock()) {
session_lock->stop();
}
}

io_context->stop();
}

} // namespace network

} // namespace blickfeld
Expand Down
5 changes: 5 additions & 0 deletions src/mockup_session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ void mockup_session::start() {
do_send();
}

void mockup_session::stop() {
socket->close();
context_thread->join();
}

void mockup_session::do_read() {
auto self(shared_from_this());

Expand Down
1 change: 1 addition & 0 deletions src/mockup_session.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class mockup_session : public std::enable_shared_from_this<mockup_session>, publ
virtual ~mockup_session();

void start();
void stop();
};

} // namespace network
Expand Down
30 changes: 24 additions & 6 deletions src/scanner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -424,16 +424,34 @@ void scanner::set_advanced_config(protocol::config::Advanced config, bool persis
conn->send_request(req, resp);
}

void scanner::set_ntp_time_synchronization(std::vector<std::string> servers, bool persist) {
void scanner::set_time_synchronization(const protocol::config::Advanced::TimeSynchronization config, bool persist, bool wait_for_sync, int max_sync_duration) {
auto cfg = this->get_advanced_config();
*cfg.mutable_time_synchronization()->mutable_ntp()->mutable_servers() = {servers.begin(), servers.end()};
cfg.mutable_time_synchronization()->CopyFrom(config);
this->set_advanced_config(cfg, persist);

if (wait_for_sync) {
protocol::Status status;
while (max_sync_duration--) {
status = this->get_status();
if (status.time_synchronization().state() == status.time_synchronization().SYNCED)
return;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
throw str_exception("Device failed to synchronize. Current state is %s.",
protocol::status::TimeSynchronization::State_Name(status.time_synchronization().state()).c_str());
}
}

void scanner::set_ptp_time_synchronization(std::vector<std::string> unicast_destinations, bool persist) {
auto cfg = this->get_advanced_config();
*cfg.mutable_time_synchronization()->mutable_ptp()->mutable_unicast_destinations() = {unicast_destinations.begin(), unicast_destinations.end()};
this->set_advanced_config(cfg, persist);
void scanner::set_ntp_time_synchronization(std::vector<std::string> servers) {
protocol::config::Advanced::TimeSynchronization cfg;
*cfg.mutable_ntp()->mutable_servers() = {servers.begin(), servers.end()};
this->set_time_synchronization(cfg);
}

void scanner::set_ptp_time_synchronization(std::vector<std::string> unicast_destinations) {
protocol::config::Advanced::TimeSynchronization cfg;
*cfg.mutable_ptp()->mutable_unicast_destinations() = {unicast_destinations.begin(), unicast_destinations.end()};
this->set_time_synchronization(cfg);
}

#endif
Expand Down

0 comments on commit 105bf6c

Please sign in to comment.