Compare commits

...

6 Commits

45 changed files with 2363 additions and 152 deletions

Binary file not shown.

View File

@@ -20,6 +20,7 @@ namespace Common
public static CSRedisClient redis3;
public static CSRedisClient redis4;
public static CSRedisClient redis5;
public static CSRedisClient redis6;
public static CSRedisClient redis_webchat;
//private static readonly string[] redisHosts = null;
private static int SessionExpireMinutes = int.Parse(ConfigurationManager.AppSettings["session_expire_minutes"]);
@@ -57,7 +58,7 @@ namespace Common
redis4 = new CSRedisClient(redisHostStr + ",password=,defaultDatabase=4");
redis5 = new CSRedisClient(redisHostStr + ",password=,defaultDatabase=5");
redis5 = new CSRedisClient(redisHostStr + ",password=,defaultDatabase=5");
redis6 = new CSRedisClient(redisHostStr + ",password=,defaultDatabase=6");
redis_webchat = new CSRedisClient(string.Format(webchat_redisstr + ",password={0},defaultDatabase=0",webchat_redis_pwd));
//Native subscribe
@@ -200,6 +201,10 @@ namespace Common
{
client = redis5;
}
else if (SliceNo == 6)
{
client = redis6;
}
else
{
client = redis;

View File

@@ -14,6 +14,7 @@ namespace Common
public string EndPoint { get; set; }
public string CurrentStatus { get; set; }
public DateTime CurrentTime { get; set; }
public long UnixTime { get; set; }
}
}

View File

@@ -77,6 +77,9 @@ namespace Common
Marshal.Copy(bytes, startIndex, structPtr, size);
structObj = Marshal.PtrToStructure(structPtr, type);
}
catch (Exception ex)
{
}
finally
{
Marshal.FreeHGlobal(structPtr);

View File

@@ -78,6 +78,7 @@
<Compile Include="MissionRequestData.cs" />
<Compile Include="MonitorLog.cs" />
<Compile Include="MyHttp.cs" />
<Compile Include="NengHao_Repeat.cs" />
<Compile Include="NewDataSQL.cs" />
<Compile Include="NewRoomStatusPush.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />

View File

@@ -236,7 +236,12 @@ namespace CommonEntity
/// <summary>
/// 这个叫是否 取电 取断电状态
/// </summary>
public bool IsTakeCard { get; set; }
public int IsTakeCard { get; set; }
/// <summary>
/// 是否插卡
/// </summary>
public int IsInsertCard { get; set; }
/// <summary>
///短离长离
@@ -244,26 +249,134 @@ namespace CommonEntity
/// </summary>
public ushort CardEvent { get; set; }
/// <summary>
/// 卡身份
/// </summary>
public ushort CardID { get; set; }
/// <summary>
/// PMS 状态
/// </summary>
public string PMS_Status { get; set; }
public int CarbonVIP { get; set; }
public double V { get; set; }
public double A { get; set; }
public double P { get; set; }
public double Energy_Consumption { get; set; }
public double Sum_Energy_Consumption { get; set; }
public List<NengHao_Repeat> NengHaoList { get; set; }
//public double V { get; set; }
//public double A { get; set; }
//public double P { get; set; }
//public double Energy_Consumption { get; set; }
//public double Sum_Energy_Consumption { get; set; }
public long CreateTime { get; set; }
public string ReportTime { get; set; }
public List<DingShiReportDate> AllDeviceData { get; set; }
public int IdentityInfo {get;set;}
/// <summary>
/// 卡身份
/// </summary>
public int IdentityInfo { get; set; }
public int Bright_G { get; set; }
}
/// <summary>
/// ts 日志
/// </summary>
public class NewVersionLog
{
public string hotel_id { get; set; }
public string room_id { get; set; }
public string device_id { get; set; }
public string ip { get; set; }
public ushort power_state { get; set; }
public ushort guest_type { get; set; }
public ushort cardless_state { get; set; }
public ulong service_mask { get; set; }
public ushort pms_state { get; set; }
public ushort carbon_state { get; set; }
public int device_count { get; set; }
public ushort comm_seq { get; set; }
public List<NengHaolog> electricity { get; set; }
public List<KongTiao> air_conditioner { get; set; }
public extra_data extra { get; set; }
public int insert_card { get; set; }
public int bright_g { get; set; }
public int version { get; set; }
}
public class ts_deviceitem
{
public short dev_type { get; set; }
public short dev_addr { get; set; }
public short dev_loop { get; set; }
public int dev_data { get; set; }
}
public class ts_faultitem
{
public short dev_type { get; set; }
public short dev_addr { get; set; }
public short dev_loop { get; set; }
public short error_type { get; set; }
public int error_data { get; set; }
}
public class ts_controlitem
{
public short dev_type { get; set; }
public short dev_addr { get; set; }
public short dev_loop { get; set; }
public short type_l { get; set; }
public int type_h { get; set; }
}
/// <summary>
/// 0x36
/// </summary>
public class DeviceActionData
{
public long ts_ms { get; set; }
public string hotel_id { get; set; }
public string room_id { get; set; }
public string device_id { get; set; }
/// <summary>
/// "上报" 或 "下发"
/// </summary>
public string direction { get; set; }
public string cmd_word { get; set; }
public int frame_id { get; set; }
public byte[] udp_raw { get; set; }
public int sys_lock_status { get; set; }
public int report_count { get; set; }
public int fault_count { get; set; }
public List<ts_deviceitem> device_list { get; set; }
public List<ts_faultitem> fault_list { get; set; }
public List<ts_controlitem> control_list { get; set; }
}
public class NengHaolog
{
public string address { get; set; }
public double voltage { get; set; }
public double ampere { get; set; }
public double power { get; set; }
//public double watt { get; set; }
public double energy { get; set; }
public double sum_energy { get; set; }
public string phase { get; set; }
}
public class KongTiao
{
public string address { get; set; }
public int state { get; set; }
public int model { get; set; }
public int speed { get; set; }
public int set_temp { get; set; }
public int now_temp { get; set; }
public int solenoid_valve { get; set; }
}
public class extra_data
{
public byte[] original_byte { get; set; }
public string source { get; set; }
public string ver { get; set; }
public object ac { get; set; }
public object meter { get; set; }
}
public class Interface3Log

View File

@@ -22,6 +22,12 @@ namespace CommonEntity
public string RemoteEndPoint { get; set; }
public string HexData { get; set; }
public DateTime CurrentTime { get; set; }
public string MAC { get; set; }
public string CurrentStatus { get; set; }
public long UnixTime { get; set; }
public byte[] LauncherVersion { get; set; }
public byte[] RebootReason { get; set; }
}
/// <summary>
/// 新版本的设备状态

View File

@@ -84,6 +84,18 @@ namespace CommonEntity
public int Time { get; set; }
public DateTime? UpdateTime { get; set; }
public AirConditionData()
{
this.AirStatus=2;
this.FanSpeed=0;
this.Mode=0;
this.Valve=0;
this.CurrentTemp = 25;
this.SettingTemp = 25;
this.Valve = 0;
this.Time = 0;
this.UpdateTime=DateTime.Now;
}
}
public class RoomTypeModalCache

View File

@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CommonEntity
{
public class NengHao_Repeat
{
public string address { get; set; }
public double dianya { get; set; }
public double dianliu { get; set; }
public double gonglv { get; set; }
public double nenghao { get; set; }
public double zongnenghao { get; set; }
}
}

View File

@@ -36,7 +36,7 @@
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DefineConstants>TRACE;DEBUG;AAA;BBB;CCC;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>

View File

@@ -96,6 +96,12 @@ namespace ConsoleApplication2
static void JieXi()
{
NewVersionLog s = new NewVersionLog();
var nas = JsonConvert.SerializeObject(s);
var data = Encoding.UTF8.GetBytes(nas);
Encoding.UTF8.GetString(data);
string strnn1 = "AA 55 2F 00 54 33 53 41 02 34 80 EB 03 6B 24 34 D0 B8 11 6B 24 01 01 01 39 01 01 00 10 F0 55 E8 03 E8 03 00 00 E8 03 00 00 E8 03 00 00 9E 00";
@@ -244,15 +250,80 @@ namespace ConsoleApplication2
}
static void RedisTest()
{
var redis_webchat = new CSRedisClient(string.Format("47.119.147.104:26379" + ",password={0},defaultDatabase=0", "1001^_^lool"));
redis_webchat.HMSet("a","b","c");
var redis_webchat = new CSRedisClient(string.Format("47.119.147.104:26379" + ",password={0},defaultDatabase=0", "1001^_^lool"));
redis_webchat.HMSet("a", "b", "c");
}
static void AAA()
{
string nnn = "AA 55 30 00 54 33 53 41 0F 15 71 FF FF FF FF 05 07 01 00 00 00 80 07 02 00 00 00 80 07 03 00 00 00 80 07 04 00 00 00 80 07 05 00 00 00 80 CA 7C";
var data = Tools.GetBytesFromString(nnn.Replace(" ", ""));
string hostNumber = "233003055055";
var cmdtype = data[8];
var device_count = data[15];
if (cmdtype == 0x0F)
{
byte[] framenolist = data.Skip(7).Take(2).ToArray();
var zhenhao = BitConverter.ToUInt16(framenolist, 0);
string RoomNUMBER = CSRedisCacheHelper.HMGet<string>(5, CacheKey.RoomNumber_HostNumber, hostNumber)[0];
if (!string.IsNullOrEmpty(RoomNUMBER))
{
var code = Tools.HostNumberToHotelCode(hostNumber);
DeviceActionData d1 = new DeviceActionData();
d1.ts_ms = Tools.GetUnixTime();
d1.hotel_id = code.ToString();
d1.room_id = RoomNUMBER;
d1.device_id = hostNumber;
d1.frame_id = zhenhao;
d1.cmd_word = "0F";
//d1.udp_raw = Tools.ByteToString(data);
d1.udp_raw = data;
d1.direction = "下发";
List<ts_deviceitem> lll1 = new List<ts_deviceitem>();
List<ts_faultitem> lll2 = new List<ts_faultitem>();
List<ts_controlitem> lll3 = new List<ts_controlitem>();
int skip = 16;
for (int i = 1; i <= device_count; i++)
{
var t1 = data.Skip(skip + (i - 1) * 6).Take(6).ToArray();
ts_controlitem ts = new ts_controlitem();
ts.dev_addr = t1[0];
ts.dev_type = t1[1];
ts.dev_loop = t1[2];
ts.type_h = t1[3];
ts.type_l = BitConverter.ToInt16(new byte[] { t1[5], t1[4] }, 0);
lll3.Add(ts);
}
d1.control_list = lll3;
}
}
}
static void Main(string[] args)
{
#if AAA
Console.WriteLine("aaaaaaaaaaaaa");
#endif
var ggg= BitConverter.ToString(new byte[]{0xaa,0xcc});
AAA();
string aaa111 = "233003";
var bj1 = aaa111.Substring(0, 3);
var a1 = int.Parse(bj1);
var a2 = int.Parse(aaa111.Substring(3, 3));
var a3 = new byte[] { Convert.ToByte(a1), Convert.ToByte(a2) };
var uuua = BitConverter.ToUInt16(a3, 0);
BitArray bit_a = new BitArray(new byte[] { 0xAA });
bool bfa = bit_a.Get(0);
bool bfaa = bit_a.Get(1);
var DDD = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.ffffff");
Console.WriteLine(DDD);
var qqqhj = CSRedisCacheHelper.Get_Partition<HostModal_Cache>("HostModalStatusReceiver_13_000000001");
var qq223= JsonConvert.DeserializeObject<HostModal_Cache>(File.ReadAllText("3.txt"));
var qq223 = JsonConvert.DeserializeObject<HostModal_Cache>(File.ReadAllText("3.txt"));
RedisTest();
Data();
Console.ReadLine();

View File

@@ -440,5 +440,9 @@ namespace Domain
///// </summary>
//public virtual string TCLAppSecret { get; set; }
public virtual string ETV_HotelID { get; set; }
public virtual int SwitchRoomStatus_PowerOff2 { get; set; }
public virtual int SwitchRoomStatus_PowerOff4 { get; set; }
public virtual int SwitchRoomStatus_PowerOff8 { get; set; }
public virtual int SwitchRoomStatus_PowerOff16 { get; set; }
}
}

View File

@@ -74,6 +74,10 @@
<property name="IsPushPMSData" column="IsPushPMSData" type="bool" />
<property name="HeTongNumber" column="HeTongNumber" type="string" />
<property name="ETV_HotelID" column="ETV_HotelID" type="string" />
<property name="SwitchRoomStatus_PowerOff2" column="SwitchRoomStatus_PowerOff2" type="int"/>
<property name="SwitchRoomStatus_PowerOff4" column="SwitchRoomStatus_PowerOff4" type="int"/>
<property name="SwitchRoomStatus_PowerOff8" column="SwitchRoomStatus_PowerOff8" type="int"/>
<property name="SwitchRoomStatus_PowerOff16" column="SwitchRoomStatus_PowerOff16" type="int"/>
<!--<property name="TCLAppId" column="TCLAppId" type="string" />
<property name="TCLAppSecret" column="TCLAppSecret" type="string" />-->
</class>

View File

@@ -125,7 +125,7 @@
<WebProjectProperties>
<UseIIS>False</UseIIS>
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>55384</DevelopmentServerPort>
<DevelopmentServerPort>4498</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>
</IISUrl>

View File

