|
Contiki 2.5
|
00001 /** 00002 * \addtogroup rimecollect 00003 * @{ 00004 */ 00005 00006 /* 00007 * Copyright (c) 2006, Swedish Institute of Computer Science. 00008 * All rights reserved. 00009 * 00010 * Redistribution and use in source and binary forms, with or without 00011 * modification, are permitted provided that the following conditions 00012 * are met: 00013 * 1. Redistributions of source code must retain the above copyright 00014 * notice, this list of conditions and the following disclaimer. 00015 * 2. Redistributions in binary form must reproduce the above copyright 00016 * notice, this list of conditions and the following disclaimer in the 00017 * documentation and/or other materials provided with the distribution. 00018 * 3. Neither the name of the Institute nor the names of its contributors 00019 * may be used to endorse or promote products derived from this software 00020 * without specific prior written permission. 00021 * 00022 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 00023 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00024 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00025 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 00026 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 00027 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 00028 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 00029 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00030 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 00031 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 00032 * SUCH DAMAGE. 00033 * 00034 * This file is part of the Contiki operating system. 00035 * 00036 * $Id: collect.c,v 1.73 2011/01/18 16:05:53 adamdunkels Exp $ 00037 */ 00038 00039 /** 00040 * \file 00041 * Tree-based hop-by-hop reliable data collection 00042 * \author 00043 * Adam Dunkels<EMAIL_ADDRESS>00044 */ 00045 00046 #include "contiki.h" 00047 00048 #include "net/rime.h" 00049 #include "net/rime/collect.h" 00050 #include "net/rime/collect-neighbor.h" 00051 #include "net/rime/collect-link-estimate.h" 00052 00053 #include "net/packetqueue.h" 00054 00055 #include "dev/radio-sensor.h" 00056 00057 #include "lib/random.h" 00058 00059 #include <string.h> 00060 #include <stdio.h> 00061 #include <stddef.h> 00062 00063 static const struct packetbuf_attrlist attributes[] = 00064 { 00065 COLLECT_ATTRIBUTES 00066 PACKETBUF_ATTR_LAST 00067 }; 00068 00069 00070 /* The recent_packets list holds the sequence number, the originator, 00071 and the connection for packets that have been recently 00072 forwarded. This list is maintained to avoid forwarding duplicate 00073 packets. */ 00074 #define NUM_RECENT_PACKETS 16 00075 00076 struct recent_packet { 00077 struct collect_conn *conn; 00078 rimeaddr_t originator; 00079 uint8_t eseqno; 00080 }; 00081 00082 static struct recent_packet recent_packets[NUM_RECENT_PACKETS]; 00083 static uint8_t recent_packet_ptr; 00084 00085 00086 /* This is the header of data packets. The header comtains the routing 00087 metric of the last hop sender. This is used to avoid routing loops: 00088 if a node receives a packet with a lower routing metric than its 00089 own, it drops the packet. */ 00090 struct data_msg_hdr { 00091 uint8_t flags, dummy; 00092 uint16_t rtmetric; 00093 }; 00094 00095 00096 /* This is the header of ACK packets. It contains a flags field that 00097 indicates if the node is congested (ACK_FLAGS_CONGESTED), if the 00098 packet was dropped (ACK_FLAGS_DROPPED), if a packet was dropped due 00099 to its lifetime was exceeded (ACK_FLAGS_LIFETIME_EXCEEDED), and if 00100 an outdated rtmetric was detected 00101 (ACK_FLAGS_RTMETRIC_NEEDS_UPDATE). The flags can contain any 00102 combination of the flags. The ACK header also contains the routing 00103 metric of the node that sends tha ACK. This is used to keep an 00104 up-to-date routing state in the network. */ 00105 struct ack_msg { 00106 uint8_t flags, dummy; 00107 uint16_t rtmetric; 00108 }; 00109 00110 #define ACK_FLAGS_CONGESTED 0x80 00111 #define ACK_FLAGS_DROPPED 0x40 00112 #define ACK_FLAGS_LIFETIME_EXCEEDED 0x20 00113 #define ACK_FLAGS_RTMETRIC_NEEDS_UPDATE 0x10 00114 00115 00116 /* These are configuration knobs that normally should not be 00117 tweaked. MAX_MAC_REXMITS defines how many times the underlying CSMA 00118 MAC layer should attempt to resend a data packet before giving 00119 up. The MAX_ACK_MAC_REXMITS defines how many times the MAC layer 00120 should resend ACK packets. The REXMIT_TIME is the lowest 00121 retransmission timeout at the network layer. It is exponentially 00122 increased for every new network layer retransmission. The 00123 FORWARD_PACKET_LIFETIME is the maximum time a packet is held in the 00124 forwarding queue before it is removed. The MAX_SENDING_QUEUE 00125 specifies the maximum length of the output queue. If the queue is 00126 full, incoming packets are dropped instead of being forwarded. */ 00127 #define MAX_MAC_REXMITS 2 00128 #define MAX_ACK_MAC_REXMITS 5 00129 #define REXMIT_TIME CLOCK_SECOND * 4 00130 #define FORWARD_PACKET_LIFETIME_BASE REXMIT_TIME * 2 00131 #define MAX_SENDING_QUEUE 3 * QUEUEBUF_NUM / 4 00132 #define MIN_AVAILABLE_QUEUE_ENTRIES 4 00133 #define KEEPALIVE_REXMITS 8 00134 #define MAX_REXMITS 31 00135 00136 MEMB(send_queue_memb, struct packetqueue_item, MAX_SENDING_QUEUE); 00137 00138 /* These specifiy the sink's routing metric (0) and the maximum 00139 routing metric. If a node has routing metric zero, it is the 00140 sink. If a node has the maximum routing metric, it has no route to 00141 a sink. */ 00142 #define RTMETRIC_SINK 0 00143 #define RTMETRIC_MAX COLLECT_MAX_DEPTH 00144 00145 /* Here we define what we mean with a significantly improved 00146 rtmetric. This is used to determine when a new parent should be 00147 chosen over an old parent and when to begin more rapidly advertise 00148 a new rtmetric. */ 00149 #define SIGNIFICANT_RTMETRIC_PARENT_CHANGE (COLLECT_LINK_ESTIMATE_UNIT + \ 00150 COLLECT_LINK_ESTIMATE_UNIT / 2) 00151 00152 /* This defines the maximum hops that a packet can take before it is 00153 dropped. */ 00154 #define MAX_HOPLIM 15 00155 00156 00157 /* Proactive probing: when there are no packets in the send 00158 queue, the system periodically sends a dummy packet to potential 00159 parents, i.e., neighbors with a lower rtmetric than we have but for 00160 which we do not yet have a link quality estimate. */ 00161 #define PROACTIVE_PROBING_INTERVAL (random_rand() % CLOCK_SECOND * 60) 00162 #define PROACTIVE_PROBING_REXMITS 15 00163 00164 /* The ANNOUNCEMENT_SCAN_TIME defines for how long the Collect 00165 implementation should listen for announcements from other nodes 00166 when it requires a route. */ 00167 #ifdef ANNOUNCEMENT_CONF_PERIOD 00168 #define ANNOUNCEMENT_SCAN_TIME ANNOUNCEMENT_CONF_PERIOD 00169 #else /* ANNOUNCEMENT_CONF_PERIOD */ 00170 #define ANNOUNCEMENT_SCAN_TIME CLOCK_SECOND 00171 #endif /* ANNOUNCEMENT_CONF_PERIOD */ 00172 00173 00174 /* Statistics structure */ 00175 struct { 00176 uint32_t foundroute; 00177 uint32_t newparent; 00178 uint32_t routelost; 00179 00180 uint32_t acksent; 00181 uint32_t datasent; 00182 00183 uint32_t datarecv; 00184 uint32_t ackrecv; 00185 uint32_t badack; 00186 uint32_t duprecv; 00187 00188 uint32_t qdrop; 00189 uint32_t rtdrop; 00190 uint32_t ttldrop; 00191 uint32_t ackdrop; 00192 uint32_t timedout; 00193 } stats; 00194 00195 /* Debug definition: draw routing tree in Cooja. */ 00196 #define DRAW_TREE 0 00197 #define DEBUG 0 00198 #if DEBUG 00199 #include <stdio.h> 00200 #define PRINTF(...) printf(__VA_ARGS__) 00201 #else 00202 #define PRINTF(...) 00203 #endif 00204 00205 /* Forward declarations. */ 00206 static void send_queued_packet(struct collect_conn *c); 00207 static void retransmit_callback(void *ptr); 00208 static void retransmit_not_sent_callback(void *ptr); 00209 static void set_keepalive_timer(struct collect_conn *c); 00210 00211 /*---------------------------------------------------------------------------*/ 00212 /** 00213 * This function computes the current rtmetric by adding the last 00214 * known rtmetric from our parent with the link estimate to the 00215 * parent. 00216 * 00217 */ 00218 static uint16_t 00219 rtmetric_compute(struct collect_conn *tc) 00220 { 00221 struct collect_neighbor *n; 00222 uint16_t rtmetric = RTMETRIC_MAX; 00223 00224 /* This function computes the current rtmetric for this node. It 00225 uses the rtmetric of the parent node in the tree and adds the 00226 current link estimate from us to the parent node. */ 00227 00228 /* The collect connection structure stores the address of its 00229 current parent. We look up the neighbor identification struct in 00230 the collect-neighbor list. */ 00231 n = collect_neighbor_list_find(&tc->neighbor_list, &tc->parent); 00232 00233 /* If n is NULL, we have no best neighbor. Thus our rtmetric is 00234 then COLLECT_RTMETRIC_MAX. */ 00235 if(n == NULL) { 00236 rtmetric = RTMETRIC_MAX; 00237 } else { 00238 /* Our rtmetric is the rtmetric of our parent neighbor plus 00239 the expected transmissions to reach that neighbor. */ 00240 rtmetric = collect_neighbor_rtmetric_link_estimate(n); 00241 } 00242 00243 return rtmetric; 00244 } 00245 /*---------------------------------------------------------------------------*/ 00246 /** 00247 * This function is called when the route advertisements need to be 00248 * transmitted more rapidly. 00249 * 00250 */ 00251 static void 00252 bump_advertisement(struct collect_conn *c) 00253 { 00254 #if !COLLECT_ANNOUNCEMENTS 00255 neighbor_discovery_start(&c->neighbor_discovery_conn, c->rtmetric); 00256 #else 00257 announcement_bump(&c->announcement); 00258 #endif /* !COLLECT_ANNOUNCEMENTS */ 00259 } 00260 /*---------------------------------------------------------------------------*/ 00261 /** 00262 * This function is called to update the current parent node. The 00263 * parent may change if new routing information has been found, for 00264 * example if a new node with a lower rtmetric and link estimate has 00265 * appeared. 00266 * 00267 */ 00268 static void 00269 update_parent(struct collect_conn *tc) 00270 { 00271 struct collect_neighbor *current; 00272 struct collect_neighbor *best; 00273 00274 /* We grab the collect_neighbor struct of our current parent. */ 00275 current = collect_neighbor_list_find(&tc->neighbor_list, &tc->parent); 00276 00277 /* We call the collect_neighbor module to find the current best 00278 parent. */ 00279 best = collect_neighbor_list_best(&tc->neighbor_list); 00280 00281 /* We check if we need to switch parent. Switching parent is done in 00282 the following situations: 00283 00284 * We do not have a current parent. 00285 * The best parent is significantly better than the current parent. 00286 00287 If we do not have a current parent, and have found a best parent, 00288 we simply use the new best parent. 00289 00290 If we already have a current parent, but have found a new parent 00291 that is better, we employ a heuristic to avoid switching parents 00292 too often. The new parent must be significantly better than the 00293 current parent. Being "significantly better" is defined as having 00294 an rtmetric that is has a difference of at least 1.5 times the 00295 COLLECT_LINK_ESTIMATE_UNIT. This is derived from the experience 00296 by Gnawali et al (SenSys 2009). */ 00297 00298 if(best != NULL) { 00299 rimeaddr_t previous_parent; 00300 00301 if(DRAW_TREE) { 00302 rimeaddr_copy(&previous_parent, &tc->parent); 00303 } 00304 00305 if(current == NULL) { 00306 /* New parent. */ 00307 PRINTF("update_parent: new parent %d.%d\n", 00308 best->addr.u8[0], best->addr.u8[1]); 00309 rimeaddr_copy(&tc->parent, &best->addr); 00310 stats.foundroute++; 00311 bump_advertisement(tc); 00312 } else { 00313 if(DRAW_TREE) { 00314 printf("#A e=%d\n", collect_neighbor_link_estimate(best)); 00315 } 00316 if(collect_neighbor_rtmetric_link_estimate(best) + 00317 SIGNIFICANT_RTMETRIC_PARENT_CHANGE < 00318 collect_neighbor_rtmetric_link_estimate(current)) { 00319 00320 /* We switch parent. */ 00321 PRINTF("update_parent: new parent %d.%d (%d) old parent %d.%d (%d)\n", 00322 best->addr.u8[0], best->addr.u8[1], 00323 collect_neighbor_rtmetric(best), 00324 tc->parent.u8[0], tc->parent.u8[1], 00325 collect_neighbor_rtmetric(current)); 00326 rimeaddr_copy(&tc->parent, &best->addr); 00327 stats.newparent++; 00328 /* Since we now have a significantly better or worse rtmetric than 00329 we had before, we let our neighbors know this quickly. */ 00330 bump_advertisement(tc); 00331 00332 if(DRAW_TREE) { 00333 printf("#A e=%d\n", collect_neighbor_link_estimate(best)); 00334 /* { 00335 int i; 00336 int etx = 0; 00337 printf("#A l="); 00338 for(i = 0; i < 8; i++) { 00339 printf("%d ", best->le.history[(best->le.historyptr - 1 - i) & 7]); 00340 etx += current->le.history[i]; 00341 } 00342 printf("\n"); 00343 }*/ 00344 } 00345 } else { 00346 if(DRAW_TREE) { 00347 printf("#A e=%d\n", collect_neighbor_link_estimate(current)); 00348 /* { 00349 int i; 00350 int etx = 0; 00351 printf("#A l="); 00352 for(i = 0; i < 8; i++) { 00353 printf("%d ", current->le.history[(current->le.historyptr - 1 - i) & 7]); 00354 etx += current->le.history[i]; 00355 } 00356 printf("\n"); 00357 }*/ 00358 } 00359 } 00360 } 00361 if(DRAW_TREE) { 00362 if(!rimeaddr_cmp(&previous_parent, &tc->parent)) { 00363 if(!rimeaddr_cmp(&previous_parent, &rimeaddr_null)) { 00364 printf("#L %d 0\n", previous_parent.u8[0]); 00365 } 00366 printf("#L %d 1\n", tc->parent.u8[0]); 00367 } 00368 } 00369 } else { 00370 /* No parent. */ 00371 if(!rimeaddr_cmp(&tc->parent, &rimeaddr_null)) { 00372 if(DRAW_TREE) { 00373 printf("#L %d 0\n", tc->parent.u8[0]); 00374 } 00375 stats.routelost++; 00376 } 00377 rimeaddr_copy(&tc->parent, &rimeaddr_null); 00378 } 00379 } 00380 /*---------------------------------------------------------------------------*/ 00381 /** 00382 * This function is called whenever there is a chance that the routing 00383 * metric has changed. The function goes through the list of neighbors 00384 * to compute the new routing metric. If the metric has changed, it 00385 * notifies neighbors. 00386 * 00387 * 00388 */ 00389 static void 00390 update_rtmetric(struct collect_conn *tc) 00391 { 00392 PRINTF("update_rtmetric: tc->rtmetric %d\n", tc->rtmetric); 00393 00394 /* We should only update the rtmetric if we are not the sink. */ 00395 if(tc->rtmetric != RTMETRIC_SINK) { 00396 uint16_t old_rtmetric, new_rtmetric; 00397 00398 /* We remember the current (old) rtmetric for later. */ 00399 old_rtmetric = tc->rtmetric; 00400 00401 /* We may need to update our parent node so we do that now. */ 00402 update_parent(tc); 00403 00404 /* We compute the new rtmetric. */ 00405 new_rtmetric = rtmetric_compute(tc); 00406 00407 /* We sanity check our new rtmetric. */ 00408 if(new_rtmetric == RTMETRIC_SINK) { 00409 /* Defensive programming: if the new rtmetric somehow got to be 00410 the rtmetric of the sink, there is a bug somewhere. To avoid 00411 destroying the network, we simply will not assume this new 00412 rtmetric. Instead, we set our rtmetric to maximum, to 00413 indicate that we have no sane route. */ 00414 new_rtmetric = RTMETRIC_MAX; 00415 } 00416 00417 /* We set our new rtmetric in the collect conn structure. Then we 00418 decide how we should announce this new rtmetric. */ 00419 tc->rtmetric = new_rtmetric; 00420 00421 if(tc->is_router) { 00422 /* If we are a router, we update our advertised rtmetric. */ 00423 #if COLLECT_ANNOUNCEMENTS 00424 announcement_set_value(&tc->announcement, tc->rtmetric); 00425 #else /* COLLECT_ANNOUNCEMENTS */ 00426 neighbor_discovery_set_val(&tc->neighbor_discovery_conn, tc->rtmetric); 00427 #endif /* COLLECT_ANNOUNCEMENTS */ 00428 00429 } 00430 PRINTF("%d.%d: new rtmetric %d\n", 00431 rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1], 00432 tc->rtmetric); 00433 00434 /* We got a new, working, route we send any queued packets we may have. */ 00435 if(old_rtmetric == RTMETRIC_MAX && new_rtmetric != RTMETRIC_MAX) { 00436 PRINTF("Sending queued packet because rtmetric was max\n"); 00437 send_queued_packet(tc); 00438 } 00439 if(DRAW_TREE) { 00440 if(old_rtmetric != new_rtmetric) { 00441 printf("#A rt=%d,p=%d\n", tc->rtmetric, tc->parent.u8[0]); 00442 } 00443 } 00444 } 00445 } 00446 /*---------------------------------------------------------------------------*/ 00447 static int 00448 enqueue_dummy_packet(struct collect_conn *c, int rexmits) 00449 { 00450 struct collect_neighbor *n; 00451 00452 packetbuf_clear(); 00453 packetbuf_set_attr(PACKETBUF_ATTR_EPACKET_ID, c->eseqno - 1); 00454 packetbuf_set_addr(PACKETBUF_ADDR_ESENDER, &rimeaddr_node_addr); 00455 packetbuf_set_attr(PACKETBUF_ATTR_HOPS, 1); 00456 packetbuf_set_attr(PACKETBUF_ATTR_TTL, 1); 00457 packetbuf_set_attr(PACKETBUF_ATTR_MAX_REXMIT, rexmits); 00458 00459 PRINTF("%d.%d: enqueueing dummy packet %d, max_rexmits %d\n", 00460 rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1], 00461 packetbuf_attr(PACKETBUF_ATTR_EPACKET_ID), 00462 packetbuf_attr(PACKETBUF_ATTR_MAX_REXMIT)); 00463 00464 /* Allocate space for the header. */ 00465 packetbuf_hdralloc(sizeof(struct data_msg_hdr)); 00466 00467 n = collect_neighbor_list_find(&c->neighbor_list, &c->parent); 00468 if(n != NULL) { 00469 return packetqueue_enqueue_packetbuf(&c->send_queue, 00470 FORWARD_PACKET_LIFETIME_BASE * rexmits, 00471 c); 00472 } 00473 return 0; 00474 } 00475 /*---------------------------------------------------------------------------*/ 00476 static void 00477 send_packet(struct collect_conn *c, struct collect_neighbor *n) 00478 { 00479 clock_time_t time; 00480 00481 PRINTF("Sending packet to %d.%d, %d transmissions\n", 00482 n->addr.u8[0], n->addr.u8[1], 00483 c->transmissions); 00484 /* Defensive programming: if a bug in the MAC/RDC layers will cause 00485 it to not call us back, we'll set up the retransmission timer 00486 with a high timeout, so that we can cancel the transmission and 00487 send a new one. */ 00488 time = 16 * REXMIT_TIME; 00489 ctimer_set(&c->retransmission_timer, time, 00490 retransmit_not_sent_callback, c); 00491 c->send_time = clock_time(); 00492 00493 unicast_send(&c->unicast_conn, &n->addr); 00494 } 00495 /*---------------------------------------------------------------------------*/ 00496 static void 00497 proactive_probing_callback(void *ptr) 00498 { 00499 struct collect_conn *c = ptr; 00500 struct packetqueue_item *i; 00501 00502 ctimer_set(&c->proactive_probing_timer, PROACTIVE_PROBING_INTERVAL, 00503 proactive_probing_callback, ptr); 00504 00505 /* Only do proactive link probing if we are not the sink and if we 00506 have a route. */ 00507 if(c->rtmetric != RTMETRIC_SINK && c->rtmetric != RTMETRIC_MAX) { 00508 /* Grab the first packet on the send queue to see if the queue is 00509 empty or not. */ 00510 i = packetqueue_first(&c->send_queue); 00511 if(i == NULL) { 00512 /* If there are no packets to send, we go through the list of 00513 neighbors to find a potential parent for which we do not have a 00514 link estimate and send a dummy packet to it. This allows us to 00515 quickly gauge the link quality of neighbors that we do not 00516 currently use as parents. */ 00517 struct collect_neighbor *n; 00518 00519 /* Find the neighbor with the lowest number of estimates. */ 00520 for(n = list_head(collect_neighbor_list(&c->neighbor_list)); 00521 n != NULL; n = list_item_next(n)) { 00522 if(n->rtmetric + COLLECT_LINK_ESTIMATE_UNIT < c->rtmetric && 00523 collect_link_estimate_num_estimates(&n->le) == 0) { 00524 rimeaddr_t current_parent; 00525 00526 PRINTF("proactive_probing_callback: found neighbor with no link estimate, %d.%d\n", 00527 n->addr.u8[RIMEADDR_SIZE - 2], n->addr.u8[RIMEADDR_SIZE - 1]); 00528 00529 rimeaddr_copy(¤t_parent, &c->parent); 00530 rimeaddr_copy(&c->parent, &n->addr); 00531 if(enqueue_dummy_packet(c, PROACTIVE_PROBING_REXMITS)) { 00532 send_queued_packet(c); 00533 } 00534 rimeaddr_copy(&c->parent, ¤t_parent); 00535 return; 00536 } 00537 } 00538 } 00539 PRINTF("%d.%d: nothing on queue\n", 00540 rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1]); 00541 return; 00542 } 00543 } 00544 /*---------------------------------------------------------------------------*/ 00545 /** 00546 * This function is called when a queued packet should be sent 00547 * out. The function takes the first packet on the output queue, adds 00548 * the necessary packet attributes, and sends the packet to the 00549 * next-hop neighbor. 00550 * 00551 */ 00552 static void 00553 send_queued_packet(struct collect_conn *c) 00554 { 00555 struct queuebuf *q; 00556 struct collect_neighbor *n; 00557 struct packetqueue_item *i; 00558 struct data_msg_hdr hdr; 00559 int max_mac_rexmits; 00560 00561 /* If we are currently sending a packet, we do not attempt to send 00562 another one. */ 00563 if(c->sending) { 00564 PRINTF("%d.%d: queue, c is sending\n", 00565 rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1]); 00566 return; 00567 } 00568 00569 00570 /* Grab the first packet on the send queue. */ 00571 i = packetqueue_first(&c->send_queue); 00572 if(i == NULL) { 00573 PRINTF("%d.%d: nothing on queue\n", 00574 rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1]); 00575 00576 return; 00577 } 00578 00579 /* We should send the first packet from the queue. */ 00580 q = packetqueue_queuebuf(i); 00581 if(q != NULL) { 00582 /* Place the queued packet into the packetbuf. */ 00583 queuebuf_to_packetbuf(q); 00584 00585 /* Pick the neighbor to which to send the packet. We use the 00586 parent in the n->parent. */ 00587 n = collect_neighbor_list_find(&c->neighbor_list, &c->parent); 00588 00589 if(n != NULL) { 00590 00591 /* If the connection had a neighbor, we construct the packet 00592 buffer attributes and set the appropriate flags in the 00593 Collect connection structure and send the packet. */ 00594 00595 PRINTF("%d.%d: sending packet to %d.%d with eseqno %d\n", 00596 rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1], 00597 n->addr.u8[0], n->addr.u8[1], 00598 packetbuf_attr(PACKETBUF_ATTR_EPACKET_ID)); 00599 00600 /* Mark that we are currently sending a packet. */ 00601 c->sending = 1; 00602 00603 /* Remember the parent that we sent this packet to. */ 00604 rimeaddr_copy(&c->current_parent, &c->parent); 00605 00606 /* This is the first time we transmit this packet, so set 00607 transmissions to zero. */ 00608 c->transmissions = 0; 00609 00610 /* Remember that maximum amount of retransmissions we should 00611 make. This is stored inside a packet attribute in the packet 00612 on the send queue. */ 00613 c->max_rexmits = packetbuf_attr(PACKETBUF_ATTR_MAX_REXMIT); 00614 00615 /* Set the packet attributes: this packet wants an ACK, so we 00616 sent the PACKETBUF_ATTR_RELIABLE flag; the MAC should retry 00617 MAX_MAC_REXMITS times; and the PACKETBUF_ATTR_PACKET_ID is 00618 set to the current sequence number on the connection. */ 00619 packetbuf_set_attr(PACKETBUF_ATTR_RELIABLE, 1); 00620 00621 max_mac_rexmits = c->max_rexmits > MAX_MAC_REXMITS? 00622 MAX_MAC_REXMITS : c->max_rexmits; 00623 packetbuf_set_attr(PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS, max_mac_rexmits); 00624 packetbuf_set_attr(PACKETBUF_ATTR_PACKET_ID, c->seqno); 00625 00626 stats.datasent++; 00627 00628 /* Copy our rtmetric into the packet header of the outgoing 00629 packet. */ 00630 memset(&hdr, 0, sizeof(hdr)); 00631 hdr.rtmetric = c->rtmetric; 00632 memcpy(packetbuf_dataptr(), &hdr, sizeof(struct data_msg_hdr)); 00633 00634 /* Send the packet. */ 00635 send_packet(c, n); 00636 00637 } else { 00638 #if COLLECT_ANNOUNCEMENTS 00639 #if COLLECT_CONF_WITH_LISTEN 00640 PRINTF("listen\n"); 00641 announcement_listen(1); 00642 ctimer_set(&c->transmit_after_scan_timer, ANNOUNCEMENT_SCAN_TIME, 00643 send_queued_packet, c); 00644 #else /* COLLECT_CONF_WITH_LISTEN */ 00645 announcement_set_value(&c->announcement, RTMETRIC_MAX); 00646 announcement_bump(&c->announcement); 00647 #endif /* COLLECT_CONF_WITH_LISTEN */ 00648 #endif /* COLLECT_ANNOUNCEMENTS */ 00649 } 00650 } 00651 } 00652 /*---------------------------------------------------------------------------*/ 00653 /** 00654 * This function is called to retransmit the first packet on the send 00655 * queue. 00656 * 00657 */ 00658 static void 00659 retransmit_current_packet(struct collect_conn *c) 00660 { 00661 struct queuebuf *q; 00662 struct collect_neighbor *n; 00663 struct packetqueue_item *i; 00664 struct data_msg_hdr hdr; 00665 int max_mac_rexmits; 00666 00667 /* Grab the first packet on the send queue, which is the one we are 00668 about to retransmit. */ 00669 i = packetqueue_first(&c->send_queue); 00670 if(i == NULL) { 00671 PRINTF("%d.%d: nothing on queue\n", 00672 rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1]); 00673 /* No packet on the queue, so there is nothing for us to send. */ 00674 return; 00675 } 00676 00677 /* Get hold of the queuebuf. */ 00678 q = packetqueue_queuebuf(i); 00679 if(q != NULL) { 00680 00681 update_rtmetric(c); 00682 00683 /* Place the queued packet into the packetbuf. */ 00684 queuebuf_to_packetbuf(q); 00685 00686 /* Pick the neighbor to which to send the packet. If we have found 00687 a better parent while we were transmitting this packet, we 00688 chose that neighbor instead. If so, we need to attribute the 00689 transmissions we made for the parent to that neighbor. */ 00690 if(!rimeaddr_cmp(&c->current_parent, &c->parent)) { 00691 /* struct collect_neighbor *current_neighbor; 00692 current_neighbor = collect_neighbor_list_find(&c->neighbor_list, 00693 &c->current_parent); 00694 if(current_neighbor != NULL) { 00695 collect_neighbor_tx(current_neighbor, c->max_rexmits); 00696 }*/ 00697 00698 PRINTF("parent change from %d.%d to %d.%d after %d tx\n", 00699 c->current_parent.u8[0], c->current_parent.u8[1], 00700 c->parent.u8[0], c->parent.u8[1], 00701 c->transmissions); 00702 00703 rimeaddr_copy(&c->current_parent, &c->parent); 00704 c->transmissions = 0; 00705 } 00706 n = collect_neighbor_list_find(&c->neighbor_list, &c->current_parent); 00707 00708 if(n != NULL) { 00709 00710 /* If the connection had a neighbor, we construct the packet 00711 buffer attributes and set the appropriate flags in the 00712 Collect connection structure and send the packet. */ 00713 00714 PRINTF("%d.%d: sending packet to %d.%d with eseqno %d\n", 00715 rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1], 00716 n->addr.u8[0], n->addr.u8[1], 00717 packetbuf_attr(PACKETBUF_ATTR_EPACKET_ID)); 00718 00719 /* Mark that we are currently sending a packet. */ 00720 c->sending = 1; 00721 packetbuf_set_attr(PACKETBUF_ATTR_RELIABLE, 1); 00722 max_mac_rexmits = c->max_rexmits - c->transmissions > MAX_MAC_REXMITS? 00723 MAX_MAC_REXMITS : c->max_rexmits - c->transmissions; 00724 packetbuf_set_attr(PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS, max_mac_rexmits); 00725 packetbuf_set_attr(PACKETBUF_ATTR_PACKET_ID, c->seqno); 00726 00727 /* Copy our rtmetric into the packet header of the outgoing 00728 packet. */ 00729 memset(&hdr, 0, sizeof(hdr)); 00730 hdr.rtmetric = c->rtmetric; 00731 memcpy(packetbuf_dataptr(), &hdr, sizeof(struct data_msg_hdr)); 00732 00733 /* Send the packet. */ 00734 send_packet(c, n); 00735 } 00736 } 00737 00738 } 00739 /*---------------------------------------------------------------------------*/ 00740 static void 00741 send_next_packet(struct collect_conn *tc) 00742 { 00743 /* Remove the first packet on the queue, the packet that was just sent. */ 00744 packetqueue_dequeue(&tc->send_queue); 00745 tc->seqno = (tc->seqno + 1) % (1 << COLLECT_PACKET_ID_BITS); 00746 00747 /* Cancel retransmission timer. */ 00748 ctimer_stop(&tc->retransmission_timer); 00749 tc->sending = 0; 00750 tc->transmissions = 0; 00751 00752 PRINTF("sending next packet, seqno %d, queue len %d\n", 00753 tc->seqno, packetqueue_len(&tc->send_queue)); 00754 00755 /* Send the next packet in the queue, if any. */ 00756 send_queued_packet(tc); 00757 } 00758 /*---------------------------------------------------------------------------*/ 00759 static void 00760 handle_ack(struct collect_conn *tc) 00761 { 00762 struct ack_msg *msg; 00763 uint16_t rtmetric; 00764 struct collect_neighbor *n; 00765 00766 PRINTF("handle_ack: sender %d.%d current_parent %d.%d, id %d seqno %d\n", 00767 packetbuf_addr(PACKETBUF_ADDR_SENDER)->u8[0], 00768 packetbuf_addr(PACKETBUF_ADDR_SENDER)->u8[1], 00769 tc->current_parent.u8[0], tc->current_parent.u8[1], 00770 packetbuf_attr(PACKETBUF_ATTR_PACKET_ID), tc->seqno); 00771 if(rimeaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_SENDER), 00772 &tc->current_parent) && 00773 packetbuf_attr(PACKETBUF_ATTR_PACKET_ID) == tc->seqno) { 00774 00775 /* printf("rtt %d / %d = %d.%02d\n", 00776 (int)(clock_time() - tc->send_time), 00777 (int)CLOCK_SECOND, 00778 (int)((clock_time() - tc->send_time) / CLOCK_SECOND), 00779 (int)(((100 * (clock_time() - tc->send_time)) / CLOCK_SECOND) % 100));*/ 00780 00781 stats.ackrecv++; 00782 msg = packetbuf_dataptr(); 00783 memcpy(&rtmetric, &msg->rtmetric, sizeof(uint16_t)); 00784 00785 /* It is possible that we receive an ACK for a packet that we 00786 think we have not yet sent: if our transmission was received by 00787 the other node, but the link-layer ACK was lost, our 00788 transmission counter may still be zero. If this is the case, we 00789 play it safe by believing that we have sent MAX_MAC_REXMITS 00790 transmissions. */ 00791 if(tc->transmissions == 0) { 00792 tc->transmissions = MAX_MAC_REXMITS; 00793 } 00794 PRINTF("Updating link estimate with %d transmissions\n", 00795 tc->transmissions); 00796 n = collect_neighbor_list_find(&tc->neighbor_list, 00797 packetbuf_addr(PACKETBUF_ADDR_SENDER)); 00798 00799 if(n != NULL) { 00800 collect_neighbor_tx(n, tc->transmissions); 00801 collect_neighbor_update_rtmetric(n, rtmetric); 00802 update_rtmetric(tc); 00803 } 00804 00805 PRINTF("%d.%d: ACK from %d.%d after %d transmissions, flags %02x, rtmetric %d\n", 00806 rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1], 00807 tc->current_parent.u8[0], tc->current_parent.u8[1], 00808 tc->transmissions, 00809 msg->flags, 00810 rtmetric); 00811 00812 /* The ack contains information about the state of the packet and 00813 of the node that received it. We do different things depending 00814 on whether or not the packet was dropped. First, we check if 00815 the receiving node was congested. If so, we add a maximum 00816 transmission number to its routing metric, which increases the 00817 chance that another parent will be chosen. */ 00818 if(msg->flags & ACK_FLAGS_CONGESTED) { 00819 PRINTF("ACK flag indicated parent was congested.\n"); 00820 collect_neighbor_set_congested(n); 00821 collect_neighbor_tx(n, tc->max_rexmits * 2); 00822 update_rtmetric(tc); 00823 } 00824 if((msg->flags & ACK_FLAGS_DROPPED) == 0) { 00825 /* If the packet was successfully received, we send the next packet. */ 00826 send_next_packet(tc); 00827 } else { 00828 /* If the packet was lost due to its lifetime being exceeded, 00829 there is not much more we can do with the packet, so we send 00830 the next one instead. */ 00831 if((msg->flags & ACK_FLAGS_LIFETIME_EXCEEDED)) { 00832 send_next_packet(tc); 00833 } else { 00834 /* If the packet was dropped, but without the node being 00835 congested or the packets lifetime being exceeded, we 00836 penalize the parent and try sending the packet again. */ 00837 PRINTF("ACK flag indicated packet was dropped by parent.\n"); 00838 collect_neighbor_tx(n, tc->max_rexmits); 00839 update_rtmetric(tc); 00840 00841 ctimer_set(&tc->retransmission_timer, 00842 REXMIT_TIME + (random_rand() % (REXMIT_TIME)), 00843 retransmit_callback, tc); 00844 } 00845 } 00846 00847 /* Our neighbor's rtmetric needs to be updated, so we bump our 00848 advertisements. */ 00849 if(msg->flags & ACK_FLAGS_RTMETRIC_NEEDS_UPDATE) { 00850 bump_advertisement(tc); 00851 } 00852 set_keepalive_timer(tc); 00853 } else { 00854 stats.badack++; 00855 } 00856 } 00857 /*---------------------------------------------------------------------------*/ 00858 static void 00859 send_ack(struct collect_conn *tc, const rimeaddr_t *to, int flags) 00860 { 00861 struct ack_msg *ack; 00862 uint16_t packet_seqno = packetbuf_attr(PACKETBUF_ATTR_PACKET_ID); 00863 00864 packetbuf_clear(); 00865 packetbuf_set_datalen(sizeof(struct ack_msg)); 00866 ack = packetbuf_dataptr(); 00867 memset(ack, 0, sizeof(struct ack_msg)); 00868 ack->rtmetric = tc->rtmetric; 00869 ack->flags = flags; 00870 00871 packetbuf_set_addr(PACKETBUF_ADDR_RECEIVER, to); 00872 packetbuf_set_attr(PACKETBUF_ATTR_PACKET_TYPE, PACKETBUF_ATTR_PACKET_TYPE_ACK); 00873 packetbuf_set_attr(PACKETBUF_ATTR_RELIABLE, 0); 00874 packetbuf_set_attr(PACKETBUF_ATTR_ERELIABLE, 0); 00875 packetbuf_set_attr(PACKETBUF_ATTR_PACKET_ID, packet_seqno); 00876 packetbuf_set_attr(PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS, MAX_ACK_MAC_REXMITS); 00877 unicast_send(&tc->unicast_conn, to); 00878 00879 PRINTF("%d.%d: collect: Sending ACK to %d.%d for %d (epacket_id %d)\n", 00880 rimeaddr_node_addr.u8[0],rimeaddr_node_addr.u8[1], 00881 to->u8[0], to->u8[1], packet_seqno, 00882 packetbuf_attr(PACKETBUF_ATTR_EPACKET_ID)); 00883 00884 RIMESTATS_ADD(acktx); 00885 stats.acksent++; 00886 } 00887 /*---------------------------------------------------------------------------*/ 00888 static void 00889 add_packet_to_recent_packets(struct collect_conn *tc) 00890 { 00891 /* Remember that we have seen this packet for later, but only if 00892 it has a length that is larger than zero. Packets with size 00893 zero are keepalive or proactive link estimate probes, so we do 00894 not record them in our history. */ 00895 if(packetbuf_datalen() > sizeof(struct data_msg_hdr)) { 00896 recent_packets[recent_packet_ptr].eseqno = 00897 packetbuf_attr(PACKETBUF_ATTR_EPACKET_ID); 00898 rimeaddr_copy(&recent_packets[recent_packet_ptr].originator, 00899 packetbuf_addr(PACKETBUF_ADDR_ESENDER)); 00900 recent_packets[recent_packet_ptr].conn = tc; 00901 recent_packet_ptr = (recent_packet_ptr + 1) % NUM_RECENT_PACKETS; 00902 } 00903 } 00904 /*---------------------------------------------------------------------------*/ 00905 static void 00906 node_packet_received(struct unicast_conn *c, const rimeaddr_t *from) 00907 { 00908 struct collect_conn *tc = (struct collect_conn *) 00909 ((char *)c - offsetof(struct collect_conn, unicast_conn)); 00910 int i; 00911 struct data_msg_hdr hdr; 00912 uint8_t ackflags = 0; 00913 struct collect_neighbor *n; 00914 00915 memcpy(&hdr, packetbuf_dataptr(), sizeof(struct data_msg_hdr)); 00916 00917 /* First update the neighbors rtmetric with the information in the 00918 packet header. */ 00919 PRINTF("node_packet_received: from %d.%d rtmetric %d\n", 00920 from->u8[0], from->u8[1], hdr.rtmetric); 00921 n = collect_neighbor_list_find(&tc->neighbor_list, 00922 packetbuf_addr(PACKETBUF_ADDR_SENDER)); 00923 if(n != NULL) { 00924 collect_neighbor_update_rtmetric(n, hdr.rtmetric); 00925 update_rtmetric(tc); 00926 } 00927 00928 /* To protect against sending duplicate packets, we keep a list of 00929 recently forwarded packet seqnos. If the seqno of the current 00930 packet exists in the list, we immediately send an ACK and drop 00931 the packet. */ 00932 if(packetbuf_attr(PACKETBUF_ATTR_PACKET_TYPE) == 00933 PACKETBUF_ATTR_PACKET_TYPE_DATA) { 00934 rimeaddr_t ack_to; 00935 uint8_t packet_seqno; 00936 00937 stats.datarecv++; 00938 00939 /* Remember to whom we should send the ACK, since we reuse the 00940 packet buffer and its attributes when sending the ACK. */ 00941 rimeaddr_copy(&ack_to, packetbuf_addr(PACKETBUF_ADDR_SENDER)); 00942 packet_seqno = packetbuf_attr(PACKETBUF_ATTR_PACKET_ID); 00943 00944 /* If the queue is more than half filled, we add the CONGESTED 00945 flag to our outgoing acks. */ 00946 if(DRAW_TREE) { 00947 printf("#A s=%d\n", packetqueue_len(&tc->send_queue)); 00948 } 00949 if(packetqueue_len(&tc->send_queue) >= MAX_SENDING_QUEUE / 2) { 00950 ackflags |= ACK_FLAGS_CONGESTED; 00951 } 00952 00953 for(i = 0; i < NUM_RECENT_PACKETS; i++) { 00954 if(recent_packets[i].conn == tc && 00955 recent_packets[i].eseqno == packetbuf_attr(PACKETBUF_ATTR_EPACKET_ID) && 00956 rimeaddr_cmp(&recent_packets[i].originator, 00957 packetbuf_addr(PACKETBUF_ADDR_ESENDER))) { 00958 /* This is a duplicate of a packet we recently received, so we 00959 just send an ACK. */ 00960 PRINTF("%d.%d: found duplicate packet from %d.%d with seqno %d, via %d.%d\n", 00961 rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1], 00962 recent_packets[i].originator.u8[0], recent_packets[i].originator.u8[1], 00963 packetbuf_attr(PACKETBUF_ATTR_EPACKET_ID), 00964 packetbuf_addr(PACKETBUF_ADDR_SENDER)->u8[0], 00965 packetbuf_addr(PACKETBUF_ADDR_SENDER)->u8[1]); 00966 send_ack(tc, &ack_to, ackflags); 00967 stats.duprecv++; 00968 return; 00969 } 00970 } 00971 00972 /* If we are the sink, the packet has reached its final 00973 destination and we call the receive function. */ 00974 if(tc->rtmetric == RTMETRIC_SINK) { 00975 struct queuebuf *q; 00976 00977 add_packet_to_recent_packets(tc); 00978 00979 /* We first send the ACK. We copy the data packet to a queuebuf 00980 first. */ 00981 q = queuebuf_new_from_packetbuf(); 00982 if(q != NULL) { 00983 send_ack(tc, &ack_to, 0); 00984 queuebuf_to_packetbuf(q); 00985 queuebuf_free(q); 00986 } else { 00987 PRINTF("%d.%d: collect: could not send ACK to %d.%d for %d: no queued buffers\n", 00988 rimeaddr_node_addr.u8[0],rimeaddr_node_addr.u8[1], 00989 ack_to.u8[0], ack_to.u8[1], 00990 packet_seqno); 00991 stats.ackdrop++; 00992 } 00993 00994 00995 PRINTF("%d.%d: sink received packet %d from %d.%d via %d.%d\n", 00996 rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1], 00997 packetbuf_attr(PACKETBUF_ATTR_EPACKET_ID), 00998 packetbuf_addr(PACKETBUF_ADDR_ESENDER)->u8[0], 00999 packetbuf_addr(PACKETBUF_ADDR_ESENDER)->u8[1], 01000 from->u8[0], from->u8[1]); 01001 01002 packetbuf_hdrreduce(sizeof(struct data_msg_hdr)); 01003 /* Call receive function. */ 01004 if(packetbuf_datalen() > 0 && tc->cb->recv != NULL) { 01005 tc->cb->recv(packetbuf_addr(PACKETBUF_ADDR_ESENDER), 01006 packetbuf_attr(PACKETBUF_ATTR_EPACKET_ID), 01007 packetbuf_attr(PACKETBUF_ATTR_HOPS)); 01008 } 01009 return; 01010 } else if(packetbuf_attr(PACKETBUF_ATTR_TTL) > 1 && 01011 tc->rtmetric != RTMETRIC_MAX) { 01012 /* If we are not the sink, we forward the packet to our best 01013 neighbor. First, we make sure that the packet comes from a 01014 neighbor that has a higher rtmetric than we have. If not, we 01015 have a loop and we inform the sender that its rtmetric needs 01016 to be updated. Second, we set our rtmetric in the outgoing 01017 packet to let the next hop know what our rtmetric is. Third, 01018 we update the hop count and ttl. */ 01019 01020 if(hdr.rtmetric <= tc->rtmetric) { 01021 ackflags |= ACK_FLAGS_RTMETRIC_NEEDS_UPDATE; 01022 } 01023 01024 packetbuf_set_attr(PACKETBUF_ATTR_HOPS, 01025 packetbuf_attr(PACKETBUF_ATTR_HOPS) + 1); 01026 packetbuf_set_attr(PACKETBUF_ATTR_TTL, 01027 packetbuf_attr(PACKETBUF_ATTR_TTL) - 1); 01028 01029 01030 PRINTF("%d.%d: packet received from %d.%d via %d.%d, sending %d, max_rexmits %d\n", 01031 rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1], 01032 packetbuf_addr(PACKETBUF_ADDR_ESENDER)->u8[0], 01033 packetbuf_addr(PACKETBUF_ADDR_ESENDER)->u8[1], 01034 from->u8[0], from->u8[1], tc->sending, 01035 packetbuf_attr(PACKETBUF_ATTR_MAX_REXMIT)); 01036 01037 /* We try to enqueue the packet on the outgoing packet queue. If 01038 we are able to enqueue the packet, we send a positive ACK. If 01039 we are unable to enqueue the packet, we send a negative ACK 01040 to inform the sender that the packet was dropped due to 01041 memory problems. We first check the size of our sending queue 01042 to ensure that we always have entries for packets that 01043 are originated by this node. */ 01044 if(packetqueue_len(&tc->send_queue) <= MAX_SENDING_QUEUE - MIN_AVAILABLE_QUEUE_ENTRIES && 01045 packetqueue_enqueue_packetbuf(&tc->send_queue, 01046 FORWARD_PACKET_LIFETIME_BASE * 01047 packetbuf_attr(PACKETBUF_ATTR_MAX_REXMIT), 01048 tc)) { 01049 add_packet_to_recent_packets(tc); 01050 send_ack(tc, &ack_to, ackflags); 01051 send_queued_packet(tc); 01052 } else { 01053 send_ack(tc, &ack_to, 01054 ackflags | ACK_FLAGS_DROPPED | ACK_FLAGS_CONGESTED); 01055 PRINTF("%d.%d: packet dropped: no queue buffer available\n", 01056 rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1]); 01057 stats.qdrop++; 01058 } 01059 } else if(packetbuf_attr(PACKETBUF_ATTR_TTL) <= 1) { 01060 PRINTF("%d.%d: packet dropped: ttl %d\n", 01061 rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1], 01062 packetbuf_attr(PACKETBUF_ATTR_TTL)); 01063 send_ack(tc, &ack_to, ackflags | 01064 ACK_FLAGS_DROPPED | ACK_FLAGS_LIFETIME_EXCEEDED); 01065 stats.ttldrop++; 01066 } 01067 } else if(packetbuf_attr(PACKETBUF_ATTR_PACKET_TYPE) == 01068 PACKETBUF_ATTR_PACKET_TYPE_ACK) { 01069 PRINTF("Collect: incoming ack %d from %d.%d (%d.%d) seqno %d (%d)\n", 01070 packetbuf_attr(PACKETBUF_ATTR_PACKET_TYPE), 01071 packetbuf_addr(PACKETBUF_ADDR_SENDER)->u8[0], 01072 packetbuf_addr(PACKETBUF_ADDR_SENDER)->u8[1], 01073 tc->current_parent.u8[0], 01074 tc->current_parent.u8[1], 01075 packetbuf_attr(PACKETBUF_ATTR_PACKET_ID), 01076 tc->seqno); 01077 handle_ack(tc); 01078 stats.ackrecv++; 01079 } 01080 return; 01081 } 01082 /*---------------------------------------------------------------------------*/ 01083 static void 01084 timedout(struct collect_conn *tc) 01085 { 01086 struct collect_neighbor *n; 01087 PRINTF("%d.%d: timedout after %d retransmissions to %d.%d (max retransmissions %d): packet dropped\n", 01088 rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1], tc->transmissions, 01089 tc->current_parent.u8[0], tc->current_parent.u8[1], 01090 tc->max_rexmits); 01091 printf("%d.%d: timedout after %d retransmissions to %d.%d (max retransmissions %d): packet dropped\n", 01092 rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1], tc->transmissions, 01093 tc->current_parent.u8[0], tc->current_parent.u8[1], 01094 tc->max_rexmits); 01095 01096 tc->sending = 0; 01097 n = collect_neighbor_list_find(&tc->neighbor_list, 01098 &tc->current_parent); 01099 if(n != NULL) { 01100 collect_neighbor_tx_fail(n, tc->max_rexmits); 01101 } 01102 update_rtmetric(tc); 01103 send_next_packet(tc); 01104 set_keepalive_timer(tc); 01105 } 01106 /*---------------------------------------------------------------------------*/ 01107 static void 01108 node_packet_sent(struct unicast_conn *c, int status, int transmissions) 01109 { 01110 struct collect_conn *tc = (struct collect_conn *) 01111 ((char *)c - offsetof(struct collect_conn, unicast_conn)); 01112 01113 /* For data packets, we record the number of transmissions */ 01114 if(packetbuf_attr(PACKETBUF_ATTR_PACKET_TYPE) == 01115 PACKETBUF_ATTR_PACKET_TYPE_DATA) { 01116 01117 tc->transmissions += transmissions; 01118 PRINTF("tx %d\n", tc->transmissions); 01119 PRINTF("%d.%d: MAC sent %d transmissions to %d.%d, status %d, total transmissions %d\n", 01120 rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1], 01121 transmissions, 01122 tc->current_parent.u8[0], tc->current_parent.u8[1], 01123 status, tc->transmissions); 01124 if(tc->transmissions >= tc->max_rexmits) { 01125 timedout(tc); 01126 stats.timedout++; 01127 } else { 01128 clock_time_t time = REXMIT_TIME / 2 + (random_rand() % (REXMIT_TIME / 2)); 01129 PRINTF("retransmission time %lu\n", time); 01130 ctimer_set(&tc->retransmission_timer, time, 01131 retransmit_callback, tc); 01132 } 01133 } 01134 } 01135 /*---------------------------------------------------------------------------*/ 01136 /** 01137 * This function is called from a ctimer that is setup when a packet 01138 * is first transmitted. If the MAC layer signals that the packet is 01139 * sent, the ctimer will be stopped before this function is called. If 01140 * this function ends up being called, we add the maximum number of 01141 * MAC layer transmissions to the transmission count, and call the 01142 * retransmit function. 01143 */ 01144 static void 01145 retransmit_not_sent_callback(void *ptr) 01146 { 01147 struct collect_conn *c = ptr; 01148 01149 PRINTF("retransmit not sent, %d transmissions\n", c->transmissions); 01150 c->transmissions += MAX_MAC_REXMITS + 1; 01151 retransmit_callback(c); 01152 } 01153 /*---------------------------------------------------------------------------*/ 01154 /** 01155 * This function is called from a ctimer that is setup when a packet 01156 * is sent. The purpose of this function is to either retransmit the 01157 * current packet, or timeout the packet. The descision is made 01158 * depending on how many times the packet has been transmitted. The 01159 * ctimer is set up in the function node_packet_sent(). 01160 */ 01161 static void 01162 retransmit_callback(void *ptr) 01163 { 01164 struct collect_conn *c = ptr; 01165 01166 PRINTF("retransmit, %d transmissions\n", c->transmissions); 01167 if(c->transmissions >= c->max_rexmits) { 01168 timedout(c); 01169 stats.timedout++; 01170 } else { 01171 c->sending = 0; 01172 retransmit_current_packet(c); 01173 } 01174 } 01175 /*---------------------------------------------------------------------------*/ 01176 #if !COLLECT_ANNOUNCEMENTS 01177 static void 01178 adv_received(struct neighbor_discovery_conn *c, const rimeaddr_t *from, 01179 uint16_t rtmetric) 01180 { 01181 struct collect_conn *tc = (struct collect_conn *) 01182 ((char *)c - offsetof(struct collect_conn, neighbor_discovery_conn)); 01183 struct collect_neighbor *n; 01184 01185 n = collect_neighbor_list_find(&tc->neighbor_list, from); 01186 01187 if(n == NULL) { 01188 collect_neighbor_list_add(&tc->neighbor_list, from, rtmetric); 01189 if(rtmetric == RTMETRIC_MAX) { 01190 bump_advertisement(tc); 01191 } 01192 } else { 01193 /* Check if the advertised rtmetric has changed to 01194 RTMETRIC_MAX. This may indicate that the neighbor has lost its 01195 routes or that it has rebooted. In either case, we bump our 01196 advertisement rate to allow our neighbor to receive a new 01197 rtmetric from us. If our neighbor already happens to have an 01198 rtmetric of RTMETRIC_MAX recorded, it may mean that our 01199 neighbor does not hear our advertisements. If this is the case, 01200 we should not bump our advertisement rate. */ 01201 if(rtmetric == RTMETRIC_MAX && 01202 collect_neighbor_rtmetric(n) != RTMETRIC_MAX) { 01203 bump_advertisement(tc); 01204 } 01205 collect_neighbor_update_rtmetric(n, rtmetric); 01206 PRINTF("%d.%d: updating neighbor %d.%d, etx %d\n", 01207 rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1], 01208 n->addr.u8[0], n->addr.u8[1], rtmetric); 01209 } 01210 01211 update_rtmetric(tc); 01212 } 01213 #else 01214 static void 01215 received_announcement(struct announcement *a, const rimeaddr_t *from, 01216 uint16_t id, uint16_t value) 01217 { 01218 struct collect_conn *tc = (struct collect_conn *) 01219 ((char *)a - offsetof(struct collect_conn, announcement)); 01220 struct collect_neighbor *n; 01221 01222 n = collect_neighbor_list_find(&tc->neighbor_list, from); 01223 01224 if(n == NULL) { 01225 /* Only add neighbors that have an rtmetric that is lower than 01226 ours. */ 01227 if(value < tc->rtmetric) { 01228 collect_neighbor_list_add(&tc->neighbor_list, from, value); 01229 PRINTF("%d.%d: new neighbor %d.%d, rtmetric %d\n", 01230 rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1], 01231 from->u8[0], from->u8[1], value); 01232 } 01233 if(value == RTMETRIC_MAX && tc->rtmetric != RTMETRIC_MAX) { 01234 bump_advertisement(tc); 01235 } 01236 } else { 01237 /* Check if the advertised rtmetric has changed to 01238 RTMETRIC_MAX. This may indicate that the neighbor has lost its 01239 routes or that it has rebooted. In either case, we bump our 01240 advertisement rate to allow our neighbor to receive a new 01241 rtmetric from us. If our neighbor already happens to have an 01242 rtmetric of RTMETRIC_MAX recorded, it may mean that our 01243 neighbor does not hear our advertisements. If this is the case, 01244 we should not bump our advertisement rate. */ 01245 if(value == RTMETRIC_MAX && 01246 collect_neighbor_rtmetric(n) != RTMETRIC_MAX) { 01247 bump_advertisement(tc); 01248 } 01249 collect_neighbor_update_rtmetric(n, value); 01250 PRINTF("%d.%d: updating neighbor %d.%d, etx %d\n", 01251 rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1], 01252 n->addr.u8[0], n->addr.u8[1], value); 01253 } 01254 01255 update_rtmetric(tc); 01256 01257 #if ! COLLECT_CONF_WITH_LISTEN 01258 if(value == RTMETRIC_MAX && 01259 tc->rtmetric != RTMETRIC_MAX) { 01260 announcement_bump(&tc->announcement); 01261 } 01262 #endif /* COLLECT_CONF_WITH_LISTEN */ 01263 } 01264 #endif /* !COLLECT_ANNOUNCEMENTS */ 01265 /*---------------------------------------------------------------------------*/ 01266 static const struct unicast_callbacks unicast_callbacks = {node_packet_received, 01267 node_packet_sent}; 01268 #if !COLLECT_ANNOUNCEMENTS 01269 static const struct neighbor_discovery_callbacks neighbor_discovery_callbacks = 01270 { adv_received, NULL}; 01271 #endif /* !COLLECT_ANNOUNCEMENTS */ 01272 /*---------------------------------------------------------------------------*/ 01273 void 01274 collect_open(struct collect_conn *tc, uint16_t channels, 01275 uint8_t is_router, 01276 const struct collect_callbacks *cb) 01277 { 01278 unicast_open(&tc->unicast_conn, channels + 1, &unicast_callbacks); 01279 channel_set_attributes(channels + 1, attributes); 01280 tc->rtmetric = RTMETRIC_MAX; 01281 tc->cb = cb; 01282 tc->is_router = is_router; 01283 tc->seqno = 10; 01284 tc->eseqno = 0; 01285 LIST_STRUCT_INIT(tc, send_queue_list); 01286 collect_neighbor_list_new(&tc->neighbor_list); 01287 tc->send_queue.list = &(tc->send_queue_list); 01288 tc->send_queue.memb = &send_queue_memb; 01289 collect_neighbor_init(); 01290 01291 #if !COLLECT_ANNOUNCEMENTS 01292 neighbor_discovery_open(&tc->neighbor_discovery_conn, channels, 01293 CLOCK_SECOND * 4, 01294 CLOCK_SECOND * 60, 01295 #ifdef COLLECT_CONF_BROADCAST_ANNOUNCEMENT_MAX_TIME 01296 COLLECT_CONF_BROADCAST_ANNOUNCEMENT_MAX_TIME, 01297 #else 01298 CLOCK_SECOND * 600UL, 01299 #endif 01300 &neighbor_discovery_callbacks); 01301 neighbor_discovery_start(&tc->neighbor_discovery_conn, tc->rtmetric); 01302 #else /* !COLLECT_ANNOUNCEMENTS */ 01303 announcement_register(&tc->announcement, channels, 01304 received_announcement); 01305 #if ! COLLECT_CONF_WITH_LISTEN 01306 announcement_set_value(&tc->announcement, RTMETRIC_MAX); 01307 #endif /* COLLECT_CONF_WITH_LISTEN */ 01308 #endif /* !COLLECT_ANNOUNCEMENTS */ 01309 01310 ctimer_set(&tc->proactive_probing_timer, PROACTIVE_PROBING_INTERVAL, 01311 proactive_probing_callback, tc); 01312 01313 } 01314 /*---------------------------------------------------------------------------*/ 01315 static void 01316 send_keepalive(void *ptr) 01317 { 01318 struct collect_conn *c = ptr; 01319 01320 set_keepalive_timer(c); 01321 01322 /* Send keepalive message only if there are no pending transmissions. */ 01323 if(c->sending == 0 && packetqueue_len(&c->send_queue) == 0) { 01324 if(enqueue_dummy_packet(c, KEEPALIVE_REXMITS)) { 01325 PRINTF("%d.%d: sending keepalive\n", 01326 rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1]); 01327 send_queued_packet(c); 01328 } 01329 } 01330 } 01331 /*---------------------------------------------------------------------------*/ 01332 static void 01333 set_keepalive_timer(struct collect_conn *c) 01334 { 01335 if(c->keepalive_period != 0) { 01336 ctimer_set(&c->keepalive_timer, (c->keepalive_period / 2) + 01337 (random_rand() % (c->keepalive_period / 2)), 01338 send_keepalive, c); 01339 } else { 01340 ctimer_stop(&c->keepalive_timer); 01341 } 01342 } 01343 /*---------------------------------------------------------------------------*/ 01344 void 01345 collect_set_keepalive(struct collect_conn *c, clock_time_t period) 01346 { 01347 c->keepalive_period = period; 01348 set_keepalive_timer(c); 01349 } 01350 /*---------------------------------------------------------------------------*/ 01351 void 01352 collect_close(struct collect_conn *tc) 01353 { 01354 #if COLLECT_ANNOUNCEMENTS 01355 announcement_remove(&tc->announcement); 01356 #else 01357 neighbor_discovery_close(&tc->neighbor_discovery_conn); 01358 #endif /* COLLECT_ANNOUNCEMENTS */ 01359 unicast_close(&tc->unicast_conn); 01360 while(packetqueue_first(&tc->send_queue) != NULL) { 01361 packetqueue_dequeue(&tc->send_queue); 01362 } 01363 } 01364 /*---------------------------------------------------------------------------*/ 01365 void 01366 collect_set_sink(struct collect_conn *tc, int should_be_sink) 01367 { 01368 if(should_be_sink) { 01369 tc->is_router = 1; 01370 tc->rtmetric = RTMETRIC_SINK; 01371 PRINTF("collect_set_sink: tc->rtmetric %d\n", tc->rtmetric); 01372 bump_advertisement(tc); 01373 01374 /* Purge the outgoing packet queue. */ 01375 while(packetqueue_len(&tc->send_queue) > 0) { 01376 packetqueue_dequeue(&tc->send_queue); 01377 } 01378 01379 /* Stop the retransmission timer. */ 01380 ctimer_stop(&tc->retransmission_timer); 01381 } else { 01382 tc->rtmetric = RTMETRIC_MAX; 01383 } 01384 #if COLLECT_ANNOUNCEMENTS 01385 announcement_set_value(&tc->announcement, tc->rtmetric); 01386 #endif /* COLLECT_ANNOUNCEMENTS */ 01387 update_rtmetric(tc); 01388 01389 bump_advertisement(tc); 01390 01391 if(DRAW_TREE) { 01392 printf("#A rt=0,p=0\n"); 01393 } 01394 } 01395 /*---------------------------------------------------------------------------*/ 01396 int 01397 collect_send(struct collect_conn *tc, int rexmits) 01398 { 01399 struct collect_neighbor *n; 01400 int ret; 01401 01402 packetbuf_set_attr(PACKETBUF_ATTR_EPACKET_ID, tc->eseqno); 01403 01404 /* Increase the sequence number for the packet we send out. We 01405 employ a trick that allows us to see that a node has been 01406 rebooted: if the sequence number wraps to 0, we set it to half of 01407 the sequence number space. This allows us to detect reboots, 01408 since if a sequence number is less than half of the sequence 01409 number space, the data comes from a node that was recently 01410 rebooted. */ 01411 01412 tc->eseqno = (tc->eseqno + 1) % (1 << COLLECT_PACKET_ID_BITS); 01413 01414 if(tc->eseqno == 0) { 01415 tc->eseqno = ((int)(1 << COLLECT_PACKET_ID_BITS)) / 2; 01416 } 01417 packetbuf_set_addr(PACKETBUF_ADDR_ESENDER, &rimeaddr_node_addr); 01418 packetbuf_set_attr(PACKETBUF_ATTR_HOPS, 1); 01419 packetbuf_set_attr(PACKETBUF_ATTR_TTL, MAX_HOPLIM); 01420 if(rexmits > MAX_REXMITS) { 01421 packetbuf_set_attr(PACKETBUF_ATTR_MAX_REXMIT, MAX_REXMITS); 01422 } else { 01423 packetbuf_set_attr(PACKETBUF_ATTR_MAX_REXMIT, rexmits); 01424 } 01425 01426 PRINTF("%d.%d: originating packet %d, max_rexmits %d\n", 01427 rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1], 01428 packetbuf_attr(PACKETBUF_ATTR_EPACKET_ID), 01429 packetbuf_attr(PACKETBUF_ATTR_MAX_REXMIT)); 01430 01431 if(tc->rtmetric == RTMETRIC_SINK) { 01432 packetbuf_set_attr(PACKETBUF_ATTR_HOPS, 0); 01433 if(tc->cb->recv != NULL) { 01434 tc->cb->recv(packetbuf_addr(PACKETBUF_ADDR_ESENDER), 01435 packetbuf_attr(PACKETBUF_ATTR_EPACKET_ID), 01436 packetbuf_attr(PACKETBUF_ATTR_HOPS)); 01437 } 01438 return 1; 01439 } else { 01440 01441 /* Allocate space for the header. */ 01442 packetbuf_hdralloc(sizeof(struct data_msg_hdr)); 01443 01444 if(packetqueue_enqueue_packetbuf(&tc->send_queue, 01445 FORWARD_PACKET_LIFETIME_BASE * 01446 packetbuf_attr(PACKETBUF_ATTR_MAX_REXMIT), 01447 tc)) { 01448 send_queued_packet(tc); 01449 ret = 1; 01450 } else { 01451 PRINTF("%d.%d: drop originated packet: no queuebuf\n", 01452 rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1]); 01453 printf("%d.%d: drop originated packet: no queuebuf\n", 01454 rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1]); 01455 ret = 0; 01456 } 01457 01458 01459 n = collect_neighbor_list_find(&tc->neighbor_list, &tc->parent); 01460 if(n != NULL) { 01461 PRINTF("%d.%d: sending to %d.%d\n", 01462 rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1], 01463 n->addr.u8[0], n->addr.u8[1]); 01464 } else { 01465 PRINTF("%d.%d: did not find any neighbor to send to\n", 01466 rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1]); 01467 #if COLLECT_ANNOUNCEMENTS 01468 #if COLLECT_CONF_WITH_LISTEN 01469 PRINTF("listen\n"); 01470 announcement_listen(1); 01471 ctimer_set(&tc->transmit_after_scan_timer, ANNOUNCEMENT_SCAN_TIME, 01472 send_queued_packet, tc); 01473 #else /* COLLECT_CONF_WITH_LISTEN */ 01474 announcement_set_value(&tc->announcement, RTMETRIC_MAX); 01475 announcement_bump(&tc->announcement); 01476 #endif /* COLLECT_CONF_WITH_LISTEN */ 01477 #endif /* COLLECT_ANNOUNCEMENTS */ 01478 01479 /* if(packetqueue_enqueue_packetbuf(&tc->send_queue, 01480 FORWARD_PACKET_LIFETIME_BASE * 01481 packetbuf_attr(PACKETBUF_ATTR_MAX_REXMIT), 01482 tc)) { 01483 return 1; 01484 } else { 01485 PRINTF("%d.%d: drop originated packet: no queuebuf\n", 01486 rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1]); 01487 printf("%d.%d: drop originated packet: no queuebuf\n", 01488 rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1]); 01489 }*/ 01490 } 01491 } 01492 return ret; 01493 } 01494 /*---------------------------------------------------------------------------*/ 01495 int 01496 collect_depth(struct collect_conn *tc) 01497 { 01498 return tc->rtmetric; 01499 } 01500 /*---------------------------------------------------------------------------*/ 01501 const rimeaddr_t * 01502 collect_parent(struct collect_conn *tc) 01503 { 01504 return &tc->current_parent; 01505 } 01506 /*---------------------------------------------------------------------------*/ 01507 void 01508 collect_purge(struct collect_conn *tc) 01509 { 01510 collect_neighbor_list_purge(&tc->neighbor_list); 01511 rimeaddr_copy(&tc->parent, &rimeaddr_null); 01512 update_rtmetric(tc); 01513 if(DRAW_TREE) { 01514 printf("#L %d 0\n", tc->parent.u8[0]); 01515 } 01516 rimeaddr_copy(&tc->parent, &rimeaddr_null); 01517 } 01518 /*---------------------------------------------------------------------------*/ 01519 void 01520 collect_print_stats(void) 01521 { 01522 PRINTF("collect stats foundroute %lu newparent %lu routelost %lu acksent %lu datasent %lu datarecv %lu ackrecv %lu badack %lu duprecv %lu qdrop %lu rtdrop %lu ttldrop %lu ackdrop %lu timedout %lu\n", 01523 stats.foundroute, stats.newparent, stats.routelost, 01524 stats.acksent, stats.datasent, stats.datarecv, 01525 stats.ackrecv, stats.badack, stats.duprecv, 01526 stats.qdrop, stats.rtdrop, stats.ttldrop, stats.ackdrop, 01527 stats.timedout); 01528 } 01529 /*---------------------------------------------------------------------------*/ 01530 /** @} */


