├── README.md ├── check_these ├── CommandQueue.cpp ├── ConfigLoader.cpp ├── CyclicMotor.cpp ├── EtherCatServer.cpp ├── Makefile ├── NameMap.cpp ├── Pdo.cpp ├── PdoEntry.cpp ├── PdoEntryCache.cpp ├── SlaveConfig.cpp ├── SyncManager.cpp ├── UserExample.cpp ├── libtmtec.so ├── tinystr.cpp ├── tinyxml.cpp ├── tinyxmlerror.cpp └── tinyxmlparser.cpp ├── doc ├── EthercatServerRequirementsAndDesign.docx ├── ethercat-demo_class_diagram.graffle └── images │ └── design.png ├── example_cpp_style_file ├── include ├── CommandQueue.h ├── ConfigLoader.h ├── CyclicMotor.h ├── EtherCatServer.h ├── NameMap.h ├── Pdo.h ├── PdoEntry.h ├── PdoEntryCache.h ├── SlaveConfig.h ├── SyncManager.h ├── TmtEcStructs.h ├── temp.h ├── tinystr.h └── tinyxml.h ├── src ├── CommandQueue.cpp ├── ConfigLoader.cpp ├── CyclicMotor.cpp ├── EtherCatServer.cpp ├── Makefile ├── NameMap.cpp ├── Pdo.cpp ├── PdoEntry.cpp ├── PdoEntryCache.cpp ├── SlaveConfig.cpp ├── SyncManager.cpp ├── UserExample.cpp ├── libtmtec.so ├── tinystr.cpp ├── tinyxml.cpp ├── tinyxmlerror.cpp └── tinyxmlparser.cpp └── test ├── test.cpp ├── test.h ├── test.o ├── test.xml └── test.xml.failed /README.md: -------------------------------------------------------------------------------- 1 | ethercat-demo (EtherCAT Server API Prototype) 2 | ============= 3 | ##1 Description 4 | The EtherCAT Server API exposes a simple interface that users can build EtherCAT based applications in C++ on Linux quickly and easily, without requiring the user to program against an EtherCAT protocol specific API. Instead, the API enables device reads and writes using the device name or ring position or alias address and a parameter name. 5 | 6 | The API includes methods to readout the current device and parameter names defined by the connected hardware, and recognizes these names in the API for reading and writing values. 7 | 8 | ##2 Quick Start Instructions 9 | 1. Install the IgH EtherLab Master. 10 | 2. Download this distribution from GitHub. 11 | 3. Build using the Makefile in src dir. 12 | 4. Copy the generated shared library: ‘libtmtec.so’ to a location in the library path. 13 | 5. Configure server to connected hardware (see section 3.2) 14 | 6. Run the ectest program. 15 | 16 | ##3 Writing a Program 17 | Link against libtmtec.so, and include EtherCatServer.h interface definition in your source files. 18 | ###3.1 Example Program 19 | The example program UserExample.c is included in the distribution. This demonstrates the capabilities of the API. 20 | ###3.2 Configuring Server to Connected Hardware 21 | Run “ethercat xml > .xml” to generate the configuration xml file. 22 | 23 | Identify location of file when configuring the server in code: 24 | ``` 25 | etherCatServer.configServer(".xml"); 26 | ``` 27 | ##4 Alias Addressing 28 | Device modules can be addressed using names, ring position and/or alias addressing. Name addressing is not currently working when two modules in the ring are the same manufacture and model. 29 | 30 | Alias addressing can be used to access a module even if the module is moved from one position in the ring to another. For this scheme to work we need to: 31 | 1. Write EPROM on the module with the alias address 32 | 2. Run the XML generator and update the XML config file each time a module is moved. (This step will be automated in a future release). 33 | 34 | ###4.1 Wrting EPROM Alias to Modules 35 | To write EPROM alias addressing, use the following ethercat command line: 36 | ``` 37 | ethercat alias –p -a 38 | ``` 39 | Where is the current ring position of the module to change the alias and is an unsigned 16 bit number. 40 | 41 | To use alias addressing in the API, use the form of the get/set parameter value method that takes deviceAlias and devicePosition as arguments, for example: 42 | ``` 43 | getParameterValue(1111, 0, “Channel 1::Input”); 44 | ``` 45 | In the example, the alias is 1111 and the position is ignored. If the alias is 0, then the ring position argument is used instead. 46 | 47 | ##Design Overview 48 | 49 | ![Design](/doc/images/design.png) 50 | 51 | A user program provides the location of the configuration XML file. This is passed to the EtherCatServer API, that in turn passes it to the ConfigLoader that loads the configuration, creating an internal data structure of SlaveConfig objects, that contain associated SyncManager objects, Pdo objects and PdoEntry objects. 52 | 53 | Starting the server calls the start() method of the CyclicMotor that initializes the state of the object and starts the detached thread that wakes up and executes at regular intervals to receive and send datagrams. 54 | 55 | Every time the CyclicMotor thread wakes up and updates its Domain process data, that data is copied to the PdoEntryCache singleton cache. Whenever a call is made to getParameterValue in the EtherCatServer API, this call in turn calls the getPdoEntryValue() call on the singleton cache. The value in the cache is always the most recently read value from the domain. 56 | 57 | Similarly, the EtherCatServer API call: setParameterValue() creates and populates PdoEntryValue structs and passes these to the CommandQueue singleton’s addToQueue() method. The CommandQueue contains accumulated commands. These commands are popped off the queue by the CyclicMotor thread each time it wakes up and finds entries on the queue. All entries are sent out with the next datagram and the queue is emptied. 58 | 59 | 60 | -------------------------------------------------------------------------------- /check_these/CommandQueue.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "TmtEcStructs.h" 4 | #include 5 | #include "CommandQueue.h" 6 | 7 | 8 | // Singleton pattern stuff 9 | 10 | CommandQueue* CommandQueue::pInstance = 0; 11 | 12 | CommandQueue* CommandQueue::instance() { 13 | if (pInstance == 0) { 14 | pInstance = new CommandQueue; 15 | } 16 | return pInstance; 17 | } 18 | 19 | CommandQueue::CommandQueue() { 20 | cmdQueue = std::queue(); 21 | }; 22 | 23 | // TODO: this class is not thread-safe. We need to implement locking. 24 | 25 | void CommandQueue::addToQueue(PdoEntryValue pdoEntryValue) { 26 | cmdQueue.push(pdoEntryValue); 27 | 28 | } 29 | 30 | PdoEntryValue CommandQueue::getNext() { 31 | 32 | PdoEntryValue pdoEntryValue = cmdQueue.front(); 33 | cmdQueue.pop(); 34 | 35 | return pdoEntryValue; 36 | }; 37 | 38 | bool CommandQueue::isEmpty() { 39 | 40 | return cmdQueue.empty(); 41 | }; 42 | 43 | -------------------------------------------------------------------------------- /check_these/ConfigLoader.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "ecrt.h" 5 | #include "tinystr.h" 6 | #include "tinyxml.h" 7 | #include "temp.h" 8 | #include 9 | #include 10 | 11 | #include "PdoEntry.h" 12 | #include "Pdo.h" 13 | #include "SyncManager.h" 14 | #include "SlaveConfig.h" 15 | #include "ConfigLoader.h" 16 | 17 | 18 | ConfigLoader::ConfigLoader() { 19 | }; 20 | 21 | void ConfigLoader::applyConfiguration(ec_master_t *master, ec_domain *domain1, std::vector *slaveConfigVector) { 22 | // configure all slaves and domain for each pdo entry of each slave 23 | 24 | //std::cout << "\ngot here::" << slaveConfigVector->size() << "\n"; 25 | 26 | ec_slave_config_t *sc; 27 | for (unsigned int si = 0; si < slaveConfigVector->size(); si++) { 28 | 29 | //std::cout << "\nin loop: si = " << si << "\n"; 30 | 31 | SlaveConfig slaveConfig = slaveConfigVector->at(si); 32 | 33 | // TEST ONLY 34 | std::vector syncManagerVector = slaveConfig.syncs; 35 | 36 | /* 37 | * code to generate sync structure for convienience api 38 | * 39 | // here we generate the structs 40 | ec_sync_info_t* syncs = new ec_sync_info_t[syncManagerVector.size()]; 41 | 42 | std::cout << "syncs size = " << syncManagerVector.size(); 43 | 44 | for (unsigned int i=0; iat(si).pdoEntries.at(i).domainOffset = offset; 147 | slaveConfigVector->at(si).pdoEntries.at(i).domainBitPos = bitpos; 148 | 149 | std::cout << std::hex << "\ndomain registration: offset = " << slaveConfigVector->at(si).pdoEntries.at(i).domainOffset << ", bit pos = " << slaveConfigVector->at(si).pdoEntries.at(i).domainBitPos; 150 | 151 | } 152 | 153 | } 154 | printf("\n"); 155 | } 156 | 157 | 158 | std::vector ConfigLoader::loadConfiguration(ec_master_t *master, std::string configFile) { 159 | 160 | // Decision: there is no public API for getting PDO entries, etc. This information *could* be obtained using the source 161 | // from the 'tool' area, but this appears to be fairly complex and would be time consuming. The information we want is still 162 | // available from the 'tool' commands, and to support new versions, it is probably cleaner to just use the command line tools 163 | // as is and use the tool outputs directly, rather than trying to recreate the tools in other code. 164 | 165 | std::vector slaveConfigList; 166 | 167 | TiXmlNode* pChild; 168 | 169 | TiXmlDocument doc(configFile.c_str()); 170 | doc.LoadFile(); 171 | printf("\n%s:\n", configFile.c_str()); 172 | 173 | TiXmlNode* root = doc.FirstChild("EtherCATInfoList"); 174 | 175 | // loop over slaves 176 | int slaveIndex = -1; 177 | for (pChild = root->FirstChild(); pChild != 0; pChild = pChild->NextSibling()) { 178 | 179 | slaveIndex++; 180 | std::cout << "\n child value = " << pChild->Value(); 181 | 182 | ec_slave_info_t *slave_info = new ec_slave_info_t; 183 | // get the slave alias 184 | int success = ecrt_master_get_slave (master, slaveIndex, slave_info); 185 | 186 | // Slave Information 187 | 188 | TiXmlElement* vendorElem = pChild->FirstChildElement("Vendor"); 189 | TiXmlElement* idElem = vendorElem->FirstChildElement("Id"); 190 | TiXmlText* idValueText = idElem->FirstChild()->ToText(); 191 | 192 | std::cout << "\n vendor id: " << idValueText->Value(); 193 | 194 | TiXmlElement* descriptionsElem = pChild->FirstChildElement("Descriptions"); 195 | TiXmlElement* devicesElem = descriptionsElem->FirstChildElement("Devices"); 196 | TiXmlElement* deviceElem = devicesElem->FirstChildElement("Device"); 197 | TiXmlElement* typeElem = deviceElem->FirstChildElement("Type"); 198 | 199 | TiXmlText* typeValueText = typeElem->FirstChild()->ToText(); 200 | 201 | std::cout << "\n device type = " << typeValueText->Value(); 202 | 203 | // product code and revision number 204 | 205 | std::cout << "\n product code = " << typeElem->Attribute("ProductCode"); 206 | std::cout << "\n revision # = " << typeElem->Attribute("RevisionNo"); 207 | 208 | // device name 209 | 210 | TiXmlElement* nameElem = deviceElem->FirstChildElement("Name"); 211 | TiXmlText* nameText = nameElem->FirstChild()->ToText(); 212 | 213 | std::cout << "\n device name = " << nameText->Value(); 214 | 215 | // fill the structure with parsed values 216 | 217 | std::string *name = new std::string(nameText->Value()); 218 | 219 | unsigned int product_code = hexCharToInt(typeElem->Attribute("ProductCode")); 220 | //unsigned int pos = slaveIndex; 221 | unsigned int pos = 0; 222 | unsigned int vendor_id = charToInt(idValueText->Value()); 223 | unsigned int alias = (success == 0) ? slave_info->alias : 0; 224 | //unsigned int alias = 0; 225 | std::cout << "\nalias = " << alias; 226 | 227 | // loop over a slave's Sync Managers 228 | // the order is important in linking them to the PDOs 229 | 230 | // create slave config 231 | 232 | SlaveConfig *slaveConfig = new SlaveConfig(*name, vendor_id, product_code, alias, pos); 233 | 234 | std::vector slavePdoVector; 235 | std::vector slavePdoEntryVector; 236 | std::vector slaveSyncManagerVector; 237 | 238 | TiXmlNode* smChild = 0; 239 | int i = -1; 240 | while ((smChild = deviceElem->IterateChildren("Sm", smChild))) { 241 | 242 | std::cout << "\n\nSync Manager " << ++i << "\n"; 243 | 244 | TiXmlElement* smElem = smChild->ToElement(); 245 | 246 | std::cout << "\n start address = " << smElem->Attribute("StartAddress"); 247 | std::cout << "\n enable = " << smElem->Attribute("Enable"); 248 | std::cout << "\n control byte = " << smElem->Attribute("ControlByte"); 249 | std::cout << "\n default size = " << smElem->Attribute("DefaultSize"); 250 | 251 | // we must only be counting those that are for Sync manager #i 252 | int txCount = countPdoChildren(deviceElem, "TxPdo", i); 253 | int rxCount = countPdoChildren(deviceElem, "RxPdo", i); 254 | 255 | ec_direction_t dir = (txCount > 0) ? EC_DIR_INPUT : EC_DIR_OUTPUT; 256 | uint8_t index = i; 257 | unsigned int n_pdos = (txCount > 0) ? txCount : rxCount; 258 | ec_watchdog_mode_t watchdog_mode = (txCount > 0) ? EC_WD_DISABLE : EC_WD_ENABLE; 259 | 260 | 261 | std::vector pdos; 262 | if (txCount > 0) { 263 | pdos = loadPdoInfo("TxPdo", deviceElem, i, n_pdos); 264 | } else { 265 | pdos = loadPdoInfo("RxPdo", deviceElem, i, n_pdos); 266 | } 267 | 268 | SyncManager *syncManager = new SyncManager(index, dir, watchdog_mode); 269 | 270 | // add to running vectors of pdos and pdo entries for this slave 271 | for(int pdoi = 0; pdoi < (int)pdos.size(); pdoi++) { 272 | Pdo pdo = pdos.at(pdoi); 273 | 274 | // add to sync manager 275 | syncManager->pdos.push_back(pdo); 276 | 277 | // add to slave totals 278 | slaveConfig->pdos.push_back(pdo); 279 | 280 | std::cout << "\n&&&& " << pdo.name << ", size = " << pdo.pdoEntries.size(); 281 | 282 | for(int pdoei = 0; pdoei < (int)pdo.pdoEntries.size(); pdoei++) { 283 | 284 | PdoEntry pdoEntry = pdo.pdoEntries.at(pdoei); 285 | 286 | slaveConfig->pdoEntries.push_back(pdoEntry); 287 | 288 | std::cout << pdoEntry.entryName << ", "; 289 | } 290 | } 291 | std::cout << "\n"; 292 | 293 | slaveSyncManagerVector.push_back(*syncManager); 294 | } 295 | 296 | // add completed sync tree structs to the slave config 297 | slaveConfig->syncs = slaveSyncManagerVector; 298 | 299 | slaveConfigList.push_back(*slaveConfig); 300 | } 301 | 302 | 303 | return slaveConfigList; 304 | 305 | } 306 | 307 | std::vector ConfigLoader::loadPdoInfo(const char* type, TiXmlElement *deviceElem, int smIndex, int n_pdos) { 308 | 309 | // allocate the tmt pdos array - new 310 | 311 | std::vector pdo_vector; 312 | 313 | 314 | std::cout << "\nnumber of pdos " << n_pdos << "\n"; 315 | 316 | if (n_pdos == 0) { 317 | return pdo_vector; 318 | } 319 | 320 | 321 | // loop over a slave's PDOs 322 | TiXmlNode* pdoChild = 0; 323 | int j = 0; 324 | for (pdoChild = deviceElem->FirstChild(type); pdoChild != 0; pdoChild = pdoChild->NextSibling()) { 325 | 326 | std::cout << "\n\nPdo " << j++ << "\n"; 327 | 328 | TiXmlElement* pdoElem = pdoChild->ToElement(); 329 | 330 | int syncMgr = charToInt(pdoElem->Attribute("Sm")); 331 | if (syncMgr != smIndex) continue; 332 | 333 | std::cout << "\n Sync Manager = " << pdoElem->Attribute("Sm"); 334 | std::cout << "\n fixed = " << pdoElem->Attribute("Fixed"); 335 | std::cout << "\n mandatory = " << pdoElem->Attribute("Mandatory"); 336 | 337 | 338 | 339 | TiXmlText* indexText = pdoElem->FirstChild("Index")->FirstChild()->ToText(); 340 | 341 | std::cout << "\n index = " << indexText->Value(); 342 | 343 | unsigned int index = hexCharToInt(indexText->Value()); 344 | 345 | std::cout << " >>>>>> 0x" << std::hex << index; 346 | 347 | std::string *name = new std::string("Not Specified"); 348 | 349 | TiXmlText* nameText = pdoElem->FirstChild("Name")->FirstChild()->ToText(); 350 | std::cout << "\n name = " << nameText->Value(); 351 | name = new std::string(nameText->Value()); 352 | 353 | 354 | int pdoEntryCount = countChildren(pdoElem, "Entry"); 355 | int n_entries = pdoEntryCount; 356 | 357 | 358 | // save this pdo 359 | Pdo *pdo = new Pdo(*name, index, n_entries); 360 | pdo_vector.push_back(*pdo); 361 | 362 | 363 | 364 | // loop over all PDO entries 365 | 366 | 367 | TiXmlNode* pdoEntryChild = 0; 368 | int k = 0; 369 | for (pdoEntryChild = pdoElem->FirstChild("Entry"); pdoEntryChild != 0; pdoEntryChild = pdoEntryChild->NextSibling()) { 370 | 371 | 372 | std::cout << "\n-----------------\n"; 373 | std::cout << "Pdo Entry " << k++ << "\n"; 374 | 375 | 376 | TiXmlText* indexText = pdoEntryChild->FirstChild("Index")->FirstChild()->ToText(); 377 | std::cout << "\n index = " << indexText->Value(); 378 | 379 | TiXmlText* bitLenText = pdoEntryChild->FirstChild("BitLen")->FirstChild()->ToText(); 380 | std::cout << "\n bit length = " << bitLenText->Value(); 381 | 382 | int bit_length = charToInt(bitLenText->Value()); 383 | unsigned int index = hexCharToInt(indexText->Value()); 384 | 385 | int subindex = 0; 386 | 387 | if (pdoEntryChild->FirstChild("SubIndex")) { 388 | 389 | TiXmlText* subIndexText = pdoEntryChild->FirstChild("SubIndex")->FirstChild()->ToText(); 390 | std::cout << "\n sub-index = " << subIndexText->Value(); 391 | subindex = charToInt(subIndexText->Value()); 392 | 393 | } else { 394 | std::cout << "\n sub-index = " << "0x00"; 395 | } 396 | 397 | std::string *entry_name = new std::string("Not Specified"); 398 | 399 | if (pdoEntryChild->FirstChild("Name")) { 400 | 401 | TiXmlText* nameText = pdoEntryChild->FirstChild("Name")->FirstChild()->ToText(); 402 | std::cout << "\n name = " << nameText->Value(); 403 | entry_name = new std::string(nameText->Value()); 404 | } else { 405 | std::cout << "\n name = " << "Not Specified " << k; 406 | } 407 | 408 | 409 | if (pdoEntryChild->FirstChild("DataType")) { 410 | TiXmlText* dataTypeText = pdoEntryChild->FirstChild("DataType")->FirstChild()->ToText(); 411 | std::cout << "\n data type = " << dataTypeText->Value(); 412 | } else { 413 | std::cout << "\n data type = " << "None"; 414 | } 415 | 416 | // save this as pdoEntry 417 | PdoEntry *pdoEntry = new PdoEntry(*entry_name, pdo->name, index, subindex, bit_length); 418 | pdo_vector.back().pdoEntries.push_back(*pdoEntry); 419 | 420 | std::cout << "\n---------" << pdo_vector.size() << "--------\n"; 421 | 422 | } 423 | std::cout << "all PDO entries done"; 424 | 425 | } 426 | 427 | return pdo_vector; 428 | 429 | } 430 | 431 | unsigned int ConfigLoader::charToInt(const char* input) { 432 | 433 | unsigned int intValue; 434 | 435 | intValue = strtol(input, NULL, 0); 436 | 437 | return intValue; 438 | } 439 | 440 | unsigned int ConfigLoader::hexCharToInt(const char* input) { 441 | 442 | unsigned int intValue; 443 | 444 | std::string *sindex = new std::string(input); 445 | 446 | sindex->replace(0, 1, "0"); 447 | 448 | std::stringstream strValue; 449 | strValue << *sindex; 450 | 451 | strValue >> std::hex >> intValue; 452 | 453 | return intValue; 454 | } 455 | 456 | 457 | 458 | int ConfigLoader::countChildren(TiXmlNode* parent) { 459 | 460 | //cout << "countChildren1"; 461 | int childCount = 0; 462 | TiXmlNode* child; 463 | for (child = parent->FirstChild(); child; child = child->NextSibling()) { 464 | childCount++; 465 | } 466 | return childCount; 467 | } 468 | 469 | int ConfigLoader::countChildren(TiXmlNode* parent, const char* name) { 470 | //cout << "countChildren2"; 471 | int childCount = 0; 472 | TiXmlNode* child; 473 | for (child = parent->FirstChild(name); child; child = 474 | child->NextSibling()) { 475 | childCount++; 476 | } 477 | return childCount; 478 | } 479 | 480 | int ConfigLoader::countPdoChildren(TiXmlNode* parent, const char* name, int smIndex) { 481 | //cout << "countPdoChildren"; 482 | int childCount = 0; 483 | TiXmlNode* child; 484 | for (child = parent->FirstChild(name); child; child = child->NextSibling()) { 485 | TiXmlElement *childElement = child->ToElement(); 486 | int candidateIndex = charToInt(childElement->Attribute("Sm")); 487 | if (candidateIndex == smIndex) { 488 | childCount++; 489 | } 490 | } 491 | return childCount; 492 | } 493 | 494 | void ConfigLoader::printSyncs(int n_syncs, ec_sync_info_t *sync) { 495 | 496 | std::cout << "in printSyncs nsyncs = " << n_syncs; 497 | 498 | for (int i=0; i 2 | #include 3 | #include "ecrt.h" 4 | 5 | #include 6 | #include "PdoEntry.h" 7 | #include "Pdo.h" 8 | #include "SyncManager.h" 9 | #include "SlaveConfig.h" 10 | #include "TmtEcStructs.h" 11 | #include "temp.h" 12 | #include "CommandQueue.h" 13 | #include "PdoEntryCache.h" 14 | #include "CyclicMotor.h" 15 | 16 | 17 | #include 18 | 19 | static bool terminateFlg = false; 20 | static ec_domain_state_t domain1_state = {}; 21 | 22 | 23 | // cyclic motor state 24 | 25 | CyclicMotor::CyclicMotor() { 26 | }; 27 | 28 | CyclicMotor::CyclicMotor(ec_master_t *master, ec_domain *domain1, uint8_t *domain1_pd, std::vector slaves) { 29 | this->master = master; 30 | this->domain1 = domain1; 31 | this->domain1_pd = domain1_pd; 32 | this->slaves = slaves; 33 | }; 34 | 35 | 36 | void CyclicMotor::start() { 37 | 38 | terminateFlg = false; 39 | 40 | std::thread t1(&CyclicMotor::startup, this); 41 | t1.detach(); 42 | 43 | }; 44 | 45 | void CyclicMotor::stop() { 46 | terminateFlg = true; 47 | }; 48 | 49 | 50 | /*****************************************************************************/ 51 | 52 | void check_domain1_state(ec_domain *domain1) 53 | { 54 | ec_domain_state_t ds; 55 | 56 | ecrt_domain_state(domain1, &ds); 57 | 58 | if (ds.working_counter != domain1_state.working_counter) 59 | printf("Domain1: WC %u.\n", ds.working_counter); 60 | if (ds.wc_state != domain1_state.wc_state) 61 | printf("Domain1: State %u.\n", ds.wc_state); 62 | 63 | domain1_state = ds; 64 | } 65 | 66 | /*****************************************************************************/ 67 | #if 0 68 | void check_master_state(void) 69 | { 70 | ec_master_state_t ms; 71 | 72 | ecrt_master_state(master, &ms); 73 | 74 | if (ms.slaves_responding != master_state.slaves_responding) { 75 | printf("%u slave(s).\n", ms.slaves_responding); 76 | } 77 | if (ms.al_states != master_state.al_states) 78 | printf("AL states: 0x%02X.\n", ms.al_states); 79 | if (ms.link_up != master_state.link_up) 80 | printf("Link is %s.\n", ms.link_up ? "up" : "down"); 81 | 82 | master_state = ms; 83 | } 84 | 85 | /*****************************************************************************/ 86 | 87 | void check_slave_config_states(void) 88 | { 89 | ec_slave_config_state_t s; 90 | 91 | ecrt_slave_config_state(sc_ana_in, &s); 92 | 93 | if (s.al_state != sc_ana_in_state.al_state) 94 | printf("AnaIn: State 0x%02X.\n", s.al_state); 95 | if (s.online != sc_ana_in_state.online) 96 | printf("AnaIn: %s.\n", s.online ? "online" : "offline"); 97 | if (s.operational != sc_ana_in_state.operational) 98 | printf("AnaIn: %soperational.\n", 99 | s.operational ? "" : "Not "); 100 | 101 | sc_ana_in_state = s; 102 | } 103 | 104 | #endif 105 | 106 | void CyclicMotor::startup() { 107 | 108 | printf("Started.\n"); 109 | 110 | while (1) { 111 | auto now = std::chrono::system_clock::now(); 112 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 113 | 114 | #if 0 115 | struct timeval t; 116 | gettimeofday(&t, NULL); 117 | printf("%u.%03u\n", t.tv_sec, t.tv_usec); 118 | #endif 119 | 120 | this->cyclic_task(this->master, this->domain1, this->domain1_pd, this->slaves); 121 | } 122 | }; 123 | 124 | 125 | 126 | void CyclicMotor::cyclic_task(ec_master_t *master, ec_domain *domain1, uint8_t *domain1_pd, std::vector slaves) { 127 | 128 | //cout << "cyclic_task::\n"; 129 | 130 | // receive process data 131 | ecrt_master_receive(master); 132 | ecrt_domain_process(domain1); 133 | 134 | // check process data state (optional) 135 | check_domain1_state(domain1); 136 | 137 | // check for master state (optional) 138 | //check_master_state(); 139 | 140 | // check for islave configuration state(s) (optional) 141 | 142 | //check_slave_config_states(); 143 | 144 | if (terminateFlg) { 145 | std::cout << "\nterminating thread"; 146 | std::terminate(); 147 | return; 148 | } 149 | 150 | // read all values into the cache 151 | // TODO: this could be in a separate thread 152 | 153 | for (unsigned int si=0; si 6 | #include 7 | #include "TmtEcStructs.h" 8 | #include "SlaveConfig.h" 9 | #include "CommandQueue.h" 10 | #include "ConfigLoader.h" 11 | 12 | #include "EtherCatServer.h" 13 | 14 | 15 | /****************************************************************************/ 16 | 17 | // Application parameters 18 | 19 | #define PRIORITY 1 20 | 21 | /****************************************************************************/ 22 | 23 | // process data 24 | static uint8_t *domain1_pd = NULL; 25 | 26 | 27 | // EtherCAT 28 | static ec_master_t *master = NULL; 29 | static ec_master_state_t master_state = {}; 30 | 31 | static ec_domain_t *domain1 = NULL; 32 | static ec_domain_state_t domain1_state = {}; 33 | 34 | 35 | static std::vector slaves; 36 | 37 | /****************************************************************************/ 38 | 39 | EtherCatServer::EtherCatServer() { 40 | pdoEntryCache = PdoEntryCache(); 41 | configLoader = ConfigLoader(); 42 | }; 43 | 44 | void EtherCatServer::startServer() { 45 | 46 | 47 | cyclicMotor = CyclicMotor(master, domain1, domain1_pd, slaves); 48 | 49 | cyclicMotor.start(); 50 | }; 51 | 52 | void EtherCatServer::stopServer() { 53 | cyclicMotor.stop(); 54 | }; 55 | 56 | int EtherCatServer::configServer(std::string configFile) { 57 | 58 | 59 | // 1. Configure the system 60 | 61 | master = ecrt_request_master(0); 62 | if (!master) 63 | return -1; 64 | 65 | 66 | // load up the configuration 67 | 68 | slaves = configLoader.loadConfiguration(master, configFile); 69 | 70 | domain1 = ecrt_master_create_domain(master); 71 | if (!domain1) 72 | return -1; 73 | 74 | // load and apply configurations 75 | configLoader.applyConfiguration(master, domain1, &slaves); 76 | 77 | printf("Activating master...\n"); 78 | if (ecrt_master_activate(master)) 79 | return -1; 80 | 81 | if (!(domain1_pd = ecrt_domain_data(domain1))) { 82 | return -1; 83 | } 84 | 85 | return 0; 86 | 87 | }; 88 | 89 | std::vector EtherCatServer::getDeviceNames() { 90 | 91 | std::vector deviceNameList; 92 | for (int si=0; si<(int)slaves.size(); si++) { 93 | deviceNameList.push_back(slaves.at(si).name); 94 | } 95 | return deviceNameList; 96 | 97 | }; 98 | 99 | // FIXME - this does not work when two device names are the same 100 | std::vector EtherCatServer::getParameterNames(std::string deviceName) { 101 | 102 | std::vector parameterNameList; 103 | for (int si=0; si<(int)slaves.size(); si++) { 104 | if (deviceName.compare(slaves.at(si).name) == 0) { 105 | for (unsigned int i=0; igetPdoEntryValue(slaveIndex, entryIndex); 159 | } 160 | 161 | // TODO: we need to template this for different types of value 162 | void EtherCatServer::setParameterValue(std::string deviceName, std::string parameterName, int value) { 163 | setParameterValue(deviceName, 0, 0, parameterName, value); 164 | } 165 | 166 | void EtherCatServer::setParameterValue(unsigned int deviceAlias, int deviceOffset, std::string parameterName, int value) { 167 | setParameterValue("", deviceAlias, deviceOffset, parameterName, value); 168 | } 169 | 170 | 171 | 172 | void EtherCatServer::setParameterValue(std::string deviceName, unsigned int alias, int pos, std::string parameterName, int value) { 173 | 174 | // TODO: implement as a hashmap of slaves by name containing a hashmap of PdoEntries by fullName. 175 | 176 | //cout << "setParameterValue:: slaves.size() = " << slaves.size(); 177 | 178 | for (int si=0; si<(int)slaves.size(); si++) { 179 | 180 | 181 | //std::cout << slaves.at(si).name << "::" << deviceName << "::" << alias; 182 | 183 | if ((deviceName.empty() && (alias == slaves.at(si).alias || (alias == 0U && si == pos))) 184 | || (deviceName.compare(slaves.at(si).name) == 0 && (alias == 0U || alias == slaves.at(si).alias))) { 185 | 186 | for (int i=0; i<(int)slaves.at(si).pdoEntries.size(); i++) { 187 | 188 | if (parameterName.compare(slaves.at(si).pdoEntries.at(i).fullName) == 0) { 189 | PdoEntryValue pdoEntryValue = PdoEntryValue(); 190 | pdoEntryValue.pdoEntryIndex = i; 191 | pdoEntryValue.slaveIndex = si; 192 | pdoEntryValue.entryValue = value; 193 | CommandQueue::instance()->addToQueue(pdoEntryValue); 194 | std::cout << "\nslave = " << slaves.at(si).name; 195 | std::cout << " ::: " << slaves.at(si).pdoEntries.at(i).fullName << ", value = " << pdoEntryValue.entryValue << " added to queue"; 196 | 197 | } 198 | 199 | } 200 | } 201 | } 202 | 203 | }; 204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /check_these/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile to build ectest 2 | 3 | all: ectest 4 | 5 | ETHER_LOC=/opt/etherlab 6 | 7 | CC=g++ 8 | CFLAGS=-c -Wall -fPIC -std=c++0x -pthread -D_GLIBCXX_USE_NANOSLEEP 9 | INCS= -I$(ETHER_LOC)/include -I../include -I$(ETHER_LOC)/tool 10 | LOPTS=-Wl,-rpath=$(ETHER_LOC)/lib 11 | LIBS=-L$(ETHER_LOC)/lib -lethercat -lpthread 12 | 13 | ectest: UserExample.o libtmtec.so 14 | $(CC) $(LOPTS) $(LIBS) -L./ -ltmtec -o $@ $^ 15 | 16 | libtmtec.so: EtherCatServer.o ConfigLoader.o CommandQueue.o PdoEntryCache.o NameMap.o CyclicMotor.o SyncManager.o Pdo.o PdoEntry.o SlaveConfig.o tinystr.o tinyxml.o tinyxmlerror.o tinyxmlparser.o 17 | $(CC) $(LOPTS) $(LIBS) -shared -o $@ $^ 18 | 19 | UserExample.o: UserExample.cpp 20 | $(CC) $(CFLAGS) $(INCS) $< 21 | 22 | EtherCatServer.o: EtherCatServer.cpp 23 | $(CC) $(CFLAGS) $(INCS) $< 24 | 25 | ConfigLoader.o: ConfigLoader.cpp 26 | $(CC) $(CFLAGS) $(INCS) $< 27 | 28 | CommandQueue.o: CommandQueue.cpp 29 | $(CC) $(CFLAGS) $(INCS) $< 30 | 31 | PdoEntryCache.o: PdoEntryCache.cpp 32 | $(CC) $(CFLAGS) $(INCS) $< 33 | 34 | NameMap.o: NameMap.cpp 35 | $(CC) $(CFLAGS) $(INCS) $< 36 | 37 | SyncManager.o: SyncManager.cpp 38 | $(CC) $(CFLAGS) $(INCS) $< 39 | 40 | Pdo.o: Pdo.cpp 41 | $(CC) $(CFLAGS) $(INCS) $< 42 | 43 | PdoEntry.o: PdoEntry.cpp 44 | $(CC) $(CFLAGS) $(INCS) $< 45 | 46 | SlaveConfig.o: SlaveConfig.cpp 47 | $(CC) $(CFLAGS) $(INCS) $< 48 | 49 | CyclicMotor.o: CyclicMotor.cpp 50 | $(CC) $(CFLAGS) $(INCS) $< 51 | 52 | tinystr.o: tinystr.cpp 53 | $(CC) $(CFLAGS) $(INCS) $< 54 | 55 | tinyxml.o: tinyxml.cpp 56 | $(CC) $(CFLAGS) $(INCS) $< 57 | 58 | tinyxmlerror.o: tinyxmlerror.cpp 59 | $(CC) $(CFLAGS) $(INCS) $< 60 | 61 | tinyxmlparser.o: tinyxmlparser.cpp 62 | $(CC) $(CFLAGS) $(INCS) $< 63 | 64 | clean: 65 | rm *.o ectest 66 | 67 | touch: 68 | touch ectest.c 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /check_these/NameMap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ecrt.h" 3 | #include "TmtEcStructs.h" 4 | #include 5 | #include "NameMap.h" 6 | 7 | 8 | NameMap::NameMap() { 9 | }; 10 | 11 | std::vector NameMap::getDevices() { 12 | 13 | Device device; 14 | 15 | std::vector deviceList; 16 | 17 | deviceList.push_back(device); 18 | 19 | return deviceList; 20 | 21 | }; 22 | 23 | std::vector NameMap::getParameters(Device device) { 24 | 25 | Parameter parameter; 26 | 27 | std::vector parameterList; 28 | 29 | parameterList.push_back(parameter); 30 | 31 | return parameterList; 32 | 33 | }; 34 | 35 | Parameter NameMap::getParameter(Device device, std::string parameterName) { 36 | Parameter parameter; 37 | 38 | return parameter; 39 | }; 40 | -------------------------------------------------------------------------------- /check_these/Pdo.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "Pdo.h" 4 | 5 | Pdo::Pdo(std::string name, int index, int n_entries) { 6 | this->name = name; 7 | this->index = index; 8 | this->n_entries = n_entries; 9 | }; 10 | -------------------------------------------------------------------------------- /check_these/PdoEntry.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "PdoEntry.h" 6 | 7 | PdoEntry::PdoEntry(std::string entryName, std::string pdoName, int index, int subindex, int bitLength) { 8 | 9 | this->entryName = entryName; 10 | 11 | // create the full name 12 | std::stringstream ss; 13 | ss << pdoName << "::" << entryName; 14 | this->fullName = ss.str(); 15 | 16 | this->index = index; 17 | this->subindex = subindex; 18 | this->bitLength = bitLength; 19 | this->domainOffset = 0; 20 | this->domainBitPos = 0; 21 | 22 | }; 23 | -------------------------------------------------------------------------------- /check_these/PdoEntryCache.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "ecrt.h" 4 | #include "TmtEcStructs.h" 5 | #include "PdoEntryCache.h" 6 | 7 | 8 | // Singleton pattern stuff 9 | 10 | PdoEntryCache* PdoEntryCache::pInstance = 0; 11 | 12 | PdoEntryCache* PdoEntryCache::instance() { 13 | if (pInstance == 0) { 14 | pInstance = new PdoEntryCache; 15 | } 16 | return pInstance; 17 | } 18 | 19 | void PdoEntryCache::updatePdoEntryValue(int slaveId, int pdoEntryId, int pdoEntryValue) { 20 | int key = (slaveId * 1000) + pdoEntryId; 21 | cacheMap[key] = pdoEntryValue; 22 | }; 23 | 24 | int PdoEntryCache::getPdoEntryValue(int slaveId, int pdoEntryId) { 25 | int key = (slaveId * 1000) + pdoEntryId; 26 | 27 | //std::cout << "\nKEY = " + key; 28 | 29 | return cacheMap[key]; 30 | 31 | }; 32 | -------------------------------------------------------------------------------- /check_these/SlaveConfig.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "SlaveConfig.h" 4 | 5 | SlaveConfig::SlaveConfig(std::string name, unsigned int vendor_id, unsigned int product_code, unsigned int alias, unsigned int pos) { 6 | this->name = name; 7 | this->vendor_id = vendor_id; 8 | this->product_code = product_code; 9 | this->alias = alias; 10 | this->pos = pos; 11 | this->domain_offset = 0; 12 | this->domain_bit_pos = 0; 13 | }; 14 | -------------------------------------------------------------------------------- /check_these/SyncManager.cpp: -------------------------------------------------------------------------------- 1 | #include "ecrt.h" 2 | #include "SyncManager.h" 3 | 4 | SyncManager::SyncManager(uint8_t index, ec_direction_t dir, ec_watchdog_mode_t watchdog_mode) { 5 | this->index = index; 6 | this->dir = dir; 7 | this->watchdog_mode = watchdog_mode; 8 | }; 9 | 10 | -------------------------------------------------------------------------------- /check_these/UserExample.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "EtherCatServer.h" 6 | 7 | 8 | int main() { 9 | 10 | // create a new server 11 | EtherCatServer etherCatServer = EtherCatServer(); 12 | 13 | // configure it with generated XML file 14 | etherCatServer.configServer("/home/smichaels/git/ethercat-demo/test/test.xml"); 15 | 16 | // print out device and parameter names 17 | std::vector deviceNames = etherCatServer.getDeviceNames(); 18 | for (unsigned int i=0; i paramNames = etherCatServer.getParameterNames(deviceNames.at(i)); 22 | for (unsigned int j=0; j(-1); 31 | 32 | 33 | // Null rep. 34 | TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, { '\0' } }; 35 | 36 | 37 | void TiXmlString::reserve (size_type cap) 38 | { 39 | if (cap > capacity()) 40 | { 41 | TiXmlString tmp; 42 | tmp.init(length(), cap); 43 | memcpy(tmp.start(), data(), length()); 44 | swap(tmp); 45 | } 46 | } 47 | 48 | 49 | TiXmlString& TiXmlString::assign(const char* str, size_type len) 50 | { 51 | size_type cap = capacity(); 52 | if (len > cap || cap > 3*(len + 8)) 53 | { 54 | TiXmlString tmp; 55 | tmp.init(len); 56 | memcpy(tmp.start(), str, len); 57 | swap(tmp); 58 | } 59 | else 60 | { 61 | memmove(start(), str, len); 62 | set_size(len); 63 | } 64 | return *this; 65 | } 66 | 67 | 68 | TiXmlString& TiXmlString::append(const char* str, size_type len) 69 | { 70 | size_type newsize = length() + len; 71 | if (newsize > capacity()) 72 | { 73 | reserve (newsize + capacity()); 74 | } 75 | memmove(finish(), str, len); 76 | set_size(newsize); 77 | return *this; 78 | } 79 | 80 | 81 | TiXmlString operator + (const TiXmlString & a, const TiXmlString & b) 82 | { 83 | TiXmlString tmp; 84 | tmp.reserve(a.length() + b.length()); 85 | tmp += a; 86 | tmp += b; 87 | return tmp; 88 | } 89 | 90 | TiXmlString operator + (const TiXmlString & a, const char* b) 91 | { 92 | TiXmlString tmp; 93 | TiXmlString::size_type b_len = static_cast( strlen(b) ); 94 | tmp.reserve(a.length() + b_len); 95 | tmp += a; 96 | tmp.append(b, b_len); 97 | return tmp; 98 | } 99 | 100 | TiXmlString operator + (const char* a, const TiXmlString & b) 101 | { 102 | TiXmlString tmp; 103 | TiXmlString::size_type a_len = static_cast( strlen(a) ); 104 | tmp.reserve(a_len + b.length()); 105 | tmp.append(a, a_len); 106 | tmp += b; 107 | return tmp; 108 | } 109 | 110 | 111 | #endif // TIXML_USE_STL 112 | -------------------------------------------------------------------------------- /check_these/tinyxmlerror.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | www.sourceforge.net/projects/tinyxml 3 | Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com) 4 | 5 | This software is provided 'as-is', without any express or implied 6 | warranty. In no event will the authors be held liable for any 7 | damages arising from the use of this software. 8 | 9 | Permission is granted to anyone to use this software for any 10 | purpose, including commercial applications, and to alter it and 11 | redistribute it freely, subject to the following restrictions: 12 | 13 | 1. The origin of this software must not be misrepresented; you must 14 | not claim that you wrote the original software. If you use this 15 | software in a product, an acknowledgment in the product documentation 16 | would be appreciated but is not required. 17 | 18 | 2. Altered source versions must be plainly marked as such, and 19 | must not be misrepresented as being the original software. 20 | 21 | 3. This notice may not be removed or altered from any source 22 | distribution. 23 | */ 24 | 25 | #include "tinyxml.h" 26 | 27 | // The goal of the seperate error file is to make the first 28 | // step towards localization. tinyxml (currently) only supports 29 | // english error messages, but the could now be translated. 30 | // 31 | // It also cleans up the code a bit. 32 | // 33 | 34 | const char* TiXmlBase::errorString[ TiXmlBase::TIXML_ERROR_STRING_COUNT ] = 35 | { 36 | "No error", 37 | "Error", 38 | "Failed to open file", 39 | "Error parsing Element.", 40 | "Failed to read Element name", 41 | "Error reading Element value.", 42 | "Error reading Attributes.", 43 | "Error: empty tag.", 44 | "Error reading end tag.", 45 | "Error parsing Unknown.", 46 | "Error parsing Comment.", 47 | "Error parsing Declaration.", 48 | "Error document empty.", 49 | "Error null (0) or unexpected EOF found in input stream.", 50 | "Error parsing CDATA.", 51 | "Error when TiXmlDocument added to document, because TiXmlDocument can only be at the root.", 52 | }; 53 | -------------------------------------------------------------------------------- /check_these/tinyxmlparser.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | www.sourceforge.net/projects/tinyxml 3 | Original code by Lee Thomason (www.grinninglizard.com) 4 | 5 | This software is provided 'as-is', without any express or implied 6 | warranty. In no event will the authors be held liable for any 7 | damages arising from the use of this software. 8 | 9 | Permission is granted to anyone to use this software for any 10 | purpose, including commercial applications, and to alter it and 11 | redistribute it freely, subject to the following restrictions: 12 | 13 | 1. The origin of this software must not be misrepresented; you must 14 | not claim that you wrote the original software. If you use this 15 | software in a product, an acknowledgment in the product documentation 16 | would be appreciated but is not required. 17 | 18 | 2. Altered source versions must be plainly marked as such, and 19 | must not be misrepresented as being the original software. 20 | 21 | 3. This notice may not be removed or altered from any source 22 | distribution. 23 | */ 24 | 25 | #include 26 | #include 27 | 28 | #include "tinyxml.h" 29 | 30 | //#define DEBUG_PARSER 31 | #if defined( DEBUG_PARSER ) 32 | # if defined( DEBUG ) && defined( _MSC_VER ) 33 | # include 34 | # define TIXML_LOG OutputDebugString 35 | # else 36 | # define TIXML_LOG printf 37 | # endif 38 | #endif 39 | 40 | // Note tha "PutString" hardcodes the same list. This 41 | // is less flexible than it appears. Changing the entries 42 | // or order will break putstring. 43 | TiXmlBase::Entity TiXmlBase::entity[ TiXmlBase::NUM_ENTITY ] = 44 | { 45 | { "&", 5, '&' }, 46 | { "<", 4, '<' }, 47 | { ">", 4, '>' }, 48 | { """, 6, '\"' }, 49 | { "'", 6, '\'' } 50 | }; 51 | 52 | // Bunch of unicode info at: 53 | // http://www.unicode.org/faq/utf_bom.html 54 | // Including the basic of this table, which determines the #bytes in the 55 | // sequence from the lead byte. 1 placed for invalid sequences -- 56 | // although the result will be junk, pass it through as much as possible. 57 | // Beware of the non-characters in UTF-8: 58 | // ef bb bf (Microsoft "lead bytes") 59 | // ef bf be 60 | // ef bf bf 61 | 62 | const unsigned char TIXML_UTF_LEAD_0 = 0xefU; 63 | const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; 64 | const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; 65 | 66 | const int TiXmlBase::utf8ByteTable[256] = 67 | { 68 | // 0 1 2 3 4 5 6 7 8 9 a b c d e f 69 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 70 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 71 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20 72 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30 73 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 74 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50 75 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 76 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range 77 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid 78 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90 79 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0 80 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0 81 | 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte 82 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0 83 | 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte 84 | 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid 85 | }; 86 | 87 | 88 | void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) 89 | { 90 | const unsigned long BYTE_MASK = 0xBF; 91 | const unsigned long BYTE_MARK = 0x80; 92 | const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; 93 | 94 | if (input < 0x80) 95 | *length = 1; 96 | else if ( input < 0x800 ) 97 | *length = 2; 98 | else if ( input < 0x10000 ) 99 | *length = 3; 100 | else if ( input < 0x200000 ) 101 | *length = 4; 102 | else 103 | { *length = 0; return; } // This code won't covert this correctly anyway. 104 | 105 | output += *length; 106 | 107 | // Scary scary fall throughs. 108 | switch (*length) 109 | { 110 | case 4: 111 | --output; 112 | *output = (char)((input | BYTE_MARK) & BYTE_MASK); 113 | input >>= 6; 114 | case 3: 115 | --output; 116 | *output = (char)((input | BYTE_MARK) & BYTE_MASK); 117 | input >>= 6; 118 | case 2: 119 | --output; 120 | *output = (char)((input | BYTE_MARK) & BYTE_MASK); 121 | input >>= 6; 122 | case 1: 123 | --output; 124 | *output = (char)(input | FIRST_BYTE_MARK[*length]); 125 | } 126 | } 127 | 128 | 129 | /*static*/ int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) 130 | { 131 | // This will only work for low-ascii, everything else is assumed to be a valid 132 | // letter. I'm not sure this is the best approach, but it is quite tricky trying 133 | // to figure out alhabetical vs. not across encoding. So take a very 134 | // conservative approach. 135 | 136 | // if ( encoding == TIXML_ENCODING_UTF8 ) 137 | // { 138 | if ( anyByte < 127 ) 139 | return isalpha( anyByte ); 140 | else 141 | return 1; // What else to do? The unicode set is huge...get the english ones right. 142 | // } 143 | // else 144 | // { 145 | // return isalpha( anyByte ); 146 | // } 147 | } 148 | 149 | 150 | /*static*/ int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) 151 | { 152 | // This will only work for low-ascii, everything else is assumed to be a valid 153 | // letter. I'm not sure this is the best approach, but it is quite tricky trying 154 | // to figure out alhabetical vs. not across encoding. So take a very 155 | // conservative approach. 156 | 157 | // if ( encoding == TIXML_ENCODING_UTF8 ) 158 | // { 159 | if ( anyByte < 127 ) 160 | return isalnum( anyByte ); 161 | else 162 | return 1; // What else to do? The unicode set is huge...get the english ones right. 163 | // } 164 | // else 165 | // { 166 | // return isalnum( anyByte ); 167 | // } 168 | } 169 | 170 | 171 | class TiXmlParsingData 172 | { 173 | friend class TiXmlDocument; 174 | public: 175 | void Stamp( const char* now, TiXmlEncoding encoding ); 176 | 177 | const TiXmlCursor& Cursor() const { return cursor; } 178 | 179 | private: 180 | // Only used by the document! 181 | TiXmlParsingData( const char* start, int _tabsize, int row, int col ) 182 | { 183 | assert( start ); 184 | stamp = start; 185 | tabsize = _tabsize; 186 | cursor.row = row; 187 | cursor.col = col; 188 | } 189 | 190 | TiXmlCursor cursor; 191 | const char* stamp; 192 | int tabsize; 193 | }; 194 | 195 | 196 | void TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding ) 197 | { 198 | assert( now ); 199 | 200 | // Do nothing if the tabsize is 0. 201 | if ( tabsize < 1 ) 202 | { 203 | return; 204 | } 205 | 206 | // Get the current row, column. 207 | int row = cursor.row; 208 | int col = cursor.col; 209 | const char* p = stamp; 210 | assert( p ); 211 | 212 | while ( p < now ) 213 | { 214 | // Treat p as unsigned, so we have a happy compiler. 215 | const unsigned char* pU = (const unsigned char*)p; 216 | 217 | // Code contributed by Fletcher Dunn: (modified by lee) 218 | switch (*pU) { 219 | case 0: 220 | // We *should* never get here, but in case we do, don't 221 | // advance past the terminating null character, ever 222 | return; 223 | 224 | case '\r': 225 | // bump down to the next line 226 | ++row; 227 | col = 0; 228 | // Eat the character 229 | ++p; 230 | 231 | // Check for \r\n sequence, and treat this as a single character 232 | if (*p == '\n') { 233 | ++p; 234 | } 235 | break; 236 | 237 | case '\n': 238 | // bump down to the next line 239 | ++row; 240 | col = 0; 241 | 242 | // Eat the character 243 | ++p; 244 | 245 | // Check for \n\r sequence, and treat this as a single 246 | // character. (Yes, this bizarre thing does occur still 247 | // on some arcane platforms...) 248 | if (*p == '\r') { 249 | ++p; 250 | } 251 | break; 252 | 253 | case '\t': 254 | // Eat the character 255 | ++p; 256 | 257 | // Skip to next tab stop 258 | col = (col / tabsize + 1) * tabsize; 259 | break; 260 | 261 | case TIXML_UTF_LEAD_0: 262 | if ( encoding == TIXML_ENCODING_UTF8 ) 263 | { 264 | if ( *(p+1) && *(p+2) ) 265 | { 266 | // In these cases, don't advance the column. These are 267 | // 0-width spaces. 268 | if ( *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 ) 269 | p += 3; 270 | else if ( *(pU+1)==0xbfU && *(pU+2)==0xbeU ) 271 | p += 3; 272 | else if ( *(pU+1)==0xbfU && *(pU+2)==0xbfU ) 273 | p += 3; 274 | else 275 | { p +=3; ++col; } // A normal character. 276 | } 277 | } 278 | else 279 | { 280 | ++p; 281 | ++col; 282 | } 283 | break; 284 | 285 | default: 286 | if ( encoding == TIXML_ENCODING_UTF8 ) 287 | { 288 | // Eat the 1 to 4 byte utf8 character. 289 | int step = TiXmlBase::utf8ByteTable[*((const unsigned char*)p)]; 290 | if ( step == 0 ) 291 | step = 1; // Error case from bad encoding, but handle gracefully. 292 | p += step; 293 | 294 | // Just advance one column, of course. 295 | ++col; 296 | } 297 | else 298 | { 299 | ++p; 300 | ++col; 301 | } 302 | break; 303 | } 304 | } 305 | cursor.row = row; 306 | cursor.col = col; 307 | assert( cursor.row >= -1 ); 308 | assert( cursor.col >= -1 ); 309 | stamp = p; 310 | assert( stamp ); 311 | } 312 | 313 | 314 | const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding ) 315 | { 316 | if ( !p || !*p ) 317 | { 318 | return 0; 319 | } 320 | if ( encoding == TIXML_ENCODING_UTF8 ) 321 | { 322 | while ( *p ) 323 | { 324 | const unsigned char* pU = (const unsigned char*)p; 325 | 326 | // Skip the stupid Microsoft UTF-8 Byte order marks 327 | if ( *(pU+0)==TIXML_UTF_LEAD_0 328 | && *(pU+1)==TIXML_UTF_LEAD_1 329 | && *(pU+2)==TIXML_UTF_LEAD_2 ) 330 | { 331 | p += 3; 332 | continue; 333 | } 334 | else if(*(pU+0)==TIXML_UTF_LEAD_0 335 | && *(pU+1)==0xbfU 336 | && *(pU+2)==0xbeU ) 337 | { 338 | p += 3; 339 | continue; 340 | } 341 | else if(*(pU+0)==TIXML_UTF_LEAD_0 342 | && *(pU+1)==0xbfU 343 | && *(pU+2)==0xbfU ) 344 | { 345 | p += 3; 346 | continue; 347 | } 348 | 349 | if ( IsWhiteSpace( *p ) ) // Still using old rules for white space. 350 | ++p; 351 | else 352 | break; 353 | } 354 | } 355 | else 356 | { 357 | while ( *p && IsWhiteSpace( *p ) ) 358 | ++p; 359 | } 360 | 361 | return p; 362 | } 363 | 364 | #ifdef TIXML_USE_STL 365 | /*static*/ bool TiXmlBase::StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ) 366 | { 367 | for( ;; ) 368 | { 369 | if ( !in->good() ) return false; 370 | 371 | int c = in->peek(); 372 | // At this scope, we can't get to a document. So fail silently. 373 | if ( !IsWhiteSpace( c ) || c <= 0 ) 374 | return true; 375 | 376 | *tag += (char) in->get(); 377 | } 378 | } 379 | 380 | /*static*/ bool TiXmlBase::StreamTo( std::istream * in, int character, TIXML_STRING * tag ) 381 | { 382 | //assert( character > 0 && character < 128 ); // else it won't work in utf-8 383 | while ( in->good() ) 384 | { 385 | int c = in->peek(); 386 | if ( c == character ) 387 | return true; 388 | if ( c <= 0 ) // Silent failure: can't get document at this scope 389 | return false; 390 | 391 | in->get(); 392 | *tag += (char) c; 393 | } 394 | return false; 395 | } 396 | #endif 397 | 398 | // One of TinyXML's more performance demanding functions. Try to keep the memory overhead down. The 399 | // "assign" optimization removes over 10% of the execution time. 400 | // 401 | const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding ) 402 | { 403 | // Oddly, not supported on some comilers, 404 | //name->clear(); 405 | // So use this: 406 | *name = ""; 407 | assert( p ); 408 | 409 | // Names start with letters or underscores. 410 | // Of course, in unicode, tinyxml has no idea what a letter *is*. The 411 | // algorithm is generous. 412 | // 413 | // After that, they can be letters, underscores, numbers, 414 | // hyphens, or colons. (Colons are valid ony for namespaces, 415 | // but tinyxml can't tell namespaces from names.) 416 | if ( p && *p 417 | && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) ) 418 | { 419 | const char* start = p; 420 | while( p && *p 421 | && ( IsAlphaNum( (unsigned char ) *p, encoding ) 422 | || *p == '_' 423 | || *p == '-' 424 | || *p == '.' 425 | || *p == ':' ) ) 426 | { 427 | //(*name) += *p; // expensive 428 | ++p; 429 | } 430 | if ( p-start > 0 ) { 431 | name->assign( start, p-start ); 432 | } 433 | return p; 434 | } 435 | return 0; 436 | } 437 | 438 | const char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding ) 439 | { 440 | // Presume an entity, and pull it out. 441 | TIXML_STRING ent; 442 | int i; 443 | *length = 0; 444 | 445 | if ( *(p+1) && *(p+1) == '#' && *(p+2) ) 446 | { 447 | unsigned long ucs = 0; 448 | ptrdiff_t delta = 0; 449 | unsigned mult = 1; 450 | 451 | if ( *(p+2) == 'x' ) 452 | { 453 | // Hexadecimal. 454 | if ( !*(p+3) ) return 0; 455 | 456 | const char* q = p+3; 457 | q = strchr( q, ';' ); 458 | 459 | if ( !q || !*q ) return 0; 460 | 461 | delta = q-p; 462 | --q; 463 | 464 | while ( *q != 'x' ) 465 | { 466 | if ( *q >= '0' && *q <= '9' ) 467 | ucs += mult * (*q - '0'); 468 | else if ( *q >= 'a' && *q <= 'f' ) 469 | ucs += mult * (*q - 'a' + 10); 470 | else if ( *q >= 'A' && *q <= 'F' ) 471 | ucs += mult * (*q - 'A' + 10 ); 472 | else 473 | return 0; 474 | mult *= 16; 475 | --q; 476 | } 477 | } 478 | else 479 | { 480 | // Decimal. 481 | if ( !*(p+2) ) return 0; 482 | 483 | const char* q = p+2; 484 | q = strchr( q, ';' ); 485 | 486 | if ( !q || !*q ) return 0; 487 | 488 | delta = q-p; 489 | --q; 490 | 491 | while ( *q != '#' ) 492 | { 493 | if ( *q >= '0' && *q <= '9' ) 494 | ucs += mult * (*q - '0'); 495 | else 496 | return 0; 497 | mult *= 10; 498 | --q; 499 | } 500 | } 501 | if ( encoding == TIXML_ENCODING_UTF8 ) 502 | { 503 | // convert the UCS to UTF-8 504 | ConvertUTF32ToUTF8( ucs, value, length ); 505 | } 506 | else 507 | { 508 | *value = (char)ucs; 509 | *length = 1; 510 | } 511 | return p + delta + 1; 512 | } 513 | 514 | // Now try to match it. 515 | for( i=0; iappend( cArr, len ); 594 | } 595 | } 596 | else 597 | { 598 | bool whitespace = false; 599 | 600 | // Remove leading white space: 601 | p = SkipWhiteSpace( p, encoding ); 602 | while ( p && *p 603 | && !StringEqual( p, endTag, caseInsensitive, encoding ) ) 604 | { 605 | if ( *p == '\r' || *p == '\n' ) 606 | { 607 | whitespace = true; 608 | ++p; 609 | } 610 | else if ( IsWhiteSpace( *p ) ) 611 | { 612 | whitespace = true; 613 | ++p; 614 | } 615 | else 616 | { 617 | // If we've found whitespace, add it before the 618 | // new character. Any whitespace just becomes a space. 619 | if ( whitespace ) 620 | { 621 | (*text) += ' '; 622 | whitespace = false; 623 | } 624 | int len; 625 | char cArr[4] = { 0, 0, 0, 0 }; 626 | p = GetChar( p, cArr, &len, encoding ); 627 | if ( len == 1 ) 628 | (*text) += cArr[0]; // more efficient 629 | else 630 | text->append( cArr, len ); 631 | } 632 | } 633 | } 634 | if ( p && *p ) 635 | p += strlen( endTag ); 636 | return ( p && *p ) ? p : 0; 637 | } 638 | 639 | #ifdef TIXML_USE_STL 640 | 641 | void TiXmlDocument::StreamIn( std::istream * in, TIXML_STRING * tag ) 642 | { 643 | // The basic issue with a document is that we don't know what we're 644 | // streaming. Read something presumed to be a tag (and hope), then 645 | // identify it, and call the appropriate stream method on the tag. 646 | // 647 | // This "pre-streaming" will never read the closing ">" so the 648 | // sub-tag can orient itself. 649 | 650 | if ( !StreamTo( in, '<', tag ) ) 651 | { 652 | SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); 653 | return; 654 | } 655 | 656 | while ( in->good() ) 657 | { 658 | int tagIndex = (int) tag->length(); 659 | while ( in->good() && in->peek() != '>' ) 660 | { 661 | int c = in->get(); 662 | if ( c <= 0 ) 663 | { 664 | SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); 665 | break; 666 | } 667 | (*tag) += (char) c; 668 | } 669 | 670 | if ( in->good() ) 671 | { 672 | // We now have something we presume to be a node of 673 | // some sort. Identify it, and call the node to 674 | // continue streaming. 675 | TiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING ); 676 | 677 | if ( node ) 678 | { 679 | node->StreamIn( in, tag ); 680 | bool isElement = node->ToElement() != 0; 681 | delete node; 682 | node = 0; 683 | 684 | // If this is the root element, we're done. Parsing will be 685 | // done by the >> operator. 686 | if ( isElement ) 687 | { 688 | return; 689 | } 690 | } 691 | else 692 | { 693 | SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); 694 | return; 695 | } 696 | } 697 | } 698 | // We should have returned sooner. 699 | SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); 700 | } 701 | 702 | #endif 703 | 704 | const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding ) 705 | { 706 | ClearError(); 707 | 708 | // Parse away, at the document level. Since a document 709 | // contains nothing but other tags, most of what happens 710 | // here is skipping white space. 711 | if ( !p || !*p ) 712 | { 713 | SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); 714 | return 0; 715 | } 716 | 717 | // Note that, for a document, this needs to come 718 | // before the while space skip, so that parsing 719 | // starts from the pointer we are given. 720 | location.Clear(); 721 | if ( prevData ) 722 | { 723 | location.row = prevData->cursor.row; 724 | location.col = prevData->cursor.col; 725 | } 726 | else 727 | { 728 | location.row = 0; 729 | location.col = 0; 730 | } 731 | TiXmlParsingData data( p, TabSize(), location.row, location.col ); 732 | location = data.Cursor(); 733 | 734 | if ( encoding == TIXML_ENCODING_UNKNOWN ) 735 | { 736 | // Check for the Microsoft UTF-8 lead bytes. 737 | const unsigned char* pU = (const unsigned char*)p; 738 | if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0 739 | && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1 740 | && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 ) 741 | { 742 | encoding = TIXML_ENCODING_UTF8; 743 | useMicrosoftBOM = true; 744 | } 745 | } 746 | 747 | p = SkipWhiteSpace( p, encoding ); 748 | if ( !p ) 749 | { 750 | SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); 751 | return 0; 752 | } 753 | 754 | while ( p && *p ) 755 | { 756 | TiXmlNode* node = Identify( p, encoding ); 757 | if ( node ) 758 | { 759 | p = node->Parse( p, &data, encoding ); 760 | LinkEndChild( node ); 761 | } 762 | else 763 | { 764 | break; 765 | } 766 | 767 | // Did we get encoding info? 768 | if ( encoding == TIXML_ENCODING_UNKNOWN 769 | && node->ToDeclaration() ) 770 | { 771 | TiXmlDeclaration* dec = node->ToDeclaration(); 772 | const char* enc = dec->Encoding(); 773 | assert( enc ); 774 | 775 | if ( *enc == 0 ) 776 | encoding = TIXML_ENCODING_UTF8; 777 | else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) ) 778 | encoding = TIXML_ENCODING_UTF8; 779 | else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) ) 780 | encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice 781 | else 782 | encoding = TIXML_ENCODING_LEGACY; 783 | } 784 | 785 | p = SkipWhiteSpace( p, encoding ); 786 | } 787 | 788 | // Was this empty? 789 | if ( !firstChild ) { 790 | SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding ); 791 | return 0; 792 | } 793 | 794 | // All is well. 795 | return p; 796 | } 797 | 798 | void TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding ) 799 | { 800 | // The first error in a chain is more accurate - don't set again! 801 | if ( error ) 802 | return; 803 | 804 | assert( err > 0 && err < TIXML_ERROR_STRING_COUNT ); 805 | error = true; 806 | errorId = err; 807 | errorDesc = errorString[ errorId ]; 808 | 809 | errorLocation.Clear(); 810 | if ( pError && data ) 811 | { 812 | data->Stamp( pError, encoding ); 813 | errorLocation = data->Cursor(); 814 | } 815 | } 816 | 817 | 818 | TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding ) 819 | { 820 | TiXmlNode* returnNode = 0; 821 | 822 | p = SkipWhiteSpace( p, encoding ); 823 | if( !p || !*p || *p != '<' ) 824 | { 825 | return 0; 826 | } 827 | 828 | p = SkipWhiteSpace( p, encoding ); 829 | 830 | if ( !p || !*p ) 831 | { 832 | return 0; 833 | } 834 | 835 | // What is this thing? 836 | // - Elements start with a letter or underscore, but xml is reserved. 837 | // - Comments: "; 1351 | 1352 | if ( !StringEqual( p, startTag, false, encoding ) ) 1353 | { 1354 | if ( document ) 1355 | document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding ); 1356 | return 0; 1357 | } 1358 | p += strlen( startTag ); 1359 | 1360 | // [ 1475201 ] TinyXML parses entities in comments 1361 | // Oops - ReadText doesn't work, because we don't want to parse the entities. 1362 | // p = ReadText( p, &value, false, endTag, false, encoding ); 1363 | // 1364 | // from the XML spec: 1365 | /* 1366 | [Definition: Comments may appear anywhere in a document outside other markup; in addition, 1367 | they may appear within the document type declaration at places allowed by the grammar. 1368 | They are not part of the document's character data; an XML processor MAY, but need not, 1369 | make it possible for an application to retrieve the text of comments. For compatibility, 1370 | the string "--" (double-hyphen) MUST NOT occur within comments.] Parameter entity 1371 | references MUST NOT be recognized within comments. 1372 | 1373 | An example of a comment: 1374 | 1375 | 1376 | */ 1377 | 1378 | value = ""; 1379 | // Keep all the white space. 1380 | while ( p && *p && !StringEqual( p, endTag, false, encoding ) ) 1381 | { 1382 | value.append( p, 1 ); 1383 | ++p; 1384 | } 1385 | if ( p && *p ) 1386 | p += strlen( endTag ); 1387 | 1388 | return p; 1389 | } 1390 | 1391 | 1392 | const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) 1393 | { 1394 | p = SkipWhiteSpace( p, encoding ); 1395 | if ( !p || !*p ) return 0; 1396 | 1397 | if ( data ) 1398 | { 1399 | data->Stamp( p, encoding ); 1400 | location = data->Cursor(); 1401 | } 1402 | // Read the name, the '=' and the value. 1403 | const char* pErr = p; 1404 | p = ReadName( p, &name, encoding ); 1405 | if ( !p || !*p ) 1406 | { 1407 | if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); 1408 | return 0; 1409 | } 1410 | p = SkipWhiteSpace( p, encoding ); 1411 | if ( !p || !*p || *p != '=' ) 1412 | { 1413 | if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); 1414 | return 0; 1415 | } 1416 | 1417 | ++p; // skip '=' 1418 | p = SkipWhiteSpace( p, encoding ); 1419 | if ( !p || !*p ) 1420 | { 1421 | if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); 1422 | return 0; 1423 | } 1424 | 1425 | const char* end; 1426 | const char SINGLE_QUOTE = '\''; 1427 | const char DOUBLE_QUOTE = '\"'; 1428 | 1429 | if ( *p == SINGLE_QUOTE ) 1430 | { 1431 | ++p; 1432 | end = "\'"; // single quote in string 1433 | p = ReadText( p, &value, false, end, false, encoding ); 1434 | } 1435 | else if ( *p == DOUBLE_QUOTE ) 1436 | { 1437 | ++p; 1438 | end = "\""; // double quote in string 1439 | p = ReadText( p, &value, false, end, false, encoding ); 1440 | } 1441 | else 1442 | { 1443 | // All attribute values should be in single or double quotes. 1444 | // But this is such a common error that the parser will try 1445 | // its best, even without them. 1446 | value = ""; 1447 | while ( p && *p // existence 1448 | && !IsWhiteSpace( *p ) // whitespace 1449 | && *p != '/' && *p != '>' ) // tag end 1450 | { 1451 | if ( *p == SINGLE_QUOTE || *p == DOUBLE_QUOTE ) { 1452 | // [ 1451649 ] Attribute values with trailing quotes not handled correctly 1453 | // We did not have an opening quote but seem to have a 1454 | // closing one. Give up and throw an error. 1455 | if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); 1456 | return 0; 1457 | } 1458 | value += *p; 1459 | ++p; 1460 | } 1461 | } 1462 | return p; 1463 | } 1464 | 1465 | #ifdef TIXML_USE_STL 1466 | void TiXmlText::StreamIn( std::istream * in, TIXML_STRING * tag ) 1467 | { 1468 | while ( in->good() ) 1469 | { 1470 | int c = in->peek(); 1471 | if ( !cdata && (c == '<' ) ) 1472 | { 1473 | return; 1474 | } 1475 | if ( c <= 0 ) 1476 | { 1477 | TiXmlDocument* document = GetDocument(); 1478 | if ( document ) 1479 | document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); 1480 | return; 1481 | } 1482 | 1483 | (*tag) += (char) c; 1484 | in->get(); // "commits" the peek made above 1485 | 1486 | if ( cdata && c == '>' && tag->size() >= 3 ) { 1487 | size_t len = tag->size(); 1488 | if ( (*tag)[len-2] == ']' && (*tag)[len-3] == ']' ) { 1489 | // terminator of cdata. 1490 | return; 1491 | } 1492 | } 1493 | } 1494 | } 1495 | #endif 1496 | 1497 | const char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) 1498 | { 1499 | value = ""; 1500 | TiXmlDocument* document = GetDocument(); 1501 | 1502 | if ( data ) 1503 | { 1504 | data->Stamp( p, encoding ); 1505 | location = data->Cursor(); 1506 | } 1507 | 1508 | const char* const startTag = ""; 1510 | 1511 | if ( cdata || StringEqual( p, startTag, false, encoding ) ) 1512 | { 1513 | cdata = true; 1514 | 1515 | if ( !StringEqual( p, startTag, false, encoding ) ) 1516 | { 1517 | if ( document ) 1518 | document->SetError( TIXML_ERROR_PARSING_CDATA, p, data, encoding ); 1519 | return 0; 1520 | } 1521 | p += strlen( startTag ); 1522 | 1523 | // Keep all the white space, ignore the encoding, etc. 1524 | while ( p && *p 1525 | && !StringEqual( p, endTag, false, encoding ) 1526 | ) 1527 | { 1528 | value += *p; 1529 | ++p; 1530 | } 1531 | 1532 | TIXML_STRING dummy; 1533 | p = ReadText( p, &dummy, false, endTag, false, encoding ); 1534 | return p; 1535 | } 1536 | else 1537 | { 1538 | bool ignoreWhite = true; 1539 | 1540 | const char* end = "<"; 1541 | p = ReadText( p, &value, ignoreWhite, end, false, encoding ); 1542 | if ( p && *p ) 1543 | return p-1; // don't truncate the '<' 1544 | return 0; 1545 | } 1546 | } 1547 | 1548 | #ifdef TIXML_USE_STL 1549 | void TiXmlDeclaration::StreamIn( std::istream * in, TIXML_STRING * tag ) 1550 | { 1551 | while ( in->good() ) 1552 | { 1553 | int c = in->get(); 1554 | if ( c <= 0 ) 1555 | { 1556 | TiXmlDocument* document = GetDocument(); 1557 | if ( document ) 1558 | document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); 1559 | return; 1560 | } 1561 | (*tag) += (char) c; 1562 | 1563 | if ( c == '>' ) 1564 | { 1565 | // All is well. 1566 | return; 1567 | } 1568 | } 1569 | } 1570 | #endif 1571 | 1572 | const char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding ) 1573 | { 1574 | p = SkipWhiteSpace( p, _encoding ); 1575 | // Find the beginning, find the end, and look for 1576 | // the stuff in-between. 1577 | TiXmlDocument* document = GetDocument(); 1578 | if ( !p || !*p || !StringEqual( p, "SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding ); 1581 | return 0; 1582 | } 1583 | if ( data ) 1584 | { 1585 | data->Stamp( p, _encoding ); 1586 | location = data->Cursor(); 1587 | } 1588 | p += 5; 1589 | 1590 | version = ""; 1591 | encoding = ""; 1592 | standalone = ""; 1593 | 1594 | while ( p && *p ) 1595 | { 1596 | if ( *p == '>' ) 1597 | { 1598 | ++p; 1599 | return p; 1600 | } 1601 | 1602 | p = SkipWhiteSpace( p, _encoding ); 1603 | if ( StringEqual( p, "version", true, _encoding ) ) 1604 | { 1605 | TiXmlAttribute attrib; 1606 | p = attrib.Parse( p, data, _encoding ); 1607 | version = attrib.Value(); 1608 | } 1609 | else if ( StringEqual( p, "encoding", true, _encoding ) ) 1610 | { 1611 | TiXmlAttribute attrib; 1612 | p = attrib.Parse( p, data, _encoding ); 1613 | encoding = attrib.Value(); 1614 | } 1615 | else if ( StringEqual( p, "standalone", true, _encoding ) ) 1616 | { 1617 | TiXmlAttribute attrib; 1618 | p = attrib.Parse( p, data, _encoding ); 1619 | standalone = attrib.Value(); 1620 | } 1621 | else 1622 | { 1623 | // Read over whatever it is. 1624 | while( p && *p && *p != '>' && !IsWhiteSpace( *p ) ) 1625 | ++p; 1626 | } 1627 | } 1628 | return 0; 1629 | } 1630 | 1631 | bool TiXmlText::Blank() const 1632 | { 1633 | for ( unsigned i=0; i 5 | 6 | #include "TmtEcStructs.h" 7 | 8 | class CommandQueue { 9 | 10 | public: std::queue cmdQueue; 11 | private: CommandQueue(); 12 | 13 | public: static CommandQueue* instance(); 14 | 15 | private: static CommandQueue* pInstance; 16 | 17 | public: void addToQueue(PdoEntryValue pdoEntryValue); 18 | public: PdoEntryValue getNext(); 19 | public: bool isEmpty(); 20 | }; 21 | 22 | #endif /* INCLUDE_COMMANDQUEUE_H_ */ 23 | -------------------------------------------------------------------------------- /include/ConfigLoader.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_CONFIGLOADER_H_ 2 | #define INCLUDE_CONFIGLOADER_H_ 3 | 4 | #include "ecrt.h" 5 | #include "tinystr.h" 6 | #include "tinyxml.h" 7 | #include 8 | #include 9 | #include "SlaveConfig.h" 10 | 11 | 12 | class ConfigLoader { 13 | 14 | public: ConfigLoader(); 15 | 16 | public: void applyConfiguration(ec_master_t *master, ec_domain *domain1, std::vector *slaveConfigVector); 17 | 18 | public: std::vector loadConfiguration(ec_master_t *master, std::string configFile); 19 | 20 | private: std::vector loadPdoInfo(const char* type, TiXmlElement *deviceElem, int smIndex, int n_pdos); 21 | 22 | private: unsigned int charToInt(const char* input); 23 | private: unsigned int hexCharToInt(const char* input); 24 | 25 | private: int countChildren(TiXmlNode* parent); 26 | 27 | private: int countChildren(TiXmlNode* parent, const char* name); 28 | private: int countPdoChildren(TiXmlNode* parent, const char* name, int smIndex); 29 | 30 | private: void printSyncs(int nSyncs, ec_sync_info_t *sync); 31 | }; 32 | 33 | #endif /* INCLUDE_CONFIGLOADER_H_ */ 34 | -------------------------------------------------------------------------------- /include/CyclicMotor.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INCLUDE_CYCLICMOTOR_H_ 3 | #define INCLUDE_CYCLICMOTOR_H_ 4 | 5 | #include "ecrt.h" 6 | #include 7 | #include "SlaveConfig.h" 8 | 9 | 10 | class CyclicMotor { 11 | 12 | private: ec_master_t *master; 13 | private: ec_domain *domain1; 14 | private: uint8_t *domain1_pd; 15 | private: std::vector slaves; 16 | 17 | public: CyclicMotor(); 18 | 19 | public: CyclicMotor(ec_master_t *master, ec_domain *domain1, unsigned int *off_dig_out, 20 | unsigned int *bp_dig_out, uint8_t *domain1_pd); 21 | 22 | public: CyclicMotor(ec_master_t *master, ec_domain *domain1, uint8_t *domain1_pd, std::vector slaves); 23 | 24 | public: void start(); 25 | 26 | public: void startup(); 27 | 28 | public: void stop(); 29 | 30 | public: void cyclic_task(ec_master_t *master, ec_domain *domain1, unsigned int *off_dig_out, 31 | unsigned int *bp_dig_out, uint8_t *domain1_pd); 32 | public: void cyclic_task(ec_master_t *master, ec_domain *domain1, uint8_t *domain1_pd, std::vector slaves); 33 | 34 | }; 35 | 36 | #endif /* INCLUDE_CYCLICMOTOR_H_ */ 37 | -------------------------------------------------------------------------------- /include/EtherCatServer.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_ETHERCATSERVER_H_ 2 | #define INCLUDE_ETHERCATSERVER_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include "PdoEntryCache.h" 8 | #include "CyclicMotor.h" 9 | #include "ConfigLoader.h" 10 | 11 | class EtherCatServer { 12 | 13 | private: PdoEntryCache pdoEntryCache; 14 | private: CyclicMotor cyclicMotor; 15 | private: ConfigLoader configLoader; 16 | 17 | public: EtherCatServer(); 18 | public: void startServer(); 19 | public: void stopServer(); 20 | public: int configServer(std::string configFile); 21 | public: std::vector getDeviceNames(); 22 | public: std::vector getParameterNames(std::string deviceName); 23 | 24 | public: void setParameterValue(std::string deviceName, std::string parameterName, int value); 25 | public: void setParameterValue(unsigned int alias, int offset, std::string parameterName, int value); 26 | private: void setParameterValue(std::string deviceName, unsigned int alias, int offset, std::string parameterName, int value); 27 | 28 | public: int getParameterValue(std::string deviceName, std::string parameterName); 29 | public: int getParameterValue(unsigned int alias, int offset, std::string parameterName); 30 | private: int getParameterValue(std::string deviceName, unsigned int alias, int offset, std::string parameterName); 31 | 32 | private: int tempStartup(); 33 | }; 34 | 35 | #endif /* INCLUDE_ETHERCATSERVER_H_ */ 36 | -------------------------------------------------------------------------------- /include/NameMap.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_NAMEMAP_H_ 2 | #define INCLUDE_NAMEMAP_H_ 3 | 4 | #include 5 | #include 6 | #include "TmtEcStructs.h" 7 | 8 | class NameMap { 9 | 10 | public: NameMap(); 11 | 12 | public: std::vector getDevices(); 13 | 14 | public: std::vector getParameters(Device device); 15 | 16 | public: Parameter getParameter(Device device, std::string paramName); 17 | 18 | }; 19 | 20 | #endif /* INCLUDE_NAMEMAP_H_ */ 21 | -------------------------------------------------------------------------------- /include/Pdo.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_PDO_H_ 2 | #define INCLUDE_PDO_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include "PdoEntry.h" 8 | 9 | class Pdo { 10 | 11 | public: Pdo(std::string name, int index, int n_entries); 12 | 13 | public: std::string name; 14 | public: uint16_t index; 15 | public: unsigned int n_entries; 16 | public: std::vector pdoEntries; 17 | 18 | }; 19 | 20 | #endif /* INCLUDE_PDO_H_ */ 21 | -------------------------------------------------------------------------------- /include/PdoEntry.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_PDOENTRY_H_ 2 | #define INCLUDE_PDOENTRY_H_ 3 | 4 | #include 5 | 6 | class PdoEntry { 7 | 8 | public: PdoEntry(std::string entryName, std::string pdoName, int index, int subindex, int bitLength); 9 | 10 | public: std::string entryName; 11 | public: std::string fullName; 12 | public: uint16_t index; 13 | public: uint8_t subindex; 14 | public: uint8_t bitLength; 15 | public: unsigned int domainOffset; 16 | public: unsigned int domainBitPos; 17 | 18 | }; 19 | 20 | #endif /* INCLUDE_PDOENTRY_H_ */ 21 | -------------------------------------------------------------------------------- /include/PdoEntryCache.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INCLUDE_PDOENTRYCACHE_H_ 3 | #define INCLUDE_PDOENTRYCACHE_H_ 4 | 5 | #include "TmtEcStructs.h" 6 | #include 7 | 8 | class PdoEntryCache { 9 | 10 | private: std::map cacheMap; 11 | 12 | public: static PdoEntryCache* instance(); 13 | 14 | private: static PdoEntryCache* pInstance; 15 | 16 | public: void updatePdoEntryValue(int slaveId, int pdoEntryId, int value); 17 | public: int getPdoEntryValue(int slaveId, int pdoEntryId); 18 | }; 19 | 20 | #endif /* INCLUDE_PDOENTRYCACHE_H_ */ 21 | -------------------------------------------------------------------------------- /include/SlaveConfig.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_SLAVECONFIG_H_ 2 | #define INCLUDE_SLAVECONFIG_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include "PdoEntry.h" 8 | #include "Pdo.h" 9 | #include "SyncManager.h" 10 | 11 | 12 | class SlaveConfig { 13 | 14 | public: SlaveConfig(std::string name, unsigned int vendor_id, unsigned int product_code, unsigned int alias, unsigned int pos); 15 | 16 | public: std::string name; 17 | public: unsigned int vendor_id; 18 | public: unsigned int product_code; 19 | public: unsigned int alias; 20 | public: unsigned int pos; 21 | public: unsigned int *domain_offset; 22 | public: unsigned int *domain_bit_pos; 23 | public: std::vector syncs; // full tree for PDO Configuration 24 | public: std::vector pdoEntries; 25 | public: std::vector pdos; 26 | 27 | }; 28 | 29 | #endif /* INCLUDE_SLAVECONFIG_H_ */ 30 | -------------------------------------------------------------------------------- /include/SyncManager.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_SYNCMANAGER_H_ 2 | #define INCLUDE_SYNCMANAGER_H_ 3 | 4 | #include 5 | 6 | #include "ecrt.h" 7 | #include "Pdo.h" 8 | 9 | class SyncManager { 10 | 11 | public: SyncManager(uint8_t index, ec_direction_t dir, ec_watchdog_mode_t watchdog_mode); 12 | 13 | public: uint8_t index; 14 | public: ec_direction_t dir; 15 | public: ec_watchdog_mode_t watchdog_mode; 16 | public: std::vector pdos; 17 | 18 | }; 19 | 20 | #endif /* INCLUDE_SYNCMANAGER_H_ */ 21 | -------------------------------------------------------------------------------- /include/TmtEcStructs.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INCLUDE_TMTECSTRUCTS_H_ 3 | #define INCLUDE_TMTECSTRUCTS_H_ 4 | 5 | struct PdoEntryValue { 6 | unsigned int slaveIndex; 7 | unsigned int pdoEntryIndex; 8 | unsigned int entryValue; 9 | }; 10 | 11 | 12 | struct Device { 13 | int deviceId; 14 | int slaveId; 15 | char *deviceName; 16 | }; 17 | 18 | struct Parameter { 19 | int parameterId; 20 | int slaveId; 21 | int pdoEntryId; 22 | char *parameterName; 23 | }; 24 | 25 | #endif /* INCLUDE_TMTECSTRUCTS_H_ */ 26 | -------------------------------------------------------------------------------- /include/temp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * temp.h 3 | * 4 | * Created on: Nov 3, 2014 5 | * Author: smichaels 6 | */ 7 | 8 | #ifndef INCLUDE_TEMP_H_ 9 | #define INCLUDE_TEMP_H_ 10 | 11 | #define FREQUENCY 100 12 | 13 | #endif /* INCLUDE_TEMP_H_ */ 14 | -------------------------------------------------------------------------------- /include/tinystr.h: -------------------------------------------------------------------------------- 1 | /* 2 | www.sourceforge.net/projects/tinyxml 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any 6 | damages arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any 9 | purpose, including commercial applications, and to alter it and 10 | redistribute it freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must 13 | not claim that you wrote the original software. If you use this 14 | software in a product, an acknowledgment in the product documentation 15 | would be appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and 18 | must not be misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | 25 | #ifndef TIXML_USE_STL 26 | 27 | #ifndef TIXML_STRING_INCLUDED 28 | #define TIXML_STRING_INCLUDED 29 | 30 | #include 31 | #include 32 | 33 | /* The support for explicit isn't that universal, and it isn't really 34 | required - it is used to check that the TiXmlString class isn't incorrectly 35 | used. Be nice to old compilers and macro it here: 36 | */ 37 | #if defined(_MSC_VER) && (_MSC_VER >= 1200 ) 38 | // Microsoft visual studio, version 6 and higher. 39 | #define TIXML_EXPLICIT explicit 40 | #elif defined(__GNUC__) && (__GNUC__ >= 3 ) 41 | // GCC version 3 and higher.s 42 | #define TIXML_EXPLICIT explicit 43 | #else 44 | #define TIXML_EXPLICIT 45 | #endif 46 | 47 | 48 | /* 49 | TiXmlString is an emulation of a subset of the std::string template. 50 | Its purpose is to allow compiling TinyXML on compilers with no or poor STL support. 51 | Only the member functions relevant to the TinyXML project have been implemented. 52 | The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase 53 | a string and there's no more room, we allocate a buffer twice as big as we need. 54 | */ 55 | class TiXmlString 56 | { 57 | public : 58 | // The size type used 59 | typedef size_t size_type; 60 | 61 | // Error value for find primitive 62 | static const size_type npos; // = -1; 63 | 64 | 65 | // TiXmlString empty constructor 66 | TiXmlString () : rep_(&nullrep_) 67 | { 68 | } 69 | 70 | // TiXmlString copy constructor 71 | TiXmlString ( const TiXmlString & copy) : rep_(0) 72 | { 73 | init(copy.length()); 74 | memcpy(start(), copy.data(), length()); 75 | } 76 | 77 | // TiXmlString constructor, based on a string 78 | TIXML_EXPLICIT TiXmlString ( const char * copy) : rep_(0) 79 | { 80 | init( static_cast( strlen(copy) )); 81 | memcpy(start(), copy, length()); 82 | } 83 | 84 | // TiXmlString constructor, based on a string 85 | TIXML_EXPLICIT TiXmlString ( const char * str, size_type len) : rep_(0) 86 | { 87 | init(len); 88 | memcpy(start(), str, len); 89 | } 90 | 91 | // TiXmlString destructor 92 | ~TiXmlString () 93 | { 94 | quit(); 95 | } 96 | 97 | TiXmlString& operator = (const char * copy) 98 | { 99 | return assign( copy, (size_type)strlen(copy)); 100 | } 101 | 102 | TiXmlString& operator = (const TiXmlString & copy) 103 | { 104 | return assign(copy.start(), copy.length()); 105 | } 106 | 107 | 108 | // += operator. Maps to append 109 | TiXmlString& operator += (const char * suffix) 110 | { 111 | return append(suffix, static_cast( strlen(suffix) )); 112 | } 113 | 114 | // += operator. Maps to append 115 | TiXmlString& operator += (char single) 116 | { 117 | return append(&single, 1); 118 | } 119 | 120 | // += operator. Maps to append 121 | TiXmlString& operator += (const TiXmlString & suffix) 122 | { 123 | return append(suffix.data(), suffix.length()); 124 | } 125 | 126 | 127 | // Convert a TiXmlString into a null-terminated char * 128 | const char * c_str () const { return rep_->str; } 129 | 130 | // Convert a TiXmlString into a char * (need not be null terminated). 131 | const char * data () const { return rep_->str; } 132 | 133 | // Return the length of a TiXmlString 134 | size_type length () const { return rep_->size; } 135 | 136 | // Alias for length() 137 | size_type size () const { return rep_->size; } 138 | 139 | // Checks if a TiXmlString is empty 140 | bool empty () const { return rep_->size == 0; } 141 | 142 | // Return capacity of string 143 | size_type capacity () const { return rep_->capacity; } 144 | 145 | 146 | // single char extraction 147 | const char& at (size_type index) const 148 | { 149 | assert( index < length() ); 150 | return rep_->str[ index ]; 151 | } 152 | 153 | // [] operator 154 | char& operator [] (size_type index) const 155 | { 156 | assert( index < length() ); 157 | return rep_->str[ index ]; 158 | } 159 | 160 | // find a char in a string. Return TiXmlString::npos if not found 161 | size_type find (char lookup) const 162 | { 163 | return find(lookup, 0); 164 | } 165 | 166 | // find a char in a string from an offset. Return TiXmlString::npos if not found 167 | size_type find (char tofind, size_type offset) const 168 | { 169 | if (offset >= length()) return npos; 170 | 171 | for (const char* p = c_str() + offset; *p != '\0'; ++p) 172 | { 173 | if (*p == tofind) return static_cast< size_type >( p - c_str() ); 174 | } 175 | return npos; 176 | } 177 | 178 | void clear () 179 | { 180 | //Lee: 181 | //The original was just too strange, though correct: 182 | // TiXmlString().swap(*this); 183 | //Instead use the quit & re-init: 184 | quit(); 185 | init(0,0); 186 | } 187 | 188 | /* Function to reserve a big amount of data when we know we'll need it. Be aware that this 189 | function DOES NOT clear the content of the TiXmlString if any exists. 190 | */ 191 | void reserve (size_type cap); 192 | 193 | TiXmlString& assign (const char* str, size_type len); 194 | 195 | TiXmlString& append (const char* str, size_type len); 196 | 197 | void swap (TiXmlString& other) 198 | { 199 | Rep* r = rep_; 200 | rep_ = other.rep_; 201 | other.rep_ = r; 202 | } 203 | 204 | private: 205 | 206 | void init(size_type sz) { init(sz, sz); } 207 | void set_size(size_type sz) { rep_->str[ rep_->size = sz ] = '\0'; } 208 | char* start() const { return rep_->str; } 209 | char* finish() const { return rep_->str + rep_->size; } 210 | 211 | struct Rep 212 | { 213 | size_type size, capacity; 214 | char str[1]; 215 | }; 216 | 217 | void init(size_type sz, size_type cap) 218 | { 219 | if (cap) 220 | { 221 | // Lee: the original form: 222 | // rep_ = static_cast(operator new(sizeof(Rep) + cap)); 223 | // doesn't work in some cases of new being overloaded. Switching 224 | // to the normal allocation, although use an 'int' for systems 225 | // that are overly picky about structure alignment. 226 | const size_type bytesNeeded = sizeof(Rep) + cap; 227 | const size_type intsNeeded = ( bytesNeeded + sizeof(int) - 1 ) / sizeof( int ); 228 | rep_ = reinterpret_cast( new int[ intsNeeded ] ); 229 | 230 | rep_->str[ rep_->size = sz ] = '\0'; 231 | rep_->capacity = cap; 232 | } 233 | else 234 | { 235 | rep_ = &nullrep_; 236 | } 237 | } 238 | 239 | void quit() 240 | { 241 | if (rep_ != &nullrep_) 242 | { 243 | // The rep_ is really an array of ints. (see the allocator, above). 244 | // Cast it back before delete, so the compiler won't incorrectly call destructors. 245 | delete [] ( reinterpret_cast( rep_ ) ); 246 | } 247 | } 248 | 249 | Rep * rep_; 250 | static Rep nullrep_; 251 | 252 | } ; 253 | 254 | 255 | inline bool operator == (const TiXmlString & a, const TiXmlString & b) 256 | { 257 | return ( a.length() == b.length() ) // optimization on some platforms 258 | && ( strcmp(a.c_str(), b.c_str()) == 0 ); // actual compare 259 | } 260 | inline bool operator < (const TiXmlString & a, const TiXmlString & b) 261 | { 262 | return strcmp(a.c_str(), b.c_str()) < 0; 263 | } 264 | 265 | inline bool operator != (const TiXmlString & a, const TiXmlString & b) { return !(a == b); } 266 | inline bool operator > (const TiXmlString & a, const TiXmlString & b) { return b < a; } 267 | inline bool operator <= (const TiXmlString & a, const TiXmlString & b) { return !(b < a); } 268 | inline bool operator >= (const TiXmlString & a, const TiXmlString & b) { return !(a < b); } 269 | 270 | inline bool operator == (const TiXmlString & a, const char* b) { return strcmp(a.c_str(), b) == 0; } 271 | inline bool operator == (const char* a, const TiXmlString & b) { return b == a; } 272 | inline bool operator != (const TiXmlString & a, const char* b) { return !(a == b); } 273 | inline bool operator != (const char* a, const TiXmlString & b) { return !(b == a); } 274 | 275 | TiXmlString operator + (const TiXmlString & a, const TiXmlString & b); 276 | TiXmlString operator + (const TiXmlString & a, const char* b); 277 | TiXmlString operator + (const char* a, const TiXmlString & b); 278 | 279 | 280 | /* 281 | TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString. 282 | Only the operators that we need for TinyXML have been developped. 283 | */ 284 | class TiXmlOutStream : public TiXmlString 285 | { 286 | public : 287 | 288 | // TiXmlOutStream << operator. 289 | TiXmlOutStream & operator << (const TiXmlString & in) 290 | { 291 | *this += in; 292 | return *this; 293 | } 294 | 295 | // TiXmlOutStream << operator. 296 | TiXmlOutStream & operator << (const char * in) 297 | { 298 | *this += in; 299 | return *this; 300 | } 301 | 302 | } ; 303 | 304 | #endif // TIXML_STRING_INCLUDED 305 | #endif // TIXML_USE_STL 306 | -------------------------------------------------------------------------------- /src/CommandQueue.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "TmtEcStructs.h" 4 | #include 5 | #include "CommandQueue.h" 6 | 7 | 8 | // Singleton pattern stuff 9 | 10 | CommandQueue* CommandQueue::pInstance = 0; 11 | 12 | CommandQueue* CommandQueue::instance() { 13 | if (pInstance == 0) { 14 | pInstance = new CommandQueue; 15 | } 16 | return pInstance; 17 | } 18 | 19 | CommandQueue::CommandQueue() { 20 | cmdQueue = std::queue(); 21 | }; 22 | 23 | // TODO: this class is not thread-safe. We need to implement locking. 24 | 25 | void CommandQueue::addToQueue(PdoEntryValue pdoEntryValue) { 26 | cmdQueue.push(pdoEntryValue); 27 | 28 | } 29 | 30 | PdoEntryValue CommandQueue::getNext() { 31 | 32 | PdoEntryValue pdoEntryValue = cmdQueue.front(); 33 | cmdQueue.pop(); 34 | 35 | return pdoEntryValue; 36 | }; 37 | 38 | bool CommandQueue::isEmpty() { 39 | 40 | return cmdQueue.empty(); 41 | }; 42 | 43 | -------------------------------------------------------------------------------- /src/ConfigLoader.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "ecrt.h" 5 | #include "tinystr.h" 6 | #include "tinyxml.h" 7 | #include "temp.h" 8 | #include 9 | #include 10 | 11 | #include "PdoEntry.h" 12 | #include "Pdo.h" 13 | #include "SyncManager.h" 14 | #include "SlaveConfig.h" 15 | #include "ConfigLoader.h" 16 | 17 | 18 | ConfigLoader::ConfigLoader() { 19 | }; 20 | 21 | void ConfigLoader::applyConfiguration(ec_master_t *master, ec_domain *domain1, std::vector *slaveConfigVector) { 22 | // configure all slaves and domain for each pdo entry of each slave 23 | 24 | //std::cout << "\ngot here::" << slaveConfigVector->size() << "\n"; 25 | 26 | ec_slave_config_t *sc; 27 | for (unsigned int si = 0; si < slaveConfigVector->size(); si++) { 28 | 29 | //std::cout << "\nin loop: si = " << si << "\n"; 30 | 31 | SlaveConfig slaveConfig = slaveConfigVector->at(si); 32 | 33 | // TEST ONLY 34 | std::vector syncManagerVector = slaveConfig.syncs; 35 | 36 | /* 37 | * code to generate sync structure for convienience api 38 | * 39 | // here we generate the structs 40 | ec_sync_info_t* syncs = new ec_sync_info_t[syncManagerVector.size()]; 41 | 42 | std::cout << "syncs size = " << syncManagerVector.size(); 43 | 44 | for (unsigned int i=0; iat(si).pdoEntries.at(i).domainOffset = offset; 147 | slaveConfigVector->at(si).pdoEntries.at(i).domainBitPos = bitpos; 148 | 149 | std::cout << std::hex << "\ndomain registration: offset = " << slaveConfigVector->at(si).pdoEntries.at(i).domainOffset << ", bit pos = " << slaveConfigVector->at(si).pdoEntries.at(i).domainBitPos; 150 | 151 | } 152 | 153 | } 154 | printf("\n"); 155 | } 156 | 157 | 158 | std::vector ConfigLoader::loadConfiguration(ec_master_t *master, std::string configFile) { 159 | 160 | // Decision: there is no public API for getting PDO entries, etc. This information *could* be obtained using the source 161 | // from the 'tool' area, but this appears to be fairly complex and would be time consuming. The information we want is still 162 | // available from the 'tool' commands, and to support new versions, it is probably cleaner to just use the command line tools 163 | // as is and use the tool outputs directly, rather than trying to recreate the tools in other code. 164 | 165 | std::vector slaveConfigList; 166 | 167 | TiXmlNode* pChild; 168 | 169 | TiXmlDocument doc(configFile.c_str()); 170 | doc.LoadFile(); 171 | printf("\n%s:\n", configFile.c_str()); 172 | 173 | TiXmlNode* root = doc.FirstChild("EtherCATInfoList"); 174 | 175 | // loop over slaves 176 | int slaveIndex = -1; 177 | for (pChild = root->FirstChild(); pChild != 0; pChild = pChild->NextSibling()) { 178 | 179 | slaveIndex++; 180 | std::cout << "\n child value = " << pChild->Value(); 181 | 182 | ec_slave_info_t *slave_info = new ec_slave_info_t; 183 | // get the slave alias 184 | int success = ecrt_master_get_slave (master, slaveIndex, slave_info); 185 | 186 | // Slave Information 187 | 188 | TiXmlElement* vendorElem = pChild->FirstChildElement("Vendor"); 189 | TiXmlElement* idElem = vendorElem->FirstChildElement("Id"); 190 | TiXmlText* idValueText = idElem->FirstChild()->ToText(); 191 | 192 | std::cout << "\n vendor id: " << idValueText->Value(); 193 | 194 | TiXmlElement* descriptionsElem = pChild->FirstChildElement("Descriptions"); 195 | TiXmlElement* devicesElem = descriptionsElem->FirstChildElement("Devices"); 196 | TiXmlElement* deviceElem = devicesElem->FirstChildElement("Device"); 197 | TiXmlElement* typeElem = deviceElem->FirstChildElement("Type"); 198 | 199 | TiXmlText* typeValueText = typeElem->FirstChild()->ToText(); 200 | 201 | std::cout << "\n device type = " << typeValueText->Value(); 202 | 203 | // product code and revision number 204 | 205 | std::cout << "\n product code = " << typeElem->Attribute("ProductCode"); 206 | std::cout << "\n revision # = " << typeElem->Attribute("RevisionNo"); 207 | 208 | // device name 209 | 210 | TiXmlElement* nameElem = deviceElem->FirstChildElement("Name"); 211 | TiXmlText* nameText = nameElem->FirstChild()->ToText(); 212 | 213 | std::cout << "\n device name = " << nameText->Value(); 214 | 215 | // fill the structure with parsed values 216 | 217 | std::string *name = new std::string(nameText->Value()); 218 | 219 | unsigned int product_code = hexCharToInt(typeElem->Attribute("ProductCode")); 220 | //unsigned int pos = slaveIndex; 221 | unsigned int pos = 0; 222 | unsigned int vendor_id = charToInt(idValueText->Value()); 223 | unsigned int alias = (success == 0) ? slave_info->alias : 0; 224 | //unsigned int alias = 0; 225 | std::cout << "\nalias = " << alias; 226 | 227 | // loop over a slave's Sync Managers 228 | // the order is important in linking them to the PDOs 229 | 230 | // create slave config 231 | 232 | SlaveConfig *slaveConfig = new SlaveConfig(*name, vendor_id, product_code, alias, pos); 233 | 234 | std::vector slavePdoVector; 235 | std::vector slavePdoEntryVector; 236 | std::vector slaveSyncManagerVector; 237 | 238 | TiXmlNode* smChild = 0; 239 | int i = -1; 240 | while ((smChild = deviceElem->IterateChildren("Sm", smChild))) { 241 | 242 | std::cout << "\n\nSync Manager " << ++i << "\n"; 243 | 244 | TiXmlElement* smElem = smChild->ToElement(); 245 | 246 | std::cout << "\n start address = " << smElem->Attribute("StartAddress"); 247 | std::cout << "\n enable = " << smElem->Attribute("Enable"); 248 | std::cout << "\n control byte = " << smElem->Attribute("ControlByte"); 249 | std::cout << "\n default size = " << smElem->Attribute("DefaultSize"); 250 | 251 | // we must only be counting those that are for Sync manager #i 252 | int txCount = countPdoChildren(deviceElem, "TxPdo", i); 253 | int rxCount = countPdoChildren(deviceElem, "RxPdo", i); 254 | 255 | ec_direction_t dir = (txCount > 0) ? EC_DIR_INPUT : EC_DIR_OUTPUT; 256 | uint8_t index = i; 257 | unsigned int n_pdos = (txCount > 0) ? txCount : rxCount; 258 | ec_watchdog_mode_t watchdog_mode = (txCount > 0) ? EC_WD_DISABLE : EC_WD_ENABLE; 259 | 260 | 261 | std::vector pdos; 262 | if (txCount > 0) { 263 | pdos = loadPdoInfo("TxPdo", deviceElem, i, n_pdos); 264 | } else { 265 | pdos = loadPdoInfo("RxPdo", deviceElem, i, n_pdos); 266 | } 267 | 268 | SyncManager *syncManager = new SyncManager(index, dir, watchdog_mode); 269 | 270 | // add to running vectors of pdos and pdo entries for this slave 271 | for(int pdoi = 0; pdoi < (int)pdos.size(); pdoi++) { 272 | Pdo pdo = pdos.at(pdoi); 273 | 274 | // add to sync manager 275 | syncManager->pdos.push_back(pdo); 276 | 277 | // add to slave totals 278 | slaveConfig->pdos.push_back(pdo); 279 | 280 | std::cout << "\n&&&& " << pdo.name << ", size = " << pdo.pdoEntries.size(); 281 | 282 | for(int pdoei = 0; pdoei < (int)pdo.pdoEntries.size(); pdoei++) { 283 | 284 | PdoEntry pdoEntry = pdo.pdoEntries.at(pdoei); 285 | 286 | slaveConfig->pdoEntries.push_back(pdoEntry); 287 | 288 | std::cout << pdoEntry.entryName << ", "; 289 | } 290 | } 291 | std::cout << "\n"; 292 | 293 | slaveSyncManagerVector.push_back(*syncManager); 294 | } 295 | 296 | // add completed sync tree structs to the slave config 297 | slaveConfig->syncs = slaveSyncManagerVector; 298 | 299 | slaveConfigList.push_back(*slaveConfig); 300 | } 301 | 302 | 303 | return slaveConfigList; 304 | 305 | } 306 | 307 | std::vector ConfigLoader::loadPdoInfo(const char* type, TiXmlElement *deviceElem, int smIndex, int n_pdos) { 308 | 309 | // allocate the tmt pdos array - new 310 | 311 | std::vector pdo_vector; 312 | 313 | 314 | std::cout << "\nnumber of pdos " << n_pdos << "\n"; 315 | 316 | if (n_pdos == 0) { 317 | return pdo_vector; 318 | } 319 | 320 | 321 | // loop over a slave's PDOs 322 | TiXmlNode* pdoChild = 0; 323 | int j = 0; 324 | for (pdoChild = deviceElem->FirstChild(type); pdoChild != 0; pdoChild = pdoChild->NextSibling()) { 325 | 326 | std::cout << "\n\nPdo " << j++ << "\n"; 327 | 328 | TiXmlElement* pdoElem = pdoChild->ToElement(); 329 | 330 | int syncMgr = charToInt(pdoElem->Attribute("Sm")); 331 | if (syncMgr != smIndex) continue; 332 | 333 | std::cout << "\n Sync Manager = " << pdoElem->Attribute("Sm"); 334 | std::cout << "\n fixed = " << pdoElem->Attribute("Fixed"); 335 | std::cout << "\n mandatory = " << pdoElem->Attribute("Mandatory"); 336 | 337 | 338 | 339 | TiXmlText* indexText = pdoElem->FirstChild("Index")->FirstChild()->ToText(); 340 | 341 | std::cout << "\n index = " << indexText->Value(); 342 | 343 | unsigned int index = hexCharToInt(indexText->Value()); 344 | 345 | std::cout << " >>>>>> 0x" << std::hex << index; 346 | 347 | std::string *name = new std::string("Not Specified"); 348 | 349 | TiXmlText* nameText = pdoElem->FirstChild("Name")->FirstChild()->ToText(); 350 | std::cout << "\n name = " << nameText->Value(); 351 | name = new std::string(nameText->Value()); 352 | 353 | 354 | int pdoEntryCount = countChildren(pdoElem, "Entry"); 355 | int n_entries = pdoEntryCount; 356 | 357 | 358 | // save this pdo 359 | Pdo *pdo = new Pdo(*name, index, n_entries); 360 | pdo_vector.push_back(*pdo); 361 | 362 | 363 | 364 | // loop over all PDO entries 365 | 366 | 367 | TiXmlNode* pdoEntryChild = 0; 368 | int k = 0; 369 | for (pdoEntryChild = pdoElem->FirstChild("Entry"); pdoEntryChild != 0; pdoEntryChild = pdoEntryChild->NextSibling()) { 370 | 371 | 372 | std::cout << "\n-----------------\n"; 373 | std::cout << "Pdo Entry " << k++ << "\n"; 374 | 375 | 376 | TiXmlText* indexText = pdoEntryChild->FirstChild("Index")->FirstChild()->ToText(); 377 | std::cout << "\n index = " << indexText->Value(); 378 | 379 | TiXmlText* bitLenText = pdoEntryChild->FirstChild("BitLen")->FirstChild()->ToText(); 380 | std::cout << "\n bit length = " << bitLenText->Value(); 381 | 382 | int bit_length = charToInt(bitLenText->Value()); 383 | unsigned int index = hexCharToInt(indexText->Value()); 384 | 385 | int subindex = 0; 386 | 387 | if (pdoEntryChild->FirstChild("SubIndex")) { 388 | 389 | TiXmlText* subIndexText = pdoEntryChild->FirstChild("SubIndex")->FirstChild()->ToText(); 390 | std::cout << "\n sub-index = " << subIndexText->Value(); 391 | subindex = charToInt(subIndexText->Value()); 392 | 393 | } else { 394 | std::cout << "\n sub-index = " << "0x00"; 395 | } 396 | 397 | std::string *entry_name = new std::string("Not Specified"); 398 | 399 | if (pdoEntryChild->FirstChild("Name")) { 400 | 401 | TiXmlText* nameText = pdoEntryChild->FirstChild("Name")->FirstChild()->ToText(); 402 | std::cout << "\n name = " << nameText->Value(); 403 | entry_name = new std::string(nameText->Value()); 404 | } else { 405 | std::cout << "\n name = " << "Not Specified " << k; 406 | } 407 | 408 | 409 | if (pdoEntryChild->FirstChild("DataType")) { 410 | TiXmlText* dataTypeText = pdoEntryChild->FirstChild("DataType")->FirstChild()->ToText(); 411 | std::cout << "\n data type = " << dataTypeText->Value(); 412 | } else { 413 | std::cout << "\n data type = " << "None"; 414 | } 415 | 416 | // save this as pdoEntry 417 | PdoEntry *pdoEntry = new PdoEntry(*entry_name, pdo->name, index, subindex, bit_length); 418 | pdo_vector.back().pdoEntries.push_back(*pdoEntry); 419 | 420 | std::cout << "\n---------" << pdo_vector.size() << "--------\n"; 421 | 422 | } 423 | std::cout << "all PDO entries done"; 424 | 425 | } 426 | 427 | return pdo_vector; 428 | 429 | } 430 | 431 | unsigned int ConfigLoader::charToInt(const char* input) { 432 | 433 | unsigned int intValue; 434 | 435 | intValue = strtol(input, NULL, 0); 436 | 437 | return intValue; 438 | } 439 | 440 | unsigned int ConfigLoader::hexCharToInt(const char* input) { 441 | 442 | unsigned int intValue; 443 | 444 | std::string *sindex = new std::string(input); 445 | 446 | sindex->replace(0, 1, "0"); 447 | 448 | std::stringstream strValue; 449 | strValue << *sindex; 450 | 451 | strValue >> std::hex >> intValue; 452 | 453 | return intValue; 454 | } 455 | 456 | 457 | 458 | int ConfigLoader::countChildren(TiXmlNode* parent) { 459 | 460 | //cout << "countChildren1"; 461 | int childCount = 0; 462 | TiXmlNode* child; 463 | for (child = parent->FirstChild(); child; child = child->NextSibling()) { 464 | childCount++; 465 | } 466 | return childCount; 467 | } 468 | 469 | int ConfigLoader::countChildren(TiXmlNode* parent, const char* name) { 470 | //cout << "countChildren2"; 471 | int childCount = 0; 472 | TiXmlNode* child; 473 | for (child = parent->FirstChild(name); child; child = 474 | child->NextSibling()) { 475 | childCount++; 476 | } 477 | return childCount; 478 | } 479 | 480 | int ConfigLoader::countPdoChildren(TiXmlNode* parent, const char* name, int smIndex) { 481 | //cout << "countPdoChildren"; 482 | int childCount = 0; 483 | TiXmlNode* child; 484 | for (child = parent->FirstChild(name); child; child = child->NextSibling()) { 485 | TiXmlElement *childElement = child->ToElement(); 486 | int candidateIndex = charToInt(childElement->Attribute("Sm")); 487 | if (candidateIndex == smIndex) { 488 | childCount++; 489 | } 490 | } 491 | return childCount; 492 | } 493 | 494 | void ConfigLoader::printSyncs(int n_syncs, ec_sync_info_t *sync) { 495 | 496 | std::cout << "in printSyncs nsyncs = " << n_syncs; 497 | 498 | for (int i=0; i 2 | #include 3 | #include "ecrt.h" 4 | 5 | #include 6 | #include "PdoEntry.h" 7 | #include "Pdo.h" 8 | #include "SyncManager.h" 9 | #include "SlaveConfig.h" 10 | #include "TmtEcStructs.h" 11 | #include "temp.h" 12 | #include "CommandQueue.h" 13 | #include "PdoEntryCache.h" 14 | #include "CyclicMotor.h" 15 | 16 | 17 | #include 18 | 19 | static bool terminateFlg = false; 20 | static ec_domain_state_t domain1_state = {}; 21 | 22 | 23 | // cyclic motor state 24 | 25 | CyclicMotor::CyclicMotor() { 26 | }; 27 | 28 | CyclicMotor::CyclicMotor(ec_master_t *master, ec_domain *domain1, uint8_t *domain1_pd, std::vector slaves) { 29 | this->master = master; 30 | this->domain1 = domain1; 31 | this->domain1_pd = domain1_pd; 32 | this->slaves = slaves; 33 | }; 34 | 35 | 36 | void CyclicMotor::start() { 37 | 38 | terminateFlg = false; 39 | 40 | std::thread t1(&CyclicMotor::startup, this); 41 | t1.detach(); 42 | 43 | }; 44 | 45 | void CyclicMotor::stop() { 46 | terminateFlg = true; 47 | }; 48 | 49 | 50 | /*****************************************************************************/ 51 | 52 | void check_domain1_state(ec_domain *domain1) 53 | { 54 | ec_domain_state_t ds; 55 | 56 | ecrt_domain_state(domain1, &ds); 57 | 58 | if (ds.working_counter != domain1_state.working_counter) 59 | printf("Domain1: WC %u.\n", ds.working_counter); 60 | if (ds.wc_state != domain1_state.wc_state) 61 | printf("Domain1: State %u.\n", ds.wc_state); 62 | 63 | domain1_state = ds; 64 | } 65 | 66 | /*****************************************************************************/ 67 | #if 0 68 | void check_master_state(void) 69 | { 70 | ec_master_state_t ms; 71 | 72 | ecrt_master_state(master, &ms); 73 | 74 | if (ms.slaves_responding != master_state.slaves_responding) { 75 | printf("%u slave(s).\n", ms.slaves_responding); 76 | } 77 | if (ms.al_states != master_state.al_states) 78 | printf("AL states: 0x%02X.\n", ms.al_states); 79 | if (ms.link_up != master_state.link_up) 80 | printf("Link is %s.\n", ms.link_up ? "up" : "down"); 81 | 82 | master_state = ms; 83 | } 84 | 85 | /*****************************************************************************/ 86 | 87 | void check_slave_config_states(void) 88 | { 89 | ec_slave_config_state_t s; 90 | 91 | ecrt_slave_config_state(sc_ana_in, &s); 92 | 93 | if (s.al_state != sc_ana_in_state.al_state) 94 | printf("AnaIn: State 0x%02X.\n", s.al_state); 95 | if (s.online != sc_ana_in_state.online) 96 | printf("AnaIn: %s.\n", s.online ? "online" : "offline"); 97 | if (s.operational != sc_ana_in_state.operational) 98 | printf("AnaIn: %soperational.\n", 99 | s.operational ? "" : "Not "); 100 | 101 | sc_ana_in_state = s; 102 | } 103 | 104 | #endif 105 | 106 | void CyclicMotor::startup() { 107 | 108 | printf("Started.\n"); 109 | 110 | while (1) { 111 | auto now = std::chrono::system_clock::now(); 112 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 113 | 114 | #if 0 115 | struct timeval t; 116 | gettimeofday(&t, NULL); 117 | printf("%u.%03u\n", t.tv_sec, t.tv_usec); 118 | #endif 119 | 120 | this->cyclic_task(this->master, this->domain1, this->domain1_pd, this->slaves); 121 | } 122 | }; 123 | 124 | 125 | 126 | void CyclicMotor::cyclic_task(ec_master_t *master, ec_domain *domain1, uint8_t *domain1_pd, std::vector slaves) { 127 | 128 | //cout << "cyclic_task::\n"; 129 | 130 | // receive process data 131 | ecrt_master_receive(master); 132 | ecrt_domain_process(domain1); 133 | 134 | // check process data state (optional) 135 | check_domain1_state(domain1); 136 | 137 | // check for master state (optional) 138 | //check_master_state(); 139 | 140 | // check for islave configuration state(s) (optional) 141 | 142 | //check_slave_config_states(); 143 | 144 | if (terminateFlg) { 145 | std::cout << "\nterminating thread"; 146 | std::terminate(); 147 | return; 148 | } 149 | 150 | // read all values into the cache 151 | // TODO: this could be in a separate thread 152 | 153 | for (unsigned int si=0; si 6 | #include 7 | #include "TmtEcStructs.h" 8 | #include "SlaveConfig.h" 9 | #include "CommandQueue.h" 10 | #include "ConfigLoader.h" 11 | 12 | #include "EtherCatServer.h" 13 | 14 | 15 | /****************************************************************************/ 16 | 17 | // Application parameters 18 | 19 | #define PRIORITY 1 20 | 21 | /****************************************************************************/ 22 | 23 | // process data 24 | static uint8_t *domain1_pd = NULL; 25 | 26 | 27 | // EtherCAT 28 | static ec_master_t *master = NULL; 29 | static ec_master_state_t master_state = {}; 30 | 31 | static ec_domain_t *domain1 = NULL; 32 | static ec_domain_state_t domain1_state = {}; 33 | 34 | 35 | static std::vector slaves; 36 | 37 | /****************************************************************************/ 38 | 39 | EtherCatServer::EtherCatServer() { 40 | pdoEntryCache = PdoEntryCache(); 41 | configLoader = ConfigLoader(); 42 | }; 43 | 44 | void EtherCatServer::startServer() { 45 | 46 | 47 | cyclicMotor = CyclicMotor(master, domain1, domain1_pd, slaves); 48 | 49 | cyclicMotor.start(); 50 | }; 51 | 52 | void EtherCatServer::stopServer() { 53 | cyclicMotor.stop(); 54 | }; 55 | 56 | int EtherCatServer::configServer(std::string configFile) { 57 | 58 | 59 | // 1. Configure the system 60 | 61 | master = ecrt_request_master(0); 62 | if (!master) 63 | return -1; 64 | 65 | 66 | // load up the configuration 67 | 68 | slaves = configLoader.loadConfiguration(master, configFile); 69 | 70 | domain1 = ecrt_master_create_domain(master); 71 | if (!domain1) 72 | return -1; 73 | 74 | // load and apply configurations 75 | configLoader.applyConfiguration(master, domain1, &slaves); 76 | 77 | printf("Activating master...\n"); 78 | if (ecrt_master_activate(master)) 79 | return -1; 80 | 81 | if (!(domain1_pd = ecrt_domain_data(domain1))) { 82 | return -1; 83 | } 84 | 85 | return 0; 86 | 87 | }; 88 | 89 | std::vector EtherCatServer::getDeviceNames() { 90 | 91 | std::vector deviceNameList; 92 | for (int si=0; si<(int)slaves.size(); si++) { 93 | deviceNameList.push_back(slaves.at(si).name); 94 | } 95 | return deviceNameList; 96 | 97 | }; 98 | 99 | // FIXME - this does not work when two device names are the same 100 | std::vector EtherCatServer::getParameterNames(std::string deviceName) { 101 | 102 | std::vector parameterNameList; 103 | for (int si=0; si<(int)slaves.size(); si++) { 104 | if (deviceName.compare(slaves.at(si).name) == 0) { 105 | for (unsigned int i=0; igetPdoEntryValue(slaveIndex, entryIndex); 159 | } 160 | 161 | // TODO: we need to template this for different types of value 162 | void EtherCatServer::setParameterValue(std::string deviceName, std::string parameterName, int value) { 163 | setParameterValue(deviceName, 0, 0, parameterName, value); 164 | } 165 | 166 | void EtherCatServer::setParameterValue(unsigned int deviceAlias, int deviceOffset, std::string parameterName, int value) { 167 | setParameterValue("", deviceAlias, deviceOffset, parameterName, value); 168 | } 169 | 170 | 171 | 172 | void EtherCatServer::setParameterValue(std::string deviceName, unsigned int alias, int pos, std::string parameterName, int value) { 173 | 174 | // TODO: implement as a hashmap of slaves by name containing a hashmap of PdoEntries by fullName. 175 | 176 | //cout << "setParameterValue:: slaves.size() = " << slaves.size(); 177 | 178 | for (int si=0; si<(int)slaves.size(); si++) { 179 | 180 | 181 | //std::cout << slaves.at(si).name << "::" << deviceName << "::" << alias; 182 | 183 | if ((deviceName.empty() && (alias == slaves.at(si).alias || (alias == 0U && si == pos))) 184 | || (deviceName.compare(slaves.at(si).name) == 0 && (alias == 0U || alias == slaves.at(si).alias))) { 185 | 186 | for (int i=0; i<(int)slaves.at(si).pdoEntries.size(); i++) { 187 | 188 | if (parameterName.compare(slaves.at(si).pdoEntries.at(i).fullName) == 0) { 189 | PdoEntryValue pdoEntryValue = PdoEntryValue(); 190 | pdoEntryValue.pdoEntryIndex = i; 191 | pdoEntryValue.slaveIndex = si; 192 | pdoEntryValue.entryValue = value; 193 | CommandQueue::instance()->addToQueue(pdoEntryValue); 194 | std::cout << "\nslave = " << slaves.at(si).name; 195 | std::cout << " ::: " << slaves.at(si).pdoEntries.at(i).fullName << ", value = " << pdoEntryValue.entryValue << " added to queue"; 196 | 197 | } 198 | 199 | } 200 | } 201 | } 202 | 203 | }; 204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile to build ectest 2 | 3 | all: ectest 4 | 5 | ETHER_LOC=/opt/etherlab 6 | 7 | CC=g++ 8 | CFLAGS=-c -Wall -fPIC -std=c++0x -pthread -D_GLIBCXX_USE_NANOSLEEP 9 | INCS= -I$(ETHER_LOC)/include -I../include -I$(ETHER_LOC)/tool 10 | LOPTS=-Wl,-rpath=$(ETHER_LOC)/lib 11 | LIBS=-L$(ETHER_LOC)/lib -lethercat -lpthread 12 | 13 | ectest: UserExample.o libtmtec.so 14 | $(CC) $(LOPTS) $(LIBS) -L./ -ltmtec -o $@ $^ 15 | 16 | libtmtec.so: EtherCatServer.o ConfigLoader.o CommandQueue.o PdoEntryCache.o NameMap.o CyclicMotor.o SyncManager.o Pdo.o PdoEntry.o SlaveConfig.o tinystr.o tinyxml.o tinyxmlerror.o tinyxmlparser.o 17 | $(CC) $(LOPTS) $(LIBS) -shared -o $@ $^ 18 | 19 | UserExample.o: UserExample.cpp 20 | $(CC) $(CFLAGS) $(INCS) $< 21 | 22 | EtherCatServer.o: EtherCatServer.cpp 23 | $(CC) $(CFLAGS) $(INCS) $< 24 | 25 | ConfigLoader.o: ConfigLoader.cpp 26 | $(CC) $(CFLAGS) $(INCS) $< 27 | 28 | CommandQueue.o: CommandQueue.cpp 29 | $(CC) $(CFLAGS) $(INCS) $< 30 | 31 | PdoEntryCache.o: PdoEntryCache.cpp 32 | $(CC) $(CFLAGS) $(INCS) $< 33 | 34 | NameMap.o: NameMap.cpp 35 | $(CC) $(CFLAGS) $(INCS) $< 36 | 37 | SyncManager.o: SyncManager.cpp 38 | $(CC) $(CFLAGS) $(INCS) $< 39 | 40 | Pdo.o: Pdo.cpp 41 | $(CC) $(CFLAGS) $(INCS) $< 42 | 43 | PdoEntry.o: PdoEntry.cpp 44 | $(CC) $(CFLAGS) $(INCS) $< 45 | 46 | SlaveConfig.o: SlaveConfig.cpp 47 | $(CC) $(CFLAGS) $(INCS) $< 48 | 49 | CyclicMotor.o: CyclicMotor.cpp 50 | $(CC) $(CFLAGS) $(INCS) $< 51 | 52 | tinystr.o: tinystr.cpp 53 | $(CC) $(CFLAGS) $(INCS) $< 54 | 55 | tinyxml.o: tinyxml.cpp 56 | $(CC) $(CFLAGS) $(INCS) $< 57 | 58 | tinyxmlerror.o: tinyxmlerror.cpp 59 | $(CC) $(CFLAGS) $(INCS) $< 60 | 61 | tinyxmlparser.o: tinyxmlparser.cpp 62 | $(CC) $(CFLAGS) $(INCS) $< 63 | 64 | clean: 65 | rm *.o ectest 66 | 67 | touch: 68 | touch ectest.c 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/NameMap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ecrt.h" 3 | #include "TmtEcStructs.h" 4 | #include 5 | #include "NameMap.h" 6 | 7 | 8 | NameMap::NameMap() { 9 | }; 10 | 11 | std::vector NameMap::getDevices() { 12 | 13 | Device device; 14 | 15 | std::vector deviceList; 16 | 17 | deviceList.push_back(device); 18 | 19 | return deviceList; 20 | 21 | }; 22 | 23 | std::vector NameMap::getParameters(Device device) { 24 | 25 | Parameter parameter; 26 | 27 | std::vector parameterList; 28 | 29 | parameterList.push_back(parameter); 30 | 31 | return parameterList; 32 | 33 | }; 34 | 35 | Parameter NameMap::getParameter(Device device, std::string parameterName) { 36 | Parameter parameter; 37 | 38 | return parameter; 39 | }; 40 | -------------------------------------------------------------------------------- /src/Pdo.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "Pdo.h" 4 | 5 | Pdo::Pdo(std::string name, int index, int n_entries) { 6 | this->name = name; 7 | this->index = index; 8 | this->n_entries = n_entries; 9 | }; 10 | -------------------------------------------------------------------------------- /src/PdoEntry.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "PdoEntry.h" 6 | 7 | PdoEntry::PdoEntry(std::string entryName, std::string pdoName, int index, int subindex, int bitLength) { 8 | 9 | this->entryName = entryName; 10 | 11 | // create the full name 12 | std::stringstream ss; 13 | ss << pdoName << "::" << entryName; 14 | this->fullName = ss.str(); 15 | 16 | this->index = index; 17 | this->subindex = subindex; 18 | this->bitLength = bitLength; 19 | this->domainOffset = 0; 20 | this->domainBitPos = 0; 21 | 22 | }; 23 | -------------------------------------------------------------------------------- /src/PdoEntryCache.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "ecrt.h" 4 | #include "TmtEcStructs.h" 5 | #include "PdoEntryCache.h" 6 | 7 | 8 | // Singleton pattern stuff 9 | 10 | PdoEntryCache* PdoEntryCache::pInstance = 0; 11 | 12 | PdoEntryCache* PdoEntryCache::instance() { 13 | if (pInstance == 0) { 14 | pInstance = new PdoEntryCache; 15 | } 16 | return pInstance; 17 | } 18 | 19 | void PdoEntryCache::updatePdoEntryValue(int slaveId, int pdoEntryId, int pdoEntryValue) { 20 | int key = (slaveId * 1000) + pdoEntryId; 21 | cacheMap[key] = pdoEntryValue; 22 | }; 23 | 24 | int PdoEntryCache::getPdoEntryValue(int slaveId, int pdoEntryId) { 25 | int key = (slaveId * 1000) + pdoEntryId; 26 | 27 | //std::cout << "\nKEY = " + key; 28 | 29 | return cacheMap[key]; 30 | 31 | }; 32 | -------------------------------------------------------------------------------- /src/SlaveConfig.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "SlaveConfig.h" 4 | 5 | SlaveConfig::SlaveConfig(std::string name, unsigned int vendor_id, unsigned int product_code, unsigned int alias, unsigned int pos) { 6 | this->name = name; 7 | this->vendor_id = vendor_id; 8 | this->product_code = product_code; 9 | this->alias = alias; 10 | this->pos = pos; 11 | this->domain_offset = 0; 12 | this->domain_bit_pos = 0; 13 | }; 14 | -------------------------------------------------------------------------------- /src/SyncManager.cpp: -------------------------------------------------------------------------------- 1 | #include "ecrt.h" 2 | #include "SyncManager.h" 3 | 4 | SyncManager::SyncManager(uint8_t index, ec_direction_t dir, ec_watchdog_mode_t watchdog_mode) { 5 | this->index = index; 6 | this->dir = dir; 7 | this->watchdog_mode = watchdog_mode; 8 | }; 9 | 10 | -------------------------------------------------------------------------------- /src/UserExample.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "EtherCatServer.h" 6 | 7 | 8 | int main() { 9 | 10 | // create a new server 11 | EtherCatServer etherCatServer = EtherCatServer(); 12 | 13 | // configure it with generated XML file 14 | etherCatServer.configServer("/home/smichaels/git/ethercat-demo/test/test.xml"); 15 | 16 | // print out device and parameter names 17 | std::vector deviceNames = etherCatServer.getDeviceNames(); 18 | for (unsigned int i=0; i paramNames = etherCatServer.getParameterNames(deviceNames.at(i)); 22 | for (unsigned int j=0; j(-1); 31 | 32 | 33 | // Null rep. 34 | TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, { '\0' } }; 35 | 36 | 37 | void TiXmlString::reserve (size_type cap) 38 | { 39 | if (cap > capacity()) 40 | { 41 | TiXmlString tmp; 42 | tmp.init(length(), cap); 43 | memcpy(tmp.start(), data(), length()); 44 | swap(tmp); 45 | } 46 | } 47 | 48 | 49 | TiXmlString& TiXmlString::assign(const char* str, size_type len) 50 | { 51 | size_type cap = capacity(); 52 | if (len > cap || cap > 3*(len + 8)) 53 | { 54 | TiXmlString tmp; 55 | tmp.init(len); 56 | memcpy(tmp.start(), str, len); 57 | swap(tmp); 58 | } 59 | else 60 | { 61 | memmove(start(), str, len); 62 | set_size(len); 63 | } 64 | return *this; 65 | } 66 | 67 | 68 | TiXmlString& TiXmlString::append(const char* str, size_type len) 69 | { 70 | size_type newsize = length() + len; 71 | if (newsize > capacity()) 72 | { 73 | reserve (newsize + capacity()); 74 | } 75 | memmove(finish(), str, len); 76 | set_size(newsize); 77 | return *this; 78 | } 79 | 80 | 81 | TiXmlString operator + (const TiXmlString & a, const TiXmlString & b) 82 | { 83 | TiXmlString tmp; 84 | tmp.reserve(a.length() + b.length()); 85 | tmp += a; 86 | tmp += b; 87 | return tmp; 88 | } 89 | 90 | TiXmlString operator + (const TiXmlString & a, const char* b) 91 | { 92 | TiXmlString tmp; 93 | TiXmlString::size_type b_len = static_cast( strlen(b) ); 94 | tmp.reserve(a.length() + b_len); 95 | tmp += a; 96 | tmp.append(b, b_len); 97 | return tmp; 98 | } 99 | 100 | TiXmlString operator + (const char* a, const TiXmlString & b) 101 | { 102 | TiXmlString tmp; 103 | TiXmlString::size_type a_len = static_cast( strlen(a) ); 104 | tmp.reserve(a_len + b.length()); 105 | tmp.append(a, a_len); 106 | tmp += b; 107 | return tmp; 108 | } 109 | 110 | 111 | #endif // TIXML_USE_STL 112 | -------------------------------------------------------------------------------- /src/tinyxmlerror.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | www.sourceforge.net/projects/tinyxml 3 | Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com) 4 | 5 | This software is provided 'as-is', without any express or implied 6 | warranty. In no event will the authors be held liable for any 7 | damages arising from the use of this software. 8 | 9 | Permission is granted to anyone to use this software for any 10 | purpose, including commercial applications, and to alter it and 11 | redistribute it freely, subject to the following restrictions: 12 | 13 | 1. The origin of this software must not be misrepresented; you must 14 | not claim that you wrote the original software. If you use this 15 | software in a product, an acknowledgment in the product documentation 16 | would be appreciated but is not required. 17 | 18 | 2. Altered source versions must be plainly marked as such, and 19 | must not be misrepresented as being the original software. 20 | 21 | 3. This notice may not be removed or altered from any source 22 | distribution. 23 | */ 24 | 25 | #include "tinyxml.h" 26 | 27 | // The goal of the seperate error file is to make the first 28 | // step towards localization. tinyxml (currently) only supports 29 | // english error messages, but the could now be translated. 30 | // 31 | // It also cleans up the code a bit. 32 | // 33 | 34 | const char* TiXmlBase::errorString[ TiXmlBase::TIXML_ERROR_STRING_COUNT ] = 35 | { 36 | "No error", 37 | "Error", 38 | "Failed to open file", 39 | "Error parsing Element.", 40 | "Failed to read Element name", 41 | "Error reading Element value.", 42 | "Error reading Attributes.", 43 | "Error: empty tag.", 44 | "Error reading end tag.", 45 | "Error parsing Unknown.", 46 | "Error parsing Comment.", 47 | "Error parsing Declaration.", 48 | "Error document empty.", 49 | "Error null (0) or unexpected EOF found in input stream.", 50 | "Error parsing CDATA.", 51 | "Error when TiXmlDocument added to document, because TiXmlDocument can only be at the root.", 52 | }; 53 | -------------------------------------------------------------------------------- /src/tinyxmlparser.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | www.sourceforge.net/projects/tinyxml 3 | Original code by Lee Thomason (www.grinninglizard.com) 4 | 5 | This software is provided 'as-is', without any express or implied 6 | warranty. In no event will the authors be held liable for any 7 | damages arising from the use of this software. 8 | 9 | Permission is granted to anyone to use this software for any 10 | purpose, including commercial applications, and to alter it and 11 | redistribute it freely, subject to the following restrictions: 12 | 13 | 1. The origin of this software must not be misrepresented; you must 14 | not claim that you wrote the original software. If you use this 15 | software in a product, an acknowledgment in the product documentation 16 | would be appreciated but is not required. 17 | 18 | 2. Altered source versions must be plainly marked as such, and 19 | must not be misrepresented as being the original software. 20 | 21 | 3. This notice may not be removed or altered from any source 22 | distribution. 23 | */ 24 | 25 | #include 26 | #include 27 | 28 | #include "tinyxml.h" 29 | 30 | //#define DEBUG_PARSER 31 | #if defined( DEBUG_PARSER ) 32 | # if defined( DEBUG ) && defined( _MSC_VER ) 33 | # include 34 | # define TIXML_LOG OutputDebugString 35 | # else 36 | # define TIXML_LOG printf 37 | # endif 38 | #endif 39 | 40 | // Note tha "PutString" hardcodes the same list. This 41 | // is less flexible than it appears. Changing the entries 42 | // or order will break putstring. 43 | TiXmlBase::Entity TiXmlBase::entity[ TiXmlBase::NUM_ENTITY ] = 44 | { 45 | { "&", 5, '&' }, 46 | { "<", 4, '<' }, 47 | { ">", 4, '>' }, 48 | { """, 6, '\"' }, 49 | { "'", 6, '\'' } 50 | }; 51 | 52 | // Bunch of unicode info at: 53 | // http://www.unicode.org/faq/utf_bom.html 54 | // Including the basic of this table, which determines the #bytes in the 55 | // sequence from the lead byte. 1 placed for invalid sequences -- 56 | // although the result will be junk, pass it through as much as possible. 57 | // Beware of the non-characters in UTF-8: 58 | // ef bb bf (Microsoft "lead bytes") 59 | // ef bf be 60 | // ef bf bf 61 | 62 | const unsigned char TIXML_UTF_LEAD_0 = 0xefU; 63 | const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; 64 | const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; 65 | 66 | const int TiXmlBase::utf8ByteTable[256] = 67 | { 68 | // 0 1 2 3 4 5 6 7 8 9 a b c d e f 69 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 70 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 71 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20 72 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30 73 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 74 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50 75 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 76 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range 77 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid 78 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90 79 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0 80 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0 81 | 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte 82 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0 83 | 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte 84 | 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid 85 | }; 86 | 87 | 88 | void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) 89 | { 90 | const unsigned long BYTE_MASK = 0xBF; 91 | const unsigned long BYTE_MARK = 0x80; 92 | const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; 93 | 94 | if (input < 0x80) 95 | *length = 1; 96 | else if ( input < 0x800 ) 97 | *length = 2; 98 | else if ( input < 0x10000 ) 99 | *length = 3; 100 | else if ( input < 0x200000 ) 101 | *length = 4; 102 | else 103 | { *length = 0; return; } // This code won't covert this correctly anyway. 104 | 105 | output += *length; 106 | 107 | // Scary scary fall throughs. 108 | switch (*length) 109 | { 110 | case 4: 111 | --output; 112 | *output = (char)((input | BYTE_MARK) & BYTE_MASK); 113 | input >>= 6; 114 | case 3: 115 | --output; 116 | *output = (char)((input | BYTE_MARK) & BYTE_MASK); 117 | input >>= 6; 118 | case 2: 119 | --output; 120 | *output = (char)((input | BYTE_MARK) & BYTE_MASK); 121 | input >>= 6; 122 | case 1: 123 | --output; 124 | *output = (char)(input | FIRST_BYTE_MARK[*length]); 125 | } 126 | } 127 | 128 | 129 | /*static*/ int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) 130 | { 131 | // This will only work for low-ascii, everything else is assumed to be a valid 132 | // letter. I'm not sure this is the best approach, but it is quite tricky trying 133 | // to figure out alhabetical vs. not across encoding. So take a very 134 | // conservative approach. 135 | 136 | // if ( encoding == TIXML_ENCODING_UTF8 ) 137 | // { 138 | if ( anyByte < 127 ) 139 | return isalpha( anyByte ); 140 | else 141 | return 1; // What else to do? The unicode set is huge...get the english ones right. 142 | // } 143 | // else 144 | // { 145 | // return isalpha( anyByte ); 146 | // } 147 | } 148 | 149 | 150 | /*static*/ int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) 151 | { 152 | // This will only work for low-ascii, everything else is assumed to be a valid 153 | // letter. I'm not sure this is the best approach, but it is quite tricky trying 154 | // to figure out alhabetical vs. not across encoding. So take a very 155 | // conservative approach. 156 | 157 | // if ( encoding == TIXML_ENCODING_UTF8 ) 158 | // { 159 | if ( anyByte < 127 ) 160 | return isalnum( anyByte ); 161 | else 162 | return 1; // What else to do? The unicode set is huge...get the english ones right. 163 | // } 164 | // else 165 | // { 166 | // return isalnum( anyByte ); 167 | // } 168 | } 169 | 170 | 171 | class TiXmlParsingData 172 | { 173 | friend class TiXmlDocument; 174 | public: 175 | void Stamp( const char* now, TiXmlEncoding encoding ); 176 | 177 | const TiXmlCursor& Cursor() const { return cursor; } 178 | 179 | private: 180 | // Only used by the document! 181 | TiXmlParsingData( const char* start, int _tabsize, int row, int col ) 182 | { 183 | assert( start ); 184 | stamp = start; 185 | tabsize = _tabsize; 186 | cursor.row = row; 187 | cursor.col = col; 188 | } 189 | 190 | TiXmlCursor cursor; 191 | const char* stamp; 192 | int tabsize; 193 | }; 194 | 195 | 196 | void TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding ) 197 | { 198 | assert( now ); 199 | 200 | // Do nothing if the tabsize is 0. 201 | if ( tabsize < 1 ) 202 | { 203 | return; 204 | } 205 | 206 | // Get the current row, column. 207 | int row = cursor.row; 208 | int col = cursor.col; 209 | const char* p = stamp; 210 | assert( p ); 211 | 212 | while ( p < now ) 213 | { 214 | // Treat p as unsigned, so we have a happy compiler. 215 | const unsigned char* pU = (const unsigned char*)p; 216 | 217 | // Code contributed by Fletcher Dunn: (modified by lee) 218 | switch (*pU) { 219 | case 0: 220 | // We *should* never get here, but in case we do, don't 221 | // advance past the terminating null character, ever 222 | return; 223 | 224 | case '\r': 225 | // bump down to the next line 226 | ++row; 227 | col = 0; 228 | // Eat the character 229 | ++p; 230 | 231 | // Check for \r\n sequence, and treat this as a single character 232 | if (*p == '\n') { 233 | ++p; 234 | } 235 | break; 236 | 237 | case '\n': 238 | // bump down to the next line 239 | ++row; 240 | col = 0; 241 | 242 | // Eat the character 243 | ++p; 244 | 245 | // Check for \n\r sequence, and treat this as a single 246 | // character. (Yes, this bizarre thing does occur still 247 | // on some arcane platforms...) 248 | if (*p == '\r') { 249 | ++p; 250 | } 251 | break; 252 | 253 | case '\t': 254 | // Eat the character 255 | ++p; 256 | 257 | // Skip to next tab stop 258 | col = (col / tabsize + 1) * tabsize; 259 | break; 260 | 261 | case TIXML_UTF_LEAD_0: 262 | if ( encoding == TIXML_ENCODING_UTF8 ) 263 | { 264 | if ( *(p+1) && *(p+2) ) 265 | { 266 | // In these cases, don't advance the column. These are 267 | // 0-width spaces. 268 | if ( *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 ) 269 | p += 3; 270 | else if ( *(pU+1)==0xbfU && *(pU+2)==0xbeU ) 271 | p += 3; 272 | else if ( *(pU+1)==0xbfU && *(pU+2)==0xbfU ) 273 | p += 3; 274 | else 275 | { p +=3; ++col; } // A normal character. 276 | } 277 | } 278 | else 279 | { 280 | ++p; 281 | ++col; 282 | } 283 | break; 284 | 285 | default: 286 | if ( encoding == TIXML_ENCODING_UTF8 ) 287 | { 288 | // Eat the 1 to 4 byte utf8 character. 289 | int step = TiXmlBase::utf8ByteTable[*((const unsigned char*)p)]; 290 | if ( step == 0 ) 291 | step = 1; // Error case from bad encoding, but handle gracefully. 292 | p += step; 293 | 294 | // Just advance one column, of course. 295 | ++col; 296 | } 297 | else 298 | { 299 | ++p; 300 | ++col; 301 | } 302 | break; 303 | } 304 | } 305 | cursor.row = row; 306 | cursor.col = col; 307 | assert( cursor.row >= -1 ); 308 | assert( cursor.col >= -1 ); 309 | stamp = p; 310 | assert( stamp ); 311 | } 312 | 313 | 314 | const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding ) 315 | { 316 | if ( !p || !*p ) 317 | { 318 | return 0; 319 | } 320 | if ( encoding == TIXML_ENCODING_UTF8 ) 321 | { 322 | while ( *p ) 323 | { 324 | const unsigned char* pU = (const unsigned char*)p; 325 | 326 | // Skip the stupid Microsoft UTF-8 Byte order marks 327 | if ( *(pU+0)==TIXML_UTF_LEAD_0 328 | && *(pU+1)==TIXML_UTF_LEAD_1 329 | && *(pU+2)==TIXML_UTF_LEAD_2 ) 330 | { 331 | p += 3; 332 | continue; 333 | } 334 | else if(*(pU+0)==TIXML_UTF_LEAD_0 335 | && *(pU+1)==0xbfU 336 | && *(pU+2)==0xbeU ) 337 | { 338 | p += 3; 339 | continue; 340 | } 341 | else if(*(pU+0)==TIXML_UTF_LEAD_0 342 | && *(pU+1)==0xbfU 343 | && *(pU+2)==0xbfU ) 344 | { 345 | p += 3; 346 | continue; 347 | } 348 | 349 | if ( IsWhiteSpace( *p ) ) // Still using old rules for white space. 350 | ++p; 351 | else 352 | break; 353 | } 354 | } 355 | else 356 | { 357 | while ( *p && IsWhiteSpace( *p ) ) 358 | ++p; 359 | } 360 | 361 | return p; 362 | } 363 | 364 | #ifdef TIXML_USE_STL 365 | /*static*/ bool TiXmlBase::StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ) 366 | { 367 | for( ;; ) 368 | { 369 | if ( !in->good() ) return false; 370 | 371 | int c = in->peek(); 372 | // At this scope, we can't get to a document. So fail silently. 373 | if ( !IsWhiteSpace( c ) || c <= 0 ) 374 | return true; 375 | 376 | *tag += (char) in->get(); 377 | } 378 | } 379 | 380 | /*static*/ bool TiXmlBase::StreamTo( std::istream * in, int character, TIXML_STRING * tag ) 381 | { 382 | //assert( character > 0 && character < 128 ); // else it won't work in utf-8 383 | while ( in->good() ) 384 | { 385 | int c = in->peek(); 386 | if ( c == character ) 387 | return true; 388 | if ( c <= 0 ) // Silent failure: can't get document at this scope 389 | return false; 390 | 391 | in->get(); 392 | *tag += (char) c; 393 | } 394 | return false; 395 | } 396 | #endif 397 | 398 | // One of TinyXML's more performance demanding functions. Try to keep the memory overhead down. The 399 | // "assign" optimization removes over 10% of the execution time. 400 | // 401 | const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding ) 402 | { 403 | // Oddly, not supported on some comilers, 404 | //name->clear(); 405 | // So use this: 406 | *name = ""; 407 | assert( p ); 408 | 409 | // Names start with letters or underscores. 410 | // Of course, in unicode, tinyxml has no idea what a letter *is*. The 411 | // algorithm is generous. 412 | // 413 | // After that, they can be letters, underscores, numbers, 414 | // hyphens, or colons. (Colons are valid ony for namespaces, 415 | // but tinyxml can't tell namespaces from names.) 416 | if ( p && *p 417 | && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) ) 418 | { 419 | const char* start = p; 420 | while( p && *p 421 | && ( IsAlphaNum( (unsigned char ) *p, encoding ) 422 | || *p == '_' 423 | || *p == '-' 424 | || *p == '.' 425 | || *p == ':' ) ) 426 | { 427 | //(*name) += *p; // expensive 428 | ++p; 429 | } 430 | if ( p-start > 0 ) { 431 | name->assign( start, p-start ); 432 | } 433 | return p; 434 | } 435 | return 0; 436 | } 437 | 438 | const char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding ) 439 | { 440 | // Presume an entity, and pull it out. 441 | TIXML_STRING ent; 442 | int i; 443 | *length = 0; 444 | 445 | if ( *(p+1) && *(p+1) == '#' && *(p+2) ) 446 | { 447 | unsigned long ucs = 0; 448 | ptrdiff_t delta = 0; 449 | unsigned mult = 1; 450 | 451 | if ( *(p+2) == 'x' ) 452 | { 453 | // Hexadecimal. 454 | if ( !*(p+3) ) return 0; 455 | 456 | const char* q = p+3; 457 | q = strchr( q, ';' ); 458 | 459 | if ( !q || !*q ) return 0; 460 | 461 | delta = q-p; 462 | --q; 463 | 464 | while ( *q != 'x' ) 465 | { 466 | if ( *q >= '0' && *q <= '9' ) 467 | ucs += mult * (*q - '0'); 468 | else if ( *q >= 'a' && *q <= 'f' ) 469 | ucs += mult * (*q - 'a' + 10); 470 | else if ( *q >= 'A' && *q <= 'F' ) 471 | ucs += mult * (*q - 'A' + 10 ); 472 | else 473 | return 0; 474 | mult *= 16; 475 | --q; 476 | } 477 | } 478 | else 479 | { 480 | // Decimal. 481 | if ( !*(p+2) ) return 0; 482 | 483 | const char* q = p+2; 484 | q = strchr( q, ';' ); 485 | 486 | if ( !q || !*q ) return 0; 487 | 488 | delta = q-p; 489 | --q; 490 | 491 | while ( *q != '#' ) 492 | { 493 | if ( *q >= '0' && *q <= '9' ) 494 | ucs += mult * (*q - '0'); 495 | else 496 | return 0; 497 | mult *= 10; 498 | --q; 499 | } 500 | } 501 | if ( encoding == TIXML_ENCODING_UTF8 ) 502 | { 503 | // convert the UCS to UTF-8 504 | ConvertUTF32ToUTF8( ucs, value, length ); 505 | } 506 | else 507 | { 508 | *value = (char)ucs; 509 | *length = 1; 510 | } 511 | return p + delta + 1; 512 | } 513 | 514 | // Now try to match it. 515 | for( i=0; iappend( cArr, len ); 594 | } 595 | } 596 | else 597 | { 598 | bool whitespace = false; 599 | 600 | // Remove leading white space: 601 | p = SkipWhiteSpace( p, encoding ); 602 | while ( p && *p 603 | && !StringEqual( p, endTag, caseInsensitive, encoding ) ) 604 | { 605 | if ( *p == '\r' || *p == '\n' ) 606 | { 607 | whitespace = true; 608 | ++p; 609 | } 610 | else if ( IsWhiteSpace( *p ) ) 611 | { 612 | whitespace = true; 613 | ++p; 614 | } 615 | else 616 | { 617 | // If we've found whitespace, add it before the 618 | // new character. Any whitespace just becomes a space. 619 | if ( whitespace ) 620 | { 621 | (*text) += ' '; 622 | whitespace = false; 623 | } 624 | int len; 625 | char cArr[4] = { 0, 0, 0, 0 }; 626 | p = GetChar( p, cArr, &len, encoding ); 627 | if ( len == 1 ) 628 | (*text) += cArr[0]; // more efficient 629 | else 630 | text->append( cArr, len ); 631 | } 632 | } 633 | } 634 | if ( p && *p ) 635 | p += strlen( endTag ); 636 | return ( p && *p ) ? p : 0; 637 | } 638 | 639 | #ifdef TIXML_USE_STL 640 | 641 | void TiXmlDocument::StreamIn( std::istream * in, TIXML_STRING * tag ) 642 | { 643 | // The basic issue with a document is that we don't know what we're 644 | // streaming. Read something presumed to be a tag (and hope), then 645 | // identify it, and call the appropriate stream method on the tag. 646 | // 647 | // This "pre-streaming" will never read the closing ">" so the 648 | // sub-tag can orient itself. 649 | 650 | if ( !StreamTo( in, '<', tag ) ) 651 | { 652 | SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); 653 | return; 654 | } 655 | 656 | while ( in->good() ) 657 | { 658 | int tagIndex = (int) tag->length(); 659 | while ( in->good() && in->peek() != '>' ) 660 | { 661 | int c = in->get(); 662 | if ( c <= 0 ) 663 | { 664 | SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); 665 | break; 666 | } 667 | (*tag) += (char) c; 668 | } 669 | 670 | if ( in->good() ) 671 | { 672 | // We now have something we presume to be a node of 673 | // some sort. Identify it, and call the node to 674 | // continue streaming. 675 | TiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING ); 676 | 677 | if ( node ) 678 | { 679 | node->StreamIn( in, tag ); 680 | bool isElement = node->ToElement() != 0; 681 | delete node; 682 | node = 0; 683 | 684 | // If this is the root element, we're done. Parsing will be 685 | // done by the >> operator. 686 | if ( isElement ) 687 | { 688 | return; 689 | } 690 | } 691 | else 692 | { 693 | SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); 694 | return; 695 | } 696 | } 697 | } 698 | // We should have returned sooner. 699 | SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); 700 | } 701 | 702 | #endif 703 | 704 | const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding ) 705 | { 706 | ClearError(); 707 | 708 | // Parse away, at the document level. Since a document 709 | // contains nothing but other tags, most of what happens 710 | // here is skipping white space. 711 | if ( !p || !*p ) 712 | { 713 | SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); 714 | return 0; 715 | } 716 | 717 | // Note that, for a document, this needs to come 718 | // before the while space skip, so that parsing 719 | // starts from the pointer we are given. 720 | location.Clear(); 721 | if ( prevData ) 722 | { 723 | location.row = prevData->cursor.row; 724 | location.col = prevData->cursor.col; 725 | } 726 | else 727 | { 728 | location.row = 0; 729 | location.col = 0; 730 | } 731 | TiXmlParsingData data( p, TabSize(), location.row, location.col ); 732 | location = data.Cursor(); 733 | 734 | if ( encoding == TIXML_ENCODING_UNKNOWN ) 735 | { 736 | // Check for the Microsoft UTF-8 lead bytes. 737 | const unsigned char* pU = (const unsigned char*)p; 738 | if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0 739 | && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1 740 | && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 ) 741 | { 742 | encoding = TIXML_ENCODING_UTF8; 743 | useMicrosoftBOM = true; 744 | } 745 | } 746 | 747 | p = SkipWhiteSpace( p, encoding ); 748 | if ( !p ) 749 | { 750 | SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); 751 | return 0; 752 | } 753 | 754 | while ( p && *p ) 755 | { 756 | TiXmlNode* node = Identify( p, encoding ); 757 | if ( node ) 758 | { 759 | p = node->Parse( p, &data, encoding ); 760 | LinkEndChild( node ); 761 | } 762 | else 763 | { 764 | break; 765 | } 766 | 767 | // Did we get encoding info? 768 | if ( encoding == TIXML_ENCODING_UNKNOWN 769 | && node->ToDeclaration() ) 770 | { 771 | TiXmlDeclaration* dec = node->ToDeclaration(); 772 | const char* enc = dec->Encoding(); 773 | assert( enc ); 774 | 775 | if ( *enc == 0 ) 776 | encoding = TIXML_ENCODING_UTF8; 777 | else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) ) 778 | encoding = TIXML_ENCODING_UTF8; 779 | else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) ) 780 | encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice 781 | else 782 | encoding = TIXML_ENCODING_LEGACY; 783 | } 784 | 785 | p = SkipWhiteSpace( p, encoding ); 786 | } 787 | 788 | // Was this empty? 789 | if ( !firstChild ) { 790 | SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding ); 791 | return 0; 792 | } 793 | 794 | // All is well. 795 | return p; 796 | } 797 | 798 | void TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding ) 799 | { 800 | // The first error in a chain is more accurate - don't set again! 801 | if ( error ) 802 | return; 803 | 804 | assert( err > 0 && err < TIXML_ERROR_STRING_COUNT ); 805 | error = true; 806 | errorId = err; 807 | errorDesc = errorString[ errorId ]; 808 | 809 | errorLocation.Clear(); 810 | if ( pError && data ) 811 | { 812 | data->Stamp( pError, encoding ); 813 | errorLocation = data->Cursor(); 814 | } 815 | } 816 | 817 | 818 | TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding ) 819 | { 820 | TiXmlNode* returnNode = 0; 821 | 822 | p = SkipWhiteSpace( p, encoding ); 823 | if( !p || !*p || *p != '<' ) 824 | { 825 | return 0; 826 | } 827 | 828 | p = SkipWhiteSpace( p, encoding ); 829 | 830 | if ( !p || !*p ) 831 | { 832 | return 0; 833 | } 834 | 835 | // What is this thing? 836 | // - Elements start with a letter or underscore, but xml is reserved. 837 | // - Comments: "; 1351 | 1352 | if ( !StringEqual( p, startTag, false, encoding ) ) 1353 | { 1354 | if ( document ) 1355 | document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding ); 1356 | return 0; 1357 | } 1358 | p += strlen( startTag ); 1359 | 1360 | // [ 1475201 ] TinyXML parses entities in comments 1361 | // Oops - ReadText doesn't work, because we don't want to parse the entities. 1362 | // p = ReadText( p, &value, false, endTag, false, encoding ); 1363 | // 1364 | // from the XML spec: 1365 | /* 1366 | [Definition: Comments may appear anywhere in a document outside other markup; in addition, 1367 | they may appear within the document type declaration at places allowed by the grammar. 1368 | They are not part of the document's character data; an XML processor MAY, but need not, 1369 | make it possible for an application to retrieve the text of comments. For compatibility, 1370 | the string "--" (double-hyphen) MUST NOT occur within comments.] Parameter entity 1371 | references MUST NOT be recognized within comments. 1372 | 1373 | An example of a comment: 1374 | 1375 | 1376 | */ 1377 | 1378 | value = ""; 1379 | // Keep all the white space. 1380 | while ( p && *p && !StringEqual( p, endTag, false, encoding ) ) 1381 | { 1382 | value.append( p, 1 ); 1383 | ++p; 1384 | } 1385 | if ( p && *p ) 1386 | p += strlen( endTag ); 1387 | 1388 | return p; 1389 | } 1390 | 1391 | 1392 | const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) 1393 | { 1394 | p = SkipWhiteSpace( p, encoding ); 1395 | if ( !p || !*p ) return 0; 1396 | 1397 | if ( data ) 1398 | { 1399 | data->Stamp( p, encoding ); 1400 | location = data->Cursor(); 1401 | } 1402 | // Read the name, the '=' and the value. 1403 | const char* pErr = p; 1404 | p = ReadName( p, &name, encoding ); 1405 | if ( !p || !*p ) 1406 | { 1407 | if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); 1408 | return 0; 1409 | } 1410 | p = SkipWhiteSpace( p, encoding ); 1411 | if ( !p || !*p || *p != '=' ) 1412 | { 1413 | if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); 1414 | return 0; 1415 | } 1416 | 1417 | ++p; // skip '=' 1418 | p = SkipWhiteSpace( p, encoding ); 1419 | if ( !p || !*p ) 1420 | { 1421 | if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); 1422 | return 0; 1423 | } 1424 | 1425 | const char* end; 1426 | const char SINGLE_QUOTE = '\''; 1427 | const char DOUBLE_QUOTE = '\"'; 1428 | 1429 | if ( *p == SINGLE_QUOTE ) 1430 | { 1431 | ++p; 1432 | end = "\'"; // single quote in string 1433 | p = ReadText( p, &value, false, end, false, encoding ); 1434 | } 1435 | else if ( *p == DOUBLE_QUOTE ) 1436 | { 1437 | ++p; 1438 | end = "\""; // double quote in string 1439 | p = ReadText( p, &value, false, end, false, encoding ); 1440 | } 1441 | else 1442 | { 1443 | // All attribute values should be in single or double quotes. 1444 | // But this is such a common error that the parser will try 1445 | // its best, even without them. 1446 | value = ""; 1447 | while ( p && *p // existence 1448 | && !IsWhiteSpace( *p ) // whitespace 1449 | && *p != '/' && *p != '>' ) // tag end 1450 | { 1451 | if ( *p == SINGLE_QUOTE || *p == DOUBLE_QUOTE ) { 1452 | // [ 1451649 ] Attribute values with trailing quotes not handled correctly 1453 | // We did not have an opening quote but seem to have a 1454 | // closing one. Give up and throw an error. 1455 | if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); 1456 | return 0; 1457 | } 1458 | value += *p; 1459 | ++p; 1460 | } 1461 | } 1462 | return p; 1463 | } 1464 | 1465 | #ifdef TIXML_USE_STL 1466 | void TiXmlText::StreamIn( std::istream * in, TIXML_STRING * tag ) 1467 | { 1468 | while ( in->good() ) 1469 | { 1470 | int c = in->peek(); 1471 | if ( !cdata && (c == '<' ) ) 1472 | { 1473 | return; 1474 | } 1475 | if ( c <= 0 ) 1476 | { 1477 | TiXmlDocument* document = GetDocument(); 1478 | if ( document ) 1479 | document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); 1480 | return; 1481 | } 1482 | 1483 | (*tag) += (char) c; 1484 | in->get(); // "commits" the peek made above 1485 | 1486 | if ( cdata && c == '>' && tag->size() >= 3 ) { 1487 | size_t len = tag->size(); 1488 | if ( (*tag)[len-2] == ']' && (*tag)[len-3] == ']' ) { 1489 | // terminator of cdata. 1490 | return; 1491 | } 1492 | } 1493 | } 1494 | } 1495 | #endif 1496 | 1497 | const char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) 1498 | { 1499 | value = ""; 1500 | TiXmlDocument* document = GetDocument(); 1501 | 1502 | if ( data ) 1503 | { 1504 | data->Stamp( p, encoding ); 1505 | location = data->Cursor(); 1506 | } 1507 | 1508 | const char* const startTag = ""; 1510 | 1511 | if ( cdata || StringEqual( p, startTag, false, encoding ) ) 1512 | { 1513 | cdata = true; 1514 | 1515 | if ( !StringEqual( p, startTag, false, encoding ) ) 1516 | { 1517 | if ( document ) 1518 | document->SetError( TIXML_ERROR_PARSING_CDATA, p, data, encoding ); 1519 | return 0; 1520 | } 1521 | p += strlen( startTag ); 1522 | 1523 | // Keep all the white space, ignore the encoding, etc. 1524 | while ( p && *p 1525 | && !StringEqual( p, endTag, false, encoding ) 1526 | ) 1527 | { 1528 | value += *p; 1529 | ++p; 1530 | } 1531 | 1532 | TIXML_STRING dummy; 1533 | p = ReadText( p, &dummy, false, endTag, false, encoding ); 1534 | return p; 1535 | } 1536 | else 1537 | { 1538 | bool ignoreWhite = true; 1539 | 1540 | const char* end = "<"; 1541 | p = ReadText( p, &value, ignoreWhite, end, false, encoding ); 1542 | if ( p && *p ) 1543 | return p-1; // don't truncate the '<' 1544 | return 0; 1545 | } 1546 | } 1547 | 1548 | #ifdef TIXML_USE_STL 1549 | void TiXmlDeclaration::StreamIn( std::istream * in, TIXML_STRING * tag ) 1550 | { 1551 | while ( in->good() ) 1552 | { 1553 | int c = in->get(); 1554 | if ( c <= 0 ) 1555 | { 1556 | TiXmlDocument* document = GetDocument(); 1557 | if ( document ) 1558 | document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); 1559 | return; 1560 | } 1561 | (*tag) += (char) c; 1562 | 1563 | if ( c == '>' ) 1564 | { 1565 | // All is well. 1566 | return; 1567 | } 1568 | } 1569 | } 1570 | #endif 1571 | 1572 | const char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding ) 1573 | { 1574 | p = SkipWhiteSpace( p, _encoding ); 1575 | // Find the beginning, find the end, and look for 1576 | // the stuff in-between. 1577 | TiXmlDocument* document = GetDocument(); 1578 | if ( !p || !*p || !StringEqual( p, "SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding ); 1581 | return 0; 1582 | } 1583 | if ( data ) 1584 | { 1585 | data->Stamp( p, _encoding ); 1586 | location = data->Cursor(); 1587 | } 1588 | p += 5; 1589 | 1590 | version = ""; 1591 | encoding = ""; 1592 | standalone = ""; 1593 | 1594 | while ( p && *p ) 1595 | { 1596 | if ( *p == '>' ) 1597 | { 1598 | ++p; 1599 | return p; 1600 | } 1601 | 1602 | p = SkipWhiteSpace( p, _encoding ); 1603 | if ( StringEqual( p, "version", true, _encoding ) ) 1604 | { 1605 | TiXmlAttribute attrib; 1606 | p = attrib.Parse( p, data, _encoding ); 1607 | version = attrib.Value(); 1608 | } 1609 | else if ( StringEqual( p, "encoding", true, _encoding ) ) 1610 | { 1611 | TiXmlAttribute attrib; 1612 | p = attrib.Parse( p, data, _encoding ); 1613 | encoding = attrib.Value(); 1614 | } 1615 | else if ( StringEqual( p, "standalone", true, _encoding ) ) 1616 | { 1617 | TiXmlAttribute attrib; 1618 | p = attrib.Parse( p, data, _encoding ); 1619 | standalone = attrib.Value(); 1620 | } 1621 | else 1622 | { 1623 | // Read over whatever it is. 1624 | while( p && *p && *p != '>' && !IsWhiteSpace( *p ) ) 1625 | ++p; 1626 | } 1627 | } 1628 | return 0; 1629 | } 1630 | 1631 | bool TiXmlText::Blank() const 1632 | { 1633 | for ( unsigned i=0; i MedalTable::generate(vector results) 4 | { 5 | return results; 6 | }; 7 | -------------------------------------------------------------------------------- /test/test.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | class MedalTable { 6 | public: vector generate(vector results); 7 | }; 8 | -------------------------------------------------------------------------------- /test/test.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmtsoftware/ethercat-demo/0662e1f5882bda92a565283905ea91cca0619150/test/test.o -------------------------------------------------------------------------------- /test/test.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmtsoftware/ethercat-demo/0662e1f5882bda92a565283905ea91cca0619150/test/test.xml -------------------------------------------------------------------------------- /test/test.xml.failed: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 2 7 | 8 | 9 | 10 | 11 | EL1252 12 | 13 | 14 | 15 | 16 | 17 | #x1a00 18 | Channel 1 19 | 20 | #x6000 21 | 1 22 | 1 23 | Input 24 | BOOL 25 | 26 | 27 | 28 | #x1a01 29 | Channel 2 30 | 31 | #x6000 32 | 2 33 | 1 34 | Input 35 | BOOL 36 | 37 | 38 | #x0000 39 | 6 40 | 41 | 42 | 43 | #x1a02 44 | Reserved 45 | 46 | 47 | #x1a13 48 | Latch 49 | 50 | #x1d09 51 | 174 52 | 8 53 | Status1 54 | UINT8 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | --------------------------------------------------------------------------------