@@ -214,18 +214,72 @@ namespace RCUHost.Implement
/// <param name="hostNumber"></param>
protected void Send(byte[] data, string hostNumber, string mac)
{
string ipAndPort = CSRedisCacheHelper.Get<string>(hostNumber, mac);
if (!string.IsNullOrEmpty(ipAndPort))
try
{
//if (HostServer!=null)
//{
// logger.Error("1111111111");
//}
//else
//{
// logger.Error("222222222222222222222");
//}
HostServer.Send(data, ipAndPort.ToString().Split(':')[0], int.Parse(ipAndPort.ToString().Split(':')[1]));
string ipAndPort = CSRedisCacheHelper.Get<string>(hostNumber, mac);
if (!string.IsNullOrEmpty(ipAndPort))
{
//这里判断0x0F
//AA 55 30 00 54 33 53 41 0F 15 71 FF FF FF FF
//05
//07 01 00 00 00 80
//07 02 00 00 00 80
//07 03 00 00 00 80
//07 04 00 00 00 80
//07 05 00 00 00 80 CA 7C
//AA 55 11 00 54 33 53 41 0F 15 71 3D 04 92 D0 6B 15
var cmdtype = data[8];
var device_count = data[15];
if (cmdtype == 0x0F)
{
byte[] framenolist = data.Skip(7).Take(2).ToArray();
var zhenhao = BitConverter.ToUInt16(framenolist, 0);
string RoomNUMBER = CSRedisCacheHelper.HMGet<string>(5, CacheKey.RoomNumber_HostNumber, hostNumber)[0];
if (!string.IsNullOrEmpty(RoomNUMBER))
{
var code = Tools.HostNumberToHotelCode(hostNumber);
DeviceActionData d1 = new DeviceActionData();
d1.ts_ms = Tools.GetUnixTime();
d1.hotel_id = code.ToString();
d1.room_id = RoomNUMBER;
d1.device_id = hostNumber;
d1.frame_id = zhenhao;
d1.cmd_word = "0F";
d1.udp_raw = data;
d1.direction = "下发";
List<ts_controlitem> lll3 = new List<ts_controlitem>();
int skip = 16;
for (int i = 1; i <= device_count; i++)
{
var t1 = data.Skip(skip + (i - 1) * 6).Take(6).ToArray();
ts_controlitem ts = new ts_controlitem();
ts.dev_addr = t1[0];
ts.dev_type = t1[1];
ts.dev_loop = t1[2];
ts.type_h = t1[3];
ts.type_l = BitConverter.ToInt16(new byte[] { t1[5], t1[4] }, 0);
lll3.Add(ts);
}
d1.control_list = lll3;
d1.report_count = lll3.Count;
string sss = Newtonsoft.Json.JsonConvert.SerializeObject(d1);
CSRedisCacheHelper.Publish("redis-0X36-0X0F", sss);
}
}
HostServer.Send(data, ipAndPort.ToString().Split(':')[0], int.Parse(ipAndPort.ToString().Split(':')[1]));
}
}
catch (Exception ex)
{
logger.Error("发送:"+ex.Message);
logger.Error(ex.StackTrace);
}
}
/// <summary>

View File

@@ -357,6 +357,7 @@ namespace RCUHost.Implement
/// <param name="needPushCommandQueue">是否需要将命令放入队列</param>
public void Send(byte[] data, IPEndPoint endPoint, bool needPushCommandQueue)
{
if (udpClient != null)
{
#region lock
@@ -405,6 +406,7 @@ namespace RCUHost.Implement
DataTongJi.TotalCount.TryAdd(TotalKey, u);
}
//logger.Error("升级拉取指令:" + Tools.ByteToString(data));
udpClient.Send(data, data.Length, endPoint);
// 使用同一个 Socket 发送回复
//udpClient.SendTo(data, endPoint);
@@ -831,6 +833,30 @@ namespace RCUHost.Implement
reader.BaseStream.Position = originalPosition;
//logger.Error("device_count error");
}
try
{
//故障数量默认0
int faultNumber = reader.ReadByte();
for (int i = 0; i < faultNumber; i++)
{
string faultNo = new DeviceAddress(reader.ReadBytes(4)).ToString();//设备类型1、设备地址1、回路地址2
byte type = reader.ReadByte();
if (!roomStatus.Faults.ContainsKey(faultNo + type.ToString()))
{
roomStatus.Faults[faultNo + type.ToString()] = new FaultStaus();
}
roomStatus.Faults[faultNo + type.ToString()].FaultNo = faultNo;
roomStatus.Faults[faultNo + type.ToString()].Type = type;//类型1在线状态0在线1离线2电量0~100%3电流4 1901故障检测次数
roomStatus.Faults[faultNo + type.ToString()].Data = reader.ReadByte();//内容
}
}
catch (Exception)
{
}
return roomStatus;
}
}
@@ -1222,6 +1248,10 @@ namespace RCUHost.Implement
byte cmdType = context111.SystemHeader.Value.CmdType;
if (cmdType == 0x68)
{
logger.Error("收到升级返回:" + Tools.ByteToString(gga.Data));
}
ushort MyFrameNO = context111.SystemHeader.Value.FrameNo;
byte[] framenolist = BitConverter.GetBytes(MyFrameNO);
@@ -1230,9 +1260,10 @@ namespace RCUHost.Implement
string CODE = context111.SystemHeader.Value.HostNumber.ToHotelCode().ToString();
string hotelCode = CODE;
string EndPoint = context111.RemoteEndPoint.ToString();
//if (cmdType != 0x01)
if (cmdType != 0x01 && cmdType != 0xb1)
//if (!(cmdType==0x01||cmdType==0xb1) )
//在线状态 不排队 注册0x01
if (true)
//if (true)
{
#region
string EndPointStr = context111.RemoteEndPoint.ToString();
@@ -1248,13 +1279,15 @@ namespace RCUHost.Implement
o.CurrentStatus = "on";
o.CurrentTime = DateTime.Now;
o.EndPoint = EndPointStr;
o.UnixTime = Tools.GetUnixTime();
o.MAC = BitConverter.ToString(gga.Data.Skip(9).Take(2).ToArray());
//新来的数据
var n = Newtonsoft.Json.JsonConvert.SerializeObject(o);
CSRedisCacheHelper.Set_PartitionWithTime(EndPointStr, n, 5, 4);
//上线
string EEE = CSRedisCacheHelper.Get<string>(EndPointStr);
//string EEE = CSRedisCacheHelper.Get<string>(EndPointStr);
var EEE = CSRedisCacheHelper.Get_Partition<string>(EndPointStr, 6);
var dtstart = CSRedisCacheHelper.ForeverGet<string>(CacheKey.ServerStartTime);
DateTime SSS = DateTime.Now;
DateTime.TryParse(dtstart, out SSS);
@@ -1263,6 +1296,8 @@ namespace RCUHost.Implement
{
CSRedisCacheHelper.Publish("redis-on_off_line", n);
}
CSRedisCacheHelper.Set_PartitionWithTime(EndPointStr, n, 5, 6);
CSRedisCacheHelper.Set_PartitionWithTime(EndPointStr, n, 20, 4);
@@ -1314,10 +1349,30 @@ namespace RCUHost.Implement
#region
if (cmdType == 0x32 || cmdType == 0x33 || cmdType == 0x34 || cmdType == 0x35 || cmdType == 0x36)
{
NewXieYi(context111, hotelCode, HostNNN, framenolist, cmdType, EndPoint);
NewXieYi(context111, hotelCode, HostNNN, framenolist, cmdType, EndPoint, MyFrameNO);
}
else
{
if (cmdType == 0x0F)
{
string RoomNUMBER = CSRedisCacheHelper.HMGet<string>(5, CacheKey.RoomNumber_HostNumber, HostNNN)[0];
if (!string.IsNullOrEmpty(RoomNUMBER))
{
//控制回复
DeviceActionData d1 = new DeviceActionData();
d1.ts_ms = Tools.GetUnixTime();
d1.hotel_id = hotelCode;
d1.room_id = RoomNUMBER;
d1.device_id = HostNNN;
d1.frame_id = MyFrameNO;
d1.cmd_word = "0F";
d1.udp_raw = OriginalByte;
d1.direction = "上报";
string sss = Newtonsoft.Json.JsonConvert.SerializeObject(d1);
CSRedisCacheHelper.Publish("redis-0X36-0X0F", sss);
}
}
#region 01
if (cmdType == 0x01)
{
@@ -1632,8 +1687,10 @@ namespace RCUHost.Implement
}
}
public static byte[] nocard_enum = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
public static object oo = new object();
private void NewXieYi(ReceiverContext context_1, string hotelCode_1, string HostNNN_1, byte[] framenolist_1, byte cmdType_1, string EndPoint_1)
private void NewXieYi(ReceiverContext context_1, string hotelCode_1, string HostNNN_1, byte[] framenolist_1, byte cmdType_1, string EndPoint_1, ushort MyFrameNO)
{
var Tuple = new Tuple<ReceiverContext, string, string, byte[], byte, string>(context_1, hotelCode_1, HostNNN_1, framenolist_1, cmdType_1, EndPoint_1);
Task.Factory.StartNew((state) =>
@@ -1658,9 +1715,14 @@ namespace RCUHost.Implement
int length = context111.Data.Length - offset - 2;
var data = OriginalByte.Skip(offset).ToArray();
var LauncherVersion = data.Take(20);
var RestartReason = data.Skip(20).Take(1);
var LauncherVersion = data.Take(20).ToArray();
var RestartReason = data.Skip(20).Take(1).ToArray();
string RoomNUMBER = CSRedisCacheHelper.HMGet<string>(5, CacheKey.RoomNumber_HostNumber, HostNNN)[0];
if (string.IsNullOrEmpty(RoomNUMBER))
{
return;
}
NewVersionHexData ns = new NewVersionHexData();
ns.CmdType = 0x33;
ns.HotelCode = hotelCode;
@@ -1668,9 +1730,17 @@ namespace RCUHost.Implement
ns.RemoteEndPoint = EndPoint;
ns.CurrentTime = DateTime.Now;
ns.HexData = Tools.ByteToString(context111.Data);
ns.RoomNumber = RoomNUMBER;
ns.MAC = BitConverter.ToString(OriginalByte.Skip(9).Take(2).ToArray());
ns.CurrentStatus = "on";
ns.UnixTime = Tools.GetUnixTime();
ns.LauncherVersion = LauncherVersion;
ns.RebootReason = RestartReason;
string mns1 = Newtonsoft.Json.JsonConvert.SerializeObject(ns);
CSRedisCacheHelper.Publish("redis-rcu-restart", mns1);
}
#region
if (cmdType == 0x35)
{
ReplyWithContent(context111, new byte[] { 0x00 }, framenolist);
@@ -1751,6 +1821,7 @@ namespace RCUHost.Implement
}
}
}
#endregion
// AA 55 35 00 54 33 53 41 34 10 80 EB 03 6B 24
// 01 //协议版本
@@ -1788,20 +1859,104 @@ namespace RCUHost.Implement
//P30~P33通道总能耗单位Wh1度电 = 1KWh
#region
if (cmdType == 0x36)
{
//Reply(context111);
string hexdata = Tools.ByteToString(context111.Data);
NewVersionHexData ns = new NewVersionHexData();
ns.CmdType = 0x36;
ns.HotelCode = hotelCode;
ns.HostNumber = HostNNN;
ns.RemoteEndPoint = EndPoint;
ns.CurrentTime = DateTime.Now;
ns.HexData = Tools.ByteToString(context111.Data);
string RoomNUMBER = CSRedisCacheHelper.HMGet<string>(5, CacheKey.RoomNumber_HostNumber, HostNNN)[0];
if (string.IsNullOrEmpty(RoomNUMBER))
{
return;
}
byte[] OriginalByte = context111.Data;
int offset = StructConverter.SizeOf(context111.SystemHeader);
int length = context111.Data.Length - offset - 2;
DeviceActionData d1 = new DeviceActionData();
d1.ts_ms = Tools.GetUnixTime();
d1.hotel_id = hotelCode;
d1.room_id = RoomNUMBER;
d1.device_id = HostNNN;
d1.frame_id = MyFrameNO;
d1.cmd_word = "36";
d1.udp_raw = OriginalByte;
d1.direction = "上报";
List<ts_deviceitem> lll1 = new List<ts_deviceitem>();
List<ts_faultitem> lll2 = new List<ts_faultitem>();
using (MemoryStream stream = new MemoryStream(context111.Data, offset, length))
{
Status status = NewStatusParse(stream);
if (status.SysLock)
{
d1.sys_lock_status = 1;
}
else
{
d1.sys_lock_status = 2;
}
if (status != null && status.Devices.Count > 0)
{
foreach (var item in status.Devices)
{
string ItemKKK = item.Key;
Device VVV = item.Value;
ts_deviceitem t1 = new ts_deviceitem();
string dizhi = VVV.Address;
t1.dev_type = short.Parse(dizhi.Substring(0, 3));
t1.dev_addr = short.Parse(dizhi.Substring(3, 3));
t1.dev_loop = short.Parse(dizhi.Substring(6, 3));
t1.dev_data = VVV.StatusReceiver;
lll1.Add(t1);
}
}
if (status.Faults != null && status.Faults.Count > 0)
{
var F1 = status.Faults;
foreach (var item in F1)
{
string dizhi = item.Value.FaultNo;
ts_faultitem t1 = new ts_faultitem();
t1.dev_type = short.Parse(dizhi.Substring(0, 3));
t1.dev_addr = short.Parse(dizhi.Substring(3, 3));
t1.dev_loop = short.Parse(dizhi.Substring(6, 3));
t1.error_type = item.Value.Type;
t1.error_data = item.Value.Data;
lll2.Add(t1);
}
}
}
d1.device_list = lll1;
d1.fault_list = lll2;
d1.report_count = lll1.Count;
d1.fault_count = lll2.Count;
string sss = Newtonsoft.Json.JsonConvert.SerializeObject(d1);
CSRedisCacheHelper.Publish("redis-0X36-0X0F", sss);
//Reply(context111);
//string hexdata = Tools.ByteToString(context111.Data);
//NewVersionHexData ns = new NewVersionHexData();
//ns.CmdType = 0x36;
//ns.HotelCode = hotelCode;
//ns.HostNumber = HostNNN;
//ns.RemoteEndPoint = EndPoint;
//ns.CurrentTime = DateTime.Now;
//ns.HexData = Tools.ByteToString(context111.Data);
//CSRedisCacheHelper.Publish("redis-rcu-hexdata", Newtonsoft.Json.JsonConvert.SerializeObject(ns));
}
#endregion
#region
if (cmdType == 0x34)
{
ReplyWithContent(context111, new byte[] { 0x00 }, framenolist);
@@ -1830,6 +1985,10 @@ namespace RCUHost.Implement
string HostID = CSRedisCacheHelper.HMGet<string>(5, CacheKey.HostId_HostNumber, HostNNN)[0];
string RoomNUMBER = CSRedisCacheHelper.HMGet<string>(5, CacheKey.RoomNumber_HostNumber, HostNNN)[0];
if (string.IsNullOrEmpty(RoomNUMBER))
{
return;
}
List<DingShiReportDate> DeviceList = new List<DingShiReportDate>();
@@ -1895,14 +2054,92 @@ namespace RCUHost.Implement
using (BinaryReader reader = new BinaryReader(stream))
{
//版本
//默认是02
var Version = reader.ReadByte();
//取电
//P1:插卡与取电状态设备类型(1Byte)
//bit0-bit1
//00 当前未取电
//01 当前已经取电
//bit2-bit3
//00 无插卡
//01 已经插卡
//下面的是是否取电
var TakeCardStatus = reader.ReadByte();
bool = false;
ushort _Int = 0;
int = 0;
if (Version == 0x02 || Version == 0x03)
{
BitArray bit_a = new BitArray(new byte[] { TakeCardStatus });
bool b0 = bit_a.Get(0);
bool b1 = bit_a.Get(1);
bool b2 = bit_a.Get(2);
bool b3 = bit_a.Get(3);
if (b0 == false && b1 == false)
{
//是否取电 = false;
_Int = 0;
}
else if (b0 == false && b1 == true)
{
_Int = 2;
}
else if (b0 == true && b1 == false)
{
//是否取电 = true;
_Int = 1;
}
if (b2 == false && b3 == false)
{
= 0;
}
else if (b2 == false && b3 == true)
{
= 2;
}
else if (b2 == true && b3 == false)
{
= 1;
}
}
//如果是01就只有取电
//从取电动作那里取数据
if (Version == 0x01)
{
string HostID_redis = CSRedisCacheHelper.HMGet<string>(5, CacheKey.HostId_HostNumber, HostNNN)[0];
if (!string.IsNullOrEmpty(HostID))
{
string KKey = CacheKey.HostModalStatus_Prefix + "_" + HostID + "_" + "004000001";
var OldHostModal = CSRedisCacheHelper.Get_Partition<HostModal_Cache>(KKey);
if (OldHostModal != null)
{
var aaa = OldHostModal.Status;
if (aaa == 01)
{
= true;
}
else if (aaa == 02)
{
= false;
}
}
else
{
}
}
}
//身份
var IdentityInfo = reader.ReadByte();
byte[] nocard_enum = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A };
//无卡逻辑状态(对应事件状态)
//0x01开门进入事件
//0x02卡在人离事件
@@ -1912,7 +2149,24 @@ namespace RCUHost.Implement
//0x06室内按键触发
//0x07短暂人离事件
//0x08长时间人离事件
//0x09:短暂人离超时
//0x0A:长时间人离超时
var NOCardInfo = reader.ReadByte();
var NOCardInfo_NoFilter = NOCardInfo;
if (!nocard_enum.Contains(NOCardInfo))
{
NOCardInfo = 0x00;
}
#region BUG处理
//只有同派酒店做这个处理
if (hotelCode.Equals("2144"))
{
if (NOCardInfo == 0x03)
{
= 2;
}
}
#endregion
//服务信息全部回路状态 1~64 64Bit 0 :关 1
//把8个字节转换成64个二进制位
@@ -1920,13 +2174,15 @@ namespace RCUHost.Implement
BitArray bitlist = new BitArray(N);
#region
int iii = 0;
for (int i = 0; i < bitlist.Length; i++)
{
if (i == 1)
{
continue;
}
string SerNo = i.ToString("000");
iii = i + 1;
//if (i == 1)
//{
// continue;
//}
string SerNo = iii.ToString("000");
//界面上显示,为了 兼容老版的 界面 显示逻辑,所以这样写
if (!string.IsNullOrEmpty(HostID))
@@ -1944,6 +2200,24 @@ namespace RCUHost.Implement
OldHostModal.Status = 2;
}
}
else
{
HostModal_Cache ccc = new HostModal_Cache();
ccc.HostID = int.Parse(HostID);
ccc.ModalType = DeviceType.ServiceInfo;
if (bitlist[i])
{
ccc.Status = 1;
}
else
{
ccc.Status = 2;
}
ccc.UpdateTime = DateTime.Now;
ccc.Modal = new RoomTypeModalCache() { };
ccc.AirConditionData = new AirConditionData();
CSRedisCacheHelper.Set_Partition<HostModal_Cache>(KKey, ccc);
}
}
//var hostModal1 = CSRedisCacheHelper.HMGet<NewVersionDeviceStatus>(1, QUDIANKey, "004000" + SerNo);
@@ -2010,9 +2284,27 @@ namespace RCUHost.Implement
//碳达人
var CarbonVIP = reader.ReadByte();
int Bright_Va = -1;
if (Version == 0x03)
{
var LiangDu = reader.ReadBytes(4);
Bright_Va = (int)LiangDu[0];
}
var CarbonVIP_NoFilter = CarbonVIP;
byte[] carbon_enum = new byte[] { 0x01, 0x02 };
if (!carbon_enum.Contains(CarbonVIP))
{
CarbonVIP = 0x00;
}
var DeviceCount = reader.ReadByte();
int DeviceCount_I = (int)DeviceCount;
List<NengHao_Repeat> nenghaoList = new List<NengHao_Repeat>();
List<NengHaolog> nenghaoList1 = new List<NengHaolog>();
List<KongTiao> kongtiaoList = new List<KongTiao>();
for (int i = 0; i < DeviceCount_I; i++)
{
var QA = reader.ReadBytes(4);
@@ -2020,6 +2312,8 @@ namespace RCUHost.Implement
#region
if (QA[0] == 0x39)
{
NengHao_Repeat nr = new NengHao_Repeat();
NengHaolog nr1 = new NengHaolog();
string address = new DeviceAddress(QA).ToString();
//设备数据长度
var DeviceInfoLen = reader.ReadByte();
@@ -2055,14 +2349,15 @@ namespace RCUHost.Implement
n.Mac = "";
n.EndPoint = EndPoint;
n.Version = ((int)Version).ToString();
if (TakeCardStatus == 0x01)
{
n.IsTakeCard = true;
}
else
{
n.IsTakeCard = false;
}
//if (TakeCardStatus == 0x01)
//{
// n.IsTakeCard = true;
//}
//else
//{
// n.IsTakeCard = false;
//}
n.IsTakeCard = ;
n.V = Math.Round(V, 2);
n.A = Math.Round(A, 2);
n.P = Math.Round(P, 2);
@@ -2077,33 +2372,21 @@ namespace RCUHost.Implement
//这里不再发送这个了,改发送全部的数据
NengHao4BaoJing ns2 = new NengHao4BaoJing()
{
HotelCode = n.HotelCode,
HostNumber = n.HostNumber,
RoomNumber = n.RoomNumber,
Mac = n.Mac,
EndPoint = n.EndPoint,
Version = n.Version,
IsTakeCard = n.IsTakeCard,
CarbonVIP = n.CarbonVIP,
V = dianya,
A = dianliu,
P = gonglv,
Energy_Consumption = nenghao,
Sum_Energy_Consumption = zongnenghao,
CreateTime = n.CreateTime,
ReportTime = n.ReportTime.ToString("yyyy-MM-dd HH:mm:ss"),
AllDeviceData = DeviceStatusList,
IdentityInfo = IdentityInfo,
CardID = IdentityInfo,
CardEvent = NOCardInfo,
PMS_Status = PMS_CurrentStatus
};
string mns = Newtonsoft.Json.JsonConvert.SerializeObject(ns2);
CSRedisCacheHelper.Publish("redis-power", mns);
nr.address = address;
nr.dianya = n.V;
nr.dianliu = n.A;
nr.gonglv = n.P;
nr.nenghao = n.KW_H;
nr.zongnenghao = n.Sum_KW_H;
nenghaoList.Add(nr);
nr1.address = address;
nr1.voltage = n.V;
nr1.ampere = n.A;
nr1.power = n.P;
nr1.energy = n.KW_H;
nr1.sum_energy = n.Sum_KW_H;
nenghaoList1.Add(nr1);
string KeyFilter = "能耗";
RCUHost.RCUHostCommon.tools.LanJieData(KeyFilter, hotelCode.ToString());
@@ -2147,6 +2430,16 @@ namespace RCUHost.Implement
int temperature = 0;//设定温度
int currentTemp = 0;//当前温度(室内温度)
KongTiaoReport(StatusReceiver, out status, out temperature, out mode, out fanspeed, out currentTemp);
KongTiao kkk = new KongTiao();
kkk.state = status;
kkk.address = address;
kkk.model = mode;
kkk.speed = fanspeed;
kkk.now_temp = currentTemp;
kkk.set_temp = temperature;
kkk.solenoid_valve = valve;
kongtiaoList.Add(kkk);
if (!string.IsNullOrEmpty(HostID))
{
string KKey = CacheKey.HostModalStatus_Prefix + "_" + HostID + "_" + address;
@@ -2195,6 +2488,69 @@ namespace RCUHost.Implement
}
#endregion
}
#region
long tf0 = Tools.GetUnixTime();
NengHao4BaoJing ns2 = new NengHao4BaoJing()
{
HotelCode = long.Parse(hotelCode),
HostNumber = HostNNN,
RoomNumber = RoomNUMBER,
Mac = "",
EndPoint = EndPoint,
Version = ((int)Version).ToString(),
IsTakeCard = _Int,
IsInsertCard = ,
CarbonVIP = CarbonVIP,
NengHaoList = nenghaoList,
//V = dianya,
//A = dianliu,
//P = gonglv,
//Energy_Consumption = nenghao,
//Sum_Energy_Consumption = zongnenghao,
CreateTime = tf0,
ReportTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
AllDeviceData = DeviceStatusList,
IdentityInfo = IdentityInfo,
//CardID = IdentityInfo,
CardEvent = NOCardInfo,
PMS_Status = PMS_CurrentStatus,
Bright_G = Bright_Va
};
string mns = Newtonsoft.Json.JsonConvert.SerializeObject(ns2);
CSRedisCacheHelper.Publish("redis-power", mns);
#endregion
#region TSlog
NewVersionLog tslog = new NewVersionLog()
{
version = (int)Version,
hotel_id = hotelCode,
room_id = RoomNUMBER,
device_id = HostNNN,
ip = EndPoint,
power_state = _Int,
insert_card = ,
bright_g = Bright_Va,
cardless_state = Convert.ToUInt16(NOCardInfo_NoFilter),
guest_type = Convert.ToUInt16(IdentityInfo),
service_mask = BitConverter.ToUInt64(N, 0),
pms_state = Convert.ToUInt16(PMS),
carbon_state = Convert.ToUInt16(CarbonVIP_NoFilter),
device_count = DeviceCount_I,
comm_seq = MyFrameNO,
electricity = nenghaoList1,
air_conditioner = kongtiaoList,
extra = new extra_data()
{
original_byte = OriginalByte
}
};
string mns111 = Newtonsoft.Json.JsonConvert.SerializeObject(tslog);
CSRedisCacheHelper.Publish("redis-tslog", mns111);
#endregion
}
}
}
@@ -2204,6 +2560,7 @@ namespace RCUHost.Implement
logger.Error("定是上报出错:" + ex.StackTrace);
}
}
#endregion
//向服务器获取房态
if (cmdType == 0x32)

