ot_int sub_parse_request(m2session* session) { ot_int score = 0; ot_u8 cmd_opcode; //ot_u8 nack = 0; /// 1. Universal Comm Processing <BR> /// - Load CCA type & CSMA disable from command extension <BR> /// - Load NA2P or A2P dialog type from command code m2qp.cmd.code = q_readbyte(&rxq); m2qp.cmd.ext = (m2qp.cmd.code & 0x80) ? q_readbyte(&rxq) : 0; dll.comm.csmaca_params = m2qp.cmd.ext & (M2_CSMACA_CAMASK | M2_CSMACA_NOCSMA); dll.comm.csmaca_params |= m2qp.cmd.code & M2_CSMACA_ARBMASK; cmd_opcode = m2qp.cmd.code & M2OP_MASK; /// 2. All Requests contain the dialog template, so load it. <BR> /// - [ num resp channels ] [ list of resp channels] <BR> /// - if number of resp channels is 0, use the current channel { ot_u8 timeout_code = q_readbyte(&rxq); dll.comm.rx_timeout = otutils_calc_timeout(timeout_code); // original contention period dll.comm.tc = dll.comm.rx_timeout; // contention period counter if (timeout_code & 0x80) { dll.comm.tx_channels = q_readbyte(&rxq); dll.comm.tx_chanlist = q_markbyte(&rxq, dll.comm.tx_channels); } else { dll.comm.tx_channels = 1; dll.comm.tx_chanlist = &dll.comm.scratch[0]; dll.comm.scratch[0] = session->channel; } } /// 3. Handle Command Queries (filtering) <BR> /// Multicast and anycast addressed requests include queries if (m2np.header.addr_ctl & 0x80) { score = sub_process_query(session); } /// 4. If the query is good (sometimes this is trivial): <BR> /// - Prepare the response header (same for all responses) <BR> /// - Run command-specific dialog data processing if (score >= 0) { q_empty(&txq); // Flush TX Queue if (m2qp.cmd.ext & M2CE_NORESP) { session->netstate |= M2_NETFLAG_SCRAP; } else { ot_u8 addressing; session->netstate &= ~M2_NETSTATE_TMASK; session->netstate |= M2_NETSTATE_RESPTX; addressing = ext_get_m2appflags(); addressing |= m2np.header.addr_ctl & 0x30; // make unicast, retain VID & NLS m2np_header(session, addressing, 0); // Create M2QP header q_writebyte(&txq, (M2TT_RESPONSE | cmd_opcode)); // Write Cmd code byte } switch ((cmd_opcode>>1) & 7) { case 0: case 1: break; case 2: sub_opgroup_shell(); break; case 3: case 4: sub_opgroup_collection(); break; case 5: break; case 6: sub_opgroup_datastream(); break; case 7: sub_ack_datastream(); break; } }
int sub_getdecnum(int* status, FILE* stream, Queue* msg) { int digits; char next; char buf[16]; int sign = 1; int force_u = 0; int number = 0; int i = 0; int size = 0; // Buffer until whitespace or ')' delimiter digits = sub_buffernum(status, stream, buf, 15); // Deal with leading minus sign if (buf[i] == '-') { sign = -1; i++; } // Go through the digits & footer // - load in numerical value, one digit at a time // - also look for the type footer: ul, us, uc, u, l, s, c, or none while (i < digits) { if ((buf[i] >= '0') && (buf[i] <= '9')) { number *= 10; number += (buf[i++] - '0'); } else { force_u = (buf[i] == 'u'); i += force_u; if (buf[i] == 'c') size = 1; // c: char (1 byte) else if (buf[i] == 's') size = 2; // s: short (2 bytes) else if (buf[i] == 'l') size = 3; // l: long (4 bytes) break; } } // Determine size in case where footer is not explicitly provided if (size == 0) { int j; int bound[] = {128, 256, 32768, 65536, 0, 0}; int max = number - (sign < 0); for (j=force_u, size=1; ; j+=2, size++) { if ((bound[j]==0) || (bound[j]>=max)) break; } } number *= sign; switch (size & 3) { case 0: case 1: q_writebyte(msg, (ot_u8)number); break; case 2: q_writeshort(msg, (ot_u16)number); break; case 3: size = 4; q_writelong(msg, (ot_u32)number); break; } return size; }
OT_WEAK void alp_stream_id_tmpl(ot_queue* out_q, void* data_type) { if _PTR_TEST(data_type) { q_writebyte(out_q, ((id_tmpl*)data_type)->length); q_writestring(out_q, ((id_tmpl*)data_type)->value, ((id_tmpl*)data_type)->length); } }
OT_WEAK void alp_stream_u32(ot_queue* out_q, void* data_type) { q_writebyte(out_q, *(ot_u32*)data_type); }
void sub_build_uhfmsg(ot_int* buffer) { /// This is the routine that builds the DASH7 UDP generic protocol message. /// The protocol has data elements marked by a letter (T, V, R, E, D) that /// signify Temperature, Voltage, RSSI (LF), PaLFi wake Event, and RX Data. /// The elements are fixed/known length. session_tmpl s_tmpl; command_tmpl c_tmpl; ot_u8* data_start; ot_u8 status; // Create a new session: you could change these parameters // Use "CHAN1" for odd events, "CHAN2" for even events s_tmpl.channel = (palfi.wake_event & 1) ? ALERT_CHAN1 : ALERT_CHAN2; s_tmpl.subnetmask = 0; // Use default subnet s_tmpl.flagmask = 0; // Use default app-flags s_tmpl.timeout = 10; // Do CSMA for no more than 10 ticks (~10 ms) otapi_new_session(&s_tmpl); // Broadcast request (takes no 2nd argument) otapi_open_request(ADDR_broadcast, NULL); // Insert Transport-Layer headers c_tmpl.type = CMDTYPE_na2p_request; c_tmpl.opcode = CMD_udp_on_file; c_tmpl.extension= CMDEXT_no_response; otapi_put_command_tmpl(&status, &c_tmpl); otapi_put_dialog_tmpl(&status, NULL); // NULL = defaults // UDP Header q_writebyte(&txq, 255); // Source Port: 255 (custom application port) q_writebyte(&txq, 255); // Destination Port (same value) data_start = txq.putcursor; // Place temperature data q_writebyte(&txq, 'T'); q_writeshort(&txq, buffer[0]); // Place Voltage data q_writebyte(&txq, 'V'); q_writeshort(&txq, buffer[1]); // Place RSSI data q_writebyte(&txq, 'R'); q_writestring(&txq, (ot_u8*)&palfi.rssi1, 3); // Place Action data q_writebyte(&txq, 'E'); q_writebyte(&txq, (ot_int)palfi.wake_event); // Dump some received data if (palfi.wake_event) { q_writebyte(&txq, 'D'); q_writestring(&txq, palfi.rxdata, 8); } // Store this information into the Port 255 file for continuous, automated // reporting by DASH7/OpenTag until it is updated next time. The length of // this information is always 23 bytes. { vlFILE* fp; fp = ISF_open_su(255); if (fp != NULL) { vl_store(fp, 23, data_start); vl_close(fp); } } // Finish Message otapi_close_request(); }