____________________________________________________________________ HOURLY Average of data over the previous hour (e.g., output at 12Z is average over period from 11Z - 12Z). 1. Surface fluxes [all in W m-2, except precipitation] (a) incident solar (shortwave) radiation (b) reflected solar (shortwave) radiation (c) upward terrestrial (longwave) radiation (d) downward terrestrial (longwave) radiation (e) sensible heat flux (f) deep-soil heat flux (g) latent heat flux (evapotranspiration) (h) precipitation [mm month-1] 2. Top-of-atmosphere fluxes [all in W m-2] (a) net solar (shortwave) radiation (b) upward terrestrial (longwave) radiation 3. Mass (a) surface pressure [Pa] (b) precipitable water [m] 4. Atmospheric vertical profiles on model grid, at selected gridpoints (a) temperature [K] (b) specific humidity [kg kg-1] (c) zonal (east-west) wind [m s-1] (d) meridional (north-south) wind [m s-1] (e) vertical wind [m s-1] (f) geopotential height [m] (g) pressure [Pa] 5. Low-Level Jet (a) meridional wind at 500 m above ground level [m/s] (b) zonal wind at 500 m above ground level [m/s] 6. Mixed-Layer Depth [m] (defined to be the midpoint of the model layer closest to the surface for which the potential temperature change with height, d(theta)/dz, exceeds +1 K/km) DAILY 1. 1.5 meter (screen height) temperature [K] (a) Daily maximum and time of maximum (period: 6UTC - 6 UTC) (b) Daily minimum and time of minimum (period: 6 UTC - 6 UTC) 2. Soil moisture at 12 UTC for each layer of the soil model used 5-DAY AVERAGE Output averaged over successive 5-day periods, yielding 12 periods for the 60-day simulation. Output should be interpolated to the levels 1000 mb, 925 mb, 850 mb, 700 mb, 500 mb, 400 mb, 300 mb and 200 mb. Horizontal wind components should be the true east-west and north-south components. 1. Temperature [K] 2. Specific humidity [kg kg-1] 3. Zonal (east-west) wind [m s-1] 4. Meridional (north-south) wind [m s-1] 5. Geopotential height [m]
Go to the IITAP home page