View File

@@ -399,7 +399,21 @@ namespace RCUHost.Implement
var hostModal = CSRedisCacheHelper.Get_Partition<HostModal_Cache>(KKey);
if (hostModal != null)
{
if (hostModal.AirConditionData == null)
{
hostModal.AirConditionData = new AirConditionData();
}
if (hostModal.Modal == null)
{
hostModal.Modal = new RoomTypeModalCache() { ModalAddress = device.Value.Address };
}
if (hostModal.Modal!=null)
{
if (string.IsNullOrEmpty(hostModal.Modal.ModalAddress))
{
hostModal.Modal.ModalAddress = device.Value.Address;
}
}
}
else
{
@@ -541,15 +555,22 @@ namespace RCUHost.Implement
{
TCLCommon.SendData(hotelcode, roomnum, TCLcuid, skillid, "SLEEP");
}
if (hostModal.Modal.Name.Equals("睡眠息屏"))
{
}
//if (hostModal.Modal.Name.Equals("睡眠息屏"))
//{
//}
//呼叫前台
if (hostModal.Modal.ModalAddress.Equals("004000023"))
if (hostModal.Modal != null && hostModal.Modal.ModalAddress.Equals("004000023"))
{
TCLCommon.SendData(hotelcode, roomnum, TCLcuid, skillid, "SIP");
}
if (hotelcode.Equals("1085"))
{
if (hostModal.Modal.ModalAddress.Equals("004000021"))
{
TCLCommon.SendData(hotelcode, roomnum, TCLcuid, skillid, "ROMANTIC");
}
}
}
#endregion
@@ -592,7 +613,7 @@ namespace RCUHost.Implement
else//设备关
{
//清理
if (hostModal.Modal.ModalAddress.Equals("004000003"))
if (hostModal.Modal != null && hostModal.Modal.ModalAddress.Equals("004000003"))
{
//通过LocationUUID确定订单号
string OrderUUID = CSRedisCacheHelper.Get_Partition<string>(CacheKey.FCSRoom_Mapping_Order + "_" + UUID, 3);
@@ -608,6 +629,8 @@ namespace RCUHost.Implement
catch (Exception ex)
{
logger.Error("取电数据有异常: " + host.SysHotel.Code + "," + host.RoomNumber + " Msg:" + ex.Message);
logger.Error(Newtonsoft.Json.JsonConvert.SerializeObject(hostModal));
logger.Error(ex.StackTrace);
}
CSRedisCacheHelper.Set_Partition<HostModal_Cache>(KKey, hostModal);
#endregion
@@ -804,21 +827,21 @@ namespace RCUHost.Implement
#endregion
case DeviceType.AirConditioner://空调
#region
//status = device.Value.StatusReceiver >> 15;//开关
//int mode = (device.Value.StatusReceiver >> 13) & 0x03;//模式
//int fanSpeed = (device.Value.StatusReceiver >> 11) & 0x03;//风速
//int valve = (device.Value.StatusReceiver >> 10) & 0x01;//阀门
//int temperature = (device.Value.StatusReceiver >> 5) & 0x1F;//设定温度
//int currentTemp = device.Value.StatusReceiver & 0x01F;//当前温度(室内温度)
int StatusReceiver = device.Value.StatusReceiver;
status = StatusReceiver >> 15;//开关
int mode = (StatusReceiver >> 12) & 0x03;//模式
int fanSpeed = (StatusReceiver >> 11) & 0x03;//风速
int valve = (StatusReceiver >> 10) & 0x01;//阀门
int temperature = (StatusReceiver >> 5) & 0x1F;//设定温度
int currentTemp = StatusReceiver & 0x01F;//当前温度(室内温度)
status = device.Value.StatusReceiver >> 15;//开关
int mode = (device.Value.StatusReceiver >> 13) & 0x03;//模式
int fanSpeed = (device.Value.StatusReceiver >> 11) & 0x03;//风速
int valve = (device.Value.StatusReceiver >> 10) & 0x01;//阀门
int temperature = (device.Value.StatusReceiver >> 5) & 0x1F;//设定温度
int currentTemp = device.Value.StatusReceiver & 0x01F;//当前温度(室内温度)
//status = StatusReceiver >> 15;//开关
//int mode = (StatusReceiver >> 12) & 0x03;//模式
//int fanSpeed = (StatusReceiver >> 11) & 0x03;//风速
//int valve = (StatusReceiver >> 10) & 0x01;//阀门
//int temperature = (StatusReceiver >> 5) & 0x1F;//设定温度
//int currentTemp = StatusReceiver & 0x01F;//当前温度(室内温度)
#region
if (status == 1)//设备开
{

View File

@@ -157,7 +157,7 @@ namespace RCUHost.Implement
//来一个数据,把所有的地址拼接起来
string YiJingChuLiGuo = CacheKey.AllReadyDealWith0E_Prefix + "_" + HostNumberOnly;
MemoryCacheHelper.Delete(YiJingChuLiGuo);
ProcessModal_NEW_NEW(host, status.Devices, isTriggerWelcomeMsg, context.MessageID, context.IsMonitor);//更新灯光及其他回路状态
ProcessModal_NEW_NEW(host, status.Devices, isTriggerWelcomeMsg, context.MessageID, context.IsMonitor, context.Data, status);//更新灯光及其他回路状态
string nnn = VVV1 + VVV2;
if (!string.IsNullOrEmpty(nnn))
{
@@ -224,7 +224,7 @@ namespace RCUHost.Implement
public static string Missionsys_Address = ConfigurationManager.AppSettings["missionsys_address"];
public static string MQTTInfo_report_url = ConfigurationManager.AppSettings["debug_log_report_url"].ToString();
private void ProcessModal_NEW_NEW(Host host, ConcurrentDictionary<string, Device> devices, bool IsTriggerWelcomeMsg, string ContextMessageId, bool ismonitor)
private void ProcessModal_NEW_NEW(Host host, ConcurrentDictionary<string, Device> devices, bool IsTriggerWelcomeMsg, string ContextMessageId, bool ismonitor, byte[] OriginalByteList, Status yuanshidata)
{
string UUID = "9dc6a0ee-dcf1-4385-b05f-09cb463838cd";
UUID = host.FCS_locationUUID;
@@ -257,6 +257,9 @@ namespace RCUHost.Implement
bool isInsert = false;
StepTongJi.SendInfo(4.3, "Task开始执行设备信息处理", ContextMessageId, ismonitor);
List<ts_deviceitem> shebei_changeaction_list = new List<ts_deviceitem>();
List<ts_faultitem> exception_list = new List<ts_faultitem>();
//StringBuilder sbSQL;
foreach (var device in devices)
{
@@ -628,7 +631,13 @@ namespace RCUHost.Implement
{
TCLCommon.SendData(hotelcode, roomnum, TCLcuid, skillid, "SIP");
}
if (hotelcode.Equals("1085"))
{
if (hostModal.Modal.ModalAddress.Equals("004000023"))
{
TCLCommon.SendData(hotelcode, roomnum, TCLcuid, skillid, "ROMANTIC");
}
}
}
#endregion
@@ -693,7 +702,7 @@ namespace RCUHost.Implement
}
catch (Exception ex)
{
logger.Error("取电数据有异常: " + host.SysHotel.Code + "," + host.RoomNumber + " Msg:" + ex.Message);
logger.Error("取电数据有异常: " + host.SysHotel.Code + "," + host.RoomNumber + " Msg:" + ex.Message + ex.StackTrace);
}
CSRedisCacheHelper.Set_Partition<HostModal_Cache>(KKey, hostModal);
#endregion
@@ -1839,9 +1848,26 @@ namespace RCUHost.Implement
}
#endregion
//0关闭设备
//1打开设备且当前设备处于关闭状态
//2打开设备且当前设备处于打开状态
if (flag == 0 || flag == 1)
{
ts_deviceitem t1 = new ts_deviceitem();
string dizhi = device.Value.Address;
t1.dev_type = short.Parse(dizhi.Substring(0, 3));
t1.dev_addr = short.Parse(dizhi.Substring(3, 3));
t1.dev_loop = short.Parse(dizhi.Substring(6, 3));
t1.dev_data = device.Value.StatusReceiver;
shebei_changeaction_list.Add(t1);
}
#region
if ((flag == 0 || flag == 1) && (!string.IsNullOrEmpty(host.SysHotel.DeviceStatusPushURL)))
{
if (host.SysHotel.DeviceStatusPushURL.ToLower().IndexOf("wangjile") > -1)//freego过滤
{
switch (hostModal.Modal.ModalAddress)
@@ -1886,18 +1912,12 @@ namespace RCUHost.Implement
}
MyHttp.SendHttpData(ttt.Item1.SysHotel.DeviceStatusPushURL, resp);
//string Key = "HttpRequest_" + resp.code + "_" + resp.roomNumber;
//var Data = MemoryCacheHelper.Get(Key);
//if (Data != null)
//{
//}
//else
//{
// //XuanZhuOperation.ReportService(ttt.Item1.SysHotel.DeviceStatusPushURL, resp);
//}
}, tup);
}
#endregion
@@ -1934,6 +1954,7 @@ namespace RCUHost.Implement
CSRedisCacheHelper.Publish("Redis-XuanZhuKafka", str111);
}
#endregion
}
else
{
@@ -1946,6 +1967,55 @@ namespace RCUHost.Implement
}
}
#region
byte[] OriginalByte = OriginalByteList;
DeviceActionData d1 = new DeviceActionData();
d1.ts_ms = Tools.GetUnixTime();
d1.hotel_id = HOTEL_CODE;
d1.room_id = ROOMNUMBER;
d1.device_id = HOSTNUMBER;
d1.frame_id = frameNo;
d1.cmd_word = "0E";
d1.udp_raw = OriginalByte;
d1.direction = "上报";
if (yuanshidata.SysLock)
{
d1.sys_lock_status = 1;
}
else
{
d1.sys_lock_status = 2;
}
if (yuanshidata.Faults != null && yuanshidata.Faults.Count > 0)
{
var F1 = yuanshidata.Faults;
foreach (var item in F1)
{
string dizhi = item.Value.FaultNo;
ts_faultitem t1 = new ts_faultitem();
t1.dev_type = short.Parse(dizhi.Substring(0, 3));
t1.dev_addr = short.Parse(dizhi.Substring(3, 3));
t1.dev_loop = short.Parse(dizhi.Substring(6, 3));
t1.error_type = item.Value.Type;
t1.error_data = item.Value.Data;
exception_list.Add(t1);
}
}
d1.device_list = shebei_changeaction_list;
d1.fault_list = exception_list;
d1.report_count = shebei_changeaction_list.Count;
d1.fault_count = exception_list.Count;
string sss111 = Newtonsoft.Json.JsonConvert.SerializeObject(d1);
CSRedisCacheHelper.Publish("redis-0X36-0X0F", sss111);
#endregion
StepTongJi.SendInfo(5, "Task中的设备处理代码执行完毕", ContextMessageId, ismonitor);
devices.Clear();
}
@@ -2221,7 +2291,7 @@ namespace RCUHost.Implement
var DDD = State as Tuple<string, XuanZhuResponse>;
string Res_P = MyHttp.SendHttpData(DDD.Item1, DDD.Item1);
//XuanZhuOperation.ReportService(DDD.Item1, DDD.Item2);
logger.Error("Fault Return:" + "");
//logger.Error("Fault Return:" + "");
}, fault_data);
}
}

