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.