{% extends "index.html" %} {% block content %}
Folder Select
Administrative
Development
Communications
Program
Search
{% include 'library.html' %} {% include 'search.html' %}
Sub-Folder
Trash Folder
File Name
Type
Last Modified
Trash
Preview
Download
Edit
Close
{% endblock %}


Tech Stack
Personal Skills
Responsibility
Creativity
Teamwork
Leadership
Quick Learning
Experience Transfer Abilities
Ability To Overcome Difficulties
Ability Not To Panic
Adapt To Change
Make Quick And Proper Decisions
Make Others Laugh
Most Proud Of Projects
MY CV
Description:
This responsive site is my first non-educational project. It aims to serve as a brief introduction to my skills, interests and background as well as to provide an overview of my education and professional experience to potential future employers.
Languages used to build the site: HTML5 CSS3
Dragon Slayer Fitness
Description:
In this project users can create their own fitness program with exercises they have chosen and monitor their proper implementation. Users can also record in their own food diary what and when they have eaten, thus keeping a close watch on their calories. The site also has a built-in calculator to count the calories needed per day to achieve the desired effect. Last but not least, site users can keep an eye on the muscular growth of each part of their body determining by themselves time periods of these checks. The administrative part is divided into three parts: a Food Administrator who has the authority to perform CRUD operations on food; an Exercise Administrator who has the authority to perform CRUD operations on exercises and a Head Administrator who can ban users and grant administrative rights to users.
Languages used to build the site: C# HTML CSS
School
Description:
In this responsive static site guests can trace the history of a school, various subjects, how to apply to them, as well as related news and gallery. There is also a page designed for further development of the site leading to student's electronic journal.
Languages used to build the site: HTML5 CSS3
Taxi
Description:
In this site guests can search for taxi companies and their price lists in countries and cities that they have chosen. In contrast to them, users can like a company and add it to favorites, thus facilitating their next search. The administrative part consists of CRUD operations concerning taxi companies, countries and cities.
Languages used to build the site: JavaScript HTML CSS
Rent Cars
Description:
In this project, guests can view cars available for rent. Users can rent them for a different period of time and keep an eye on the cars they have rented. The Administrator can perform CRUD operations on both the cars and the rental itself.
Languages used to build the site: JavaScript TypeScript HTML CSS
Languages
Bulgarian (native)
Understanding
Speaking
Writing
English
Understanding
Speaking
Writing
Passions
Coding
Since I have started learning programming, I aim to gain further knowledge. In the rapidly changing sphere of programming, I am always motivated to learn more and more.
Swimming
I have been a swimmer for as long as I can remember and competing since the age of six. My first significant accomplishment was when I finished 11th out of 100 competitors. I cherish that moment to this day but my best positions were soon to follow. This has always pushed me to set higher goals and showed me the importance of determination and hard work.
Chess
Chess is the other sport which has fascinated me. I started playing chess slightly after my debut in swimming. My respect for strategy, planning and thinking in advance helped me stand my ground against competitive level opponents. The interesting fact is that in my first chess tournament I also finished 11th.
Workout
In my opinion, a good workout early in the morning is the perfect way to start a day. Contrary to expectations, physical training helps me stay energized and motivated through the entire day.
Gaming
My initial interest in computing in general was gaming and the quest to find out how computer games were programmed got me excited about programming.
Something More
A Day In My Life
Legend:
ASleeping
BWorking out
CStudying
DRelaxing
ESpending time with friends
FPlaying games
Favourite Movies:
The Lord Of The Rings Trilogy
Mulan
Scent Of A Woman
Operation "Shmenti Kapeli"
Favourite Games:
Diablo II
Heroes Of Might And Magic IV
The Lord Of The Rings The Battle For Middle Earth II
Age Of Empires II