View File

@@ -97,13 +97,13 @@ namespace RCUHost.Implement
Upgrade_Status = "升级失败";
break;
}
BarData bbb = new BarData();
bbb.HostID = host.ID;
bbb.Upgrade_status = Upgrade_Status;
bbb.Upgrade_DateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
UploadCurrentVersionReceiver.UP_Grade_Json(host, bbb);
host.UpgradeTime = DateTime.Now;
HostRepository.Update(host);
}

View File

@@ -48,8 +48,8 @@ namespace RCUHost.Implement
//添加升级功能标志,告诉缓存,不能被拦截
foreach (var item in hosts)
{
string Key="Upgrade_UpdateSQL_"+ item.HostNumber;
MemoryCacheHelper.Set(Key,item.ID,DateTimeOffset.Now.AddMinutes(3));
string Key = "Upgrade_UpdateSQL_" + item.HostNumber;
MemoryCacheHelper.Set(Key, item.ID, DateTimeOffset.Now.AddMinutes(3));
}
FileInfo fileInfo = new FileInfo(updateFile);
@@ -58,6 +58,7 @@ namespace RCUHost.Implement
foreach (var host in hosts)
{
SendUpdateRequest(host, fileMd5, blockNum, (byte)fileType, fileHref.Substring(fileHref.IndexOf("/")));//发送升级通知
Thread.Sleep(300);
this.updateHostList.Add(new UpdateHostWorker(host, hostUpdate, MessageIP, fileHref, updateFile));
if (hostUpdate == null)//从api获取升级文件方式为null保存主机表升级状态信息
{
@@ -84,8 +85,10 @@ namespace RCUHost.Implement
public override void Process(ReceiverContext context1)
{
int startIndex = StructConverter.SizeOf(context1.SystemHeader);
//var mydata = Tools.ByteToString(context1.Data);
//logger.Error(string.Format("升级返回:{0}", mydata));
UpdateHostPacketReply? reply1 = DecodeUpdateHostPacketReply(context1.Data, startIndex);
var TTT = new Tuple<ReceiverContext, UpdateHostPacketReply?>(context1,reply1);
var TTT = new Tuple<ReceiverContext, UpdateHostPacketReply?>(context1, reply1);
//logger.Error(string.Format("收到tftp升级回复命令{0}:{1}{2},解析结果:{3}", context.RemoteEndPoint.Address.ToString(), context.RemoteEndPoint.Port, Tools.ByteToString(context.Data), reply.HasValue));
if (reply1.HasValue)
{
@@ -95,8 +98,20 @@ namespace RCUHost.Implement
var context = NNN.Item1;
var reply = NNN.Item2;
var updateHostWorker = this.updateHostList.FirstOrDefault(r => r.Host.HostNumber == context.SystemHeader.Value.HostNumber.ToString());
//logger.Error(string.Format("酒店{0}客房{1}升级({2}),状态{3}", updateHostWorker.Host.SysHotel.Code, updateHostWorker.Host.RoomNumber, updateHostWorker.RemoteFile, reply.Value.Status));
SaveSystemLog(30, "升级主机", string.Format("收到主机({0})升级回复命令,状态:{1}", updateHostWorker.Host.RoomNumber, reply.Value.Status), "收到命令", "RCU", context.RemoteEndPoint.Address.ToString(), updateHostWorker.Host.SysHotel.ID);
var data = Tools.ByteToString(context1.Data);
logger.Error(string.Format("主机{0}升级返回:{1}", context.SystemHeader.Value.HostNumber.ToString(), data));
if (updateHostWorker == null)
{
return;
}
try
{
SaveSystemLog(30, "升级主机", string.Format("收到主机({0})升级回复命令,状态:{1}", updateHostWorker.Host.RoomNumber, reply.Value.Status), "收到命令", "RCU", context.RemoteEndPoint.Address.ToString(), updateHostWorker.Host.SysHotel.ID);
}
catch (Exception)
{
}
if (updateHostWorker.HostUpdate == null)
{
BarData bbb = new BarData();
@@ -114,6 +129,18 @@ namespace RCUHost.Implement
HostRepository.SetUpgradeStatus(updateHostWorker.Host, 1);//升级完成
bbb.Upgrade_status = "升级完成";
break;
case UpdateHostPacketReply.BlockNumError:
bbb.Upgrade_status = "块错误";
HostRepository.SetUpgradeStatus(updateHostWorker.Host, 2);//升级失败
break;
case UpdateHostPacketReply.FileTypeError:
bbb.Upgrade_status = "文件错误";
HostRepository.SetUpgradeStatus(updateHostWorker.Host, 2);//升级失败
break;
case UpdateHostPacketReply.FileMD5Error:
bbb.Upgrade_status = "文件MD5校验错误";
HostRepository.SetUpgradeStatus(updateHostWorker.Host, 2);//升级失败
break;
default:
HostRepository.SetUpgradeStatus(updateHostWorker.Host, 2);//升级失败
bbb.Upgrade_status = "升级失败";
@@ -147,6 +174,21 @@ namespace RCUHost.Implement
hostUpdateStatus.Status = 1;//升级完成
updateHostList.Remove(updateHostWorker);
break;
case UpdateHostPacketReply.BlockNumError:
bbb.Upgrade_status = "块错误";
hostUpdateStatus.Status = 2;//升级失败
updateHostList.Remove(updateHostWorker);
break;
case UpdateHostPacketReply.FileTypeError:
bbb.Upgrade_status = "文件错误";
hostUpdateStatus.Status = 2;//升级失败
updateHostList.Remove(updateHostWorker);
break;
case UpdateHostPacketReply.FileMD5Error:
bbb.Upgrade_status = "文件MD5校验错误";
hostUpdateStatus.Status = 2;//升级失败
updateHostList.Remove(updateHostWorker);
break;
default:
bbb.Upgrade_status = "升级失败";
hostUpdateStatus.Status = 2;//升级失败
@@ -158,7 +200,7 @@ namespace RCUHost.Implement
hostUpdateStatus.UpdatedTime = DateTime.Now;
HostUpdateStatusRepository.SaveOrUpdate(hostUpdateStatus);
}
},TTT);
}, TTT);
}
}
public override CommandType CommandType
@@ -175,7 +217,10 @@ namespace RCUHost.Implement
/// <param name="updateFileMd5">升级文件MD5值</param>
private void SendUpdateRequest(Host host, string updateFileMd5, ushort blockNum, byte fileType, string fileName)
{
logger.Error("主机升级:" + host.SysHotel.Code + ":" + host.RoomNumber + " FileMd5:" + updateFileMd5 + " FileName:" + fileName);
byte[] data = CreateUpdateRequestPacket(updateFileMd5, blockNum, fileType, fileName);
logger.Error("升级HostNumber为" + host.HostNumber);
logger.Error("升级指令为:" + Tools.ByteToString(data));
Send(data, host.HostNumber, host.MAC);// host.IP, host.Port);
}
/// <summary>

