|
2 | 2 | /// \brief Utility that starts the ALICE Lowlevel Frontend (ALF) DIM server |
3 | 3 | /// |
4 | 4 | /// This file contains a bunch of classes that together form the server part of ALF. |
5 | | -/// It is single-threaded, because an earlier multi-threaded implementation ran into strange locking issues with DIM's |
6 | | -/// thread, and it was determined that it would cost less time to rewrite than to debug. |
7 | 5 | /// |
8 | 6 | /// The idea is that the DIM thread calls the RPC handler functions of the ProgramAliceLowlevelFrontendServer class. |
9 | 7 | /// These handlers then, depending on the RPC: |
10 | | -/// a) Execute the request immediately (such as for register reads and writes) |
11 | | -/// b) Put a corresponding command object in a lock-free thread-safe queue (such as for publish start/stop commands), |
12 | | -/// the mCommandQueue. The main thread of ProgramAliceLowlevelFrontendServer periodically takes commands from this |
13 | | -/// queue and handles them. |
| 8 | +/// a) Execute the request immediately (such as for register reads and writes) |
| 9 | +/// b) Put a corresponding command object in a lock-free thread-safe queue (such as for publish start/stop commands), |
| 10 | +/// the mCommandQueue. The main thread of ProgramAliceLowlevelFrontendServer periodically takes commands from this |
| 11 | +/// queue and handles them by starting or stopping a publish service. |
| 12 | +/// |
| 13 | +/// Decoupling the DIM thread from the main thread was necessary to prevent strange DIM locking issues on exit. |
14 | 14 | /// |
15 | 15 | /// \author Pascal Boeschoten ([email protected]) |
16 | 16 |
|
@@ -145,7 +145,7 @@ struct Service |
145 | 145 | std::unique_ptr<DimService> dimService; |
146 | 146 | }; |
147 | 147 |
|
148 | | -/// |
| 148 | +/// Thread-safe queue for commands |
149 | 149 | class CommandQueue |
150 | 150 | { |
151 | 151 | public: |
@@ -235,7 +235,7 @@ class ProgramAliceLowlevelFrontendServer: public AliceO2::Common::Program |
235 | 235 | for (auto link : links) { |
236 | 236 | getLogger() << "Initializing link " << link << endm; |
237 | 237 | LinkInfo linkInfo {serial, link}; |
238 | | - |
| 238 | + |
239 | 239 | // Object for generating DNS names |
240 | 240 | Alf::ServiceNames names(serial, link); |
241 | 241 |
|
@@ -422,6 +422,40 @@ class ProgramAliceLowlevelFrontendServer: public AliceO2::Common::Program |
422 | 422 | return true; |
423 | 423 | } |
424 | 424 |
|
| 425 | + static bool isLineComment(const std::string& line) |
| 426 | + { |
| 427 | + return line.find('#') == 0; |
| 428 | + } |
| 429 | + |
| 430 | + template <typename Iter> |
| 431 | + static std::vector<Sca::CommandData> linesToCommandDataVector(Iter begin, Iter end) |
| 432 | + { |
| 433 | + std::vector<Sca::CommandData> pairs; |
| 434 | + for (Iter i = begin; i != end; ++i) { |
| 435 | + const std::string& line = *i; |
| 436 | + if (!isLineComment(line)) { |
| 437 | + pairs.push_back(stringToScaCommandDataPair(line)); |
| 438 | + } |
| 439 | + } |
| 440 | + return pairs; |
| 441 | + } |
| 442 | + |
| 443 | + static Sca::CommandData stringToScaCommandDataPair(const std::string& string) |
| 444 | + { |
| 445 | + // The pairs are comma-separated, so we split them. |
| 446 | + std::vector<std::string> pair = split(string, scaPairSeparator().c_str()); |
| 447 | + if (pair.size() != 2) { |
| 448 | + getLogger() << "SCA command-data pair not formatted correctly: parts=" << pair.size() << " str='" << string << |
| 449 | + "'" << endm; |
| 450 | + BOOST_THROW_EXCEPTION( |
| 451 | + AlfException() << ErrorInfo::Message("SCA command-data pair not formatted correctly")); |
| 452 | + } |
| 453 | + Sca::CommandData commandData; |
| 454 | + commandData.command = convertHexString(pair[0]); |
| 455 | + commandData.data = convertHexString(pair[1]); |
| 456 | + return commandData; |
| 457 | + }; |
| 458 | + |
425 | 459 | /// RPC handler for register reads |
426 | 460 | static std::string registerRead(const std::string& parameter, BarSharedPtr channel) |
427 | 461 | { |
@@ -502,14 +536,11 @@ class ProgramAliceLowlevelFrontendServer: public AliceO2::Common::Program |
502 | 536 | // Convert command-data pair string sequence to binary format |
503 | 537 | size_t skip = 2; // Number of arguments to skip for the array |
504 | 538 | ServiceDescription::ScaSequence sca; |
505 | | - sca.commandDataPairs.resize(params.size() - skip); |
506 | | - for (size_t i = 0; (i + skip) < params.size(); ++i) { |
507 | | - sca.commandDataPairs[i] = stringToScaCommandDataPair(params[i + skip]); |
508 | | - } |
| 539 | + sca.commandDataPairs = linesToCommandDataVector(params.begin() + skip, params.end()); |
509 | 540 |
|
510 | 541 | auto command = std::make_unique<CommandQueue::Command>(); |
511 | 542 | command->start = true; |
512 | | - command->description.type = sca; |
| 543 | + command->description.type = std::move(sca); |
513 | 544 | command->description.dnsName = ServiceNames(linkInfo.serial, linkInfo.link).publishScaSequenceSubdir(dnsName); |
514 | 545 | command->description.interval = std::chrono::milliseconds(int64_t(b::lexical_cast<double>(interval) * 1000.0)); |
515 | 546 | command->description.linkInfo = linkInfo; |
@@ -593,59 +624,34 @@ class ProgramAliceLowlevelFrontendServer: public AliceO2::Common::Program |
593 | 624 | { |
594 | 625 | getLogger() << "SCA_SEQUENCE size=" << parameter.size() << " bytes" << endm; |
595 | 626 |
|
596 | | - // We first split on \n to get the pairs of SCA command and SCA data |
| 627 | + // We split on \n to get the pairs of SCA command and SCA data |
597 | 628 | auto lines = split(parameter, argumentSeparator()); |
| 629 | + auto commandDataPairs = linesToCommandDataVector(lines.begin(), lines.end()); |
| 630 | + |
598 | 631 | std::stringstream resultBuffer; |
599 | 632 | auto sca = Sca(*bar2, bar2->getCardType(), linkInfo.link); |
600 | 633 |
|
601 | | - for (const auto& line : lines) { |
602 | | - // Walk through the tokens, these should be the pairs (or comments). |
603 | | - if (isLineComment(line)) { |
604 | | - continue; |
605 | | - } else { |
606 | | - auto commandData = stringToScaCommandDataPair(line); |
607 | | - try { |
608 | | - sca.write(commandData); |
609 | | - auto result = sca.read(); |
610 | | - getLogger() << (b::format("cmd=0x%x data=0x%x result=0x%x") % commandData.command % commandData.data % |
611 | | - result.data).str() << endm; |
612 | | - resultBuffer << std::hex << result.data << '\n'; |
613 | | - } catch (const ScaException &e) { |
614 | | - // If an SCA error occurs, we stop executing the sequence of commands and return the results as far as we got |
615 | | - // them, plus the error message. |
616 | | - getLogger() << InfoLogger::InfoLogger::Error |
617 | | - << (b::format("SCA_SEQUENCE cmd=0x%x data=0x%x serial=%d link=%d error='%s'") % commandData.command |
618 | | - % commandData.data % linkInfo.serial % linkInfo.link % e.what()).str() << endm; |
619 | | - resultBuffer << e.what(); |
620 | | - break; |
621 | | - } |
| 634 | + for (const auto& commandData : commandDataPairs) { |
| 635 | + try { |
| 636 | + sca.write(commandData); |
| 637 | + auto result = sca.read(); |
| 638 | + getLogger() << (b::format("cmd=0x%x data=0x%x result=0x%x") % commandData.command % commandData.data % |
| 639 | + result.data).str() << endm; |
| 640 | + resultBuffer << std::hex << result.data << '\n'; |
| 641 | + } catch (const ScaException &e) { |
| 642 | + // If an SCA error occurs, we stop executing the sequence of commands and return the results as far as we got |
| 643 | + // them, plus the error message. |
| 644 | + getLogger() << InfoLogger::InfoLogger::Error |
| 645 | + << (b::format("SCA_SEQUENCE cmd=0x%x data=0x%x serial=%d link=%d error='%s'") % commandData.command |
| 646 | + % commandData.data % linkInfo.serial % linkInfo.link % e.what()).str() << endm; |
| 647 | + resultBuffer << e.what(); |
| 648 | + break; |
622 | 649 | } |
623 | 650 | } |
624 | 651 |
|
625 | 652 | return resultBuffer.str(); |
626 | 653 | } |
627 | 654 |
|
628 | | - static bool isLineComment(const std::string& line) |
629 | | - { |
630 | | - return line.find('#') == 0; |
631 | | - } |
632 | | - |
633 | | - static Sca::CommandData stringToScaCommandDataPair(const std::string& string) |
634 | | - { |
635 | | - // The pairs are comma-separated, so we split them. |
636 | | - std::vector<std::string> pair = split(string, scaPairSeparator().c_str()); |
637 | | - if (pair.size() != 2) { |
638 | | - getLogger() << "SCA command-data pair not formatted correctly: parts=" << pair.size() << " str='" << string << |
639 | | - "'" << endm; |
640 | | - BOOST_THROW_EXCEPTION( |
641 | | - AlfException() << ErrorInfo::Message("SCA command-data pair not formatted correctly")); |
642 | | - } |
643 | | - Sca::CommandData commandData; |
644 | | - commandData.command = convertHexString(pair[0]); |
645 | | - commandData.data = convertHexString(pair[1]); |
646 | | - return commandData; |
647 | | - }; |
648 | | - |
649 | 655 | /// Command queue for passing commands from DIM RPC calls (which are in separate threads) to the main program loop |
650 | 656 | std::shared_ptr<CommandQueue> mCommandQueue; |
651 | 657 | /// serial -> link -> vector of RPC servers |
|
0 commit comments