// src/holt_widget.cc - Copyright 2005, 2006, University // of Padova, dept. of Pure and Applied // Mathematics // // This file is part of SGPEMv2. // // This is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 3 of the License, or // (at your option) any later version. // // SGPEMv2 is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with SGPEMv2. If not, see http://www.gnu.org/licenses/. #include "cairo_elements.hh" #include "sgpemv2/history.hh" #include "sgpemv2/schedulable.hh" #include "sgpemv2/process.hh" #include "sgpemv2/resource.hh" #include "sgpemv2/simulation.hh" #include "sgpemv2/string_utils.hh" #include "sgpemv2/thread.hh" #include "holt_widget.hh" #include "sgpemv2/templates/deletor.tcc" #include "sgpemv2/templates/sequences.tcc" #include #include #include #ifndef NDEBUG #include #endif #include using namespace sgpem; using namespace std; #ifndef M_SQRT1_2 #define M_SQRT1_2 0.70710678118654752440 #endif #ifndef M_PI #define M_PI 3.14159265358979323846 #endif HoltNode::HoltNode(Vec2 pt) : _radius(20) { _pos = pt; } HoltNode::~HoltNode() { } Vec2 HoltNode::set_position(Vec2 pt) { Vec2 pt_old = _pos; _pos = pt; return pt_old; } Vec2 HoltNode::get_position() { return _pos; } double HoltNode::set_radius(double radius) { double old_rad = _radius; _radius = radius; return old_rad; } double HoltNode::get_radius() { return _radius; } HoltResource::HoltResource(const Resource& resource, resource_key_t resource_key, Vec2 pt) : HoltNode(pt), _resource(&resource), _resource_key(resource_key), _used_places(0) { } HoltResource::~HoltResource() { } void HoltResource::draw(Cairo::RefPtr& cr) { static const Color white(1, 1, 1); static const float x_percent = 3.5f / 4; static const float y_percent = 4.5f / 5; CairoElements ce(cr); // outline cr->set_source_rgb (0, 0, 0); // draw rectangle Rectangle area(_pos.real() - _radius, _pos.imag() - _radius, 2*_radius, 2*_radius); ce.draw_3dcube(area, white, x_percent, y_percent); cr->save(); // clip text outside region cr->rectangle(_pos.real() - _radius, _pos.imag() + _radius * (1 - 2.0 * y_percent), 2 * _radius * x_percent, 2 * _radius * y_percent); cr->clip(); // draw text Cairo::TextExtents extents; cr->get_text_extents(_resource->get_name(), extents); double xpos = _pos.real() + _radius * (x_percent-1) - extents.width/2.0; double ypos = _pos.imag() + _radius * (1 - y_percent); // + extents.height/2; // left aligned if too large if(xpos<_pos.real() - _radius) xpos = _pos.real() - _radius; cr->move_to(xpos, ypos ); cr->show_text(_resource->get_name()); // show used/total places Glib::ustring used; to_string((int)_used_places, used); Glib::ustring total; to_string((int)_resource->get_places(), total); Glib::ustring msg = used + "/" + total; cr->get_text_extents(msg, extents); xpos = _pos.real() + _radius * (x_percent-1) - extents.width/2.0; ypos = _pos.imag() + _radius * (1 - y_percent) + extents.height*2; cr->move_to(xpos, ypos); cr->show_text(msg); // stroke all cr->stroke(); cr->restore(); } Vec2 HoltResource::get_intersection_to(Vec2 pt) { Vec2 final = _pos; Vec2 segment = pt - _pos; double len = std::abs(segment); if(len>0) { // segment to unary modulus vector (versor) segment /= len; if(abs(segment.real())>=M_SQRT1_2) { // direction East(>0) or West(<0) segment *= _radius/abs(segment.real()); } else { // direction North(<0) or South(>0) segment *= _radius/abs(segment.imag()); } final = _pos + segment; } return final; } void HoltResource::allocate(unsigned int places) { _used_places += places; } unsigned int HoltResource::get_allocated() { return _used_places; } HoltSchedulable::HoltSchedulable(const Schedulable& schedulable, Vec2 pt) : HoltNode(pt) { _schedulable = &schedulable; } HoltSchedulable::~HoltSchedulable() { } void HoltSchedulable::draw(Cairo::RefPtr& cr) { static const Color red(1, 0, 0); static const Color yellow(1, 0.8, 0); static const Color green(0, 1, 0); cr->save(); // save context state const Point center(_pos.real(), _pos.imag()); const Color* color = &red; // draw circle CairoElements cel(cr); // filling switch(_schedulable->get_state()) { case Schedulable::state_running: color = &green; break; case Schedulable::state_ready: color = &yellow; break; case Schedulable::state_blocked: color = &red; break; case Schedulable::state_future: case Schedulable::state_terminated: // should never get here assert((_schedulable->get_state() & (Schedulable::state_future | Schedulable::state_terminated)) == 0); } cel.draw_3dsphere(center, _radius, *color); // clip text outside region cr->arc(_pos.real(), _pos.imag(), _radius, 0, 2*M_PI); cr->clip(); // draw text Cairo::TextExtents extents; cr->get_text_extents(_schedulable->get_name(), extents); double text_width = extents.width; if(text_width>_radius*2.0) text_width=_radius*2.0; cr->move_to(_pos.real() - text_width/2, _pos.imag() + extents.height/2); cr->show_text(_schedulable->get_name()); // stroke all cr->stroke(); cr->restore(); // restore context state } Vec2 HoltSchedulable::get_intersection_to(Vec2 pt) { // schedulables ar circles // intersection is a radius from center then... Vec2 final = _pos; // take segment Vec2 segment = pt - _pos; double len = std::abs(segment); // and normalize to radius if(len>0) { segment *= _radius/len; final = _pos + segment; } return final; } HoltRequest::HoltRequest(HoltSchedulable& hp, HoltResource& hr, Request::state state, unsigned int places) : _hp(&hp), _hr(&hr), _state(state) { hr.allocate(places); } HoltRequest::~HoltRequest() { } void HoltRequest::draw(Cairo::RefPtr& cr) { Vec2 resource = _hp->get_intersection_to(_hr->get_position()); Vec2 schedulable = _hr->get_intersection_to(_hp->get_position()); cr->save(); switch(_state) { case Request::state_unallocable: // red cr->set_source_rgb(1, 0, 0); arrow(cr, schedulable, resource); break; case Request::state_allocated: // green cr->set_source_rgb(0, 1, 0); arrow(cr, resource, schedulable); break; case Request::state_future: break; case Request::state_exhausted: break; case Request::state_allocable: // yellow cr->set_source_rgb(1, 0.7, 0); arrow(cr, schedulable, resource); break; } // stroke all and restore status cr->stroke(); cr->restore(); } void HoltRequest::arrow(Cairo::RefPtr& cr, Vec2 first, Vec2 second) { // option to draw the line a little translated // this allow to draw two counterflow arrows // without overlap bool traslate = true; double line_width = cr->get_line_width(); if(traslate) { // if needed calc parallel versor of arrow Vec2 direction = second - first; direction /= std::abs(direction); // set unary modulus // and rotate it 90 degrees Vec2 dir_rotation(0.0, 1.0); direction *= dir_rotation; // translation modulus equals line width direction *= line_width; first += direction; second += direction; } // draw main line cr->move_to(second.real(), second.imag()); cr->line_to(first.real(), first.imag()); // some calculation to draw arrowpoint... // take a short line parallel to main line Vec2 arrowside = second - first; arrowside *= 5.0*line_width/std::abs(arrowside); // make a rotation component Vec2 deviation(5.0, 1.0); deviation /= std::abs(deviation); // make left and right sides rotating that first short line Vec2 side1 = arrowside * deviation; Vec2 side2 = arrowside * conj(deviation); // draw one side cr->move_to(first.real(), first.imag()); cr->line_to(first.real()+side1.real(), first.imag()+side1.imag()); // draw the other side cr->move_to(first.real(), first.imag()); cr->line_to(first.real()+side2.real(), first.imag()+side2.imag()); } HoltWidget::HoltWidget(Simulation& simulation) : Glib::ObjectBase("sgpem_HoltWidget"), SimulationObserver(), HistoryObserver(), CairoWidget(), _simulation(&simulation), _radius(20), _n_proc(0), _n_res(0), _arrange_mode(arrange_horizontal), _show_threads(false) { // Register this SimulationObserver: _simulation->attach(*this); // Register this HistoryObserver: _simulation->get_history().attach(*this); } HoltWidget::~HoltWidget() { // Free allocated memory const HoltResources& const_holt_resources = _holt_resources; for(Iseq it = iseq(const_holt_resources); it; ++it) delete it->second; for_each_in(_holt_schedulables, memory::deletor()); for_each_in(_holt_requests, memory::deletor()); // Unregister this HistoryObserver: _simulation->get_history().detach(*this); // Unregister this SimulationObserver: _simulation->detach(*this); } double HoltWidget::get_radius() { return _arrange_mode; } double HoltWidget::set_radius(double radius) { double old_radius = _radius; if(radius>0) _radius = radius; return old_radius; } HoltWidget::arrange_mode HoltWidget::get_arrange_mode() { return _arrange_mode; } HoltWidget::arrange_mode HoltWidget::set_arrange_mode(arrange_mode mode) { arrange_mode old_mode = _arrange_mode; _arrange_mode = mode; arrange(); return old_mode; } void HoltWidget::arrange() { #ifndef NDEBUG std::cout << "HoltWidget::arrange" << endl; #endif // Differents graph's dispositions are made easy by // usual complex number algebra: // // v1 += v2 - translation // v1 *= d - scaling // v1 *= v2 - rotating if abs(v2)==1.0 // v1 *= v2 - rotating and scaling by modulus if abs(v2)!=1.0 // // where v1, v2 are Vec2 (complex), d is double // // to draw a linear disposition the algorithm starts with // first desired position and sums an inc vetor each step // // to draw a circular disposition the center and radius are taken // then every position is calculated summing to center a radius (inc) // vector. The inc vector is rotated each step // Vec2 pos; // position of current HoltNode derived object (all) Vec2 inc; // increment (vertical and horizontal) or radius (circular) Vec2 cen; // center of rotation (circular only) Vec2 mul; // rotation multiplier (circular only) // vectors building if(_arrange_mode==arrange_horizontal) { // first position pos = Vec2(2*_radius, 2*_radius); // increment inc = Vec2(3*_radius, 0); } else if(_arrange_mode==arrange_vertical) { // first position pos = Vec2(2*_radius, 2*_radius); // increment inc = Vec2(0, 3*_radius); } else // _arrange_mode==arrange_circular { int sx = _draw_w; // get_width(); int sy = _draw_h; // get_height(); int nelem = _holt_resources.size()+_holt_schedulables.size(); // take minimum between x and y /* if(sx<=sy) inc = Vec2((sx/2-2*_radius), 0); else inc = Vec2((sy/2-2*_radius), 0); */ if(nelem>1) { // take minimum between x and y if(sx<=sy) inc = Vec2(-(sx/2-2*_radius), 0); else inc = Vec2(-(sy/2-2*_radius), 0); } else { // zero or one object always at center inc = Vec2(0, 0); } // calc rotation angle if(nelem>0) mul = Vec2(cos(2*M_PI/(double)nelem), sin(2*M_PI/(double)nelem)); else mul = Vec2(0,0); cen = Vec2(2*_radius+std::abs(inc), 2*_radius+std::abs(inc)); // cen = Vec2(sx/2.0, sy/2.0); pos = inc + cen; } // positions all resources HoltResources::const_iterator riter = _holt_resources.begin(); while(riter!=_holt_resources.end()) { HoltResource* hr = (*riter).second; hr->set_position(pos); hr->set_radius(_radius); // calc next position if(_arrange_mode!=arrange_circular) { pos += inc; } else // _arrange_mode==arrange_circular { inc *= mul; pos = cen + inc; } riter++; } // positions all schedulables // if linear disposition then must reevaluate start and increment // when circular continue with previous values to end circle if(_arrange_mode==arrange_horizontal) { pos = Vec2(2*_radius, 8*_radius); inc = Vec2(3*_radius, 0); } else if(_arrange_mode==arrange_vertical) { pos = Vec2(8*_radius, 2*_radius); inc = Vec2(0, 3*_radius); } typedef HoltProcesses::const_iterator holt_proc_iterator; holt_proc_iterator piter = _holt_schedulables.begin(); while(piter!=_holt_schedulables.end()) { HoltSchedulable* hp = (*piter); hp->set_position(pos); hp->set_radius(_radius); if(_arrange_mode!=arrange_circular) { pos += inc; } else // _arrange_mode==arrange_circular { inc *= mul; pos = cen + inc; } piter++; } } bool HoltWidget::get_show_threads() { return _show_threads; } bool HoltWidget::set_show_threads(bool show) { bool old_show = _show_threads; _show_threads = show; // resize_redraw(); return old_show; } void HoltWidget::update(const Simulation& /*changed_simulation*/) { } void HoltWidget::update(const History& /*changed_history*/) { #ifndef NDEBUG std::cout << "HoltWidget::update - History" << endl; #endif // take new data acquire(); // Force redraw resize_redraw(); } void HoltWidget::acquire() { #ifndef NDEBUG std::cout << "HoltWidget::acquire" << endl; #endif // clear all previous collected items const HoltResources& const_holt_resources = _holt_resources; for(Iseq it = iseq(const_holt_resources); it; ++it) delete it->second; _holt_resources.clear(); for_each_in(_holt_schedulables, memory::deletor()); _holt_schedulables.clear(); for_each_in(_holt_requests, memory::deletor()); _holt_requests.clear(); _n_res = _n_proc = 0; const History& hist = _simulation->get_history(); const Environment& env = hist.get_last_environment(); // arbitrary position (will be changed by arrange()) Vec2 pos(2*_radius, 2*_radius); const Environment::Resources& rvect = env.get_resources(); // scan all resources making an HolResource object Environment::Resources::const_iterator riter = rvect.begin(); while(riter!=rvect.end()) { resource_key_t index = (*riter).first; Resource* r = (*riter).second; // create HolResource object and insert it in container HoltResource *hr = new HoltResource(*r, index, pos); HoltResources::iterator temp = _holt_resources.insert(std::pair(index, hr)).first; // count resources _n_res++; // skip to next riter++; // calc next position pos += Vec2(4*_radius, 0); } pos = Vec2(2*_radius, 8*_radius); // iter trough processes const Environment::Processes& pvect = env.get_processes(); Environment::Processes::const_iterator proc_iter = pvect.begin(); while(proc_iter!=pvect.end()) { Process* p = (*proc_iter); proc_iter++; // create a new HoltScedulable per process // if running or ready or blocked and // show threads is disabled Schedulable::state proc_state = p->get_state(); if(proc_state==Schedulable::state_running || proc_state==Schedulable::state_ready || proc_state==Schedulable::state_blocked ) { HoltSchedulable *hp; if(!_show_threads) { hp = new HoltSchedulable(*p, pos); _holt_schedulables.push_back(hp); pos += Vec2(4*_radius, 0); _n_proc++; } // iter trough threads, requests, subrequests const std::vector& tvect = p->get_threads(); std::vector::const_iterator thr_iter = tvect.begin(); while(thr_iter!=tvect.end()) { Thread* t = (*thr_iter); thr_iter++; // create a new HoltScedulable per process // if running or ready or blocked and // show threads is disabled Schedulable::state thr_state = t->get_state(); if(thr_state==Schedulable::state_running || thr_state==Schedulable::state_ready || thr_state==Schedulable::state_blocked ) { if(_show_threads) { hp = new HoltSchedulable(*t, pos); _holt_schedulables.push_back(hp); pos += Vec2(4*_radius, 0); _n_proc++; } // iter trough requests const std::vector& rvect = t->get_requests(); std::vector::const_iterator req_iter = rvect.begin(); while(req_iter!=rvect.end()) { Request* r = (*req_iter); req_iter++; // os << " request arrival_time: " << r->get_instant() << endl; // iter trough subrequests const std::vector& srvect = r->get_subrequests(); std::vector::const_iterator subr_iter = srvect.begin(); while(subr_iter!=srvect.end()) { SubRequest* sr = (*subr_iter); subr_iter++; // create a new HoltRequest per subrequest // if unallocable or allocated or allocable // tieing it with current process or thread Request::state subr_state = sr->get_state(); if(subr_state==Request::state_unallocable || subr_state==Request::state_allocated || subr_state==Request::state_allocable ) { Environment::Resources::const_iterator pos = env.get_resources().find(sr->get_resource_key()); HoltResources::const_iterator hpos = _holt_resources.find(sr->get_resource_key()); if (pos != env.get_resources().end() && hpos!=_holt_resources.end()) { // associates process (or thread) with resource) unsigned int nplaces = 0; if(sr->get_state()==Request::state_allocated) nplaces++; HoltRequest *hreq = new HoltRequest(*hp, *(hpos->second), sr->get_state(), nplaces); _holt_requests.push_back(hreq); } } // ~ if(subr_state==Request::state_unallocable ... etc } // ~ while(subr_iter!=srvect.end()) } // ~ while(req_iter!=rvect.end()) } // ~ if(thr_state==Schedulable::state_running ... } // ~ while(thr_iter!=tvect.end()) } // ~ if(proc_state==Schedulable::state_running ...etc } } void HoltWidget::draw_widget(Cairo::RefPtr& ctx) { #ifndef NDEBUG std::cout << "HoltWidget::draw_widget" << endl; #endif // dispose objects arrange(); // draw text: T = n as title if(_draw_w>0) { const History& hist = _simulation->get_history(); const unsigned int hist_front = hist.get_front() == 0 ? 0 : hist.get_front() - 1; Cairo::TextExtents extents; stringstream ss; ss << "T = " << hist_front; ctx->get_text_extents(ss.str(), extents); ctx->move_to(_draw_w/2 - extents.width/2.0, extents.height*2.0); ctx->show_text(ss.str()); ctx->stroke(); } // draw all resources typedef HoltResources::const_iterator holt_res_iterator; holt_res_iterator riter = _holt_resources.begin(); while(riter!=_holt_resources.end()) { HoltResource* hr = (*riter).second; hr->draw(ctx); riter++; } // draw all schedulables typedef HoltProcesses::const_iterator holt_proc_iterator; holt_proc_iterator piter = _holt_schedulables.begin(); while(piter!=_holt_schedulables.end()) { HoltSchedulable* hp = (*piter); hp->draw(ctx); piter++; } // draw all arrows typedef HoltRequests::const_iterator holt_requ_iterator; holt_requ_iterator reqiter = _holt_requests.begin(); while(reqiter!=_holt_requests.end()) { HoltRequest* hreq = (*reqiter); hreq->draw(ctx); reqiter++; } } void HoltWidget::calc_drawing_size(Cairo::RefPtr& ctx, size_t& width, size_t& height) { #ifndef NDEBUG cout << "Holt widget BEFORE calc_drawing_size width=" << width << " height=" << height << endl; #endif // the disposition of objects take care that: // - an object has a 2.0*_radius diameter // - a space _radius wide is required around objects // // this is the reason because the expression 4 * _radius // recurs many times in the code below // int max = _n_proc; if(_n_res>_n_proc) max = _n_res; Cairo::TextExtents extents; static const Glib::ustring val("Process 999 Resource 999"); ctx->get_text_extents(val, extents); _radius = extents.width/4.0; switch(_arrange_mode) { case arrange_horizontal: // max number of items times the space required by one width = (size_t) (max * 4 * _radius); // aspace of 4 * _radius between rows height = (size_t) (10 * _radius); break; case arrange_vertical: // aspace of 4 * _radius between columns width = (size_t) (10 * _radius); // max number of items times the space required by one height = (size_t) (max * 4 * _radius); break; case arrange_circular: // lilltle more complex diameter calculation // the circle is divided in tot sectors int tot = (_n_proc + _n_res); // each sector is alpha wide double alpha = 0; if(tot>0) alpha = M_PI / tot; // each element is placed on vertex of a tot sides polygon // side length double side = 2 * sin(alpha/2); // calc of expected limiting circle diameter double diam = 4 * _radius; if(side>0) { diam = 4 * _radius / side; } if(diam<4 * _radius) diam = 4 * _radius; width = height = (size_t) (diam + 4 * _radius); break; } } /* void HoltWidget::calc_widget_size(size_t& width, size_t& height) { cout << "Holt widget BEFORE calc_widget_size width=" << width << " height=" << height << endl; width = height = 20; // default minimum size } */