View File

@@ -60,6 +60,10 @@ namespace RCUHost.Implement
{
try
{
if (string.IsNullOrEmpty(bbb.Upgrade_status))
{
return;
}
string id = host.ID.ToString();
string Key = CacheKey.UPGradeProgressBar + "_" + id;
var DDD = CSRedisCacheHelper.Get<BarData>(Key);

View File

@@ -0,0 +1,327 @@
# UDP数据推送与Redis缓存方案
## 1. 需求分析
当UDP数据到达时需要将数据推送到一些外部接口但这些接口所需的数据如房间号在UDP数据中并不包含而是存储在数据库中。为了减少数据库查询开销提高推送效率需要
1. 定期从数据库获取必要信息并更新到Redis
2. UDP数据到达时从Redis快速获取所需信息
3. 实现高效的数据推送机制
## 2. 解决方案设计
### 2.1 定时任务设计
创建一个定时任务定期从ApiController获取主机信息并更新到Redis
```csharp
public class HostInfoSyncJob : IJob
{
public void Execute()
{
try
{
// 创建HttpClient实例
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(System.Configuration.ConfigurationManager.AppSettings["CurrentUrl"]);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// 调用API获取所有主机信息
var response = client.GetAsync("api/GetAllHostInfo").Result;
if (response.IsSuccessStatusCode)
{
var hostInfos = response.Content.ReadAsAsync<List<HostInfoDto>>().Result;
// 更新Redis缓存
foreach (var hostInfo in hostInfos)
{
// 缓存主机基本信息
string hostInfoKey = CacheKey.HostInfo_Key_HostNumber + "_" + hostInfo.HostNumber;
CSRedisCacheHelper.Forever<HostInfoDto>(hostInfoKey, hostInfo);
// 缓存主机号到房间号的映射
string hostToRoomKey = CacheKey.HostToRoom_Mapping + "_" + hostInfo.HostNumber;
CSRedisCacheHelper.Forever<string>(hostToRoomKey, hostInfo.RoomNumber);
// 缓存房间号到主机号的映射
string roomToHostKey = CacheKey.RoomToHost_Mapping + "_" + hostInfo.RoomNumber;
CSRedisCacheHelper.Forever<string>(roomToHostKey, hostInfo.HostNumber);
}
}
}
}
catch (Exception ex)
{
log4net.LogManager.GetLogger(typeof(HostInfoSyncJob)).Error("同步主机信息到Redis失败: " + ex.Message);
}
}
}
```
### 2.2 ApiController扩展
在ApiController中添加获取所有主机信息的接口
```csharp
/// <summary>
/// 获取所有主机信息用于定时同步到Redis
/// </summary>
/// <returns></returns>
public ActionResult GetAllHostInfo()
{
try
{
List<HostInfoDto> hostInfos = new List<HostInfoDto>();
// 获取所有酒店
var hotels = SysHotelManager.LoadAll();
foreach (var hotel in hotels)
{
// 获取酒店下所有主机
var hosts = HostManager.LoadAll(hotel.ID);
foreach (var host in hosts)
{
HostInfoDto dto = new HostInfoDto
{
HostNumber = host.HostNumber,
RoomNumber = host.RoomNumber,
HotelID = hotel.ID,
HotelCode = hotel.Code,
HotelName = hotel.Name,
Status = host.Status,
RoomStatus = host.RoomStatus?.Name,
MAC = host.MAC
};
hostInfos.Add(dto);
}
}
return Json(hostInfos, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
logger.Error("获取所有主机信息失败: " + ex.Message);
return Json(new List<HostInfoDto>(), JsonRequestBehavior.AllowGet);
}
}
```
### 2.3 UDP数据处理与推送
在UDP数据处理模块中从Redis获取所需信息并推送到外部接口
```csharp
public class UdpDataPusher
{
private static readonly string PushApiUrl = System.Configuration.ConfigurationManager.AppSettings["PushApiUrl"];
public static void PushUdpData(string hostNumber, string udpData)
{
try
{
// 从Redis获取主机信息
string hostInfoKey = CacheKey.HostInfo_Key_HostNumber + "_" + hostNumber;
var hostInfo = CSRedisCacheHelper.Get<HostInfoDto>(hostInfoKey);
if (hostInfo == null)
{
// 如果Redis中没有从数据库获取并更新Redis
hostInfo = GetHostInfoFromDatabase(hostNumber);
if (hostInfo == null)
{
log4net.LogManager.GetLogger(typeof(UdpDataPusher)).Error("无法获取主机信息: " + hostNumber);
return;
}
// 更新到Redis
CSRedisCacheHelper.Forever<HostInfoDto>(hostInfoKey, hostInfo);
}
// 构建推送数据
PushData pushData = new PushData
{
HostNumber = hostNumber,
RoomNumber = hostInfo.RoomNumber,
HotelCode = hostInfo.HotelCode,
HotelName = hostInfo.HotelName,
UdpData = udpData,
Timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")
};
// 推送到外部接口
SendPushRequest(pushData);
}
catch (Exception ex)
{
log4net.LogManager.GetLogger(typeof(UdpDataPusher)).Error("推送UDP数据失败: " + ex.Message);
}
}
private static HostInfoDto GetHostInfoFromDatabase(string hostNumber)
{
// 创建HttpClient实例
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(System.Configuration.ConfigurationManager.AppSettings["CurrentUrl"]);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// 调用API获取主机信息
var response = client.GetAsync($"api/GetHostInfo?hostNumber={hostNumber}").Result;
if (response.IsSuccessStatusCode)
{
return response.Content.ReadAsAsync<HostInfoDto>().Result;
}
}
return null;
}
private static void SendPushRequest(PushData data)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(PushApiUrl);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var content = new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json");
var response = client.PostAsync("", content).Result;
if (!response.IsSuccessStatusCode)
{
log4net.LogManager.GetLogger(typeof(UdpDataPusher)).Error("推送失败: " + response.StatusCode);
}
}
}
}
```
### 2.4 缓存键设计
在CacheKey类中添加新的缓存键常量
```csharp
public class CacheKey
{
// 现有缓存键...
// 新增缓存键
public static string HostToRoom_Mapping = "HostToRoom_Mapping";
public static string RoomToHost_Mapping = "RoomToHost_Mapping";
public static string HostInfo_Key_HostNumber = "HostInfo_Key_HostNumber";
}
```
### 2.5 数据模型设计
```csharp
public class HostInfoDto
{
public string HostNumber { get; set; }
public string RoomNumber { get; set; }
public int HotelID { get; set; }
public string HotelCode { get; set; }
public string HotelName { get; set; }
public bool Status { get; set; }
public string RoomStatus { get; set; }
public string MAC { get; set; }
}
public class PushData
{
public string HostNumber { get; set; }
public string RoomNumber { get; set; }
public string HotelCode { get; set; }
public string HotelName { get; set; }
public string UdpData { get; set; }
public string Timestamp { get; set; }
}
```
### 2.6 定时任务注册
在Global.asax.cs中注册定时任务
```csharp
protected override void Application_Start(object sender, EventArgs e)
{
// 现有代码...
// 注册定时任务
JobManager.AddJob(
() => new HostInfoSyncJob().Execute(),
s => s.ToRunEvery(5).Minutes() // 每5分钟执行一次
);
// 现有代码...
}
```
## 3. 性能优化
### 3.1 缓存策略优化
1. **缓存过期时间**:对于主机信息,使用永久缓存,只有在定时任务执行时更新
2. **缓存预热**系统启动时执行一次全量同步确保Redis中有初始数据
3. **增量更新**:定时任务可以只更新发生变化的主机信息,减少网络传输
### 3.2 并发处理优化
1. **异步推送**使用异步方式推送数据避免阻塞UDP处理线程
2. **批量处理**对于短时间内的多个UDP数据进行批量推送
3. **连接池**使用HttpClient连接池减少连接建立开销
### 3.3 错误处理优化
1. **重试机制**:推送失败时进行有限次数的重试
2. **降级策略**当Redis不可用时直接从数据库获取数据
3. **监控告警**:对推送失败的情况进行监控和告警
## 4. 部署与配置
### 4.1 配置文件设置
在Web.config中添加必要的配置
```xml
<appSettings>
<!-- 现有配置... -->
<!-- 推送API配置 -->
<add key="PushApiUrl" value="http://example.com/api/push" />
<!-- 定时同步间隔(分钟) -->
<add key="SyncIntervalMinutes" value="5" />
</appSettings>
```
### 4.2 依赖项
- Newtonsoft.Json
- FluentScheduler用于定时任务
- CSRedis用于Redis操作
## 5. 测试方案
### 5.1 功能测试
1. **定时同步测试**验证定时任务是否正常执行Redis中是否有数据
2. **UDP数据推送测试**模拟UDP数据验证是否能正确获取信息并推送
3. **边界情况测试**测试Redis不可用、网络异常等情况
### 5.2 性能测试
1. **并发测试**模拟多个UDP数据同时到达测试推送性能
2. **响应时间测试**测量从UDP数据到达至推送完成的时间
3. **负载测试**:测试系统在高负载下的稳定性
## 6. 总结
本方案通过以下步骤实现了UDP数据的高效推送
1. **定时同步**定期从数据库获取主机信息并更新到Redis
2. **快速获取**UDP数据到达时从Redis快速获取所需信息
3. **异步推送**:使用异步方式将数据推送到外部接口
4. **容错处理**当Redis不可用时从数据库获取数据作为降级方案
该方案显著减少了数据库查询开销提高了UDP数据处理和推送的效率适用于高并发场景下的实时数据推送需求。

View File

@@ -599,6 +599,8 @@ namespace WebSite.Controllers
/// </summary>
/// <param name="jsonData"></param>
/// <returns></returns>
//public ActionResult GetRoomAirList_Deprecate(string jsonData)
public ActionResult GetRoomAirList(string jsonData)
{
//return Json(new { IsSuccess = false, Result = "接口修改调整" }, JsonRequestBehavior.AllowGet);
@@ -927,6 +929,7 @@ namespace WebSite.Controllers
/// </summary>
/// <param name="jsonData"></param>
/// <returns></returns>
//public ActionResult GetRoomLightList_Deprecate(string jsonData)
public ActionResult GetRoomLightList(string jsonData)
{
@@ -1059,6 +1062,7 @@ namespace WebSite.Controllers
/// </summary>
/// <param name="jsonData"></param>
/// <returns></returns>
//public ActionResult GetRoomCurtainList_Deprecate(string jsonData)
public ActionResult GetRoomCurtainList(string jsonData)
{
@@ -3819,20 +3823,7 @@ namespace WebSite.Controllers
{
return Json(new { IsSuccess = false, Data = "hostid_lists不能为空" });
}
int roomTypeID = 0;
int.TryParse(b, out roomTypeID);
logger.Error("微信端开始升级: " + fileName + " " + roomTypeID + " host_list: " + host_list_str);
var host_list = Newtonsoft.Json.JsonConvert.DeserializeObject<List<int>>(host_list_str);
//让服务器获取升级文件
HostUpdateController.GetUpdateFileByRoomTypeID(roomTypeID);
string lll = JsonConvert.SerializeObject(host_list);
HostUpdateController hh = new HostUpdateController();
logger.Error("开始升级: " + fileName + " host_list: " + lll);
//升级
hh.ShengJI_NEW(host_list, fileName, HostManager);
ReallyUpgrade(b, host_list_str, fileName);
return Json(new { IsSuccess = true, Data = "正在升级,请等待……" });
}
catch (Exception ex)
@@ -3841,6 +3832,52 @@ namespace WebSite.Controllers
}
}
[HttpPost()]
public ActionResult WebChatUpgradeTimer()
{
try
{
//1412
//固件:BLV-V9 || Launcher_C1F_V04 || C1F_A_L4_31_240723_NM.bin
//100331-YHF_T
//YHF_T
//2438
//研发测试房型_1
//固件:BLV-C5 || Launcher_C1F_V04 || C1F_A_L4_41_250605_NM.bin
string roomTypeID = "1412";
string host_list_str = "[24540]";
//string fileName = "C1F_A_L4_41_250704_NM.bin";
string fileName = "C1F_A_L4_31_240723_NM.bin";
ReallyUpgrade(roomTypeID, host_list_str, fileName);
return Json(new { IsSuccess = true, Data = "正在升级,请等待……" });
}
catch (Exception ex)
{
logger.Error("定时升级出错:"+ex.Message);
return Json(new { IsSuccess = false, Data = ex.Message });
}
}
private void ReallyUpgrade(string ID, string host_list_str, string fileName)
{
int roomTypeID = 0;
int.TryParse(ID, out roomTypeID);
logger.Error("微信端开始升级: " + fileName + " " + roomTypeID + " host_list: " + host_list_str);
var host_list = Newtonsoft.Json.JsonConvert.DeserializeObject<List<int>>(host_list_str);
//让服务器获取升级文件
HostUpdateController.GetUpdateFileByRoomTypeID(roomTypeID);
string lll = JsonConvert.SerializeObject(host_list);
HostUpdateController hh = new HostUpdateController();
logger.Error("开始升级: " + fileName + " host_list: " + lll);
//升级
hh.ShengJI_NEW(host_list, fileName, HostManager,roomTypeID);
}
[HttpPost()]
public ActionResult QueryUpdateHostProgressBar(string HostIDList)
{
@@ -3902,6 +3939,23 @@ namespace WebSite.Controllers
}
}
[HttpPost()]
public ActionResult Upgrade_V2(string roomtype_id, string host_list_str, string fileName)
{
int roomTypeID = 0;
int.TryParse(roomtype_id, out roomTypeID);
logger.Error("微信端开始升级: " + fileName + " " + roomTypeID + " host_list: " + host_list_str);
var host_list = Newtonsoft.Json.JsonConvert.DeserializeObject<List<int>>(host_list_str);
//不用获取,直接在本地使用
string lll = JsonConvert.SerializeObject(host_list);
HostUpdateController hh = new HostUpdateController();
logger.Error("开始升级: " + fileName + " host_list: " + lll);
//升级
return hh.(host_list, fileName,roomTypeID,HostManager);
}
#endregion

View File

@@ -345,12 +345,31 @@ namespace WebSite.Controllers
return Json(new { IsSuccess = false, Message = "升级文件不合法!" });
}
string fileHref = "Uploads/room_type/" + hosts[hosts.Count - 1].RoomType.ID + "/" + fileName;//升级文件相对路径
//string fileMd5 = "";
//using (Stream stream = System.IO.File.Open(Tools.GetApplicationPath() + fileHref, FileMode.Open))
//{
// fileMd5 = Tools.ComputeFileHash(stream);
// stream.Close();
//}
string fileMd5 = "";
using (Stream stream = System.IO.File.Open(Tools.GetApplicationPath() + fileHref, FileMode.Open))
string FileKey = "FileMD5_" + fileHref;
string kkka = CSRedisCacheHelper.Get_Partition<string>(FileKey, 3);
if (!string.IsNullOrEmpty(kkka))
{
fileMd5 = Tools.ComputeFileHash(stream);
stream.Close();
fileMd5 = kkka;
}
else
{
using (Stream stream = System.IO.File.Open(Tools.GetApplicationPath() + fileHref, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
fileMd5 = Tools.ComputeFileHash(stream);
CSRedisCacheHelper.Set_PartitionWithTime<string>(FileKey, fileMd5, 5, 3);
stream.Close();
}
}
//HostUpdate hostUpdate = HostUpdateManager.Get(fileMd5, CurrentHotelID);
//if (null == hostUpdate)
//{
@@ -384,7 +403,7 @@ namespace WebSite.Controllers
}
public ActionResult ShengJI_NEW(IList<int> idList, string fileName, IHostManager HostManager1)
public ActionResult (IList<int> idList, string fileName, int RoomTypeID, IHostManager HostManager1)
{
try
{
@@ -410,13 +429,92 @@ namespace WebSite.Controllers
default:
return Json(new { IsSuccess = false, Message = "升级文件不合法!" });
}
string fileHref = "Uploads/room_type/" + hosts[hosts.Count - 1].RoomType.ID + "/" + fileName;//升级文件相对路径
string fileHref = "Uploads/V2/room_type/" + RoomTypeID + "/" + fileName;//升级文件相对路径
string fileMd5 = "";
using (Stream stream = System.IO.File.Open(Tools.GetApplicationPath() + fileHref, FileMode.Open))
string FileKey = "FileMD5_" + fileHref;
string kkka = CSRedisCacheHelper.Get_Partition<string>(FileKey, 3);
if (!string.IsNullOrEmpty(kkka))
{
fileMd5 = Tools.ComputeFileHash(stream);
stream.Close();
fileMd5 = kkka;
}
else
{
using (Stream stream = System.IO.File.Open(Tools.GetApplicationPath() + fileHref, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
fileMd5 = Tools.ComputeFileHash(stream);
CSRedisCacheHelper.Set_PartitionWithTime<string>(FileKey, fileMd5, 5, 3);
stream.Close();
}
}
HostManager1.UpdateHostC(null, fileType, fileHref, fileMd5, hosts);
//IList<string> hostNumberList = new List<string>();
//foreach (Host h in hosts)
//{
// hostNumberList.Add(h.RoomNumber);
//}
return Json(new { IsSuccess = true, Message = "shengji" });
}
catch (Exception ex)
{
logger.Error(ex.Message);
logger.Error(ex.StackTrace);
return Json(new { IsSuccess = false, Message = ex.Message });
}
}
public ActionResult ShengJI_NEW(IList<int> idList, string fileName, IHostManager HostManager1, int RoomTypeID)
{
try
{
IList<Host> hosts = new List<Host>();
foreach (int id in idList)
{
Host host = HostManager1.Get(id);
if (host != null)
{
hosts.Add(host);
}
}
FileType fileType = FileType.Config;
switch (fileName.Substring(fileName.Length - 3).ToLower())
{
case "bin":
case "hex":
fileType = FileType.Firmware;
break;
case "dat":
fileType = FileType.Config;
break;
default:
return Json(new { IsSuccess = false, Message = "升级文件不合法!" });
}
//string fileHref = "Uploads/room_type/" + hosts[hosts.Count - 1].RoomType.ID + "/" + fileName;//升级文件相对路径
string fileHref = "Uploads/room_type/" + RoomTypeID + "/" + fileName;//升级文件相对路径
//string fileMd5 = "";
//using (Stream stream = System.IO.File.Open(Tools.GetApplicationPath() + fileHref, FileMode.Open))
//{
// fileMd5 = Tools.ComputeFileHash(stream);
// stream.Close();
//}
string fileMd5 = "";
string FileKey = "FileMD5_" + fileHref;
string kkka = CSRedisCacheHelper.Get_Partition<string>(FileKey, 3);
if (!string.IsNullOrEmpty(kkka))
{
fileMd5 = kkka;
}
else
{
using (Stream stream = System.IO.File.Open(Tools.GetApplicationPath() + fileHref, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
fileMd5 = Tools.ComputeFileHash(stream);
CSRedisCacheHelper.Set_PartitionWithTime<string>(FileKey, fileMd5, 6, 3);
stream.Close();
}
}
HostManager1.UpdateHostC(null, fileType, fileHref, fileMd5, hosts);
IList<string> hostNumberList = new List<string>();
foreach (Host h in hosts)

View File

@@ -7433,6 +7433,7 @@ namespace WebSite.Controllers
{
var action = item.action;
var name = item.applianceName;
//var area = item.area;
List<HostModal> q1 = new List<HostModal>();
if (name.Equals("灯"))
{
@@ -7440,6 +7441,10 @@ namespace WebSite.Controllers
}
else
{
//if (!string.IsNullOrEmpty(area))
//{
// name = area+ name;
//}
q1 = hostModals.Where(A => A.Modal.Name.Equals(name) || A.Modal.AliasName.Equals(name)).ToList();
}
if (q1 != null && q1.Count > 0)
@@ -7473,8 +7478,14 @@ namespace WebSite.Controllers
{
var action = item.action;
var name = item.applianceName;
//var area = item.area;
var value = item.value;
var q1 = hostModals.Where(A => A.Modal.Type == DeviceType.AirConditioner && A.Modal.Name.Equals(name));
//if (!string.IsNullOrEmpty(area))
//{
// name = area + name;
//}
var q1 = hostModals.Where(A => A.Modal.Type == DeviceType.AirConditioner && (A.Modal.Name.Equals(name) || A.Modal.AliasName.Equals(name)));
foreach (var item_inter in q1)
{
int status = 0;
@@ -7553,7 +7564,12 @@ namespace WebSite.Controllers
{
var action = item.action;
var name = item.applianceName;
//var area = item.area;
int hid = int.Parse(hotelid);
//if (!string.IsNullOrEmpty(area))
//{
// name = area + name;
//}
var scene = RoomTypeSceneManager.LoadAll().Where(A => A.HotelID == hid && A.Name.Equals(name)).FirstOrDefault();
if (scene != null)
{
@@ -7581,7 +7597,7 @@ namespace WebSite.Controllers
i.RequestId = msgid;
i.Platform = "ETV";
i.CommandDescription = "success: 控制设备"+JsonConvert.SerializeObject(lla);
i.CommandDescription = "success: 控制设备" + JsonConvert.SerializeObject(lla);
SendMQTTData.Send(i);
}
return Json(new { code = 200, msg = "控制成功" }, JsonRequestBehavior.AllowGet);
@@ -7607,6 +7623,7 @@ namespace WebSite.Controllers
public string action { get; set; }
public string applianceName { get; set; }
public string value { get; set; }
//public string area { get; set; }
}
public class TV_ResponseData
{

View File

@@ -306,6 +306,10 @@ namespace WebSite.Controllers
sysHotel.IsPushPMSData = entity.IsPushPMSData;
sysHotel.HeTongNumber = entity.HeTongNumber;
sysHotel.ETV_HotelID = entity.ETV_HotelID;
sysHotel.SwitchRoomStatus_PowerOff2 = entity.SwitchRoomStatus_PowerOff2;
sysHotel.SwitchRoomStatus_PowerOff4 = entity.SwitchRoomStatus_PowerOff4;
sysHotel.SwitchRoomStatus_PowerOff8 = entity.SwitchRoomStatus_PowerOff8;
sysHotel.SwitchRoomStatus_PowerOff16 = entity.SwitchRoomStatus_PowerOff16;
//sysHotel.TCLAppId = entity.TCLAppId;
//sysHotel.TCLAppSecret = entity.TCLAppSecret;
SysHotelManager.Update(sysHotel);
@@ -390,6 +394,12 @@ namespace WebSite.Controllers
TakeOut.SysHotel.IsPushPMSData = entity.IsPushPMSData;
TakeOut.SysHotel.HeTongNumber = entity.HeTongNumber;
TakeOut.SysHotel.ETV_HotelID = entity.ETV_HotelID;
TakeOut.SysHotel.SwitchRoomStatus_PowerOff2 = entity.SwitchRoomStatus_PowerOff2;
TakeOut.SysHotel.SwitchRoomStatus_PowerOff4 = entity.SwitchRoomStatus_PowerOff4;
TakeOut.SysHotel.SwitchRoomStatus_PowerOff8 = entity.SwitchRoomStatus_PowerOff8;
TakeOut.SysHotel.SwitchRoomStatus_PowerOff16 = entity.SwitchRoomStatus_PowerOff16;
//TakeOut.SysHotel.TCLAppId = entity.TCLAppId;
//TakeOut.SysHotel.TCLAppSecret = entity.TCLAppSecret;

View File

@@ -47,6 +47,7 @@ namespace WebSite
private IRoomStatusManager RoomStatusManager;
private IDeviceControlReceiver DeviceControlReceiver;
private IOverviewManager OverviewManager { get; set; }
public IHostModalManager HostModalManager { get; set; }
public static IHostServer hostServer { get; set; }
//private IGroupManager GroupManager;
@@ -57,7 +58,7 @@ namespace WebSite
private object _lock = new object(), _lock1 = new object();
//private List<Host> _hosts = null;//西软pms对接使用
public static int TimeInterval = 10;
public static int TimeInterval = 1;
public static string IntervalKey = "TimeInterval";
/// <summary>
/// 凌晨ECO
@@ -521,6 +522,7 @@ namespace WebSite
RoomStatusManager = (IRoomStatusManager)cxt.GetObject("Manager.RoomStatus");
DeviceControlReceiver = (IDeviceControlReceiver)cxt.GetObject("RCUHost.DeviceControlReceiver");
OverviewManager = (IOverviewManager)cxt.GetObject("Manager.Overview");
HostModalManager = (IHostModalManager)cxt.GetObject("Manager.HostModal");
_client = new syncstatus.syncstatusSoapClient();
Timer timer2 = new Timer(20000);//每20秒扫描一次
@@ -735,6 +737,19 @@ namespace WebSite
Host host = HostManager.GetByRoomNumber(T1.Item2, T1.Item1);
if (host != null)
{
int status_id2 = host.SysHotel.SwitchRoomStatus_PowerOff2;
int status_id4 = host.SysHotel.SwitchRoomStatus_PowerOff4;
int status_id8 = host.SysHotel.SwitchRoomStatus_PowerOff8;
int status_id16 = host.SysHotel.SwitchRoomStatus_PowerOff16;
if (status_id2 != 0 || status_id4 != 0 || status_id8 != 0 || status_id16 != 0)
{
if (status_id2 == roomStatus.ID||status_id4==roomStatus.ID||status_id8==roomStatus.ID||status_id16==roomStatus.ID)
{
HostModal h1 = new HostModal();
h1.Modal = new RoomTypeModal() { ModalAddress = "004000001", Type = DeviceType.ServiceInfo };
HostModalManager.SetDevice(host, h1, 2, 0, 25, 0, 0, 0);
}
}
HostManager.ChangeRoomStatus(host, T1.Item3);
string Key = CacheKey.SyncRoomStatus + "_" + host.HostNumber;
RoomStatusRequest ddd = new RoomStatusRequest();

11
WebSite/HTMLPage1.htm Normal file
View File

@@ -0,0 +1,11 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script src="Scripts/jquery-1.8.3.min.js" type="text/javascript"></script>
</head>
<body>
</body>
</html>

View File

@@ -1303,6 +1303,15 @@ namespace WebSite.Resource {
}
}
/// <summary>
/// 查找类似 Apartment for Rent 的本地化字符串。
/// </summary>
internal static string ChuZu {
get {
return ResourceManager.GetString("ChuZu", resourceCulture);
}
}
/// <summary>
/// 查找类似 Circuit 的本地化字符串。
/// </summary>
@@ -1663,6 +1672,15 @@ namespace WebSite.Resource {
}
}
/// <summary>
/// 查找类似 For Rent 的本地化字符串。
/// </summary>
internal static string DaiZu {
get {
return ResourceManager.GetString("DaiZu", resourceCulture);
}
}
/// <summary>
/// 查找类似 Data 的本地化字符串。
/// </summary>
@@ -6028,6 +6046,15 @@ namespace WebSite.Resource {
}
}
/// <summary>
/// 查找类似 Switch RoomStatus To Power Off 的本地化字符串。
/// </summary>
internal static string RoomStatusSwitch {
get {
return ResourceManager.GetString("RoomStatusSwitch", resourceCulture);
}
}
/// <summary>
/// 查找类似 Temp 的本地化字符串。
/// </summary>
@@ -7657,6 +7684,15 @@ namespace WebSite.Resource {
}
}
/// <summary>
/// 查找类似 CheckOut 的本地化字符串。
/// </summary>
internal static string TuiFang {
get {
return ResourceManager.GetString("TuiFang", resourceCulture);
}
}
/// <summary>
/// 查找类似 TV 的本地化字符串。
/// </summary>

View File

@@ -2899,4 +2899,16 @@ Single circuit status</value>
<data name="WelcomeBGM" xml:space="preserve">
<value>WelcomeBGM</value>
</data>
<data name="ChuZu" xml:space="preserve">
<value>Apartment for Rent</value>
</data>
<data name="DaiZu" xml:space="preserve">
<value>For Rent</value>
</data>
<data name="TuiFang" xml:space="preserve">
<value>CheckOut</value>
</data>
<data name="RoomStatusSwitch" xml:space="preserve">
<value>Switch RoomStatus To Power Off</value>
</data>
</root>

View File

@@ -1302,6 +1302,15 @@ namespace WebSite.Resource {
}
}
/// <summary>
/// 查找类似 出租 的本地化字符串。
/// </summary>
internal static string ChuZu {
get {
return ResourceManager.GetString("ChuZu", resourceCulture);
}
}
/// <summary>
/// 查找类似 回路 的本地化字符串。
/// </summary>
@@ -1662,6 +1671,15 @@ namespace WebSite.Resource {
}
}
/// <summary>
/// 查找类似 待租 的本地化字符串。
/// </summary>
internal static string DaiZu {
get {
return ResourceManager.GetString("DaiZu", resourceCulture);
}
}
/// <summary>
/// 查找类似 数据 的本地化字符串。
/// </summary>
@@ -6045,6 +6063,15 @@ namespace WebSite.Resource {
}
}
/// <summary>
/// 查找类似 状态更新断电​ 的本地化字符串。
/// </summary>
internal static string RoomStatusSwitch {
get {
return ResourceManager.GetString("RoomStatusSwitch", resourceCulture);
}
}
/// <summary>
/// 查找类似 客房温度 的本地化字符串。
/// </summary>
@@ -7665,6 +7692,15 @@ namespace WebSite.Resource {
}
}
/// <summary>
/// 查找类似 退房 的本地化字符串。
/// </summary>
internal static string TuiFang {
get {
return ResourceManager.GetString("TuiFang", resourceCulture);
}
}
/// <summary>
/// 查找类似 电视 的本地化字符串。
/// </summary>

