Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5,878 changes: 2,939 additions & 2,939 deletions BACnetClient.cs

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions Base/BacnetWriteAccessSpecification.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace System.IO.BACnet;

public struct BacnetWriteAccessSpecification
{
public BacnetObjectId objectIdentifier;
public ICollection<BacnetPropertyValue> propertyValues;

public BacnetWriteAccessSpecification(BacnetObjectId objectIdentifier, ICollection<BacnetPropertyValue> propertyValues)
{
this.objectIdentifier = objectIdentifier;
this.propertyValues = propertyValues;
}
}
96 changes: 96 additions & 0 deletions Serialize/ASN1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1075,6 +1075,102 @@ public static int decode_read_access_specification(byte[] buffer, int offset, in
return len;
}

public static int decode_write_access_specification(BacnetAddress address, byte[] buffer, int offset, int apdu_len, out BacnetWriteAccessSpecification value)
{
var len = 0;

value = new BacnetWriteAccessSpecification();

/* Tag 0: Object ID */
if (!decode_is_context_tag(buffer, offset + len, 0))
return -1;
len++;
len += decode_object_id(buffer, offset + len, out value.objectIdentifier.type,
out value.objectIdentifier.instance);

/* Tag 1: sequence of WriteAccessSpecification */
if (!decode_is_opening_tag_number(buffer, offset + len, 1))
return -1;
len++; /* opening tag is only one octet */

/* properties */
var propertyIdAndValues = new List<BacnetPropertyValue>();
while (apdu_len - len > 1 && !decode_is_closing_tag_number(buffer, offset + len, 1))
{
var p_ref = new BacnetPropertyValue();

/* Tag 0: propertyIdentifier */
if (!IS_CONTEXT_SPECIFIC(buffer[offset + len]))
return -1;

len += decode_tag_number_and_value(buffer, offset + len, out var tagNumber, out var lenValue_type);
if (tagNumber != 0)
return -1;

/* Should be at least the unsigned value + 1 tag left */
if (len + lenValue_type >= apdu_len)
return -1;
len += decode_enumerated(buffer, offset + len, lenValue_type, out p_ref.property.propertyIdentifier);
/* Assume most probable outcome */
p_ref.property.propertyArrayIndex = BACNET_ARRAY_ALL;
/* Tag 1: Optional propertyArrayIndex */
if (IS_CONTEXT_SPECIFIC(buffer[offset + len]) && !IS_CLOSING_TAG(buffer[offset + len]))
{
var tmp = decode_tag_number_and_value(buffer, offset + len, out tagNumber, out lenValue_type);
if (tagNumber == 1)
{
len += tmp;
/* Should be at least the unsigned array index + 1 tag left */
if (len + lenValue_type >= apdu_len)
return -1;
len += decode_unsigned(buffer, offset + len, lenValue_type, out p_ref.property.propertyArrayIndex);
}
}

/* Tag 2: propertyValue */
len += decode_tag_number_and_value(buffer, offset + len, out tagNumber, out lenValue_type);
if (tagNumber != 2 || !decode_is_opening_tag(buffer, offset + len - 1))
return -1;

var bValues = new List<BacnetValue>();
while (!decode_is_closing_tag(buffer, offset + len))
{
var l = bacapp_decode_application_data(address, buffer, offset + len, apdu_len + offset,
value.objectIdentifier.type, (BacnetPropertyIds)p_ref.property.propertyIdentifier, out var bValue);
if (l <= 0) return -1;
len += l;
bValues.Add(bValue);
}
len++; /* closing tag 2 */
p_ref.value = bValues;

/* Tag 3: Optional priority */
p_ref.priority = (byte)BACNET_NO_PRIORITY;
if (IS_CONTEXT_SPECIFIC(buffer[offset + len]) && !IS_CLOSING_TAG(buffer[offset + len]))
{
var tmp = decode_tag_number_and_value(buffer, offset + len, out tagNumber, out lenValue_type);
if (tagNumber == 3)
{
len += tmp;
/* Should be at least the unsigned priority + 1 tag left */
if (len + lenValue_type >= apdu_len)
return -1;
len += decode_unsigned(buffer, offset + len, lenValue_type, out var priority);
p_ref.priority = (byte)priority;
}
}
propertyIdAndValues.Add(p_ref);
}

/* closing tag */
if (!decode_is_closing_tag_number(buffer, offset + len, 1))
return -1;
len++;

value.propertyValues = propertyIdAndValues;
return len;
}