FILM TITLE: SECRETS & LIES DIRECTOR: MIKE LEIGH COUNTRY: ENGLAND 1996 CINEMATOGRAPHY:DICK POPE MUSIC: ANDREW DICKSON CAST: Timothy Spall, Phyllis Logan, Brenda Blethyn, Claire Rushbrook, Marianne Jean-Baptiste SUPER FEATURES:The acting, as is customary in Leigh's films.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
To watch this film, or any of Mike Leigh's films, one really has to appreciate the finer points of acting, in its most developed moments, as is done in the world's best stages. In this sense, Mr. Leigh is the best theatre director in film at work today, if Peter Brook has retired.
Some say, that acting is a method, that is learned through a few clever movements, and actions. Some say that acting is a living truth of some sort that brings about an internal response, which the actor can not avoid in any way, and acts with. Mr. Leigh's films, are about the best that one can get out of the acting, in its furthest point. The result is rather unusual for film standards, in that it is not exactly a "showy" exercise, or a "fun" thing, specially when the moments get serious and troublesome for each character. Taking a slight clue from film makers like Jean Luc Godard and Francois Truffaut, this director keeps the camera on the actors longer, thus avoiding the standard crossover conversational shots, and the results are one of two styles... we either get bored because we have to live through someone's catharsis, or we just get tremendously excited, because we can really feel the whole thing, and the director did not quit on the story, or actor, thus making the film come off much less as entertainment, and a lot more as a serious exercise in some form of internal psychoanalysis. The latter is not a favorite American style at all, where many audiences thought the film "boring", and "slow'.
Life is not always a Hollywood movie on steroids and madness, or a Disney fantasy, trying to help us escape our very own dreary lives, something that Hollywood has been guilty of ever since its beginning. In Europe, film is not always entertainment, and its directions tend to be towards the "literary" side of things, or to the "artistic" sides of things, which is always over looked in America due to its top ten money making film mentality.
But few films can even stand up to the incredible ensemble acting work that this film provides, that would make the best theatre people of the 20th century so proud that their craft has developed so beautifully.... the material may not exactly be Ibsen, or Shaw, or Albee, or Williams, but the results are no less different and strong. If you had been through an evening of such strong acting, you would have given the actors a standing ovation.
This is the story of a young woman that goes out to meet her birth mother. She had been adopted when she was young, and her adoptive parents chose not to hide anything and let her know that she was adopted, and where she was acquired from. And when her parents finally die, her journey starts. She goes after the company that took care of her adoption. The counselor ( the weakest of all the actors in this film ), is a bit of the 60's mentality in hipness, but not in content. Like all doctors she also lives in her own world, and do not really believe people can handle their own affairs. But Hortense is stronger than that, and she continues on her search.
It leads her to a rather dysfunctional family, who happens to have kept it all a secret in regards to this particular adoption, that even the mother does not remember, thus suggesting that she might have had other children also given away. Cynthia, in her earlier days, seems to have had numerous affairs, and enjoyed herself. But as she graces into her fifties, her ability to deal with the past is catching up with her. When she finally agrees to meet the young woman, she is convinced that she does not have another child. She finally remembers, that when she was fifteen, she was forced to put a child she had up for adoption, and that the father was a young American black boy, she vaguely remembers. However, this is only the beginning of the story. Here her family plays a major role. Her second daughter is pregnant and about to get married. And she has a problem with her mother, which seems to be stemming from some dishonest feelings. The mother's brother has helped all his life, and has helped keep all the indiscretions hidden from her family. But when Hortense shows up, the whole thing gets rather complicated, specially towards the end, when the younger sister realizes she has another sister, and that she is black, which is a major shock.
If anything, one would have loved to see this film as a part of the "living theatre" scene, since it's story and material could easily become endless. Film tends to tie up things cleverly a la Moliere, just so we do not have questions. And although this film answers the main questions about its story, it raises others, and this is so true of life and its daily moments... call this cinema verite, rather than just another entertainment story.
What really sets apart this film, is without a doubt, Brenda Blethyn, as Cynthia, the mother who has had a daughter adopted, but has never really dealt with her feelings in he matter. She is un-employed and a "victim" of sorts of the system, in that she is un-educated, and pretty much out of the realm of possibilities in life to learn a whole lot more. But while she is in denial, her maternal instincts take over, and she befriends Hortense, which adds a nice touch to the whole thing... the problem is how to break years and years of lies to the rest of her family. Hortense has always been a part of the truthful side of her life and feelings... the others have not.
Few films stand out for their knicks and knacks. If one is mostly interested in catching adventure stories and fantasies, please avoid this film, so you don't have to say anything not nice. If one appreciates the further development of the acting styles, as an art form, few films will ever stand up to this one. And fewer directors still will ever allow such great freedoms to let the characters be who they are.
Quite worth seeing. Very well designed, even from a conceptual stand point. The camera is mostly on hand, and staying with each character... and even though it can be "pressing" it never "abuses" the character at all. It allows for the character to have some space, something that even the likes of Godard and Truffaut did not do. by comparison, they were very mechanical in form, style and substance, if not eccentric.
4.5 GIBLOONS Copyright (c) Pedro Sena 1997. All Rights Reserved.
The review above was posted to the
rec.arts.movies.reviews newsgroup (de.rec.film.kritiken for German reviews).
The Internet Movie Database accepts no responsibility for the contents of the
review and has no editorial control. Unless stated otherwise, the copyright
belongs to the author.
Please direct comments/criticisms of the review to relevant newsgroups.
Broken URLs inthe reviews are the responsibility of the author.
The formatting of the review is likely to differ from the original due
to ASCII to HTML conversion.
Related links: index of all rec.arts.movies.reviews reviews


