Inheritance: MsMessage
Exemple #1
0
    internal void ProcessInPacket(MsMessage msg) {
      if(_statistic.value && msg.MsgTyp!=MsMessageType.EncapsulatedMessage && msg.MsgTyp!=MsMessageType.PUBLISH) {
        Stat(false, msg.MsgTyp);
      }
      switch(msg.MsgTyp) {
      case MsMessageType.WILLTOPIC: {
          var tmp=msg as MsWillTopic;
          if(state==State.WillTopic) {
            _willPath=tmp.Path;
            _willRetain=tmp.Retain;
            state=State.WillMsg;
            ProccessAcknoledge(msg);
          }
        }
        break;
      case MsMessageType.WILLMSG: {
          var tmp=msg as MsWillMsg;
          if(state==State.WillMsg) {
            _wilMsg=tmp.Payload;
            Log.Info("{0}.state {1} => WILLTOPICREQ", Owner.path, state);
            state=State.PreConnect;
            ProccessAcknoledge(msg);
            Send(new MsConnack(MsReturnCode.Accepted));
          }
        }
        break;
      case MsMessageType.SUBSCRIBE: {
          var tmp=msg as MsSubscribe;

          SyncMsgId(msg.MessageId);
          Topic.Subscription s=null;
          ushort topicId=tmp.topicId;
          if(tmp.topicIdType!=TopicIdType.Normal || tmp.path.IndexOfAny(new[] { '+', '#' })<0) {
            TopicInfo ti=null;
            if(tmp.topicIdType==TopicIdType.Normal) {
              ti=GetTopicInfo(tmp.path, false);
            } else {
              ti=GetTopicInfo(tmp.topicId, tmp.topicIdType);
            }
            topicId=ti.TopicId;
          }
          Send(new MsSuback(tmp.qualityOfService, topicId, msg.MessageId, MsReturnCode.Accepted));
          if(state==State.PreConnect) {
            state=State.Connected;
          }
          s=Owner.Subscribe(tmp.path, PublishTopic, tmp.qualityOfService);
          _subsscriptions.Add(s);
        }
        break;
      case MsMessageType.REGISTER: {
          var  tmp=msg as MsRegister;
          ResetTimer();
          try {
            TopicInfo ti = GetTopicInfo(tmp.TopicPath, false);
            if(ti.topic!=null) {
              if(ti.topic.valueType==typeof(SmartTwi)) {
                if(ti.topic.GetValue()==null) {
                  ti.topic.SetValue(new SmartTwi(ti.topic), new TopicChanged(TopicChanged.ChangeArt.Value, Owner));
                } else {
                  (ti.topic as DVar<SmartTwi>).value.Reset();
                }
              } else if(ti.topic.valueType==typeof(TWIDriver)) {
                if(ti.topic.GetValue()==null) {
                  ti.topic.SetValue(new TWIDriver(ti.topic), new TopicChanged(TopicChanged.ChangeArt.Value, Owner));
                } else {
                  (ti.topic as DVar<TWIDriver>).value.Reset();
                }
              }
            }
            Send(new MsRegAck(ti.TopicId, tmp.MessageId, MsReturnCode.Accepted));
          }
          catch(Exception) {
            Send(new MsRegAck(0, tmp.MessageId, MsReturnCode.NotSupportes));
            Log.Warning("Unknown variable type by register {0}, {1}", Owner.path, tmp.TopicPath);
          }
        }
        break;
      case MsMessageType.REGACK: {
          var  tmp=msg as MsRegAck;
          ProccessAcknoledge(tmp);
          TopicInfo ti=_topics.FirstOrDefault(z => z.TopicId==tmp.TopicId);
          if(ti==null) {
            if(tmp.TopicId!=0xFFFF) { // 0xFFFF - remove variable
              Log.Warning("{0} RegAck({1:X4}) for unknown variable", Owner.path, tmp.TopicId);
            }
            return;
          }
          if(tmp.RetCode==MsReturnCode.Accepted) {
            ti.registred=true;
            if(ti.it!=TopicIdType.PreDefined) {
              Send(new MsPublish(ti.topic, ti.TopicId, QoS.AtLeastOnce));
            }
          } else {
            Log.Warning("{0} registred failed: {1}", ti.path, tmp.RetCode.ToString());
            _topics.Remove(ti);
            ti.topic.Remove();
          }
        }
        break;
      case MsMessageType.PUBLISH: {
          var tmp=msg as MsPublish;
          if(_statistic.value) {
            Stat(false, msg.MsgTyp, tmp.Dup);
          }
          TopicInfo ti=_topics.Find(z => z.TopicId==tmp.TopicId && z.it==tmp.topicIdType);
          if(ti==null && tmp.topicIdType!=TopicIdType.Normal) {
            ti=GetTopicInfo(tmp.TopicId, tmp.topicIdType, false);
          }
          if(tmp.qualityOfService==QoS.AtMostOnce || (tmp.qualityOfService==QoS.MinusOne && (tmp.topicIdType==TopicIdType.PreDefined || tmp.topicIdType==TopicIdType.ShortName))) {
            ResetTimer();
          } else if(tmp.qualityOfService==QoS.AtLeastOnce) {
            SyncMsgId(tmp.MessageId);
            Send(new MsPubAck(tmp.TopicId, tmp.MessageId, ti!=null?MsReturnCode.Accepted:MsReturnCode.InvalidTopicId));
          } else if(tmp.qualityOfService==QoS.ExactlyOnce) {
            SyncMsgId(tmp.MessageId);
            // QoS2 not supported, use QoS1
            Send(new MsPubAck(tmp.TopicId, tmp.MessageId, ti!=null?MsReturnCode.Accepted:MsReturnCode.InvalidTopicId));
          } else {
            throw new NotSupportedException("QoS -1 not supported "+Owner.path);
          }
          if(tmp.topicIdType==TopicIdType.PreDefined && tmp.TopicId>=LOG_D_ID && tmp.TopicId<=LOG_E_ID) {
            string str=string.Format("{0} msgId={2:X4}  msg={1}", this.Owner.name, tmp.Data==null?"null":(BitConverter.ToString(tmp.Data)+"["+ Encoding.ASCII.GetString(tmp.Data.Select(z => (z<0x20 || z>0x7E)?(byte)'.':z).ToArray())+"]"), tmp.MessageId);
            switch(tmp.TopicId) {
            case LOG_D_ID:
              Log.Debug("{0}", str);
              break;
            case LOG_I_ID:
              Log.Info("{0}", str);
              break;
            case LOG_W_ID:
              Log.Warning("{0}", str);
              break;
            case LOG_E_ID:
              Log.Error("{0}", str);
              break;
            }
          } else if(ti!=null) {
            if(tmp.Dup && _lastInPub!=null && tmp.MessageId==_lastInPub.MessageId) {  // arready recieved
            } else {
              SetValue(ti, tmp.Data, tmp.Retained);
            }
            _lastInPub=tmp;
          }
        }
        break;
      case MsMessageType.PUBACK: {
          ProccessAcknoledge(msg);
        }
        break;
      case MsMessageType.PINGREQ: {
          var tmp=msg as MsPingReq;
          if(state==State.ASleep) {
            if(string.IsNullOrEmpty(tmp.ClientId) || tmp.ClientId==Owner.name) {
              state=State.AWake;
              ProccessAcknoledge(msg);    // resume send proccess
            } else {
              SendGw(this, new MsDisconnect());
              state=State.Lost;
              Log.Warning("{0} PingReq from unknown device: {1}", Owner.path, tmp.ClientId);
            }
          } else {
            ResetTimer();
            if(_gate!=null) {
              _gate.SendGw(this, new MsMessage(MsMessageType.PINGRESP));
              if(_statistic.value) {
                Stat(true, MsMessageType.PINGRESP, false);
              }
            }
          }
        }
        break;
      case MsMessageType.DISCONNECT:
        Disconnect((msg as MsDisconnect).Duration);
        break;
      case MsMessageType.CONNECT:
        Connect(msg as MsConnect);
        break;
      case MsMessageType.EncapsulatedMessage: {
          Topic devR=Topic.root.Get("/dev");
          var fm=msg as MsForward;
          if(fm.msg==null) {
            if(_verbose.value) {
              Log.Warning("bad message {0}:{1}", _gate, fm.ToString());
            }
            return;
          }
          if(fm.msg.MsgTyp==MsMessageType.SEARCHGW) {
            _gate.SendGw(this, new MsGwInfo(gwIdx));
          } else if(fm.msg.MsgTyp==MsMessageType.DHCP_REQ) {
            var dr=fm.msg as MsDhcpReq;
            //******************************
            List<byte> ackAddr=new List<byte>();
            byte[] respPrev=null;

            foreach(byte hLen in dr.hLen) {
              if(hLen==0) {
                continue;
              } else if(hLen<=8) {
                byte[] resp;
                if(respPrev!=null && respPrev.Length==hLen) {
                  resp=respPrev;
                } else {
                  resp=new byte[hLen];

                  for(int i=0; i<5; i++) {
                    for(int j=0; j<resp.Length; j++) {
                      resp[j]=(byte)_rand.Next((i<3 && hLen==1)?32:1, (i<3 && hLen==1)?126:(j==0?254:255));
                    }
                    if(devR.children.Select(z => z as DVar<MsDevice>).Where(z => z!=null && z.value!=null).All(z => !z.value.CheckAddr(resp))) {
                      break;
                    } else if(i==4) {
                      for(int j=0; j<resp.Length; j++) {
                        resp[j]=0xFF;
                      }
                    }
                  }
                  respPrev=resp;
                }
                ackAddr.AddRange(resp);
              } else {
                if(_verbose.value) {
                  Log.Warning("{0}:{1} DhcpReq.hLen is too high", BitConverter.ToString(fm.addr), fm.msg.ToString());
                }
                ackAddr=null;
                break;
              }
            }
            if(ackAddr!=null) {
              _gate.SendGw(this, new MsForward(fm.addr, new MsDhcpAck(gwIdx, dr.xId, ackAddr.ToArray())));
            }
            //******************************
          } else {
            if(fm.msg.MsgTyp==MsMessageType.CONNECT) {
              var cm=fm.msg as MsConnect;
              if(fm.addr!=null && fm.addr.Length==2 && fm.addr[1]==0xFF) {    // DHCP V<0.3
                _gate.SendGw(this, new MsForward(fm.addr, new MsConnack(MsReturnCode.Accepted)));

                byte[] nAddr=new byte[1];
                do {
                  nAddr[0]=(byte)(_rand.Next(32, 254));
                } while(!devR.children.Select(z => z as DVar<MsDevice>).Where(z => z!=null && z.value!=null).All(z => !z.value.CheckAddr(nAddr)));
                Log.Info("{0} new addr={1:X2}", cm.ClientId, nAddr[0]);
                _gate.SendGw(this, new MsForward(fm.addr, new MsPublish(null, PredefinedTopics[".cfg/XD_DeviceAddr"], QoS.AtLeastOnce) { MessageId=1, Data=nAddr }));
              } else {
                DVar<MsDevice> dDev=devR.Get<MsDevice>(cm.ClientId);
                if(dDev.value==null) {
                  dDev.value=new MsDevice(this, fm.addr);
                  Thread.Sleep(0);
                  dDev.value.Owner=dDev;
                } else {
                  this.RemoveNode(dDev.value);
                  dDev.value._gate=this;
                  dDev.value.Addr=fm.addr;
                }
                this.AddNode(dDev.value);
                dDev.value.Connect(cm);
                foreach(var dub in devR.children.Select(z => z.GetValue() as MsDevice).Where(z => z!=null && z!=dDev.value && z.Addr!=null && z.Addr.SequenceEqual(fm.addr) && z._gate==this).ToArray()) {
                  dub.Addr=null;
                  dub._gate=null;
                  dub.state=State.Disconnected;
                }
              }
            } else {
              MsDevice dev=devR.children.Select(z => z.GetValue() as MsDevice).FirstOrDefault(z => z!=null && z.Addr!=null && z.Addr.SequenceEqual(fm.addr) && z._gate==this);
              if(dev!=null 
                && ((dev.state!=State.Disconnected && dev.state!=State.Lost) 
                  || fm.msg.MsgTyp==MsMessageType.CONNECT 
                  || (fm.msg.MsgTyp==MsMessageType.PUBLISH && (fm.msg as MsPublish).qualityOfService==QoS.MinusOne))) {
                dev.ProcessInPacket(fm.msg);
              } else if(fm.msg.MsgTyp==MsMessageType.PUBLISH && (fm.msg as MsPublish).qualityOfService==QoS.MinusOne) {
                var tmp=fm.msg as MsPublish;
                if(tmp.topicIdType==TopicIdType.PreDefined && tmp.TopicId>=LOG_D_ID && tmp.TopicId<=LOG_E_ID) {
                  string str=string.Format("{0}: msgId={2:X4} msg={1}", BitConverter.ToString(this.Addr), tmp.Data==null?"null":(BitConverter.ToString(tmp.Data)+"["+ Encoding.ASCII.GetString(tmp.Data.Select(z => (z<0x20 || z>0x7E)?(byte)'.':z).ToArray())+"]"), tmp.MessageId);
                  switch(tmp.TopicId) {
                  case LOG_D_ID:
                    Log.Debug(str);
                    break;
                  case LOG_I_ID:
                    Log.Info(str);
                    break;
                  case LOG_W_ID:
                    Log.Warning(str);
                    break;
                  case LOG_E_ID:
                    Log.Error(str);
                    break;
                  }
                }
              } else {
                if(dev==null || dev.Owner==null) {
                  if(_verbose.value) {
                    Log.Debug("{0} via {1} unknown device", BitConverter.ToString(fm.addr), this.name);
                  }
                } else {
                  if(_verbose.value) {
                    Log.Debug("{0} via {1} inactive", dev.Owner.name, this.name);
                  }
                }
                _gate.SendGw(this, new MsForward(fm.addr, new MsDisconnect()));
              }
            }
          }
        }
        break;
      }
    }
