CMS 3D CMS Logo

utilities.cc
Go to the documentation of this file.
2 
3 #include <algorithm>
4 #include <fstream>
5 #include <regex>
6 #include <unordered_map>
7 
8 #ifdef CMSSW_GIT_HASH
10 #endif
12 
13 namespace {
14 
15  l1t::demo::BoardData createBoardDataFromRows(const std::string& id,
16  const std::vector<size_t>& channels,
17  const std::vector<std::vector<l1t::demo::Frame>>& dataRows) {
18  l1t::demo::BoardData boardData(id);
19 
20  for (size_t i = 0; i < channels.size(); i++) {
21  std::vector<l1t::demo::Frame> channelData(dataRows.size());
22  for (size_t j = 0; j < dataRows.size(); j++)
23  channelData.at(j) = dataRows.at(j).at(i);
24  boardData.add(channels.at(i), channelData);
25  }
26 
27  return boardData;
28  }
29 
30  std::vector<std::string> searchAndTokenize(std::istream& file, const std::string& linePrefix) {
32 
33  while (getline(file, line)) {
34  // Strip leading spaces
35  size_t startIndex = line.find_first_not_of(" \t");
36  if (startIndex != std::string::npos)
37  line = line.substr(startIndex);
38 
39  if (line.empty())
40  continue;
41  if (line[0] == '#')
42  continue;
43 
44  if (line.rfind(linePrefix, 0) != std::string::npos) {
45  std::vector<std::string> tokens;
46 
47  // Split the line into tokens
48  const std::regex delimiterRegex("\\s+");
49  std::sregex_token_iterator it(line.begin() + linePrefix.size(), line.end(), delimiterRegex, -1);
50 
51  for (; it != std::sregex_token_iterator(); it++) {
52  const std::string token(it->str());
53  if (token.empty())
54  continue;
55  tokens.push_back(token);
56  }
57 
58  return tokens;
59  } else
60  throw std::logic_error("Found unexpected line found when searching for \"" + linePrefix + "\": \"" + line +
61  "\"");
62  }
63  throw std::logic_error("Couldn't find any line starting with \"" + linePrefix + "\"");
64  }
65 
66 } // namespace
67 
68 namespace l1t::demo {
69 
71  static const std::unordered_map<std::string, FileFormat> kFormatStringMap({{"EMP", FileFormat::EMP},
72  {"emp", FileFormat::EMP},
73  {"APx", FileFormat::APx},
74  {"apx", FileFormat::APx},
75  {"X20", FileFormat::X20},
76  {"x20", FileFormat::X20}});
77 
78  const auto it = kFormatStringMap.find(s);
79  if (it == kFormatStringMap.end())
80  throw std::runtime_error("Could not convert '" + s + "' to FileFormat enum value");
81 
82  return it->second;
83  }
84 
85  BoardData readAPxFile(std::istream&, const FileFormat);
86 
87  BoardData readEMPFile(std::istream&, const FileFormat);
88 
89  BoardData readX20File(std::istream&, const FileFormat);
90 
92  std::ifstream file(filePath);
93 
94  if (not file.is_open())
95  throw std::runtime_error("Could not open file '" + filePath + "'");
96 
97  return read(file, format);
98  }
99 
100  BoardData read(std::istream& file, const FileFormat format) {
101  if (format == FileFormat::APx)
102  return readAPxFile(file, format);
103  else if (format == FileFormat::EMP)
104  return readEMPFile(file, format);
105  else
106  return readX20File(file, format);
107  }
108 
109  BoardData readAPxFile(std::istream& file, const FileFormat format) {
111 
112  // Complain if file is empty
113  if (not std::getline(file, line))
114  throw std::runtime_error("Specified file is empty!");
115 
116  // If first line is sideband, skip it and move onto 2nd line
117  if (line.find("#Sideband") == 0) {
118  if (not std::getline(file, line))
119  throw std::runtime_error("APx file has incorrect format: Link labels and data missing!");
120  }
121 
122  // Parse link labels
123  if (line.find("#LinkLabel") != 0)
124  throw std::runtime_error(
125  "APx file has incorrect format: Link header does not start with '#LinkLabel' (line is '" + line + "')");
126 
127  std::vector<size_t> indices;
128  const std::regex delimiterRegex("\\s+");
129  std::sregex_token_iterator it(line.begin() + 10, line.end(), delimiterRegex, -1);
130  for (; it != std::sregex_token_iterator(); it++) {
131  const std::string token(it->str());
132  if (token.empty())
133  continue;
134 
135  if (token.find("LINK_") != 0)
136  throw std::runtime_error("Link column name '" + token + "' (does not start with 'LINK_')");
137  if (token.size() == 5)
138  throw std::runtime_error("Link column name '" + token + "' is too short");
139  if (not std::all_of(token.begin() + 5, token.end(), ::isdigit))
140  throw std::runtime_error("Link column name '" + token + "' does not end with a number");
141 
142  indices.push_back(std::stoul(token.substr(5)));
143  }
144 
145  // Check for '#BeginData' line
146  if (not std::getline(file, line))
147  throw std::runtime_error("APx file has incorrect format: Data missing!");
148  if (line != "#BeginData")
149  throw std::runtime_error("APx file has incorrect format: '#BeginData' line missing (found '" + line + "')");
150 
151  // Parse link data
152  std::vector<std::vector<l1t::demo::Frame>> dataRows;
153  while (std::getline(file, line)) {
154  it = std::sregex_token_iterator(line.begin(), line.end(), delimiterRegex, -1);
155  size_t count = 0;
156  for (; it != std::sregex_token_iterator(); it++, count++) {
157  const std::string token(it->str());
158 
159  if ((token.find("0x") != 0) or (not std::all_of(token.begin() + 2, token.end(), ::isxdigit)))
160  throw std::runtime_error("APx file has incorrect format: Data token '" + token +
161  "' is not hexadecimal number");
162 
163  if (count == 0) {
164  size_t rowIndex = std::stoul(token, nullptr, 16);
165  if (rowIndex != dataRows.size())
166  throw std::runtime_error("APx file has incorrect format: Expected data row " +
167  std::to_string(dataRows.size()) + ", but found row " + std::to_string(rowIndex));
168  dataRows.push_back(std::vector<l1t::demo::Frame>(indices.size()));
169  }
170  // Sideband info
171  else if ((count % 2) == 1) {
172  uint16_t sbValue = std::stoul(token, nullptr, 16);
173  dataRows.back().at((count - 1) / 2).valid = (sbValue & 0x1);
174  dataRows.back().at((count - 1) / 2).start = ((sbValue >> 1) & 0x1);
175  dataRows.back().at((count - 1) / 2).end = ((sbValue >> 3) & 0x1);
176  }
177  // Data word
178  else
179  dataRows.back().at((count - 1) / 2).data = ap_uint<64>(std::stoull(token, nullptr, 16));
180  }
181 
182  if (count != (2 * indices.size() + 1))
183  throw std::runtime_error("APx file has incorrect format: Line has incorrect number of tokens (expected " +
184  std::to_string(2 * indices.size() + 1) + ", found " + std::to_string(count) + "!");
185  }
186 
187  return createBoardDataFromRows("", indices, dataRows);
188  }
189 
190  BoardData readEMPFile(std::istream& file, const FileFormat format) {
191  // 1) Search for ID string
193  while (getline(file, line)) {
194  if (line.empty())
195  continue;
196  if (line[0] == '#')
197  continue;
198 
199  if (line.rfind("Board ", 0) != std::string::npos) {
200  id = line.substr(6);
201  break;
202  } else
203  throw std::logic_error("Found unexpected line found when searching for board ID: \"" + line + "\"");
204  }
205 
206  // 2) Search for column labels (i.e. list of channels/links)
207  searchAndTokenize(file, "Quad/Chan :");
208  const auto tokens = searchAndTokenize(file, "Link :");
209  std::vector<size_t> channels;
210  std::transform(tokens.begin(), tokens.end(), std::back_inserter(channels), [](const std::string& s) {
211  return std::stoull(s);
212  });
213 
214  // 3) Read the main data rows
215  const std::regex delimiterRegex("\\s+");
216  static const std::regex frameRegex("([01]s)?([01]v)([0-9a-fA-F]{16})");
217  std::vector<std::vector<Frame>> dataRows;
218  while (file.good() and getline(file, line)) {
219  if (line.empty() or line[0] == '#')
220  continue;
221 
222  std::ostringstream prefixStream;
223  prefixStream << "Frame ";
224  prefixStream << std::setw(4) << std::setfill('0') << dataRows.size();
225  prefixStream << " :";
226 
227  const std::string prefix(prefixStream.str());
228  if (line.rfind(prefix, 0) == std::string::npos)
229  throw std::logic_error("Found unexpected line found when searching for \"" + prefix + "\": \"" + line + "\"");
230 
231  std::vector<l1t::demo::Frame> row;
232  std::sregex_token_iterator it(line.begin() + prefix.size(), line.end(), delimiterRegex, -1);
233  for (; it != std::sregex_token_iterator(); it++) {
234  const std::string token(it->str());
235  if (token.empty())
236  continue;
237 
238  std::smatch what;
239  if (not std::regex_match(token, what, frameRegex))
240  throw std::logic_error("Token '" + token + "' doesn't match the valid format");
241 
243  // Import strobe if the strobe group is matched
244  if (what[1].matched) {
245  value.strobe = (what[1] == "1s");
246  }
247 
248  value.valid = (what[2] == "1v");
249  value.data = ap_uint<64>(std::stoull(what[3].str(), nullptr, 16));
250 
251  row.push_back(value);
252  }
253 
254  dataRows.push_back(row);
255  }
256 
257  return createBoardDataFromRows(id, channels, dataRows);
258  }
259 
260  BoardData readX20File(std::istream& file, const FileFormat format) {
261  throw std::runtime_error("Reading X20 file format not yet implemented. Will be done ASAP.");
262  }
263 
264  void writeAPxFile(const BoardData&, std::ostream&, const FileFormat);
265 
266  void writeEMPFile(const BoardData&, std::ostream&, const FileFormat);
267 
268  void writeX20File(const BoardData&, std::ostream&, const FileFormat);
269 
270  void write(const BoardData& data, const std::string& filePath, const FileFormat format) {
271  // Open file
272 #ifdef CMSSW_GIT_HASH
273  edm::LogInfo("L1TDemonstratorTools")
274 #else
275  std::cout
276 #endif
277  << "Writing board data (" << std::distance(data.begin(), data.end()) << " channels, "
278  << data.begin()->second.size() << " frames) to file '" << filePath << "' (format: " << format << ")"
279  << std::endl;
280  std::ofstream file(filePath);
281 
282  if (not file.is_open())
283  throw std::runtime_error("Could not open file '" + filePath + "'");
284 
285  write(data, file, format);
286  }
287 
288  void write(const BoardData& data, std::ostream& file, const FileFormat format) {
289  // Check that number of frames is same for every channel
290  const auto firstChannel = data.begin();
291 
292  for (const auto& channel : data) {
293  const auto i = channel.first;
294  const auto channelData = channel.second;
295  if (channelData.size() != firstChannel->second.size())
296  throw std::runtime_error("Cannot write board data to file - channels do not all have the same length (" +
297  std::to_string(channelData.size()) + " words on channel " + std::to_string(i) +
298  ", but " + std::to_string(firstChannel->second.size()) + " words on channel " +
299  std::to_string(firstChannel->first) + ")");
300  }
301 
302  // Call relevant write function
303  switch (format) {
304  case FileFormat::APx:
306  return;
307  case FileFormat::EMP:
309  return;
310  case FileFormat::X20:
312  return;
313  }
314  }
315 
316  void writeAPxFile(const BoardData& data, std::ostream& file, const FileFormat format) {
317  // Note: APx sideband encoding
318  // Short-term, simulation only:
319  // 0 -> Valid
320  // 1 -> EOF
321  // Planned (from ~ May 2021)
322  // 0 -> Valid
323  // 1 -> SOF (Start Of Frame)
324  // 2 -> FFO (First Frame of Orbit)
325  // 3 -> EOF (End Of Frame)
326  // 4 -> FERR (Frame Error)
327  // 5 -> RSV1
328  // 6 -> RSV2
329  // 7 -> RSV3
330 
331  file << std::setfill('0');
332  file << "#Sideband ON" << std::endl;
333 
334  // Channel header
335  file << "#LinkLabel";
336  for (const auto& channel : data) {
337  const auto i = channel.first;
338  file << " LINK_" << std::setw(2) << i << " ";
339  }
340  file << std::endl;
341 
342  file << "#BeginData" << std::endl;
343 
344  // Frames
345  file << std::hex;
346  const auto firstChannel = data.begin();
347  for (size_t i = 0; i < firstChannel->second.size(); i++) {
348  file << "0x" << std::setw(4) << i;
349  for (const auto& channel : data) {
350  //const auto j = channel.first;
351  const auto channelData = channel.second;
352  uint16_t sideband = channelData.at(i).valid;
353  sideband |= channelData.at(i).start << 1;
354  sideband |= channelData.at(i).end << 3;
355  file << " 0x" << std::setw(2) << sideband;
356  file << " 0x" << std::setw(16) << uint64_t(channelData.at(i).data);
357  }
358  file << std::endl;
359  }
360  }
361 
362  void writeEMPFile(const BoardData& data, std::ostream& file, const FileFormat format) {
363  file << std::setfill('0');
364 
365  // Board name/id
366  file << "Board CMSSW" << std::endl;
367 
368  // Quad/chan header
369  file << " Quad/Chan :";
370  for (const auto& channel : data) {
371  const auto i = channel.first;
372  file << " q" << std::setw(2) << i / 4 << 'c' << std::setw(1) << i % 4 << " ";
373  }
374  file << std::endl;
375 
376  // Link header
377  file << " Link :";
378  for (const auto& channel : data) {
379  const auto i = channel.first;
380  file << " " << std::setw(3) << i << " ";
381  }
382  file << std::endl;
383 
384  // Frames
385  const auto firstChannel = data.begin();
386  for (size_t i = 0; i < firstChannel->second.size(); i++) {
387  file << "Frame " << std::setw(4) << i << " :";
388  for (const auto& channel : data) {
389  //const auto j = channel.first;
390  const auto channelData = channel.second;
391  file << " ";
392  //TODO: Add strobe if zero anywhere on channel
393  file << " ";
394  file << std::setw(1) << channelData.at(i).valid << "v" << std::setw(16) << std::hex
395  << uint64_t(channelData.at(i).data);
396  }
397  file << std::endl << std::dec;
398  }
399  }
400 
401  void writeX20File(const BoardData& data, std::ostream& file, const FileFormat format) {
402  throw std::runtime_error("Writing X20 file format not yet implemented. Will be done ASAP.");
403  }
404 
405 } // namespace l1t::demo
BoardData readAPxFile(std::istream &, const FileFormat)
Definition: utilities.cc:109
std::string to_string(const V &value)
Definition: OMSAccess.h:77
BoardData readEMPFile(std::istream &, const FileFormat)
Definition: utilities.cc:190
FileFormat parseFileFormat(const std::string &)
Definition: utilities.cc:70
void write(const BoardData &, const std::string &filePath, const FileFormat)
Definition: utilities.cc:270
The Signals That Services Can Subscribe To This is based on ActivityRegistry and is current per Services can connect to the signals distributed by the ActivityRegistry in order to monitor the activity of the application Each possible callback has some defined which we here list in angle e< void, edm::EventID const &, edm::Timestamp const & > We also list in braces which AR_WATCH_USING_METHOD_ is used for those or
Definition: Activities.doc:12
void writeAPxFile(const BoardData &, std::ostream &, const FileFormat)
Definition: utilities.cc:316
Definition: value.py:1
Log< level::Info, false > LogInfo
unsigned long long uint64_t
Definition: Time.h:13
char data[epos_bytes_allocation]
Definition: EPOS_Wrapper.h:79
void writeX20File(const BoardData &, std::ostream &, const FileFormat)
Definition: utilities.cc:401
BoardData read(const std::string &filePath, const FileFormat)
Definition: utilities.cc:91
void writeEMPFile(const BoardData &, std::ostream &, const FileFormat)
Definition: utilities.cc:362
BoardData readX20File(std::istream &, const FileFormat)
Definition: utilities.cc:260
Class representing information that&#39;s stored in the input or output buffers on a phase-2 board...
Definition: BoardData.h:13
#define str(s)
unsigned transform(const HcalDetId &id, unsigned transformCode)