View File

@@ -2901,4 +2901,16 @@
<data name="WelcomeBGM" xml:space="preserve">
<value>欢迎背景乐</value>
</data>
<data name="ChuZu" xml:space="preserve">
<value>出租</value>
</data>
<data name="DaiZu" xml:space="preserve">
<value>待租</value>
</data>
<data name="RoomStatusSwitch" xml:space="preserve">
<value>状态更新断电​</value>
</data>
<data name="TuiFang" xml:space="preserve">
<value>退房</value>
</data>
</root>

View File

@@ -1302,6 +1302,15 @@ namespace WebSite.Resource {
}
}
/// <summary>
/// 查找类似 出租 的本地化字符串。
/// </summary>
internal static string ChuZu {
get {
return ResourceManager.GetString("ChuZu", resourceCulture);
}
}
/// <summary>
/// 查找类似 回路 的本地化字符串。
/// </summary>
@@ -1662,6 +1671,15 @@ namespace WebSite.Resource {
}
}
/// <summary>
/// 查找类似 待租 的本地化字符串。
/// </summary>
internal static string DaiZu {
get {
return ResourceManager.GetString("DaiZu", resourceCulture);
}
}
/// <summary>
/// 查找类似 數據 的本地化字符串。
/// </summary>
@@ -6045,6 +6063,15 @@ namespace WebSite.Resource {
}
}
/// <summary>
/// 查找类似 切換狀態即斷電 的本地化字符串。
/// </summary>
internal static string RoomStatusSwitch {
get {
return ResourceManager.GetString("RoomStatusSwitch", resourceCulture);
}
}
/// <summary>
/// 查找类似 客房溫度 的本地化字符串。
/// </summary>
@@ -7667,6 +7694,15 @@ namespace WebSite.Resource {
}
}
/// <summary>
/// 查找类似 退房 的本地化字符串。
/// </summary>
internal static string TuiFang {
get {
return ResourceManager.GetString("TuiFang", resourceCulture);
}
}
/// <summary>
/// 查找类似 電視 的本地化字符串。
/// </summary>

