Re: [Bacsharp-developers] BACNET_OBJECT_TYPE: Value correction - from 31 to 54
Brought to you by:
anders-agren,
skarg
From: Dennis V M. <d.m...@d2...> - 2018-10-17 20:41:03
|
I believe the MAC address handling might also be off – 2 bugs? 1. In ‘NDPU.Parse()’ (?) – added the missing code to deal with a MAC address: //PEP Other SLEN values ... // DVM: MAC Address if (SLEN == 6) { SADR = new byte[6]; SADR[5] = bytes[len++]; SADR[4] = bytes[len++]; SADR[3] = bytes[len++]; SADR[2] = bytes[len++]; SADR[1] = bytes[len++]; SADR[0] = bytes[len++]; SAddress = BitConverter.ToUInt32(SADR, 0); } 1. In ‘SendReadProperty()’ (?) - commented out the original 2 lines & replaced each with some code: // Get the MAC address (0x0D) //sendBytes[8] = 0x01; // MAC address length //sendBytes[9] = 0x0D; // Destination MAC layer address var macAddrBytes = BitConverter.GetBytes(device.MacAddress); // DVM: // sendBytes[len++] = 0x01; // MAC address length - adjust for other lengths ... sendBytes[len++] = device.SourceLength; // sendBytes[len++] = macAddrBytes[0]; for (var idx = 0; idx < (device.SourceLength - macAddrBytes.Length); idx ++) { sendBytes[len++] = 0x00; } for (var idx = 0; idx < macAddrBytes.Length; idx++) { sendBytes[len++] = macAddrBytes[(macAddrBytes.Length - 1) - idx]; } sendBytes[len++] = 0xFF; // Hop count = 255 You might also want to start with the following code (as a quick-running guide) to adding the parsing of ‘Abort’, ‘Error’ & ‘Reject’: // DVM: public static void /*APDU*/ ParseAbort( ref byte[] bytes, int pos, out BnAbortInfo bnAbortInfo) { bnAbortInfo = BnAbortInfo.Create(); bnAbortInfo.BnServer = (bytes[pos] & 0x01) == 1; Trace.WriteLine($"\tServer - '{bnAbortInfo.BnServer}'"); // Move on past the 'PDU-Type'-'Reserved'-'Server' (- 'first header octet') pos++; // {1} bnAbortInfo.BnOriginalInvokeId = bytes[pos++]; Trace.WriteLine($"\tOriginal Invoke ID - '{bnAbortInfo.BnOriginalInvokeId}'"); // {1} bnAbortInfo.BnAbortReason = (BACnetEnums.BACNET_ABORT_REASON) (uint)bytes[pos++]; Trace.WriteLine($"\tAbort Reason - '{bnAbortInfo.BnAbortReason}'"); } // DVM: public static void /*APDU*/ ParseError( ref byte[] bytes, int pos, out BnErrorInfo bnErrorInfo) { // Skip the 'PDU-Type'-'Reserved' (- 'first header octet') pos++; bnErrorInfo = BnErrorInfo.Create(); // {1} bnErrorInfo.BnOriginalInvokeId = bytes[pos++]; Trace.WriteLine($"\tOriginal Invoke ID - '{bnErrorInfo.BnOriginalInvokeId }'"); // {1} // 'BACnetConfirmedServiceChoice' bnErrorInfo.BnErrorChoice = (BACnetEnums.BACNET_CONFIRMED_SERVICE)bytes[pos++]; Trace.WriteLine($"\tError Choice - '{bnErrorInfo.BnErrorChoice}'"); // {2} // 'X'91' Application Tag 9 (Enumerated, L=1) (Error Class)' ? // TODO: Parse the 1st app-tag; skipping it for now pos++; bnErrorInfo.BnErrorClass = (BACnetEnums.BACNET_ERROR_CLASS) (uint)bytes[pos++]; Trace.WriteLine($"\tError Class - '{bnErrorInfo.BnErrorClass}'"); // {2} // 'X'91' Application Tag 9 (Enumerated, L=1) (Error Code)' ? // TODO: Parse the 2nd app-tag; skipping it for now pos++; bnErrorInfo.BnErrorCode = (BACnetEnums.BACNET_ERROR_CODE) bytes[pos++]; Trace.WriteLine($"\tError Code - '{bnErrorInfo.BnErrorCode}'"); } // DVM: public static void /*APDU*/ ParseReject( ref byte[] bytes, int pos, out BnRejectInfo bnRejectInfo) { // Skip 'Reserved' ? pos++; bnRejectInfo = BnRejectInfo.Create(); // {1} bnRejectInfo.BnOriginalInvokeId = bytes[pos++]; Trace.WriteLine($"\tOriginal Invoke ID - '{bnRejectInfo.BnOriginalInvokeId}'"); // {1} bnRejectInfo.BnRejectReason = (BACnetEnums.BACNET_REJECT_REASON) (uint)bytes[pos++]; Trace.WriteLine($"\tReject Reason - '{bnRejectInfo.BnRejectReason}'"); } So, plugging them into ‘SendReadProperty()’ something like so: else { if (pduType == (uint) BACnetEnums.BACNET_PDU_TYPE.ERROR) { TraceWriteLine(@"ERROR!!"); // Verify the Invoke ID is the same var ic = (byte)(_invokeCounter == 0 ? 255 : _invokeCounter - 1); if (ic == recvBytes[apduOffset + 1] ) { APDU.ParseError(ref recvBytes, apduOffset); return false; // This will still execute the finally } } else { if (pduType == (uint)BACnetEnums.BACNET_PDU_TYPE.REJECT) { TraceWriteLine(@"REJECT!"); // Verify the Invoke ID is the same var ic = (byte)(_invokeCounter == 0 ? 255 : _invokeCounter - 1); if (ic == recvBytes[apduOffset + 1] ) { APDU.ParseReject(ref recvBytes, apduOffset); return false; // This will still execute the finally } } else { if (pduType == (uint)BACnetEnums.BACNET_PDU_TYPE.ABORT) { TraceWriteLine(@"ABORT!!!"); // Verify the Invoke ID is the same var ic = (byte)(_invokeCounter == 0 ? 255 : _invokeCounter - 1); if (ic == recvBytes[apduOffset + 1]) { APDU.ParseAbort(ref recvBytes, apduOffset); return false; // This will still execute the finally } } } } } My last comment about ‘SetObjectID()’ was not quite right – I was incorrectly staring at a App-Tag, so what I’d given was a method for parsing an App-Tag; e.g. the 2 methods together (new one & original one) with different names for distinction/clarity): // DVM: // Application-Tag & BACnet 'Object-Identifier' (- e.g. for 'I-Am' service) // (- VTS: "iAmDeviceIdentifier" / app-tag & Bn-Obj-Id - e.g. "device,257" == 'C4 02 00 01 01') public static uint /*APDU*/ SetAppTagObjectID(ref byte[] bytes, uint pos, BACnetEnums.BACNET_OBJECT_TYPE bnObjType, uint objInstNum) { // Assemble Object ID portion of APDU // E.g. Right for creating an 'I-Am' response // DVM: 'Tag Number' - 'Class' (application-tag/'0' - defaulted here), 'Length' (to the object-id length/'4') bytes[pos++] = (0x0C << 4) | 4; //PEP Context Specific Tag number could differ bytes[pos++] = 0x0C; // Tag number (BACnet Object ID) // <-- original //bytes[pos++] = 0x01; //bytes[pos++] = 0x00; //bytes[pos++] = 0x00; //bytes[pos++] = 0x00; var value = (uint)bnObjType; value = value & BACnetEnums.BACNET_MAX_OBJECT; value = value << BACnetEnums.BACNET_INSTANCE_BITS; value = value | (objInstNum & BACnetEnums.BACNET_MAX_INSTANCE); //len = encode_unsigned32(apdu, value); byte[] temp4 = new byte[4]; temp4 = BitConverter.GetBytes(value); bytes[pos++] = temp4[3]; bytes[pos++] = temp4[2]; bytes[pos++] = temp4[1]; bytes[pos++] = temp4[0]; return pos; } // SD-Context-Specific-Tag & BACnet 'Object-Identifier' (- e.g. for 'ReadProperty' service) // (- VTS: "objectIdentifier" / SD-ctx-specific-tag & Bn-Obj-Id) // DVM: Renamed - was originally called 'SetObjectID' public static uint /*APDU*/ SetCtxSpecificTagObjectID(ref byte[] bytes, uint pos, BACnetEnums.BACNET_OBJECT_TYPE bnObjType, uint objInstNum) { // Assemble Object ID portion of APDU //PEP Context Specific Tag number could differ bytes[pos++] = 0x0C; // Tag number (BACnet Object ID) //bytes[pos++] = 0x01; //bytes[pos++] = 0x00; //bytes[pos++] = 0x00; //bytes[pos++] = 0x00; var value = (uint)bnObjType; value = value & BACnetEnums.BACNET_MAX_OBJECT; value = value << BACnetEnums.BACNET_INSTANCE_BITS; value = value | (objInstNum & BACnetEnums.BACNET_MAX_INSTANCE); //len = encode_unsigned32(apdu, value); byte[] temp4 = new byte[4]; temp4 = BitConverter.GetBytes(value); bytes[pos++] = temp4[3]; bytes[pos++] = temp4[2]; bytes[pos++] = temp4[1]; bytes[pos++] = temp4[0]; return pos; } So, for example, for the new one that I’ve drafted – ‘SetAppTagObjectID()’, you could use it for a ‘I-Am’ listen method for mocking a device; e.g. something loosely like: public void ListenForWhoIs() { var sock = _udpReceiveClnt.Client; var remoteIpEndPoint = new IPEndPoint(IPAddress.Any, RemoteUdpPortNum); TraceWriteLine( $"\tRemoteIpEndPoint = '{remoteIpEndPoint}' (AF = '{remoteIpEndPoint.AddressFamily}'{(remoteIpEndPoint.AddressFamily == AddressFamily.InterNetworkV6 ? $", Is Ipv6 Link Local = '{remoteIpEndPoint.Address.IsIPv6LinkLocal}'" : string.Empty)})"); TraceWriteLine(string.Empty); while (true) { // YIELD: // Application.DoEvents(); Yield(); // Process the response packets if (sock.Available > 0) { var recvBytes = _udpReceiveClnt.Receive(ref remoteIpEndPoint); TraceRecvBytes(recvBytes); TraceWriteLine( $"\tRemoteIpEndPoint = '{remoteIpEndPoint}' (AF = '{remoteIpEndPoint.AddressFamily}'{(remoteIpEndPoint.AddressFamily == AddressFamily.InterNetworkV6 ? $", Is Ipv6 Link Local = '{remoteIpEndPoint.Address.IsIPv6LinkLocal}'" : string.Empty)})"); TraceWriteLine(string.Empty); // Parse and save the BACnet data var apduOffset = NPDU.Parse(recvBytes, 4); // BVLL is always 4 bytes if (APDU.ParseWhoIs(recvBytes, apduOffset)) { var sendBytes = new byte[25]; sendBytes[0] = BACnetEnums.BACNET_BVLC_TYPE_BIP; sendBytes[1] = BACnetEnums.BACNET_UNICAST_NPDU; sendBytes[2] = 0; sendBytes[3] = 12; sendBytes[4] = BACnetEnums.BACNET_PROTOCOL_VERSION; sendBytes[5] = 0x20; // Control flags sendBytes[6] = 0xFF; // Destination network address (65535) sendBytes[7] = 0xFF; sendBytes[8] = 0; // Destination MAC layer address length, 0 = Broadcast sendBytes[9] = 0xFF; // Hop count = 255 sendBytes[10] = (byte)BACnetEnums.BACNET_PDU_TYPE.UNCONFIRMED_SERVICE_REQUEST; sendBytes[11] = (byte)BACnetEnums.BACNET_UNCONFIRMED_SERVICE.I_AM; // Device Identifier var pos = APDU.SetAppTagObjectID( ref sendBytes, 12, BACnetEnums.BACNET_OBJECT_TYPE.DEVICE, MockDevObjInstNum); /* // E.g.: For obj-inst-# '257', would equate to: // - Application tag sendBytes[12] = BACnetTag.GenerateAppTag( BACnetEnums.BACNET_APPLICATION_TAG.OBJECT_ID); // - BACnet Object Identifier sendBytes[13] = 0x02; sendBytes[14] = 0x00; sendBytes[15] = 0x01; sendBytes[16] = 0x01; */ // maxADPULengthAccepted sendBytes[17] = 0x22; sendBytes[18] = 0x04; sendBytes[19] = 0x00; // segmentationSupported sendBytes[20] = 0x91; sendBytes[21] = 0x03; // vendorID sendBytes[22] = 0x22; sendBytes[23] = 0x01; sendBytes[24] = 0x04; _broadcastIpEp = new IPEndPoint(BroadcastIpAddress, RemoteUdpPortNum); // VTS - 0,0 (- Global broadcast) Trace.WriteLine( $"\tBroadcastIpEndPoint = '{_broadcastIpEp}' (AF = '{_broadcastIpEp.AddressFamily}'{(_broadcastIpEp.AddressFamily == AddressFamily.InterNetworkV6 ? $", Is Ipv6 Link Local = '{_broadcastIpEp.Address.IsIPv6LinkLocal}'" : string.Empty)})"); _udpSendClnt.EnableBroadcast = true; TraceSendBytes(sendBytes); _udpSendClnt.Send(sendBytes, sendBytes.Length, _broadcastIpEp); } } } } From: Steve Karg <st...@ka...> Sent: 16 October 2018 22:49 To: Discussion for developers of the BACsharp BACnet stack <bac...@li...> Subject: Re: [Bacsharp-developers] BACNET_OBJECT_TYPE: Value correction - from 31 to 54 Hi Dennis, Thank you for the bug-fix notice. I updated the object type enumerations in the BACsharp repo. Best Regards, Steve On Sun, Oct 14, 2018 at 11:27 AM Dennis V McEnaney <d.m...@d2...<mailto:d.m...@d2...>> wrote: // DBS: Value correction LIGHTING_OUTPUT = 54, // 31, [http://dev.d2i.co/d2i-email-signature/wlogo.png] Dennis McEnaney Technical Lead DDI 020 3102 4100<tel:02031024100> [http://dev.d2i.co/d2i-email-signature/wdot.png] TEL 020 3817 7839<tel:02038177839> [http://dev.d2i.co/d2i-email-signature/wdot.png] WEB d2i.co<http://d2i.co/> Portsoken House, 155-157 Minories, London, EC3N 1LJ [http://dev.d2i.co/d2i-email-signature/wico-linkedin.png]<https://www.linkedin.com/company/d2-interactive-uk>[http://dev.d2i.co/d2i-email-signature/wico-facebook.png]<https://www.facebook.com/d2iglobal>[http://dev.d2i.co/d2i-email-signature/wico-twitter.png]<https://twitter.com/d2interactive>[http://dev.d2i.co/d2i-email-signature/wico-instagram.png]<https://www.instagram.com/d2interactive/>[http://dev.d2i.co/d2i-email-signature/wico-google.png]<https://plus.google.com/116460778442548453069/posts> D2 Interactive Limited, Company Reg No. 7116878, VAT No. 984-4271-88, Registered Office: Portsoken House, 155-157 Minories, London, EC3N 1LJ. This email may be private and confidential, and contain legally privileged information. If you are not the addressee you should not disclose, copy, circulate or in any other way use the information contained in this transmission. Such unauthorised use is prohibited and may be unlawful. If you are not the intended recipient, please contact us immediately. P Save a tree - we only print the emails we really need. _______________________________________________ Bacsharp-developers mailing list Bac...@li...<mailto:Bac...@li...> https://lists.sourceforge.net/lists/listinfo/bacsharp-developers |