public void Decode(byte[] buffer, ref int pos) { networkNumber = (uint)buffer[pos++] << 8; networkNumber |= buffer[pos++]; MACaddress.length = buffer[pos++]; switch (MACaddress.length) { case 0: // indicates a broadcast, perfectly legal. break; case 1: MACaddress.uintMACaddress = buffer[pos++]; break; case 6: // extract the IP address myIPEndPoint ipep = new myIPEndPoint(); ipep.Decode(buffer, pos); MACaddress.ipMACaddress = ipep; pos += 6; break; default: BACnetLibraryCL.Panic("Illegal MAC address length??"); break; } }
public bool Equals(MACaddress madr) { if (length != madr.length) { return(false); } switch (length) { case 0: break; case 1: if (madr.uintMACaddress != uintMACaddress) { return(false); } break; case 6: if (ipMACaddress.Equals(madr.ipMACaddress) != true) { return(false); } break; default: BACnetLibraryCL.Panic("Illegal MAC address length??"); return(false); } return(true); }
public bool seenOnRouterInit; // These flags help us establish that. public bool Decode(byte[] buf, ref int iptr) { networkNumber = BACnetLibraryCL.ExtractUint16(buf, ref iptr); ID = buf[iptr++]; portInfoLen = buf[iptr++]; if (portInfoLen != 0) { // we are not ready to handle this. BACnetLibraryCL.Panic("todo"); } iptr += (int)portInfoLen; return(true); }
public void ServerApplication() { while (true) { // check to see if we have any live Incoming messages.. while (_apm.pktQueueToApplication.Count > 0) { try { // process the incoming queue BACnetPacket bacpkt = _apm.pktQueueToApplication.Dequeue(); if (bacpkt.npdu.isNPDUmessage) { switch (bacpkt.npdu.function) { case BACnetEnums.BACNET_NETWORK_MESSAGE_TYPE.NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK: // find the site, establish the device, make sure that it is marked as a router, save the information _apm.MessageTodo("I am router received"); break; case BACnetEnums.BACNET_NETWORK_MESSAGE_TYPE.NETWORK_MESSAGE_INIT_RT_TABLE_ACK: // find the site, establish the device, make sure that it is marked as a router, save the information _apm.MessageTodo("Init rt table ack received"); break; case BACnetEnums.BACNET_NETWORK_MESSAGE_TYPE.NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK: _apm.MessageTodo("m0053 - ----- Implementing " + bacpkt.npdu.function.ToString()); break; } } else { // todo - a certain amount of redundancy exists below, resolve. BACnetLibraryCL.RespondToAPDU(_apm, _bnm, bacpkt); } } catch (Exception ex) { _apm.MessagePanic("m0052 - Router application layer fault" + ex.ToString()); } } System.Threading.Thread.Sleep(10); } }
public override string ToString() { switch (length) { case 0: return("Broadcast"); case 1: return(uintMACaddress.ToString()); case 6: return(ipMACaddress.ToString()); default: BACnetLibraryCL.Panic("Illegal MAC length"); return("Illegal MAC address"); } }
// Build the 'header' part of the BACnet Packet private static int InsertReadPropertyResponse(AppManager apm, BACnetPacket requestBACpkt, BACnetPacket responseCRP, byte[] outbuf, BACnetEnums.BACNET_PROPERTY_ID pID) { int optr; optr = responseCRP.apdu_offset; // build APDU to provide services supported. responseCRP.EncodeNPDU(outbuf, ref optr); BACnetLibraryCL.InsertPDU(outbuf, ref optr, BACnetEnums.BACNET_PDU_TYPE.PDU_TYPE_COMPLEX_ACK, requestBACpkt.invokeID, requestBACpkt.confirmedServiceChoice); requestBACpkt.objectID.EncodeContextTag(outbuf, ref optr, 0); BACnetLibraryCL.InsertContextTag(outbuf, ref optr, 1, (int)pID); return(optr); }
// Create a new Application Tag public BACnetObjectIdentifier(byte[] buf, ref int offset, BACnetEnums.TAG tagType, BACnetEnums.BACNET_APPLICATION_TAG appTag) { // is the next parameter even an application tag if ((buf[offset] & 0x08) != 0x00) { // we have an unexpected context tag, sort this out BACnetLibraryCL.Panic("Not a context tag"); // todo, now is there a way to avoid creating the object? Have to flag it at least... return; } if ((BACnetEnums.BACNET_APPLICATION_TAG)(((buf[offset] & 0xf0) >> 4)) != appTag) { // we have an unexpected context tag, sort this out BACnetLibraryCL.Panic("Unexpected application tag"); // todo, now is there a way to avoid creating the object? Have to flag it at least... return; } int contextTagSize = buf[offset] & 0x07; offset++; switch (appTag) { case BACnetEnums.BACNET_APPLICATION_TAG.BACNET_APPLICATION_TAG_OBJECT_ID: if (contextTagSize != 4) { // we dont have a legal object ID! BACnetLibraryCL.Panic("Illegal length"); return; } this.objectType = (BACnetEnums.BACNET_OBJECT_TYPE)(((uint)buf[offset] << 2) | ((uint)buf[offset + 1] >> 6)); objectInstance = ((uint)buf[offset + 1] & 0x3f) << 16; objectInstance |= ((uint)buf[offset + 2]) << 8; objectInstance |= ((uint)buf[offset + 3]); offset += 4; return; } }
// Create a new Context Tag public BACnetObjectIdentifier(byte[] buf, ref int offset, BACnetEnums.TAG tagType, int tagValue) { // is the next parameter even a context tag if ((buf[offset] & 0x08) != 0x08) { // we have an unexpected context tag, sort this out BACnetLibraryCL.Panic("Not a context tag"); // todo, now is there a way to avoid creating the object? Have to flag it at least... return; } if ((buf[offset] & 0xf0) != (tagValue << 4)) { // we have an unexpected context tag, sort this out BACnetLibraryCL.Panic("Unexpected context tag"); // todo, now is there a way to avoid creating the object? Have to flag it at least... return; } int contextTagSize = buf[offset] & 0x07; // the length of a bacnet object identifier better be 4 if (contextTagSize != 4) { // we have an unexpected context tag, sort this out BACnetLibraryCL.Panic("Unbelievable length of object identifier"); // todo, now is there a way to avoid creating the object? Have to flag it at least... return; } objectType = (BACnetEnums.BACNET_OBJECT_TYPE)(((uint)buf[offset + 1] << 2) | ((uint)buf[offset + 2] >> 6)); objectInstance = ((uint)buf[offset + 2] & 0x3f) << 16; objectInstance |= ((uint)buf[offset + 3]) << 8; objectInstance |= ((uint)buf[offset + 4]); offset += 5; }
public void Encode(byte[] buffer, ref int pos) { buffer[pos++] = (byte)(this.networkNumber >> 8); buffer[pos++] = (byte)(this.networkNumber & 0xff); buffer[pos++] = (byte)MACaddress.length; switch (MACaddress.length) { case 1: buffer[pos++] = (byte)MACaddress.uintMACaddress; break; case 6: MACaddress.ipMACaddress.Encode(buffer, ref pos); break; default: BACnetLibraryCL.Panic("Illegal MAC address length??"); break; } }
public void EncodeBACnetNew(byte[] outbuf, ref int optr) { int startBACnetPacket = optr; // BVLC Part // http://www.bacnetwiki.com/wiki/index.php?title=BACnet_Virtual_Link_Control outbuf[optr++] = BACnetEnums.BACNET_BVLC_TYPE_BIP; if (npdu.isBroadcast) { outbuf[optr++] = (byte)BACnetEnums.BACNET_BVLC_FUNCTION.BVLC_ORIGINAL_BROADCAST_NPDU; } else { outbuf[optr++] = (byte)BACnetEnums.BACNET_BVLC_FUNCTION.BVLC_ORIGINAL_UNICAST_NPDU; } int store_length_here = optr; optr += 2; // Start of NPDU // http://www.bacnetwiki.com/wiki/index.php?title=NPDU outbuf[optr++] = 0x01; // Always 1 int store_NPCI = optr; outbuf[optr++] = 0x00; // Control if (npdu.isNPDUmessage) { outbuf[store_NPCI] |= 0x80; // Indicating Network Layer Message } if (npdu.expectingReply) { outbuf[store_NPCI] |= 0x04; // todo- magic number } if (npdu.isBroadcast) { outbuf[store_NPCI] |= 0x20; // Control byte - indicate DADR present outbuf[optr++] = 0xff; // DNET Network - B'cast outbuf[optr++] = 0xff; outbuf[optr++] = 0x00; // DLEN } else { // insert dadr - but only if the device is NOT directly coupled. See page 59. If the device is directly coupled // then the ethernet address in the packet will suffice. if (!dadr.directlyConnected) { // need to insert destination DADR here outbuf[store_NPCI] |= 0x20; // Control byte - indidate DADR present dadr.Encode(outbuf, ref optr); } } // we are a router, so we need to add source address under most circumstances. (not broadcast who-is) if (srcDevice.adr != null) { outbuf[store_NPCI] |= 0x08; // Control byte - indidate SADR present srcDevice.adr.Encode(outbuf, ref optr); } if (npdu.isBroadcast || !dadr.directlyConnected) { // insert hopcount here. hopcount -= 10; outbuf[optr++] = (byte)hopcount; } // APDU start // http://www.bacnetwiki.com/wiki/index.php?title=APDU if (apdu_present) { // APDU start // http://www.bacnetwiki.com/wiki/index.php?title=APDU for (int i = 0; i < apdu_length; i++) { outbuf[optr++] = buffer[apdu_offset + i]; // Encoded APDU type == 01 == Unconfirmed Request } } else if (npdu.isNPDUmessage) { // Build the Nsdu // http://www.bacnetwiki.com/wiki/index.php?title=Network_Layer_Message outbuf[optr++] = (byte)npdu.function; switch (npdu.function) { case BACnetEnums.BACNET_NETWORK_MESSAGE_TYPE.NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK: break; case BACnetEnums.BACNET_NETWORK_MESSAGE_TYPE.NETWORK_MESSAGE_INIT_RT_TABLE: outbuf[optr++] = 0x00; // Number of port mappings break; case BACnetEnums.BACNET_NETWORK_MESSAGE_TYPE.NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK: if (numberList != null) { foreach (uint i in numberList) { BACnetLibraryCL.InsertUint16(outbuf, ref optr, i); } } break; default: _apm.MessageTodo("m0023 Implement " + npdu.function.ToString()); break; } } else { // build an APDU. switch (this.pduType) { case BACnetEnums.BACNET_PDU_TYPE.PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST: switch (this.unconfirmedServiceChoice) { case BACnetEnums.BACNET_UNCONFIRMED_SERVICE.SERVICE_UNCONFIRMED_I_AM: // APDU start // http://www.bacnetwiki.com/wiki/index.php?title=APDU outbuf[optr++] = 0x10; // Encoded APDU type == 01 == Unconfirmed Request outbuf[optr++] = 0x00; // Unconfirmed Service Choice: I-Am // object identifier, device object BACnetObjectIdentifier bnoid = new BACnetObjectIdentifier(); bnoid.SetType(BACnetEnums.BACNET_OBJECT_TYPE.OBJECT_DEVICE); _apm.MessageTodo("m0038 - Establish a mechanism to determine our OWN Device ID"); bnoid.SetInstance(_apm.ourDeviceId); bnoid.EncodeApplicationTag(outbuf, ref optr); // Maximum APDU length (Application Tag, Integer) Unsigned apdulen = new Unsigned(1476); apdulen.Encode(outbuf, ref optr); // Segmentation supported, (Application Tag, Enum) BACnetSegmentation bsg = new BACnetSegmentation(); bsg.Encode(outbuf, ref optr); // Vendor ID, (Application Tag, Integer) BACnetLibraryCL.InsertApplicationTagUint16(outbuf, ref optr, _apm.ourVendorID); break; default: _apm.MessageTodo("m0022 Build missing service type"); break; } break; default: _apm.MessageTodo("m0021 Build missing PDU type"); break; } } outbuf[store_length_here] = (byte)(((optr - startBACnetPacket) >> 8) & 0xff); outbuf[store_length_here + 1] = (byte)((optr - startBACnetPacket) & 0xff); }
public bool DecodeBACnet(byte[] buffer, int totalMessageLength) { int offset = 0; try { // this whole section http://www.bacnetwiki.com/wiki/index.php?title=BACnet_Virtual_Link_Control ADR sadr = null; if (buffer[offset] != BACnetEnums.BACNET_BVLC_TYPE_BIP) { // todo3, panic log _apm.MessageProtocolError("m0013 - Not a BACnet/IP message"); return(false); } // we could receive an original broadcast, a unicast, a forwarded here... // BVLC Function Types. See http://www.bacnetwiki.com/wiki/index.php?title=BVLC_Function switch ((BACnetEnums.BACNET_BVLC_FUNCTION)buffer[offset + 1]) { case BACnetEnums.BACNET_BVLC_FUNCTION.BVLC_FORWARDED_NPDU: npdu_offset = offset + 10; break; case BACnetEnums.BACNET_BVLC_FUNCTION.BVLC_ORIGINAL_UNICAST_NPDU: case BACnetEnums.BACNET_BVLC_FUNCTION.BVLC_ORIGINAL_BROADCAST_NPDU: // all these OK npdu_offset = offset + 4; break; default: BACnetLibraryCL.Panic("m0012 nosuch bvlc function"); break; } // Investigate the NPDU // http://www.bacnetwiki.com/wiki/index.php?title=NPDU if (buffer[npdu_offset] != BACnetEnums.BACNET_PROTOCOL_VERSION) { // we have a major problem, microprotocol version has changed. http://www.bacnetwiki.com/wiki/index.php?title=BACnet_Virtual_Link_Control BACnetLibraryCL.Panic("m0011 BVLC microprotocol problem"); return(false); } // expecting reply? if ((buffer[npdu_offset + 1] & 0x04) == 0x04) { npdu.expectingReply = true; } // destination address present? // http://www.bacnetwiki.com/wiki/index.php?title=NPCI if ((buffer[npdu_offset + 1] & 0x20) == 0x20) { //da_present = true; dadr = new ADR(); // dnet, dadr and hop count present int dAddrOffset = npdu_offset + 2; dadr.Decode(buffer, ref dAddrOffset); if (dadr.MACaddress.length == 0) { npdu.isBroadcast = true; // broadcast, but check the DNET if (dadr.networkNumber != 0xffff) { throw new Exception("m0010 - Broadcast according to DLEN, but DNET not 0xffff"); // todo, this means directed broadcast, need to deal with this still } } if (dadr.MACaddress.length != 1 && dadr.MACaddress.length != 6 && dadr.MACaddress.length != 0) { // panic throw new Exception("m0009 - Unexpected DADR len"); } // todo, pick up variable length destination address } // Source address present? // http://www.bacnetwiki.com/wiki/index.php?title=NPCI if ((buffer[npdu_offset + 1] & 0x08) == 0x08) { //sa_present = true; sadr = new ADR(); int sa_offset = npdu_offset + 2; // however, if there is a destination address, move the sa_offset up appropriate amount if (dadr != null) { sa_offset = npdu_offset + 2 + 3 + (int)dadr.MACaddress.length; } // means SADR, SNET present sadr.Decode(buffer, ref sa_offset); // SA exists (included MAC and so this means the received IP address needs to be the fromBIP } else { // at this point, if SADR not discovered within the NPDU, then the SADR MAC address is the Ethernet/IP fromaddress // and the device can (must) be considered 'direcly connected' if (directlyConnectedIPEndPointOfDevice != null) { // and even though the device is directly connected, because we are a router, we have an allocated network number to provide // the network number is available outside this class, and will be filled in by the calling function if it is relevant. srcDevice.adr = new ADR(0, directlyConnectedIPEndPointOfDevice); } else { throw new Exception("m0063 - No From-Address can be determined"); } } if (dadr != null) { if (sadr != null) { hopcount = (uint)(buffer[npdu_offset + 2 + 2 + 1 + dadr.MACaddress.length + 2 + 1 + sadr.MACaddress.length]); } else { hopcount = (uint)(buffer[npdu_offset + 2 + 2 + 1 + dadr.MACaddress.length]); } // true broadcast, but check the hopcount if (hopcount == 0) { // out of hops, should never happen to us, so sound a warning // todo, remove this for a functioning systems System.Windows.Forms.MessageBox.Show("m0008 Hopcount of 0 detected"); return(false); } } // finished resolving sadr, and dadr. Now populate our devices as required if (sadr != null) { if (srcDevice.adr != null) { // means this adr was partially populated elsewhere. srcDevice.adr.networkNumber = sadr.networkNumber; srcDevice.adr.MACaddress = sadr.MACaddress; } else { srcDevice.adr = sadr; } } if ((buffer[npdu_offset + 1] & 0x80) == 0x80) // todo magic number { // NSDU contains Network Layer Message // http://www.bacnetwiki.com/wiki/index.php?title=Network_Layer_Protocol_Control_Information apdu_present = false; npdu.isNPDUmessage = true; // calculate offset to the NSDU nsdu_offset = npdu_offset + 2; if (sadr != null) { nsdu_offset += 2 + 1 + (int)sadr.MACaddress.length; } if (dadr != null) { nsdu_offset += 2 + 1 + (int)dadr.MACaddress.length + 1; } npdu.function = (BACnetEnums.BACNET_NETWORK_MESSAGE_TYPE)buffer[nsdu_offset]; switch (npdu.function) { case BACnetEnums.BACNET_NETWORK_MESSAGE_TYPE.NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK: // there may be a list of network numbers after the header if ((length - nsdu_offset >= 3)) { numberList = new List <uint>(); } for (int i = 0; i < (length - nsdu_offset - 1) / 2; i++) { int tref = nsdu_offset + 1 + i * 2; numberList.Add(BACnetLibraryCL.ExtractUint16(buffer, ref tref)); } break; case BACnetEnums.BACNET_NETWORK_MESSAGE_TYPE.NETWORK_MESSAGE_INIT_RT_TABLE_ACK: int tiptr = nsdu_offset + 1; int numPorts = buffer[tiptr++]; routerPortList = new List <RouterPort>(); for (int i = 0; i < numPorts; i++) { RouterPort rp = new RouterPort(); rp.Decode(buffer, ref tiptr); routerPortList.Add(rp); } break; default: // todo break; } } else { // NSDU contains APDU // http://www.bacnetwiki.com/wiki/index.php?title=Network_Layer_Protocol_Control_Information // determine if SNET, SLEN, SADR present apdu_present = true; apdu_offset = npdu_offset + 2; if (sadr != null) { apdu_offset += 2 + 1 + (int)sadr.MACaddress.length; } if (dadr != null) { apdu_offset += 2 + 1 + (int)dadr.MACaddress.length + 1; } apdu_length = length - apdu_offset; if (apdu_length < 0) { _apm.MessagePanic("m0006 Illegal APDU length"); return(false); } // todo - need to extract the apdu for others to refer to. However, APDUs may be customer specific, so extract this as a buffer // only for now, and do some spot checks for relevant functions such as I-Am and Who-Is. apdu_buf = new byte[2000]; Buffer.BlockCopy(buffer, apdu_offset, apdu_buf, 0, apdu_length); // the offset here is the APDU. Start parsing APDU. // todo, decided to leave the enum values unshifted today 11/27/09 pduType = (BACnetEnums.BACNET_PDU_TYPE)(buffer[apdu_offset] & 0xf0); // make sure that we can handle the rest of the packet //if ((buffer[apdu_offset] & 0x0f) != 0 ) //{ // throw new Exception("m0056 - Cannot handle segmented messages yet"); //} int tptr = apdu_offset + 1; if (pduType == BACnetEnums.BACNET_PDU_TYPE.PDU_TYPE_CONFIRMED_SERVICE_REQUEST) { // some PDUs have max segs here tptr++; } //_apm.MessageTodo("Remove apdu++"); //apdu_offset++; //apdu_offset++; // now the next byte is the invoke ID invokeID = buffer[tptr++]; switch (pduType) { case BACnetEnums.BACNET_PDU_TYPE.PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST: // todo, offset is not always 1, depends on the flags in the first byte. DecodeUnconfirmedService(apdu_buf, apdu_length); break; case BACnetEnums.BACNET_PDU_TYPE.PDU_TYPE_CONFIRMED_SERVICE_REQUEST: DecodeConfirmedService(apdu_buf); break; case BACnetEnums.BACNET_PDU_TYPE.PDU_TYPE_SIMPLE_ACK: break; case BACnetEnums.BACNET_PDU_TYPE.PDU_TYPE_COMPLEX_ACK: confirmedServiceChoice = (BACnetEnums.BACNET_CONFIRMED_SERVICE)buffer[apdu_offset + 2]; DecodeComplexACK(buffer, apdu_offset); break; case BACnetEnums.BACNET_PDU_TYPE.PDU_TYPE_SEGMENT_ACK: _apm.MessageTodo("m0093 - Segment ACK"); errorFlag = true; break; case BACnetEnums.BACNET_PDU_TYPE.PDU_TYPE_ERROR: _apm.MessageTodo("m0064 - Error"); errorFlag = true; break; case BACnetEnums.BACNET_PDU_TYPE.PDU_TYPE_REJECT: pduRejectReason = (BACnetEnums.BACNET_BACNET_REJECT_REASON)buffer[apdu_offset++]; errorFlag = true; break; case BACnetEnums.BACNET_PDU_TYPE.PDU_TYPE_ABORT: _apm.MessageTodo("m0066 - PDU abort"); errorFlag = true; break; default: throw new Exception("m0003 - Illegal PDU type"); } } } catch (Exception ex) { _apm.MessagePanic("m0001 - BACnet Decode Failed " + ex.ToString()); } return(true); }
public static void RespondToAPDU(AppManager apm, BACnetManager bnm, BACnetPacket incomingPacket) { byte[] outbuf = new byte[2000]; int optr; PacketLog pktlog; BACnetPacket outgoingBACpacket = new BACnetPacket(apm, bnm); // fill in some CRP parameters for packet log display outgoingBACpacket.confirmedServiceChoice = incomingPacket.confirmedServiceChoice; outgoingBACpacket.apduConfirmedServiceTypeFlag = incomingPacket.apduConfirmedServiceTypeFlag; outgoingBACpacket.propertyID = incomingPacket.propertyID; outgoingBACpacket.apduUnconfirmedServiceFlag = incomingPacket.apduUnconfirmedServiceFlag; outgoingBACpacket.pduType = incomingPacket.pduType; outgoingBACpacket.unconfirmedServiceChoice = incomingPacket.unconfirmedServiceChoice; outgoingBACpacket.directlyConnectedIPEndPointOfDevice = incomingPacket.directlyConnectedIPEndPointOfDevice; BACnetPacket incomingBACpacket = (BACnetPacket)incomingPacket; if (incomingBACpacket.apduConfirmedServiceTypeFlag == true) { switch (incomingBACpacket.confirmedServiceChoice) { case BACnetEnums.BACNET_CONFIRMED_SERVICE.SERVICE_CONFIRMED_READ_PROPERTY: switch (incomingBACpacket.propertyID) { case BACnetEnums.BACNET_PROPERTY_ID.PROP_OBJECT_NAME: // supply the first (and only) (device) object optr = InsertReadPropertyResponse(apm, incomingBACpacket, outgoingBACpacket, outbuf, incomingBACpacket.propertyID); // Device Object ID InsertContextOpeningTag(outbuf, ref optr, 3); InsertApplicationTagString(outbuf, ref optr, apm.ourDeviceName); InsertContextClosingTag(outbuf, ref optr, 3); // Send it off SendOffPacket(apm, outgoingBACpacket, outbuf, optr); break; case BACnetEnums.BACNET_PROPERTY_ID.PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: optr = InsertReadPropertyResponse(apm, incomingBACpacket, outgoingBACpacket, outbuf, incomingBACpacket.propertyID); BACnetLibraryCL.InsertContextOpeningTag(outbuf, ref optr, 3); // hardcode the bitstring until we see where else it can be used, then generalized. outbuf[optr++] = 0x83; // Assume length = 3, including unused bits byte // outbuf[optr++] = 0x03; outbuf[optr++] = 0x06; // trailing bits not used InsertBitString(outbuf, optr, 2, (int)BACnetEnums.BACNET_OBJECT_TYPE.OBJECT_DEVICE); optr += 2; BACnetLibraryCL.InsertContextClosingTag(outbuf, ref optr, 3); SendOffPacket(apm, outgoingBACpacket, outbuf, optr); break; case BACnetEnums.BACNET_PROPERTY_ID.PROP_PROTOCOL_SERVICES_SUPPORTED: optr = InsertReadPropertyResponse(apm, incomingBACpacket, outgoingBACpacket, outbuf, incomingBACpacket.propertyID); BACnetLibraryCL.InsertContextOpeningTag(outbuf, ref optr, 3); //List<BACnetEnums.BACNET_SERVICES_SUPPORTED> servicesList = new List<BACnetEnums.BACNET_SERVICES_SUPPORTED>(); //servicesList.Add(BACnetEnums.BACNET_SERVICES_SUPPORTED.SERVICE_SUPPORTED_READ_PROPERTY); //// servicesList.Add(BACnetEnums.BACNET_SERVICES_SUPPORTED.SERVICE_SUPPORTED_WRITE_PROPERTY); //servicesList.Add(BACnetEnums.BACNET_SERVICES_SUPPORTED.SERVICE_SUPPORTED_DEVICE_COMMUNICATION_CONTROL); ////servicesList.Add(BACnetEnums.BACNET_SERVICES_SUPPORTED.SERVICE_SUPPORTED_REINITIALIZE_DEVICE); //servicesList.Add(BACnetEnums.BACNET_SERVICES_SUPPORTED.SERVICE_SUPPORTED_TIME_SYNCHRONIZATION); //servicesList.Add(BACnetEnums.BACNET_SERVICES_SUPPORTED.SERVICE_SUPPORTED_WHO_IS); //// todo, what about i-am ? //BACnetLibraryCL.InsertApplicationTagString(outbuf, ref optr, servicesList ); // hardcode the bitstring until we see where else it can be used, then generalized. outbuf[optr++] = 0x85; // Assume length = 6 outbuf[optr++] = 0x06; // Length, including the next byte outbuf[optr++] = 0x05; // 5 trailing bits not used InsertBitString(outbuf, optr, 5, (int)BACnetEnums.BACNET_SERVICES_SUPPORTED.SERVICE_SUPPORTED_READ_PROPERTY); InsertBitString(outbuf, optr, 5, (int)BACnetEnums.BACNET_SERVICES_SUPPORTED.SERVICE_SUPPORTED_DEVICE_COMMUNICATION_CONTROL); InsertBitString(outbuf, optr, 5, (int)BACnetEnums.BACNET_SERVICES_SUPPORTED.SERVICE_SUPPORTED_TIME_SYNCHRONIZATION); InsertBitString(outbuf, optr, 5, (int)BACnetEnums.BACNET_SERVICES_SUPPORTED.SERVICE_SUPPORTED_WHO_IS); InsertBitString(outbuf, optr, 5, (int)BACnetEnums.BACNET_SERVICES_SUPPORTED.SERVICE_SUPPORTED_WRITE_PROPERTY); InsertBitString(outbuf, optr, 5, (int)BACnetEnums.BACNET_SERVICES_SUPPORTED.SERVICE_SUPPORTED_REINITIALIZE_DEVICE); optr += 5; BACnetLibraryCL.InsertContextClosingTag(outbuf, ref optr, 3); SendOffPacket(apm, outgoingBACpacket, outbuf, optr); break; case BACnetEnums.BACNET_PROPERTY_ID.PROP_OBJECT_LIST: // Only respond if array index specified if (!incomingPacket.arrayIndexDecoded) { throw new Exception("m0058 - Not expecting open object list"); } if (incomingPacket.arrayIndex == 0) { optr = InsertReadPropertyResponse(apm, incomingBACpacket, outgoingBACpacket, outbuf, incomingBACpacket.propertyID); // arrayIndex 0 InsertContextTag(outbuf, ref optr, 2, 0); // Object count InsertContextOpeningTag(outbuf, ref optr, 3); InsertApplicationTag(outbuf, ref optr, BACnetEnums.BACNET_APPLICATION_TAG.BACNET_APPLICATION_TAG_UNSIGNED_INT, 1); InsertContextClosingTag(outbuf, ref optr, 3); // Send it off SendOffPacket(apm, outgoingBACpacket, outbuf, optr); break; // supply the number of objects } else if (incomingPacket.arrayIndex == 1) { // supply the first (and only) (device) object optr = InsertReadPropertyResponse(apm, incomingBACpacket, outgoingBACpacket, outbuf, incomingBACpacket.propertyID); // arrayIndex 1 InsertContextTag(outbuf, ref optr, 2, 1); // Device Object ID InsertContextOpeningTag(outbuf, ref optr, 3); incomingPacket.objectID.EncodeApplicationTag(outbuf, ref optr); InsertContextClosingTag(outbuf, ref optr, 3); // Send it off SendOffPacket(apm, outgoingBACpacket, outbuf, optr); } else { throw new Exception("m0059 - Illegal object list index"); } break; case BACnetEnums.BACNET_PROPERTY_ID.PROP_OBJECT_TYPE: // supply the first (and only) (device) object optr = InsertReadPropertyResponse(apm, incomingBACpacket, outgoingBACpacket, outbuf, incomingBACpacket.propertyID); // Device Object ID InsertContextOpeningTag(outbuf, ref optr, 3); InsertApplicationTag(outbuf, ref optr, BACnetEnums.BACNET_APPLICATION_TAG.BACNET_APPLICATION_TAG_ENUMERATED, (uint)BACnetEnums.BACNET_OBJECT_TYPE.OBJECT_DEVICE); InsertContextClosingTag(outbuf, ref optr, 3); // Send it off SendOffPacket(apm, outgoingBACpacket, outbuf, optr); break; case BACnetEnums.BACNET_PROPERTY_ID.PROP_SYSTEM_STATUS: // supply the first (and only) (device) object optr = InsertReadPropertyResponse(apm, incomingBACpacket, outgoingBACpacket, outbuf, incomingBACpacket.propertyID); // Device Object ID InsertContextOpeningTag(outbuf, ref optr, 3); InsertApplicationTag(outbuf, ref optr, BACnetEnums.BACNET_APPLICATION_TAG.BACNET_APPLICATION_TAG_ENUMERATED, (uint)BACnetEnums.BACNET_DEVICE_STATUS.STATUS_OPERATIONAL); InsertContextClosingTag(outbuf, ref optr, 3); // Send it off SendOffPacket(apm, outgoingBACpacket, outbuf, optr); break; case BACnetEnums.BACNET_PROPERTY_ID.PROP_OBJECT_IDENTIFIER: // supply the first (and only) (device) object optr = InsertReadPropertyResponse(apm, incomingBACpacket, outgoingBACpacket, outbuf, incomingBACpacket.propertyID); // Device Object ID InsertContextOpeningTag(outbuf, ref optr, 3); incomingPacket.objectID.EncodeApplicationTag(outbuf, ref optr); InsertContextClosingTag(outbuf, ref optr, 3); // Send it off SendOffPacket(apm, outgoingBACpacket, outbuf, optr); break; case BACnetEnums.BACNET_PROPERTY_ID.PROP_VENDOR_NAME: // supply the first (and only) (device) object optr = InsertReadPropertyResponse(apm, incomingBACpacket, outgoingBACpacket, outbuf, incomingBACpacket.propertyID); // Device Object ID InsertContextOpeningTag(outbuf, ref optr, 3); InsertApplicationTagString(outbuf, ref optr, apm.ourVendorName); InsertContextClosingTag(outbuf, ref optr, 3); // Send it off SendOffPacket(apm, outgoingBACpacket, outbuf, optr); break; case BACnetEnums.BACNET_PROPERTY_ID.PROP_MODEL_NAME: // supply the first (and only) (device) object optr = InsertReadPropertyResponse(apm, incomingBACpacket, outgoingBACpacket, outbuf, incomingBACpacket.propertyID); // Device Object ID InsertContextOpeningTag(outbuf, ref optr, 3); InsertApplicationTagString(outbuf, ref optr, apm.ourModelName); InsertContextClosingTag(outbuf, ref optr, 3); // Send it off SendOffPacket(apm, outgoingBACpacket, outbuf, optr); break; case BACnetEnums.BACNET_PROPERTY_ID.PROP_FIRMWARE_REVISION: case BACnetEnums.BACNET_PROPERTY_ID.PROP_APPLICATION_SOFTWARE_VERSION: RespondReadPropertyWithString(apm, incomingBACpacket, outgoingBACpacket, outbuf, apm.ourFirmwareRevision); break; case BACnetEnums.BACNET_PROPERTY_ID.PROP_VENDOR_IDENTIFIER: RespondReadPropertyWithUint(apm, incomingBACpacket, outgoingBACpacket, outbuf, apm.ourVendorID); break; case BACnetEnums.BACNET_PROPERTY_ID.PROP_PROTOCOL_VERSION: RespondReadPropertyWithUint(apm, incomingBACpacket, outgoingBACpacket, outbuf, 1); break; case BACnetEnums.BACNET_PROPERTY_ID.PROP_PROTOCOL_REVISION: RespondReadPropertyWithUint(apm, incomingBACpacket, outgoingBACpacket, outbuf, 4); break; default: apm.MessageTodo("m0046 - Implement read property " + incomingBACpacket.propertyID.ToString()); break; } break; default: apm.MessageTodo("m0044 - Implement confirmed service " + incomingBACpacket.confirmedServiceChoice.ToString()); break; } } if (incomingBACpacket.apduUnconfirmedServiceFlag == true) { switch (incomingBACpacket.pduType) { case BACnetEnums.BACNET_PDU_TYPE.PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST: switch (incomingBACpacket.unconfirmedServiceChoice) { case BACnetEnums.BACNET_UNCONFIRMED_SERVICE.SERVICE_UNCONFIRMED_WHO_IS: // we need to respond // ONLY if we are in the range, and if not, ignore. if (incomingBACpacket.lowRange != 0 || incomingBACpacket.highRange != 0) { if (apm.ourDeviceId < incomingBACpacket.lowRange || apm.ourDeviceId > incomingBACpacket.highRange) { // This packet not for us apm.MessageLog("m0034 - Ranged Who-Is not addressed to us, ignoring"); break; } } // Compose the response (I-am) outgoingBACpacket.BACnetPort = incomingPacket.BACnetPort; outgoingBACpacket.npdu.isBroadcast = true; outgoingBACpacket.hopcount = 256; // destination address of the BACnet packet. (Must either be b'cast or directlyConnectedIPEP optr = 0; outgoingBACpacket.npdu.isNPDUmessage = false; // makes it an APDU outgoingBACpacket.pduType = BACnetEnums.BACNET_PDU_TYPE.PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST; outgoingBACpacket.unconfirmedServiceChoice = BACnetEnums.BACNET_UNCONFIRMED_SERVICE.SERVICE_UNCONFIRMED_I_AM; outgoingBACpacket.EncodeBACnetNew(outbuf, ref optr); // send the message // if there is no available adapter, this try will throw try { apm.bnm.bacnet_listen_socket.SendTo(outbuf, optr, SocketFlags.None, incomingPacket.directlyConnectedIPEndPointOfDevice); pktlog = new PacketLog(true, incomingPacket.directlyConnectedIPEndPointOfDevice, outgoingBACpacket); pktlog.BACnetPacket = (BACnetPacket)outgoingBACpacket; apm.bnm.BACnetMessageLog.Enqueue(pktlog); } catch (SocketException) { apm.MessageTodo("Either the network cable is unplugged, or there is no configured Ethernet Port on this computer"); } break; case BACnetEnums.BACNET_UNCONFIRMED_SERVICE.SERVICE_UNCONFIRMED_I_AM: break; default: apm.MessageTodo("Implement " + incomingBACpacket.unconfirmedServiceChoice.ToString()); break; } break; default: apm.MessageTodo("Implement " + incomingBACpacket.pduType.ToString()); break; } } }
public static void ReadPropertyObjectList(BACnetManager bnm, Device device) { byte[] data = new byte[1024]; int optr = 0; // BVLC Part // http://www.bacnetwiki.com/wiki/index.php?title=BACnet_Virtual_Link_Control data[optr++] = BACnetEnums.BACNET_BVLC_TYPE_BIP; // 81 data[optr++] = (byte)BACnetEnums.BACNET_BVLC_FUNCTION.BVLC_ORIGINAL_UNICAST_NPDU; //0a int store_length_here = optr; optr += 2; // Start of NPDU // http://www.bacnetwiki.com/wiki/index.php?title=NPDU data[optr++] = 0x01; // Version data[optr++] = 0x24; // NCPI - Dest present, expecting reply if (device.adr != null) { device.adr.Encode(data, ref optr); } else { // this means we have an ethernet/IP address for a MAC address. At present // we then dont know the network number // todo - resolve the network number issue ADR tempAdr = new ADR(0, device.directlyConnectedIPEndPointOfDevice); tempAdr.Encode(data, ref optr); } data[optr++] = 0xff; // Hopcount // APDU start // http://www.bacnetwiki.com/wiki/index.php?title=APDU // Unconfirmed Request // Structure described here http://www.bacnetwiki.com/wiki/index.php?title=BACnet-Confirmed-Request-PDU data[optr++] = 0x02; // PDU Type=0 and SA=1 data[optr++] = 0x04; // Max Resp (Encoded) data[optr++] = 0x01; // Invoke ID data[optr++] = 0x0c; // Service Choice 12 = ReadProperty // Service Request // Object type, instance (Device Object) (Encode as context tag 0) device.deviceObjectID.EncodeContextTag(data, ref optr, 0); // Property Identifier (Object List) InsertContextTag(data, ref optr, 1, (int)BACnetEnums.BACNET_PROPERTY_ID.PROP_OBJECT_LIST); // todo, why is this not context encoded? BACnetLibraryCL.InsertInt16(data, ref store_length_here, optr); // todo bnm.insidesocket.MySend(data, optr, device.packet.fromBIP); }
public void EncodeApplicationTag(byte[] buffer, ref int pos) { UInt32 objid = ((UInt32)objectType << 22) | (objectInstance & 0x3ffffff); BACnetLibraryCL.InsertApplicationTag(buffer, ref pos, BACnetEnums.BACNET_APPLICATION_TAG.BACNET_APPLICATION_TAG_OBJECT_ID, objid); }
public void UpdateDeviceTreeView(BACnetManager bnm, BACnetPacket pkt) { if (!pkt.apdu_present) { // means that this packet must be a NPDU message. Treat it differently switch (pkt.npdu.function) { case BACnetEnums.BACNET_NETWORK_MESSAGE_TYPE.NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK: //if (pkt.numberList.Count > 0) //{ // means we had a response from a router - establish the router in our tree. myTreeNode mtnd = EstablishTreeNodeDevice(bnm, pkt); if (mtnd != null) { // found, or established, the treenode matching the device, // now add the objects to it. // update the type of device - we now know it is a router (even if we did not know before). mtnd.device.type = BACnetEnums.DEVICE_TYPE.Router; mtnd.Text = "Router"; if (pkt.numberList != null) { foreach (int bno in pkt.numberList) { bool found = false; for (int i = 0; i < mtnd.Nodes.Count; i++) { if (mtnd.Nodes[i].GetType() == typeof(myTreeNode)) { myTreeNode mtnObj = (myTreeNode)mtnd.Nodes[i]; if (mtnObj.type == myTreeNode.TREENODE_OBJ_TYPE.NetworkNumber && mtnObj.networkNumber == bno) { // if we get here, the object already exists in the list and we must not add it again. mtnObj.networkNumberFromWhoIsRouter = true; found = true; break; } } } if (!found) { //// not found, so add myTreeNode ntn = mtnd.AddMyTreeNodeObject(myTreeNode.TREENODE_OBJ_TYPE.NetworkNumber, "Network " + bno.ToString()); ntn.networkNumber = (uint)bno; ntn.networkNumberFromWhoIsRouter = true; ntn.ToolTipText = "Do not right click on this item, it has no effect"; mtnd.Expand(); } } } else { _apm.MessageTodo("m0032"); } } //} break; case BACnetEnums.BACNET_NETWORK_MESSAGE_TYPE.NETWORK_MESSAGE_INIT_RT_TABLE_ACK: myTreeNode mtnrt = EstablishTreeNodeDevice(bnm, pkt); if (mtnrt != null) { // found, or established, the treenode matching the device, // now add the objects to it. // update the type of device - we now know it is a router (even if we did not know before). mtnrt.device.type = BACnetEnums.DEVICE_TYPE.Router; mtnrt.Text = "Router"; foreach (RouterPort rp in pkt.routerPortList) { bool found = false; for (int i = 0; i < mtnrt.Nodes.Count; i++) { if (mtnrt.Nodes[i].GetType() == typeof(myTreeNode)) { myTreeNode mtnObj = (myTreeNode)mtnrt.Nodes[i]; if (mtnObj.type == myTreeNode.TREENODE_OBJ_TYPE.NetworkNumber && mtnObj.networkNumber == rp.networkNumber) { // if we get here, the object already exists in the list and we must not add it again. found = true; mtnObj.networkNumberFromInitRouterTable = true; break; } } } if (!found) { //// not found, so add myTreeNode ntn = mtnrt.AddMyTreeNodeObject(myTreeNode.TREENODE_OBJ_TYPE.NetworkNumber, "Network " + rp.networkNumber.ToString()); ntn.networkNumber = rp.networkNumber; ntn.networkNumberFromInitRouterTable = true; // ntn.ToolTipText = "Do not right click on this item, it has no effect"; mtnrt.Expand(); } } } break; } return; } switch (pkt.pduType) { case BACnetEnums.BACNET_PDU_TYPE.PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST: switch (pkt.unconfirmedServiceChoice) { case BACnetEnums.BACNET_UNCONFIRMED_SERVICE.SERVICE_UNCONFIRMED_I_AM: pkt.srcDevice.directlyConnectedIPEndPointOfDevice = pkt.directlyConnectedIPEndPointOfDevice; // was fromBIP // todo, not sure where I use this - check bnm.deviceList.Add(pkt.srcDevice); AddDeviceToTreeNode(bnm, pkt); // bnm.newPacketQueue.Enqueue(packet); break; case BACnetEnums.BACNET_UNCONFIRMED_SERVICE.SERVICE_UNCONFIRMED_WHO_IS: // ignore reflected who-is messages break; default: BACnetLibraryCL.Panic("Todo"); break; } break; case BACnetEnums.BACNET_PDU_TYPE.PDU_TYPE_COMPLEX_ACK: switch (pkt.confirmedServiceChoice) { case BACnetEnums.BACNET_CONFIRMED_SERVICE.SERVICE_CONFIRMED_READ_PROPERTY: switch (pkt.propertyID) { case BACnetEnums.BACNET_PROPERTY_ID.PROP_OBJECT_LIST: // what we need to do here is list the objects on the treeview... // find the device in the tree // add the objects to it myTreeNode mtn = findTreeNodeDevice(bnm, pkt); if (mtn != null) { // found the treenode matching the device, add the objects to it. if (pkt.objectList != null) { foreach (BACnetObjectIdentifier bno in pkt.objectList) { // does it exist? if so, ignore if (findTreeNodeObject(mtn, bno) == null) { // not found, so add myTreeNode ntn = new myTreeNode(); ntn.oID = bno; ntn.type = myTreeNode.TREENODE_OBJ_TYPE.BACnetObject; ntn.Text = bno.objectType.ToString() + " Instance: " + bno.objectInstance.ToString(); ntn.ToolTipText = "Do not right click on this item, it has no effect"; mtn.Nodes.Add(ntn); } } } // now remove the object list??? (but it will be removed when packet is destroyed.... } break; default: _apm.MessagePanic("m0030 " + pkt.propertyID.ToString()); break; } break; default: _apm.MessagePanic("m0029"); break; } break; case BACnetEnums.BACNET_PDU_TYPE.PDU_TYPE_CONFIRMED_SERVICE_REQUEST: case BACnetEnums.BACNET_PDU_TYPE.PDU_TYPE_REJECT: // These messages do not affect the Device TreeView, so we will ignore them.. break; default: _apm.MessageTodo("m0027 - Device Treeview needs to parse this message still: " + pkt.pduType.ToString()); break; } }