View File

@@ -2903,4 +2903,16 @@
<data name="WelcomeBGM" xml:space="preserve">
<value>歡迎詞背景樂</value>
</data>
<data name="ChuZu" xml:space="preserve">
<value>出租</value>
</data>
<data name="DaiZu" xml:space="preserve">
<value>待租</value>
</data>
<data name="RoomStatusSwitch" xml:space="preserve">
<value>切換狀態即斷電</value>
</data>
<data name="TuiFang" xml:space="preserve">
<value>退房</value>
</data>
</root>

View File

@@ -388,6 +388,17 @@
</div>
</td>
</tr>
<tr>
<th align="right">
<label for="IsPowerOff1"> <%: Html.Language("RoomStatusSwitch")%>: </label>
</th>
<td>
<input id="SwitchRoomStatus_PowerOff2" type="checkbox" name="SwitchRoomStatus_PowerOff2" style="vertical-align: top;" value="2" <%: Model.SwitchRoomStatus_PowerOff2==2 ? "checked='checked'" : "" %> /> <%: Html.Language("ChuZu")%>
<input id="SwitchRoomStatus_PowerOff4" type="checkbox" name="SwitchRoomStatus_PowerOff4" style="vertical-align: top;" value="4" <%: Model.SwitchRoomStatus_PowerOff4==4 ? "checked='checked'" : "" %> /> <%: Html.Language("DaiZu")%>
<input id="SwitchRoomStatus_PowerOff8" type="checkbox" name="SwitchRoomStatus_PowerOff8" style="vertical-align: top;" value="8" <%: Model.SwitchRoomStatus_PowerOff8==8 ? "checked='checked'" : "" %> /> <%: Html.Language("TuiFang")%>
</td>
</tr>
<%-- TCL 功能--%>
<%-- <tr>
<th align="right">

View File

@@ -841,6 +841,7 @@
<Content Include="easyui-1.4.2\themes\metro\window.css" />
<Content Include="easyui-1.4.2\themes\mobile.css" />
<Content Include="Global.asax" />
<Content Include="HTMLPage1.htm" />
<Content Include="Images\app\Call.png" />
<Content Include="Images\app\loading.gif" />
<Content Include="Images\app\bottom.png" />

View File

@@ -0,0 +1,406 @@
# 物联网系统项目分析报告
## 1. 项目概览
本项目是一个基于UDP通信的物联网系统主要用于酒店客房RCURoom Control Unit主机的管理和控制。系统架构较为古老经历了从纯SQLServer存储到SQLServer+Redis混合存储的演进过程。
### 1.1 核心功能
- RCU主机通信与管理
- 客房设备状态监控与控制(灯光、空调、窗帘等)
- 房态管理(入住、退房、待租、空房)
- 智能语音设备集成(小度、天猫精灵等)
- 能源管理与统计
- 故障报警与处理
### 1.2 技术栈
- **开发语言**C#
- **通信协议**UDP
- **数据存储**SQLServer + Redis
- **ORM框架**NHibernate
- **缓存**Redis + C# MemoryCache
- **消息队列**Redis Stream
## 2. 系统架构分析
### 2.1 架构分层
系统采用典型的分层架构设计,主要包含以下层次:
1. **通信层**基于UDP协议的RCU主机通信
2. **业务逻辑层**:服务层和控制器
3. **数据访问层**基于NHibernate的数据库操作
4. **存储层**SQLServer和Redis混合存储
5. **表现层**Web API和管理界面
### 2.2 核心流程图
```mermaid
sequenceDiagram
participant RCU as RCU主机
participant HostServer as UDP通信服务器
participant Redis as Redis缓存
participant SQLServer as SQLServer数据库
participant Service as 业务服务层
participant API as Web API
RCU->>HostServer: 上报设备状态
HostServer->>Redis: 缓存实时状态
HostServer->>Service: 处理业务逻辑
Service->>SQLServer: 持久化数据
API->>Redis: 查询设备状态
API->>Service: 下发控制命令
Service->>HostServer: 转发控制命令
HostServer->>RCU: 控制设备
```
### 2.3 模块依赖关系
- **Common**:公共工具类和辅助方法
- **RCUHost**UDP通信核心模块
- **Service**:业务逻辑服务
- **Dao**:数据访问层
- **Domain**:领域模型
- **WebSite**Web API和管理界面
- **CommonEntity**:公共实体类
## 3. 核心模块分析
### 3.1 UDP通信模块
UDP通信是系统的核心负责与RCU主机的双向通信。
#### 3.1.1 主要功能
- 监听UDP端口3339接收RCU上报数据
- 解析UDP数据包分发到对应接收器
- 封装并发送控制命令到RCU主机
- 心跳检测与设备状态管理
- Redis Stream处理高并发数据
#### 3.1.2 关键代码
```csharp
// UDP服务器启动
public void Start()
{
udpClient = new UdpClient(3339);
uint IOC_IN = 0x80000000;
uint IOC_VENDOR = 0x18000000;
uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12;
udpClient.Client.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null);
udpClient.BeginReceive(ReceiveCallback, new UdpState(udpClient));
}
// 数据接收回调
private void ReceiveCallback(IAsyncResult ar)
{
UdpState state = ar.AsyncState as UdpState;
IPEndPoint remoteEP111 = new IPEndPoint(IPAddress.Any, 0);
byte[] receiveBuffer111 = state.UdpClient.EndReceive(ar, ref remoteEP111);
if (receiveBuffer111.Length > 0)
{
ReceiverContext context = new ReceiverContext(receiveBuffer111, remoteEP111, GetNextCustomer());
// 处理数据...
}
}
```
### 3.2 房间状态管理模块
负责处理RCU主机上报的房间状态数据包括设备状态、房卡状态、门磁状态等。
#### 3.2.1 主要功能
- 解析并处理房间状态数据包
- 缓存设备状态到Redis
- 持久化状态到SQLServer
- 触发相关业务逻辑(如欢迎词、节能控制等)
#### 3.2.2 关键代码
```csharp
public override void Process(ReceiverContext context)
{
string HostNumberOnly = context.SystemHeader.Value.HostNumber.ToString();
string hotelcode = context.SystemHeader.Value.HostNumber.ToHotelCode().ToString();
// 获取主机信息
Host host = null;
string Key = CacheKey.HostInfo_Key_HostNumber + "_" + HostNumberOnly;
object obj = MemoryCacheHelper.Get(Key);
if (obj != null)
{
host = obj as Host;
}
else
{
Host host_O = HostRepository.GetByHostNumber(HostNumberOnly);
MemoryCacheHelper.SlideSet(Key, host_O);
host = host_O;
}
// 处理设备状态
using (MemoryStream stream = new MemoryStream(context.Data, offset, length))
{
bool isTriggerWelcomeMsg = true;
Status status = DecodeRoomStatus_NEW(stream, HostNumberOnly, out isTriggerWelcomeMsg, host.SysHotel.Code, host.RoomNumber);
if (status != null)
{
// 缓存状态到Redis
CSRedisCacheHelper.Forever<ushort>(k1, context.SystemHeader.Value.FrameNo);
CSRedisCacheHelper.Forever<bool>(k2, !status.SysLock);
CSRedisCacheHelper.Forever<float>(k3, status.ElecQty);
CSRedisCacheHelper.Forever<byte>(k4, status.HostTemp);
// 处理设备状态
if (status.Devices != null && status.Devices.Count > 0)
{
ProcessModal_NEW_NEW(host, status.Devices, isTriggerWelcomeMsg, context.MessageID, context.IsMonitor);
}
}
}
}
```
### 3.3 主机管理模块
负责RCU主机的基本信息管理、升级、配置等操作。
#### 3.3.1 主要功能
- 主机信息管理(添加、修改、删除)
- 主机固件升级
- 主机配置管理
- 主机状态监控
#### 3.3.2 关键代码
```csharp
public override Host Get(object id)
{
var host = base.Get(id);
if (host != null)
{
host.Status = Common.CSRedisCacheHelper.Contains(host.HostNumber, host.MAC);
}
return host;
}
public void ChangeRoomStatus(Host host, RoomStatus roomStatus, string wxValidate)
{
// 更新主机房态
host.RoomStatus = roomStatus;
CurrentRepository.Update(host);
// 处理相关业务逻辑
if (roomStatus.ID == 2 || roomStatus.ID == 8) // 开房和退房时重置
{
// 重置智能设备
if (!string.IsNullOrEmpty(host.XiaoDuCUID))
{
// 重置小度
}
}
}
```
## 4. 数据存储分析
### 4.1 SQLServer存储
#### 4.1.1 主要存储内容
- 主机基本信息tb_Hosts
- 房间类型tb_RoomTypes
- 设备模板tb_Modals
- 主机设备关联tb_HostModal
- 房态信息tb_RoomStatus
- 历史记录(如房卡记录、故障记录等)
#### 4.1.2 数据访问方式
- NHibernate ORM框架
- 原生SQL查询
- 存储过程调用
### 4.2 Redis存储
#### 4.2.1 主要存储内容
- 实时设备状态
- 会话信息
- 监控日志
- 缓存热点数据
- 消息队列Redis Stream
#### 4.2.2 数据访问方式
- CSRedisClient客户端
- 发布/订阅机制
- Stream数据结构
#### 4.2.3 缓存键设计
```csharp
public class CacheKey
{
public static string HostModalStatus_Prefix = "HostModalStatusReceiver";
public static string RoomStatus_Prefix = "RoomStatusReceiver";
public static string HostInfo_Key_HostNumber = "HostInfo_Key_HostNumber";
public static string HostFrameNo = "HostFrameNo";
public static string PowerSupply = "PowerSupply";
public static string LockVoltage = "LockVoltage";
public static string HostTemp = "HostTemp";
// 更多缓存键...
}
```
### 4.3 存储优化分析
当前系统已经开始使用Redis缓存部分设备状态但仍存在以下问题
1. **缓存策略不完善**:部分热点数据未缓存
2. **数据同步问题**SQLServer与Redis数据同步机制不够健壮
3. **缓存过期策略**:部分缓存项过期时间设置不合理
4. **Redis使用效率**未充分利用Redis的高级特性
## 5. 性能优化建议
### 5.1 UDP通信优化
1. **数据包处理优化**
- 减少数据包解析时间
- 优化线程池使用
- 增加数据包校验机制
2. **并发处理优化**
- 合理设置并发线程数
- 使用异步非阻塞IO
- 优化Redis Stream消费速度
### 5.2 数据存储优化
1. **Redis缓存优化**
- 扩大Redis缓存范围将更多热点数据纳入缓存
- 优化缓存键设计,提高缓存命中率
- 合理设置缓存过期时间
- 使用Redis Pipeline减少网络往返时间
2. **SQLServer优化**
- 优化数据库索引
- 减少大表查询
- 优化存储过程性能
- 考虑使用读写分离
3. **数据同步优化**
- 建立更可靠的SQLServer与Redis数据同步机制
- 使用消息队列确保数据一致性
- 实现数据同步监控和故障恢复
### 5.3 代码质量优化
1. **内存管理**
- 减少大对象分配
- 使用对象池减少GC压力
- 优化Stream处理
2. **线程管理**
- 减少线程创建开销
- 合理使用线程池
- 避免线程死锁和竞态条件
3. **异常处理**
- 优化异常处理逻辑
- 减少异常抛出频率
- 增加异常监控和告警
### 5.4 架构优化
1. **微服务拆分**
- 将核心功能拆分为独立微服务
- 减少服务间耦合
- 提高系统可扩展性
2. **消息队列引入**
- 使用专业消息队列替代Redis Stream
- 提高消息处理可靠性
- 实现消息持久化
3. **API优化**
- 实现RESTful API设计
- 增加API版本控制
- 优化API响应时间
## 6. 代码质量分析
### 6.1 代码结构
- **优点**:模块划分清晰,职责明确
- **缺点**:部分模块代码过于庞大,可维护性差
### 6.2 命名规范
- **优点**大部分代码遵循C#命名规范
- **缺点**:部分变量和方法命名不够清晰
### 6.3 注释文档
- **优点**:关键方法有注释说明
- **缺点**:部分复杂逻辑缺少注释,文档不完善
### 6.4 异常处理
- **优点**:大部分异常有捕获和处理
- **缺点**:部分异常处理过于简单,缺少详细日志
### 6.5 代码重复
- **问题**:存在较多代码重复现象
- **建议**:提取公共方法,减少代码冗余
## 7. 总结与建议
### 7.1 系统优势
1. **成熟稳定**:系统经过长期运行验证,核心功能稳定可靠
2. **功能完善**涵盖了酒店RCU管理的核心功能
3. **扩展性好**:支持多种智能设备集成
4. **实时性强**基于UDP的通信机制保证了实时性
### 7.2 系统劣势
1. **技术栈陈旧**:使用的技术较为古老,缺乏现代化特性
2. **性能瓶颈**:在高并发场景下存在性能瓶颈
3. **代码质量**:部分代码质量较差,维护成本高
4. **存储优化不足**Redis使用不够充分未完全发挥其优势
### 7.3 改进建议
1. **技术栈升级**
- 考虑使用.NET Core/5+替代传统.NET Framework
- 引入现代化的缓存和消息队列解决方案
2. **架构重构**
- 采用微服务架构,提高系统可扩展性
- 引入容器化部署,简化运维
3. **存储优化**
- 进一步扩大Redis的使用范围
- 优化缓存策略,提高缓存命中率
- 建立更可靠的数据同步机制
4. **代码质量提升**
- 进行代码重构,提高可维护性
- 引入代码规范检查工具
- 完善单元测试和集成测试
5. **监控与告警**
- 建立完善的系统监控体系
- 实现智能告警机制
- 提供可视化的系统运行状态 dashboard
### 7.4 结论
本系统作为一个运行多年的物联网系统,虽然架构较为古老,但核心功能稳定可靠。通过合理的优化和重构,可以显著提升系统性能和可维护性,延长系统生命周期。建议在保持系统稳定性的前提下,逐步推进技术升级和架构优化,以适应不断增长的业务需求和技术发展趋势。

