I am using System.IO.BACnet library for my C# BACNet client.
I am trying to send to server "ReadPropertyMultiple" request, however I am unable to read non-array BACNet properties, since the System.IO.BACnet.BacnetClient.ReadPropertyMultipleRequest requires list of System.IO.BACnet.BacnetPropertyReference(s) ...
public bool ReadPropertyMultipleRequest(BacnetAddress address, BacnetObjectId objectId, IList<BacnetPropertyReference> propertyIdAndArrayIndex, out IList<BacnetReadAccessResult> values, byte invokeId = 0);
.. and the System.IO.BACnet.BacnetPropertyReference requires propertyArrayIndex ..
public struct BacnetPropertyReference
{
public uint propertyIdentifier;
public uint propertyArrayIndex;
public BacnetPropertyReference(uint id, uint arrayIndex);
public BacnetPropertyIds GetPropertyId();
public override string ToString();
}
.. which, when not set, defaults to 0. This causes, that after request with this list is sent, all properties are requested with propertyArrayIndex: 0, which fail for non-array object properties.
Example:
Request:
Response:
What is the right way to not add the propertyArrayIndex into request and thus be able to read non-array properties with ReadPropertyMultiple request?
If you request array-index "0" you should (in theory) receive the number of (following) elements, and not the full list of values (/remaining elements containing values).
Can't you use (the) 'Read-Property' (service) for non-array items (?).
The solution is to set propertyArrayIndex = uint.MaxValue.
For example:
BacnetPropertyReference reff = new BacnetPropertyReference((uint) BacnetPropertyIds.PROP_PRESENT_VALUE, uint.MaxValue);
Related
DTO looks like this:
public List<IFormFile> Images { get; set; }
public int[] Numbers { get; set; }
Method Signature:
public async Task<IActionResult> AddSection([FromForm]TheDto theDto)
Add the form data in React:
const handleSubmit = (data: any) => {
const formData = new FormData();
for(const name in data) {
formData.append(name, data[name]);
}
postService.addSection(formData);
}
It's then sent using axios.
Other values go through fine which are not in an array. I've inspected the data being added to FormData and the data is correct, I have 2 arrays, one holding File[], the other holding number[] and the names match that of the DTO so I'm now at a loss. Have I missed something?
I've used the same method on a single image and that's worked fine.
Edit:
If I log data in the above function, data has this:
{images: Array(2), numbers: Array(3)}
images: (2) [File, File]
numbers: (3) [40, 43, 77]
Further edit:
I can see that the issue is that when I'm appending the data, I'm only adding the string value for the arrays. So I think that the 2 issues are making sure I'm appending the data in the arrays properly and in a way that the .NET array understands to bind the data correctly.
There may be a better way of doing this and I'd be happy to listen to any better ways but I changed the way I appended the data for the arrays, it's now working ok.
for(const name in data) {
if (Array.isArray(data[name]) {
for (let i = 0; i < data[name].length; i++) {
formData.append(name, data[name][i]);
}
} else {
formData.append(name, data[name]);
}
}
I'm currently working on a public library that will allow you to create a dhcp package with all kinds of options like dhcp options. When you create it, a byte array is created, which you can send as payload via a UdpSocket. I have created two classes for this: 1. dhcppacket, 2. dhcpoption. So far the creation of a Dhcp Packet with any DhcpOptions works already.
Now I want to parse incoming DhcpPackets, which are passed in form of a byte array, back to a DhcpPacket object to be able to read important information like the message type or from the passed DhcpOptions like subnet mask. The fixed part (beginning) of the DhcpPacket I have already agreed. But now I fail with the DhcpOptions, because they are variable. How to create X objects from a byte array.
Given is a byte-array with not given length
dhcpOptions = pPayload.Skip(240).ToArray();
From this payload I now want to create objects of the class "DhcpOption". Each Dhcp Option starts with a unique byte e.g. 0x01 = DhcpOption containing the subnet mask. Then follows a byte, which indicates the length of the DhcpOption including the following values. If, for example, a DNS server is communicated via a Dhcp option, the length is 0x04, since an IPv4 address is transferred as value. The total length of the Dhcp option in the payload is therefore 6 bytes. But since all options do not have the same length, I do not know how exactly I get to the start of the next DHCP option.
Example for creating a dhcp option from the ByteArray:
DhcpOption option = new DhcpOption{
optionId = byteArray[0],
optionLength = byteArray[1],
optionValue = byteArray.Skip(2).Take(optionLength).ToArray(),
};
Searched are X-Objects of the class:
public class DhcpOption
{
/// <summary>
/// Define the DHCP options to be created by name
/// </summary>
public dhcpOptionIds optionId { get; set; } = new dhcpOptionIds();
private byte[] optionIdBytes = new byte[] { };
/// <summary>
/// Define the required length for the optionValue
/// </summary>
public byte[] optionLength { get; set; } = new byte[] { };
/// <summary>
/// Define the value for the option e.g. subnet mask
/// </summary>
public byte[] optionValue { get; set; } = new byte[] { };
/// <summary>
/// Create the DHCP option as byte array. Is then specified as an option in the DhcpPacket.
/// </summary>
/// <returns></returns>
public byte[] buildDhcpOption()
{
object selected = Convert.ChangeType(optionId, optionId.GetTypeCode());
optionIdBytes = new byte[] { Convert.ToByte(selected, null) };
return optionIdBytes.Concat(optionLength).Concat(optionValue).ToArray();
}
}
Instead of a pure byte array I would like a list of dhcp options. So List<DhcpOption> can be used to search for a desired Dhcp-option with for or foreach and then read out the value.
Here is the GitHub repo, so I don't have to copy the code.
https://github.com/Marschall-dev/DhcpDotNet/blob/main/DhcpDotNet/DhcpDotNet/DhcpPacket.cs
If you have a fixed protocol you need to follow, the typical approach I use is a BinaryReader/BinaryWriter.
Create a memory stream from the byte-array and feed to the binaryReader. If you have a list of multiple different options, the data for each option should be prefixed by the type of option, and optionally, length. So you can first read the type of option, and then have a switch that handles each specific option type. Repeat this until all options are read.
Serializing and deserializing should usually be the inverse of each other, so you might want to use BinaryWriter for serialization. Unit tests are very useful to ensure you can serialize and deserialize messages without involving any network connectivity. If you do not need to follow a existing protocol I would recommend a serialization library, like protobuf.net.
I have a class like this:
using UnityEngine;
[System.Serializable]
public class PlayerInfo
{
public string playerId;
public string deviceId;
public static PlayerInfo CreateFromJSON(string jsonString)
{
return JsonUtility.FromJson<PlayerInfo>(jsonString);
}
}
And my client receives updates with a function like this:
void OnPlayerLocalJoin(Socket socket, Packet packet, params object[] args)
{
Debug.Log(args[0]);
}
According to the documentation (which need more detail), the args should use the default json decoder, but I see it returns args as System.Object[], but oddly enough when I try args[0] my log returns:
System.Collections.Generic.Dictionary`2[System.String,System.Object]
When I print out the raw "packet" object, I do see my object:
[ "playerLocalUpdate", {"playerId":"abc","deviceId":"150B"} ]
No matter what I try, I cannot get the second part of this array to be a dictionary or better yet, how can I get it to be an instance of PlayerInfo
Debug.Log(packet.ToString());
var serialized = JsonUtility.ToJson(packet.ToString());
Debug.Log(serialized);
I'm trying to follow the docs: https://besthttp-documentation.readthedocs.io/en/latest/#3.%20Socket.IO/2%20Subscribing%20and%20receiving%20events/
The easy bit is you want to do
PlayerInfo newPlayerInfo = PlayerInfo.CreateFromJSON(jsonString);
where jsonString is '{"playerId":"abc","deviceId":"150B"}'. We can see that from the Unity docs. I think you've worked that out already.
As you say, you can see this string arriving in your packet. As you also say, it's not clear from the HTTP/2 docs what args contains. Although the dictionary may be the string we want converted to a dictionary, which isn't much use to us. You could investigate further by casting args[0] to Dictionary<string, object> and then doing a foreach on it and logging the results. I'm also unclear what args[1] might contain?
Maybe it is best to get it out of the packet. My guess is that to get the jsonString you need to do something like:
var packetArray = JsonUtility.FromJson<object[]>(packet.ToString());
string jsonString = packetArray[1].ToString();
That's because packet is JSON and we want to convert FROM the JSON string (which represents an array) to a C# array. Then we want the second item in the array. I think. It's hard to be sure without access to the code.
I want to serialize blockreference handle in an xml file and (its properties). So I store this value at the initialization :
blockReference.ObjectId.Handle.Value; // decimal value = 10658
But when I select the blockReference in AutoCAD the handle has changed.
private void database_ObjectModified(object sender, ObjectEventArgs e)
{
long currentId = e.DBObject.ObjectId.Handle.Value; // Now it's 10659 !!!!
...
}
Do I use handle no correctly ?
Use theEntityObj.Handle.Value
If you get if from the ObjectId, it will change when you close/open a drawing.
Note a Handle can change in some cases, for instance, if you have a LINE inside a block, if you BEDIT the block, the HANDLES will change.
When you use the Handle in the XML file you need to get the ObjectId by its .Handle and then use ObjectId that is valid only in the current session to read/write the object. See http://through-the-interface.typepad.com/through_the_interface/2007/02/getting_access_.html
Look for the Database.GetObjectId() method in the SDK docs (the managed interface CHM file). This is it in a nutshell...
public ObjectId GetObjectId(
[MarshalAs(UnmanagedType.U1)] bool createIfNotFound,
Handle objHandle,
int identifier
);
[MarshalAs(UnmanagedType.U1)] bool createIfNotFound Input Boolean indicating to create a objectId stub if input handle is not found
Handle objHandle Input Handle object containing the handle being passed in
int identifier Reserved for future use
I had a webmethod working which returned a byte array to the caller:
public byte[] DownloadPDF(string URI)
I had to change this to return another output (a string). So, I decided to change the method completely by now returning void and having 3 parameters like this:
public void DownloadFile(string URI, out byte[] docContents, out string returnFiletype)
My web service compiles correctly but I suspect something is wrong with the 2nd parameter (i.e. the byte array) because when I "Add Web Reference" and build my proxy class, the method has only 2 parameters, not 3):
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/DownloadFile", RequestNamespace="http://tempuri.org/", ResponseNamespace="http://tempuri.org/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
[return: System.Xml.Serialization.XmlElementAttribute("docContents", DataType="base64Binary")]
public byte[] DownloadFile(string URI, out string returnFiletype) {
object[] results = this.Invoke("DownloadFile", new object[] {
URI});
returnFiletype = ((string)(results[1]));
return ((byte[])(results[0]));
}
I don't see why my 2nd parameter, the byte array, is being ignored, but it appears to be the source of the problem.
This of course messes me up in the web client app where I get an error message at compile time:
No overload for method 'DownloadFile' takes '3' arguments
Here is the code in the web client where I need to pass 3 arguments:
myBrokerASMXProxy.ASMXProxy.FileService client = new myASMXProxy.ASMXProxy.FileService();
byte[] fileDataBytes;
string fileType;
client.DownloadFile(URI, fileDataBytes, fileType);
I am thinking of changing it back to return a byte array and add just a single "out" parameter but I thought I should ask you experts about this and in general, what is the best practice for handling multiple output requirements.
Why don't your try putting this signature:
public bool DownloadFile(string URI, out byte[] docContents, out string returnFiletype)
To see what happens? I agree with Jon Skeet, but you can still can return a bool with the result of the operation
The byte array isn't being ignored - it's being put as the return type instead. I don't know why it's doing that, but it makes more sense in my view. I wouldn't use out parameters in a void method. I suspect the proxy generator just takes any method with out parameters and turns the first one into a return type.