static PT_THREAD( MNT_check_commands(pt_t* pt) ) { mnt_event_t ev; u8 swap; PT_BEGIN(pt); // as long as there are no command PT_WAIT_UNTIL(pt, OK == FIFO_get(&MNT.cmds_fifo, &MNT.cmd_fr)); // silently ignore incoming response if ( MNT.cmd_fr.resp == 1 ) { DPT_unlock(&MNT.interf); PT_RESTART(pt); } switch (MNT.cmd_fr.cmde) { case FR_TAKE_OFF: // generate take-off event PT_WAIT_UNTIL(pt, (ev = MNT_EV_TAKE_OFF) && OK == FIFO_put(&MNT.ev_fifo, &ev) ); break; case FR_MINUT_TIME_OUT: MNT_open_time(&MNT.cmd_fr); break; case FR_STATE: if ( (MNT.cmd_fr.argv[0] == 0x7a) || (MNT.cmd_fr.argv[0] == 0x8b) ) { //MNT.state = MNT.cmd_fr.argv[1]; } // don't respond, response will be done by CMN PT_RESTART(pt); break; case FR_APPLI_START: MNT.started = 1; // don't respond PT_RESTART(pt); break; default: // shall never happen break; } // build the response to the current command swap = MNT.cmd_fr.orig; MNT.cmd_fr.orig = MNT.cmd_fr.dest; MNT.cmd_fr.dest = swap; MNT.cmd_fr.resp = 1; // enqueue it PT_WAIT_UNTIL(pt, OK == FIFO_put(&MNT.out_fifo, &MNT.cmd_fr)); PT_RESTART(pt); PT_END(pt); }
int DLGSM::PT_send_recv(struct pt *pt, char *ret, char *cmd, int tout) { static uint32_t ts, startts; static struct pt linerecv_pt; static char gotsmtg = 0; PT_BEGIN(pt); *ret = 0; gotsmtg = 0; ts = millis(); GSM_send(cmd); PT_WAIT_UNTIL(pt, _gsmserial.available() || (millis() - ts) > tout); ts = millis(); if (!_gsmserial.available()) { PT_RESTART(pt); } startts = millis(); _gsm_wline = 1; while (_gsmserial.available() || (millis() - startts) < tout) { PT_WAIT_THREAD(pt, PT_recvline(&linerecv_pt, ret, _gsm_buff, _gsm_buffsize, tout, 1)); if (*ret > gotsmtg) gotsmtg = *ret; else if (*ret == 0) _tout_cnt++; ts = millis(); } if (*ret == 0) *ret = gotsmtg; _gsm_wline = 0; PT_END(pt); }
/*---------------------------------------------------------------------------*/ static PT_THREAD(handle_dhcp(void)) { PT_BEGIN(&s.pt); /* try_again:*/ s.state = STATE_SENDING; s.ticks = CLOCK_SECOND; do { send_discover(); s.timer_init = platform_timer_op( ELUA_DHCP_TIMER_ID, PLATFORM_TIMER_OP_START, 0 ); PT_WAIT_UNTIL(&s.pt, uip_newdata() || platform_timer_get_diff_us( ELUA_DHCP_TIMER_ID, s.timer_init, platform_timer_op( ELUA_DHCP_TIMER_ID, PLATFORM_TIMER_OP_READ, 0 ) ) >= s.ticks ); if(uip_newdata() && parse_msg() == DHCPOFFER) { uip_flags &= ~UIP_NEWDATA; s.state = STATE_OFFER_RECEIVED; break; } uip_flags &= ~UIP_NEWDATA; if(s.ticks < CLOCK_SECOND * 60) { s.ticks *= 2; } else { s.ipaddr[0] = 0; goto dhcp_failed; } } while(s.state != STATE_OFFER_RECEIVED); s.ticks = CLOCK_SECOND; do { send_request(); s.timer_init = platform_timer_op( ELUA_DHCP_TIMER_ID, PLATFORM_TIMER_OP_START, 0 ); PT_WAIT_UNTIL(&s.pt, uip_newdata() || platform_timer_get_diff_us( ELUA_DHCP_TIMER_ID, s.timer_init, platform_timer_op( ELUA_DHCP_TIMER_ID, PLATFORM_TIMER_OP_READ, 0 ) ) >= s.ticks ); if(uip_newdata() && parse_msg() == DHCPACK) { uip_flags &= ~UIP_NEWDATA; s.state = STATE_CONFIG_RECEIVED; break; } uip_flags &= ~UIP_NEWDATA; if(s.ticks <= CLOCK_SECOND * 10) { s.ticks += CLOCK_SECOND; } else { PT_RESTART(&s.pt); } } while(s.state != STATE_CONFIG_RECEIVED); dhcp_failed: dhcpc_configured(&s); /* * PT_END restarts the thread so we do this instead. Eventually we * should reacquire expired leases here. */ while(1) { PT_YIELD(&s.pt); } PT_END(&s.pt); }
// check cone changings static PT_THREAD( MNT_check_cone(pt_t* pt) ) { mnt_event_t ev; PT_BEGIN(pt); // if current time is higher than the time-out target time PT_WAIT_UNTIL(pt, TIME_get() > MNT.sampling_rate); // set next sampling period MNT.sampling_rate += SAMPLING_PERIOD; // read cone state u8 cone_state = CONE & _BV(CONE_PIN); // check if the cone state has not changed if ( cone_state == MNT.cone_state ) { PT_RESTART(pt); } // save new cone state MNT.cone_state = cone_state; // else generate the correspondig change event switch (MNT.cone_state) { case CONE_STATE_OPEN: PT_WAIT_UNTIL(pt, (ev = MNT_EV_CONE_OPEN) && OK == FIFO_put(&MNT.ev_fifo, &ev) ); break; case CONE_STATE_CLOSED: PT_WAIT_UNTIL(pt, (ev = MNT_EV_CONE_CLOSED) && OK == FIFO_put(&MNT.ev_fifo, &ev) ); break; default: break; } PT_RESTART(pt); PT_END(pt); }
int DLGSM::PT_GPRS_check_conn_state(struct pt *pt, char *ret) { static struct pt child_pt; static uint8_t k; char iret; char d[15]; PT_BEGIN(pt); get_from_flash(&(gsm_string_table[5]), _gsm_buff); *ret = 0; k = 0; PT_WAIT_THREAD(pt, PT_send_recv_confirm(&child_pt, ret, _gsm_buff, "STATE:", 20000)); if (strcmp_P(_gsm_buff, PSTR("STATE:")) >= 0) { while (k < GPRSS_LEN) { if (strcmp_flash(_gsm_buff+7, &(gprs_state_table[k]), d) == 0) { *ret = k; if (k == GPRSS_IP_INITIAL || k == GPRSS_IP_START || k == GPRSS_IP_CONFIG) { // Reinitialize GPRS PT_WAIT_WHILE(pt, CONN_get_flag(CONN_NETWORK) == 0); PT_WAIT_THREAD(pt, PT_GSM_init(&child_pt, &iret)); PT_WAIT_WHILE(pt, CONN_get_flag(CONN_GPRS_NET) == 0); PT_WAIT_THREAD(pt, PT_GPRS_init(&child_pt, &iret)); } else if (k == GPRSS_IP_GPRSACT) { // Just need to query the local IP... get_from_flash(&(gprs_init_string_table[9]), _gsm_buff); PT_WAIT_THREAD(pt, PT_send_recv(&child_pt, &iret, _gsm_buff, 1000)); } else if (k == GPRSS_CONNECT_OK) { CONN_set_flag(CONN_CONNECTED, 1); } else if (k == GPRSS_TCP_CLOSED || k == GPRSS_UDP_CLOSED) { CONN_set_flag(CONN_CONNECTED, 0); } else if (k == GPRSS_PDP_DEACT) { // Reinitialize GPRS PT_WAIT_WHILE(pt, CONN_get_flag(CONN_NETWORK) == 0); PT_WAIT_THREAD(pt, PT_GSM_init(&child_pt, &iret)); PT_WAIT_WHILE(pt, CONN_get_flag(CONN_GPRS_NET) == 0); PT_WAIT_THREAD(pt, PT_GPRS_init(&child_pt, &iret)); } *ret = k; PT_EXIT(pt); } k++; } } else { PT_RESTART(pt); } *ret = -1; PT_END(pt); }
// check a time-out has elapsed static PT_THREAD( MNT_check_time_out(pt_t* pt) ) { mnt_event_t ev; PT_BEGIN(pt); // if current time is higher than the time-out target time PT_WAIT_UNTIL(pt, TIME_get() > MNT.time_out); // prevent any further time-out MNT.time_out = TIME_MAX; // generate the time-out event PT_WAIT_UNTIL(pt, (ev = MNT_EV_TIME_OUT) && OK == FIFO_put(&MNT.ev_fifo, &ev) ); PT_RESTART(pt); PT_END(pt); }
static PT_THREAD( MNT_send_frame(pt_t* pt) ) { PT_BEGIN(pt); // wait until an outgoing frame is available PT_WAIT_UNTIL(pt, OK == FIFO_get(&MNT.out_fifo, &MNT.out_fr)); // send the frame throught the dispatcher DPT_lock(&MNT.interf); // some retry may be needed PT_WAIT_UNTIL(pt, OK == DPT_tx(&MNT.interf, &MNT.out_fr)); // release the dispatcher DPT_unlock(&MNT.interf); // loop back for the next frame to send PT_RESTART(pt); PT_END(pt); }
/*---------------------------------------------------------------------------*/ static PT_THREAD(handle_dhcp(void)) { PT_BEGIN(&s.pt); /* try_again:*/ s.state = STATE_SENDING; s.ticks = CLOCK_SECOND; do { send_discover(); timer_set(&s.timer, s.ticks); PT_YIELD(&s.pt); PT_WAIT_UNTIL(&s.pt, uip_newdata() || timer_expired(&s.timer)); if(uip_newdata() && parse_msg() == DHCPOFFER) { s.state = STATE_OFFER_RECEIVED; break; } if(s.ticks < CLOCK_SECOND * 60) { s.ticks *= 2; } } while(s.state != STATE_OFFER_RECEIVED); s.ticks = CLOCK_SECOND; do { send_request(); timer_set(&s.timer, s.ticks); PT_YIELD(&s.pt); PT_WAIT_UNTIL(&s.pt, uip_newdata() || timer_expired(&s.timer)); if(uip_newdata() && parse_msg() == DHCPACK) { s.state = STATE_CONFIG_RECEIVED; break; } if(s.ticks <= CLOCK_SECOND * 10) { s.ticks += CLOCK_SECOND; } else { PT_RESTART(&s.pt); } } while(s.state != STATE_CONFIG_RECEIVED); #if 0 printf("Got IP address %d.%d.%d.%d\n", uip_ipaddr1(s.ipaddr), uip_ipaddr2(s.ipaddr), uip_ipaddr3(s.ipaddr), uip_ipaddr4(s.ipaddr)); printf("Got netmask %d.%d.%d.%d\n", uip_ipaddr1(s.netmask), uip_ipaddr2(s.netmask), uip_ipaddr3(s.netmask), uip_ipaddr4(s.netmask)); printf("Got DNS server %d.%d.%d.%d\n", uip_ipaddr1(s.dnsaddr), uip_ipaddr2(s.dnsaddr), uip_ipaddr3(s.dnsaddr), uip_ipaddr4(s.dnsaddr)); printf("Got default router %d.%d.%d.%d\n", uip_ipaddr1(s.default_router), uip_ipaddr2(s.default_router), uip_ipaddr3(s.default_router), uip_ipaddr4(s.default_router)); printf("Lease expires in %ld seconds\n", ntohs(s.lease_time[0])*65536ul + ntohs(s.lease_time[1])); #endif dhcpc_configured(&s); /* timer_stop(&s.timer);*/ /* * PT_END restarts the thread so we do this instead. Eventually we * should reacquire expired leases here. */ while(1) { PT_YIELD(&s.pt); } PT_END(&s.pt); }
/*---------------------------------------------------------------------------*/ static PT_THREAD(handle_dhcp(void)) { PT_BEGIN(&s.pt); #if defined PORT_APP_MAPPER dhcpc_running = 1; #endif if (s.state == STATE_RENEW) goto send_request_section; /* try_again:*/ s.state = STATE_SENDING; s.ticks = CLOCK_SECOND; //sendString("\r\ndhcpc handle dhcp passed: STATE_SENDING"); do { send_discover(); timer_set(&s.timer, s.ticks); // NOTE: fixed as per http://www.mail-archive.com/[email protected]/msg00003.html PT_YIELD_UNTIL(&s.pt, uip_newdata() || timer_expired(&s.timer)); //sendString("Just got something\n\r"); if(uip_newdata()) { //sendString("Data\n\r"); if (parse_msg() == DHCPOFFER) { s.state = STATE_OFFER_RECEIVED; break; } } else { //sendString("Timeout\n\r"); if(s.ticks < CLOCK_SECOND * 60) { s.ticks *= 2; } else { s.ticks = CLOCK_SECOND; } } } while(s.state != STATE_OFFER_RECEIVED); //sendString("\r\ndhcpc handle dhcp passed: STATE_OFFER_RECEIVED"); s.ticks = CLOCK_SECOND; send_request_section: do { send_request(); timer_set(&s.timer, s.ticks); // NOTE: fixed as per http://www.mail-archive.com/[email protected]/msg00003.html PT_YIELD_UNTIL(&s.pt, uip_newdata() || timer_expired(&s.timer)); if(uip_newdata()) { msg_type = parse_msg(); if (msg_type == DHCPACK) { s.state = STATE_CONFIG_RECEIVED; break; } else if (msg_type == DHCPNAK) { s.state = STATE_FAIL; goto close_and_clean_up; } } else { if(s.ticks <= CLOCK_SECOND * 10) { s.ticks += CLOCK_SECOND; } else { PT_RESTART(&s.pt); //sendString("\r\ndhcpc handle RESTARTING!"); } } } while(s.state != STATE_CONFIG_RECEIVED); //sendString("\r\ndhcpc handle dhcp passed: STATE_CONFIG_RECEIVED"); #if DEBUG_SERIAL printf_P(PSTR("Got IP address %d.%d.%d.%d\r\n"), uip_ipaddr1(s.ipaddr), uip_ipaddr2(s.ipaddr), uip_ipaddr3(s.ipaddr), uip_ipaddr4(s.ipaddr)); printf_P(PSTR("Got netmask %d.%d.%d.%d\r\n"), uip_ipaddr1(s.netmask), uip_ipaddr2(s.netmask), uip_ipaddr3(s.netmask), uip_ipaddr4(s.netmask)); printf_P(PSTR("Got DNS server %d.%d.%d.%d\r\n"), uip_ipaddr1(s.dnsaddr), uip_ipaddr2(s.dnsaddr), uip_ipaddr3(s.dnsaddr), uip_ipaddr4(s.dnsaddr)); printf_P(PSTR("Got default router %d.%d.%d.%d\r\n"), uip_ipaddr1(s.default_router), uip_ipaddr2(s.default_router), uip_ipaddr3(s.default_router), uip_ipaddr4(s.default_router)); printf_P(PSTR("Lease expires in %ld seconds\r\n"), ntohs(s.lease_time[0])*65536ul + ntohs(s.lease_time[1])); #endif dhcpc_configured(&s); /* timer_stop(&s.timer);*/ /* * PT_END restarts the thread so we do this instead. Eventually we * should reacquire expired leases here. */ /* while(1) { PT_YIELD(&s.pt); } */ close_and_clean_up: #if defined PORT_APP_MAPPER dhcpc_running = 0; #endif // all done with the connection, clean up uip_udp_remove(s.conn); s.conn = NULL; //sendString("\r\ndhcpc handle dhcp passed: END"); PT_END(&s.pt); }
static PT_THREAD( LOG_log(pt_t* pt) ) { u32 time; u8 is_filtered; u8 i; PT_BEGIN(pt); // systematically unlock the channel // because most of time no response is sent // when a response is needed, the channel will be locked DPT_unlock(&LOG.interf); switch ( LOG.state ) { case LOG_OFF: default: // empty the log fifo (void)FIFO_get(&LOG.in_fifo, &LOG.fr); // loop back for next frame PT_RESTART(pt); break; case LOG_RAM: #ifdef SAVE_IN_RAM_ENABLED #endif break; case LOG_EEPROM: // if address is out of range if ( LOG.eeprom_addr >= EEPROM_END_ADDR ) { // logging is no more possible // so quit PT_EXIT(pt); } break; case LOG_SDCARD: // if address is out of range if ( LOG.sdcard_addr >= SDCARD_END_ADDR ) { // logging is no more possible // so quit PT_EXIT(pt); } break; } // wait while no frame is present in the fifo PT_WAIT_WHILE(pt, KO == FIFO_get(&LOG.in_fifo, &LOG.fr)); // if it is a log command if ( (LOG.fr.cmde == FR_LOG_CMD) && (!LOG.fr.resp) ) { // treat it LOG_command(&LOG.fr); // send the response DPT_lock(&LOG.interf); PT_WAIT_UNTIL(pt, OK == DPT_tx(&LOG.interf, &LOG.fr)); DPT_unlock(&LOG.interf); // and wait till the next frame PT_RESTART(pt); } // filter the frame according to its origin is_filtered = OK; // by default, every frame is filtered for ( i = 0; i < sizeof(LOG.orig_filter); i++ ) { // passthrough or frame origin and filter acceptance match if ( (LOG.orig_filter[i] == 0x00) || (LOG.orig_filter[i] == LOG.fr.orig) ){ is_filtered = KO; break; } } // if frame is filtered away if ( is_filtered ) { // lop back for next frame PT_RESTART(pt); } // build the log packet LOG.block.index = LOG.index; time = TIME_get(); LOG.block.time[0] = (u8)(time >> 16); LOG.block.time[1] = (u8)(time >> 8); LOG.block.fr = LOG.fr; switch ( LOG.state ) { case LOG_OFF: default: // shall never happen but just in case // loop back for next frame PT_RESTART(pt); break; case LOG_RAM: #ifdef SAVE_IN_RAM_ENABLED LOG.ram_buffer[LOG.ram_index] = LOG.block; if ( LOG.ram_index < (RAM_BUFFER_SIZE - 1) ) { LOG.ram_index++; } #endif break; case LOG_EEPROM: // save it to eeprom PT_WAIT_UNTIL(pt, EEP_write(LOG.eeprom_addr, (u8*)&LOG.block, sizeof(log_t))); // wait until saving is done PT_WAIT_UNTIL(pt, EEP_is_fini()); break; case LOG_SDCARD: // save it to sdcard (fill the write buffer) PT_WAIT_UNTIL(pt, SD_write(LOG.sdcard_addr, (u8*)&LOG.block, sizeof(log_t))); // wait until saving is done break; } // loop back to treat the next frame to log PT_RESTART(pt); PT_END(pt); }
/*---------------------------------------------------------------------------*/ static PT_THREAD(handle_dhcp(void)) { PT_BEGIN(&s.pt); /* try_again:*/ s.state = STATE_SENDING; s.ticks = CLOCK_SECOND; do { send_discover(); /* Sending does not clear the NEWDATA flag. The packet doesn't actually get sent until we yield at least once. If we don't clear the flag ourselves, we will enter an infinite loop here. This is arguably a bug in uip.c and the uip_send() function should probably clear the NEWDATA flag. */ uip_flags=uip_flags&(~UIP_NEWDATA); timer_set(&s.timer, s.ticks); PT_WAIT_UNTIL(&s.pt, uip_newdata() || timer_expired(&s.timer)); if(uip_newdata() && parse_msg() == DHCPOFFER) { s.state = STATE_OFFER_RECEIVED; break; } if(s.ticks < CLOCK_SECOND * 20) { s.ticks *= 2; } } while(s.state != STATE_OFFER_RECEIVED); s.ticks = CLOCK_SECOND; do { send_request(); uip_flags=uip_flags&(~UIP_NEWDATA); timer_set(&s.timer, s.ticks); PT_WAIT_UNTIL(&s.pt, uip_newdata() || timer_expired(&s.timer)); if(uip_newdata() && parse_msg() == DHCPACK) { s.state = STATE_CONFIG_RECEIVED; break; } if(s.ticks <= CLOCK_SECOND * 10) { s.ticks += CLOCK_SECOND; } else { PT_RESTART(&s.pt); } } while(s.state != STATE_CONFIG_RECEIVED); #if 0 printf("Got IP address %d.%d.%d.%d\n", uip_ipaddr_to_quad(&s.ipaddr)); printf("Got netmask %d.%d.%d.%d\n", uip_ipaddr_to_quad(&s.netmask)); printf("Got DNS server %d.%d.%d.%d\n", uip_ipaddr_to_quad(&s.dnsaddr)); printf("Got default router %d.%d.%d.%d\n", uip_ipaddr_to_quad(&s.default_router)); printf("Lease expires in %ld seconds\n", uip_ntohs(s.lease_time[0])*65536ul + uip_ntohs(s.lease_time[1])); #endif dhcpc_configured(&s); /* timer_stop(&s.timer);*/ /* * PT_END restarts the thread so we do this instead. Eventually we * should reacquire expired leases here. */ while(1) { PT_YIELD(&s.pt); } PT_END(&s.pt); }
static PT_THREAD (handle_dhcp (void)) { PT_BEGIN (&dhcpcState->pt); dhcpcState->state = STATE_SENDING; dhcpcState->ticks = CLOCK_SECOND; do { send_discover (); timer_set (&dhcpcState->timer, dhcpcState->ticks); PT_WAIT_UNTIL (&dhcpcState->pt, uip_newdata () || timer_expired (&dhcpcState->timer)); if (uip_newdata () && (parse_msg () == DHCPOFFER)) { uip_flags &= ~UIP_NEWDATA; dhcpcState->state = STATE_OFFER_RECEIVED; break; } uip_flags &= ~UIP_NEWDATA; if (dhcpcState->ticks < CLOCK_SECOND * 60) dhcpcState->ticks *= 2; else { dhcpcState->ipaddr [0] = dhcpcState->ipaddr [1] = 0; goto dhcpcf; } } while (dhcpcState->state != STATE_OFFER_RECEIVED); dhcpcState->ticks = CLOCK_SECOND; do { send_request (); timer_set (&dhcpcState->timer, dhcpcState->ticks); PT_WAIT_UNTIL (&dhcpcState->pt, uip_newdata () || timer_expired (&dhcpcState->timer)); if (uip_newdata () && (parse_msg () == DHCPACK)) { uip_flags &= ~UIP_NEWDATA; dhcpcState->state = STATE_CONFIG_RECEIVED; break; } uip_flags &= ~UIP_NEWDATA; if (dhcpcState->ticks <= CLOCK_SECOND * 10) dhcpcState->ticks += CLOCK_SECOND; else PT_RESTART (&dhcpcState->pt); } while (dhcpcState->state != STATE_CONFIG_RECEIVED); dhcpcf: dhcpc_configured (dhcpcState); /* timer_stop (&dhcpcState->timer);*/ /* * PT_END restarts the thread so we do this instead. Eventually we * should reacquire expired leases here. */ while (1) PT_YIELD (&dhcpcState->pt); PT_END (&dhcpcState->pt); }
static PT_THREAD( TSN_tsn(pt_t* pt) ) { u32 local_time; union { u32 full; u8 part[4]; } remote_time; dna_list_t* list; u8 nb_is; u8 nb_bs; PT_BEGIN(pt); // every second PT_WAIT_UNTIL(pt, TIME_get() > TSN.time_out); TSN.time_out += TIME_1_SEC; // retrieve self and BC node address list = DNA_list(&nb_is, &nb_bs); // build the time request TSN.fr.orig = DNA_SELF_ADDR(list); TSN.fr.dest = DNA_BC_ADDR(list); TSN.fr.cmde = FR_TIME_GET; TSN.fr.resp = 0; TSN.fr.error = 0; TSN.fr.eth = 0; TSN.fr.serial = 0; // send the time request DPT_lock(&TSN.interf); PT_WAIT_UNTIL(pt, OK == DPT_tx(&TSN.interf, &TSN.fr)); DPT_unlock(&TSN.interf); // wait for the answer PT_WAIT_UNTIL(pt, FIFO_get(&TSN.queue, &TSN.fr) && TSN.fr.resp); // immediatly unlock DPT_unlock(&TSN.interf); // rebuild remote time (AVR is little endian) remote_time.part[0] = TSN.fr.argv[3]; remote_time.part[1] = TSN.fr.argv[2]; remote_time.part[2] = TSN.fr.argv[1]; remote_time.part[3] = TSN.fr.argv[0]; // read local time local_time = TIME_get(); // check whether we are in the future if ( local_time > remote_time.full ) { // then the local time is running too fast // so slow it down TSN.time_correction--; } // check whether we are in the past if ( local_time < remote_time.full ) { // then the local time is running too slow // so speed it up TSN.time_correction++; } // update time increment TIME_set_incr(10 * TIME_1_MSEC + TSN.time_correction); // loop back PT_RESTART(pt); PT_END(pt); }