#include "Caller.h" #include "sls/ZmqSocket.h" #include "sls/bit_utils.h" #include "sls/file_utils.h" #include "sls/logger.h" #include "sls/string_utils.h" #include #include namespace sls { // some helper functions to print std::vector Caller::getAllCommands() { std::vector ret; for (auto it : functions) ret.push_back(it.first); return ret; } std::map Caller::GetDeprecatedCommands() { return deprecated_functions; } void Caller::call(const std::string &command, const std::vector &arguments, int detector_id, int action, std::ostream &os, int receiver_id) { cmd = command; args = arguments; // copy args before replacing SuggestIfRemoved(cmd); std::string temp; while (temp != cmd) { temp = cmd; ReplaceIfDeprecated(cmd); } det_id = detector_id; rx_id = receiver_id; auto it = functions.find(cmd); if (it != functions.end()) { auto ret = ((*this).*(it->second))(action); os << cmd << ' ' << ret; } else { throw RuntimeError(cmd + " Unknown command, use list to list all commands"); } } bool Caller::ReplaceIfDeprecated(std::string &command) { auto d_it = deprecated_functions.find(command); if (d_it != deprecated_functions.end()) { // insert old command into arguments (for dacs) if (d_it->second == "dac") { args.insert(args.begin(), command); LOG(logWARNING) << command << " is deprecated and will be removed. Please migrate to: " << d_it->second << " " << command; } else { LOG(logWARNING) << command << " is deprecated and will be removed. Please migrate to: " << d_it->second; } command = d_it->second; return true; } return false; } void Caller::SuggestIfRemoved(const std::string &command) { auto r_it = removed_functions.find(command); if (r_it != removed_functions.end()) { std::ostringstream oss; oss << command << " is removed and is no longer available. Please use: " << r_it->second; throw RuntimeError(oss.str()); } } std::string Caller::list(int action) { if (action == defs::HELP_ACTION) { return "[deprecated(optional)]\n\tlists all available commands, list " "deprecated - list deprecated commands\n"; } if (args.empty()) { std::string ret = "free\n"; ret += "user\n"; for (auto &f : functions) { ret += f.first + "\n"; } return ret; } else if (args.size() == 1) { if (args[0] == "deprecated") { std::ostringstream os; os << "The following " << deprecated_functions.size() << " commands are deprecated\n"; const size_t field_width = 20; for (const auto &it : deprecated_functions) { os << std::right << std::setw(field_width) << it.first << " -> " << it.second << '\n'; } return os.str(); } else { throw RuntimeError( "Could not decode argument. Possible options: deprecated"); } } else { WrongNumberOfParameters(0); return ""; } } /* Network Configuration (Detector<->Receiver) */ IpAddr Caller::getDstIpFromAuto() { std::string rxHostname = det->getRxHostname(std::vector{det_id}).squash("none"); // Hostname could be ip try to decode otherwise look up the hostname auto val = IpAddr{rxHostname}; if (val == 0) { val = HostnameToIp(rxHostname.c_str()); } return val; } IpAddr Caller::getSrcIpFromAuto() { std::string hostname = det->getHostname(std::vector{det_id}).squash("none"); // Hostname could be ip try to decode otherwise look up the hostname auto val = IpAddr{hostname}; if (val == 0) { val = HostnameToIp(hostname.c_str()); } return val; } UdpDestination Caller::getUdpEntry() { UdpDestination udpDestination{}; udpDestination.entry = rx_id; for (auto it : args) { size_t pos = it.find('='); std::string key = it.substr(0, pos); std::string value = it.substr(pos + 1); if (key == "ip") { if (value == "auto") { auto val = getDstIpFromAuto(); LOG(logINFO) << "Setting udp_dstip of detector " << det_id << " to " << val; udpDestination.ip = val; } else { udpDestination.ip = IpAddr(value); } } else if (key == "ip2") { if (value == "auto") { auto val = getDstIpFromAuto(); LOG(logINFO) << "Setting udp_dstip2 of detector " << det_id << " to " << val; udpDestination.ip2 = val; } else { udpDestination.ip2 = IpAddr(value); } } else if (key == "mac") { udpDestination.mac = MacAddr(value); } else if (key == "mac2") { udpDestination.mac2 = MacAddr(value); } else if (key == "port") { udpDestination.port = StringTo(value); } else if (key == "port2") { udpDestination.port2 = StringTo(value); } } return udpDestination; } void Caller::WrongNumberOfParameters(size_t expected) { if (expected == 0) { throw RuntimeError("Command " + cmd + " expected no parameter/s but got " + std::to_string(args.size()) + "\n"); } throw RuntimeError("Command " + cmd + " expected (or >=) " + std::to_string(expected) + " parameter/s but got " + std::to_string(args.size()) + "\n"); } int Caller::GetLevelAndInsertIntoArgs(std::string levelSeparatedCommand) { if (cmd != levelSeparatedCommand) { LOG(logWARNING) << "This command is deprecated and will be removed. " "Please migrate to " << levelSeparatedCommand; int level = cmd[cmd.find_first_of("012")] - '0'; args.insert(args.begin(), std::to_string(level)); return true; } return false; } std::string Caller::free(int action) { // This function is purely for help, actual functionality is in the caller return "free\n\tFree detector shared memory\n"; } std::string Caller::user(int action) { // This function is purely for help, actual functionality is in the caller return "user\n\tUser details from shared memory (hostname, type, PID, " "User, Date).\n"; } std::string Caller::hostname(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "\n\tFrees shared memory and sets hostname (or IP address) of " "all modules concatenated by +.\n\t Virtual servers can already " "use the port in hostname separated by ':' and ports incremented " "by 2 to accomodate the stop server as well. The row and column " "values in the udp/zmq header are affected by the order in this " "command and the detsize command. The modules are stacked row by " "row until they reach the y-axis limit set by detsize (if " "specified). Then, stacking continues in the next column and so " "on. This only affects row and column in udp/zmq header." << '\n'; } else if (action == defs::GET_ACTION) { if (!args.empty()) { WrongNumberOfParameters(0); } auto t = det->getHostname(std::vector{det_id}); os << OutString(t) << '\n'; } else if (action == defs::PUT_ACTION) { if (args.empty()) { WrongNumberOfParameters(1); } if (det_id != -1) { throw RuntimeError("Cannot execute this at module level"); } // only args[0], but many hostames concatenated with + if (args[0].find('+') != std::string::npos) { auto t = split(args[0], '+'); det->setHostname(t); os << ToString(t) << '\n'; } // either hostnames separated by space, or single hostname else { det->setHostname(args); os << ToString(args) << '\n'; } } else { throw RuntimeError("Unknown action"); } return os.str(); } void Caller::EmptyDataCallBack(detectorData *data, uint64_t frameIndex, uint32_t subFrameIndex, void *this_pointer) { LOG(logDEBUG) << "EmptyDataCallBack to start up zmq sockets"; } std::string Caller::acquire(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "\n\tAcquire the number of frames set up.\n\tBlocking command, " "where control server is blocked and cannot accept other " "commands until acquisition is done. \n\t- sets acquiring " "flag\n\t- starts the receiver listener (if enabled)\n\t- starts " "detector acquisition for number of frames set\n\t- monitors " "detector status from running to idle\n\t- stops the receiver " "listener (if enabled)\n\t- increments file index if file write " "enabled\n\t- resets acquiring flag" << '\n'; } else { if (det->empty()) { throw RuntimeError("This shared memory has no detectors added."); } if (det_id >= 0) { throw RuntimeError("Individual detectors not allowed for readout."); } if (action == defs::READOUT_ZMQ_ACTION) { det->registerDataCallback(&(EmptyDataCallBack), this); } det->acquire(); if (det->getUseReceiverFlag().squash(false)) { os << "\nAcquired "; os << det->getFramesCaught() << '\n'; } } return os.str(); } std::string Caller::versions(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "\n\tPrint all versions and detector type" << '\n'; } else if (action == defs::GET_ACTION) { if (!args.empty()) { WrongNumberOfParameters(0); } std::string vType = "Unknown"; std::string vFirmware = "Unknown"; std::string vServer = "Unknown"; std::string vKernel = "Unknown"; std::string vHardware = "Unknown"; bool eiger = false; std::string vBebFirmware = "Unknown"; std::string vFeblFirmware = "Unknown"; std::string vFebrFirmware = "Unknown"; bool receiver = false; std::string vReceiver = "Unknown"; std::string vRelease = det->getPackageVersion(); std::string vClient = det->getClientVersion(); if (det->size() != 0) { // shared memory has detectors vType = OutString(det->getDetectorType()); eiger = (det->getDetectorType().squash() == defs::EIGER); receiver = det->getUseReceiverFlag().squash(false); if (receiver) { // cannot connect to receiver try { vReceiver = OutString( det->getReceiverVersion(std::vector{det_id})); } catch (const std::exception &e) { } } // cannot connect to Detector try { auto firmwareVersion = det->getFirmwareVersion(std::vector{det_id}); vFirmware = OutStringHex(firmwareVersion); vServer = OutString( det->getDetectorServerVersion(std::vector{det_id})); vKernel = OutString( det->getKernelVersion({std::vector{det_id}})); vHardware = OutString( det->getHardwareVersion(std::vector{det_id})); if (eiger) { vBebFirmware = OutString(firmwareVersion); vFeblFirmware = OutString(det->getFrontEndFirmwareVersion( defs::FRONT_LEFT, std::vector{det_id})); vFebrFirmware = OutString(det->getFrontEndFirmwareVersion( defs::FRONT_RIGHT, std::vector{det_id})); } } catch (const std::exception &e) { } } os << "\nType : " << vType << "\nRelease : " << vRelease << "\nClient : " << vClient; if (eiger) { os << "\nFirmware (Beb) : " << vBebFirmware << "\nFirmware (Febl) : " << vFeblFirmware << "\nFirmware (Febr) : " << vFebrFirmware; } else { os << "\nFirmware : " << vFirmware; } os << "\nServer : " << vServer << "\nKernel : " << vKernel << "\nHardware : " << vHardware; if (receiver) os << "\nReceiver : " << vReceiver; os << std::dec << '\n'; } else if (action == defs::PUT_ACTION) { throw RuntimeError("cannot put"); } else { throw RuntimeError("Unknown action"); } return os.str(); } std::string Caller::threshold(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "[eV] [(optinal settings)" "\n\t[Eiger][Mythen3] Threshold in eV. It loads trim files from " "settingspath."; if (cmd == "thresholdnotb") { os << "Trimbits are not loaded."; } os << "\n\t" << cmd << " [eV1] [eV2] [eV3] [(optional settings)]" "\n\t[Mythen3] Threshold in eV for each counter. It loads trim " "files from settingspath. An energy of -1 will pick up values " " from detector."; if (cmd == "thresholdnotb") { os << "Trimbits are not loaded."; } os << '\n'; } else if (action == defs::GET_ACTION) { if (cmd == "thresholdnotb") { throw RuntimeError("cannot get"); } if (!args.empty()) { WrongNumberOfParameters(0); } defs::detectorType type = det->getDetectorType().squash(); if (type == defs::EIGER) { auto t = det->getThresholdEnergy(std::vector{det_id}); os << OutString(t) << '\n'; } else if (type == defs::MYTHEN3) { auto t = det->getAllThresholdEnergy(std::vector{det_id}); os << OutString(t) << '\n'; } else { throw RuntimeError("Not implemented for this detector\n"); } } else if (action == defs::PUT_ACTION) { defs::detectorType type = det->getDetectorType().squash(); if (type == defs::EIGER && args.size() != 1 && args.size() != 2) { WrongNumberOfParameters(1); } if (type == defs::MYTHEN3 && (args.size() < 1 || args.size() > 4)) { WrongNumberOfParameters(1); } bool trimbits = (cmd == "thresholdnotb") ? false : true; std::array energy = {StringTo(args[0]), 0, 0}; energy[1] = energy[0]; energy[2] = energy[0]; defs::detectorSettings sett = defs::STANDARD; // check if argument has settings or get it if (args.size() == 2 || args.size() == 4) { sett = StringTo(args[args.size() - 1]); } else { sett = det->getSettings(std::vector{det_id}) .tsquash("Inconsistent settings between detectors"); } // get other threshold values if (args.size() > 2) { energy[1] = StringTo(args[1]); energy[2] = StringTo(args[2]); } switch (type) { case defs::EIGER: det->setThresholdEnergy(energy[0], sett, trimbits, std::vector{det_id}); break; case defs::MYTHEN3: det->setThresholdEnergy(energy, sett, trimbits, std::vector{det_id}); break; default: throw RuntimeError("Not implemented for this detector\n"); } os << ToString(args) << '\n'; } else { throw RuntimeError("Unknown action"); } return os.str(); } std::string Caller::trimen(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "[trim_ev1] [trim_Ev2 (optional)] [trim_ev3 (optional)] " "...\n\t[Eiger][Mythen3] list of trim energies, where " "corresponding default trim files exist in corresponding trim " "folders." << '\n'; } else if (action == defs::GET_ACTION) { if (!args.empty()) { WrongNumberOfParameters(0); } auto t = det->getTrimEnergies(std::vector{det_id}); os << OutString(t) << '\n'; } else if (action == defs::PUT_ACTION) { std::vector t(args.size()); if (!args.empty()) { for (size_t i = 0; i < t.size(); ++i) { t[i] = StringTo(args[i]); } } det->setTrimEnergies(t, std::vector{det_id}); os << ToString(args) << '\n'; } else { throw RuntimeError("Unknown action"); } return os.str(); } std::string Caller::badchannels(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "[fname|none|0]\n\t[Gotthard2][Mythen3] Sets the bad channels " "(from file of bad channel numbers) to be masked out. None or 0 " "unsets all the badchannels.\n\t[Mythen3] Also does trimming" << '\n'; } else if (action == defs::GET_ACTION) { if (args.size() != 1) { WrongNumberOfParameters(1); } det->getBadChannels(args[0], std::vector{det_id}); os << "successfully retrieved" << '\n'; } else if (action == defs::PUT_ACTION) { bool parse = false; if (args.size() == 0) { WrongNumberOfParameters(1); } else if (args.size() == 1) { if (args[0] == "none" || args[0] == "0") { det->setBadChannels(std::vector{}, std::vector{det_id}); } else if (args[0].find(".") != std::string::npos) { det->setBadChannels(args[0], std::vector{det_id}); } else { parse = true; } } // parse multi args or single one with range or single value if (parse || args.size() > 1) { // get channels auto list = getChannelsFromStringList(args); det->setBadChannels(list, std::vector{det_id}); } os << "successfully loaded" << '\n'; } else { throw RuntimeError("Unknown action"); } return os.str(); } std::string Caller::udp_srcip(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "[x.x.x.x] or auto\n\tIp address of the detector (source) udp " "interface. Must be same subnet as destination udp " "ip.\n\t[Eiger] Set only for 10G. For 1G, detector will replace " "with its own DHCP IP address. \n\tOne can also set this to " "'auto' for 1 GbE data and virtual detectors. It will set to IP " "of detector." << '\n'; } else if (action == defs::GET_ACTION) { auto t = det->getSourceUDPIP(std::vector{det_id}); if (!args.empty()) { WrongNumberOfParameters(0); } os << OutString(t) << '\n'; } else if (action == defs::PUT_ACTION) { if (args.size() != 1) { WrongNumberOfParameters(1); } IpAddr val; if (args[0] == "auto") { val = getSrcIpFromAuto(); LOG(logINFO) << "Setting udp_srcip of detector " << det_id << " to " << val; } else { val = IpAddr(args[0]); } det->setSourceUDPIP(val, std::vector{det_id}); os << val << '\n'; } else { throw RuntimeError("Unknown action"); } return os.str(); } std::string Caller::udp_srcip2(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "[x.x.x.x] or auto\n\t[Jungfrau][Moench][Gotthard2] Ip address " "of the " "detector (source) udp interface 2. Must be same subnet as " "destination udp ip2.\n\t [Jungfrau][Moench] top half or inner " "interface\n\t [Gotthard2] veto debugging. \n\tOne can also set " "this to 'auto' for 1 GbE data and virtual detectors. It will " "set to IP of detector." << '\n'; } else if (action == defs::GET_ACTION) { auto t = det->getSourceUDPIP2(std::vector{det_id}); if (!args.empty()) { WrongNumberOfParameters(0); } os << OutString(t) << '\n'; } else if (action == defs::PUT_ACTION) { if (args.size() != 1) { WrongNumberOfParameters(1); } IpAddr val; if (args[0] == "auto") { val = getSrcIpFromAuto(); LOG(logINFO) << "Setting udp_srcip2 of detector " << det_id << " to " << val; } else { val = IpAddr(args[0]); } det->setSourceUDPIP2(val, std::vector{det_id}); os << val << '\n'; } else { throw RuntimeError("Unknown action"); } return os.str(); } std::string Caller::udp_dstip(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "[x.x.x.x] or auto\n\tIp address of the receiver (destination) " "udp interface. If 'auto' used, then ip is set to ip of " "rx_hostname." << '\n'; } else if (action == defs::GET_ACTION) { auto t = det->getDestinationUDPIP(std::vector{det_id}); if (!args.empty()) { WrongNumberOfParameters(0); } os << OutString(t) << '\n'; } else if (action == defs::PUT_ACTION) { if (args.size() != 1) { WrongNumberOfParameters(1); } if (args[0] == "auto") { auto val = getDstIpFromAuto(); LOG(logINFO) << "Setting udp_dstip of detector " << det_id << " to " << val; det->setDestinationUDPIP(val, std::vector{det_id}); os << val << '\n'; } else { auto val = IpAddr(args[0]); det->setDestinationUDPIP(val, std::vector{det_id}); os << args.front() << '\n'; } } else { throw RuntimeError("Unknown action"); } return os.str(); } std::string Caller::udp_dstip2(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "[x.x.x.x] or auto\n\t[Jungfrau][Moench][Gotthard2] Ip address " "of the " "receiver (destination) udp interface 2. If 'auto' used, then ip " "is set to ip of rx_hostname.\n\t[Jungfrau][Moench] bottom half " "\n\t[Gotthard2] veto debugging. " << '\n'; } else if (action == defs::GET_ACTION) { auto t = det->getDestinationUDPIP2(std::vector{det_id}); if (!args.empty()) { WrongNumberOfParameters(0); } os << OutString(t) << '\n'; } else if (action == defs::PUT_ACTION) { if (args.size() != 1) { WrongNumberOfParameters(1); } if (args[0] == "auto") { auto val = getDstIpFromAuto(); LOG(logINFO) << "Setting udp_dstip2 of detector " << det_id << " to " << val; det->setDestinationUDPIP2(val, std::vector{det_id}); os << val << '\n'; } else { auto val = IpAddr(args[0]); det->setDestinationUDPIP2(val, std::vector{det_id}); os << args.front() << '\n'; } } else { throw RuntimeError("Unknown action"); } return os.str(); } std::string Caller::rx_hostname(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "[hostname or ip address]\n\t" "[hostname or ip address]:[tcp port]\n\t" "[hostname1]:[tcp_port1]+[hostname2]:[tcp_port2]+\n\t" "Receiver hostname or IP. If port included, then the receiver " "tcp port.\n\t" "Used for TCP control communication between client and receiver " "to configure receiver. Also updates receiver with detector " "parameters. Also resets any prior receiver property (not on " "detector). " << '\n'; } else if (action == defs::GET_ACTION) { if (!args.empty()) { WrongNumberOfParameters(0); } auto t = det->getRxHostname(std::vector{det_id}); os << OutString(t) << '\n'; } else if (action == defs::PUT_ACTION) { if (args.size() < 1) { WrongNumberOfParameters(1); } // multiple arguments if (args.size() > 1) { // multiple in mulitple if (args[0].find('+') != std::string::npos) { throw RuntimeError( "Cannot add multiple receivers at module level"); } if (det_id != -1) { throw RuntimeError( "Cannot add multiple receivers at module level"); } det->setRxHostname(args); os << ToString(args) << '\n'; } // single argument else { // multiple receivers concatenated with + if (args[0].find('+') != std::string::npos) { if (det_id != -1) { throw RuntimeError( "Cannot add multiple receivers at module level"); } auto t = split(args[0], '+'); det->setRxHostname(t); os << ToString(t) << '\n'; } // single receiver else { det->setRxHostname(args[0], std::vector{det_id}); os << ToString(args) << '\n'; } } } else { throw RuntimeError("Unknown action"); } return os.str(); } std::string Caller::rx_zmqip(int action) { std::string helpMessage = "\n\t[deprecated] The receiver zmq socket (publisher) will " "listen to all interfaces ('tcp://0.0.0.0:[port]'to all interfaces " "(from v9.0.0). This command does nothing and will be removed " "(from v10.0.0). This change makes no difference to the user.\n"; std::ostringstream os; if (action == defs::HELP_ACTION) { os << helpMessage << '\n'; } else if (action == defs::GET_ACTION) { os << ZMQ_PUBLISHER_IP << '\n'; } else if (action == defs::PUT_ACTION) { LOG(logWARNING) << helpMessage << '\n'; os << ZMQ_PUBLISHER_IP << '\n'; } else { throw RuntimeError("Unknown action"); } return os.str(); } std::string Caller::rx_roi(int action) { std::ostringstream os; std::string helpMessage = std::string("[xmin] [xmax] [ymin] [ymax]\n") + "\tDefines a single region of interest (ROI) in the receiver.\n" "\tFor example, to set a single ROI: 0 100 20 30\n\n" "\tTo specify multiple ROIs, use square brackets between ROIs and " "commas inside for each ROI. \n" "\tInside each bracket, no spaces allowed.\n\n" "\tIf you use semicolon (along with '['and ']' to separate rois), \n" "\tenclose the entire list in quotes.\n" "\tExamples:\n" "\t [0,100,0,100] [200,300,0,100]\n" "\t \"[0,100,0,100];[200,300,0,100]\"\n\n" "\tNotes:\n" "\t- ROIs can only be set at the multi-module level.\n" "\t- If multi module ROIs ends up with more then 1 ROI per UDP " "port or if they overlap each other, it will throw an error.\n" "\t- ROIs coordinates assume no gap pixels, even if they are enabled " "in gui.\n" "\t- To retrieve ROIs per port, specify the module ID when using the " "get command.\n" "\t- Use the command 'rx_clearroi' to clear all ROIs.\n" "\t- Changing the number of UDP interfaces will automatically clear " "the current ROIs.\n\n" "\t- Cannot be set for CTB or Xilinx CTB.\n"; if (action == defs::HELP_ACTION) { os << helpMessage; } else if (action == defs::GET_ACTION) { if (!args.empty()) { WrongNumberOfParameters(0); } auto t = det->getRxROI(det_id); os << ToString(t) << '\n'; } else if (action == defs::PUT_ACTION) { if (det_id != -1) { throw RuntimeError("Cannot set receiver ROI at module level"); } // Support multiple args with bracketed ROIs, or single arg with // semicolon-separated vector in quotes bool isVectorInput = std::all_of(args.begin(), args.end(), [](const std::string &a) { return a.find('[') != std::string::npos && a.find(']') != std::string::npos; }); std::vector rois; try { // single roi in previous format: [xmin,xmax,ymin,ymax] if (!isVectorInput) { auto t = parseRoi(args); rois.emplace_back(t); } // multiple roi or single roi with brackets // multiple roi: multiple args with bracketed ROIs, or single arg // with semicolon-bracketed Rois in quotes else { for (const auto &arg : args) { auto subRois = parseRoiVector(arg); rois.insert(rois.end(), subRois.begin(), subRois.end()); } } } catch (const std::exception &e) { throw RuntimeError("Could not parse ROI: Did you use spaces inside " "the brackets? Use sls_detector_help " + cmd + " to get the right syntax expected."); } det->setRxROI(rois); os << ToString(rois) << '\n'; } else { throw RuntimeError("Unknown action"); } return os.str(); } std::vector Caller::parseRoiVector(const std::string &input) { std::vector rois; std::stringstream ss(input); std::string token; while (std::getline(ss, token, ']')) { // remove spaces and semicolons token.erase( std::remove_if(token.begin(), token.end(), [](char c) { return std::isspace(c) || c == ';'; }), token.end()); if (token.empty()) continue; if (token.front() != '[') { throw RuntimeError("Each ROI must be enclosed in square brackets: " "[xmin,xmax,ymin,ymax]"); } token = token.substr(1, token.size() - 1); // remove brackets std::vector parts; std::stringstream inner(token); std::string num; while (std::getline(inner, num, ',')) { parts.push_back(num); } auto roi = parseRoi(parts); rois.emplace_back(roi); } return rois; } defs::ROI Caller::parseRoi(const std::vector &parts) { if (parts.size() != 2 && parts.size() != 4) { throw RuntimeError( "Could not parse ROI. A ROI must have 2 or 4 integers"); } defs::ROI roi; roi.xmin = StringTo(parts[0]); roi.xmax = StringTo(parts[1]); if (parts.size() == 4) { roi.ymin = StringTo(parts[2]); roi.ymax = StringTo(parts[3]); } return roi; } std::string Caller::ratecorr(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "[n_rate (in ns)]\n\t[Eiger] Dead time correction constant in " "ns. -1 will set to default tau of settings from trimbit file. 0 " "will unset rate correction." << '\n'; } else if (action == defs::GET_ACTION) { if (!args.empty()) { WrongNumberOfParameters(0); } auto t = det->getRateCorrection(std::vector{det_id}); os << OutString(t) << '\n'; } else if (action == defs::PUT_ACTION) { if (args.size() != 1) { WrongNumberOfParameters(1); } int tau = StringTo(args[0]); if (tau == -1) { det->setDefaultRateCorrection(std::vector{det_id}); auto t = det->getRateCorrection(std::vector{det_id}); os << OutString(t) << '\n'; } else { auto t = StringTo(args[0], "ns"); det->setRateCorrection(t, std::vector{det_id}); os << args.front() << "ns\n"; } } else { throw RuntimeError("Unknown action"); } return os.str(); } std::string Caller::burstmode(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "[burst_internal or 0, burst_external or 1, cw_internal or 2, " "cw_external or 3]\n\t[Gotthard2] Default is burst_internal " "type. Also changes clkdiv 2, 3, 4" << '\n'; } else { if (action == defs::GET_ACTION) { if (!args.empty()) { WrongNumberOfParameters(0); } auto t = det->getBurstMode(std::vector{det_id}); os << OutString(t) << '\n'; } else if (action == defs::PUT_ACTION) { if (args.size() != 1) { WrongNumberOfParameters(1); } defs::burstMode t; try { int ival = StringTo(args[0]); switch (ival) { case 0: t = defs::BURST_INTERNAL; break; case 1: t = defs::BURST_EXTERNAL; break; case 2: t = defs::CONTINUOUS_INTERNAL; break; case 3: t = defs::CONTINUOUS_EXTERNAL; break; default: throw RuntimeError("Unknown burst mode " + args[0]); } } catch (...) { t = StringTo(args[0]); } det->setBurstMode(t, std::vector{det_id}); os << ToString(t) << '\n'; // no args to convert 0,1,2 as well } else { throw RuntimeError("Unknown action"); } } return os.str(); } std::string Caller::vetostream(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "[none|lll|10gbe|...]\n\t[Gotthard2] Enable or disable the 2 " "veto streaming interfaces available. Can include more than one " "interface. \n\tDefault: none. lll (low latency link) is the " "default interface to work with. \n\t10GbE is for debugging and " "also enables second interface in receiver for listening to veto " "packets (writes a separate file if writing enabled). Also " "restarts client and receiver zmq sockets if zmq streaming " "enabled." << '\n'; } else if (action == defs::GET_ACTION) { if (!args.empty()) { WrongNumberOfParameters(0); } auto t = det->getVetoStream(std::vector{det_id}); os << OutString(t) << '\n'; } else if (action == defs::PUT_ACTION) { if (args.empty()) { WrongNumberOfParameters(1); } defs::streamingInterface interface = defs::streamingInterface::NONE; for (const auto &arg : args) { if (arg == "none") { if (args.size() > 1) { throw RuntimeError( std::string( "cannot have other arguments with 'none'. args: ") + ToString(args)); } break; } interface = interface | (StringTo(arg)); } det->setVetoStream(interface, std::vector{det_id}); os << ToString(interface) << '\n'; } else { throw RuntimeError("Unknown action"); } return os.str(); } std::string Caller::counters(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "[i0] [i1] [i2]... \n\t[Mythen3] List of counters indices " "enabled. Each element in list can be 0 - 2 and must be non " "repetitive. Enabling counters sets vth dacs to remembered " "values and disabling sets them to disabled values." << '\n'; } else if (action == defs::GET_ACTION) { if (!args.empty()) { WrongNumberOfParameters(0); } auto mask = det->getCounterMask(std::vector{det_id}).squash(-1); os << ToString(getSetBits(mask)) << '\n'; } else if (action == defs::PUT_ACTION) { if (args.empty()) { WrongNumberOfParameters(1); } if (std::any_of(args.cbegin(), args.cend(), [](std::string s) { return (StringTo(s) < 0 || StringTo(s) > 2); })) { throw RuntimeError("Invalid counter indices list. Example: 0 1 2"); } // convert vector to counter enable mask uint32_t mask = 0; for (size_t i = 0; i < args.size(); ++i) { int val = StringTo(args[i]); // already enabled earlier if (mask & (1 << val)) { std::ostringstream oss; oss << "Duplicate counter values (" << val << ") in arguments"; throw RuntimeError(oss.str()); } mask |= (1 << val); } det->setCounterMask(mask, std::vector{det_id}); os << ToString(args) << '\n'; } else { throw RuntimeError("Unknown action"); } return os.str(); } std::string Caller::samples(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "[n_samples]\n\t[Ctb][Xilinx_Ctb] Number of samples (analog, " "digitial and " "transceiver) expected.\n" << '\n'; } else if (action == defs::GET_ACTION) { if (!args.empty()) { WrongNumberOfParameters(0); } auto a = det->getNumberOfAnalogSamples(std::vector{det_id}); // get also digital samples for ctb and compare with analog auto det_type = det->getDetectorType().squash(defs::GENERIC); if (det_type == defs::CHIPTESTBOARD || det_type == defs::XILINX_CHIPTESTBOARD) { auto d = det->getNumberOfDigitalSamples(std::vector{det_id}); auto t = det->getNumberOfTransceiverSamples(std::vector{det_id}); int as = a.squash(-1); int ds = d.squash(-1); int ts = t.squash(-1); if (as == -1 || ds == -1 || ts == -1 || as != ds || as != ts) { // check if a == d? throw RuntimeError( "Different samples. Use asamples, dsamples or tsamples."); } } os << OutString(a) << '\n'; } else if (action == defs::PUT_ACTION) { if (args.size() != 1) { WrongNumberOfParameters(1); } det->setNumberOfAnalogSamples(StringTo(args[0]), std::vector{det_id}); // set also digital samples for ctb auto det_type = det->getDetectorType().squash(defs::GENERIC); if (det_type == defs::CHIPTESTBOARD || det_type == defs::XILINX_CHIPTESTBOARD) { det->setNumberOfDigitalSamples(StringTo(args[0]), std::vector{det_id}); det->setNumberOfTransceiverSamples(StringTo(args[0]), std::vector{det_id}); } os << args.front() << '\n'; } else { throw RuntimeError("Unknown action"); } return os.str(); } std::string Caller::slowadc(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "[n_channel (0-7 for channel]\n\t[Ctb] Slow " "ADC channel in mV" << '\n'; } else if (action == defs::GET_ACTION) { if (args.size() != 1) { WrongNumberOfParameters(0); } int nchan = StringTo(args[0]); if (nchan < 0 || nchan > 7) { throw RuntimeError("Unknown adc argument " + args[0]); } auto t = det->getSlowADC( static_cast(nchan + defs::SLOW_ADC0), std::vector{det_id}); Result result(t.size()); for (unsigned int i = 0; i < t.size(); ++i) { result[i] = t[i] / 1000.00; } os << OutString(result) << " mV\n"; } else if (action == defs::PUT_ACTION) { throw RuntimeError("cannot put"); } else { throw RuntimeError("Unknown action"); } return os.str(); } std::string Caller::patwaittime(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "[0-6] [n_clk] \n\t[Ctb][Mythen3][Xilinx Ctb] Wait time in clock " "cycles for the loop provided.\n\t[Mythen3] Level options: 0-3 " "only." << '\n'; return os.str(); } // parse level bool deprecated_cmd = GetLevelAndInsertIntoArgs("patwaittime"); int level = 0; try { if (args.size() > 0) level = StringTo(args[0]); } catch (const std::exception &e) { LOG(logERROR) << "Could not scan level."; throw; } if (!deprecated_cmd && args.size() >= 1) os << args[0] << ' '; if (action == defs::GET_ACTION) { if (args.size() != 1 && args.size() != 2) WrongNumberOfParameters(1); // with time unit if (args.size() == 2) { auto t = det->getPatternWaitInterval(level, std::vector{det_id}); os << OutString(t, args[1]) << '\n'; } // in clocks else { auto t = det->getPatternWaitClocks(level, std::vector{det_id}); os << OutString(t) << '\n'; } } else if (action == defs::PUT_ACTION) { if (args.size() != 2 && args.size() != 3) WrongNumberOfParameters(2); // clocks (all digits) if (args.size() == 2 && std::all_of(args[1].begin(), args[1].end(), ::isdigit)) { uint64_t waittime = StringTo(args[1]); det->setPatternWaitClocks(level, waittime, std::vector{det_id}); os << waittime << '\n'; } // time else { time::ns converted_time{0}; try { if (args.size() == 2) { std::string tmp_time(args[1]); std::string unit = RemoveUnit(tmp_time); converted_time = StringTo(tmp_time, unit); } else { converted_time = StringTo(args[1], args[2]); } } catch (...) { throw RuntimeError("Could not convert argument to time::ns"); } det->setPatternWaitInterval(level, converted_time, std::vector{det_id}); os << args[1]; if (args.size() == 3) os << ' ' << args[2]; os << '\n'; } } else { throw RuntimeError("Unknown action"); } return os.str(); } std::string Caller::rx_dbitlist(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "[all] or [none] or [i0] [i1] [i2]... \n\t[Ctb] List of digital " "signal bits enabled and rearranged according to the signals " "(all samples of each signal is put together). If 'all' is used " "instead of a list, all digital bits (64) enabled. Each element " "in list can be 0 - 63 and must be non repetitive. The option " "'none' will still spit out all data as is from the detector, " "but without rearranging it. Please note that when using the " "receiver list, the data size will be bigger if the number of " "samples is not divisible by 8 as every signal bit is padded to " "the next byte when combining all the samples in the receiver." << '\n'; } else if (action == defs::GET_ACTION) { if (!args.empty()) { WrongNumberOfParameters(0); } auto t = det->getRxDbitList(std::vector{det_id}); os << OutString(t) << '\n'; } else if (action == defs::PUT_ACTION) { if (args.empty()) { WrongNumberOfParameters(1); } std::vector t; if (args[0] == "all") { t.resize(64); for (unsigned int i = 0; i < 64; ++i) { t[i] = i; } } // 'none' option already covered as t is empty by default else if (args[0] != "none") { unsigned int ntrim = args.size(); t.resize(ntrim); for (unsigned int i = 0; i < ntrim; ++i) { t[i] = StringTo(args[i]); } } det->setRxDbitList(t, std::vector{det_id}); os << ToString(args) << '\n'; } else { throw RuntimeError("Unknown action"); } return os.str(); } std::string Caller::rx_jsonaddheader(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "[key1] [value1] [key2] [value2]...[keyn] [valuen]\n\tAdditional " "json header to be streamed out from receiver via zmq. Default " "is empty. Max 20 characters for each key/value. Use only if to " "be processed by an intermediate user process listening to " "receiver zmq packets. Empty value deletes header. " << '\n'; } else if (action == defs::GET_ACTION) { if (!args.empty()) { WrongNumberOfParameters(0); } auto t = det->getAdditionalJsonHeader(std::vector{det_id}); os << OutString(t) << '\n'; } else if (action == defs::PUT_ACTION) { // arguments can be empty std::map json; for (size_t i = 0; i < args.size(); i = i + 2) { // last value is empty if (i + 1 >= args.size()) { json[args[i]] = ""; } else { json[args[i]] = args[i + 1]; } } det->setAdditionalJsonHeader(json, std::vector{det_id}); os << ToString(json) << '\n'; } else { throw RuntimeError("Unknown action"); } return os.str(); } std::string Caller::execcommand(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "[command]\n\tExecutes command on detector server console." << '\n'; } else if (action == defs::GET_ACTION) { throw RuntimeError("Cannot get."); } else if (action == defs::PUT_ACTION) { std::string command; for (auto &i : args) { command += (i + ' '); } auto t = det->executeCommand(command, std::vector{det_id}); os << OutString(t) << '\n'; } else { throw RuntimeError("Unknown action"); } return os.str(); } std::string Caller::dacvalues(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "[(optional unit) mV] \n\tGets the values for every " "dac for this detector." << '\n'; } else if (action == defs::GET_ACTION) { bool mv = false; if (args.size() == 1) { if ((args[0] != "mv") && (args[0] != "mV")) { throw RuntimeError("Unknown argument " + args[0] + ". Did you mean mV?"); } mv = true; } else if (args.size() > 1) { WrongNumberOfParameters(1); } auto t = det->getDacList(); auto names = det->getDacNames(); auto name_it = names.begin(); os << '['; auto it = t.cbegin(); os << ToString(*name_it++) << ' '; os << OutString(det->getDAC(*it++, mv, std::vector{det_id})) << (!args.empty() ? " mV" : ""); while (it != t.cend()) { os << ", " << ToString(*name_it++) << ' '; os << OutString(det->getDAC(*it++, mv, std::vector{det_id})) << (!args.empty() ? " mV" : ""); } os << "]\n"; } else if (action == defs::PUT_ACTION) { throw RuntimeError("Cannot put"); } else { throw RuntimeError("Unknown action"); } return os.str(); } std::string Caller::currentsource(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "\n\t[0|1]\n\t\t[Gotthard2] Enable or disable current source. " "Default " "is disabled.\n\t[0|1] [fix|nofix] [select source] [(only for " "chipv1.1)normal|low]\n\t\t[Jungfrau] Disable or enable current " "source with some parameters. The select source is 0-63 for " "chipv1.0 and a 64 bit mask for chipv1.1. To disable, one needs " "only one argument '0'." << '\n'; } else if (action == defs::GET_ACTION) { if (args.size() != 0) { WrongNumberOfParameters(0); } auto t = det->getCurrentSource(std::vector{det_id}); os << OutString(t) << '\n'; } else if (action == defs::PUT_ACTION) { if (args.size() == 1) { det->setCurrentSource( defs::currentSrcParameters(StringTo(args[0]))); } else if (args.size() >= 3) { // scan fix bool fix = false; if (args[1] == "fix") { fix = true; } else if (args[1] == "nofix") { fix = false; } else { throw RuntimeError("Invalid argument: " + args[1] + ". Did you mean fix or nofix?"); } if (args.size() == 3) { det->setCurrentSource(defs::currentSrcParameters( fix, StringTo(args[2]))); } else if (args.size() == 4) { bool normalCurrent = false; if (args[3] == "normal") { normalCurrent = true; } else if (args[3] == "low") { normalCurrent = false; } else { throw RuntimeError("Invalid argument: " + args[3] + ". Did you mean normal or low?"); } det->setCurrentSource(defs::currentSrcParameters( fix, StringTo(args[2]), normalCurrent)); } else { throw RuntimeError( "Invalid number of parareters for this command."); } } else { throw RuntimeError( "Invalid number of parareters for this command."); } os << ToString(args) << '\n'; } else { throw RuntimeError("Unknown action"); } return os.str(); } std::string Caller::gaincaps(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "[cap1, cap2, ...]\n\t[Mythen3] gain, options: C10pre, C15sh, " "C30sh, C50sh, C225ACsh, C15pre" << '\n'; } else if (action == defs::GET_ACTION) { if (!args.empty()) WrongNumberOfParameters(0); auto tmp = det->getGainCaps(); Result csr; for (auto val : tmp) { if (val) csr.push_back(static_cast(val)); } os << OutString(csr) << '\n'; } else if (action == defs::PUT_ACTION) { if (args.size() < 1) { WrongNumberOfParameters(1); } int caps = 0; for (const auto &arg : args) { if (arg != "0") caps |= StringTo(arg); } det->setGainCaps(caps); os << OutString(args) << '\n'; } else { throw RuntimeError("Unknown action"); } return os.str(); } std::string Caller::sleep(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "[duration] [(optional unit) ns|us|ms|s]\n\tSleep for duration. " "Mainly for config files for firmware developers." "Default unit is s." << '\n'; } else if (action == defs::GET_ACTION) { throw RuntimeError("Cannot get."); } else if (action == defs::PUT_ACTION) { if (args.size() != 1 && args.size() != 2) { WrongNumberOfParameters(1); } time::ns converted_time{0}; try { if (args.size() == 1) { std::string tmp_time(args[0]); std::string unit = RemoveUnit(tmp_time); converted_time = StringTo(tmp_time, unit); } else { converted_time = StringTo(args[0], args[1]); } } catch (...) { throw RuntimeError("Could not convert argument to time::ns"); } std::this_thread::sleep_for(converted_time); os << "for " << ToString(converted_time) << " completed" << '\n'; } else { throw RuntimeError("Unknown action"); } return os.str(); } std::string Caller::define_reg(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "[Ctb][Xilinx Ctb]" "\n\t[reg name] [reg address]" "\n\n\tSets a user defined register in shared memory. The name " "can be upto 32 characters long." "\n\teg." "\n\tsls_detector_put define_reg test_reg 0x200" "\n\n\tOne can retrieve the address using the name and vice " "versa." "\n\teg." "\n\tsls_detector_get define_reg test_reg" "\n\tsls_detector_get define_reg 0x200" "\n\n\tOne can then use this user-defined name in other commands " "instead of hard coding the address such as for reg, setbit, " "clearbit and getbit commands." "\n\teg." "\n\tsls_detector_put reg test_reg 0x1" "\n\tsls_detector_put setbit test_reg 2" "\n\tsls_detector_put clearbit test_reg 2" "\n\tsls_detector_get getbit test_reg 2" << '\n'; return os.str(); } if (det_id != -1) { throw RuntimeError("Cannot use define at module level. Use the default " "multi-module level"); } if (action == defs::GET_ACTION) { if (args.size() != 1) { WrongNumberOfParameters(1); } // get name from address if (is_hex_or_dec_uint(args[0])) { auto addr = parseRegisterAddress(args[0]); auto t = det->getRegisterName(addr); os << t << '\n'; } // get address from name else { auto t = det->getRegisterAddress(args[0]); os << t.str() << '\n'; } } else if (action == defs::PUT_ACTION) { if (args.size() != 2) { WrongNumberOfParameters(2); } auto name = args[0]; auto addr = parseRegisterAddress(args[1]); det->setRegisterDefinition(name, addr); os << "addr " << name << ' ' << addr.str() << '\n'; } else { throw RuntimeError("Unknown action"); } return os.str(); } std::string Caller::define_bit(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "[Ctb][Xilinx Ctb]" "\n\t[bit name] [regiser name/address] [bit position]" "\n\n\tSets a user defined bit name in shared memory " "representing the register address and the bit position. The " "address can be named prior using define_reg command. The name " "can be upto 32 characters long." "\n\teg." "\n\tsls_detector_put define_bit test_bit test_reg 2" "\n\tsls_detector_put define_bit test_bit 0x200 2" "\n\n\tOne can retrieve the bit address using the name and vice " "versa using both register name or address and bit position." "\n\teg." "\n\tsls_detector_get define_bit test_bit" "\n\tsls_detector_get define_bit test_reg 2" "\n\tsls_detector_get define_bit 0x200 2" "\n\n\tOne can then use this user-defined name in other commands " "such as for setbit, clearbit and getbit commands. When using " "bit names, please dont use register name or address as bit name " "is already tied to a specific register." "\n\teg." "\n\tsls_detector_put setbit test_bit" "\n\tsls_detector_put clearbit test_bit" "\n\tsls_detector_get getbit test_bit" << '\n'; return os.str(); } if (det_id != -1) { throw RuntimeError("Cannot use define at module level. Use the default " "multi-module level"); } if (action == defs::GET_ACTION) { // get position from name if (args.size() == 1) { auto t = det->getBitAddress(args[0]); os << det->toRegisterNameBitString(t) << '\n'; } // get name from position and address else if (args.size() == 2) { auto addr = parseBitAddress(args[0], args[1]); auto t = det->getBitName(addr); os << t << '\n'; } else { WrongNumberOfParameters(1); } } else if (action == defs::PUT_ACTION) { if (args.size() != 3) { WrongNumberOfParameters(3); } if (!is_int(args[2])) { throw RuntimeError("Bit position must be an integer value."); } auto name = args[0]; auto addr = parseBitAddress(args[1], args[2]); det->setBitDefinition(name, addr); os << ToString(args) << '\n'; } else { throw RuntimeError("Unknown action"); } return os.str(); } std::string Caller::definelist_reg(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "List of user-defined register definitions in shared memory." << '\n'; } else if (action == defs::PUT_ACTION) { throw RuntimeError("cannot put"); } else if (action == defs::GET_ACTION) { if (!args.empty()) { WrongNumberOfParameters(0); } auto t = det->getRegisterDefinitions(); os << '\n' << ToString(t) << '\n'; } else { throw RuntimeError("Unknown action"); } return os.str(); } std::string Caller::definelist_bit(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "List of user-defined bit definitions in shared memory." << '\n'; } else if (action == defs::PUT_ACTION) { throw RuntimeError("cannot put"); } else if (action == defs::GET_ACTION) { if (!args.empty()) { WrongNumberOfParameters(0); } auto t = det->getBitDefinitions(); os << "\n["; for (const auto &[key, val] : t) { os << key << ": "; os << det->toRegisterNameBitString(val); if (&key != &t.rbegin()->first) { os << ", "; } os << '\n'; } os << "]\n"; } else { throw RuntimeError("Unknown action"); } return os.str(); } std::string Caller::reg(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "[address] [32 bit value][(optional)--validate]" "\n\tReads/writes to a 32 bit register in hex." "\n\tAdvanced Function!\n\tGoes to stop server. Hence, can be " "called while calling blocking acquire()." "\n\t\t Use --validate to enforce validation when writing to " "register." "\n\t[Eiger] +0x100 for only left, +0x200 for only right." "\n\t[Ctb][Xilinx_Ctb] Address can also be a user-defined name " "that was set previously using the define command." "\n\t\teg." "\n\t\tsls_detector_put reg 0x200 0xFFF --validate" "\n\t\tsls_detector_get reg test_reg" "\n\t\tsls_detector_put reg test_reg 0xFF" << '\n'; } else { if (action == defs::PUT_ACTION) { if (args.size() < 2 || args.size() > 3) { WrongNumberOfParameters(2); } auto validate = parseValidate(); auto addr = getRegisterAddress(args[0]); auto val = parseRegisterValue(args[1]); det->writeRegister(addr, val, validate, std::vector{det_id}); os << addr.str() << " " << val.str() << '\n'; } else if (action == defs::GET_ACTION) { if (args.size() != 1) { WrongNumberOfParameters(1); } auto addr = getRegisterAddress(args[0]); auto t = det->readRegister(addr, std::vector{det_id}); os << OutString(t) << '\n'; } else { throw RuntimeError("Unknown action"); } } return os.str(); } std::string Caller::getbit(int action) { return bitoperations(action); } std::string Caller::setbit(int action) { return bitoperations(action); } std::string Caller::clearbit(int action) { return bitoperations(action); } std::string Caller::bitoperations(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { if (cmd == "getbit") { os << "[reg address in hex] [bit index]\n\tGets bit in address." << '\n'; } else if (cmd == "setbit") { os << "[reg address in hex] [bit index]\n\tSets bit in " "address.\n\tUse --validate to force validation." << '\n'; } else if (cmd == "clearbit") { os << "[reg address in hex] [bit index]\n\tClears bit in " "address.\n\tUse --validate to force validation." << '\n'; } else { throw RuntimeError("Unknown command"); } os << "\n\t\t[Ctb][Xilinx_Ctb] Address or bit position can also be a " "user-defined name that was set previously using the define " "command. When using bit names, avoid register name/ address as " "the bit name is tied to a specific register already." "\n\n\teg." "\n\tsls_detector_get getbit 0x200 2" "\n\tsls_detector_get getbit test_reg 2" "\n\tsls_detector_get getbit test_bit" "\n\tsls_detector_put setbit 0x200 2" "\n\tsls_detector_put setbit test_reg 2" "\n\tsls_detector_put setbit test_bit" "\n\tsls_detector_put clearbit test_bit"; os << '\n'; } else { if (action != defs::GET_ACTION && action != defs::PUT_ACTION) { throw RuntimeError("Unknown action"); } auto validate = parseValidate(); auto addr = getBitAddress(); if (action == defs::GET_ACTION) { if (cmd == "setbit" || cmd == "clearbit") throw RuntimeError("Cannot get"); auto t = det->getBit(addr, std::vector{det_id}); os << OutString(t) << '\n'; } else { if (cmd == "getbit") throw RuntimeError("Cannot put"); if (cmd == "setbit") det->setBit(addr, validate, std::vector{det_id}); else if (cmd == "clearbit") det->clearBit(addr, validate, std::vector{det_id}); else throw RuntimeError("Unknown command"); os << ToString(args) << "\n"; } } return os.str(); } RegisterAddress Caller::parseRegisterAddress(const std::string &addr) const { try { return RegisterAddress(StringTo(addr)); } catch (const std::exception &e) { throw RuntimeError("Could not parse register address " + addr + ". Must be an integer value"); } } BitAddress Caller::parseBitAddress(const std::string &addr, const std::string &bitPos) const { auto address = getRegisterAddress(addr); uint32_t bitPosition = 0; // parse bit position if (!is_hex_or_dec_uint(bitPos)) { throw RuntimeError("Bit position must be an integer value."); } try { bitPosition = StringTo(bitPos); } catch (const std::exception &e) { throw RuntimeError("Could not parse bit position " + bitPos + ". Must be an integer value"); } return BitAddress(address, bitPosition); } RegisterValue Caller::parseRegisterValue(const std::string &addr) const { try { return RegisterValue(StringTo(addr)); } catch (const std::exception &e) { throw RuntimeError("Could not parse register value " + addr + ". Must be an integer value"); } } bool Caller::parseValidate() { auto it = std::find(args.begin(), args.end(), "--validate"); bool validate = (it != args.end()); // invalid argument (--options), throw if (!validate) { auto invalid_it = std::find_if(args.begin(), args.end(), [](const auto &s) { // only looks for the first characters return s.rfind("--", 0) == 0 && s != "--validate"; }); if (invalid_it != args.end()) { throw RuntimeError("Unknown option '" + *invalid_it + "'. Did you mean '--validate'?"); } } // --validate should be the last argument (remove it from args) else { if (it != args.end() - 1) { throw RuntimeError("'--validate' should be the last argument."); } args.pop_back(); } return validate; } RegisterAddress Caller::getRegisterAddress(const std::string &saddr) const { if (is_hex_or_dec_uint(saddr)) { return parseRegisterAddress(saddr); } return det->getRegisterAddress(saddr); } BitAddress Caller::getBitAddress() const { int args_size = args.size(); // address and bit position if (args_size == 2) { return parseBitAddress(args[0], args[1]); } // bit name if (args_size == 1) { return det->getBitAddress(args[0]); } throw RuntimeError("Invalid number of parameters for bit address."); } std::string Caller::dac(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { if (args.size() == 0) os << GetHelpDac(""); else os << args[0] << GetHelpDac(args[0]) << '\n'; return os.str(); } bool isCtb = false; auto detType = det->getDetectorType().squash(defs::GENERIC); if (detType == defs::CHIPTESTBOARD || detType == defs::XILINX_CHIPTESTBOARD) { isCtb = true; } if (action == defs::GET_ACTION) { auto index = parseDacIndex(0, isCtb); auto mV = parseMV(1); auto t = det->getDAC(index, mV, std::vector{det_id}); os << args[0] << ' ' << OutString(t) << (mV ? " mV" : "") << '\n'; } else if (action == defs::PUT_ACTION) { auto index = parseDacIndex(0, isCtb); if (args.size() < 2) { WrongNumberOfParameters(2); } auto val = StringTo(args[1]); auto mV = parseMV(2); det->setDAC(index, val, mV, std::vector{det_id}); os << args[0] << ' ' << args[1] << (mV ? " mV" : "") << '\n'; } else { throw RuntimeError("Unknown action"); } return os.str(); } defs::dacIndex Caller::parseDacIndex(int argIndex, bool isCtb) { if (argIndex >= (int)args.size()) { throw RuntimeError("Invalid arguments. DAC index is required."); } auto arg = args[argIndex]; if (isCtb) { // dac index if (is_int(arg)) { return StringTo(arg); } // dac name return det->getDacIndex(arg); } // not ctb if (is_int(arg)) { throw RuntimeError("DAC index is not supported for your detector. " "Please use dac name. Use daclist command to get " "the list of dac names for your detector."); } return StringTo(arg); } bool Caller::parseMV(int argIndex) { if (argIndex < (int)args.size()) { auto arg = args[argIndex]; if (arg != "mv" && arg != "mV") { throw RuntimeError("Unknown argument " + arg + ". Did you mean mV?"); } return true; } return false; } std::string Caller::powerdac(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "[powername][mV value]\n\t[Ctb][Xilinx Ctb] Controls the dac " "used for Power supply. Default names for powername are v_a, " "v_b, v_c, v_d, v_io, v_chip(v_chip only applies to Ctb, not " "Xilinx_Ctb). If custom names are assigned using the 'powername' " "command, those names could be used instead instead of the " "defaults. By default, all are set to minimum values. \n\t[Ctb] " "v_chip can also be queried to get the vchip dac value, although " "its rail cannot be enabled or disabled by the user. It is " "enabled by default. Its dac value is automatically updated " "whenever a power dac is modified. It is then set to the max of " "power dacs + 200mV." << '\n'; return os.str(); } auto detType = det->getDetectorType().squash(defs::GENERIC); if (detType != defs::CHIPTESTBOARD && detType != defs::XILINX_CHIPTESTBOARD) { throw RuntimeError("This command is only applicable for ChipTestBoard " "and Xilinx ChipTestBoard."); } if (det_id != -1) { throw RuntimeError("Cannot use powerdac at module level."); } auto index = parsePowerIndex(0); if (action == defs::GET_ACTION) { auto t = det->getPowerDAC(index); os << args[0] << ' ' << OutString(t) << '\n'; } else if (action == defs::PUT_ACTION) { if (args.size() != 2) { WrongNumberOfParameters(2); } auto val = StringTo(args[1]); det->setPowerDAC(index, val); os << args[0] << ' ' << args[1] << '\n'; } else { throw RuntimeError("Unknown action"); } return os.str(); } defs::dacIndex Caller::parsePowerIndex(int argIndex) { if (argIndex >= (int)args.size()) { throw RuntimeError("Invalid arguments. Power name is required."); } auto arg = args[argIndex]; // power default names if (is_int(arg) || arg == "v_a" || arg == "v_b" || arg == "v_c" || arg == "v_d" || arg == "v_io" || arg == "v_chip") { return StringTo(arg); } // power name auto names = det->getPowerNames(); auto it = std::find(names.begin(), names.end(), arg); if (it != names.end()) { return det->getPowerIndex(arg); } throw RuntimeError( "Unknown power name '" + arg + "'. Use 'powername' command to see defined power names."); } std::string Caller::power(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "[all|list of power names] [on|off]\n\t[Ctb][Xilinx Ctb] Enable " "or " "disable power rails. Power name can be all, v_a, v_b, v_c, v_d " "or " "v_io or any defines using 'powername'. If power name is set to " "'all', the command applies to all 'powers'. Enabling the power " "rails is in parallel, whereas retrieving the states of multiple " "power rails, they are queried sequentially " "(one after another), not in parallel." << '\n'; return os.str(); } auto detType = det->getDetectorType().squash(defs::GENERIC); if (detType != defs::CHIPTESTBOARD && detType != defs::XILINX_CHIPTESTBOARD) { throw RuntimeError("This command is only applicable for ChipTestBoard " "and Xilinx ChipTestBoard."); } // 'all' argument bool all = false; if (std::find(args.begin(), args.end(), "all") != args.end()) { all = true; } // number of args if (action == defs::GET_ACTION && args.size() != 1) WrongNumberOfParameters(1); if (all) { if (action == defs::PUT_ACTION && args.size() != 2) { WrongNumberOfParameters(2); } } else { if (args.size() < 1 || args.size() > 6) WrongNumberOfParameters(1); } if (action == defs::GET_ACTION) { if (!all) { auto t = det->isPowerEnabled(parsePowerIndex(0)); os << args[0] << ' ' << ToString(t, defs::OnOff) << '\n'; } else { // get each state and store in map std::map m; auto powerIndices = det->getPowerList(); for (const auto &index : powerIndices) { auto name = ToString(index); auto state = det->isPowerEnabled(index); m[name] = ToString(state, defs::OnOff); } if (m.empty()) { throw RuntimeError("Could not get power states."); } auto first = m.begin()->second; // if all the same, print in short form, else print all states if (std::all_of(m.begin(), m.end(), [&](const auto &p) { return p.second == first; })) { os << "all " << first << '\n'; } else { os << ToString(m) << '\n'; } } } else if (action == defs::PUT_ACTION) { // enable arg std::string lastArg = args.back(); if (lastArg != "on" && lastArg != "off") { throw RuntimeError("Last argument '" + lastArg + "' is enable. Options: 'on' or 'off'"); } bool enable = StringTo(lastArg, defs::OnOff); // power indices std::vector powerIndices; if (all) { powerIndices = det->getPowerList(); } else { // push back indices from command line for (size_t i = 0; i < args.size() - 1; ++i) { powerIndices.push_back(parsePowerIndex(i)); } } det->setPowerEnabled(powerIndices, enable); args.pop_back(); os << ToString(args) << ' ' << ToString(enable, defs::OnOff) << '\n'; } else { throw RuntimeError("Unknown action"); } return os.str(); } std::string Caller::powervalues(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { os << "\n\t\t[Ctb][Xilinx_Ctb] Get dac values of all powers if " "enabled, else '0'." << '\n'; return os.str(); } auto detType = det->getDetectorType().squash(defs::GENERIC); if (detType != defs::CHIPTESTBOARD && detType != defs::XILINX_CHIPTESTBOARD) { throw RuntimeError("This command is only applicable for ChipTestBoard " "and Xilinx ChipTestBoard."); } if (action == defs::GET_ACTION) { if (!args.empty()) { WrongNumberOfParameters(0); } auto t = det->getPowerList(); auto names = det->getPowerNames(); auto name_it = names.begin(); os << '['; auto it = t.cbegin(); while (it != t.cend()) { if (it != t.cbegin()) os << ", "; os << ToString(*name_it++) << ': ['; os << ToString(det->isPowerEnabled(*it), defs::OnOff) << ", "; os << det->getPowerDAC(*it) << " mV ]"; ++it; } os << "]" << '\n'; } else if (action == defs::PUT_ACTION) { throw RuntimeError("Cannot put"); } else { throw RuntimeError("Unknown action"); } return os.str(); } } // namespace sls