using System;
|
using System.Collections.Generic;
|
using System.Net;
|
using System.Net.Sockets;
|
using System.Text;
|
|
using Shared.Common;
|
using ZigBee.Device;
|
|
|
namespace ZigBee.Common
|
{
|
public static class Application
|
{
|
#region ■ 变量声明___________________________
|
|
public static bool isInited;
|
/// <summary>
|
/// 是否正在搜索网关
|
/// </summary>
|
public static bool IsSearchingGateway = false;
|
/// <summary>
|
/// 上一次的住宅ID
|
/// </summary>
|
private static string oldHomeID = string.Empty;
|
/// <summary>
|
/// 接收的IP
|
/// </summary>
|
private static List<string> listReceiveIP = new List<string>();
|
|
#endregion
|
|
#region ■ 初始化_____________________________
|
|
public static void Init()
|
{
|
if (isInited)
|
{
|
return;
|
}
|
isInited = true;
|
|
//初始化Socket
|
FindGateWaySocket.Start();
|
|
//发送广播数据
|
StartSendBroadcastData();
|
|
//接收广播数据
|
StartReceiveBroadcastData();
|
|
///开启远程连接
|
StartRemoteMqtt();
|
}
|
|
#endregion
|
|
#region ■ 发送广播数据_______________________
|
|
/// <summary>
|
/// 发送广播数据
|
/// </summary>
|
private static void StartSendBroadcastData()
|
{
|
new System.Threading.Thread(() =>
|
{
|
var searchCount = 3;
|
var broadBytes = new byte[44];// byteHomeId[0] ,//H
|
broadBytes[0] = 0xfe;
|
broadBytes[1] = 0x29;
|
broadBytes[2] = 0x00;
|
broadBytes[3] = 0x00;
|
broadBytes[4] = 0x00;
|
broadBytes[5] = 0x00;
|
broadBytes[6] = 0x00;
|
broadBytes[43] = 0x02;
|
while (true)
|
{
|
try
|
{
|
//检测能否发送广播
|
if (CheckCanSendBroadcast(broadBytes) == false)
|
{
|
continue;
|
}
|
|
var broadcastIpAddress = new Shared.Net.NetWiFi().BroadcastIpAddress;
|
if (Shared.Application.IsWifi)
|
{
|
if (0 < listReceiveIP.Count)
|
{
|
searchCount = 3;
|
ZbGateway.IsRemote = false;
|
//当网关的连接方式改变时,记录当前的连接方式
|
Shared.Phone.UserCenter.HdlGatewayLogic.Current.CheckGatewayByConnectChanged(Shared.Phone.UserCenter.GatewayConnectMode.WIFI);
|
}
|
else
|
{
|
if (searchCount < 0)
|
{
|
ZbGateway.IsRemote = true;
|
//当网关的连接方式改变时,记录当前的连接方式
|
Shared.Phone.UserCenter.HdlGatewayLogic.Current.CheckGatewayByConnectChanged(Shared.Phone.UserCenter.GatewayConnectMode.Remote);
|
}
|
}
|
}
|
else
|
{
|
ZbGateway.IsRemote = true;
|
//当网关的连接方式改变时,记录当前的连接方式
|
Shared.Phone.UserCenter.HdlGatewayLogic.Current.CheckGatewayByConnectChanged(Shared.Phone.UserCenter.GatewayConnectMode.Remote);
|
|
for (int i = 0; i < ZbGateway.GateWayList.Count; i++)
|
{
|
ZbGateway.GateWayList[i].DisConnectLocalMqttClient("1");
|
}
|
//非wifi情况下,不需要广播
|
System.Threading.Thread.Sleep(1000);
|
continue;
|
}
|
searchCount--;
|
|
//每秒广播发现一次网关
|
int count = 2;
|
listReceiveIP.Clear();
|
while (0 < count--)
|
{
|
//点对点发送(先发一条已有的点播,回复几率高一点)
|
for (int i = 0; i < listReceiveIP.Count; i++)
|
{
|
FindGateWaySocket.BeginSend(new System.Net.IPEndPoint(System.Net.IPAddress.Parse(listReceiveIP[i]), 7624), broadBytes);
|
}
|
//广播发送
|
if (broadcastIpAddress.ToString() != "0.0.0.0")
|
{
|
FindGateWaySocket.BeginSend(new System.Net.IPEndPoint(broadcastIpAddress, 7624), broadBytes);
|
}
|
System.Threading.Thread.Sleep(1000);
|
}
|
}
|
catch { }
|
}
|
})
|
{ IsBackground = true }.Start();
|
}
|
|
#endregion
|
|
#region ■ 接收广播数据_______________________
|
|
/// <summary>
|
/// 接收广播数据
|
/// </summary>
|
private static void StartReceiveBroadcastData()
|
{
|
//数据接收
|
new System.Threading.Thread(async () =>
|
{
|
while (true)
|
{
|
try
|
{
|
if (FindGateWaySocket.udpClient == null || FindGateWaySocket.udpClient.Available <= 0)
|
{
|
System.Threading.Thread.Sleep(500);
|
continue;
|
}
|
var ipEndPoint = new System.Net.IPEndPoint(0, 0);
|
var bytes = FindGateWaySocket.udpClient.Receive(ref ipEndPoint);
|
if (bytes[43] == 0xA2)
|
{
|
//广播回复网关的基本信息处理
|
var ipAddress = $"{bytes[3]}.{bytes[4]}.{bytes[5]}.{bytes[6]}";
|
var homeID = System.Text.Encoding.UTF8.GetString(bytes, 7, 36);
|
homeID = homeID.Replace('\0', ' ').Trim();
|
var isMainGateWay = bytes[44] == 1;
|
var time = (bytes[48] & 0xff) << 24 | (bytes[47] & 0xff) << 16 | (bytes[46] & 0xff) << 8 | (bytes[45] & 0xff);
|
var gwNameLength = bytes[49];
|
var gwName = System.Text.Encoding.UTF8.GetString(bytes, 50, gwNameLength);
|
var gwIdLength = bytes[49 + gwNameLength + 1];
|
var id = Encoding.UTF8.GetString(bytes, 49 + gwNameLength + 2, gwIdLength);
|
var pubKeyLengthByte0 = bytes[49 + gwNameLength + 1 + gwIdLength + 1];
|
var pubKeyLengthByte1 = bytes[49 + gwNameLength + 1 + gwIdLength + 2];
|
int pubKeyLength = ((pubKeyLengthByte1 & 0xff) << 8 | (pubKeyLengthByte0 & 0xff));
|
var pubKey = Encoding.UTF8.GetString(bytes, 49 + gwNameLength + 1 + gwIdLength + 2 + 1, pubKeyLength);
|
var zbGateWay = new ZbGateway
|
{
|
GwIP = ipAddress,
|
GwName = gwName,
|
HomeId = homeID,
|
IsMainGateWay = isMainGateWay,
|
GwId = id,
|
GatewayOnlineFlage = true,
|
PubKey = pubKey
|
};
|
//通讯到网关列表
|
if (listReceiveIP.Find(obj => obj == zbGateWay.GwId) == null)
|
{
|
//网关匹配当前住宅中到网关
|
if (Shared.Common.Config.Instance.HomeId == homeID)
|
{
|
listReceiveIP.Add(ipAddress);
|
}
|
//UI界面正在搜索,不必配当前住宅到到网关此时也通讯
|
else if (IsSearchingGateway)
|
{
|
listReceiveIP.Add(ipAddress);
|
}
|
}
|
//网关列表存储处理
|
var gateWay = ZbGateway.GateWayList.Find(obj => obj.GwId == zbGateWay.GwId);
|
if (gateWay == null)
|
{
|
ZbGateway.GateWayList.Add(zbGateWay);
|
await zbGateWay.StartLocalMqtt(ipAddress);
|
}
|
else
|
{
|
gateWay.IsVirtual = false;
|
//将该网关标识为【可搜索到,即:在线】
|
gateWay.GatewayOnlineFlage = true;
|
if (gateWay.GwIP != ipAddress)
|
{
|
await gateWay.DisConnectLocalMqttClient("2");
|
ZbGateway.GateWayList.Remove(gateWay);
|
gateWay = zbGateWay;
|
ZbGateway.GateWayList.Add(gateWay);
|
await zbGateWay.StartLocalMqtt(ipAddress);
|
}
|
else
|
{
|
gateWay.PubKey = pubKey;
|
gateWay.GwName = gwName;
|
gateWay.HomeId = homeID;
|
await gateWay.StartLocalMqtt(ipAddress);
|
}
|
|
//主网关设置
|
if (isMainGateWay && oldHomeID == gateWay.HomeId)
|
{
|
for (int i = 0; i < ZbGateway.GateWayList.Count; i++)
|
{
|
var gw = ZbGateway.GateWayList[i];
|
//网关ID不是当前的网关,则把网关列表中其他网关标记为不是主网关
|
if (gw.GwId != id && oldHomeID == gw.HomeId)
|
{
|
gw.IsMainGateWay = false;
|
}
|
}
|
//标记当前网关是主网关
|
gateWay.IsMainGateWay = true;
|
}
|
}
|
|
//测试能否广播得到网关,通常情况下不检测
|
if (Shared.Phone.UserCenter.UserCenterResourse.HideOption.CheckCanReceiveGateway == 1)
|
{
|
if (Shared.Phone.UserCenter.UserCenterResourse.DicReceiveGatewayTest == null)
|
{
|
//防止异常,虽然几率很低
|
Shared.Phone.UserCenter.UserCenterResourse.HideOption.CheckCanReceiveGateway = 0;
|
continue;
|
}
|
if (Shared.Phone.UserCenter.UserCenterResourse.DicReceiveGatewayTest.ContainsKey(id) == false)
|
{
|
if (gateWay == null)
|
{
|
Shared.Phone.UserCenter.UserCenterResourse.DicReceiveGatewayTest[id] = zbGateWay;
|
}
|
else
|
{
|
Shared.Phone.UserCenter.UserCenterResourse.DicReceiveGatewayTest[id] = gateWay;
|
}
|
}
|
}
|
}
|
else
|
{
|
//调试用
|
if (Shared.Phone.UserCenter.UserCenterResourse.HideOption.WriteSocketReceiveDataToFile == 1)
|
{
|
var data33 = Encoding.UTF8.GetString(bytes).Replace("�)", string.Empty).TrimStart('\0').TrimEnd('\0');
|
Shared.Phone.UserCenter.HdlLogLogic.Current.WriteLog(3, data33);
|
}
|
}
|
}
|
catch (Exception ex)
|
{
|
//调试用
|
if (Shared.Phone.UserCenter.UserCenterResourse.HideOption.WriteSocketReceiveDataToFile == 1)
|
{
|
Shared.Phone.UserCenter.HdlLogLogic.Current.WriteLog(3, "Receive异常\r\n" + ex.Message);
|
}
|
}
|
}
|
})
|
{ IsBackground = true }.Start();
|
}
|
|
#endregion
|
|
#region ■ 开启远程连接_______________________
|
|
/// <summary>
|
/// 开启远程连接
|
/// </summary>
|
private static void StartRemoteMqtt()
|
{
|
new System.Threading.Thread(async () =>
|
{
|
while (true)
|
{
|
try
|
{
|
//定时检测远程连接情况
|
await ZbGateway.StartRemoteMqtt();
|
}
|
catch { }
|
System.Threading.Thread.Sleep(2000);
|
}
|
})
|
{ IsBackground = true }.Start();
|
}
|
|
#endregion
|
|
#region ■ 一般方法___________________________
|
|
/// <summary>
|
/// 重新搜索,注意:调用该方法,则促使全部网关的mqtt全部断开,然后重新搜索(可能会有1秒延迟)
|
/// </summary>
|
public static void ReSearch()
|
{
|
//为了那么多少万分之一的几率,这里设置成别的另一类的值
|
oldHomeID = "**";
|
}
|
|
/// <summary>
|
/// 检测能否发送广播
|
/// </summary>
|
/// <returns></returns>
|
private static bool CheckCanSendBroadcast(byte[] broadBytes)
|
{
|
if (string.IsNullOrEmpty(Config.Instance.HomeId))
|
{
|
//住宅ID为空只有一种可能就是退出了登录,这里的上一次住宅ID要清空
|
oldHomeID = "?";
|
System.Threading.Thread.Sleep(1000);
|
return false;
|
}
|
//首次进入网关,和切换住宅会清除网关列表,重新搜索存储
|
if (Config.Instance.HomeId != oldHomeID)
|
{
|
//因为那一瞬间,有可能mqtt会加回来,所以先加缓存
|
var list = new List<ZbGateway>();
|
list.AddRange(ZbGateway.GateWayList);
|
//然后清空掉
|
ZbGateway.GateWayList.Clear();
|
//最后再断开mqtt连接
|
for (int i = 0; i < list.Count; i++)
|
{
|
list[i]?.DisConnectLocalMqttClient("1");
|
}
|
list.Clear();
|
oldHomeID = Shared.Common.Config.Instance.HomeId;
|
var tempBytes = System.Text.Encoding.UTF8.GetBytes(Shared.Common.Config.Instance.HomeId);
|
System.Array.Copy(tempBytes, 0, broadBytes, 7, 36 < tempBytes.Length ? 36 : tempBytes.Length);
|
//住宅中已经存在的网关如果局域网不存在,需要在当前住宅中虚拟一个网关ID相同的网关
|
var gateWayFileList = Global.FileListByHomeId().FindAll(obj => obj.StartsWith("Gateway_"));
|
foreach (var filePath in gateWayFileList)
|
{
|
var paths = filePath.Split('_');
|
if (paths.Length < 3)
|
continue;
|
|
var gateWay = ZbGateway.GateWayList.Find(obj => (obj != null) && (obj.GwId == paths[2]));
|
if (gateWay == null)
|
{
|
gateWay = new ZbGateway { IsVirtual = true };
|
gateWay.GwId = paths[2];
|
gateWay.HomeId = Config.Instance.HomeId;
|
ZbGateway.GateWayList.Add(gateWay);
|
}
|
}
|
}
|
return true;
|
}
|
|
#endregion
|
|
#region ■ Socket_____________________________
|
|
/// <summary>
|
/// 接收处理UDP数据包
|
/// </summary>
|
/// <typeparam name="T"></typeparam>
|
public static class FindGateWaySocket
|
{
|
//本地Socket
|
public static UdpClient udpClient;
|
|
/// <summary>
|
/// 启动Socket接收和发送功能
|
/// </summary>
|
/// <param name="port"></param>
|
public static void Start()
|
{
|
if (udpClient!=null)
|
{
|
return;
|
}
|
udpClient = new UdpClient { EnableBroadcast = true };
|
}
|
|
/// <summary>
|
/// 停止Socket
|
/// </summary>
|
public static void Stop()
|
{
|
try
|
{
|
udpClient?.Close();
|
}
|
catch { }
|
udpClient = null;
|
|
Console.WriteLine("BusSocket关闭成功!");
|
}
|
|
/// <summary>
|
/// 异步发送数据
|
/// </summary>
|
/// <param name="tempPacket"></param>
|
public static void BeginSend(System.Net.IPEndPoint iPEndPoint, byte[] bytes)
|
{
|
try
|
{
|
if (udpClient == null)
|
{
|
return;
|
}
|
udpClient.Send(bytes, bytes.Length, iPEndPoint);
|
}
|
catch (Exception ex)
|
{
|
//调试用
|
if (Shared.Phone.UserCenter.UserCenterResourse.HideOption.WriteSocketReceiveDataToFile == 1)
|
{
|
Shared.Phone.UserCenter.HdlLogLogic.Current.WriteLog(3, "发送异常(BeginSendTo)\r\n" + ex.Message);
|
}
|
}
|
}
|
}
|
|
#endregion
|
}
|
}
|