42int main(
int argc,
char* argv[]) {
43 po::options_description options(
"Usage: chimeratk-device-to-jmap [options]");
44 options.add_options()(
"help",
"print this help text")(
"device", po::value<std::string>()->required(),
45 "device to read the register catalogue from, given as a ChimeraTK device descriptor (CDD) or an alias name")(
46 "dmap", po::value<std::string>(),
"dmap file path used to resolve alias names; does not affect --device")(
47 "output", po::value<std::string>()->required(),
"path of the jmap file to write (never written to stdout)")(
48 "string-length", po::value<size_t>(),
49 "fixed byte size per element assumed for ASCII string registers (default 80)");
51 po::variables_map variableMap;
53 po::store(po::parse_command_line(argc, argv, options), variableMap);
55 catch(
const po::error& error) {
56 std::cerr <<
"Error: " << error.what() <<
"\n\n";
57 std::cerr << options <<
"\n";
61 if(variableMap.count(
"help")) {
62 std::cout << options <<
"\n";
63 std::cout <<
"Limitations:\n";
64 std::cout <<
" - Two-dimensional registers are skipped (a single warning lists all affected registers).\n";
65 std::cout <<
" - Writable registers with AccessMode::wait_for_new_data become read-only interrupts (a single "
66 "warning lists all affected registers).\n";
67 std::cout <<
" - Addresses in the output are synthetic.\n";
68 std::cout <<
" - ASCII strings are written with a fixed length of --string-length bytes per element "
74 po::notify(variableMap);
76 catch(
const po::error& error) {
77 std::cerr <<
"Error: " << error.what() <<
"\n\n";
78 std::cerr << options <<
"\n";
82 if(variableMap.count(
"dmap")) {
86 std::string deviceString = variableMap[
"device"].as<std::string>();
87 std::string outputPath = variableMap[
"output"].as<std::string>();
88 size_t stringLength = 80;
89 if(variableMap.count(
"string-length")) {
90 stringLength = variableMap[
"string-length"].as<
size_t>();
100 std::cerr <<
"Error: " << error.what() <<
"\n";
106 std::vector<const ChimeraTK::BackendRegisterInfoBase*> registers;
107 registers.reserve(catalogue.getNumberOfRegisters());
108 for(
const auto& registerInfo : catalogue) {
109 registers.push_back(®isterInfo);
116 json addressSpace = json::object();
117 std::vector<std::string> twoDimensionalRegisters;
118 std::vector<std::string> writableInterruptRegisters;
119 size_t interruptId = 0;
120 size_t addressOffset = 0;
123 for(
const auto* registerInfo : registers) {
124 auto name = registerInfo->getRegisterName();
125 if(registerInfo->getNumberOfDimensions() > 1) {
126 twoDimensionalRegisters.push_back(name);
130 auto descriptor = registerInfo->getDataDescriptor();
134 if(waitForNewData && registerInfo->isWriteable() && !isVoid) {
135 writableInterruptRegisters.push_back(name);
138 json entry = json::object();
141 entry[
"triggeredByInterrupt"] = json::array({interruptId++});
147 entry[
"triggeredByInterrupt"] = json::array({interruptId++});
153 entry[
"numberOfElements"] = registerInfo->getNumberOfElements();
154 entry[
"bytesPerElement"] = elementBytes;
155 entry[
"address"] = json::object({{
"type",
"IO"}, {
"channel", 0}, {
"offset", addressOffset}});
156 addressOffset +=
padToAlignment(elementBytes * registerInfo->getNumberOfElements());
164 catch(
const std::logic_error& error) {
165 std::cerr <<
"Error: " << error.what() <<
"\n";
170 output[
"mapFormatVersion"] =
"1.0";
171 output[
"metadata"] = json::object();
172 output[
"interruptHandler"] = json::object();
173 output[
"addressSpace"] = addressSpace;
175 if(!twoDimensionalRegisters.empty()) {
176 std::cerr <<
"Warning: the following two-dimensional registers are unsupported and were skipped:\n";
177 for(
const auto& reg : twoDimensionalRegisters) {
178 std::cerr <<
" " << reg <<
"\n";
181 if(!writableInterruptRegisters.empty()) {
182 std::cerr <<
"Warning: the following writable registers with AccessMode::wait_for_new_data were written as "
183 "read-only interrupts:\n";
184 for(
const auto& reg : writableInterruptRegisters) {
185 std::cerr <<
" " << reg <<
"\n";
189 std::ofstream outputFile(outputPath);
190 if(!outputFile.is_open()) {
191 std::cerr <<
"Error: could not open output file '" << outputPath <<
"' for writing.\n";
197 outputFile << output.dump(2) <<
"\n";
295 auto& destination = children[components[0]];
296 if(components.size() > 1) {
300 if(!destination.is_object()) {
301 destination = json::object();
303 if(!destination.contains(
"children")) {
304 destination[
"children"] = json::object();
306 std::vector<std::string> rest(components.begin() + 1, components.end());
312 for(
const auto& [key, value] : entry.items()) {
313 if(destination.contains(key)) {
314 throw std::logic_error(
"Internal inconsistency: register path '" + entry.dump() +
315 "' conflicts with existing "
319 destination[key] = value;
void insertRegister(json &children, const std::vector< std::string > &components, const json &entry)
Insert a register entry into the addressSpace hierarchy, mirroring the module path via "children" obj...