public static int decode_device_obj_property_ref(byte[] buffer, int offset, int apdu_len, out BacnetDeviceObjectPropertyReference value)
{
var len = 0;
Expand Down
88 changes: 9 additions & 79 deletions Serialize/Services.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2397,91 +2397,21 @@ public static void EncodeCreateObjectAcknowledge(EncodeBuffer buffer, BacnetObje
ASN1.encode_application_object_id(buffer, objectId.type, objectId.instance);
}

public static int DecodeWritePropertyMultiple(BacnetAddress address, byte[] buffer, int offset, int apduLen, out BacnetObjectId objectId, out ICollection<BacnetPropertyValue> valuesRefs)
public static int DecodeWritePropertyMultiple(BacnetAddress address, byte[] buffer, int offset, int apduLen, out ICollection<BacnetWriteAccessSpecification> properties)
{
var len = 0;
objectId = new BacnetObjectId();
valuesRefs = null;

/* Context tag 0 - Object ID */
len += ASN1.decode_tag_number_and_value(buffer, offset + len, out var tagNumber, out var lenValue);
if (tagNumber == 0 && apduLen > len)
{
apduLen -= len;
if (apduLen >= 4)
{
len += ASN1.decode_object_id(buffer, offset + len, out objectId.type, out objectId.instance);
}
else
return -1;
}
else
return -1;

/* Tag 1: sequence of WriteAccessSpecification */
if (!ASN1.decode_is_opening_tag_number(buffer, offset + len, 1))
return -1;
len++;
var values = new List<BacnetWriteAccessSpecification>();
properties = null;

var _values = new LinkedList<BacnetPropertyValue>();
while (apduLen - len > 1)
while (apduLen - len > 0)
{
var newEntry = new BacnetPropertyValue();

/* tag 0 - Property Identifier */
len += ASN1.decode_tag_number_and_value(buffer, offset + len, out tagNumber, out lenValue);
uint propertyId;
if (tagNumber == 0)
len += ASN1.decode_enumerated(buffer, offset + len, lenValue, out propertyId);
else
return -1;

/* tag 1 - Property Array Index - optional */
var ulVal = ASN1.BACNET_ARRAY_ALL;
len += ASN1.decode_tag_number_and_value(buffer, offset + len, out tagNumber, out lenValue);
if (tagNumber == 1)
{
len += ASN1.decode_unsigned(buffer, offset + len, lenValue, out ulVal);
len += ASN1.decode_tag_number_and_value(buffer, offset + len, out tagNumber, out lenValue);
}
newEntry.property = new BacnetPropertyReference(propertyId, ulVal);

/* tag 2 - Property Value */
if (tagNumber == 2 && ASN1.decode_is_opening_tag(buffer, offset + len - 1))
{
var values = new List<BacnetValue>();
while (!ASN1.decode_is_closing_tag(buffer, offset + len))
{
var l = ASN1.bacapp_decode_application_data(address, buffer, offset + len, apduLen + offset, objectId.type, (BacnetPropertyIds)propertyId, out var value);
if (l <= 0) return -1;
len += l;
values.Add(value);
}
len++;
newEntry.value = values;
}
else
return -1;

/* tag 3 - Priority - optional */
ulVal = ASN1.BACNET_NO_PRIORITY;
len += ASN1.decode_tag_number_and_value(buffer, offset + len, out tagNumber, out lenValue);
if (tagNumber == 3)
len += ASN1.decode_unsigned(buffer, offset + len, lenValue, out ulVal);
else
len--;
newEntry.priority = (byte)ulVal;

_values.AddLast(newEntry);
var tmp = ASN1.decode_write_access_specification(address, buffer, offset + len, apduLen - len, out var value);
if (tmp < 0) return -1;
len += tmp;
values.Add(value);
}

/* Closing tag 1 - List of Properties */
if (!ASN1.decode_is_closing_tag_number(buffer, offset + len, 1))
return -1;
len++;

valuesRefs = _values;

properties = values;
return len;
}

Expand Down
20 changes: 18 additions & 2 deletions Storage/Property.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,14 @@ public static BacnetValue DeserializeValue(string value, BacnetApplicationTags t
case BacnetApplicationTags.BACNET_APPLICATION_TAG_ENUMERATED:
return new BacnetValue(type, uint.Parse(value));
case BacnetApplicationTags.BACNET_APPLICATION_TAG_DATE:
return new BacnetValue(type, DateTime.Parse(value));
// Format: yyyy/MM/dd (bacnet-stack compatible)
return new BacnetValue(type, DateTime.ParseExact(value, "yyyy/MM/dd", CultureInfo.InvariantCulture));
case BacnetApplicationTags.BACNET_APPLICATION_TAG_TIME:
return new BacnetValue(type, DateTime.Parse(value));
// Format: HH:mm:ss.hh where hh = hundredths (0-99) (bacnet-stack compatible)
return new BacnetValue(type, DateTime.ParseExact(value, "HH:mm:ss.ff", CultureInfo.InvariantCulture));
case BacnetApplicationTags.BACNET_APPLICATION_TAG_DATETIME:
// Format: yyyy/MM/dd-HH:mm:ss.hh where hh = hundredths (0-99) (bacnet-stack compatible)
return new BacnetValue(type, DateTime.ParseExact(value, "yyyy/MM/dd-HH:mm:ss.ff", CultureInfo.InvariantCulture));
case BacnetApplicationTags.BACNET_APPLICATION_TAG_OBJECT_ID:
return new BacnetValue(type, BacnetObjectId.Parse(value));
case BacnetApplicationTags.BACNET_APPLICATION_TAG_READ_ACCESS_SPECIFICATION:
Expand Down Expand Up @@ -100,6 +105,17 @@ public static string SerializeValue(BacnetValue value, BacnetApplicationTags typ
: string.Join(";", ((BacnetValue[])value.Value)
.Select(v => SerializeValue(v, v.Tag)));
}
case BacnetApplicationTags.BACNET_APPLICATION_TAG_DATE:
// Format: yyyy/MM/dd (bacnet-stack compatible)
return ((DateTime)value.Value).ToString("yyyy/MM/dd", CultureInfo.InvariantCulture);
case BacnetApplicationTags.BACNET_APPLICATION_TAG_TIME:
// Format: HH:mm:ss.hh where hh = hundredths (0-99) (bacnet-stack compatible)
return ((DateTime)value.Value).ToString("HH:mm:ss.", CultureInfo.InvariantCulture)
+ (((DateTime)value.Value).Millisecond / 10).ToString("D2", CultureInfo.InvariantCulture);
case BacnetApplicationTags.BACNET_APPLICATION_TAG_DATETIME:
// Format: yyyy/MM/dd-HH:mm:ss.hh where hh = hundredths (0-99) (bacnet-stack compatible)
return ((DateTime)value.Value).ToString("yyyy/MM/dd-HH:mm:ss.", CultureInfo.InvariantCulture)
+ (((DateTime)value.Value).Millisecond / 10).ToString("D2", CultureInfo.InvariantCulture);
default:
return value.Value.ToString();
}
Expand Down
Loading