View File

@@ -0,0 +1,327 @@
# 物联网系统网页访问性能问题分析报告
## 1. 问题概述
本项目在用户量较大时IIS应用程序池频繁重启主要原因与网页访问功能使用的复杂存储过程查询有关。系统采用SQLServer存储过程进行后台数据查询在高并发场景下性能急剧下降导致应用程序池崩溃重启。
## 2. 核心问题分析
### 2.1 存储过程使用情况
系统在以下关键功能中使用了复杂存储过程:
1. **回路状态记录查询**`QueryHostModalRecords`
2. **回路异常记录查询**`QueryHostAbnormalRecords`
3. **能源统计查询**`QueryEnergyStatistics`
4. **房态查询**`QueryRoomRentState``QueryRoomState`
### 2.2 性能瓶颈分析
通过代码分析,发现以下性能瓶颈:
1. **无缓存机制**所有查询直接访问数据库没有使用Redis或内存缓存
2. **同步处理**:所有请求采用同步方式处理,没有实现异步操作
3. **资源管理不当**:数据库连接管理不规范,可能导致连接泄漏
4. **缺乏请求限流**:没有实现请求频率限制,高并发时系统压力过大
5. **存储过程复杂度**:存储过程逻辑复杂,可能包含大量数据处理和计算
6. **分页参数固定**:如`LoadHostModalRecords`方法中固定使用`page=1, rows=100`,可能查询过多数据
### 2.3 代码问题分析
1. **OverviewController.cs**
- 方法`LoadHostModalRecords`固定使用`page=1, rows=100`,忽略了传入的分页参数
- 代码中存在"严重怀疑这里的调用有问题"的注释,表明开发人员已意识到问题
2. **OverviewRepository.cs**
- 所有数据库操作采用同步方式
- 没有实现查询结果缓存
- 资源管理不规范,如数据库连接的处理
3. **Global.asax.cs**
- 虽然实现了全局异常捕获,但没有针对性能问题的特殊处理
- 没有实现请求限流机制
## 3. 优化建议
### 3.1 存储过程优化
1. **存储过程重构**
- 简化存储过程逻辑,将复杂计算移至应用层
- 添加适当的索引,优化查询性能
- 实现存储过程的参数化避免SQL注入风险
2. **查询优化**
- 合理使用分页,避免一次性查询大量数据
- 添加查询条件过滤,减少返回数据量
- 优化JOIN操作减少表关联复杂度
### 3.2 缓存机制引入
1. **Redis缓存**
- 对高频查询结果进行缓存,如房态信息、设备状态等
- 设置合理的缓存过期时间,确保数据及时性
- 实现缓存预热,系统启动时加载常用数据
2. **内存缓存**
- 对短期高频访问的数据使用MemoryCache
- 实现缓存依赖,确保数据一致性
### 3.3 异步处理
1. **异步API实现**
- 将控制器方法改为异步方法,使用`async/await`
- 实现异步数据库操作,减少线程阻塞
2. **并行处理**
- 对独立的查询操作使用并行处理,提高响应速度
- 合理控制并发度,避免系统资源耗尽
### 3.4 资源管理优化
1. **数据库连接管理**
- 使用连接池,避免频繁创建和销毁连接
- 确保连接正确关闭,避免连接泄漏
2. **内存管理**
- 减少大对象分配避免GC压力
- 使用对象池,重用频繁创建的对象
### 3.5 请求限流与保护
1. **请求限流**
- 实现基于IP的请求频率限制
- 对高频API添加令牌桶限流
2. **熔断机制**
- 实现服务熔断,避免级联失败
- 添加超时设置,防止请求长时间阻塞
3. **监控告警**
- 增加性能监控,实时跟踪系统状态
- 设置告警阈值,及时发现性能异常
### 3.6 架构优化
1. **微服务拆分**
- 将报表查询等重负载功能拆分为独立服务
- 减少服务间耦合,提高系统可扩展性
2. **读写分离**
- 实现数据库读写分离,减轻主库压力
- 报表查询等操作定向到从库
3. **消息队列**
- 使用消息队列处理异步任务,如统计计算等
- 提高系统的可靠性和弹性
## 4. 具体优化方案
### 4.1 紧急优化措施(短期)
1. **添加缓存机制**
- 对房态查询结果使用Redis缓存过期时间5分钟
- 对设备状态查询结果使用内存缓存过期时间30秒
2. **优化存储过程调用**
- 修正`LoadHostModalRecords`方法,使用正确的分页参数
- 添加参数验证,避免无效查询
3. **实现请求限流**
-`Application_BeginRequest`中添加请求频率限制
- 对高频API如`LoadHostModalRecords`设置更严格的限制
### 4.2 中期优化措施
1. **存储过程重构**
- 简化`QueryHostModalRecords`存储过程逻辑
- 添加适当的索引和查询优化
2. **异步处理实现**
- 将控制器方法改为异步方法
- 实现异步数据库操作
3. **缓存策略完善**
- 设计合理的缓存键结构
- 实现缓存预热和缓存更新机制
### 4.3 长期优化措施
1. **微服务架构改造**
- 将报表功能拆分为独立微服务
- 实现服务发现和负载均衡
2. **数据库架构优化**
- 实现读写分离
- 考虑使用时序数据库存储历史数据
3. **监控系统完善**
- 实现分布式追踪
- 建立性能基准和告警机制
## 5. 代码优化建议
### 5.1 OverviewController.cs 优化
```csharp
// 优化前
public ActionResult LoadHostModalRecords(int? page, int? rows, string order, string sort, string roomNumber, string startTime, string endTime, DeviceType? deviceType, string modalIds)
{
long total = 0;
var list = OverviewManager.LoadHostModalRecords(out total, 1, 100, order, sort, roomNumber, startTime, endTime, deviceType, modalIds, CurrentHotelID);
var result = new { total = total, rows = list };
return Content(JsonConvert.SerializeObject(result, new DataTableConverter()));
}
// 优化后
public async Task<ActionResult> LoadHostModalRecordsAsync(int? page, int? rows, string order, string sort, string roomNumber, string startTime, string endTime, DeviceType? deviceType, string modalIds)
{
// 验证分页参数
int pageNum = page ?? 1;
int pageSize = rows ?? 20;
if (pageSize > 100) pageSize = 100; // 限制最大页大小
// 生成缓存键
string cacheKey = $"HostModalRecords_{CurrentHotelID}_{pageNum}_{pageSize}_{order}_{sort}_{roomNumber}_{startTime}_{endTime}_{deviceType?.ToString()}_{modalIds}";
// 尝试从缓存获取
var cachedResult = CSRedisCacheHelper.Get<object>(cacheKey);
if (cachedResult != null)
{
return Json(cachedResult, JsonRequestBehavior.AllowGet);
}
// 异步查询数据
long total = 0;
var list = await Task.Run(() =>
OverviewManager.LoadHostModalRecords(out total, pageNum, pageSize, order, sort, roomNumber, startTime, endTime, deviceType, modalIds, CurrentHotelID)
);
var result = new { total = total, rows = list };
// 缓存结果过期时间5分钟
CSRedisCacheHelper.Set(cacheKey, result, 300);
return Content(JsonConvert.SerializeObject(result, new DataTableConverter()));
}
```
### 5.2 OverviewRepository.cs 优化
```csharp
// 优化前
public DataTable LoadHostModalRecords(out long total, int page, int rows, string order, string sort, string roomNumber, string startTime, string endTime, DeviceType? deviceType, string modalIds, int hotelID)
{
total = 0;
IDbCommand cmd = Session.Connection.CreateCommand();
cmd.CommandText = "QueryHostModalRecords";
cmd.CommandType = CommandType.StoredProcedure;
// 参数设置...
IDbDataAdapter da = new SqlDataAdapter(cmd as SqlCommand);
DataSet ds = new DataSet();
da.Fill(ds);
total = ds.Tables[0].Rows.Count;
return ds.Tables[0];
}
// 优化后
public async Task<DataTable> LoadHostModalRecordsAsync(out long total, int page, int rows, string order, string sort, string roomNumber, string startTime, string endTime, DeviceType? deviceType, string modalIds, int hotelID)
{
total = 0;
// 使用异步数据库操作
using (var connection = new SqlConnection(connectionString))
{
await connection.OpenAsync();
using (var command = connection.CreateCommand())
{
command.CommandText = "QueryHostModalRecords";
command.CommandType = CommandType.StoredProcedure;
// 参数设置...
using (var adapter = new SqlDataAdapter(command as SqlCommand))
{
DataSet ds = new DataSet();
// 使用异步填充
await Task.Run(() => adapter.Fill(ds));
total = ds.Tables[0].Rows.Count;
return ds.Tables[0];
}
}
}
}
```
### 5.3 Global.asax.cs 优化
```csharp
// 添加请求限流和缓存管理
protected void Application_BeginRequest(object sender, EventArgs e)
{
try
{
var ip = Request.UserHostAddress;
var url = Request.Url.AbsolutePath.ToLower();
// 定期清理恶意IP记录
var nowUtc = DateTime.UtcNow;
CleanupIpRecordIfNeeded(nowUtc);
// 检查请求频率
if (IsHighFrequencyRequest(ip))
{
logger.Error("高频请求IP:" + ip + " URL:" + url);
Response.StatusCode = 429; // Too Many Requests
Response.Write("请求过于频繁,请稍后再试");
Context.ApplicationInstance.CompleteRequest();
return;
}
// 恶意路径检查...
}
catch (Exception ex)
{
logger.Error("请求处理出错:" + ex.Message);
}
}
private bool IsHighFrequencyRequest(string ip)
{
var now = DateTime.UtcNow;
var info = _ipRequests.GetOrAdd(ip, _ => new RequestInfo());
lock (info)
{
info.RequestCount++;
info.LastRequest = now;
// 如果10秒内超过30次请求认为是高频请求
if (info.RequestCount > 30 && (now - info.FirstRequest).TotalSeconds < 10)
{
return true;
}
// 超过统计窗口则重置计数窗口
if ((now - info.FirstRequest).TotalSeconds >= 10)
{
info.FirstRequest = now;
info.RequestCount = 1;
}
}
return false;
}
```
## 5. 预期优化效果
通过实施上述优化方案,预计可以达到以下效果:
1. **响应时间提升**:高频查询响应时间从秒级降至毫秒级
2. **并发能力提升**系统并发处理能力提升5-10倍
3. **稳定性增强**IIS应用程序池不再频繁重启
4. **资源利用率提高**CPU和内存使用更加合理
5. **用户体验改善**:页面加载速度显著提升
## 6. 结论
本项目网页访问性能问题的根本原因在于复杂存储过程的不合理使用和缺乏有效的缓存机制。通过实施上述优化方案可以显著提高系统的性能和稳定性解决IIS应用程序池频繁重启的问题。
建议按照"紧急优化措施→中期优化措施→长期优化措施"的顺序逐步实施,确保系统在优化过程中的稳定性。同时,建立完善的性能监控体系,及时发现和解决新出现的性能问题。