void tsm_set_confirmed_unsegmented_transaction( uint8_t invokeID, BACNET_ADDRESS * dest, BACNET_NPDU_DATA * ndpu_data, uint8_t * apdu, uint16_t apdu_len) { uint16_t j = 0; uint8_t index; if (invokeID) { index = tsm_find_invokeID_index(invokeID); if (index < MAX_TSM_TRANSACTIONS) { /* SendConfirmedUnsegmented */ TSM_List[index].state = TSM_STATE_AWAIT_CONFIRMATION; TSM_List[index].RetryCount = 0; /* start the timer */ TSM_List[index].RequestTimer = apdu_timeout(); /* copy the data */ for (j = 0; j < apdu_len; j++) { TSM_List[index].apdu[j] = apdu[j]; } TSM_List[index].apdu_len = apdu_len; npdu_copy_data(&TSM_List[index].npdu_data, ndpu_data); bacnet_address_copy(&TSM_List[index].dest, dest); } } return; }
/* if we wanted to find out what we sent (i.e. when we get an ack) */ bool tsm_get_transaction_pdu( uint8_t invokeID, BACNET_ADDRESS * dest, BACNET_NPDU_DATA * ndpu_data, uint8_t * apdu, uint16_t * apdu_len) { uint16_t j = 0; uint8_t index; bool found = false; if (invokeID) { index = tsm_find_invokeID_index(invokeID); /* how much checking is needed? state? dest match? just invokeID? */ if (index < MAX_TSM_TRANSACTIONS) { /* FIXME: we may want to free the transaction so it doesn't timeout */ /* retrieve the transaction */ /* FIXME: bounds check the pdu_len? */ *apdu_len = (uint16_t) TSM_List[index].apdu_len; for (j = 0; j < *apdu_len; j++) { apdu[j] = TSM_List[index].apdu[j]; } npdu_copy_data(ndpu_data, &TSM_List[index].npdu_data); bacnet_address_copy(dest, &TSM_List[index].dest); found = true; } } return found; }
/* returns number of bytes sent on success, zero on failure */ int dlmstp_send_pdu( BACNET_ADDRESS * dest, /* destination address */ BACNET_NPDU_DATA * npdu_data, /* network information */ uint8_t * pdu, /* any data to be sent - may be null */ unsigned pdu_len) { /* number of bytes of data */ int bytes_sent = 0; unsigned i = 0; if (!Transmit_Packet.ready) { if (npdu_data->data_expecting_reply) { Transmit_Packet.frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; } else { Transmit_Packet.frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; } Transmit_Packet.pdu_len = pdu_len; for (i = 0; i < pdu_len; i++) { Transmit_Packet.pdu[i] = pdu[i]; } bacnet_address_copy(&Transmit_Packet.address, dest); bytes_sent = pdu_len + MAX_HEADER; Transmit_Packet.ready = true; } return bytes_sent; }
static void print_address_cache( void) { BACNET_ADDRESS address; unsigned total_addresses = 0; unsigned dup_addresses = 0; struct address_entry *addr; uint8_t local_sadr = 0; /* NOTE: this string format is parsed by src/address.c, so these must be compatible. */ printf(";%-7s %-20s %-5s %-20s %-4s\n", "Device", "MAC (hex)", "SNET", "SADR (hex)", "APDU"); printf(";-------- -------------------- ----- -------------------- ----\n"); addr = Address_Table.first; while (addr) { bacnet_address_copy(&address, &addr->address); total_addresses++; if (addr->Flags & BAC_ADDRESS_MULT) { dup_addresses++; printf(";"); } else { printf(" "); } printf(" %-7u ", addr->device_id); print_macaddr(address.mac, address.mac_len); printf(" %-5hu ", address.net); if (address.net) { print_macaddr(address.adr, address.len); } else { print_macaddr(&local_sadr, 1); } printf(" %-4hu ", addr->max_apdu); printf("\n"); addr = addr->next; } printf(";\n; Total Devices: %u\n", total_addresses); if (dup_addresses) { printf("; * Duplicate Devices: %u\n", dup_addresses); } }
/************************************************************************** * Description: handles recurring strictly timed task * Returns: none * Notes: called by ISR every 5 milliseconds **************************************************************************/ void bacnet_task_timed( void) { struct mstp_rx_packet *pkt = NULL; uint16_t pdu_len = 0; BACNET_ADDRESS src; pdu_len = dlmstp_receive(&src, &PDUBuffer[0], sizeof(PDUBuffer), 5); if (pdu_len) { pkt = (struct mstp_rx_packet *) Ringbuf_Data_Peek(&Receive_Queue); if (pkt) { memcpy(pkt->buffer, PDUBuffer, MAX_MPDU); bacnet_address_copy(&pkt->src, &src); pkt->length = pdu_len; Ringbuf_Data_Put(&Receive_Queue, pkt); } } }
/** * Adds the address to the list of COV addresses * * @param dest - address to be added if there is room in the list * * @return index number 0..N, or -1 if unable to add */ static int cov_address_add( BACNET_ADDRESS * dest) { int index = -1; unsigned i = 0; bool found = false; bool valid = false; BACNET_ADDRESS *cov_dest = NULL; if (dest) { for (i = 0; i < MAX_COV_ADDRESSES; i++) { valid = COV_Addresses[i].valid; if (valid) { cov_dest = &COV_Addresses[i].dest; found = bacnet_address_same(dest, cov_dest); if (found) { index = i; break; } } } if (!found) { /* find a free place to add a new address */ for (i = 0; i < MAX_COV_ADDRESSES; i++) { valid = COV_Addresses[i].valid; if (!valid) { index = i; cov_dest = &COV_Addresses[i].dest; bacnet_address_copy(cov_dest, dest); COV_Addresses[i].valid = true; break; } } } } return index; }
static bool cov_list_subscribe( BACNET_ADDRESS * src, BACNET_SUBSCRIBE_COV_DATA * cov_data, BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code) { bool existing_entry = false; int index; int first_invalid_index = -1; bool found = true; /* unable to subscribe - resources? */ /* unable to cancel subscription - other? */ /* existing? - match Object ID and Process ID */ for (index = 0; index < MAX_COV_SUBCRIPTIONS; index++) { if (COV_Subscriptions[index].flag.valid) { if ((COV_Subscriptions[index].monitoredObjectIdentifier.type == cov_data->monitoredObjectIdentifier.type) && (COV_Subscriptions[index].monitoredObjectIdentifier.instance == cov_data->monitoredObjectIdentifier.instance) && (COV_Subscriptions[index].subscriberProcessIdentifier == cov_data->subscriberProcessIdentifier)) { existing_entry = true; if (cov_data->cancellationRequest) { COV_Subscriptions[index].flag.valid = false; } else { bacnet_address_copy(&COV_Subscriptions[index].dest, src); COV_Subscriptions[index].flag.issueConfirmedNotifications = cov_data->issueConfirmedNotifications; COV_Subscriptions[index].lifetime = cov_data->lifetime; COV_Subscriptions[index].flag.send_requested = true; } if (COV_Subscriptions[index].invokeID) { tsm_free_invoke_id(COV_Subscriptions[index].invokeID); COV_Subscriptions[index].invokeID = 0; } break; } } else { if (first_invalid_index < 0) { first_invalid_index = index; } } } if (!existing_entry && (first_invalid_index >= 0) && (!cov_data->cancellationRequest)) { index = first_invalid_index; found = true; COV_Subscriptions[index].flag.valid = true; bacnet_address_copy(&COV_Subscriptions[index].dest, src); COV_Subscriptions[index].monitoredObjectIdentifier.type = cov_data->monitoredObjectIdentifier.type; COV_Subscriptions[index].monitoredObjectIdentifier.instance = cov_data->monitoredObjectIdentifier.instance; COV_Subscriptions[index].subscriberProcessIdentifier = cov_data->subscriberProcessIdentifier; COV_Subscriptions[index].flag.issueConfirmedNotifications = cov_data->issueConfirmedNotifications; COV_Subscriptions[index].invokeID = 0; COV_Subscriptions[index].lifetime = cov_data->lifetime; COV_Subscriptions[index].flag.send_requested = true; } else if (!existing_entry) { if (first_invalid_index < 0) { /* Out of resources */ *error_class = ERROR_CLASS_RESOURCES; *error_code = ERROR_CODE_NO_SPACE_TO_ADD_LIST_ELEMENT; found = false; } else { /* cancellationRequest - valid object not subscribed */ /* From BACnet Standard 135-2010-13.14.2 ...Cancellations that are issued for which no matching COV context can be found shall succeed as if a context had existed, returning 'Result(+)'. */ found = true; } } return found; }
bool dlmstp_compare_data_expecting_reply( uint8_t * request_pdu, uint16_t request_pdu_len, uint8_t src_address, uint8_t * reply_pdu, uint16_t reply_pdu_len, BACNET_ADDRESS * dest_address) { uint16_t offset; /* One way to check the message is to compare NPDU src, dest, along with the APDU type, invoke id. Seems a bit overkill */ struct DER_compare_t { BACNET_NPDU_DATA npdu_data; BACNET_ADDRESS address; uint8_t pdu_type; uint8_t invoke_id; uint8_t service_choice; }; struct DER_compare_t request; struct DER_compare_t reply; /* unused parameters */ request_pdu_len = request_pdu_len; reply_pdu_len = reply_pdu_len; /* decode the request data */ request.address.mac[0] = src_address; request.address.mac_len = 1; offset = npdu_decode(&request_pdu[0], NULL, &request.address, &request.npdu_data); if (request.npdu_data.network_layer_message) { return false; } request.pdu_type = request_pdu[offset] & 0xF0; if (request.pdu_type != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) { return false; } request.invoke_id = request_pdu[offset + 2]; /* segmented message? */ if (request_pdu[offset] & BIT3) request.service_choice = request_pdu[offset + 5]; else request.service_choice = request_pdu[offset + 3]; /* decode the reply data */ bacnet_address_copy(&reply.address, dest_address); offset = npdu_decode(&reply_pdu[0], &reply.address, NULL, &reply.npdu_data); if (reply.npdu_data.network_layer_message) { return false; } /* reply could be a lot of things: confirmed, simple ack, abort, reject, error */ reply.pdu_type = reply_pdu[offset] & 0xF0; switch (reply.pdu_type) { case PDU_TYPE_CONFIRMED_SERVICE_REQUEST: reply.invoke_id = reply_pdu[offset + 2]; /* segmented message? */ if (reply_pdu[offset] & BIT3) reply.service_choice = reply_pdu[offset + 5]; else reply.service_choice = reply_pdu[offset + 3]; break; case PDU_TYPE_SIMPLE_ACK: reply.invoke_id = reply_pdu[offset + 1]; reply.service_choice = reply_pdu[offset + 2]; break; case PDU_TYPE_COMPLEX_ACK: reply.invoke_id = reply_pdu[offset + 1]; /* segmented message? */ if (reply_pdu[offset] & BIT3) reply.service_choice = reply_pdu[offset + 4]; else reply.service_choice = reply_pdu[offset + 2]; break; case PDU_TYPE_ERROR: reply.invoke_id = reply_pdu[offset + 1]; reply.service_choice = reply_pdu[offset + 2]; break; case PDU_TYPE_REJECT: case PDU_TYPE_ABORT: reply.invoke_id = reply_pdu[offset + 1]; break; default: return false; } /* these don't have service choice included */ if ((reply.pdu_type == PDU_TYPE_REJECT) || (reply.pdu_type == PDU_TYPE_ABORT)) { if (request.invoke_id != reply.invoke_id) { return false; } } else { if (request.invoke_id != reply.invoke_id) { return false; } if (request.service_choice != reply.service_choice) { return false; } } if (request.npdu_data.protocol_version != reply.npdu_data.protocol_version) { return false; } if (request.npdu_data.priority != reply.npdu_data.priority) { return false; } if (!bacnet_address_same(&request.address, &reply.address)) { return false; } return true; }