private static void First4BytesHeaderEncode(IList <byte> b, BacnetBvlcFunctions function, int msgLength) { b[0] = BVLL_TYPE_BACNET_IP; b[1] = (byte)function; b[2] = (byte)((msgLength & 0xFF00) >> 8); b[3] = (byte)((msgLength & 0x00FF) >> 0); }
private void First4BytesHeaderEncode(byte[] b, BacnetBvlcFunctions function, int msg_length) { b[0] = BVLL_TYPE_BACNET_IP; b[1] = (byte)function; b[2] = (byte)(((msg_length) & 0xFF00) >> 8); b[3] = (byte)(((msg_length) & 0x00FF) >> 0); }
void Bvlc_MessageReceived(System.Net.IPEndPoint sender, BacnetBvlcFunctions function, BacnetBvlcResults result, object data) { if (!sender.Equals(BBMDep)) { return; // don't care } if (InvokeRequired) // GUI call back { BeginInvoke(new Action <System.Net.IPEndPoint, BacnetBvlcFunctions, BacnetBvlcResults, object>(Bvlc_MessageReceived), new object[] { sender, function, result, data }); return; } if (function == BacnetBvlcFunctions.BVLC_READ_BROADCAST_DIST_TABLE_ACK) // an table (could be empty) { List <Tuple <IPEndPoint, IPAddress> > Entries = (List <Tuple <IPEndPoint, IPAddress> >)data; BBMDTable.Rows.Clear(); foreach (Tuple <IPEndPoint, IPAddress> tpl in Entries) { BBMDTable.Rows.Add(new object[] { tpl.Item1.Address.ToString(), tpl.Item1.Port, tpl.Item2.ToString() }); } lbBBMDlInfo.Visible = false; } if (function == BacnetBvlcFunctions.BVLC_READ_FOREIGN_DEVICE_TABLE_ACK) { List <Tuple <IPEndPoint, ushort, ushort> > Entries = (List <Tuple <IPEndPoint, ushort, ushort> >)data; FDRTable.Rows.Clear(); foreach (Tuple <IPEndPoint, ushort, ushort> tpl in Entries) { FDRTable.Rows.Add(new object[] { tpl.Item1.ToString(), tpl.Item2.ToString(), tpl.Item3.ToString() }); } lbFDRInfo.Visible = false; } if ((function == BacnetBvlcFunctions.BVLC_RESULT) && (result == BacnetBvlcResults.BVLC_RESULT_SUCCESSFUL_COMPLETION) && (WriteInOperation)) { WriteInOperation = false; transport.Bvlc.SendReadBroadCastTable(BBMDep); } if ((function == BacnetBvlcFunctions.BVLC_RESULT) && (result == BacnetBvlcResults.BVLC_RESULT_READ_BROADCAST_DISTRIBUTION_TABLE_NAK)) { lbBBMDlInfo.Visible = true; lbBBMDlInfo.Text = "Read Broadcast Table Rejected"; } if ((function == BacnetBvlcFunctions.BVLC_RESULT) && (result == BacnetBvlcResults.BVLC_RESULT_WRITE_BROADCAST_DISTRIBUTION_TABLE_NAK) && (WriteInOperation)) { lbBBMDlInfo.Visible = true; WriteInOperation = false; lbBBMDlInfo.Text = "Write Broadcast Table Rejected"; } if ((function == BacnetBvlcFunctions.BVLC_RESULT) && (result == BacnetBvlcResults.BVLC_RESULT_READ_FOREIGN_DEVICE_TABLE_NAK)) { lbFDRInfo.Visible = true; lbFDRInfo.Text = "Read FDR Table Rejected"; } }
// Encode is called by internal services if the BBMD is also an active device public int Encode(byte[] buffer, int offset, BacnetBvlcFunctions function, int msgLength) { // offset always 0, we are the first after udp // do the job First4BytesHeaderEncode(buffer, function, msgLength); // optional BBMD service if (_bbmdFdServiceActivated && function == BacnetBvlcFunctions.BVLC_ORIGINAL_BROADCAST_NPDU) { var me = _myBbmdTransport.LocalEndPoint; // just sometime working, enable to get the local ep, always 0.0.0.0 if the socket is open with // System.Net.IPAddress.Any // So in this case don't send a bad message if (me.Address.ToString() != "0.0.0.0") { Forward_NPDU(buffer, msgLength, false, me); // send to all BBMDs and FDs } } return(4); // ready to send }
// Decode is called each time an Udp Frame is received public int Decode(byte[] buffer, int offset, out BacnetBvlcFunctions function, out int msgLength, IPEndPoint sender) { // offset always 0, we are the first after udp // and a previous test by the caller guaranteed at least 4 bytes into the buffer function = (BacnetBvlcFunctions)buffer[1]; msgLength = (buffer[2] << 8) | (buffer[3] << 0); if (buffer[0] != BVLL_TYPE_BACNET_IP || buffer.Length != msgLength) { return(-1); } switch (function) { case BacnetBvlcFunctions.BVLC_RESULT: var resultCode = (buffer[4] << 8) + buffer[5]; MessageReceived?.Invoke(sender, function, (BacnetBvlcResults)resultCode, null); return(0); // not for the upper layers case BacnetBvlcFunctions.BVLC_ORIGINAL_UNICAST_NPDU: return(4); // only for the upper layers case BacnetBvlcFunctions.BVLC_ORIGINAL_BROADCAST_NPDU: // Normaly received in an IP local or global broadcast packet // Send to FDs & BBMDs, not broadcast or it will be made twice ! if (_bbmdFdServiceActivated) { Forward_NPDU(buffer, msgLength, false, sender); } return(4); // also for the upper layers case BacnetBvlcFunctions.BVLC_FORWARDED_NPDU: // Sent only by a BBMD, broadcast on it network, or broadcast demand by one of it's FDs if (_bbmdFdServiceActivated && msgLength >= 10) { bool ret; lock (_bbmds) ret = _bbmds.Exists(items => items.Key.Address.Equals(sender.Address)); // verify sender (@ not Port!) presence in the table if (ret) // message from a know BBMD address, sent to all FDs and broadcast { SendToFDs(buffer, msgLength); // send without modification // Assume all BVLC_FORWARDED_NPDU are directly sent to me in the // unicast mode and not by the way of the local broadcast address // ie my mask must be 255.255.255.255 in the others BBMD tables // If not, it's not really a big problem, devices on the local net will // receive two times the message (after all it's just WhoIs, Iam, ...) _myBbmdTransport.Send(buffer, msgLength, new IPEndPoint(IPAddress.Parse(_broadcastAdd), _myBbmdTransport.SharedPort)); } } return(10); // also for the upper layers case BacnetBvlcFunctions.BVLC_DISTRIBUTE_BROADCAST_TO_NETWORK: // Sent by a Foreign Device, not a BBMD if (_bbmdFdServiceActivated) { // Send to FDs except the sender, BBMDs and broadcast lock (_foreignDevices) { if (_foreignDevices.Exists(item => item.Key.Equals(sender))) // verify previous registration { Forward_NPDU(buffer, msgLength, true, sender); } else { SendResult(sender, BacnetBvlcResults.BVLC_RESULT_DISTRIBUTE_BROADCAST_TO_NETWORK_NAK); } } } return(0); // not for the upper layers case BacnetBvlcFunctions.BVLC_REGISTER_FOREIGN_DEVICE: if (_bbmdFdServiceActivated && msgLength == 6) { var ttl = (buffer[4] << 8) + buffer[5]; // unit is second RegisterForeignDevice(sender, ttl); SendResult(sender, BacnetBvlcResults.BVLC_RESULT_SUCCESSFUL_COMPLETION); // ack } return(0); // not for the upper layers // We don't care about Read/Write operation in the BBMD/FDR tables (who realy use it ?) case BacnetBvlcFunctions.BVLC_READ_FOREIGN_DEVICE_TABLE: SendResult(sender, BacnetBvlcResults.BVLC_RESULT_READ_FOREIGN_DEVICE_TABLE_NAK); return(0); case BacnetBvlcFunctions.BVLC_DELETE_FOREIGN_DEVICE_TABLE_ENTRY: SendResult(sender, BacnetBvlcResults.BVLC_RESULT_DELETE_FOREIGN_DEVICE_TABLE_ENTRY_NAK); return(0); case BacnetBvlcFunctions.BVLC_READ_BROADCAST_DIST_TABLE: SendResult(sender, BacnetBvlcResults.BVLC_RESULT_READ_BROADCAST_DISTRIBUTION_TABLE_NAK); return(0); case BacnetBvlcFunctions.BVLC_WRITE_BROADCAST_DISTRIBUTION_TABLE: case BacnetBvlcFunctions.BVLC_READ_BROADCAST_DIST_TABLE_ACK: { var nbEntries = (msgLength - 4) / 10; var entries = new List <Tuple <IPEndPoint, IPAddress> >(); for (var i = 0; i < nbEntries; i++) { long add = BitConverter.ToInt32(buffer, 4 + i * 10); Array.Reverse(buffer, 8 + i * 10, 2); var port = BitConverter.ToUInt16(buffer, 8 + i * 10); // new IPAddress(long) with 255.255.255.255 (ie -1) not OK var mask = new byte[4]; Array.Copy(buffer, 10 + i * 10, mask, 0, 4); var entry = new Tuple <IPEndPoint, IPAddress>(new IPEndPoint(new IPAddress(add), port), new IPAddress(mask)); entries.Add(entry); } if (MessageReceived != null && function == BacnetBvlcFunctions.BVLC_READ_BROADCAST_DIST_TABLE_ACK) { MessageReceived(sender, function, BacnetBvlcResults.BVLC_RESULT_SUCCESSFUL_COMPLETION, entries); } // Today we don't accept it if (function == BacnetBvlcFunctions.BVLC_WRITE_BROADCAST_DISTRIBUTION_TABLE) { SendResult(sender, BacnetBvlcResults.BVLC_RESULT_WRITE_BROADCAST_DISTRIBUTION_TABLE_NAK); } return(0); } case BacnetBvlcFunctions.BVLC_READ_FOREIGN_DEVICE_TABLE_ACK: { var nbEntries = (msgLength - 4) / 10; var entries = new List <Tuple <IPEndPoint, ushort, ushort> >(); for (var i = 0; i < nbEntries; i++) { long add = BitConverter.ToInt32(buffer, 4 + i * 10); Array.Reverse(buffer, 8 + i * 10, 2); var port = BitConverter.ToUInt16(buffer, 8 + i * 10); Array.Reverse(buffer, 10 + i * 10, 2); var ttl = BitConverter.ToUInt16(buffer, 10 + i * 10); Array.Reverse(buffer, 12 + i * 10, 2); var remainTtl = BitConverter.ToUInt16(buffer, 12 + i * 10); var entry = new Tuple <IPEndPoint, ushort, ushort>(new IPEndPoint(new IPAddress(add), port), ttl, remainTtl); entries.Add(entry); } MessageReceived?.Invoke(sender, function, BacnetBvlcResults.BVLC_RESULT_SUCCESSFUL_COMPLETION, entries); return(0); } // error encoding function or experimental one default: return(-1); } }
// Decode is called each time an Udp Frame is received public int Decode(byte[] buffer, int offset, out BacnetBvlcFunctions function, out int msg_length, IPEndPoint sender) { // offset always 0, we are the first after udp // and a previous test by the caller guaranteed at least 4 bytes into the buffer function = (BacnetBvlcFunctions)buffer[1]; msg_length = (buffer[2] << 8) | (buffer[3] << 0); if ((buffer[0] != BVLL_TYPE_BACNET_IP) || (buffer.Length != msg_length)) { return(-1); } switch (function) { case BacnetBvlcFunctions.BVLC_RESULT: return(4); // only for the upper layers case BacnetBvlcFunctions.BVLC_ORIGINAL_UNICAST_NPDU: return(4); // only for the upper layers case BacnetBvlcFunctions.BVLC_ORIGINAL_BROADCAST_NPDU: // Normaly received in an IP local or global broadcast packet // Send to FDs & BBMDs, not broadcast or it will be made twice ! if (BBMD_FD_ServiceActivated) { Forward_NPDU(buffer, msg_length, false, sender); } return(4); // also for the upper layers case BacnetBvlcFunctions.BVLC_FORWARDED_NPDU: // Sent only by a BBMD, broadcast on it network, or broadcast demand by one of it's FDs if (BBMD_FD_ServiceActivated && (msg_length >= 10)) { bool ret; lock (BBMDs) ret = BBMDs.Exists(items => items.Key.Address.Equals(sender.Address)); // verify sender (@ not Port!) presence in the table if (ret) // message from a know BBMD address, sent to all FDs and broadcast { SendToFDs(buffer, msg_length); // send without modification // Assume all BVLC_FORWARDED_NPDU are directly sent to me in the // unicast mode and not by the way of the local broadcast address // ie my mask must be 255.255.255.255 in the others BBMD tables // If not, it's not really a big problem, devices on the local net will // receive two times the message (after all it's just WhoIs, Iam, ...) MyBBMDTransport.Send(buffer, msg_length, new IPEndPoint(IPAddress.Parse(BroadcastAdd), MyBBMDTransport.SharedPort)); } } return(10); // also for the upper layers case BacnetBvlcFunctions.BVLC_DISTRIBUTE_BROADCAST_TO_NETWORK: // Sent by a Foreign Device, not a BBMD if (BBMD_FD_ServiceActivated) { // Send to FDs except the sender, BBMDs and broadcast lock (ForeignDevices) { if (ForeignDevices.Exists(item => item.Key.Equals(sender))) // verify previous registration { Forward_NPDU(buffer, msg_length, true, sender); } else { SendResult(sender, BacnetBvlcResults.BVLC_RESULT_DISTRIBUTE_BROADCAST_TO_NETWORK_NAK); } } } return(4); // also for the upper layers case BacnetBvlcFunctions.BVLC_REGISTER_FOREIGN_DEVICE: if (BBMD_FD_ServiceActivated && (msg_length == 6)) { int TTL = (buffer[4] << 8) + buffer[5]; // unit is second RegisterForeignDevice(sender, TTL); SendResult(sender, BacnetBvlcResults.BVLC_RESULT_SUCCESSFUL_COMPLETION); // ack } return(-1); // not for the upper layers // We don't care about Read/Write operation in the BBMD/FDR tables (who realy use it ?) case BacnetBvlcFunctions.BVLC_READ_FOREIGN_DEVICE_TABLE: //SendResult(sender, BacnetBvlcResults.BVLC_RESULT_READ_FOREIGN_DEVICE_TABLE_NAK); return(-1); case BacnetBvlcFunctions.BVLC_DELETE_FOREIGN_DEVICE_TABLE_ENTRY: //SendResult(sender, BacnetBvlcResults.BVLC_RESULT_DELETE_FOREIGN_DEVICE_TABLE_ENTRY_NAK); return(-1); case BacnetBvlcFunctions.BVLC_READ_BROADCAST_DIST_TABLE: //SendResult(sender, BacnetBvlcResults.BVLC_RESULT_READ_BROADCAST_DISTRIBUTION_TABLE_NAK); return(-1); case BacnetBvlcFunctions.BVLC_WRITE_BROADCAST_DISTRIBUTION_TABLE: //SendResult(sender, BacnetBvlcResults.BVLC_RESULT_WRITE_BROADCAST_DISTRIBUTION_TABLE_NAK); return(-1); // error encoding function or experimental one default: return(-1); } }