Exemple #2
0
    private void ParseInPacket(byte[] buf) {
      var msgTyp=(MsMessageType)(buf[0]>1?buf[1]:buf[2]);
      switch(msgTyp) {
      case MsMessageType.WILLTOPIC: {
          var msg=new MsWillTopic(buf) { Addr=this.Addr };
          Stat(false, msgTyp);
          PrintPacket(this, msg, buf);
          if(state==State.WillTopic) {
            _willPath=msg.Path;
            _willRetain=msg.Retain;
            state=State.WillMsg;
            ProccessAcknoledge(msg);
          }
        }
        break;
      case MsMessageType.WILLMSG: {
          Stat(false, msgTyp);
          var msg=new MsWillMsg(buf) { Addr=this.Addr };
          PrintPacket(this, msg, buf);
          if(state==State.WillMsg) {
            _wilMsg=msg.Payload;
            state=State.PreConnect;
            ProccessAcknoledge(msg);
            Send(new MsConnack(MsReturnCode.Accepted));
            Log.Info("{0} connected", Owner.path);
          }
        }
        break;
      case MsMessageType.SUBSCRIBE: {
          var msg=new MsSubscribe(buf) { Addr=this.Addr };
          Stat(false, msgTyp, msg.dup);
          PrintPacket(this, msg, buf);

          SyncMsgId(msg.MessageId);
          Topic.Subscription s=null;
          ushort topicId=msg.topicId;
          if(msg.topicIdType!=TopicIdType.Normal || msg.path.IndexOfAny(new[] { '+', '#' })<0) {
            TopicInfo ti=null;
            if(msg.topicIdType==TopicIdType.Normal) {
              ti=GetTopicInfo(msg.path, false);
            } else {
              ti=GetTopicInfo(msg.topicId, msg.topicIdType);
            }
            topicId=ti.TopicId;
          }
          Send(new MsSuback(msg.qualityOfService, topicId, msg.MessageId, MsReturnCode.Accepted));
          if(state==State.PreConnect) {
            state=State.Connected;
          }
          s=Owner.Subscribe(msg.path, PublishTopic, msg.qualityOfService);
          _subsscriptions.Add(s);
        }
        break;
      case MsMessageType.REGISTER: {
          var msg=new MsRegister(buf) { Addr=this.Addr };
          Stat(false, msgTyp);
          PrintPacket(this, msg, buf);
          ResetTimer();
          try {
            TopicInfo ti = GetTopicInfo(msg.TopicPath, false);
            if(ti.topic!=null && ti.topic.valueType==typeof(SmartTwi)) {
              if(ti.topic.GetValue()==null) {
                ti.topic.SetValue(new SmartTwi(ti.topic), new TopicChanged(TopicChanged.ChangeArt.Value, Owner));
              } else {
                (ti.topic as DVar<SmartTwi>).value.Reset();
              }
            }
            Send(new MsRegAck(ti.TopicId, msg.MessageId, MsReturnCode.Accepted));
          }
          catch(Exception) {
            Send(new MsRegAck(0, msg.MessageId, MsReturnCode.NotSupportes));
            Log.Warning("Unknown variable type by register {0}, {1}", Owner.path, msg.TopicPath);
          }
        }
        break;
      case MsMessageType.REGACK: {
          var msg=new MsRegAck(buf) { Addr=this.Addr };
          Stat(false, msgTyp);
          PrintPacket(this, msg, buf);
          ProccessAcknoledge(msg);
          TopicInfo ti=_topics.FirstOrDefault(z => z.TopicId==msg.TopicId);
          if(ti==null) {
            if(msg.TopicId!=0) {
              Log.Warning("{0} RegAck({1:X4}) for unknown variable", Owner.path, msg.TopicId);
            }
            return;
          }
          if(msg.RetCode==MsReturnCode.Accepted) {
            ti.registred=true;
            if(ti.it!=TopicIdType.PreDefined) {
              Send(new MsPublish(ti.topic, ti.TopicId, QoS.AtLeastOnce));
            }
          } else {
            Log.Warning("{0} registred failed: {1}", ti.path, msg.RetCode.ToString());
            ti.it=TopicIdType.NotUsed;
            //_topics.Remove(ti);
            //ti.topic.Remove();
          }
        }
        break;
      case MsMessageType.PUBLISH: {
          var msg=new MsPublish(buf) { Addr=this.Addr };
          Stat(false, msgTyp, msg.Dup);
          PrintPacket(this, msg, buf);
          TopicInfo ti=_topics.Find(z => z.TopicId==msg.TopicId && z.it==msg.topicIdType);
          if(ti==null && msg.topicIdType!=TopicIdType.Normal) {
            ti=GetTopicInfo(msg.TopicId, msg.topicIdType, false);
          }
          if(msg.qualityOfService==QoS.AtMostOnce) {
            ResetTimer();
          } else if(msg.qualityOfService==QoS.AtLeastOnce) {
            SyncMsgId(msg.MessageId);
            Send(new MsPubAck(msg.TopicId, msg.MessageId, ti!=null?MsReturnCode.Accepted:MsReturnCode.InvalidTopicId));
          } else if(msg.qualityOfService==QoS.ExactlyOnce) {
            SyncMsgId(msg.MessageId);
            // QoS2 not supported, use QoS1
            Send(new MsPubAck(msg.TopicId, msg.MessageId, ti!=null?MsReturnCode.Accepted:MsReturnCode.InvalidTopicId));
          } else {
            throw new NotSupportedException("QoS -1 not supported "+Owner.path);
          }
          if(msg.topicIdType==TopicIdType.PreDefined && msg.TopicId>=LOG_D_ID && msg.TopicId<=LOG_E_ID) {
            string str=string.Format("{0}:{1} msg={2} msgId={3} ", this.Owner.path, BitConverter.ToString(msg.Addr), msg.Data==null?"null":BitConverter.ToString(msg.Data), msg.MessageId);
            switch(msg.TopicId) {
            case LOG_D_ID:
              Log.Debug(str);
              break;
            case LOG_I_ID:
              Log.Info(str);
              break;
            case LOG_W_ID:
              Log.Warning(str);
              break;
            case LOG_E_ID:
              Log.Error(str);
              break;
            }
          } else if(ti!=null) {
            SetValue(ti, msg.Data);
          }
        }
        break;
      case MsMessageType.PUBACK: {
          var msg=new MsPubAck(buf) { Addr=this.Addr };
          Stat(false, msgTyp);
          PrintPacket(this, msg, buf);
          ProccessAcknoledge(msg);
        }
        break;
      case MsMessageType.PINGREQ: {
          var msg=new MsPingReq(buf) { Addr=this.Addr };
          Stat(false, msgTyp);
          PrintPacket(this, msg, buf);
          if(state==State.ASleep) {
            if(string.IsNullOrEmpty(msg.ClientId) || msg.ClientId==Owner.name) {
              //if(++_reconnectCnt>1024) {
              //  _reconnectCnt=0;
              //  Send(new MsDisconnect());
              //  state=State.Disconnected;
              //  Log.Info("{0} refresh connection", Owner.path);
              //} else {
              state=State.AWake;
              ProccessAcknoledge(msg);    // resume send proccess
              //}
            } else {
              Send(new MsDisconnect());
              state=State.Lost;
              Log.Warning("{0} PingReq from unknown device: {1}", Owner.path, msg.ClientId);
            }
          } else {
            ResetTimer();
            if(_gate!=null) {
              _gate.Send(new MsMessage(MsMessageType.PINGRESP) { Addr=this.Addr });
              Stat(true, MsMessageType.PINGRESP, false);
            }
          }
        }
        break;
      case MsMessageType.DISCONNECT: {
          var msg=new MsDisconnect(buf) { Addr=this.Addr };
          Stat(false, msgTyp);
          PrintPacket(this, msg, buf);
          Disconnect(msg.Duration);
        }
        break;
      default:
        Log.Warning("{0} unknown packet: {1}", Owner!=null?Owner.path:"null", BitConverter.ToString(buf));
        break;
      }
    }
      private void ParseInPacket(byte[] addr, byte[] buf) {
        if(addr[0]==0) {
          Log.Warning("Packet with broadcast address via {0}:{1:X2}:{2}", _port.PortName, BitConverter.ToString(addr), BitConverter.ToString(buf));
          return;
        }

        Topic devR=Topic.root.Get("/dev");
        var msgTyp=(MsMessageType)(buf[0]>1?buf[1]:buf[3]);
        if(msgTyp==MsMessageType.SEARCHGW) {
          PrintPacket(null, new MsSearchGW(buf) { Addr=addr }, buf);
          this.Send(new MsGwInfo(_gwAddr) { Addr=new byte[] { 0 } });
        } else if(msgTyp==MsMessageType.CONNECT) {
          var msg=new MsConnect(buf) { Addr=addr };
          if(addr[0]==_gwAddr) {
            _advTick=DateTime.Now.AddSeconds(2.6);  // Send Advertise in 2.6 sec.
          }
          if(addr[0]==0xFF) {
            PrintPacket(null, msg, buf);
            Send(new MsConnack(MsReturnCode.Accepted) { Addr=msg.Addr });
            byte[] nAddr=new byte[1];
            var r=new Random(DateTime.Now.Millisecond);
            do {
              nAddr[0]=(byte)(8+r.Next(0xF6));  //0x08 .. 0xFE
            } while(devR.children.Select(z => z.GetValue() as MsDevice).Any(z => z!=null && nAddr.SequenceEqual(z.Addr)));
            Log.Info("{0} new addr={1}", msg.ClientId, BitConverter.ToString(nAddr));
            var pm=new MsPublish(null, PredefinedTopics[".cfg/XD_DeviceAddr"], QoS.AtLeastOnce) { Addr=msg.Addr, MessageId=1, Data=nAddr };
            Send(pm);
          } else { // msg.Addr!=0xFF
            DVar<MsDevice> dev=devR.Get<MsDevice>(msg.ClientId); // 
            if(!msg.CleanSession && (dev.value==null || !dev.value.Addr.SequenceEqual(msg.Addr) || dev.value.state==State.Disconnected || dev.value.state==State.Lost)) {
              PrintPacket(dev, msg, buf);
              Send(new MsConnack(MsReturnCode.InvalidTopicId) { Addr=msg.Addr });
              return;
            }
            if(dev.value==null) {
              dev.value=new MsDevice();
            }
            dev.value._gate=this;
            if(dev.value.Addr==null || !msg.Addr.SequenceEqual(dev.value.Addr)) {
              dev.value.Addr=msg.Addr;
            }
            PrintPacket(dev, msg, buf);
            Thread.Sleep(0);
            dev.value.Connect(msg);
            if(msg.Addr[0]==_gwAddr) {
              dev.value.via=_port.PortName;
              _gwTopic=dev;
            } else {
              dev.value.via= _gwTopic==null?string.Empty:_gwTopic.name;
            }
          }  // msg.Addr!=0xFF
        } else { // msgType==Connect
          MsDevice dev=devR.children.Select(z => z.GetValue() as MsDevice).FirstOrDefault(z => z!=null && z.Addr!=null && addr.SequenceEqual(z.Addr) && z._gate==this);
          if(dev!=null && dev.state!=State.Disconnected && dev.state!=State.Lost) {
            dev.ParseInPacket(buf);
          } else {
            if(dev==null || dev.Owner==null) {
              Log.Debug("unknown device: [{0:X2}:{1}]", addr[0], BitConverter.ToString(buf));
            } else {
              Log.Debug("inactive device: [{0}:{1}]", dev.Owner.name, BitConverter.ToString(buf));
            }
            Send(new MsDisconnect() { Addr=addr });
          }
        }
      }