Added sample Confirmed Private Transfer implementation files and demo for same.
This commit is contained in:
@@ -0,0 +1,255 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "apdu.h"
|
||||
#include "npdu.h"
|
||||
#include "abort.h"
|
||||
//#include "arf.h"
|
||||
/* demo objects */
|
||||
#include "device.h"
|
||||
#include "ai.h"
|
||||
#include "ao.h"
|
||||
#if defined(BACFILE)
|
||||
#include "bacfile.h"
|
||||
#endif
|
||||
#include "mydata.h"
|
||||
#include "ptransfer.h"
|
||||
|
||||
#define MYMAXSTR 32
|
||||
#define MYMAXBLOCK 8
|
||||
|
||||
DATABLOCK MyData[MYMAXBLOCK];
|
||||
|
||||
uint8_t IOBufferPT[MAX_APDU]; // Buffer for building response in
|
||||
|
||||
void ProcessPT(BACNET_PRIVATE_TRANSFER_DATA *data)
|
||||
|
||||
{
|
||||
int iLen; // Index to current location in data
|
||||
char cBlockNumber;
|
||||
uint32_t ulTemp;
|
||||
int tag_len;
|
||||
uint8_t tag_number;
|
||||
uint32_t len_value_type;
|
||||
BACNET_CHARACTER_STRING bsTemp;
|
||||
|
||||
iLen = 0;
|
||||
|
||||
// Decode the block number
|
||||
|
||||
tag_len = decode_tag_number_and_value(&data->serviceParameters[iLen], &tag_number, &len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) { // Bail out early if wrong type
|
||||
data->serviceParametersLen = 0; // and signal unexpected error
|
||||
return;
|
||||
}
|
||||
|
||||
iLen += decode_unsigned(&data->serviceParameters[iLen], len_value_type, &ulTemp);
|
||||
cBlockNumber = (char)ulTemp;
|
||||
if(cBlockNumber < MY_MAX_BLOCK) {
|
||||
if(data->serviceNumber == MY_SVC_READ) {
|
||||
// Read Response is an unsigned int with 0 for success or a non 0 error code
|
||||
// For a successful read the 0 success code is followed by the block number
|
||||
// and then the block contents which consist of 2 unsigned ints (in 0 to 255
|
||||
// range as they are really chars) a single precision real and a string which
|
||||
// will be up to 32 chars + a nul
|
||||
|
||||
iLen = 0;
|
||||
|
||||
iLen += encode_application_unsigned(&IOBufferPT[iLen], MY_ERR_OK); // Signal success
|
||||
iLen += encode_application_unsigned(&IOBufferPT[iLen], cBlockNumber); // Followed by the block number
|
||||
iLen += encode_application_unsigned(&IOBufferPT[iLen], MyData[cBlockNumber].cMyByte1); // And Then the block contents
|
||||
iLen += encode_application_unsigned(&IOBufferPT[iLen], MyData[cBlockNumber].cMyByte2);
|
||||
iLen += encode_application_real(&IOBufferPT[iLen], MyData[cBlockNumber].fMyReal);
|
||||
characterstring_init_ansi(&bsTemp, MyData[cBlockNumber].sMyString);
|
||||
iLen += encode_application_character_string(&IOBufferPT[iLen], &bsTemp);
|
||||
}
|
||||
else { // Write operation
|
||||
// Write block consists of the block number followed by the block contents as
|
||||
// described above for the read operation. The returned result is an unsigned
|
||||
// response which is 0 for success and a non 0 error code otherwise.
|
||||
|
||||
tag_len = decode_tag_number_and_value(&data->serviceParameters[iLen], &tag_number, &len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) {
|
||||
data->serviceParametersLen = 0;
|
||||
return;
|
||||
}
|
||||
iLen += decode_unsigned(&data->serviceParameters[iLen], len_value_type, &ulTemp);
|
||||
MyData[cBlockNumber].cMyByte1 = (char)ulTemp;
|
||||
|
||||
tag_len = decode_tag_number_and_value(&data->serviceParameters[iLen], &tag_number, &len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) {
|
||||
data->serviceParametersLen = 0;
|
||||
return;
|
||||
}
|
||||
iLen += decode_unsigned(&data->serviceParameters[iLen], len_value_type, &ulTemp);
|
||||
MyData[cBlockNumber].cMyByte2 = (char)ulTemp;
|
||||
|
||||
tag_len = decode_tag_number_and_value(&data->serviceParameters[iLen], &tag_number, &len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_REAL) {
|
||||
data->serviceParametersLen = 0;
|
||||
return;
|
||||
}
|
||||
iLen += decode_real(&data->serviceParameters[iLen], &MyData[cBlockNumber].fMyReal);
|
||||
|
||||
tag_len = decode_tag_number_and_value(&data->serviceParameters[iLen], &tag_number, &len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_CHARACTER_STRING) {
|
||||
data->serviceParametersLen = 0;
|
||||
return;
|
||||
}
|
||||
decode_character_string(&data->serviceParameters[iLen], len_value_type, &bsTemp);
|
||||
strncpy(MyData[cBlockNumber].sMyString, characterstring_value(&bsTemp), MY_MAX_STR); // Only copy as much as we can accept
|
||||
MyData[cBlockNumber].sMyString[MY_MAX_STR] = '\0'; // Make sure it is nul terminated
|
||||
|
||||
iLen = encode_application_unsigned(&IOBufferPT[0], MY_ERR_OK); // Signal success
|
||||
}
|
||||
}
|
||||
else {
|
||||
iLen = encode_application_unsigned(&IOBufferPT[0], MY_ERR_BAD_INDEX); // Signal bad index
|
||||
}
|
||||
data->serviceParametersLen = iLen;
|
||||
data->serviceParameters = IOBufferPT;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called when we receive a private transfer packet.
|
||||
* We parse the data, send the private part for processing and then send the
|
||||
* response which the application generates.
|
||||
* If there are any BACnet level errors we send an error response from here.
|
||||
* If there are any application level errors they will be packeged up in the
|
||||
* response block which we send back to the originator of the request.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
void handler_conf_private_trans(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_CONFIRMED_SERVICE_DATA * service_data)
|
||||
{
|
||||
BACNET_PRIVATE_TRANSFER_DATA data;
|
||||
int len;
|
||||
int pdu_len;
|
||||
bool error;
|
||||
int bytes_sent;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
BACNET_ADDRESS my_address;
|
||||
BACNET_ERROR_CLASS error_class;
|
||||
BACNET_ERROR_CODE error_code;
|
||||
|
||||
len = 0;
|
||||
pdu_len = 0;
|
||||
error = false;
|
||||
bytes_sent = 0;
|
||||
error_class = ERROR_CLASS_OBJECT;
|
||||
error_code = ERROR_CODE_UNKNOWN_OBJECT;
|
||||
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,"Received Confirmed Private Transfer Request!\n");
|
||||
#endif
|
||||
// encode the NPDU portion of the response packet as it will be needed
|
||||
// no matter what the outcome.
|
||||
|
||||
datalink_get_my_address(&my_address);
|
||||
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
|
||||
pdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, &npdu_data);
|
||||
|
||||
if (service_data->segmented_message)
|
||||
{
|
||||
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,"CPT: Segmented Message. Sending Abort!\n");
|
||||
#endif
|
||||
goto CPT_ABORT;
|
||||
}
|
||||
|
||||
len = ptransfer_decode_service_request(service_request, service_len, &data);
|
||||
/* bad decoding - send an abort */
|
||||
if (len < 0)
|
||||
{
|
||||
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id, ABORT_REASON_OTHER, true);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,"CPT: Bad Encoding. Sending Abort!\n");
|
||||
#endif
|
||||
goto CPT_ABORT;
|
||||
}
|
||||
|
||||
// Simple example with service number of 0 for read block and 1 for write block
|
||||
// We also only support our own vendor ID. In theory we could support others
|
||||
// for compatability purposes but these interfaces are rarely documented...
|
||||
|
||||
if((data.vendorID == BACNET_VENDOR_ID) && (data.serviceNumber <= MY_SVC_WRITE)){ // We only try to understand our own IDs and service numbers
|
||||
ProcessPT(&data); // Will either return a result block or an app level status block
|
||||
if(data.serviceParametersLen == 0){ // No respopnse means fatal error
|
||||
error = true;
|
||||
error_class = ERROR_CLASS_SERVICES;
|
||||
error_code = ERROR_CODE_OTHER;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,"CPT: Error servicing request!\n");
|
||||
#endif
|
||||
}
|
||||
len = ptransfer_ack_encode_apdu(&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id, &data);
|
||||
}
|
||||
else // Not our vendor ID or bad service parameter
|
||||
{
|
||||
error = true;
|
||||
error_class = ERROR_CLASS_SERVICES;
|
||||
error_code = ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,"CPT: Not our Vendor ID or invalid service code!\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
if (error){
|
||||
len = ptransfer_error_encode_apdu(&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id, error_class, error_code, &data);
|
||||
}
|
||||
CPT_ABORT:
|
||||
pdu_len += len;
|
||||
bytes_sent = datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len);
|
||||
|
||||
#if PRINT_ENABLED
|
||||
if (bytes_sent <= 0)
|
||||
{
|
||||
fprintf(stderr,"Failed to send PDU (%s)!\n",
|
||||
strerror(errno));
|
||||
}
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "apdu.h"
|
||||
#include "npdu.h"
|
||||
#include "abort.h"
|
||||
//#include "arf.h"
|
||||
/* demo objects */
|
||||
#include "device.h"
|
||||
#include "ai.h"
|
||||
#include "ao.h"
|
||||
#include "ptransfer.h"
|
||||
#include "mydata.h"
|
||||
#if defined(BACFILE)
|
||||
#include "bacfile.h"
|
||||
#endif
|
||||
|
||||
extern uint8_t IOBufferPT[300]; // Somewhere to build the encoded result block for Private Transfers
|
||||
|
||||
|
||||
void DecodeBlock(char cBlockNum, uint8_t *pData)
|
||||
|
||||
{
|
||||
int iLen;
|
||||
uint32_t ulTemp;
|
||||
int tag_len;
|
||||
uint8_t tag_number;
|
||||
uint32_t len_value_type;
|
||||
BACNET_CHARACTER_STRING bsName;
|
||||
DATABLOCK Response;
|
||||
|
||||
iLen = 0;
|
||||
|
||||
if(cBlockNum >= MY_MAX_BLOCK)
|
||||
return;
|
||||
|
||||
tag_len = decode_tag_number_and_value(&pData[iLen], &tag_number, &len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT)
|
||||
return;
|
||||
|
||||
iLen += decode_unsigned(&pData[iLen], len_value_type, &ulTemp);
|
||||
Response.cMyByte1 = (char)ulTemp;
|
||||
|
||||
tag_len = decode_tag_number_and_value(&pData[iLen], &tag_number, &len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT)
|
||||
return;
|
||||
|
||||
iLen += decode_unsigned(&pData[iLen], len_value_type, &ulTemp);
|
||||
Response.cMyByte2 = (char)ulTemp;
|
||||
|
||||
tag_len = decode_tag_number_and_value(&pData[iLen], &tag_number, &len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_REAL)
|
||||
return;
|
||||
|
||||
iLen += decode_real(&pData[iLen], &Response.fMyReal);
|
||||
|
||||
tag_len = decode_tag_number_and_value(&pData[iLen], &tag_number, &len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_CHARACTER_STRING)
|
||||
return;
|
||||
|
||||
iLen += decode_character_string(&pData[iLen], len_value_type, &bsName);
|
||||
strncpy(Response.sMyString, characterstring_value(&bsName), MY_MAX_STR);
|
||||
Response.sMyString[MY_MAX_STR] = '\0'; // Make sure it is nul terminated
|
||||
|
||||
printf("Private Transfer Read Block Response\n");
|
||||
printf("Data Block: %d\n", (int)cBlockNum);
|
||||
printf(" First Byte : %d\n", (int)Response.cMyByte1);
|
||||
printf(" Second Byte : %d\n", (int)Response.cMyByte2);
|
||||
printf(" Real : %f\n", Response.fMyReal);
|
||||
printf(" String : %s\n\n", Response.sMyString);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ProcessPTA(BACNET_PRIVATE_TRANSFER_DATA *data)
|
||||
|
||||
{
|
||||
int iLen; // Index to current location in data
|
||||
uint32_t uiErrorCode;
|
||||
char cBlockNumber;
|
||||
uint32_t ulTemp;
|
||||
int tag_len;
|
||||
uint8_t tag_number;
|
||||
uint32_t len_value_type;
|
||||
|
||||
iLen = 0;
|
||||
|
||||
// Error code is returned for read and write operations
|
||||
|
||||
tag_len = decode_tag_number_and_value(&data->serviceParameters[iLen], &tag_number, &len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) {
|
||||
#if PRINT_ENABLED
|
||||
printf("CPTA: Bad Encoding!\n");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
iLen += decode_unsigned(&data->serviceParameters[iLen], len_value_type, &uiErrorCode);
|
||||
|
||||
if(data->serviceNumber == MY_SVC_READ) { // Read I/O block so should be full block of data or error
|
||||
// Decode the error type and if necessary block number and then fetch the info
|
||||
|
||||
if(uiErrorCode == MY_ERR_OK) {
|
||||
// Block Number
|
||||
tag_len = decode_tag_number_and_value(&data->serviceParameters[iLen], &tag_number, &len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) {
|
||||
#if PRINT_ENABLED
|
||||
printf("CPTA: Bad Encoding!\n");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
iLen += decode_unsigned(&data->serviceParameters[iLen], len_value_type, &ulTemp);
|
||||
cBlockNumber = (char)ulTemp;
|
||||
DecodeBlock(cBlockNumber, &data->serviceParameters[iLen]);
|
||||
}
|
||||
else { // Read error
|
||||
printf("Private Transfer read operation returned error code: %u\n", uiErrorCode);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else { // Write I/O block - should just be an OK type message
|
||||
printf("Private Transfer write operation returned error code: %u\n", uiErrorCode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* This is called when we receive a private transfer packet ack.
|
||||
* We parse the response which the remote application generated
|
||||
* and decide what to do next...
|
||||
*/
|
||||
|
||||
void handler_conf_private_trans_ack(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data)
|
||||
{
|
||||
BACNET_PRIVATE_TRANSFER_DATA data;
|
||||
int len;
|
||||
|
||||
/*
|
||||
* Note:
|
||||
* We currently don't look at the source address and service data
|
||||
* but we probably should to verify that the ack is oneit is what
|
||||
* we were expecting. But this is just to silence some compiler
|
||||
* warnings from Borland.
|
||||
*/
|
||||
src = src;
|
||||
service_data = service_data;
|
||||
|
||||
len = 0;
|
||||
|
||||
|
||||
|
||||
#if PRINT_ENABLED
|
||||
printf("Received Confirmed Private Transfer Ack!\n");
|
||||
#endif
|
||||
|
||||
len = ptransfer_decode_service_request(service_request, service_len, &data); // Same decode for ack as for service request!
|
||||
if (len < 0)
|
||||
{
|
||||
#if PRINT_ENABLED
|
||||
printf("cpta: Bad Encoding!\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
ProcessPTA(&data); // See what to do with the response
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#if 0
|
||||
void PTErrorHandler(
|
||||
BACNET_ADDRESS * src,
|
||||
uint8_t invoke_id,
|
||||
BACNET_ERROR_CLASS error_class,
|
||||
BACNET_ERROR_CODE error_code)
|
||||
{
|
||||
/* FIXME: verify src and invoke id */
|
||||
(void) src;
|
||||
(void) invoke_id;
|
||||
printf("BACnet Error: %s: %s\r\n",
|
||||
bactext_error_class_name((int) error_class),
|
||||
bactext_error_code_name((int) error_code));
|
||||
Error_Detected = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include "config.h"
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "address.h"
|
||||
#include "tsm.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "dcc.h"
|
||||
#include "ptransfer.h"
|
||||
/* some demo stuff needed */
|
||||
#include "handlers.h"
|
||||
#include "txbuf.h"
|
||||
#include "mydata.h"
|
||||
|
||||
|
||||
uint8_t Send_Private_Transfer_Request(
|
||||
uint32_t device_id,
|
||||
uint16_t vendor_id,
|
||||
uint32_t service_number,
|
||||
char block_number,
|
||||
DATABLOCK *block
|
||||
)
|
||||
|
||||
{ /* NULL=optional */
|
||||
BACNET_ADDRESS dest;
|
||||
BACNET_ADDRESS my_address;
|
||||
unsigned max_apdu = 0;
|
||||
uint8_t invoke_id = 0;
|
||||
bool status = false;
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
int bytes_sent = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
static uint8_t pt_req_buffer[300]; // Somewhere to build the request packet
|
||||
BACNET_PRIVATE_TRANSFER_DATA pt_block;
|
||||
BACNET_CHARACTER_STRING bsTemp;
|
||||
|
||||
/* if we are forbidden to send, don't send! */
|
||||
if (!dcc_communication_enabled())
|
||||
return 0;
|
||||
|
||||
/* is the device bound? */
|
||||
status = address_get_by_device(device_id, &max_apdu, &dest);
|
||||
/* is there a tsm available? */
|
||||
if (status)
|
||||
invoke_id = tsm_next_free_invokeID();
|
||||
if (invoke_id) {
|
||||
/* encode the NPDU portion of the packet */
|
||||
datalink_get_my_address(&my_address);
|
||||
npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL);
|
||||
pdu_len =
|
||||
npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address,
|
||||
&npdu_data);
|
||||
/* encode the APDU portion of the packet */
|
||||
|
||||
pt_block.vendorID = vendor_id;
|
||||
pt_block.serviceNumber = service_number;
|
||||
if(service_number == MY_SVC_READ)
|
||||
{
|
||||
len += encode_application_unsigned(&pt_req_buffer[len], block_number); // The block number we want to retrieve
|
||||
}
|
||||
else
|
||||
{
|
||||
len += encode_application_unsigned(&pt_req_buffer[len], block_number); // The block number
|
||||
len += encode_application_unsigned(&pt_req_buffer[len], block->cMyByte1); // And Then the block contents
|
||||
len += encode_application_unsigned(&pt_req_buffer[len], block->cMyByte2);
|
||||
len += encode_application_real(&pt_req_buffer[len], block->fMyReal);
|
||||
characterstring_init_ansi(&bsTemp, block->sMyString);
|
||||
len += encode_application_character_string(&pt_req_buffer[len], &bsTemp);
|
||||
}
|
||||
|
||||
pt_block.serviceParameters = &pt_req_buffer[0];
|
||||
pt_block.serviceParametersLen = len;
|
||||
len = ptransfer_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id, &pt_block);
|
||||
pdu_len += len;
|
||||
|
||||
/* will it fit in the sender?
|
||||
note: if there is a bottleneck router in between
|
||||
us and the destination, we won't know unless
|
||||
we have a way to check for that and update the
|
||||
max_apdu in the address binding table. */
|
||||
|
||||
if ((unsigned) pdu_len < max_apdu) {
|
||||
tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest,
|
||||
&npdu_data, &Handler_Transmit_Buffer[0], pdu_len);
|
||||
bytes_sent =
|
||||
datalink_send_pdu(&dest, &npdu_data,
|
||||
&Handler_Transmit_Buffer[0], pdu_len);
|
||||
#if PRINT_ENABLED
|
||||
if (bytes_sent <= 0)
|
||||
fprintf(stderr,
|
||||
"Failed to Send Private Transfer Request (%s)!\n",
|
||||
strerror(errno));
|
||||
#endif
|
||||
} else {
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
invoke_id = 0;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"Failed to Send Private Transfer Request "
|
||||
"(exceeds destination maximum APDU)!\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return invoke_id;
|
||||
}
|
||||
Reference in New Issue
Block a user