Feature/add bacnet binary lighting object (#522)
* Added Binary Lighting Output object example. * Changed piface example app to support binary-lighting-output object type and blink warn * Changed example device object to not create objects when device object-table is overridden * Fixed unit testing for device object
This commit is contained in:
+1
-4
@@ -118,9 +118,6 @@ static void Init_Service_Handlers(void)
|
||||
/* handle communication so we can shutup when asked */
|
||||
apdu_set_confirmed_handler(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL,
|
||||
handler_device_communication_control);
|
||||
/* handle the data coming back from private requests */
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_PRIVATE_TRANSFER,
|
||||
handler_unconfirmed_private_transfer);
|
||||
/* configure the cyclic timers */
|
||||
mstimer_set(&BACnet_Task_Timer, 1000UL);
|
||||
mstimer_set(&BACnet_TSM_Timer, 50UL);
|
||||
@@ -297,7 +294,7 @@ static void bacnet_output_init(void)
|
||||
Channel_Reference_List_Member_Element_Set(
|
||||
light_channel_instance, 1 + i, &member);
|
||||
|
||||
object_instance++;
|
||||
object_instance = 1 + i;
|
||||
}
|
||||
Color_Write_Present_Value_Callback_Set(Color_Write_Value_Handler);
|
||||
Color_Temperature_Write_Present_Value_Callback_Set(
|
||||
|
||||
@@ -15,6 +15,7 @@ BACNET_OBJECT_SRC := \
|
||||
$(BACNET_OBJECT_DIR)/ao.c \
|
||||
$(BACNET_OBJECT_DIR)/av.c \
|
||||
$(BACNET_OBJECT_DIR)/bi.c \
|
||||
$(BACNET_OBJECT_DIR)/blo.c \
|
||||
$(BACNET_OBJECT_DIR)/bo.c \
|
||||
$(BACNET_OBJECT_DIR)/bv.c \
|
||||
$(BACNET_OBJECT_DIR)/channel.c \
|
||||
|
||||
@@ -10,6 +10,7 @@ SRC = main.c \
|
||||
device.c \
|
||||
$(BACNET_OBJECT_DIR)/netport.c \
|
||||
$(BACNET_OBJECT_DIR)/bi.c \
|
||||
$(BACNET_OBJECT_DIR)/blo.c \
|
||||
$(BACNET_OBJECT_DIR)/bo.c
|
||||
|
||||
CFLAGS += -DMAX_TSM_TRANSACTIONS=1
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
/* include the OS specific */
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacnet/basic/object/bi.h"
|
||||
#include "bacnet/basic/object/blo.h"
|
||||
#include "bacnet/basic/object/bo.h"
|
||||
#if (BACNET_PROTOCOL_REVISION >= 17)
|
||||
#include "bacnet/basic/object/netport.h"
|
||||
@@ -88,6 +89,18 @@ static object_functions_t My_Object_Table[] = {
|
||||
Binary_Input_Change_Of_Value_Clear, NULL /* Intrinsic Reporting */,
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
|
||||
NULL /* Create */, NULL /* Delete */, NULL /* Timer */ },
|
||||
{ OBJECT_BINARY_LIGHTING_OUTPUT, Binary_Lighting_Output_Init,
|
||||
Binary_Lighting_Output_Count, Binary_Lighting_Output_Index_To_Instance,
|
||||
Binary_Lighting_Output_Valid_Instance,
|
||||
Binary_Lighting_Output_Object_Name,
|
||||
Binary_Lighting_Output_Read_Property,
|
||||
Binary_Lighting_Output_Write_Property,
|
||||
Binary_Lighting_Output_Property_Lists, NULL /* ReadRangeInfo */,
|
||||
NULL /* Iterator */, NULL /* Value_Lists */, NULL /* COV */,
|
||||
NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
|
||||
Binary_Lighting_Output_Create, Binary_Lighting_Output_Delete,
|
||||
Binary_Lighting_Output_Timer },
|
||||
{ OBJECT_BINARY_OUTPUT, Binary_Output_Init, Binary_Output_Count,
|
||||
Binary_Output_Index_To_Instance, Binary_Output_Valid_Instance,
|
||||
Binary_Output_Object_Name, Binary_Output_Read_Property,
|
||||
@@ -1979,3 +1992,30 @@ bool DeviceGetRRInfo(BACNET_READ_RANGE_DATA *pRequest, /* Info on the request */
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Updates all the object timers with elapsed milliseconds
|
||||
* @param milliseconds - number of milliseconds elapsed
|
||||
*/
|
||||
void Device_Timer(uint16_t milliseconds)
|
||||
{
|
||||
struct object_functions *pObject;
|
||||
unsigned count = 0;
|
||||
uint32_t instance;
|
||||
|
||||
pObject = Object_Table;
|
||||
while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) {
|
||||
if (pObject->Object_Count) {
|
||||
count = pObject->Object_Count();
|
||||
}
|
||||
while (count) {
|
||||
count--;
|
||||
if ((pObject->Object_Timer) &&
|
||||
(pObject->Object_Index_To_Instance)) {
|
||||
instance = pObject->Object_Index_To_Instance(count);
|
||||
pObject->Object_Timer(instance, milliseconds);
|
||||
}
|
||||
}
|
||||
pObject++;
|
||||
}
|
||||
}
|
||||
|
||||
+150
-67
@@ -1,33 +1,17 @@
|
||||
/**************************************************************************
|
||||
/**
|
||||
* @file
|
||||
* @brief
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date January 2023
|
||||
*
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/basic/binding/address.h"
|
||||
@@ -39,40 +23,117 @@
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/iam.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacnet/basic/object/bacfile.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
#include "bacnet/dcc.h"
|
||||
#include "bacnet/getevent.h"
|
||||
#include "bacport.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/basic/sys/mstimer.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/version.h"
|
||||
/* include the device object */
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacnet/basic/object/bi.h"
|
||||
#include "bacnet/basic/object/blo.h"
|
||||
#include "bacnet/basic/object/bo.h"
|
||||
#include "pifacedigital.h"
|
||||
|
||||
/** @file server/main.c Example server application using the BACnet Stack. */
|
||||
|
||||
/* (Doxygen note: The next two lines pull all the following Javadoc
|
||||
* into the ServerDemo module.) */
|
||||
/** @addtogroup ServerDemo */
|
||||
/*@{*/
|
||||
/* number of binary outputs on the PiFace card */
|
||||
#define PIFACE_OUTPUTS_MAX 8
|
||||
|
||||
/** Buffer used for receiving */
|
||||
static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
|
||||
|
||||
/* current version of the BACnet stack */
|
||||
static const char *BACnet_Version = BACNET_VERSION_TEXT;
|
||||
/* task timer for various BACnet timeouts */
|
||||
static struct mstimer BACnet_Task_Timer;
|
||||
/* task timer for TSM timeouts */
|
||||
static struct mstimer BACnet_TSM_Timer;
|
||||
/* task timer for address binding timeouts */
|
||||
static struct mstimer BACnet_Address_Timer;
|
||||
/* task timer for object functionality */
|
||||
static struct mstimer BACnet_Object_Timer;
|
||||
/* track the state of of the output */
|
||||
static bool PiFace_Output_State[PIFACE_OUTPUTS_MAX];
|
||||
|
||||
/**
|
||||
* @brief output write value request
|
||||
* @param index - 0..N index of the output
|
||||
* @param value - value of the write ON or OFF
|
||||
*/
|
||||
static void piface_write_output(int index, BACNET_BINARY_LIGHTING_PV value)
|
||||
{
|
||||
if (index < PIFACE_OUTPUTS_MAX) {
|
||||
if (value == BINARY_LIGHTING_PV_OFF) {
|
||||
pifacedigital_digital_write(index, 0);
|
||||
PiFace_Output_State[index] = false;
|
||||
printf("OUTPUT[%u]=OFF\n", index);
|
||||
} else if (value == BINARY_LIGHTING_PV_ON) {
|
||||
pifacedigital_digital_write(index, 1);
|
||||
PiFace_Output_State[index] = true;
|
||||
printf("OUTPUT[%u]=ON\n", index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Callback for write value request
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @param old_value - value prior to write
|
||||
* @param value - value of the write
|
||||
*/
|
||||
static void Binary_Lighting_Output_Write_Value_Handler(uint32_t object_instance,
|
||||
BACNET_BINARY_LIGHTING_PV old_value,
|
||||
BACNET_BINARY_LIGHTING_PV value)
|
||||
{
|
||||
unsigned index;
|
||||
|
||||
index = Binary_Lighting_Output_Instance_To_Index(object_instance);
|
||||
if (index < PIFACE_OUTPUTS_MAX) {
|
||||
printf("BLO-WRITE: OUTPUT[%u]=%d present=%d feedback=%d target=%d\n",
|
||||
index, (int)value,
|
||||
(int)Binary_Lighting_Output_Present_Value(object_instance),
|
||||
(int)old_value,
|
||||
(int)Binary_Lighting_Output_Lighting_Command_Target_Value(
|
||||
object_instance));
|
||||
piface_write_output(index, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Callback for blink warning notification
|
||||
* @param object_instance - object-instance number of the object
|
||||
*/
|
||||
static void Binary_Lighting_Output_Blink_Warn_Handler(uint32_t object_instance)
|
||||
{
|
||||
unsigned index;
|
||||
|
||||
index = Binary_Lighting_Output_Instance_To_Index(object_instance);
|
||||
if (index < PIFACE_OUTPUTS_MAX) {
|
||||
/* blink is just toggle on/off every one second */
|
||||
if (PiFace_Output_State[index]) {
|
||||
printf("BLO-BLINK: OUTPUT[%u]=%d\n", index,
|
||||
BINARY_LIGHTING_PV_OFF);
|
||||
piface_write_output(index, BINARY_LIGHTING_PV_OFF);
|
||||
} else {
|
||||
printf(
|
||||
"BLO-BLINK: OUTPUT[%u]=%d\n", index, BINARY_LIGHTING_PV_ON);
|
||||
piface_write_output(index, BINARY_LIGHTING_PV_ON);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Initialize the handlers we will utilize.
|
||||
* @see Device_Init, apdu_set_unconfirmed_handler, apdu_set_confirmed_handler
|
||||
*/
|
||||
static void Init_Service_Handlers(void)
|
||||
{
|
||||
uint32_t i = 0;
|
||||
uint32_t object_instance;
|
||||
|
||||
Device_Init(NULL);
|
||||
/* we need to handle who-is to support dynamic device binding */
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is);
|
||||
@@ -107,9 +168,21 @@ static void Init_Service_Handlers(void)
|
||||
/* handle communication so we can shutup when asked */
|
||||
apdu_set_confirmed_handler(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL,
|
||||
handler_device_communication_control);
|
||||
/* handle the data coming back from private requests */
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_PRIVATE_TRANSFER,
|
||||
handler_unconfirmed_private_transfer);
|
||||
/* configure the cyclic timers */
|
||||
mstimer_set(&BACnet_Task_Timer, 1000UL);
|
||||
mstimer_set(&BACnet_TSM_Timer, 50UL);
|
||||
mstimer_set(&BACnet_Address_Timer, 60UL * 1000UL);
|
||||
mstimer_set(&BACnet_Object_Timer, 1000UL);
|
||||
/* create some objects */
|
||||
for (i = 0; i < PIFACE_OUTPUTS_MAX; i++) {
|
||||
object_instance = 1 + i;
|
||||
Binary_Lighting_Output_Create(object_instance);
|
||||
Binary_Output_Create(object_instance);
|
||||
}
|
||||
Binary_Lighting_Output_Write_Value_Callback_Set(
|
||||
Binary_Lighting_Output_Write_Value_Handler);
|
||||
Binary_Lighting_Output_Blink_Warn_Callback_Set(
|
||||
Binary_Lighting_Output_Blink_Warn_Handler);
|
||||
}
|
||||
|
||||
static void piface_init(void)
|
||||
@@ -159,6 +232,7 @@ static void piface_task(void)
|
||||
unsigned i = 0;
|
||||
BACNET_BINARY_PV present_value = BINARY_INACTIVE;
|
||||
bool pin_status = false;
|
||||
uint32_t object_instance;
|
||||
|
||||
for (i = 0; i < MAX_BINARY_INPUTS; i++) {
|
||||
if (!Binary_Input_Out_Of_Service(i)) {
|
||||
@@ -181,13 +255,24 @@ static void piface_task(void)
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i = 0; i < MAX_BINARY_OUTPUTS; i++) {
|
||||
if (!Binary_Output_Out_Of_Service(i)) {
|
||||
present_value = Binary_Output_Present_Value(i);
|
||||
if (present_value == BINARY_INACTIVE) {
|
||||
pifacedigital_digital_write(i, 0);
|
||||
} else {
|
||||
pifacedigital_digital_write(i, 1);
|
||||
for (i = 0; i < PIFACE_OUTPUTS_MAX; i++) {
|
||||
object_instance = Binary_Output_Index_To_Instance(i);
|
||||
if (Binary_Output_Valid_Instance(object_instance)) {
|
||||
if (!Binary_Output_Out_Of_Service(object_instance)) {
|
||||
present_value = Binary_Output_Present_Value(object_instance);
|
||||
if (present_value == BINARY_INACTIVE) {
|
||||
if (PiFace_Output_State[i]) {
|
||||
printf("BO-WRITE: OUTPUT[%u]=%d\n", i,
|
||||
BINARY_LIGHTING_PV_OFF);
|
||||
piface_write_output(i, BINARY_LIGHTING_PV_OFF);
|
||||
}
|
||||
} else {
|
||||
if (!PiFace_Output_State[i]) {
|
||||
printf("BO-WRITE: OUTPUT[%u]=%d\n", i,
|
||||
BINARY_LIGHTING_PV_OFF);
|
||||
piface_write_output(i, BINARY_LIGHTING_PV_ON);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -209,12 +294,9 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
BACNET_ADDRESS src = { 0 }; /* address where message came from */
|
||||
uint16_t pdu_len = 0;
|
||||
unsigned timeout = 1; /* milliseconds */
|
||||
time_t last_seconds = 0;
|
||||
time_t current_seconds = 0;
|
||||
uint32_t elapsed_seconds = 0;
|
||||
uint32_t elapsed_milliseconds = 0;
|
||||
uint32_t address_binding_tmr = 0;
|
||||
unsigned timeout_ms = 1; /* milliseconds */
|
||||
unsigned long seconds = 0;
|
||||
unsigned long milliseconds;
|
||||
|
||||
/* allow the device ID to be set */
|
||||
if (argc > 1) {
|
||||
@@ -233,41 +315,42 @@ int main(int argc, char *argv[])
|
||||
atexit(datalink_cleanup);
|
||||
piface_init();
|
||||
atexit(piface_cleanup);
|
||||
/* configure the timeout values */
|
||||
last_seconds = time(NULL);
|
||||
/* broadcast an I-Am on startup */
|
||||
Send_I_Am(&Handler_Transmit_Buffer[0]);
|
||||
/* loop forever */
|
||||
for (;;) {
|
||||
/* input */
|
||||
current_seconds = time(NULL);
|
||||
|
||||
/* returns 0 bytes on timeout */
|
||||
pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout);
|
||||
|
||||
pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout_ms);
|
||||
/* process */
|
||||
if (pdu_len) {
|
||||
npdu_handler(&src, &Rx_Buf[0], pdu_len);
|
||||
}
|
||||
/* at least one second has passed */
|
||||
elapsed_seconds = (uint32_t)(current_seconds - last_seconds);
|
||||
if (elapsed_seconds) {
|
||||
last_seconds = current_seconds;
|
||||
dcc_timer_seconds(elapsed_seconds);
|
||||
datalink_maintenance_timer(elapsed_seconds);
|
||||
dlenv_maintenance_timer(elapsed_seconds);
|
||||
elapsed_milliseconds = elapsed_seconds * 1000;
|
||||
handler_cov_timer_seconds(elapsed_seconds);
|
||||
tsm_timer_milliseconds(elapsed_milliseconds);
|
||||
if (mstimer_expired(&BACnet_Task_Timer)) {
|
||||
mstimer_reset(&BACnet_Task_Timer);
|
||||
/* 1 second tasks */
|
||||
dcc_timer_seconds(1);
|
||||
datalink_maintenance_timer(1);
|
||||
dlenv_maintenance_timer(1);
|
||||
handler_cov_timer_seconds(1);
|
||||
}
|
||||
if (mstimer_expired(&BACnet_TSM_Timer)) {
|
||||
mstimer_reset(&BACnet_TSM_Timer);
|
||||
tsm_timer_milliseconds(mstimer_interval(&BACnet_TSM_Timer));
|
||||
}
|
||||
handler_cov_task();
|
||||
/* scan cache address */
|
||||
address_binding_tmr += elapsed_seconds;
|
||||
if (address_binding_tmr >= 60) {
|
||||
address_cache_timer(address_binding_tmr);
|
||||
address_binding_tmr = 0;
|
||||
if (mstimer_expired(&BACnet_Address_Timer)) {
|
||||
mstimer_reset(&BACnet_Address_Timer);
|
||||
/* address cache */
|
||||
seconds = mstimer_interval(&BACnet_Address_Timer) / 1000;
|
||||
address_cache_timer(seconds);
|
||||
}
|
||||
/* output/input */
|
||||
if (mstimer_expired(&BACnet_Object_Timer)) {
|
||||
mstimer_reset(&BACnet_Object_Timer);
|
||||
milliseconds = mstimer_interval(&BACnet_Object_Timer);
|
||||
Device_Timer(milliseconds);
|
||||
}
|
||||
piface_task();
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ SRC = main.c \
|
||||
$(BACNET_OBJECT_DIR)/av.c \
|
||||
$(BACNET_OBJECT_DIR)/bi.c \
|
||||
$(BACNET_OBJECT_DIR)/bo.c \
|
||||
$(BACNET_OBJECT_DIR)/blo.c \
|
||||
$(BACNET_OBJECT_DIR)/bv.c \
|
||||
$(BACNET_OBJECT_DIR)/channel.c \
|
||||
$(BACNET_OBJECT_DIR)/color_object.c \
|
||||
|
||||
Reference in New Issue
Block a user