This operation cannot be undone.
Device Identifier plugin allows you to remember the devices of your users for Multi-factor authentication.


×
Invalid username or password.
×
You have been logged out.
Sign In
User Name
Password
Remember Me
Sign In
Sign Up
Sign Up


Class Index
Built-In Namespace
_global_
Class
Codec
The Codec is used by Resources that are shared on top of the Data Channel, like file sharing and Textual Chat, to decode and code the data in a consistent way by all the peers.
Class
Conversation
Class
DataBroker
The DataBroker Class handles all the operations to choose the right codecs and the channels to send, receive and handle messages related to the Data.
Class
DataMessage
Class
Identity
The Identity represents a user and contains all information needed to support Conversation services including the service endpoint to retrieve the protocol stack (Messaging Stub) that will be used to establish a signalling channel with the Identity domain messaging server.
Class
Idp
Class
Message
Message - This class is a data-holder for all messages that are sent between the domains.
Class
MessageFactory
This class creates WONDER-compliant messages.
Class
MessagingStub
The MessagingStub implements the the protocol Stack used to communicate with a certain Messaging server.
Class
Participant
The Participant class handles all operations needed to manage the participation of an Identity (User) in a conversation including the WebRTC PeerConnection functionalities.
Class


SweetFreeStuff.com
///////////////////////////////////////////
6 Free Donuts at Krispy Kreme
Posted: 07 Dec 2015 10:34 AM PST
http://feedproxy.google.com/&edburner&utm_medium=email
Download the Krispy Kreme app for iOS or Android and join the rewards
program. After a while you will receive a notification with the coupon!
The post 6 Free Donuts at Krispy Kreme appeared first on SweetFreeStuff.com.
///////////////////////////////////////////
Free Sponsored By the Sun T-shirt and Stickers
Posted: 07 Dec 2015 05:43 AM PST
http://feedproxy.google.com/&edburner&utm_medium=email
Click on Get Sponsored Now! and fill out the form. Free Sponsored By the
Sun T-shirt and Stickers
The post Free Sponsored By the Sun T-shirt and Stickers appeared first on
SweetFreeStuff.com.
///////////////////////////////////////////
Free Smokin Barrel Peanuts or Trail Mix at Stripes Stores
Posted: 06 Dec 2015 11:23 PM PST
http://feedproxy.google.com/&edburner&utm_medium=email
Free Smokin Barrel Peanuts or Trail Mix at Stripes Stores. Expires 7/8.
They are located in Texas, Oklahoma and New Mexico.
The post Free Smokin Barrel Peanuts or Trail Mix at Stripes Stores appeared
first on SweetFreeStuff.com.


