Closed spadilam closed 6 years ago
1) The listOfProperties
parameter of a WriteAccessSpecification
is a sequence of PropertyValue
, not a PropertyReference
.
2) After you parse the value, it needs to go into the PropertyValue
like prop_value.value = value
, it's not a parameter of the request.
3) It looks like you are missing the priority.
It might make it simpler to understand if you parse the parameters in the same order as they are in the request, i.e., property identifier followed by the optional array index, then the value, then optionally the priority. Once you are happy with the way parsing a PropertyValue
is working, you can nest that in a loop for more than one of them, then parse the object identifier off the front and build a WriteAccessSpecification
, then loop around that for multiple objects.
Thanks for the info ... Your suggestions helped us find the below code.
''' def do_writemultiple(self, args):
args = args.split()
try:
i = 0
addr = args[i]
i += 1
write_access_spec_list = []
write_values_list = []
while i < len(args):
obj_type = args[i]
i += 1
if obj_type.isdigit():
obj_type = int(obj_type)
elif not get_object_class(obj_type):
raise ValueError("unknown object type")
obj_inst = int(args[i])
i += 1
prop_value_list = []
while i < len(args):
prop_id = args[i]
if prop_id not in PropertyIdentifier.enumerations:
break
if prop_id in ('all', 'required', 'optional'):
pass
else:
datatype = get_datatype(obj_type, prop_id)
if not datatype:
raise ValueError("invalid property for object type")
i += 1
# get the property value from args
value = args[i]
# get the datatype
datatype = get_datatype(obj_type, args[i-1])
# increment for the next property
i += 1
# change atomic values into something encodeable, null is a special case
if value == 'null':
value = Null()
elif issubclass(datatype, AnyAtomic):
dtype, dvalue = value.split(':')
datatype = {
'b': Boolean,
'u': lambda x: Unsigned(int(x)),
'i': lambda x: Integer(int(x)),
'r': lambda x: Real(float(x)),
'd': lambda x: Double(float(x)),
'o': OctetString,
'c': CharacterString,
'bs': BitString,
'date': Date,
'time': Time,
}[dtype]
value = datatype(dvalue)
elif issubclass(datatype, Atomic):
if datatype is Integer:
value = int(value)
elif datatype is Real:
value = float(value)
elif datatype is Unsigned:
value = int(value)
value = datatype(value)
value = Any(value)
elif not isinstance(value, datatype):
raise TypeError("invalid result datatype, expecting %s" % (datatype.__name__,))
# build a property reference
prop_value = PropertyValue(propertyIdentifier=prop_id, value=value, priority=1)
# add it to the list
prop_value_list.append(prop_value)
# check for an array index
if (i < len(args)) and args[i].isdigit():
prop_value.propertyArrayIndex = int(args[i])
# check for at least one property
if not prop_value_list:
raise ValueError("provide at least one property")
# build a read access specification
write_access_spec = WriteAccessSpecification(
objectIdentifier=(obj_type, obj_inst),
listOfProperties=prop_value_list,
)
# add it to the list
write_access_spec_list.append(write_access_spec)
# add values to list
write_values_list.append(value)
# check for at least one
if not write_access_spec_list:
raise RuntimeError("at least one read access specification required")
# build the request
request = WritePropertyMultipleRequest(
listOfWriteAccessSpecs=write_access_spec_list,
)
request.pduDestination = Address(addr)
# save the value
request.propertyValue = Any()
# make an IOCB
iocb = IOCB(request)
deferred(self.this_application.request_io, iocb)
# give it to the application
self.this_application.request_io(iocb)
# wait for it to complete
iocb.wait()
# do something for success
if iocb.ioResponse:
apdu = iocb.ioResponse
# should be an ack
if not isinstance(apdu, SimpleAckPDU):
return
return 'ack'
# do something for error/reject/abort
if iocb.ioError:
print(str(iocb.ioError) + '\n')
except Exception as error:
print("exception: %r", error)
'''
Hi @spadilam @JoelBender , I am currently working on the Writepropertymultiple and i have taken the above as a reference to implement it but i am getting an Attribute Error : object has no attribute 'split' in args = args.split()
So please suggest on these
Print out the contents of args like print(type(args), args)
and see what is being passed into the function.
Hi , @JoelBender I got this when I print(type(args), args) <class 'bacpypes.apdu.WritePropertyMultipleRequest'> <bacpypes.apdu.WritePropertyMultipleRequest(16,1) instance at 0xb5ce5b08>
From the client side, I am sending the two Objects like ['analogOutput 1 presentValue 33','binaryOutput 1 presentValue inactive']
please tell me to access the values of the particular property (ex:presentValue) of request where I can access objectIdentifier and propertyIdentifier but not the propertyValue.So please help me to find out access the value
Good start, now check out the class definition here and you'll see that it is composed of a single element, a list of WriteAccessSpecification
, which contains an objectIdentifier
. So try:
for i, elem in enumerate(apdu.listOfWriteAccessSpecs):
print(i, ":", elem.objectIdentifier)
and you'll see that piece of the puzzle. Keep digging!
I am able to get the details of objectIdentifier,propertyIdentifier,propertyArrayIndex and priority but not the propertyValue,So kindly suggest me how to access the propertyValue from the WriteMultiplePropertyRequest
FYI
def do_WritePropertyMultipleRequest(self, apdu):
if apdu.serviceChoice == 16:
print("in writemultiple")
if isinstance(apdu, WritePropertyMultipleRequest):
print("inside if")
print(f"APDU = {apdu}")
write_access_specs = apdu.listOfWriteAccessSpecs
for write_access_spec in write_access_specs:
object_id = write_access_spec.objectIdentifier
obj = self.get_object_id(object_id)
obj_type,obj_inst = object_id
for prop_reference in write_access_spec.listOfProperties:
obj_count_wm += 1
property_id = prop_reference.propertyIdentifier
property_value = prop_reference.value
property_arrIndex = prop_reference.propertyArrayIndex
property_priority = prop_reference.priority
For propertyValue when I tried to print I got an Value like <bacpypes.constructeddata.Any object at 0xb52c3598> and I couldn't able to decipher it
Any is a challenge because it can be anything! It could be a simple thing, like an integer or boolean, or a list of things, or "context encoded" blob of bits that you need to know more. Inside the Any object is a tagList
which is a list of "tags" used to encode primitive data standardized in BACnet, or "open" and "close" tags that act like the beginning and end of a structure, or just some blob with a context number.
If you look in the standard you'll see that most of the property values of objects are the same type, like the Object_Name is always a character string. But the Present_Value property depends on the object type, so when you get the value back it can be anything, and the ReadProperty service is designed to read anything. The tag list has a debug_contents()
method that can help.
Thanks @JoelBender for the suggestion
Apologies for the late reply
Regarding the WriteMultipleProperty, I am able to get the "propertyValue" and able to send the response back to the client..
Team we are trying to write writemutliple service by referring to the writeproperty and readmultipleproperty, but no luck. Below is the code used to achieve write multiple.