created
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,345 @@
|
||||
/*####COPYRIGHTBEGIN####
|
||||
-------------------------------------------
|
||||
Copyright (C) 2005 Steve Karg
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
The Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA.
|
||||
|
||||
As a special exception, if other files instantiate templates or
|
||||
use macros or inline functions from this file, or you compile
|
||||
this file and link it with other works to produce a work based
|
||||
on this file, this file does not by itself cause the resulting
|
||||
work to be covered by the GNU General Public License. However
|
||||
the source code for this file must still be made available in
|
||||
accordance with section (3) of the GNU General Public License.
|
||||
|
||||
This exception does not invalidate any other reasons why a work
|
||||
based on this file might be covered by the GNU General Public
|
||||
License.
|
||||
-------------------------------------------
|
||||
####COPYRIGHTEND####*/
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define STRICT
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <winsock2.h>
|
||||
#include <stdint.h> // for standard integer types uint8_t etc.
|
||||
#include <stdbool.h> // for the standard bool type.
|
||||
#include "bacdcode.h"
|
||||
#include "bip.h"
|
||||
|
||||
#define close closesocket
|
||||
|
||||
static int BIP_Socket = -1;
|
||||
/* port to use - stored in network byte order */
|
||||
static uint16_t BIP_Port = 0;
|
||||
/* IP Address - stored in network byte order */
|
||||
static struct in_addr BIP_Address;
|
||||
/* Broadcast Address */
|
||||
static struct in_addr BIP_Broadcast_Address;
|
||||
/* Subnet Mask */
|
||||
static struct in_addr BIP_Subnet_Mask;
|
||||
|
||||
bool bip_valid(void)
|
||||
{
|
||||
return (BIP_Socket != -1);
|
||||
}
|
||||
|
||||
void bip_cleanup(void)
|
||||
{
|
||||
if (bip_valid())
|
||||
close(BIP_Socket);
|
||||
BIP_Socket = -1;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void set_network_address(struct in_addr *net_address,
|
||||
uint8_t octet1, uint8_t octet2, uint8_t octet3, uint8_t octet4)
|
||||
{
|
||||
union {
|
||||
uint8_t byte[4];
|
||||
uint32_t value;
|
||||
} long_data = {{0}};
|
||||
|
||||
long_data.byte[0] = octet1;
|
||||
long_data.byte[1] = octet2;
|
||||
long_data.byte[2] = octet3;
|
||||
long_data.byte[3] = octet4;
|
||||
|
||||
net_address->s_addr = htonl(long_data.value);
|
||||
}
|
||||
|
||||
void bip_set_address(uint8_t octet1, uint8_t octet2,
|
||||
uint8_t octet3, uint8_t octet4)
|
||||
{
|
||||
set_network_address(&BIP_Address, octet1, octet2, octet3, octet4);
|
||||
}
|
||||
|
||||
void bip_set_broadcast_address(uint8_t octet1, uint8_t octet2,
|
||||
uint8_t octet3, uint8_t octet4)
|
||||
{
|
||||
set_network_address(&BIP_Broadcast_Address,
|
||||
octet1, octet2, octet3, octet4);
|
||||
}
|
||||
|
||||
void bip_set_subnet_mask(uint8_t octet1, uint8_t octet2,
|
||||
uint8_t octet3, uint8_t octet4)
|
||||
{
|
||||
set_network_address(&BIP_Subnet_Mask,
|
||||
octet1, octet2, octet3, octet4);
|
||||
}
|
||||
|
||||
void bip_set_port(uint16_t port)
|
||||
{
|
||||
BIP_Port = htons(port);
|
||||
}
|
||||
|
||||
bool bip_init(void)
|
||||
{
|
||||
int rv = 0; // return from socket lib calls
|
||||
struct sockaddr_in sin = {-1};
|
||||
int value = 1;
|
||||
|
||||
/* local broadcast address */
|
||||
//BIP_Broadcast_Address.s_addr = BIP_Address.s_addr;
|
||||
//BIP_Broadcast_Address.s_addr |= ~(BIP_Subnet_Mask.s_addr);
|
||||
BIP_Broadcast_Address.s_addr = INADDR_BROADCAST;
|
||||
/* configure standard BACnet/IP port */
|
||||
bip_set_port(0xBAC0);
|
||||
|
||||
// assumes that the driver has already been initialized
|
||||
BIP_Socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (BIP_Socket < 0)
|
||||
return false;
|
||||
|
||||
// bind the socket to the local port number and IP address
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
sin.sin_port = BIP_Port;
|
||||
memset(&(sin.sin_zero), '\0', 8);
|
||||
rv = bind(BIP_Socket,
|
||||
(const struct sockaddr*)&sin, sizeof(struct sockaddr));
|
||||
if (rv < 0)
|
||||
{
|
||||
close(BIP_Socket);
|
||||
BIP_Socket = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* function to send a packet out the BACnet/IP socket (Annex J) */
|
||||
/* returns number of bytes sent on success, negative number on failure */
|
||||
static int bip_send(
|
||||
struct sockaddr_in *bip_dest,
|
||||
uint8_t *pdu, // any data to be sent - may be null
|
||||
unsigned pdu_len) // number of bytes of data
|
||||
{
|
||||
int bytes = 0;
|
||||
uint8_t mtu[MAX_MPDU] = { 0 };
|
||||
int mtu_len = 0;
|
||||
int i = 0;
|
||||
|
||||
// assumes that the driver has already been initialized
|
||||
if (BIP_Socket < 0)
|
||||
return BIP_Socket;
|
||||
|
||||
mtu[0] = 0x81; /* BVLL for BACnet/IP */
|
||||
if (bip_dest->sin_addr.s_addr == BIP_Broadcast_Address.s_addr)
|
||||
mtu[1] = 0x0B; /* Original-Broadcast-NPDU */
|
||||
else
|
||||
mtu[1] = 0x0A; /* Original-Unicast-NPDU */
|
||||
mtu_len = 2;
|
||||
mtu_len += encode_unsigned16(&mtu[mtu_len], pdu_len);
|
||||
memcpy(&mtu[mtu_len], pdu, pdu_len);
|
||||
mtu_len += pdu_len;
|
||||
|
||||
/* Send the packet */
|
||||
bytes = sendto(BIP_Socket, (char *)mtu, mtu_len, 0,
|
||||
(struct sockaddr *)bip_dest,
|
||||
sizeof(struct sockaddr));
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/* function to send a packet out the BACnet/IP socket (Annex J) */
|
||||
/* returns number of bytes sent on success, negative number on failure */
|
||||
int bip_send_pdu(
|
||||
BACNET_ADDRESS *dest, // destination address
|
||||
uint8_t *pdu, // any data to be sent - may be null
|
||||
unsigned pdu_len) // number of bytes of data
|
||||
{
|
||||
int i = 0; // counter
|
||||
struct sockaddr_in bip_dest;
|
||||
uint32_t network_address = 0;
|
||||
uint16_t network_port = 0;
|
||||
|
||||
/* load destination IP address */
|
||||
bip_dest.sin_family = AF_INET;
|
||||
if (dest->mac_len == 6)
|
||||
{
|
||||
(void)decode_unsigned32(&dest->mac[0], &(bip_dest.sin_addr.s_addr));
|
||||
(void)decode_unsigned16(&dest->mac[4], &(bip_dest.sin_port));
|
||||
memset(&(bip_dest.sin_zero), '\0', 8);
|
||||
}
|
||||
/* broadcast */
|
||||
else if (dest->mac_len == 0)
|
||||
{
|
||||
bip_dest.sin_addr.s_addr = BIP_Broadcast_Address.s_addr;
|
||||
bip_dest.sin_port = BIP_Port;
|
||||
memset(&(bip_dest.sin_zero), '\0', 8);
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
|
||||
/* function to send a packet out the BACnet/IP socket */
|
||||
/* returns 1 on success, 0 on failure */
|
||||
return bip_send(&bip_dest, // destination address
|
||||
pdu, // any data to be sent - may be null
|
||||
pdu_len); // number of bytes of data
|
||||
}
|
||||
|
||||
// receives a BACnet/IP packet
|
||||
// returns the number of octets in the PDU, or zero on failure
|
||||
uint16_t bip_receive(
|
||||
BACNET_ADDRESS *src, // source address
|
||||
uint8_t *pdu, // PDU data
|
||||
uint16_t max_pdu, // amount of space available in the PDU
|
||||
unsigned timeout) // number of milliseconds to wait for a packet
|
||||
{
|
||||
int received_bytes;
|
||||
uint8_t buf[MAX_MPDU] = {0}; // data
|
||||
uint16_t pdu_len = 0; // return value
|
||||
fd_set read_fds;
|
||||
int max;
|
||||
struct timeval select_timeout;
|
||||
struct sockaddr_in sin = {-1};
|
||||
int sin_len = sizeof(sin);
|
||||
|
||||
/* Make sure the socket is open */
|
||||
if (BIP_Socket < 0)
|
||||
return 0;
|
||||
|
||||
/* we could just use a non-blocking socket, but that consumes all
|
||||
the CPU time. We can use a timeout; it is only supported as
|
||||
a select. */
|
||||
if (timeout >= 1000)
|
||||
{
|
||||
select_timeout.tv_sec = timeout / 1000;
|
||||
select_timeout.tv_usec =
|
||||
1000 * (timeout - select_timeout.tv_sec * 1000);
|
||||
}
|
||||
else
|
||||
{
|
||||
select_timeout.tv_sec = 0;
|
||||
select_timeout.tv_usec = 1000 * timeout;
|
||||
}
|
||||
FD_ZERO(&read_fds);
|
||||
FD_SET(BIP_Socket, &read_fds);
|
||||
max = BIP_Socket;
|
||||
/* see if there is a packet for us */
|
||||
if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0)
|
||||
received_bytes = recvfrom(BIP_Socket,
|
||||
(char *)&buf[0], MAX_MPDU, 0,
|
||||
(struct sockaddr *)&sin, &sin_len);
|
||||
else
|
||||
return 0;
|
||||
|
||||
/* See if there is a problem */
|
||||
if (received_bytes < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* no problem, just no bytes */
|
||||
if (received_bytes == 0)
|
||||
return 0;
|
||||
|
||||
/* the signature of a BACnet/IP packet */
|
||||
if (buf[0] != 0x81)
|
||||
return 0;
|
||||
/* Original-Broadcast-NPDU or Original-Unicast-NPDU */
|
||||
if ((buf[1] == 0x0B) || (buf[1] == 0x0A))
|
||||
{
|
||||
// copy the source address
|
||||
src->mac_len = 6;
|
||||
(void)encode_unsigned32(&src->mac[0],
|
||||
sin.sin_addr.s_addr);
|
||||
(void)encode_unsigned16(&src->mac[4],
|
||||
sin.sin_port);
|
||||
// FIXME: check destination address
|
||||
// see if it is broadcast or for us
|
||||
/* decode the length of the PDU - length is inclusive of BVLC */
|
||||
(void)decode_unsigned16(&buf[2],&pdu_len);
|
||||
/* copy the buffer into the PDU */
|
||||
pdu_len -= 4; /* BVLC header */
|
||||
if (pdu_len < max_pdu)
|
||||
memmove(&pdu[0],&buf[4],pdu_len);
|
||||
// ignore packets that are too large
|
||||
// clients should check my max-apdu first
|
||||
else
|
||||
pdu_len = 0;
|
||||
}
|
||||
|
||||
return pdu_len;
|
||||
}
|
||||
|
||||
void bip_get_my_address(BACNET_ADDRESS *my_address)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
my_address->mac_len = 6;
|
||||
(void)encode_unsigned32(&my_address->mac[0],
|
||||
BIP_Address.s_addr);
|
||||
(void)encode_unsigned16(&my_address->mac[4],
|
||||
BIP_Port);
|
||||
my_address->net = 0; /* local only, no routing */
|
||||
my_address->len = 0; /* no SLEN */
|
||||
for (i = 0; i < MAX_MAC_LEN; i++)
|
||||
{
|
||||
/* no SADR */
|
||||
my_address->adr[i] = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void bip_get_broadcast_address(
|
||||
BACNET_ADDRESS *dest) // destination address
|
||||
{
|
||||
int i = 0; // counter
|
||||
|
||||
if (dest)
|
||||
{
|
||||
dest->mac_len = 6;
|
||||
(void)encode_unsigned32(&dest->mac[0],
|
||||
BIP_Broadcast_Address.s_addr);
|
||||
(void)encode_unsigned16(&dest->mac[4],
|
||||
BIP_Port);
|
||||
dest->net = BACNET_BROADCAST_NETWORK;
|
||||
dest->len = 0; /* no SLEN */
|
||||
for (i = 0; i < MAX_MAC_LEN; i++)
|
||||
{
|
||||
/* no SADR */
|
||||
dest->adr[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
// This is one way to use the embedded BACnet stack under Win32
|
||||
// compiled with Borland C++ 5.02
|
||||
#include <winsock2.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "config.h"
|
||||
#include "bacdef.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "handlers.h"
|
||||
#include "bip.h"
|
||||
|
||||
// buffer used for receive
|
||||
static uint8_t Rx_Buf[MAX_MPDU] = {0};
|
||||
|
||||
static BYTE TargetIP[] = {192, 168, 0, 191};
|
||||
static BYTE NetMask[] = {255, 255, 255, 0};
|
||||
|
||||
static void Init_Device_Parameters(void)
|
||||
{
|
||||
// configure my initial values
|
||||
Device_Set_Object_Instance_Number(126);
|
||||
Device_Set_Vendor_Name("Lithonia Lighting");
|
||||
Device_Set_Vendor_Identifier(42);
|
||||
Device_Set_Model_Name("Simple BACnet Server");
|
||||
Device_Set_Firmware_Revision("1.00");
|
||||
Device_Set_Application_Software_Version("none");
|
||||
Device_Set_Description("Example of a simple BACnet server");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void Init_Service_Handlers(void)
|
||||
{
|
||||
// we need to handle who-is to support dynamic device binding
|
||||
apdu_set_unconfirmed_handler(
|
||||
SERVICE_UNCONFIRMED_WHO_IS,
|
||||
WhoIsHandler);
|
||||
// set the handler for all the services we don't implement
|
||||
// It is required to send the proper reject message...
|
||||
apdu_set_unrecognized_service_handler_handler(
|
||||
UnrecognizedServiceHandler);
|
||||
// we must implement read property - it's required!
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_READ_PROPERTY,
|
||||
ReadPropertyHandler);
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_WRITE_PROPERTY,
|
||||
WritePropertyHandler);
|
||||
}
|
||||
|
||||
static void NetInitialize(void)
|
||||
// initialize the TCP/IP stack
|
||||
{
|
||||
int Result;
|
||||
int Code;
|
||||
WSADATA wd;
|
||||
|
||||
Result = WSAStartup(MAKEWORD(2,2), &wd);
|
||||
|
||||
if (Result != 0)
|
||||
{
|
||||
Code = WSAGetLastError();
|
||||
printf("TCP/IP stack initialization failed, error code: %i\n",
|
||||
Code);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
BACNET_ADDRESS src = {0}; // address where message came from
|
||||
uint16_t pdu_len = 0;
|
||||
unsigned timeout = 100; // milliseconds
|
||||
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
Init_Device_Parameters();
|
||||
Init_Service_Handlers();
|
||||
// init the data link layer
|
||||
NetInitialize();
|
||||
bip_set_address(TargetIP[0], TargetIP[1], TargetIP[2], TargetIP[3]);
|
||||
bip_set_address(NetMask[0], NetMask[1], NetMask[2], NetMask[3]);
|
||||
if (!bip_init())
|
||||
return 1;
|
||||
|
||||
printf("BACnet stack running...\n");
|
||||
// loop forever
|
||||
for (;;)
|
||||
{
|
||||
// input
|
||||
|
||||
// returns 0 bytes on timeout
|
||||
pdu_len = bip_receive(
|
||||
&src,
|
||||
&Rx_Buf[0],
|
||||
MAX_MPDU,
|
||||
timeout);
|
||||
|
||||
// process
|
||||
|
||||
if (pdu_len)
|
||||
{
|
||||
npdu_handler(
|
||||
&src,
|
||||
&Rx_Buf[0],
|
||||
pdu_len);
|
||||
}
|
||||
if (I_Am_Request)
|
||||
{
|
||||
I_Am_Request = false;
|
||||
Send_IAm();
|
||||
}
|
||||
// output
|
||||
|
||||
// blink LEDs, Turn on or off outputs, etc
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef STDBOOL_H
|
||||
#define STDBOOL_H
|
||||
|
||||
// C99 Boolean types for compilers without C99 support
|
||||
|
||||
#ifndef __cplusplus
|
||||
typedef int _Bool;
|
||||
#ifndef bool
|
||||
#define bool _Bool
|
||||
#endif
|
||||
#ifndef true
|
||||
#define true 1
|
||||
#endif
|
||||
#ifndef false
|
||||
#define false 0
|
||||
#endif
|
||||
#define __bool_true_false_are_defined 1
|
||||
#endif
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
// Defines the standard integer types that are used in code
|
||||
// for the x86 processor and Borland Compiler
|
||||
|
||||
#ifndef STDINT_H
|
||||
#define STDINT_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
typedef unsigned char uint8_t; // 1 byte 0 to 255
|
||||
typedef signed char int8_t; // 1 byte -127 to 127
|
||||
typedef unsigned short uint16_t; // 2 bytes 0 to 65535
|
||||
typedef signed short int16_t; // 2 bytes -32767 to 32767
|
||||
//typedef unsigned short long uint24_t; // 3 bytes 0 to 16777215
|
||||
typedef unsigned long uint32_t; // 4 bytes 0 to 4294967295
|
||||
typedef signed long int32_t; // 4 bytes -2147483647 to 2147483647
|
||||
// typedef signed long long int64_t;
|
||||
// typedef unsigned long long uint64_t;
|
||||
|
||||
#endif // STDINT_H
|
||||
Reference in New Issue
Block a user