Tutorial:
Press any key from the first two rows of letters on the keyboard to play a note
Press 1, 2, 3, 4 to change the waveform
Press arrow UP and DOWN to change the octave
Drums: Z = kick, X = snare, C = hihat
[You should visit this page using Google Chrome]
INSTRUCTIONS


Bulma-Social v2.1.0
Social Buttons and Colors for Bulma
Documentation
Everything you need to use Bulma-Social
You only need 1 CSS file to use Bulma-Social
Import the social providers you really need
Compile your prefered collection of social providers in a single CSS file


Please Sign In
Email
Password
Sign in
New here?
Sign Up
Error
Success!
Indicates a successful or positive action.
Info!
Indicates a neutral informative change or action.


Links:
Kyle Hanquist is an Assistant Professor of Aerospace and Mechanical Engineering (AME) at the University of Arizona, where he is the Director of the Computational Hypersonics and Nonequilibrium Laboratory (CHANL). His research group is active in the development and application of physical models and computational methods for simulation of complex phenomena such as nonequilibrium and reactive flows experienced during hypersonic flight.
Previously to joining the department, Prof. Hanquist was a Postdoctoral Research Fellow and Lecturer in the Department of Aerospace Engineering at the University of Michigan. He has research experiences at NASA Ames Research Center in the area of aerothermodynamics and heat shield design and has teaching interests in the area of fluid mechanics and computational methods. He earned his Ph.D. (2017) and M.S.E. (2015) in Aerospace Engineering from the University of Michigan and a B.S.E. (2012) in Mechanical Engineering from the University of Nebraska.
Dr. Hanquist is a member of the American Institute of Aeronautics and Astronautics (AIAA), the AIAA Thermophysics Technical Committee, and the American Physical Society (APS).


