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:
Steve Karg
2024-01-29 09:41:40 -06:00
committed by GitHub
parent 34b1d24bb7
commit 6cb875aae6
20 changed files with 2342 additions and 91 deletions
+1 -4
View File
@@ -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(
+1
View File
@@ -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 \
+1
View File
@@ -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
+40
View File
@@ -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
View File
@@ -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();
}
+1
View File
@@ -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 \