25 #if VALIDATE_INPUT_FILE_AGAINST_SCHEMA == 1 
   26 #include <libxml++/validators/schemavalidator.h> 
   29 #include "SolverConfig.h" 
   37 using namespace xmlpp;
 
   42                 #ifdef VALIDATE_INPUT_FILE_AGAINST_SCHEMA 
   43                 SchemaValidator validator(
string(INSTALL_PATH)+
"/share/"+
string(PROJECT_NAME)+
"-"+
string(PROGRAM_VERSION)+
"/dataset.xsd");
 
   44                 validator.validate(parser.get_document());
 
   46                 const Node *rootNode = parser.get_document()->get_root_node();
 
   47                 processRootNode(rootNode);
 
   54         const Element *dataset = castToElement(rootNode);
 
   55         mDatasetName = getTextFromElement(dataset, 
"name", 
false);
 
   56         mDatasetDescription = getTextFromElement(dataset, 
"desc", 
false);
 
   59         const Node::NodeList instances = dataset->get_children(
"instance");
 
   60         for (Node *instance : instances)
 
   61                 processInstanceNode(instance);
 
   66         mAidToActivity.clear();
 
   67         mPointToLocation.clear();
 
   68         const Element *instance = castToElement(node);
 
   69         line.name(getTextFromElement(instance, 
"name", 
false));
 
   70         line.description(getTextFromElement(instance, 
"desc", 
false));
 
   72         const NodeSet robots = instance->find(
"robots/robot");
 
   73         for (
const Node* robotNode : robots)
 
   74                 line.addRobot(processRobotNode(robotNode));
 
   76         for (
Robot* r : line.robots())  {
 
   78                         setValue(mAidToActivity, a->aid(), a, caller());
 
   81         const NodeSet interRobotOperations = instance->find(
"inter-robot-operations/operation");
 
   82         for (
const Node* op : interRobotOperations)
 
   83                 line.addInterRobotOperation(processInterRobotOperationNode(op));
 
   85         const NodeSet collisionZones = instance->find(
"collision-zones/collision-pair");
 
   86         for (
const Node* cz : collisionZones)
 
   87                 line.addCollision(processCollisionPairNode(cz));
 
   89         line.productionCycleTime(stod(getTextFromElement(instance, 
"production-cycle-time")));
 
   96         mLines.push_back(line);
 
   97         mMapping.push_back(move(mapping));
 
  103         const Element *robotElement = castToElement(robotNode);
 
  105         robot->name(getTextFromElement(robotElement, 
"name", 
false));
 
  106         robot->description(getTextFromElement(robotElement, 
"desc", 
false));
 
  109         const NodeSet powerModes = robotElement->find(
"power-saving-modes/power-mode");
 
  110         for (
const Node* node : powerModes)     {
 
  111                 const Element *powerMode = castToElement(node);
 
  113                 mode->name(getTextFromElement(powerMode, 
"name", 
false));
 
  114                 mode->description(getTextFromElement(powerMode, 
"desc", 
false));
 
  115                 mode->minimalDelay(stod(getTextFromElement(powerMode, 
"minimal-idle-time")));
 
  116                 string inputPower = getTextFromElement(powerMode, 
"expected-input-power", 
false);
 
  117                 if (!inputPower.empty())
 
  118                         mode->expectedInputPower(stod(inputPower));
 
  120                 robot->addPowerMode(mode);
 
  123         const NodeSet staticActivities = robotElement->find(
"activities/static-activity");
 
  124         for (
const Node* node : staticActivities)
 
  125                 robot->addActivity(processStaticActivityNode(node, robot->powerModes()));
 
  127         const NodeSet dynamicActivities = robotElement->find(
"activities/dynamic-activity");
 
  128         for (
const Node* node : dynamicActivities)
 
  129                 robot->addActivity(processDynamicActivityNode(node));
 
  136         const Element *activityElement = castToElement(node);
 
  139         if (getAttribute(activityElement, 
"last_in_cycle", 
false) == 
"true")
 
  140                 staticActivity->lastInCycle(
true);
 
  142         staticActivity->name(getTextFromElement(activityElement, 
"name", 
false));
 
  143         staticActivity->description(getTextFromElement(activityElement, 
"desc", 
false));
 
  145         const NodeSet locations = activityElement->find(
"locations/location");
 
  146         for (
const Node *loc : locations)       {
 
  148                 const Element *locElement = castToElement(loc);
 
  150                 location->point(stoul(getTextFromElement(locElement, 
"point")));
 
  152                 const NodeSet locDepPowerInput = locElement->find(
"location-dependent-power-consumption/consumption");
 
  153                 for (
const Node *ldpcNode : locDepPowerInput)   {
 
  154                         const Element *ldpcElement = castToElement(ldpcNode);
 
  155                         uint32_t pid = stoul(getAttribute(ldpcElement, 
"pid"));
 
  156                         auto spmIt = find_if(robotModes.cbegin(), robotModes.cend(), [pid](
RobotPowerMode* m) { 
return pid == m->pid(); });
 
  157                         if (spmIt != robotModes.cend()) {
 
  159                                 ldpc.inputPower(stod(getAttribute(ldpcElement, 
"input_power")));
 
  160                                 location->addLocationDependentPowerConsumption(ldpc);
 
  162                                 throw InvalidDatasetFile(caller(), 
"Invalid value of 'pid' attribute!", ldpcNode->get_line());
 
  166                 location->parent(staticActivity);
 
  167                 setValue(mPointToLocation, location->point(), location, caller());
 
  169                 staticActivity->addLocation(location);
 
  172         double minimalDelay = F64_MAX;
 
  174                 minimalDelay = min(minimalDelay, pwrm->minimalDelay());
 
  176         staticActivity->minAbsDuration(max(minimalDelay, stod(getTextFromElement(activityElement, 
"min-duration"))));
 
  177         staticActivity->maxAbsDuration(stod(getTextFromElement(activityElement, 
"max-duration")));
 
  179         if (abs(staticActivity->maxAbsDuration()-staticActivity->minAbsDuration()) < 
TIME_ERR)
 
  180                 staticActivity->maxAbsDuration(staticActivity->minAbsDuration()+
TIME_ERR);
 
  182         return staticActivity;
 
  187         const Element *activityElement = castToElement(node);
 
  190         dynamicActivity->name(getTextFromElement(activityElement, 
"name", 
false));
 
  191         dynamicActivity->description(getTextFromElement(activityElement, 
"desc", 
false));
 
  193         double minDuration = F64_MAX, maxDuration = F64_MIN;
 
  194         const Element *movements = castToElement(activityElement->get_first_child(
"movements"));
 
  195         string a1 = getAttribute(movements, 
"from_aid", 
false), a2 = getAttribute(movements, 
"to_aid", 
false);
 
  196         int64_t fromActivity = (a1.empty() ? -1 : stol(a1)), toActivity = (a2.empty() ? -1 : stol(a2));
 
  198         const NodeSet movementSet = movements->find(
"movement");
 
  199         for (
const Node *movementNode : movementSet)    {
 
  200                 const Element *movementElement = castToElement(movementNode);
 
  203                 mv->minDuration(stod(getTextFromElement(movementElement, 
"min-duration")));
 
  204                 mv->maxDuration(stod(getTextFromElement(movementElement, 
"max-duration")));
 
  206                         string mid = to_string(mv->mid());
 
  207                         delete dynamicActivity; 
delete mv;
 
  208                         throw InvalidDatasetFile(caller(), 
"Dynamic activity's movement "+mid+
" must have positive duration!", movementNode->get_line());
 
  212                 if (abs(mv->maxDuration()-mv->minDuration()) < 
TIME_ERR)
 
  213                         mv->maxDuration(mv->minDuration()+
TIME_ERR);
 
  215                 const NodeSet monomials = movementElement->find(
"energy-function/monomial");
 
  216                 for (
const Node *monomialNode : monomials)      {
 
  217                         const Element *monomialElement = castToElement(monomialNode);
 
  218                         int32_t degree = stoi(getAttribute(monomialElement, 
"degree"));
 
  219                         double coeff = stod(getAttribute(monomialElement, 
"coeff"));
 
  220                         mv->addMonomial({degree, coeff});
 
  223                 uint32_t fromPoint = stoul(getTextFromElement(movementElement, 
"from-point"));
 
  224                 uint32_t toPoint = stoul(getTextFromElement(movementElement, 
"to-point"));
 
  226                 Location *locFrom = 
nullptr, *locTo = 
nullptr;
 
  228                 auto fSit = mPointToLocation.find(fromPoint), tSit = mPointToLocation.find(toPoint);
 
  229                 if (fSit == mPointToLocation.cend() || tSit == mPointToLocation.cend()) {
 
  230                         delete dynamicActivity; 
delete mv;
 
  231                         throw InvalidDatasetFile(caller(), 
"Dynamic activity's point is not linked with any static activity!", movementNode->get_line());
 
  233                         locFrom = fSit->second; locTo = tSit->second;
 
  234                         assert(locFrom != 
nullptr && locTo != 
nullptr && 
"Unexpected null pointers!");
 
  235                         actFrom = locFrom->parent(); actTo = locTo->parent();
 
  236                         assert(actFrom != 
nullptr && actTo != 
nullptr && 
"Should not be nullptr as it has been set in processRobotNode method!");
 
  239                 if ((actFrom->aid() != fromActivity && fromActivity != -1) || (actTo->aid() != toActivity && toActivity != -1)) {
 
  240                         delete dynamicActivity; 
delete mv;
 
  241                         throw InvalidDatasetFile(caller(), 
"Optional attributes 'from_aid' and/or 'to_aid' are invalid!", movementNode->get_line());
 
  244                 minDuration = min(minDuration, mv->minDuration());
 
  245                 maxDuration = max(maxDuration, mv->maxDuration());
 
  246                 setValue(mMovementToPoints, mv, {fromPoint, toPoint}, caller());
 
  248                 dynamicActivity->addMovement(mv);
 
  251         dynamicActivity->minAbsDuration(minDuration);
 
  252         dynamicActivity->maxAbsDuration(maxDuration);
 
  254         return dynamicActivity;
 
  259         const Element *operationElement = castToElement(interRobotOperationNode);
 
  261         operation->name(getTextFromElement(operationElement, 
"name", 
false));
 
  262         operation->description(getTextFromElement(operationElement, 
"desc", 
false));
 
  264         const NodeSet timeLags = operationElement->find(
"time-compatibility/time-lag");
 
  265         for (
const Node *timeLagNode : timeLags)        {
 
  266                 const Element *timeLagElement = castToElement(timeLagNode);
 
  268                 uint32_t fromId = stoul(getTextFromElement(timeLagElement, 
"from-activity"));
 
  269                 uint32_t toId = stoul(getTextFromElement(timeLagElement, 
"to-activity"));
 
  270                 double length = stod(getTextFromElement(timeLagElement, 
"length"));
 
  271                 int32_t height = stoi(getTextFromElement(timeLagElement, 
"height"));
 
  273                 auto sit1 = mAidToActivity.find(fromId), sit2 = mAidToActivity.find(toId);
 
  274                 if (sit1 != mAidToActivity.cend() && sit2 != mAidToActivity.cend())     {
 
  275                         operation->addTimeLag({sit1->second, sit2->second, length, height});
 
  277                         string msg = 
"Time lag '"+to_string(fromId)+
" -> "+to_string(toId)+
"' between non-existing activities!\n";
 
  278                         throw InvalidDatasetFile(caller(), msg+
"Invalid inter-robot time-lag!", timeLagNode->get_line());
 
  282         const NodeSet spaceComp = operationElement->find(
"spatial-compatibility/compatible-pair");
 
  283         for (
const Node *cmpPairNode : spaceComp)       {
 
  284                 const Element *cmpPairElement = castToElement(cmpPairNode);
 
  285                 const NodeSet locations = cmpPairElement->find(
"location");
 
  288                         vector<Location*> cmpPair;
 
  289                         for (uint32_t i = 0; i < min(locations.size(), 2ul); ++i)       {
 
  290                                 const Element *locElement = castToElement(locations[i]);
 
  291                                 auto sit = mAidToActivity.find(stoul(getAttribute(locElement, 
"aid")));
 
  292                                 if (sit != mAidToActivity.cend())       {
 
  296                                                 cmpPair.push_back(l);
 
  298                                                 string msg = 
"Spatial compatibility can only be resolved between static activities!";
 
  302                                         throw InvalidDatasetFile(caller(), 
"Invalid 'aid' attribute in '<location>' element!", locElement->get_line());
 
  306                         if (cmpPair.size() == 2)
 
  307                                 operation->addSpatialCompatibilityPair(cmpPair.front(), cmpPair.back());
 
  309                                 throw InvalidDatasetFile(caller(), 
"Invalid number of elements in the '<compatible-pair>' element!");
 
  312                         throw_with_nested(
InvalidDatasetFile(caller(), 
"Cannot process the spatial compatibility pair!", cmpPairElement->get_line()));
 
  321         const Element *collisionPairElement = castToElement(collisionPairNode);
 
  323         vector<ActivityMode*> collisionPair;
 
  324         NodeSet locations = collisionPairElement->find(
"location");
 
  325         NodeSet movements = collisionPairElement->find(
"movement");
 
  327         for (
const Node *locNode : locations)   {
 
  328                 const Element *locElement = castToElement(locNode);
 
  329                 auto sit = mAidToActivity.find(stoul(getAttribute(locElement, 
"aid")));
 
  330                 if (sit != mAidToActivity.cend())       {
 
  333                                 collisionPair.emplace_back(sa->
findLocation(stoul(getAttribute(locElement, 
"lid"))));
 
  335                                 string msg = 
"Referenced 'aid' does not correspond to the static activity for 'location' element!";
 
  339                         throw InvalidDatasetFile(caller(), 
"Invalid 'aid' attribute in 'location' element!", locNode->get_line());
 
  343         for (
const Node *mvNode : movements)    {
 
  344                 const Element *mvElement = castToElement(mvNode);
 
  345                 auto sit = mAidToActivity.find(stoul(getAttribute(mvElement, 
"aid")));
 
  346                 if (sit != mAidToActivity.cend())       {
 
  349                                 collisionPair.emplace_back(da->
findMovement(stoul(getAttribute(mvElement, 
"mid"))));
 
  351                                 string msg = 
"Referenced 'aid' does not correspond to the dynamic activity for 'movement' element!";
 
  355                         throw InvalidDatasetFile(caller(), 
"Invalid 'aid' attribute in 'movement' element!", mvNode->get_line());
 
  359         if (collisionPair.size() != 2)
 
  360                 throw InvalidDatasetFile(caller(), 
"Invalid number of child elements in 'collision-pair' element!");
 
  362         return { collisionPair.front(), collisionPair.back() };
 
  366         const Element *element = 
dynamic_cast<const Element*
>(node);
 
  367         if (element == 
nullptr)
 
  368                 throw InvalidDatasetFile(caller(), 
"Invalid cast to the 'Element*' type!", (node != 
nullptr ? node->get_line() : -1));
 
  373         Attribute *attr = node->get_attribute(attributeName);
 
  374         if (attr == 
nullptr && required)        {
 
  375                 string msg = 
"Cannot get '"+attributeName+
"' from the '"+string(node->get_name())+
"' element!";
 
  376                 throw InvalidDatasetFile(caller(), 
"Failed to get the attribute from the element!", node->get_line());
 
  380                 return attr->get_value();
 
  387         const Node::NodeList children = elementNode->get_children(childName);
 
  388         if (children.size() == 1)       {
 
  389                 const Element *elementChild = castToElement(children.front());
 
  390                 for (
const Node *child : elementChild->get_children())  {
 
  391                         const TextNode* textNode = 
dynamic_cast<const TextNode*
>(child);
 
  392                         if (textNode != 
nullptr)        {
 
  393                                 if (!textNode->is_white_space())
 
  394                                         text += textNode->get_content();
 
  396                                 throw InvalidDatasetFile(caller(), 
"Invalid cast to 'TextNode*' type!", (child != 
nullptr ? child->get_line() : -1));
 
  399         } 
else if (children.size() > 1) {
 
  400                 throw InvalidDatasetFile(caller(), 
"Only from one child the text can be retrieved!", elementNode->get_line());
 
  401         } 
else if (children.empty() && required)        {
 
  402                 string msg = 
"Cannot find the '"+string(childName)+
"' element!\n";
 
  403                 throw InvalidDatasetFile(caller(), msg+
"Invalid input xml file!", elementNode->get_line());
 
The instance of the class corresponds to a robot movement between two coordinates. 
 
The base class incorporating common properties of robot operations and movements. ...
 
The instance of this class specifies input power of the robot for a particular robot configuration an...
 
Instance of this class includes all the data structures and methods related to a robot. 
 
const xmlpp::Element * castToElement(const xmlpp::Node *node) const 
The method casts xmlpp::Node to xmlpp::Element, and in case of success the result is returned...
 
string DATASET_FILE
Dataset with problems to be solved. 
 
Collection of movements between two static activities. 
 
std::map< uint32_t, Location * > pointToLocation
Unique coordinate, i.e. a point, to the related location. 
 
void readDatasetFromXmlFile()
It parses the XML dataset (specified in Settings::DATASET_FILE) and checks it against schema...
 
void processInstanceNode(const xmlpp::Node *node)
It parses the problem instance specified by the node. 
 
std::pair< ActivityMode *, ActivityMode * > processCollisionPairNode(const xmlpp::Node *collisionPairNode) const 
It parses an element containing time disjunctive pair, and returns the pointers to colliding location...
 
Various auxiliary functions used across the program. 
 
Movement * findMovement(const uint32_t &mid) const 
It finds a movement according to its identification. 
 
constexpr double TIME_ERR
Minimal recognizable difference in the time. 
 
Thrown if the dataset file contains ill-specified robotic cells. 
 
The file defines extended exceptions for the better error handling in the program. 
 
Collection of locations in which a robot operation (or waiting) can be performed. ...
 
The file declares XmlReader class which purpose is to parse and check XML datasets. 
 
It represents the power saving mode of the robot. 
 
StaticActivity * processStaticActivityNode(const xmlpp::Node *node, const std::vector< RobotPowerMode * > &robotModes)
An instance of the static activity is created from the parsed data and power saving modes of the robo...
 
std::string getTextFromElement(const xmlpp::Element *element, const std::string &childName, bool required=true) const 
It tries to extract the text from the child element, e.g. getTextFromElement(instanceElement, "name", false) returns the name of the instance or an empty string if the name is not specified. 
 
Location * findLocation(const uint32_t &lid) const 
It finds a location according to its identification. 
 
The inter-robot operation corresponding to the workpiece/weldment handling. 
 
Robot * processRobotNode(const xmlpp::Node *robotNode)
It parses the data related to the robot, and creates the instance of Robot class. ...
 
The file defines allowed inaccuracies in a solution and constants for floats. 
 
std::string getAttribute(const xmlpp::Element *node, const std::string &attributeName, bool required=true) const 
It parses the value assigned to the specified attribute and returns it in the form of string...
 
void initialiseDataStructures(const std::map< uint32_t, Location * > &pointToLocation, const std::map< Movement *, std::pair< uint32_t, uint32_t >> &movementToPoints)
It recursively sets parents and initializes relations between activities. 
 
DynamicActivity * processDynamicActivityNode(const xmlpp::Node *node)
An instance of the dynamic activity is created from the parsed data. 
 
Location of the robot used either during work (welding) or waiting. 
 
void processRootNode(const xmlpp::Node *rootNode)
It parses all the dataset and stores the data to member variables. 
 
The robotic cell corresponds to an instance of this class. 
 
The structure contains the maps for fast searching in the robotic cell. 
 
The file contains various classes devoted to abstract representation of the robotic cell...
 
InterRobotOperation * processInterRobotOperationNode(const xmlpp::Node *interRobotOperationNode) const 
It creates a new inter-robot operation from parsed data. 
 
std::map< uint32_t, Activity * > aidToActivity
Activity identification to its pointer.