Page MenuHomec4science

price.cc
No OneTemporary

File Metadata

Created
Wed, Jul 23, 00:55

price.cc

#include <click/config.h>
//#include <click/standard/scheduleinfo.hh>
#include "util.hh"
#include "price.hh"
CLICK_DECLS
Price::Price() : _update_timer(this), _active_timer(this), _check_traffic_timer(this), _packet_timer(this) {}
Price::~Price(){}
int Price::configure(Vector<String> &conf, ErrorHandler *errh){
_debug = false;
_is_int1 = false;
_is_int2 = false;
_period_ms = DEFAULT_PERIOD; //500 msec default
_init_nr_aggressive_slots = NB_AGGR_SLOTS; //defined in price.hh
_traffic_goes_through = false;
#ifdef CLICK_USERLEVEL
double alpha_price_d = 0.05;
double delta_d = 0.0;
double alpha_price_aggressive_d = AGGRESSIVE_ALPHA;
_init_price = 0.2;
#else
_alpha_price = 100;
#endif
_delta = 0;
EtherAddress wifi_addr, plc_addr, wifi2_addr;
EtherAddress empty_addr = EtherAddress();
_node_index = -1;
_nr_updates = 0;
_sum_prices = DEFAULT_SUM_PRICE;
_max_num_hop_routes = 1;
if (Args(conf, this, errh)
.read_m("NEIGHBORS", _neighbors_name)
.read("DEBUG", _debug)
.read("WIFI_ADDR", wifi_addr)
.read("PLC_ADDR", plc_addr)
.read("WIFI2_ADDR", wifi2_addr)
.read("PERIOD", _period_ms)
.read("NR_SLOTS_AGGRESSIVE", _init_nr_aggressive_slots)
#ifdef CLICK_USERLEVEL
.read("ALPHA_PRICE", alpha_price_d)
.read("ALPHA_PRICE_AGGRESSIVE", alpha_price_aggressive_d)
.read("DELTA", delta_d)
.read("INIT_PRICE", _init_price)
#else
.read("ALPHA_PRICE", _alpha_price) //1 per 1000
.read("ALPHA_PRICE_AGGRESSIVE", _alpha_price_aggressive)
.read("DELTA", _delta) //1 per 1000
#endif
.read("NODE_INDEX", _node_index)
.complete() < 0)
return -1;
#ifdef CLICK_USERLEVEL
_alpha_price = alpha_price_d;
_delta = delta_d;
_alpha_price_aggressive = alpha_price_aggressive_d;
#else
_alpha_s = (uint32_t)int_divide((uint64_t)_alpha_price * (uint64_t)SCALING_FACTOR, 1000);
_alpha_aggr_s = (uint32_t)int_divide((uint64_t)_alpha_price_aggressive * (uint64_t)SCALING_FACTOR, 1000);
_alpha_delta_s = (uint32_t)int_divide((uint64_t)_alpha_price * (uint64_t)_delta * (uint64_t)SCALING_FACTOR, 1e6);
_alpha_aggr_delta_s = (uint32_t)int_divide((uint64_t)_alpha_price_aggressive * (uint64_t)_delta * (uint64_t)SCALING_FACTOR, 1e6);
#endif
_nr_aggressive_slots = _init_nr_aggressive_slots;
if(_node_index != -1){
//we are part of a compound element, we need to adapt the names of the elements...
String index_str = String(_node_index);
String tempstr = String("node")+index_str.c_str();
tempstr = tempstr + "/";
_neighbors_name = tempstr + _neighbors_name.c_str();
}
if(wifi_addr != empty_addr){
_my_eth_addr = wifi_addr;
_is_int1 = true;
_name_element = "Price-WiFi";
}
if(plc_addr != empty_addr){
if(wifi2_addr!=empty_addr)
return errh->error("[Price] Error: needs exactly one interface address.");
_my_eth_addr = plc_addr;
_is_int2 = true;
_name_element = "Price-PLC";
}
if(wifi2_addr != empty_addr) {
_my_eth_addr = wifi2_addr;
_is_int2 = true;
_name_element = "Price-WiFi2";
}
if((_is_int1 && _is_int2) || (!_is_int1 && !_is_int2)){
click_chatter("[Price] Error: needs exactly one interface address.");
return 1;
}
if(_debug)
click_chatter("[%s] configure done. My eth address is: %s\n", _name_element.c_str(), _my_eth_addr.unparse().c_str());
return 0;
}
int Price::initialize(ErrorHandler *errh){
Element::initialize(errh);
_neighbors_element = (Neighbors*) router()->find(_neighbors_name, errh);
_update_timer.initialize(this);
_active_timer.initialize(this);
_check_traffic_timer.initialize(this);
_packet_timer.initialize(this);
//_update_timer.schedule_after_sec(8); //> 5 secs so that we know some neighbors at first iteration
return 0;
}
void Price::push(int port, Packet* p){
if(!_check_traffic_timer.scheduled())
_check_traffic_timer.schedule_after_sec(10);
_packet_timer.schedule_after_msec(_period_ms);
if(port == 0){
//packet to forward
EtherAddress *dst = (EtherAddress *)p->data();
if(dst->is_broadcast() || dst->is_group()){
p->kill();
return;
}
update_price(p, dst);
output(0).push(p);
} else {
//Receives a layer 2.5 frame.
//From input 1 : control traffic (loads of neighboring links)
parse_load_pkt(p);
}
if(!_update_timer.scheduled()) {
if(_now <= (_first_packet + Timestamp(1)))
_update_timer.schedule_after_msec(200);
else
_update_timer.schedule_after_msec(_period_ms);
}
}
void Price::update_price(Packet *p, EtherAddress *dst){
//adds (d_l * \sum_{k \in I(l)}{\gamma_k}) to the corresponding field in the header
//compute d_l:
//double d_l;
//if(capa < 0.1) d_l = 10.0; //corresponds to a "minimum" capacity of 100 kbps
//else d_l = 1.0/capa;
//Add corresponding value to header field:
//double qr_component = d_l * sum_prices;
#ifdef CLICK_USERLEVEL
double capa = ((double)(_neighbors_element->get_capa_for_neighbor(*dst))/1000.0);
if(capa < 0.1) capa = 0.1;
float qr_component = (float) _sum_prices/capa;
#else
uint32_t capa = _neighbors_element->get_capa_for_neighbor(*dst);
if(capa < 100) capa = 100; //corresponds to a minimum capacity of 100 kbps
uint32_t qr_component = int_divide(_sum_prices, capa);
#endif
empower_header *header = (empower_header *)(p->data() + 14);
OutgoingLinkPrice *link = _outgoing_links.get_pointer(*dst);
_now.assign_now();
if(header->_type != TRAFFIC_FORCED) {
_traffic_goes_through = true;
if(link==0) {
OutgoingLinkPrice link;
#ifdef CLICK_USERLEVEL
link.price = _init_price;
#endif
link.rate_demand = header->_rate_demand;
link.rate_demand_route[header->_route] = header->_rate_demand;
link.last_seen = _now;
if(_debug) click_chatter("[%s] New Link to %s, rate_demand = %d", _name_element.c_str(), dst->unparse().c_str(),header->_rate_demand);
_outgoing_links[*dst] = link;
int hop_route = header->_route.number_hops();
if(hop_route > _max_num_hop_routes)
_max_num_hop_routes = hop_route;
_update_timer.schedule_now();
if(_first_packet == Timestamp())
_first_packet.assign_now();
}
else {
// if(!is_in_hash_table(link->rate_demand_route, header->_route)) {
// if(_debug) click_chatter("[%s] New rate demand to %s, is %d", _name_element.c_str(), dst->unparse().c_str(),header->_rate_demand);
// link->rate_demand += header->_rate_demand;
// link->rate_demand_route[header->_route] = header->_rate_demand;
// int hop_route = header->_route.number_hops();
// if(hop_route > _max_num_hop_routes)
// _max_num_hop_routes = hop_route;
// }
// else
if (header->_rate_demand!=link->rate_demand_route[header->_route]) {
if(_debug) {
if((Timestamp::now()-_last_print_demand).sec() > 0) {
click_chatter("[%s] Rate demand to %s is now %d", _name_element.c_str(), dst->unparse().c_str(),header->_rate_demand);
_last_print_demand.assign_now();
}
}
link->rate_demand = link->rate_demand + (int) header->_rate_demand - link->rate_demand_route[header->_route];
link->rate_demand_route[header->_route] = header->_rate_demand;
}
link->last_seen = _now;
}
}
header->_route_price = header->_route_price + qr_component;
if(_sum_prices > 0) {
#ifdef CLICK_USERLEVEL
if(_debug && _nr_updates % 1000 == 0) click_chatter("[%s %s] updating packet on route %s. sum_prices = %f, capa = %f Mbps => qr_component = %f, new price %f",
_name_element.c_str(), _now.unparse().c_str(), header->_route.unparse().c_str(),
_sum_prices, capa, qr_component, header->_route_price);
#endif
_nr_updates++;
}
}
void Price::parse_load_pkt(Packet *p){
//receives layer-2.5 frames
load_pkt *payload = (load_pkt *)(p->data()+sizeof(empower_header));
EtherAddress pkt_src = payload->src;
//Update the "y_self" and "gamma_self" of this neighbor (or create new entry):
_now.assign_now();
NeighborPrice neighbor;
neighbor.sum_airtime_demand = payload->airtime_demand;
neighbor.sum_prices = payload->sum_prices;
neighbor.last_seen = _now;
if(_debug) {
if(neighbor.sum_airtime_demand > 0 || neighbor.sum_prices > 0) {
#ifdef CLICK_USERLEVEL
click_chatter("[%s %s] Node %d: Neighbor with address %s just sent its sum_airtime_demand (%f) and sum_prices (%f)", _name_element.c_str(),
Timestamp::now().unparse().c_str(), _node_index, pkt_src.unparse().c_str(), payload->airtime_demand, payload->sum_prices);
#else
click_chatter("[Price %s] Node %d: Neighbor with address %s just sent its sum_airtime_demand (%d) and sum_prices (%d)",
Timestamp::now().unparse().c_str(), _node_index, pkt_src.unparse().c_str(), payload->airtime_demand, payload->sum_prices);
#endif
}
}
_neighbors[pkt_src] = neighbor;
p->kill();
}
void Price::run_timer(Timer *timer){
//Update my own stuff and let the neighbors know
if(timer == &_packet_timer) {
_first_packet = Timestamp();
}
else if(timer == &_update_timer) {
_active_timer.schedule_after_sec(30);
_now.assign_now();
if(_debug) click_chatter("[%s %s] Run update timer",_name_element.c_str(), Timestamp::now().unparse().c_str());
#ifdef CLICK_USERLEVEL
//double version. works internally using Mbps
//0. decide step size
double current_alpha = _alpha_price;
if(_nr_aggressive_slots > 0){
current_alpha = _alpha_price_aggressive;
_nr_aggressive_slots --;
}
else {
// shorter routes need greater alphas to converge quickly
if (_max_num_hop_routes == 1)
current_alpha*=4;
if (_max_num_hop_routes == 2)
current_alpha*=2;
}
//1. For each outgoing link l, compute airtime demand
// sum over all links to obtain self contribution to y (called y_self)
double y_self = 0.0;
for(HashTable<EtherAddress, OutgoingLinkPrice>::iterator it = _outgoing_links.begin(); it; ++it){
OutgoingLinkPrice* l = _outgoing_links.get_pointer(it.key());
if(l != 0){
double sum_x_s = (double)(l->rate_demand);
sum_x_s = sum_x_s / 1000; //transform to Mbps
double capa = (double)(((double)(_neighbors_element->get_capa_for_neighbor(it.key())))/1000.0);
if(capa < 0.1) capa = 0.1; //Assume at least 100 kbps
if((_debug && sum_x_s > 0) || (sum_x_s/capa) > 2) click_chatter("[%s] link towards address %s has a demand of %f Mbps and capa = %f Mbps", _name_element.c_str(),
it.key().unparse().c_str(), sum_x_s, capa);
y_self += (double)(sum_x_s/capa);
l->rate_demand = 0;
l->rate_demand_route.clear();
}
}
if(_debug && y_self > 0) click_chatter("[%s] y_self (own airtime demand) = %f", _name_element.c_str(), y_self);
if(y_self > 2) {
y_self = 2;
}
//2. Compute y (all the y_l's are equal, as all links l use the same medium for one given price element)
double y = y_self; //scaled y
for(HashTable<EtherAddress, NeighborPrice>::iterator it = _neighbors.begin(); it; ++it){
y += it.value().sum_airtime_demand; //add the "y_self" of neighbors
}
if(_debug && y > 0) click_chatter("[%s] y (neighborhood airtime demand) = %f", _name_element.c_str(), y);
double gamma_self = 0.0;
//3. - Update gamma_l for each outgoing link l
// - Compute gamma_self, the self-contribution to the sum of prices in my neighborhood
for(HashTable<EtherAddress, OutgoingLinkPrice>::iterator it = _outgoing_links.begin(); it; ++it){
OutgoingLinkPrice* l = _outgoing_links.get_pointer(it.key());
if(l != 0){
if((_now-it.value().last_seen).msecval() >= 10*_period_ms) continue;
double old_price = l->price;
if(_now > (_first_packet + Timestamp(0,Timestamp::subsec_per_sec/2)) ) // 500 msec: at beginning, don't update price (we did not get neighbors' airtime yet)
l->price += current_alpha * (y - 1.0 + _delta); //core of the dynamical system
if(l->price > 2.0) l->price = 2.0; // price shouldn't be that big, this may be an error
if(l->price < 0.0) l->price = 0.0;
if(_debug) click_chatter("[Price] link towards address %s has price %f (was %f)", it.key().unparse().c_str(), l->price, old_price);
gamma_self += l->price;
}
}
if(_debug && gamma_self > 0) click_chatter("[%s] gamma_self = %f (current alpha %f, delta %f)", _name_element.c_str(), gamma_self, current_alpha, _delta);
//4. Compute the sum of prices in the whole neighborhood
_sum_prices = gamma_self; //this is a global variable, we re-use it when computing contribution to q_r's.
for(HashTable<EtherAddress, NeighborPrice>::iterator it = _neighbors.begin(); it; ++it){
if((_now-it.value().last_seen).msecval() >= 10*_period_ms) continue;
if(it.value().sum_prices > 2.0) it.value().sum_prices = 2.0; // price shouldn't be that big, this may be an error
if(_debug && (it.value().sum_prices > 0 || it.value().sum_airtime_demand > 0)) click_chatter("[%s] Price for neighbor %s is %f (airtime demand is %f)", _name_element.c_str(),
it.key().unparse().c_str(), it.value().sum_prices, it.value().sum_airtime_demand);
_sum_prices += it.value().sum_prices;
}
if(_debug && _sum_prices > 0) click_chatter("[%s] total neighborhood prices = %f", _name_element.c_str(), _sum_prices);
#else
//integer version
//1. For each outgoing link l, compute airtime demand
// sum over all links to obtain self contribution to y (called y_self)
uint32_t current_alpha = _alpha_price;
uint32_t current_alpha_s = _alpha_s;
uint32_t current_alpha_delta_s = _alpha_delta_s;
if(_nr_aggressive_slots > 0){
current_alpha = _alpha_price_aggressive;
current_alpha_s = _alpha_aggr_s;
current_alpha_delta_s = _alpha_aggr_delta_s;
_nr_aggressive_slots --;
}
uint64_t S_y_self = 0; //scaled y_self
for(HashTable<EtherAddress, OutgoingLinkPrice>::iterator it = _outgoing_links.begin(); it; ++it){
OutgoingLinkPrice* l = _outgoing_links.get_pointer(it.key());
if(l != 0){
if((_now-it.value().last_seen).msecval() >= 10*_period_ms) continue;
uint64_t sum_x_s = (uint64_t)SCALING_FACTOR * (uint64_t)l->rate_demand; //in S * kbps
if(_debug) click_chatter("[Price] S * sum_x_s = %lu", sum_x_s);
uint32_t capa = _neighbors_element->get_capa_for_neighbor(it.key());
if(_debug) click_chatter("[Price] link towards address %s has rate demand = %d and capa = %d Mbps", it.key().unparse().c_str(), l->rate_demand, int_divide(capa,1024));
if(capa < 100) capa = 100; //Assume at least 100 kbps
S_y_self += int_divide(sum_x_s, capa); //64-bit division
l->rate_demand = 0;
l->rate_demand_route.clear();
}
}
if(_debug) click_chatter("[Price] (scaled) y_self = %lu", S_y_self);
//2. Compute y (all the y_l's are equal, as all links l use the same medium for one given price element)
uint64_t S_y = S_y_self; //scaled y
for(HashTable<EtherAddress, NeighborPrice>::iterator it = _neighbors.begin(); it; ++it){
S_y += it.value().sum_airtime_demand; //add the "y_self" (multiplied by SCALING_FACTOR) of neighbors
}
if(_debug) click_chatter("[Price] (scaled) y = %lu. Updating my gamma_l's ...", S_y);
//3. - Update gamma_l for each outgoing link l
// - Compute gamma_self, the self-contribution to the sum of prices in my neighborhood
uint32_t gamma_self = 0;
for(HashTable<EtherAddress, OutgoingLinkPrice>::iterator it = _outgoing_links.begin(); it; ++it){
OutgoingLinkPrice* l = _outgoing_links.get_pointer(it.key());
if(l != 0){
uint64_t temp = (uint64_t)(l->price) + int_divide( ((uint64_t)current_alpha*S_y), 1000) + (uint64_t)current_alpha_delta_s;
if(_debug){
click_chatter("[Price] l->price = %lu, current_alpha*S_y = %lu, current_alpha*S_y/1000 = %lu, current_alpha_delta_s = %lu. => temp = %lu",
(uint64_t)(l->price), ((uint64_t)current_alpha*S_y), int_divide( ((uint64_t)current_alpha*S_y), (uint32_t)1000), (uint64_t)current_alpha_delta_s, temp);
click_chatter("[Price] current_alpha_s = %lu", (uint64_t)current_alpha_s);
}
if(temp <= (uint64_t)current_alpha_s){
l->price = 0;
} else {
l->price = (uint32_t)(temp - (uint64_t)current_alpha_s);
}
gamma_self += l->price;
}
}
if(_debug) click_chatter("[Price] (scaled) gamma_self = %d", gamma_self);
//4. Compute the sum of prices in the whole neighborhood
_sum_prices = gamma_self; //this is a global variable, we re-use it when computing contribution to q_r's.
for(HashTable<EtherAddress, NeighborPrice>::iterator it = _neighbors.begin(); it; ++it){
if((_now-it.value().last_seen).msecval() >= 10*_period_ms) continue;
_sum_prices += it.value().sum_prices;
}
if(_debug) click_chatter("[Price] (scaled) total neighborhood price = %d.", _sum_prices);
#endif //end linuxmodule version
if(_debug) {
// print only if useful (non-zero)
#ifdef CLICK_USERLEVEL
if(y_self > 0 || gamma_self > 0)
click_chatter("[%s] Sending price information (y_self=%f, gamma_self=%f).", _name_element.c_str(), y_self, gamma_self);
#else
if(S_y_self > 0 || gamma_self > 0)
click_chatter("[%s] Sending price information (airtime demand=%d, sum prices=%d).", _name_element.c_str(), (uint32_t) S_y_self, gamma_self);
#endif
}
if(_traffic_goes_through) {
//5. Send y_self and gamma_self to neighbors (broadcast)
uint32_t datalength = sizeof(empower_header) + sizeof(load_pkt);
WritablePacket *pkt = Packet::make(100, 0, datalength, 0);
memset(pkt->data(), 0, pkt->length());
//set header:
empower_header *header = (empower_header *)pkt->data();
load_pkt *payload = (load_pkt *)(pkt->data() + sizeof(empower_header));
if(_is_int1) header->_type = INT1_LOAD_NBOR;
else if(_is_int2) header->_type = INT2_LOAD_NBOR;
memcpy(&(payload->src), &_my_eth_addr, sizeof(EtherAddress));
//set payload:
#ifdef CLICK_USERLEVEL
payload->airtime_demand = y_self;
#else
payload->airtime_demand = (uint32_t)S_y_self;
#endif
payload->sum_prices = gamma_self;
output(1).push(pkt);
}
//6. Clean my neighbors by removing all neighbors that the Neighbors element doesn't know
// Note that we don't need to "clean" the outgoing links (they will naturally have 0 traffic)
Vector<EtherAddress> toremove = Vector<EtherAddress>();
for(HashTable<EtherAddress, NeighborPrice>::iterator it = _neighbors.begin(); it; ++it){
if(!_neighbors_element->neighbor_exists(it.key())){
toremove.push_back(it.key());
}
}
for(int i=0; i<toremove.size(); ++i){
_neighbors.erase(toremove[i]);
}
//_update_timer.schedule_after_msec(_period_ms);
}
else if(timer == &_active_timer) {
if(_debug) click_chatter("[%s] Resetting element...", _name_element.c_str());
reset();
}
else if(timer == &_check_traffic_timer) {
if(_debug)
click_chatter("[%s %s] No traffic goes through", _name_element.c_str(), Timestamp::now().unparse().c_str());
_traffic_goes_through = false;
}
}
String Price::print_info() {
StringAccum s;
s << "["<< _name_element <<"] I have " << _outgoing_links.size() << " neighbor(s):" << "\n";
for(HashTable<EtherAddress, OutgoingLinkPrice>::iterator it = _outgoing_links.begin(); it; ++it){
OutgoingLinkPrice price = it.value();
s << "For neighbor " << it.key().unparse() << ", rate demand =" << price.rate_demand <<
", price=" << price.price;
}
return s.take_string();
}
void
Price::reset() {
if(_debug)
click_chatter("[%s %s] Resetting Price element",_name_element.c_str(), Timestamp::now().unparse().c_str());
_nr_aggressive_slots = _init_nr_aggressive_slots;
_sum_prices = DEFAULT_SUM_PRICE;
_outgoing_links.clear();
_neighbors.clear();
_max_num_hop_routes = 1;
_traffic_goes_through = false;
_first_packet = Timestamp();
}
static String
print_handler(Element *e, void *param) {
Price *price = (Price *)e;
return price->print_info();
}
int
Price::alpha_price_handler(const String &s, Element *e, void *arg, ErrorHandler *errh) {
Price *price_elmt = (Price *) e;
#ifdef CLICK_USERLEVEL
double new_alpha_price;
if(!cp_double(s, &new_alpha_price))
return errh->error("Invalid alpha: must be double");
if(((intptr_t) arg) == 0)
price_elmt->set_alpha_price((double)new_alpha_price);
if(((intptr_t) arg) == 1)
price_elmt->set_alpha_price_aggr((double)new_alpha_price);
#else
uint32_t new_alpha_price;
if(!cp_integer(s, &new_alpha_price))
return errh->error("Invalid alpha: must be integer");
price_elmt->set_alpha_price(new_alpha_price);
#endif
return 0;
}
int
Price::delta_handler(const String &s, Element *e, void *, ErrorHandler *errh) {
Price *price_elmt = (Price *) e;
#ifdef CLICK_USERLEVEL
double new_delta;
if(!cp_double(s, &new_delta))
return errh->error("Invalid delta: must be double");
price_elmt->set_delta((double)new_delta);
#else
uint32_t new_delta;
if(!cp_integer(s, &new_delta))
return errh->error("Invalid delta: must be integer");
price_elmt->set_delta(new_delta);
#endif
return 0;
}
static String
read_parameters_handler(Element *e, void *param) {
Price *pr_elmt = (Price *)e;
StringAccum s;
s << "alpha_price=" << pr_elmt->get_alpha_price() << "\n";
s << "delta=" << pr_elmt->get_delta();
return s.take_string();
}
int
Price::reset_handler(const String &, Element *e, void *,
ErrorHandler *) {
Price *pr = (Price *)e;
pr->reset();
return 0;
}
int
Price::debug_handler(const String &s, Element *e, void *,
ErrorHandler *errh) {
Price *elmt = (Price *)e;
int debug;
if(!cp_integer(s, &debug))
return errh->error("Debug must be 0 or 1");
if (!(debug == 0 || debug == 1))
return errh->error("Debug must be 0 or 1");
elmt->set_debug(debug==1);
return 0;
}
int
Price::nr_aggr_handler(const String &s, Element *e, void *,
ErrorHandler *errh) {
Price *elmt = (Price *)e;
int arg;
if(!cp_integer(s, &arg))
return errh->error("Argument must be an integer");
elmt->set_nr_aggr(arg);
return 0;
}
#ifdef CLICK_USERLEVEL
int
Price::init_price_handler(const String &s, Element *e, void *arg, ErrorHandler *errh) {
Price *price_elmt = (Price *) e;
double new_init_price;
if(!cp_double(s, &new_init_price))
return errh->error("Invalid price: must be double");
price_elmt->set_init_price(new_init_price);
return 0;
}
#endif
void Price::add_handlers() {
add_read_handler("print_info", print_handler,0);
add_read_handler("parameters", read_parameters_handler,0);
add_write_handler("alpha", alpha_price_handler, 0);
add_write_handler("alpha_aggr", alpha_price_handler, 1);
add_write_handler("nr_aggr_slots", nr_aggr_handler, 0);
add_write_handler("delta", delta_handler, 0);
add_write_handler("reset_price", reset_handler, 0);
add_write_handler("debug", debug_handler, 0);
#ifdef CLICK_USERLEVEL
add_write_handler("init_price", init_price_handler, 0);
#endif
}
CLICK_ENDDECLS
EXPORT_ELEMENT(Price)

Event Timeline