Home Page
Open Source
Contact Us
Free Downloads
Releases and Credits
Software Status
License
About rrproj
assigning people and/or equipment to projects
High-Hope Technique
History of the AI
View Source Files
and how they are organized
Making Programs
Development Notes
Using aiParts
Your Application
Support and Services
|
|
aiParts Source File
//**********************************************************************
// rrproj_fctry.cpp function bodies for rrproj_fctry.h
//
// Copyright (c) 2008 Brian Marshall
//
// See the license at end of this file.
//
// Developers/Contributers:
// [BRM] Brian Marshall - Calgary - bmarshal@agt.net
//
// 08/01/28 [BRM] began development
//
//----------------------------------------------------------------------
#include "rrproj_fctry.h"
// for debugging...
// #include
// #include
// using namespace std;
//======================================================================
// Factory Data
//
// 3 datasets:
// - problem - setup with data from the user
// - solver - factory creates it from the problem dataset
// - factory - temporary data owned by the factory
//
// variable names in functions:
// The first letter of some variables in functions indicates the
// dataset from which the function is set:
// pemp is an emp from the problem
// semp is an emp from the solver
// fempid is an empid from the factory
//
// class name prefix
// rpp rrproj problem (setup by application)
// rps rrproj solver (used by the problem)
// rpf rrproj factory (solver-factory)
//
//----------------------------------------------------------------------
// Constructor
rpfFactory::rpfFactory (rppProblem *pdata) {
m_problem_data = pdata;
m_emp_ids = new aipPandemonium;
if (m_emp_ids) m_emp_ids->set_is_distinct();
m_opts = new aipPandemonium;
if (m_opts) m_opts->set_is_distinct();
}
//----------------------------------------------------------------------
// Destructor
rpfFactory::~rpfFactory () {
if (m_emp_ids) delete m_emp_ids;
if (m_opts) delete m_opts;
}
//----------------------------------------------------------------------
// create and setup a new rpsProblem from the problem data.
rpsProblem * rpfFactory::make_rps_problem () {
if (!m_problem_data) return 0;
rpsProblem *sp = new rpsProblem ();
if (!sp) return 0;
sp->set_num_try (m_problem_data->num_tries());
int status = 1;
if (status) status = setup(sp);
if (!status) return 0;
return sp;
}
//----------------------------------------------------------------------
// setup an rpsProblem - return true on success
int rpfFactory::setup (rpsProblem *sprob) {
int status = 1;
// iterators in the problem data
rppPosDayItr pditr = pprob()->posday_iterator();
rppPosEmpItr peitr = pprob()->posemp_iterator();
rppAssgnItr aitr = pprob()->assgn_iterator();
// iterators in the factory data
rpfOptItr oitr = opt_iterator();
// setup pos, posday, dcsn
if (status) status = handle_pd (pditr, sprob);
// setup emp, empday
if (status) status = handle_pe_a (peitr, aitr, sprob);
// add to the opt list
if (status) status = handle_a_ed (aitr, sprob);
// add to opt list
if (status) status = handle_pe_pd_ed (peitr, sprob);
// setup options from opt list
if (status) status = handle_o (oitr, sprob);
return status;
}
//----------------------------------------------------------------------
// handle the set of PosDays in the problem data
//
// create positions and handle individual posdays
int rpfFactory::handle_pd (rppPosDayItr pditr, rpsProblem *sprob) {
int status = 1;
long min_yyyymmdd = 20200101;
long max_yyyymmdd = 19800101;
long prev_pos_id = very_unlikely_id();
long pner; // num-emps_required in the problem
rppPosDay *ppd; // posday in the problem
rpsPosDay *spd; // posday in the solver
rpsPosition *spos; // pos in the solver
if (status) { // create (Pos), (PosDay), (Dcsn)
for (ppd = pditr.first(); ppd; ppd = pditr.next()) {
if (ppd->pos_id() != prev_pos_id) {
spos = new rpsPosition(ppd->pos_id(), sprob);
if (!spos) { status = 0; log(" Err new rpsPosition"); break; }
sprob->add_position(spos);
prev_pos_id = ppd->pos_id();
}
spd = new rpsPosDay(spos, ppd->yyyymmdd(), ppd->proj_id(),
ppd->start_hhmm(), ppd->end_hhmm());
if (!spd) { status = 0; log("Err new rpsPosDay"); break; }
spos->add_posday(spd);
pner = ppd->num_emps_req();
if (pner < 1 || pner > 1000) {
status = 0; log("Err bad num-emps-required"); break;
}
rpsDecision *sdcsn = new rpsDecision(spd, pner);
if (!sdcsn) {status = 0; log("Err new rpsDecision"); break; }
sprob->add_decision(sdcsn);
spd->set_dcsn(sdcsn);
long ppd_yyyymmdd = ppd->yyyymmdd();
if (ppd_yyyymmdd < min_yyyymmdd) min_yyyymmdd = ppd_yyyymmdd;
if (ppd_yyyymmdd > max_yyyymmdd) max_yyyymmdd = ppd_yyyymmdd;
}
}
if (status) {
sprob->set_day_range (min_yyyymmdd, max_yyyymmdd);
}
return status;
}
//----------------------------------------------------------------------
// handle the sets of posemps and assignments in the problem data
//
// create a distinct list of empids, and then create emps and empdays
// (which also uses empdows and empoffs)
int rpfFactory::handle_pe_a (rppPosEmpItr peitr, rppAssgnItr aitr,
rpsProblem *sprob) {
int status = 1;
rpfEmpId *feid; // an empid owned by the factory
long prev_eid = very_unlikely_id();
if (status) { // start {EmpId}
rppPosEmp *ppe; // a posemp in the problem
for (ppe = peitr.first(); ppe; ppe = peitr.next()) {
if (ppe->emp_id() != prev_eid) {
feid = new rpfEmpId(ppe->emp_id());
if (!feid) { status = 0; log("Err new rpfEmpId"); break; }
m_emp_ids->add (feid); // list is forced distinct
prev_eid = ppe->emp_id();
}
}
}
if (status) { // add to {EmpId}
rppAssignment *pa; // an assignment in the problem
for (pa = aitr.first(); pa; pa = aitr.next()) {
if (pa->emp_id() != prev_eid) {
feid = new rpfEmpId(pa->emp_id());
if (!feid) { status = 0; log("Err new rpfEmpId"); break; }
m_emp_ids->add (feid); // list is forced distinct
prev_eid = pa->emp_id();
}
}
}
// we now have a distinct list of emp_ids
rpsEmployee *semp; // an emp in the solver
if (status) { // create (Emp)
rpfEmpIdItr eiditr = empid_iterator();
for (feid = eiditr.first(); feid; feid = eiditr.next()) {
semp = new rpsEmployee(feid->emp_id(), sprob);
if (!semp) { status = 0; log("Err new rpsEmployee"); break; }
sprob->add_employee(semp);
status = handle_e (semp, sprob);
if (!status) break;
}
}
return status;
}
//----------------------------------------------------------------------
// handle a solver emp - create empday
int rpfFactory::handle_e (rpsEmployee *semp, rpsProblem *sprob) {
int status = 1;
if (status) {
apply_empdaypos(semp);
}
if (status) { // create (EmpDay)
aipTime curday( (sprob->start_day()) );
aipTime lastday( (sprob->end_day()) );
for ( ; curday <= lastday; curday.add_days(1) ) {
long x_yyyymmdd = curday.yyyymmdd();
if (!x_yyyymmdd) { status = 0; log("Err h_e yyyymmdd"); break; }
if ( emp_is_off (semp->emp_id(), curday) ) continue;
long x_start_hhmm, x_end_hhmm;
long x_dow = curday.days_since_sunday();
get_dow_data (semp->emp_id(), x_dow,
&x_start_hhmm, &x_end_hhmm);
if (x_start_hhmm == x_end_hhmm) continue;
rpsEmpDay *sed = new rpsEmpDay (semp, x_yyyymmdd,
x_start_hhmm, x_end_hhmm);
if (!sed) { status = 0; log("Err new rpsEmpDay"); break; }
semp->add_empday(sed);
}
}
return status;
}
//----------------------------------------------------------------------
// handle the set of assignments (plus solution EmpDays)
//
// start a list of options (that is forced to have distinct keys)
int rpfFactory::handle_a_ed (rppAssgnItr aitr, rpsProblem *sprob) {
int status = 1;
if (status) { // create {opt} from [Assgnmnt]
rppAssignment *pa; // an assignment in the problem
rpsPosDay *spd; // a posday in the solver
rpsEmpDay *sed; // an empday in the solver
for (pa = aitr.first(); pa; pa = aitr.next()) {
aipTime curday(pa->start_yyyymmdd());
aipTime lastday(pa->end_yyyymmdd());
for ( ; curday <= lastday; curday.add_days(1) ) {
spd = find_posday (pa->pos_id(), curday.yyyymmdd(),
pa->proj_id(), sprob);
if (!spd) continue;
sed = find_empday (pa->emp_id(), curday.yyyymmdd(), sprob);
if (!sed) continue;
rpfOpt *fopt = new rpfOpt (spd, sed, aipManditory);
if (!fopt) { status = 0; log("Err new rpfOpt"); break; }
m_opts->add (fopt); // list is forced distinct
}
}
}
return status;
}
//----------------------------------------------------------------------
// handle the set of PosEmps (plus solution PosDays and EmpDays)
//
// add to the opt-list
int rpfFactory::handle_pe_pd_ed (rppPosEmpItr peitr,
rpsProblem *sprob) {
int status = 1;
if (status) { // create {opt} from [PosEmp]
rppPosEmp * ppe; // a posemp from the problem
rpsPosition * sp; // pointers to objects in the solver
rpsPosDay * spd;
rpsEmployee * se;
rpsEmpDay * sed;
rpsPositionItr pitr = sprob->position_iterator();
rpsEmployeeItr eitr = sprob->employee_iterator();
pitr.first();
eitr.first();
// for each pos_emp...
for ( ppe = peitr.first(); ppe; ppe = peitr.next() ) {
sp = (rpsPosition*)(pitr.find(ppe->pos_id()));
if (!sp) continue;
se = (rpsEmployee*)(eitr.find(ppe->emp_id()));
if (!se) continue;
rpsPosDayItr pditr = sp->posday_iterator();
rpsEmpDayItr editr = se->empday_iterator();
// for each pos_day and emp_day...
for ( spd = pditr.first(); spd; spd = pditr.next() ) {
for ( sed = editr.first(); sed; sed = editr.next() ) {
if (spd->yyyymmdd() == sed->yyyymmdd()) {
if ( sed->start_time() > spd->start_time() ||
sed->end_time() < spd->end_time() ) continue;
rpfOpt *fopt = new rpfOpt (spd, sed, aipPrettyGood);
if (!fopt) { status = 0; log("Err new rpfOpt"); break; }
m_opts->add (fopt); // list is forced distinct
}
} // end of loop through emp_days (in the solver)
} // end of loop through pos_days (in the solver)
} // end of loop through posemps (in the problem)
}
return status;
}
//----------------------------------------------------------------------
// handle the list of opts
//
// create solver-options and attach them to decisions
int rpfFactory::handle_o (rpfOptItr foitr, rpsProblem *sprob) {
int status = 1;
if (!sprob) { status = 0; log("handle_o sprob"); }
if (status) { // create (Option) from {opt}
rpfOpt * fo; // an opt-list element (stored in the factory)
for ( fo = foitr.first(); fo; fo = foitr.next() ) {
rpsDecision *sd = fo->pos_day()->dcsn();
if (!sd) { status = 0; log("Err h_o dcsn"); break; }
rpsOption *sopt = new rpsOption (fo->emp_day(),
fo->g_constant());
if (!sopt) { status = 0; log("new rpsOption"); break; }
sd->add_rps_option (sopt);
} // end of elements in distinct opt-list
}
return status;
}
//----------------------------------------------------------------------
// return a posday from the solver given an pos_id and a date
rpsPosDay * rpfFactory::find_posday (long a_pos_id, long a_yyyymmdd,
long a_proj_id, rpsProblem *sprob) {
if (!sprob) return 0;
rpsPositionItr pitr = sprob->position_iterator();
rpsPosition * sp = (rpsPosition*)(pitr.find(a_pos_id));
if (!sp) return 0;
rpsPosDayItr pditr = sp->posday_iterator();
rpsPosDay * spd = (rpsPosDay*)
(pditr.find(a_pos_id, a_yyyymmdd, a_proj_id));
return spd;
}
//----------------------------------------------------------------------
// return an empday from the solver given an emp_id and a date
rpsEmpDay * rpfFactory::find_empday (long emp_id, long a_yyyymmdd,
rpsProblem *sprob) {
if (!sprob) return 0;
rpsEmployeeItr eitr = sprob->employee_iterator();
rpsEmployee * se = (rpsEmployee*)(eitr.find(emp_id));
if (!se) return 0;
rpsEmpDayItr editr = se->empday_iterator();
rpsEmpDay * sed;
for ( sed = editr.first(); sed; sed = editr.next() ) {
if (sed->yyyymmdd() == a_yyyymmdd) break;
}
return sed;
}
//----------------------------------------------------------------------
// apply historic EmpDayPos data to the emp
void rpfFactory::apply_empdaypos (rpsEmployee *e) {
rppEmpDayPosItr edpitr = pprob()->empdaypos_iterator();
rppEmpDayPos *edp = (rppEmpDayPos*)(edpitr.find(e->emp_id()));
if (!edp) return;
long last_yyyymmdd = edp->yyyymmdd();
for ( edp = edpitr.next(); edp; edp = edpitr.next() ) {
if (edp->emp_id() != e->emp_id()) break;
if (edp->yyyymmdd() > last_yyyymmdd) {
last_yyyymmdd = edp->yyyymmdd();
}
}
aipTime lw(last_yyyymmdd);
e->set_start_last_work(lw);
}
//----------------------------------------------------------------------
// return true if emp is off on the specified day
int rpfFactory::emp_is_off (long a_emp_id, aipTime a_day) {
int is_off = 0;
rppEmpOffItr eoitr = pprob()->empoff_iterator();
rppEmpOff *eoff = (rppEmpOff*)(eoitr.find(a_emp_id));
for ( ; eoff; eoff = eoitr.next() ) {
if (eoff->emp_id() != a_emp_id) break;
if (a_day >= eoff->start_day() && a_day <= eoff->end_day() ) {
is_off = 1;
break;
}
}
return is_off;
}
//----------------------------------------------------------------------
// get data about the day of week for the emp or, if there is none
// for this emp, set default values - return false on failure
void rpfFactory::get_dow_data (long a_emp_id, long a_dow,
long *a_start_hhmm, long *a_end_hhmm) {
int is_any = 0;
int is_match = 0;
*a_start_hhmm = *a_end_hhmm = 0;
rppEmpDowItr edowitr = pprob()->empdow_iterator();
rppEmpDow *edow = (rppEmpDow*)(edowitr.find(a_emp_id));
for ( ; edow; edow = edowitr.next()) {
if (edow->emp_id() != a_emp_id) break;
is_any = 1;
if (edow->dow() == a_dow) {
*a_start_hhmm = edow->start_hhmm();
*a_end_hhmm = edow->end_hhmm();
is_match = 1;
break;
}
}
if (!is_match && !is_any) {
*a_start_hhmm = 0; // emp can work all days
*a_end_hhmm = 2359;
}
}
//----------------------------------------------------------------------
// get an iterator for empids in the list owned by the factory
rpfEmpIdItr rpfFactory::empid_iterator () const {
rpfEmpIdItr i(m_emp_ids);
return i;
}
//----------------------------------------------------------------------
// get an iterator for opts in the list owned by the factory
rpfOptItr rpfFactory::opt_iterator () const {
rpfOptItr i(m_opts);
return i;
}
//======================================================================
// rpfOpt
//
//----------------------------------------------------------------------
// Constructor
rpfOpt::rpfOpt (rpsPosDay *a_pos_day, rpsEmpDay *a_emp_day,
aipG a_g_constant) {
m_pos_day = a_pos_day;
m_emp_day = a_emp_day;
m_g_constant = a_g_constant;
m_proj_id = m_pos_day ? m_pos_day->proj_id() : 0;
m_pos_id = m_pos_day ? m_pos_day->pos_id() : 0;
m_emp_id = m_emp_day ? m_emp_day->emp_id() : 0;
m_yyyymmdd = m_emp_day ? m_emp_day->yyyymmdd() : 0;
}
//----------------------------------------------------------------------
// Destructor
rpfOpt::~rpfOpt () {}
//======================================================================
// License
//
// Permission is hereby granted, free of charge, to any
// person obtaining a copy of this software and associated
// documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to
// whom the Software is furnished to do so, subject to the
// following conditions:
//
// The copyright notice and this license shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
//======================================================================
|