Innovate UK approved this spend profile on
The spend profile has been approved.
The spend profile is not generated.
The spend profile has been rejected.
This overview details the spend profile of each partner.
You can download each spend profile to enable sharing with the monitoring officer.
This overview details the spend profile.
You can download the spend profile to enable sharing with the monitoring officer.
Innovation Lead
Innovation Lead
The spend profile must be sent to Innovate UK for their review and approval.


[ChID:33]
[ScID:120]
*** NOT SO IMPORTANT ***
And now for something completely different.
[/ScID]
[/ChID]
[ChID:34]
[/ChID]
[ChID:1]
[ScID:1]
Hal Spacejock was sitting at the Black Gull's flight console, his attention riveted to a small chessboard balanced amongst the toggle switches, flashing lights and status displays. a few weeks earlier he'd read an article extolling the benefits of the ancient game: how playing it would sharpen his mind, improve his memory and increase his attraction to the opposite sex. Chess had been an important part of his daily routine ever since, but after two hundred and seventy-six losses in a row Hal was beginning to doubt the article's claims. He didn't feel any smarter and he couldn't remember the last time he'd spoken to a member of the opposite sex, let alone had one attracted to him.  Underpowered and outdated, it was still more than capable of running the ship's accounts, navigation and life support systems while beating humans at simple board games. However, since Hal was the only human aboard the Black Gull, his choice of opponents was limited.
"Your turn," said the Navcom, in a neutral female voice.
"I'm thinking."
"While you're planning your opening move, can I tell you about a special offer?"
"What kind of offer?" asked Hal suspiciously.
"Planet books have a chess title on sale."
"Really? Put it on main."
The wide viewscreen above the console turned red, and the word 'SaLE' appeared in vibrating yellow text. The letters grew legs and marched off the screen, bringing a wire basket on wheels into view.
"I don't need all this crap," said Hal. "Just show me the deal."
"Almost there," said the Navcom. "Keep watching."
A flock of pigeons burst from the basket, leaving a tumbling cloud of feathers which dropped to the ground and formed the words 'Special Offer'. a gust of wind blew the feathers away, and a book title flashed up on the screen.
"Chess for the intellectually challenged?" said Hal, staring at the cover in disbelief. "Is this some kind of joke?"
"It's part of a popular series," said the Navcom.
"What are the others? Interstellar navigation for nutters? Moon landings for morons?"
"Shall I add those titles to your basket?"
. . .
[/ScID]
[/ChID]
[ChID:2]
[ScID:2]
On planet Forg, a small crowd had gathered outside the local sky hockey stadium. South Forgberg was not a prosperous area - the semi-detached houses were modest and the residents faced a constant struggle to live within their means. It was unusual to see building work or renovations, so the extensive refurbishment to the decrepit old stadium had been a talking point for months.
. . .
[/ScID]
[ScID:3]
Hal was pacing the Black Gull's flight deck, ready to put his fist through the nearest wall.  What do you mean you didn't save his details?"
"I erased the record after you turned the job down."
"So look it up again!"
"Negative, we can't afford the search fees." The Navcom hesitated. "Incidentally, it's your move."
"How can you think of a bloody chess game at a time like this?"
"You're only saying that because you're losing."
. . .
[/ScID]
[/ChID]
[ChID:3]
[ScID:4]
"No sign of Jerling's pilot," said Hal, who was standing in the Black Gull's airlock peering through a scratched, yellowed porthole. He cupped his hands to the plastic and squinted, but it made little difference. "There could be an army out there and I wouldn't know it."
"Why don't you open the door?" asked the Navcom.
"No thanks. Vurdi's robot might be hanging around."
. . .
[/ScID]
[/ChID]
[ChID:4]
[ScID:5]
Hal emerged from the Black Gull's airlock, blinking in the sudden light. On the far side of the landing field the sun was setting behind the Lamira spaceport's administration block, which shimmered in the late afternoon heat. Clustered around the spaceport buildings were the 'A' list - modern, powerful ships fitted with every comfort. Parked close to the amenities, their crews could dine at one of several restaurants, enjoy the heated swimming pool and browse the shopping arcade at their leisure.
Hal's ship was somewhat further down the alphabet, and was therefore sitting in a disused corner of the field about as far from the amenities as the nearest moon. The area around the Black Gull was little more than a graveyard for derelicts, and most of the landing pads nearby were occupied by graffiti-splashed wrecks with jagged gaps in their crumpled hulls. Some of the ships seemed familiar, and when Hal looked closer he realised the rusted hulks were Rigel-class freighters like his own. One or two were actually in better shape.
. . .
[/ScID]
[ScID:6]
Clunk dropped the last chess piece into the small wooden box and looked around the flight deck. Despite his best efforts with the mop, it didn't look particularly clean, but compared to its previous state it was as sterile as a hospital ward. After a moment's hesitation, he sat in the pilot's chair. "Navcom, do you have a business directory?"
"Yes."
"Run a search, please. All details on a company called Incubots."
There was a brief pause. "Owned by Redge Muller. Incubots specialises in robot programming and advanced pilot training."
Clunk looked relieved. "So that's what Mr. Jerling has in store for me. When I questioned him on the subject he was rather evasive."
"Humans tell lies about the most trivial matters."
. . .
[/ScID]
[ScID:7]
Hal left the maintenance vehicle in the spaceport's outer carpark and walked to the admin block. An information kiosk directed him to an elevator, where he pressed the button marked 'Portmaster'.
Hal watched the floor numbers changing as he dropped further and further underground. He'd expected the Portmaster to have a spacious office with a view of the whole landing field, but instead he seemed to have an office in the basement. Below the basement, amended Hal, eying the elevator's control panel. He'd passed that already.
. . .
[/ScID]
[ScID:8]
"Simulation suspended, incoming message."
The cloud cities of Aklam faded from Clunk's vision. "What?"
"Incoming message."
"Are we meant to answer it?"
"It's Mr. Spacejock," said the Navcom.
Clunk sat up straight. "Please open the connection."
"Hey, robot!" called Hal.
"Yes, sir?"
"Call Jerling and get me a loan. I need three hundred in cash for landing fees and fuel."
"I don't think he'll lend you any money," said Clunk dubiously.
. . .
[/ScID]
[/ChID]
[ChID:5]
[ScID:9]
Portmaster Linten studied Hal across the desk, eyes narrowed. "Are you telling me this Jerling character will pay your bill?"
"He has to. I can't deliver his cargo if the Gull is stuck here, can I?"
The commset buzzed and Linten leant forward. "Yes?"
There was a crackling sound. "Help! Fire!" said a voice over the noise.
"Who is this? What are you talking about?"
"The grass is burning," cried the voice. "There's a fire on the landing field!"
"Which pad?"
"Fifty-two," said Hal, smothering a grin. "That's Clunk." He leant towards the commset. "Clunk, is the ship in danger from this, er, fire?"
"Not yet, Mr Spacejock, but it soon will be. Would you like me to move it out of the way?"
"You keep your hands off the controls. I'll be there in a tick."
"You'll have to hurry, the fire's right up to the refuelling cluster. If that explodes--"
. . .
[/ScID]
[ScID:10]
"Deploy fire hose!" shouted Clunk.
"Deploying." There was a whining sound outside the hull, which stopped with a sharp crack.
"What was that?"
"The reel just fell off," said the Navcom.
. . .
[/ScID]
[/ChID]
[ChID:6]
[ScID:58 (Unused)]
This bird is not dead ...

[/ScID]
[/ChID (Unused)]


evol module
Evol
is clear dsl for composable evolutionary algorithms that optimised for joy.
Evol is a library that helps you make evolutionary algorithms. The goal is to have a library that is fun and clear to use, but not fast.
If you're looking at the library for the first time we recommend that you first take a look at the examples in the /examples folder on github. It is usually a better starting point to get started.
Any details can be discovered on the docs. We hope that this library makes it fun to write heuristics again.
The Gist
The main idea is that you should be able to define a complex algorithm in a composable way. To explain what we mean by this: let's consider two evolutionary algorithms for travelling salesman problems.
The first approach takes a collections of solutions and applies:
- a survival where only the top 50% solutions survive
- the population reproduces using a crossover of genes
- certain members mutate
- repeat this, maybe 1000 times or more!
We can also think of another approach:
- pick the best solution of the population
- make random changes to this parent and generate new solutions
- repeat this, maybe 1000 times or more!
One could even combine the two algorithms into a new one:
- run algorithm 1 50 times
- run algorithm 2 10 times
- repeat this, maybe 1000 times or more!
You might notice that many parts of these algorithms are similar and it is the goal of this library is to automate these parts. In fact, you can expect the code for these algorithms to look something like this.
A speudo-example of what is decribed about looks a bit like this:
import random from evol import Population, Evolution population = Population(init_func=init_func, eval_func=eval_func, size=100) def pick_n_parents(population, num_parents): return [random.choice(population) for i in range(num_parents)] def crossover(*parents): ... def random_copy(parent): ... evo1 = (Evolution(name="first_algorithm") .survive(fraction=0.5) .breed(parentpicker=pick_n_parents, combiner=combiner, num_parents=2, n_max=100) .mutate(lambda x: add_noise(x, 0.1))) evo2 = (Evolution(name="second_algorithm") .survive(n=1) .breed(parentpicker=pick_n_parents, combiner=random_copy, num_parents=1, n_max=100)) for i in range(1001): population.evolve(evo1, n=50).evolve(evo2, n=10)
Getting Started
The best place to get started is the /examples
folder on github.
This folder contains self contained examples that work out of the
box.
Contributing Guide
Local development
Python can help you. Don't reinstall all the time, rather use a virtulenv that has a link to the code.
python setup.py develop
When you submit a pull request it will be tested in travis. Once the build is green the review can start. Please try to keep your branch up to date to ensure you're not missing any tests. Larger commits need to be discussed in github before we can accept them.
Generating New Documentation
Updating documentation is currently a manual step. From the docs
folder:
pdoc --html --overwrite evol cp -rf evol/* . rm -rf evol
If you want to confirm that it works you can open the index.html
file.
""" ![Imgur](https://i.imgur.com/7MHcIq1.png) `Evol` is clear dsl for composable evolutionary algorithms that optimised for joy. Evol is a library that helps you make evolutionary algorithms. The goal is to have a library that is fun and clear to use, but not fast. If you're looking at the library for the first time we recommend that you first take a look at the examples in the /examples folder on github. It is usually a better starting point to get started. Any details can be discovered on the docs. We hope that this library makes it fun to write heuristics again. The Gist --------------------------------------- The main idea is that you should be able to define a complex algorithm in a composable way. To explain what we mean by this: let's consider two evolutionary algorithms for travelling salesman problems. The first approach takes a collections of solutions and applies: 1. a survival where only the top 50% solutions survive 2. the population reproduces using a crossover of genes 3. certain members mutate 4. repeat this, maybe 1000 times or more!  We can also think of another approach: 1. pick the best solution of the population 2. make random changes to this parent and generate new solutions 3. repeat this, maybe 1000 times or more!  One could even combine the two algorithms into a new one: 1. run algorithm 1 50 times 2. run algorithm 2 10 times 3. repeat this, maybe 1000 times or more!  You might notice that many parts of these algorithms are similar and it is the goal of this library is to automate these parts. In fact, you can expect the code for these algorithms to look something like this. A speudo-example of what is decribed about looks a bit like this: import random from evol import Population, Evolution population = Population(init_func=init_func, eval_func=eval_func, size=100) def pick_n_parents(population, num_parents): return [random.choice(population) for i in range(num_parents)] def crossover(*parents): ... def random_copy(parent): ... evo1 = (Evolution(name="first_algorithm") .survive(fraction=0.5) .breed(parentpicker=pick_n_parents, combiner=combiner, num_parents=2, n_max=100) .mutate(lambda x: add_noise(x, 0.1))) evo2 = (Evolution(name="second_algorithm") .survive(n=1) .breed(parentpicker=pick_n_parents, combiner=random_copy, num_parents=1, n_max=100)) for i in range(1001): population.evolve(evo1, n=50).evolve(evo2, n=10) Getting Started --------------------------------------- The best place to get started is the `/examples` folder on github. This folder contains self contained examples that work out of the box. Contributing Guide --------------------------------------- ### Local development Python can help you. Don't reinstall all the time, rather use a virtulenv that has a link to the code. python setup.py develop When you submit a pull request it will be tested in travis. Once the build is green the review can start. Please try to keep your branch up to date to ensure you're not missing any tests. Larger commits need to be discussed in github before we can accept them. ### Generating New Documentation Updating documentation is currently a manual step. From the `docs` folder: pdoc --html --overwrite evol cp -rf evol/* . rm -rf evol If you want to confirm that it works you can open the `index.html` file. """ from .individual import Individual from .evolution import Evolution from .population import Population, ContestPopulation from .logger import BaseLogger
Sub-modules
Evolution objects in evol
are objects that describe how the
evolutionary algorithm will change members of a population.
Evolution objects contain the same methods as population objects
but because an evolution is seperate from a population you can
play around with them more easily.
Helpers in evol
are functions that help you when you are
designing algorithms. We archive these helping functions per usecase.

Internally we work with individuals because that allows us to
seperate the fitness calculation from the datastructure. This
saves a lot of CPU power.
Population objects in evol
are a collection of chromosomes
at some point in an evolutionary algorithm. You can apply
evolutionary steps by directly calling methods on the population
or by appyling an evol.Evolution
object.


---
layout: default
permalink: /promotions/
---
{% assign sorted_promotions = (site.data.promotions.promotions | sort: 'year' | reverse) %}
{% for promotion in sorted_promotions %}
{% assign remainder = promotion.year | modulo:2 %}
{% if remainder == 1 %}
{% assign panel = 'panel-odd-year' %}
{% else %}
{% assign panel = '' %}
{% endif %}
{% for student in promotion.students %}
{% endfor %}
{% t promotions.promotion %} {{ promotion.master }} {{ promotion.year }}
{% for student in promotion.students %}
{{ student.name }} |
| Email |
{{student.email}} |
| {% t promotions.recentjob %} |
{{student.last-known-job}}, {{student.last-known-company}} |
{% endfor %}
{% endfor %}