前面提到过,使用家居设备驱动程序(以后简称SHDD)是SHP平台监控不同厂商设备的核心机制,并且在ISmartHome.cs文档中,规范了驱动程序的内容。当然,遗憾的是,它仅仅只是一个契约,理解实现起来有一定的困难和疑惑,本节通过编写一个具体的设备驱动程序,来帮助您编写SHDD。
首先,我们需要生产一个设备:智能冰箱。当然,我们不是电商,目前的智能冰箱业没有我们期望的功能。怎么办?OK,那就设计一个模拟的智能冰箱吧。
用C#编写一个模拟冰箱(暂不考虑智能化),很简单,2天就写完了。运行界面如下:
这样模拟操作“智能冰箱”,鼠标点击冰箱图片:模拟打开/关闭冰箱门,右键点击是放入一个食品,左键点击是取走一个食品(未来的智能冰箱,很可能支持RFID,而食品都贴有电子标签);冰箱有三个储存室,温度可以单独设定;冰箱门30秒未关会报警,冰箱有故障也会报警;冰箱有待机和工作两种状态,工作状态有两种
模式:省电模式和正常制冷模式。
接下来,如何为这个智能冰箱编写驱动程序。
驱动程序暴露设备的数据、操作功能,所以首先要决定哪些功能期望暴露出去。我们这样设计它的子设备(你也可以增加或隐藏一些子设备):
DO设备2个
DO0:冰箱待机/制冷开关
DO1:正常/省电模式转换开关
DI设备2个
DI0:冰箱门未关信号
DI 1:冰箱故障报警信号
AO设备3个
3个储存室的设定温度
AI设备3个
3个储存室的实际温度
SO设备0个
目前不需要,用过将来支持语音功能,可添加
SI设备4个
SI0:放入一个食品,报告放入食品的名称和过期日期
SI1:取走一个食品,报告取走食品的名称和过期日期
SI2:报告冰箱所有过期食品的列表
SI3:报告冰箱所有食品的列表(不主动发送)
现在,打开VS2013,在解决方案中添加一个类库项目,取名叫RefrigeratorDriver。在项目属性中,修改命名空间为eaglesmartrefrigerator。使用.NET framework 4版本。生成的程序集也改为eaglesmartrefrigerator。保存。
重新打开项目文件,添加对HomeLibrary类库的引用,并重命名类:public class SmartHome : ISmartHome, IWriteReadInterface;关键:必须实现ISmartHome接口和IWriteReadInterface接口。接口是可以多继承的。
cs文件名也重命名为RefrigeratorDriver.cs。然后在类定义的 ISmartHome接口处弹右键,选择“实现接口”,这样,在源代码中,VS给您生成了整个代码的框架。你现在需要做的,就是实现这些代码(适当添加内部使用的一些属性和方法,帮助你实现接口)。见下图
先
先不去编码实现这个接口。我们接下来,先实现设备类和6个子设备类。添加7个类的定义:
public class HomeDevice : IHomeDevice { }
public class DeviceDO : IDeviceDO { }
public class DeviceDI : IDeviceDI { }
public class DeviceAO : IDeviceAO { }
public class DeviceSO : IDeviceSO { }
public class DeviceSI : IDeviceSI { }
同样的方法生成7各类的代码框架。
好了,有了8个类。它们代表了整个设备系统具有的功能。先实现那个接口较好?经过反复实践,我们发现,6个子设备几乎可以设计得一样(通过把操作子设备的方法移到上层的设备类去实现后达到这样的效果);一般来说,具有整体 - 部分关系的结构类,“整体类”具有管理操控“部分”类的责任。而我们的软件架构正是这样设计的,如果忘记了,请参阅本系列博文的第二部分。
就这样,先把6个子类实现了。他们以后都不需要改动。在您编写其他设备系统的驱动程序时,直接复制过去接OK了。简单吧。下面是实现的完整免费代码(转载务必注明来源):
#region 后面6个子设备,都一样,复制到新的设备驱动程序源代码中即可
public class DeviceDO : IDeviceDO // author吴志辉 2014.4
{
private DeviceType devicetype;
public DeviceType DeviceType
{
get
{
return devicetype;
}
set
{
devicetype = value;
}
}
private DOType dotype; //7、DO类型
public DOType DoType
{
get
{
return dotype;
}
set
{
dotype = value;
}
}
private HomeLibrary.Devices.PowerState powerstate; //6、上电状态
public HomeLibrary.Devices.PowerState PowerState
{
get
{
return powerstate;
}
set
{
if (powerstate != value)
{
powerstate = value;
if (OnDigitalDataChanged != null)
OnDigitalDataChanged(new DataChangedEventArgs<bool>(this, ON));
}
}
}
public bool ON //通断状态:计算字段
{
get
{
if (PowerState == HomeLibrary.Devices.PowerState.PowerON) //电源接通
{
return (DoType == DOType.Open) ? true : false;
}
else
{
return (DoType == DOType.Open) ? false : true;
}
}
}
private int parentid; //1、设备编号
public int ParentID
{
get
{
return parentid;
}
set
{
parentid = value;
}
}
private ushort id; //2、子设备编号
public ushort Id
{
get
{
return id;
}
set
{
id = value;
}
}
private int tag; //3、子设备编号
public int Tag
{
get
{
return tag;
}
set
{
tag = value;
}
}
private string unitname; //4、计量单位文本描述
public string UnitName
{
get
{
return unitname;
}
set
{
unitname = value;
}
}
private string functiondescription; //5、DO子设备功能描述
public string FunctionDescription
{
get
{
return functiondescription;
}
set
{
functiondescription = value;
}
}
private string pictureoff; //8、图像文件
public string PictureOFF
{
get
{
return pictureoff;
}
set
{
pictureoff = value;
}
}
private string pictureon; //9、图像文件
public string PictureON
{
get
{
return pictureon;
}
set
{
pictureon = value;
}
}
public DeviceDO() //构造函数
{
Id = 0;
devicetype = DeviceType.DO;
DoType = DOType.Open; //常开开关
PowerState = HomeLibrary.Devices.PowerState.PowerON; //电源断开
PictureOFF = "";
PictureON = "";
UnitName = "";
functiondescription = "DO子设备";
}
public void ReadFromStream(BinaryReader br)
{
devicetype = (DeviceType)br.ReadByte();
parentid = br.ReadInt32(); //1、设备编号
id = br.ReadUInt16(); //2、子设备编号
tag = br.ReadInt32(); //3、tag
unitname = br.ReadString(); //4、计量单位文本描述
functiondescription = br.ReadString(); //5、DO子设备功能描述
dotype = (DOType)br.ReadByte(); //6、DO类型
powerstate = (HomeLibrary.Devices.PowerState)br.ReadByte(); //7、上电状态
pictureoff = br.ReadString(); //8、图像文件
pictureon = br.ReadString(); //9、图像文件
}
public void WriteToStream(BinaryWriter bw)
{
bw.Write((byte)devicetype);
bw.Write(parentid); //1、设备编号
bw.Write(id); //2、子设备编号
bw.Write(tag); //3、tag对象
bw.Write(unitname); //4、计量单位文本描述
bw.Write(functiondescription); //5、DO子设备功能描述
bw.Write((byte)dotype); //6、DO类型
bw.Write((byte)powerstate); //7、上电状态
bw.Write(pictureoff); //8、图像文件
bw.Write(pictureon); //9、图像文件
}
public event DigitalDataChanged OnDigitalDataChanged;
}
public class DeviceAO : IDeviceAO
{
private DeviceType devicetype;
public DeviceType DeviceType
{
get
{
return devicetype;
}
set
{
devicetype = value;
}
}
private int parentid; //1、设备编号
public int ParentID
{
get
{
return parentid;
}
set
{
parentid = value;
}
}
private ushort id; //2、子设备编号
public ushort Id
{
get
{
return id;
}
set
{
id = value;
}
}
private int tag; //3、子设备编号
public int Tag
{
get
{
return tag;
}
set
{
tag = value;
}
}
public string unitname; //4、计量单位文本描述
public string UnitName
{
get
{
return unitname;
}
set
{
unitname = value;
}
}
private string functiondescription; //5、AO子设备功能描述
public string FunctionDescription
{
get
{
return functiondescription;
}
set
{
functiondescription = value;
}
}
private float aovalue; //6、AO数据
public float AoValue
{
get
{
return aovalue;
}
set
{
if (aovalue != value)
{
aovalue = value;
if (OnAnalogDataChanged != null)
OnAnalogDataChanged(new DataChangedEventArgs<float>(this, aovalue));
}
}
}
private string picture; //7、图像文件
public string Picture
{
get
{
return picture;
}
set
{
picture = value;
}
}
byte dotplace;
public byte DotPlace
{
get
{
return dotplace;
}
set
{
if (value != dotplace)
dotplace = value;
}
}
public DeviceAO()
{
Id = 0;
devicetype = DeviceType.AO;
Picture = "";
UnitName = "";
DotPlace = 1;
functiondescription = "AO子设备";
}
public void ReadFromStream(BinaryReader br)
{
devicetype = (DeviceType)br.ReadByte();
parentid = br.ReadInt32(); //1、设备编号
id = br.ReadUInt16(); //2、子设备编号
tag = br.ReadInt32(); //3、tag
unitname = br.ReadString(); //4、计量单位文本描述
functiondescription = br.ReadString(); //5、DO子设备功能描述
aovalue = br.ReadSingle(); //6、模拟量数据
picture = br.ReadString(); //7、图像文件
dotplace = br.ReadByte(); //8、小数点位数
}
public void WriteToStream(BinaryWriter bw)
{
bw.Write((byte)devicetype);
bw.Write(parentid); //1、设备编号
bw.Write(id); //2、子设备编号
bw.Write(tag); //3、tag
bw.Write(unitname); //4、计量单位文本描述
bw.Write(functiondescription); //5、AO子设备功能描述
bw.Write(aovalue); //6、模拟量数据
bw.Write(picture); //7、图像文件
bw.Write(dotplace); //8、小数点位数
}
public event AnalogDataChanged OnAnalogDataChanged;
}
public class DeviceDI : IDeviceDI
{
private DeviceType devicetype;
public DeviceType DeviceType
{
get
{
return devicetype;
}
set
{
devicetype = value;
}
}
private int parentid; //1、设备编号
public int ParentID
{
get
{
return parentid;
}
set
{
parentid = value;
}
}
private ushort id; //2、子设备编号
public ushort Id
{
get
{
return id;
}
set
{
id = value;
}
}
private int tag; //3、子设备编号
public int Tag
{
get
{
return tag;
}
set
{
tag = value;
}
}
public string unitname; //4、计量单位文本描述
public string UnitName
{
get
{
return unitname;
}
set
{
unitname = value;
}
}
private string functiondescription; //5、AO子设备功能描述
public string FunctionDescription
{
get
{
return functiondescription;
}
set
{
functiondescription = value;
}
}
private bool hassignal; //6、是否有信号
public bool HasSignal
{
get
{
return hassignal;
}
set
{
if (hassignal != value)
{
hassignal = value;
if (OnDigitalDataChanged != null)
OnDigitalDataChanged(new DataChangedEventArgs<bool>(this, hassignal));
}
}
}
private string pictureoff; //7、OFF图像
public string PictureOFF
{
get
{
return pictureoff;
}
set
{
pictureoff = value;
}
}
private string pictureon; //8、ON图像
public string PictureON
{
get
{
return pictureon;
}
set
{
pictureon = value;
}
}
public DeviceDI()
{
devicetype = DeviceType.DI;
functiondescription = "DI子设备";
PictureOFF = "";
PictureON = "";
UnitName = "";
}
public void ReadFromStream(BinaryReader br)
{
devicetype = (DeviceType)br.ReadByte();
parentid = br.ReadInt32(); //1、设备编号
id = br.ReadUInt16(); //2、子设备编号
tag = br.ReadInt32(); //3、tag
unitname = br.ReadString(); //4、计量单位文本描述
functiondescription = br.ReadString(); //5、DI子设备功能描述
hassignal = br.ReadBoolean(); //6、是否有信号
pictureoff = br.ReadString(); //7、图像文件
pictureon = br.ReadString(); //8、图像文件
}
public void WriteToStream(BinaryWriter bw)
{
bw.Write((byte)devicetype);
bw.Write(parentid); //1、设备编号
bw.Write(id); //2、子设备编号
bw.Write(tag); //3、tag
bw.Write(unitname); //4、计量单位文本描述
bw.Write(functiondescription); //5、DI子设备功能描述
bw.Write(hassignal); //6、是否有信号
bw.Write(pictureoff); //7、图像文件
bw.Write(pictureon); //8、图像文件
}
public event DigitalDataChanged OnDigitalDataChanged;
}
public class DeviceAI : IDeviceAI
{
private DeviceType devicetype;
public DeviceType DeviceType
{
get
{
return devicetype;
}
set
{
devicetype = value;
}
}
private int parentid; //1、设备编号
public int ParentID
{
get
{
return parentid;
}
set
{
parentid = value;
}
}
private ushort id; //2、子设备编号
public ushort Id
{
get
{
return id;
}
set
{
id = value;
}
}
private int tag; //3、通用数据
public int Tag
{
get
{
return tag;
}
set
{
tag = value;
}
}
public string unitname; //4、计量单位文本描述
public string UnitName
{
get
{
return unitname;
}
set
{
unitname = value;
}
}
private string functiondescription; //5、AO子设备功能描述
public string FunctionDescription
{
get
{
return functiondescription;
}
set
{
functiondescription = value;
}
}
private float aivalue;
public float AiValue //6、采集的模拟量数据
{
get
{
return aivalue;
}
set
{
if (aivalue != value)
{
aivalue = value;
if (OnAnalogDataChanged != null)
OnAnalogDataChanged(new DataChangedEventArgs<float>(this, aivalue));
}
}
}
private string picture; //7、图像文件
public string Picture
{
get
{
return picture;
}
set
{
picture = value;
}
}
byte dotplace;
public byte DotPlace
{
get
{
return dotplace;
}
set
{
if (value != dotplace)
dotplace = value;
}
}
public DeviceAI()
{
devicetype = DeviceType.AI;
functiondescription = "AI子设备";
Picture = "";
UnitName = "";
dotplace = 0;
}
public void ReadFromStream(BinaryReader br)
{
devicetype = (DeviceType)br.ReadByte();
parentid = br.ReadInt32(); //1、设备编号
id = br.ReadUInt16(); //2、子设备编号
tag = br.ReadInt32(); //3、tag
unitname = br.ReadString(); //4、计量单位文本描述
functiondescription = br.ReadString(); //5、DI子设备功能描述
aivalue = br.ReadSingle(); //6、采集的模拟量数据
picture = br.ReadString(); //7、图像文件
dotplace = br.ReadByte(); //8、小数点位数
}
public void WriteToStream(BinaryWriter bw)
{
bw.Write((byte)devicetype);
bw.Write(parentid); //1、设备编号
bw.Write(id); //2、子设备编号
bw.Write(tag); //3、tag
bw.Write(unitname); //4、计量单位文本描述
bw.Write(functiondescription); //5、子设备功能描述
bw.Write(aivalue); //6、采集的模拟量数据
bw.Write(picture); //7、图像文件
bw.Write(dotplace); //8、小数点位数
}
public event AnalogDataChanged OnAnalogDataChanged;
}
public class DeviceSI : IDeviceSI
{
public event StreamDataChanged OnStreamDataChanged;
private DeviceType devicetype;
public DeviceType DeviceType
{
get
{
return devicetype;
}
set
{
devicetype = value;
}
}
private int parentid; //1、设备编号
public int ParentID
{
get
{
return parentid;
}
set
{
parentid = value;
}
}
private ushort id; //2、子设备编号
public ushort Id
{
get
{
return id;
}
set
{
id = value;
}
}
private int tag; //3、通用数据
public int Tag
{
get
{
return tag;
}
set
{
tag = value;
}
}
private StreamType streamtype; //4、流类型
public StreamType StreamType
{
get
{
return streamtype;
}
set
{
streamtype = value;
}
}
public string unitname; //5、计量单位文本描述
public string UnitName
{
get
{
return unitname;
}
set
{
unitname = value;
}
}
private string functiondescription; //6、子设备功能描述
public string FunctionDescription
{
get
{
return functiondescription;
}
set
{
functiondescription = value;
}
}
private string picture; //7、图像文件
public string Picture
{
get
{
return picture;
}
set
{
picture = value;
}
}
private byte[] sivalue; //8、字节流数据,一般不保存
public byte[] SiValue
{
get
{
return sivalue;
}
set
{
if (MemoryCompare(sivalue, value) != 0)
{
sivalue = new byte[value.Length];
Buffer.BlockCopy(value, 0, sivalue, 0, value.Length);
if (OnStreamDataChanged != null)
OnStreamDataChanged(new DataChangedEventArgs<byte[]>(this, sivalue));
}
}
}
public static int MemoryCompare(byte[] b1, byte[] b2)
{
int result = 0;
//if (b1 == null && b2 == null) return 0;
if (b1 == null) return -1;
if (b2 == null) return 1;
if (b1.Length != b2.Length)
result = b1.Length - b2.Length;
else
{
for (int i = 0; i < b1.Length; i++)
{
if (b1[i] != b2[i])
{
result = (int)(b1[i] - b2[i]);
break;
}
}
}
return result;
}
public DeviceSI()
{
devicetype = DeviceType.SI;
functiondescription = "SI子设备";
Picture = "";
UnitName = "";
sivalue = new byte[0];
streamtype = StreamType.TEXT;
}
public void ReadFromStream(BinaryReader br)
{
devicetype = (DeviceType)br.ReadByte();
parentid = br.ReadInt32(); //1、设备编号
id = br.ReadUInt16(); //2、子设备编号
tag = br.ReadInt32(); //3、tag
streamtype = (StreamType)br.ReadByte(); //4流类型
unitname = br.ReadString(); //5、计量单位文本描述
functiondescription = br.ReadString(); //6、子设备功能描述
picture = br.ReadString(); //7、图像文件
}
public void WriteToStream(BinaryWriter bw)
{
bw.Write((byte)devicetype);
bw.Write(parentid); //1、设备编号
bw.Write(id); //2、子设备编号
bw.Write(tag); //3、tag
bw.Write((byte)streamtype); //4流类型
bw.Write(unitname); //5、计量单位文本描述
bw.Write(functiondescription); //6、子设备功能描述
bw.Write(picture); //7、图像文件
}
}
public class DeviceSO : IDeviceSO
{
public event StreamDataChanged OnStreamDataChanged;
private DeviceType devicetype;
public DeviceType DeviceType
{
get
{
return devicetype;
}
set
{
devicetype = value;
}
}
private int parentid; //1、设备编号
public int ParentID
{
get
{
return parentid;
}
set
{
parentid = value;
}
}
private ushort id; //2、子设备编号
public ushort Id
{
get
{
return id;
}
set
{
id = value;
}
}
private int tag; //3、通用数据
public int Tag
{
get
{
return tag;
}
set
{
tag = value;
}
}
private StreamType streamtype; //4、流类型
public StreamType StreamType
{
get
{
return streamtype;
}
set
{
streamtype = value;
}
}
public string unitname; //5、计量单位文本描述
public string UnitName
{
get
{
return unitname;
}
set
{
unitname = value;
}
}
private string functiondescription; //6、子设备功能描述
public string FunctionDescription
{
get
{
return functiondescription;
}
set
{
functiondescription = value;
}
}
private string picture; //7、图像文件
public string Picture
{
get
{
return picture;
}
set
{
picture = value;
}
}
private byte[] sovalue; //8、字节流数据,一般不保存
public byte[] SoValue
{
get
{
return sovalue;
}
set
{
if (DeviceSI.MemoryCompare(sovalue, value) != 0)
{
sovalue = new byte[value.Length];
Buffer.BlockCopy(value, 0, sovalue, 0, value.Length);
if (OnStreamDataChanged != null)
OnStreamDataChanged(new DataChangedEventArgs<byte[]>(this, sovalue));
}
}
}
public DeviceSO()
{
devicetype = DeviceType.SO;
functiondescription = "SO子设备";
Picture = "";
streamtype = StreamType.TEXT;
}
public void ReadFromStream(BinaryReader br)
{
devicetype = (DeviceType)br.ReadByte();
parentid = br.ReadInt32(); //1、设备编号
id = br.ReadUInt16(); //2、子设备编号
tag = br.ReadInt32(); //3、tag
streamtype = (StreamType)br.ReadByte(); //4流类型
unitname = br.ReadString(); //5、计量单位文本描述
functiondescription = br.ReadString(); //6、子设备功能描述
picture = br.ReadString(); //7、图像文件
}
public void WriteToStream(BinaryWriter bw)
{
bw.Write((byte)devicetype);
bw.Write(parentid); //1、设备编号
bw.Write(id); //2、子设备编号
bw.Write(tag); //3、tag
bw.Write((byte)streamtype); //4流类型
bw.Write(unitname); //5、计量单位文本描述
bw.Write(functiondescription); //6、子设备功能描述
bw.Write(picture); //7、图像文件
}
}
#endregion
代码虽多,其实核心部分是事件响应机制的实现,请注意了。
接下来,最关键的两个对象如何实现。SmartHome和HomeDevice也具有整体- 部分关系。所有先编写“部分”类,一般“部分”类相对简单,先易后难是大多数人做事的取向。下面是实现冰箱这个设备类的完整代码:
public class HomeDevice : IHomeDevice //金鹰智能家居设备定义:一类设备
{
#region 接口属性
private int deviceid; //1
public int DeviceID
{
get
{
return deviceid;
}
set
{
deviceid = value;
}
}
private string devicename; //2
public string DeviceName
{
get
{
return devicename;
}
set
{
devicename = value;
}
}
private string positiondescription; //3
public string PositionDescription
{
get
{
return positiondescription;
}
set
{
positiondescription = value;
}
}
private bool used; //4
public bool Used
{
get
{
return used;
}
set
{
used = value;
}
}
#endregion
public string GetState(DeviceType DeviceType)
{
string result = "";
if (DeviceType == DeviceType.DO)
{
for (int i = 0; i < DODevices.Count; i++)
{
IDeviceDO dv = DODevices[i];
result += String.Format("({0}){1}{2} ", dv.Id + 1, dv.UnitName, (dv.ON ? "√" : "ㄨ"));
}
}
else if (DeviceType == DeviceType.DI)
{
for (int i = 0; i < DIDevices.Count; i++)
{
IDeviceDI dv = DIDevices[i];
result += String.Format("({0}){1}{2} ", dv.Id + 1, dv.UnitName, (dv.HasSignal ? "√" : "ㄨ"));
}
}
else if (DeviceType == DeviceType.AI)
{
for (int i = 0; i < AIDevices.Count; i++)
{
IDeviceAI dv = AIDevices[i];
string fstr = "({0}){1:f" + dv.DotPlace.ToString() + "}{2}";
result += String.Format(fstr, dv.Id + 1, dv.AiValue, dv.UnitName);
if (i < AIDevices.Count - 1) result += ", ";
}
}
else if (DeviceType == DeviceType.AO)
{
for (int i = 0; i < AODevices.Count; i++)
{
IDeviceAO dv = AODevices[i];
string fstr = "({0}){1:f" + dv.DotPlace.ToString() + "}{2}";
result += String.Format(fstr, dv.Id + 1, dv.AoValue, dv.UnitName);
if (i < AODevices.Count - 1) result += ", ";
}
}
else if (DeviceType == DeviceType.SI)
{
for (int i = 0; i < SIDevices.Count; i++)
{
IDeviceSI dv = SIDevices[i];
if (dv.StreamType == StreamType.TEXT && dv.SiValue != null)
result += String.Format("{0}:{1} ", dv.Id + 1, Encoding.UTF8.GetString(dv.SiValue));
else result += String.Format("{0}:{1} ", dv.Id + 1, dv.StreamType);
}
}
else if (DeviceType == DeviceType.SO)
{
for (int i = 0; i < SODevices.Count; i++)
{
IDeviceSO dv = SODevices[i];
if (dv.StreamType == StreamType.TEXT && dv.SoValue != null)
result += String.Format("{0}:{1} ", dv.Id + 1, Encoding.UTF8.GetString(dv.SoValue));
else result += String.Format("{0}:{1} ", dv.Id + 1, dv.StreamType);
}
}
return result;
}
public HomeDevice() //构造函数
{
DeviceName = "";
PositionDescription = "";
DODevices = new List<IDeviceDO>();
DIDevices = new List<IDeviceDI>();
AODevices = new List<IDeviceAO>();
AIDevices = new List<IDeviceAI>();
SODevices = new List<IDeviceSO>();
SIDevices = new List<IDeviceSI>();
}
~HomeDevice()
{
DODevices.Clear();
DIDevices.Clear();
AODevices.Clear();
AIDevices.Clear();
SODevices.Clear();
SIDevices.Clear();
}
public void ReadFromStream(BinaryReader br)
{
DeviceID = br.ReadInt32(); //1 设备唯一识别码:
Used = br.ReadBoolean(); //2 设备能否被使用或禁止,被禁用的设备不受监控
DeviceName = br.ReadString(); //3 设备名称文本描述
PositionDescription = br.ReadString(); //4 设备位置信息描述
int count = br.ReadUInt16(); //5 DO子设备的数量
DODevices.Clear();
for (int i = 0; i < count; i++)
{
DeviceDO device = new DeviceDO();
device.ReadFromStream(br);
DODevices.Add(device);
}
count = br.ReadUInt16(); //6 DI子设备的数量
DIDevices.Clear();
for (int i = 0; i < count; i++)
{
DeviceDI device = new DeviceDI();
device.ReadFromStream(br);
DIDevices.Add(device);
}
count = br.ReadUInt16(); //7 AO子设备的数量
AODevices.Clear();
for (int i = 0; i < count; i++)
{
DeviceAO device = new DeviceAO();
device.ReadFromStream(br);
AODevices.Add(device);
}
count = br.ReadUInt16(); //8 AI子设备的数量
AIDevices.Clear();
for (int i = 0; i < count; i++)
{
DeviceAI device = new DeviceAI();
device.ReadFromStream(br);
AIDevices.Add(device);
}
count = br.ReadUInt16(); //9 SO子设备的数量
SODevices.Clear();
for (int i = 0; i < count; i++)
{
DeviceSO device = new DeviceSO();
device.ReadFromStream(br);
SODevices.Add(device);
}
count = br.ReadUInt16(); //10 SI子设备的数量
SIDevices.Clear();
for (int i = 0; i < count; i++)
{
DeviceSI device = new DeviceSI();
device.ReadFromStream(br);
SIDevices.Add(device);
}
}
public void WriteToStream(BinaryWriter bw)
{
bw.Write(DeviceID); //1 设备唯一识别码:
bw.Write(Used); //2 设备能否被使用或禁止,被禁用的设备不受监控
bw.Write(DeviceName); //3 设备名称文本描述
bw.Write(PositionDescription); //4 设备位置信息描述
bw.Write((ushort)DODevices.Count); //5 DO子设备的数量
for (int i = 0; i < DODevices.Count; i++)
{
IDeviceDO device = DODevices[i];
device.WriteToStream(bw);
}
bw.Write((ushort)DIDevices.Count); //6 DI子设备的数量
for (int i = 0; i < DIDevices.Count; i++)
{
IDeviceDI device = DIDevices[i];
device.WriteToStream(bw);
}
bw.Write((ushort)AODevices.Count); //7 AO子设备的数量
for (int i = 0; i < AODevices.Count; i++)
{
IDeviceAO device = AODevices[i];
device.WriteToStream(bw);
}
bw.Write((ushort)AIDevices.Count); //8 AI子设备的数量
for (int i = 0; i < AIDevices.Count; i++)
{
IDeviceAI device = AIDevices[i];
device.WriteToStream(bw);
}
bw.Write((ushort)SODevices.Count); //9 SO子设备的数量
for (int i = 0; i < SODevices.Count; i++)
{
IDeviceSO device = SODevices[i];
device.WriteToStream(bw);
}
bw.Write((ushort)SIDevices.Count); //10 SI子设备的数量
for (int i = 0; i < SIDevices.Count; i++)
{
IDeviceSI device = SIDevices[i];
device.WriteToStream(bw);
}
}
private List<IDeviceAI> aidevices; //5
public List<IDeviceAI> AIDevices
{
get
{
return aidevices;
}
set
{
aidevices = value;
}
}
private List<IDeviceAO> aodevices; //6
public List<IDeviceAO> AODevices
{
get
{
return aodevices;
}
set
{
aodevices = value;
}
}
private List<IDeviceDI> didevices; //7
public List<IDeviceDI> DIDevices
{
get
{
return didevices;
}
set
{
didevices = value;
}
}
private List<IDeviceDO> dodevices; //8
public List<IDeviceDO> DODevices
{
get
{
return dodevices;
}
set
{
dodevices = value;
}
}
private List<IDeviceSI> sidevices; //9
public List<IDeviceSI> SIDevices
{
get
{
return sidevices;
}
set
{
sidevices = value;
}
}
private List<IDeviceSO> sodevices; //10
public List<IDeviceSO> SODevices
{
get
{
return sodevices;
}
set
{
sodevices = value;
}
}
public int GetAIDeviceCount()
{
return 3;
}
public int GetAODeviceCount()
{
return 3;
}
public int GetDIDeviceCount()
{
return 2;
}
public int GetDODeviceCount()
{
return 2;
}
public int GetSIDeviceCount()
{
return 4;
}
public int GetSODeviceCount()
{
return 0;
}
public IDeviceAI MakeNewDeviceAI()
{
DeviceAI dv = new DeviceAI();
dv.ParentID =deviceid;
dv.Id = (ushort)AIDevices.Count;
return dv;
}
public IDeviceAO MakeNewDeviceAO()
{
DeviceAO dv = new DeviceAO();
dv.ParentID = deviceid;
dv.Id = (ushort)AODevices.Count;
return dv;
}
public IDeviceDI MakeNewDeviceDI()
{
DeviceDI dv = new DeviceDI();
dv.ParentID = deviceid;
dv.Id = (ushort)DIDevices.Count;
return dv;
}
public IDeviceDO MakeNewDeviceDO()
{
DeviceDO dv = new DeviceDO();
dv.ParentID = deviceid;
dv.Id = (ushort)DODevices.Count;
return dv;
}
public IDeviceSI MakeNewDeviceSI()
{
DeviceSI dv = new DeviceSI();
dv.ParentID = deviceid;
dv.Id = (ushort)SIDevices.Count;
return dv;
}
public IDeviceSO MakeNewDeviceSO()
{
DeviceSO dv = new DeviceSO();
dv.ParentID = deviceid;
dv.Id = (ushort)SODevices.Count;
return dv;
}
}
仔细研究,会发现HomeDevice对象,主要功能就是管理着它的个体对象的集合,并负责往上级报告各类子设备的状态数据(方法GetState(DeviceType DeviceType))。
轮到最重量级对象SmartHome登场了。先看几个重要的方法:
void InitComm(object[] commObjects); //用于初始化通信对象:传递多个通信对象,如串口,Tcpsocket等
bool GetDOState(int deviceID); //获取该设备DO输出电源状态的方法:厂家必须提供,如果有代码处理,返回true
bool GetDIState(int deviceID); //获取本设备DI输入状态的方法:厂家必须要实现
bool GetAOState(int deviceID); //获取本设备AO输出数据的方法:厂家提供
bool GetAIState(int deviceID); //获取本设备AI输入状态的方法:厂家必须提供
bool GetSOState(int deviceID); //获取子设备SO输出数据的方法:厂家提供,可能无用
bool GetSIState(int deviceID); //获取子设备SI输入状态的方法:厂家必须提供
void SendDO(int deviceID, ushort subID, bool On); //对DO输出设备,必须实现输出控制
void SendAO(int deviceID, ushort subID, float[] value); //对AO输出设备,必须实现输出控制
void SendSO(int deviceID, ushort subID, byte[] value); //对SO输出设备,必须实现输出控制
void ProcessDeviceStateData(byte[] states); //厂家实现的通用处理接收数据的方法
可以归纳为四类功能:
1、初始化通信参数InitComm
2、获取特定设备的状态
3、控制特定子设备工作。对于没有的子设备,我们空实现即可。
4、处理设备系统发来的数据ProcessDeviceStateData。
对串口设备,这些数据直接来源于串口设备。我们约定:由驱动程序直接操控串口设备。
对IPC通信的设备,来源于消息队列,我们约定:由驱动程序自己去消息队列中读取;一般是设置一个50毫秒的定时器,周期性读取数据,如果有,交给ProcessDeviceStateData处理。
对TCP/IP通信而来的数据,我们约定:由监控程序SHM负责接收TCP/IP通信数据,然后直接调用驱动程序的ProcessDeviceStateData方法处理。参见下面的SHM处理TCp/IP通信事件的代码:
private void newClient_OnTcpDataReceived(TextReceivedEventArgs<byte[]> e) //收到原始数据
{
if (this.InvokeRequired)
{
this.Invoke(new OnDataReceived(newClient_OnTcpDataReceived), e);
}
else
{
stringJson json = stringJson.ConvertBytesTostringJson(e.Datagram,
SmartHomeChannel.SHFLAG);
if (json != null)
ProcessCommand(json, e.TcpClient);
}
}
void ProcessCommand(stringJson json, ConnectClient client) //★★★★处理客户端发来的指令
{
if (json == null) return; //非系统指令
string cmd = json.GetValume("cmd");
if (cmd == null) return; //无命令
if (chkShowTcp.Checked)
{
txtJson.Text += "\r\n收到数据:" + json.ToString();
txtJson.SelectionStart = txtJson.TextLength;
txtJson.SelectionLength = 0;
txtJson.ScrollToCaret();
}
if (cmd == SHProtocol.LOGIN) //联系指令
{
#region
string user = json.GetValume("user");
string password = json.GetValume("password");
if (user == null || password == null) return;
if (user != txtUser.Text || password != txtPassword.Text) //检查客户端非法性
{
//json.DeleteName("longin"); //不再要求自动登录
json.AddNameValume("longin", "1");
json.AddNameValume("error", "登录信息错误!");
byte[] mes = json.GetBytes();
client.Send(mes);
return;
}
client.canComunication = true; //可以正常通信了
client.User = user;
json.AddNameValume("login", "OK");
json.AddNameValume("ip", client.IP);
byte[] ret = json.GetBytes();
client.Send(ret);
tStartListen.Abort(); //连接成功后,停止监听其他连接
btnStartListen.Text = "启动监听";
lbTCP.Text = "监听停止,与客户TCP通信工作中...";
#endregion
}
else //其他指令
SmartHome.ProcessDeviceStateData(json.GetBytes()); //设备系统TCP传来的数据,传给智能家居驱动去处理!
}
这几个约定很重要,记住了。免得您重复去接收和处理数据。
为什么需要InitComm接口呢?传递给他的参数是一个对象列表,意味着可以传递任何对象进来。
我们是这样约定的:
第一个参数是串口通信对象,一般在SHM已经正确设置好了。如果不适用串口通信,必须传递null占位。
第二参数是传递一个TCP通信的客户端列表对象。 它是SHM中连接设备系统的TCP通信对象列表。
第三个参数是传递一个共享内存对象,第四参数传递共享内存对象的数据结构。用于IPC通信。
下面是智能冰箱SmartHome的完整实现代码:
public class SmartHome : ISmartHome, IWriteReadInterface //金鹰智能家居定义:类名必须统一设计为“SmartHome”
{
public SerialPort comm = null; //智能家居程序如果直接与串口设备通信,需要外部传递一个串口通信对象,如果网络通信,就用socket
public SmartHomeChannel smarthomechannel; //映射到共享内存的对象指针
public ShareMemory smarthomeshareMemory; //共享内存对象:用于进程间交换数据
List<ConnectClient> Clients = new List<ConnectClient>(); //TCP通信对象立标
#region 接口属性实现
private List<IHomeDevice> homedevices;
public List<IHomeDevice> HomeDevices
{
get
{
return homedevices;
}
set
{
homedevices = value;
}
}
private string homename;
public string HomeName
{
get
{
return homename;
}
set
{
homename = value;
}
}
private string password;
public string Password
{
get
{
return password;
}
set
{
password = value;
}
}
#endregion
public string FileName { get; set; } //保存智能家居的数据文件,不采用数据库
public SmartHome(string _filename) //带参数的构造函数
{
FileName = _filename;
homedevices = new List<IHomeDevice>();
ReadFromFile(FileName);
timer = new System.Timers.Timer(50);
timer.Elapsed += timer_Elapsed;
}
private System.Timers.Timer timer; //定时器,检查新数据
private bool isDealing = false;
void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) //★★检查是否有设备系统发来的新数据
{
if (isDealing) return; //防止重入
try
{
isDealing = true;
stringJson json = smarthomeshareMemory.GetMessage(); //设备系统发来信息
//ProcessDeviceStateData(json.GetBytes()); //由于是TCP通信,不处理
json = smarthomeshareMemory.GetNotify(); //SHM发来的通知:一般是控制指令
ProcessNotifyData(json);
}
catch //(Exception ex)
{
isDealing = false;
//MessageBox.Show(ex.ToString());
}
isDealing = false;
}
public void ProcessNotifyData(stringJson json) //★★处理服务器发来的指令
{
if (json == null) return;
string cmd = json.GetValume("cmd");
if (cmd == null) return;
else if (cmd == SHProtocol.DEVSTATE) //获取并返回SHA的启动状态
{
//已经取消了该功能,直接在 SendXX接口中实现
}
else if (cmd == SHProtocol.SHACTRL) //SHM发来的控制指令SHACTRL = "505";
{ //给某个SHA系统的设备发指令[appid=N][devid=M][subid=X][type=Y][act=K]
#region
this.TCPNotifyDevice(json); //直接转发给设备系统
#endregion
}
}
public void InitComm(object[] commObjects) //应用程序需要调用该方法之前,需要正确设置commObject
{
if (commObjects == null) return;
if (commObjects[0] is SerialPort)
comm = (SerialPort)commObjects[0];
if (commObjects[1] is List<ConnectClient>)
Clients = (List<ConnectClient>)commObjects[1]; //TCP通信,这个是必须的
//还需要共享内存来传递信息
if (commObjects[2] is ShareMemory)
smarthomeshareMemory = (ShareMemory)commObjects[2];
if (commObjects[3] is SmartHomeChannel)
smarthomechannel = (SmartHomeChannel)commObjects[3];
//StartRefrigerator(smarthomechannel); //启动模拟冰箱
timer.Start(); //启动定时检查数据
}
~SmartHome()
{
homedevices.Clear();
}
void StartRefrigerator(SmartHomeChannel smarthomeChannel) //启动APP,不是IPC通信,不需要
{
}
private void InitSmartHome() //当没有保存SH的文件时,首次初始化SH,厂家必须提供该功能,用于暴露设备系统的功能
{
HomeName = "金鹰智能冰箱ESR2014"; //最多32个汉字
Password = "";
IHomeDevice hd = new HomeDevice(); //1、有一个10路的开关设备
hd.DeviceID = 0;
hd.DeviceName = "金鹰智能冰箱ESR2014";
hd.Used = true;
hd.PositionDescription = "家庭";
HomeDevices.Add(hd);
#region
string[] DOfun = { "工作/待机转换开关", "省电模式开关" };
for (int i = 0; i < DOfun.Length; i++) //添加DO设备
{
IDeviceDO dv = new DeviceDO();
dv.DeviceType = DeviceType.DO;
dv.Id = (ushort)i;
dv.Tag = 0;
dv.DoType = DOType.Open;
dv.FunctionDescription = DOfun[i];
dv.ParentID = hd.DeviceID;
dv.UnitName = "";
hd.DODevices.Add(dv);
}
string[] DIfun = { "冰箱门未关报警", "冰箱故障报警" };
for (int i = 0; i < DIfun.Length; i++) //添加DI子设备
{
IDeviceDI dv = new DeviceDI();
dv.DeviceType = DeviceType.DI;
dv.Id = (ushort)i;
dv.HasSignal = false;
dv.Tag = 0;
dv.FunctionDescription = DIfun[i];
dv.ParentID = hd.DeviceID;
dv.UnitName = "";
hd.DIDevices.Add(dv);
}
string[] AOfun = { "设置冷藏室温度", "设置冷冻室温度", "设置冰冻室温度" };
for (int i = 0; i < AOfun.Length; i++) //添加DI子设备
{
IDeviceAO dv = new DeviceAO();
dv.DeviceType = DeviceType.AO;
dv.Id = (ushort)i;
dv.AoValue = 2.0f;
dv.Tag = 0;
dv.FunctionDescription = AOfun[i];
dv.ParentID = hd.DeviceID;
dv.UnitName = "℃";
hd.AODevices.Add(dv);
}
string[] AIfun = { "冷藏室温度", "冷冻室温度", "冰冻室温度" };
for (int i = 0; i < AIfun.Length; i++) //添加DI子设备
{
IDeviceAI dv = new DeviceAI();
dv.DeviceType = DeviceType.AI;
dv.Id = (ushort)i;
dv.AiValue = 15.0f;
dv.Tag = 0;
dv.FunctionDescription = AIfun[i];
dv.ParentID = hd.DeviceID;
dv.UnitName = "℃";
hd.AIDevices.Add(dv);
}
string[] SIfun = { "放入食品", "取走食品", "过期食品", "所有食品" };
for (int i = 0; i < SIfun.Length; i++) //添加DI子设备
{
IDeviceSI dv = new DeviceSI();
dv.DeviceType = DeviceType.SI;
dv.Id = (ushort)i;
dv.StreamType = StreamType.TEXT;
dv.Tag = 0;
dv.FunctionDescription = SIfun[i];
dv.ParentID = hd.DeviceID;
dv.UnitName = "";
hd.SIDevices.Add(dv);
}
#endregion
}
public void ReadFromFile(string filename) //从文件读入SH对象的数据
{
if (!File.Exists(filename))
{
InitSmartHome();
return;
}
homedevices.Clear(); //先清除原来设备数据
FileStream fs = new FileStream(filename, FileMode.Open);
BinaryReader br = new BinaryReader(fs, Encoding.UTF8);
try
{
ReadFromStream(br);
br.Close();
br.Dispose();
fs.Close();
fs.Dispose();
}
catch { }
}
public void SaveToFile() //SmartHome对象写入文件
{
FileStream fs = new FileStream(FileName, FileMode.Create);
BinaryWriter bw = new BinaryWriter(fs, Encoding.UTF8);
try
{
WriteToStream(bw);
bw.Close();
bw.Dispose();
fs.Close();
fs.Dispose();
}
catch { }
}
public void ReadFromStream(BinaryReader br) // 从流中读入SH对象的数据
{
homename = br.ReadString(); //1、读取名字
password = br.ReadString(); //2、读取口令
int count = br.ReadInt32(); //3、读取设备数量
for (int i = 0; i < count; i++)
{
IHomeDevice hd = new HomeDevice();
hd.ReadFromStream(br);
homedevices.Add(hd);
}
}
public void WriteToStream(BinaryWriter bw) //SmartHome对象写入流
{
bw.Write(homename); //1、写入名字
bw.Write(password); //2、写入口令
bw.Write(homedevices.Count); //3、写入设备数量
for (int i = 0; i < homedevices.Count; i++)
{
IHomeDevice hd = homedevices[i];
hd.WriteToStream(bw);
}
}
public void ProcessDeviceStateData(byte[] states) //★★★★厂家实现的通用处理接收数据的方法
{
stringJson json = stringJson.ConvertBytesTostringJson(states, SmartHomeChannel.SHFLAG);
string cmd = json.GetValume("cmd");
if (cmd == null) return; //无命令
if (cmd == SHProtocol.DEVSTATE) //状态指令:设备系统一般只返回状态数据,没有控制指令
{ //某个SHA系统的设备的子设备状态数据[appid=N][devid=M][type=Y][subid=X][value=V]
string stype = json.GetValume("type"); if (stype == null) return;
string devid = json.GetValume("devid"); if (devid == null) return;
string subid = json.GetValume("subid");
string value = json.GetValume("value"); if (value == null) return;
DeviceType type = (DeviceType)Enum.Parse(typeof(DeviceType), stype);
if (type == DeviceType.DI)
{
ProcessDIData(devid, value, subid); //★★处理DI数据
}
else if (type == DeviceType.AI)
{
ProcessAIData(devid, value, subid); //★★处理AI数据
}
else if (type == DeviceType.DO)
{
ProcessDOData(devid, value, subid);
}
else if (type == DeviceType.AO)
{
ProcessAOData(devid, value, subid); //★★处理AO数据
}
else if (type == DeviceType.SI)
{
if (subid == null) return; //只有单独报告SI的数据
//string streamtype = json.GetValume("sitype");
//if (streamtype != StreamType.TEXT.ToString()) return; //只处理TEXT流的情况,其他以后完善
int id = -1;
if (!int.TryParse(devid, out id)) return;
if (id < 0) return;
int deviceid = id;
IHomeDevice hd = FindHomeDevice(deviceid);
if (hd == null) return;
int sid = int.Parse(subid);
if (sid < 0 || sid >= hd.AODevices.Count) return;
IDeviceSI dv = hd.SIDevices[sid];
dv.SiValue = Encoding.UTF8.GetBytes(value); //可能引发事件
}
}
}
private void ProcessAOData(string dvid, string state, string subid = null) //★★处理DO数据
{
int id = -1;
if (!int.TryParse(dvid, out id)) return;
if (id < 0) return;
int deviceid = id;
IHomeDevice hd = FindHomeDevice(deviceid);
if (hd == null) return;
if (subid != null) //单独子设备报告数据
{
int sid = int.Parse(subid);
if (sid < 0 || sid >= hd.AODevices.Count) return;
IDeviceAO dv = hd.AODevices[sid];
dv.AoValue = float.Parse(state); //可能引发事件
return;
}
string[] ff = state.Split(new char[] { ',' }); //AO子设备数量,如果数量比原来的大,要补充子设备
int count = ff.Length;
for (int i = hd.AODevices.Count; i < count; i++) //补充子设备
{
DeviceAO dv = new DeviceAO();
dv.Id = (ushort)i;
dv.FunctionDescription = "AO模拟信号";
dv.ParentID = deviceid;
dv.UnitName = "";
hd.AODevices.Add(dv);
}
for (int i = 0; i < count; i++) //设置各DI设备状态
{
IDeviceAO dv = hd.AODevices[i];
if (i < ff.Length)
dv.AoValue = float.Parse(ff[i]); //触发事件
}
}
private void ProcessAIData(string dvid, string state, string subid = null) //★★处理DO数据
{
int id = -1;
if (!int.TryParse(dvid, out id)) return;
if (id < 0) return;
ushort deviceid = (ushort)id;
IHomeDevice hd = FindHomeDevice(deviceid);
if (hd == null) return;
if (subid != null) //单独子设备报告数据
{
int sid = int.Parse(subid);
if (sid < 0 || sid >= hd.AIDevices.Count) return;
IDeviceAI dv = hd.AIDevices[sid];
dv.AiValue = float.Parse(state); //可能引发事件
return;
}
string[] sf = state.Split(new char[] { ',' }); //AI子设备数量,如果数量比原来的大,要补充子设备
int count = sf.Length;
for (int i = hd.AIDevices.Count; i < count; i++) //补充子设备
{
DeviceAI dv = new DeviceAI();
dv.Id = (ushort)i;
dv.FunctionDescription = "AI模拟信号";
dv.ParentID = deviceid;
dv.UnitName = "";
hd.AIDevices.Add(dv);
}
for (int i = 0; i < count; i++) //设置各DI设备状态
{
IDeviceAI dv = hd.AIDevices[i];
if (i < sf.Length)
dv.AiValue = float.Parse(sf[i]); //触发事件
}
}
private void ProcessDOData(string dvid, string state, string subid = null) //★★处理DO数据
{
int id = -1;
if (!int.TryParse(dvid, out id)) return;
if (id < 0) return;
int deviceid = id;
IHomeDevice hd = FindHomeDevice(deviceid);
if (hd == null) return;
if (subid!=null ) //单独子设备报告数据
{
int sid = int.Parse(subid);
if (sid < 0 || sid >= hd.DODevices.Count) return;
IDeviceDO dv = hd.DODevices[sid];
dv.PowerState = (state =="1")? PowerState.PowerON : PowerState.PowerOFF;
return;
}
int count = state.Length; //DO子设备数量,如果数量比原来的大,要补充子设备
for (int i = hd.DODevices.Count; i < count; i++) //补充子设备
{
DeviceDO dv = new DeviceDO();
dv.Id = (ushort)i;
dv.FunctionDescription = "屋内分开关";
dv.ParentID = deviceid;
dv.UnitName = "";
hd.DODevices.Add(dv);
}
for (int i = 0; i < count; i++) //设置各DO设备状态
{
IDeviceDO dv = hd.DODevices[i];
dv.PowerState = state[i] == '1' ? PowerState.PowerON : PowerState.PowerOFF;
}
}
private void ProcessDIData(string dvid, string state, string subid = null) //★★处理DI数据
{
int id = -1;
if (!int.TryParse(dvid, out id)) return;
if (id < 0) return;
ushort deviceid = (ushort)id;
IHomeDevice hd = FindHomeDevice(deviceid);
if (hd == null) return;
if (subid != null) //单独子设备报告数据
{
int sid = int.Parse(subid);
if (sid < 0 || sid >= hd.DIDevices.Count) return;
IDeviceDI dv = hd.DIDevices[sid];
dv.HasSignal = (state== "1");
return;
}
int count = state.Length; //DI子设备数量,如果数量比原来的大,要补充子设备
for (int i = hd.DIDevices.Count; i < count; i++) //补充子设备
{
DeviceDI dv = new DeviceDI();
dv.Id = (ushort)i;
dv.FunctionDescription = "DI开闭信号";
dv.ParentID = deviceid;
dv.UnitName = "";
hd.DIDevices.Add(dv);
}
for (int i = 0; i < count; i++) //设置各DI设备状态
{
IDeviceDI dv = hd.DIDevices[i];
dv.HasSignal = state[i] == '1'; //触发事件
}
}
private IHomeDevice FindHomeDevice(int deviceid) //搜索设备号匹配的设备
{
for (int i = 0; i < HomeDevices.Count; i++)
{
if (HomeDevices[i].DeviceID == deviceid)
return HomeDevices[i];
}
return null;
}
public bool GetAIState(int deviceID)
{
IHomeDevice hd = FindHomeDevice(deviceID);
if (hd == null) return false;
if (hd.AIDevices.Count == 0) return false;
if (Clients.Count == 0) return false; //没有连接的客户端,或者断开了
stringJson json = new stringJson(SmartHomeChannel.SHFLAG);
//SHACTRL = "505"; //给某个SHA系统的设备发指令[appid=N][devid=M][subid=X][type=Y]
[act=K]
json.AddNameValume("cmd", SHProtocol.DEVSTATE);
json.AddNameValume("appid", smarthomechannel.appid.ToString());
json.AddNameValume("devid", deviceID.ToString());
json.AddNameValume("type", DeviceType.AI.ToString());
try
{
TCPNotifyDevice(json);
Thread.Sleep(50);
}
catch { return false; }
return true;
}
public bool GetAOState(int deviceID)
{
IHomeDevice hd = FindHomeDevice(deviceID);
if (hd == null) return false;
if (hd.AODevices.Count == 0) return false;
if (Clients.Count == 0) return false; //没有连接的客户端,或者断开了
stringJson json = new stringJson(SmartHomeChannel.SHFLAG);
//SHACTRL = "505"; //给某个SHA系统的设备发指令[appid=N][devid=M][subid=X][type=Y]
[act=K]
json.AddNameValume("cmd", SHProtocol.DEVSTATE);
json.AddNameValume("appid", smarthomechannel.appid.ToString());
json.AddNameValume("devid", deviceID.ToString());
json.AddNameValume("type", DeviceType.AO.ToString());
try
{
TCPNotifyDevice(json);
Thread.Sleep(50);
}
catch { return false; }
return true;
}
public bool GetSIState(int deviceID)
{
return false; //throw new NotImplementedException();//无此设备,空实现
}
public bool GetSOState(int deviceID)
{
return false; //throw new NotImplementedException();//无此设备,空实现
}
public bool GetDIState(int deviceID) //获取DI状态的方法
{
IHomeDevice hd = FindHomeDevice(deviceID);
if (hd == null) return false;
if (hd.DIDevices.Count == 0) return false;
if (Clients.Count == 0) return false; //没有连接的客户端,或者断开了
stringJson json = new stringJson(SmartHomeChannel.SHFLAG);
//SHACTRL = "505"; //给某个SHA系统的设备发指令[appid=N][devid=M][subid=X][type=Y][act=K]
json.AddNameValume("cmd", SHProtocol.DEVSTATE);
json.AddNameValume("appid", smarthomechannel.appid.ToString());
json.AddNameValume("devid", deviceID.ToString());
json.AddNameValume("type", DeviceType.DI.ToString());
try
{
TCPNotifyDevice(json);
Thread.Sleep(50);
}
catch { return false; }
return true;
}
void TCPNotifyDevice(stringJson json) //用TCP通信,通知设备系统
{
byte[] msg = json.GetBytes();
for (int i = 0; i < Clients.Count; i++)
Clients[i].Send(msg);
}
public bool GetDOState(int deviceID) //获取DO状态的方法
{
IHomeDevice hd = FindHomeDevice(deviceID);
if (hd == null) return false;
if (hd.DODevices.Count == 0) return false;
if (Clients.Count == 0) return false; //没有连接的客户端,或者断开了
stringJson json = new stringJson(SmartHomeChannel.SHFLAG);
//SHACTRL = "505"; //给某个SHA系统的设备发指令[appid=N][devid=M][subid=X][type=Y][act=K]
json.AddNameValume("cmd", SHProtocol.DEVSTATE);
json.AddNameValume("appid", smarthomechannel.appid.ToString());
json.AddNameValume("devid", deviceID.ToString());
json.AddNameValume("type", DeviceType.DO.ToString());
try
{
TCPNotifyDevice(json);
Thread.Sleep(50);
}
catch { return false; }
return true;
}
//SHACTRL = "505"; //给某个SHA系统的设备发指令[appid=N][devid=M][subid=X][type=Y][act=K]
public void SendDO(int deviceID, ushort subID, bool On) //通断DO设备:厂家实现!
{
IHomeDevice hd = FindHomeDevice(deviceID);
if (hd == null) return;
if (hd.DODevices.Count == 0) return;
if (Clients.Count == 0) return; //没有连接的客户端,或者断开了
stringJson json = new stringJson(SmartHomeChannel.SHFLAG);
//SHACTRL = "505"; //给某个SHA系统的设备发指令[appid=N][devid=M][subid=X][type=Y][act=K]
json.AddNameValume("cmd", SHProtocol.SHACTRL);
json.AddNameValume("appid", smarthomechannel.appid.ToString());
json.AddNameValume("devid", deviceID.ToString());
json.AddNameValume("subid", subID.ToString());
json.AddNameValume("type", DeviceType.DO.ToString());
json.AddNameValume("act", On ? "1" : "0");
try
{
TCPNotifyDevice(json);
Thread.Sleep(50);
}
catch { }
}
public void SendAO(int deviceID, ushort subID, float[] value)
{
IHomeDevice hd = FindHomeDevice(deviceID);
if (hd == null) return;
if (hd.AODevices.Count == 0) return;
if (Clients.Count == 0) return; //没有连接的客户端,或者断开了
stringJson json = new stringJson(SmartHomeChannel.SHFLAG);
//SHACTRL = "505"; //给某个SHA系统的设备发指令[appid=N][devid=M][subid=X][type=Y][act=K]
json.AddNameValume("cmd", SHProtocol.SHACTRL);
json.AddNameValume("appid", smarthomechannel.appid.ToString());
json.AddNameValume("devid", deviceID.ToString());
json.AddNameValume("subid", subID.ToString());
json.AddNameValume("type", DeviceType.AO.ToString());
string s = "";
for (int i = 0; i < value.Length; i++)
{
s += value[i].ToString();
if (i < value.Length - 1) s += ",";
}
json.AddNameValume("act", s);
try
{
TCPNotifyDevice(json);
Thread.Sleep(50);
}
catch { }
}
public void SendSO(int deviceID, ushort subID, byte[] value)
{
return; //未完成,目前没有该类设备
}
public int GetDeviceCount() //只有一个设备
{
return 1;
}
public IHomeDevice MakeNewHomeDevice() //建立新设备
{
return new HomeDevice();
}
public void GetAllDeviceInfo()
{
return; //空实现;需要手动添加、删除设备
}
}
可以看到有个内部方法InitSmartHome。这是一个很重要的方法,特别是对智能家电设备。必须在这里暴露设备的所有功能。系统第一次运行时,会调用该方法获取设备信息,并保存到文件中。下次再运行时,就直接从文件中读取设备信息,因为可能对设备的某些无关紧要的属性进行了重新设置,如显示的图片名称。如果要恢复出厂时的设备信息,需要从 SHP删除该设备系统,再重新加入SHP。
编译该项目得到驱动程序的连接库文件eaglesmartrefrigerator.dll。拷贝到SHP的运行目录下。
最后,要修改原来的模拟冰箱程序,加入通信功能。添加一个TCP通信类,用下面的方法处理其收到的数据:
private void ProcessCommand(stringJson json) //★★★处理收到的数据
{
byte[] rev = json.GetBytes();
txtInfo.Text += "\r\n收到数据包:" + json.ToString(); //调试使用
string cmd = json.GetValume("cmd");
if (cmd == SHProtocol.LOGIN) // "500"
{
#region 登陆验证
string login = json.GetValume("login");
if (login != null && login == "1") //要求自动登录
SendRegInfo(); //发送链接信息
else if (login == "OK") //成功登陆,检查操作频道权限
{
ReportFoodsInfo();
lbBCState.Text = "登录SHM成功:" + refrigerator.ServerIP;
}
else
{
tcpClient.Close();
txtInfo.Text += "\r\n[登录信息错误]";
btnConnectSHM.Enabled = true;
}
#endregion
}
else if (cmd == SHProtocol.SHACTRL) // 控制设备命令
{
#region
string stype = json.GetValume("type");
if (stype == null) return;
string devid = json.GetValume("devid");
string subid = json.GetValume("subid");
DeviceType type = (DeviceType)Enum.Parse(typeof(DeviceType), stype);
if (type == DeviceType.DO) //发来DO指令
{
#region
string act = json.GetValume("act");
if (act == null || subid == null) return;
if (subid == "0") //开机或待机模式
{
if (refrigerator.TurnON != (act == "1"))
{
refrigerator.TurnON = (act == "1");
ShowTurnOn();
}
}
else if (subid == "1") //正常/省电模式
{
int mode = int.Parse(act);
if (mode<0 || mode >1) return;
if (cbMode.SelectedIndex != mode)
cbMode.SelectedIndex = mode;
}
#endregion
}
else if (type == DeviceType.AO) //发来AO指令:控制温度
{
#region
string act = json.GetValume("act");
if (act == null || subid == null) return;
string[] sf = act.Split(new char[] { ',' });
Decimal f = Decimal.Parse(sf[0]); //只有第一个是有效温度值
int id = int.Parse(subid);
if (id==0)
{
if (nTemp1.Value != f) nTemp1.Value = f; //会自动引发事件
}
else if (id == 1)
{
if (nTemp2.Value != f) nTemp2.Value = f;
}
else if (id == 2)
{
if (nTemp3.Value != f) nTemp3.Value = f;
}
#endregion
}
#endregion
}
else if (cmd == SHProtocol.DEVSTATE) // 获取状态命令
{
#region
string stype = json.GetValume("type");
if (stype == null) return;
string devid = json.GetValume("devid");
string subid = json.GetValume("subid");
DeviceType type = (DeviceType)Enum.Parse(typeof(DeviceType), stype);
if (type == DeviceType.DO) //发来获取DO状态指令
{
NotifySHMDOState();
}
else if (type == DeviceType.DI) //发来获取DI状态指令
{
NotifySHMDIState();
}
else if (type == DeviceType.AO) //发来获取AO状态指令:设定温度
{
NotifySHMSetTemperature();
}
else if (type == DeviceType.AI) //发来获取AI状态指令:实际温度
{
NotifySHMRealTemperature();
}
#endregion
}
}
void NotifySHMDOState()
{
stringJson json = new stringJson(SmartHomeChannel.SHFLAG);
json.AddNameValume("cmd", SHProtocol.DEVSTATE);
json.AddNameValume("appid", AppId.ToString());
json.AddNameValume("devid", "0");
json.AddNameValume("type", DeviceType.DO.ToString());
string tmp = refrigerator.TurnON ? "1" : "0";
tmp += refrigerator.Mode == 0 ? "0" : "1";
json.AddNameValume("value", tmp);
NotifySHM(json);
}
void NotifySHMDIState()
{
stringJson json = new stringJson(SmartHomeChannel.SHFLAG);
json.AddNameValume("cmd", SHProtocol.DEVSTATE);
json.AddNameValume("appid", AppId.ToString());
json.AddNameValume("devid", "0");
json.AddNameValume("type", DeviceType.DI.ToString());
string tmp = refrigerator.DoorOpen ? "1" : "0";
tmp += refrigerator.Alarm ? "1" : "0";
json.AddNameValume("value", tmp);
NotifySHM(json);
}
void NotifySHMRealTemperature()
{
stringJson json = new stringJson(SmartHomeChannel.SHFLAG);
json.AddNameValume("cmd", SHProtocol.DEVSTATE);
json.AddNameValume("appid", AppId.ToString());
json.AddNameValume("devid", "0");
json.AddNameValume("type", DeviceType.AI.ToString());
string tmp = string.Format("{0:f1}", (float)lbTemp1.Tag);
tmp += "," + string.Format("{0:f1}", (float)lbTemp2.Tag);
tmp += "," + string.Format("{0:f1}", (float)lbTemp3.Tag);
json.AddNameValume("value", tmp);
NotifySHM(json);
}
……
void NotifySHM(stringJson json) //通知SHM
{
byte[] send = json.GetBytes(); //通信协议打包在stringJson结构中
if (tcpClient != null && tcpClient.Connected)
{
tcpClient.Send(send); //TCP通知方式,WIFI设备常用的手段
}
……
}
我们看到,不管是在驱动程序还是设备应用程序中,在处理通信数据时,大量使用stringJson结构,并对其进行解析。解析的内容,就是我们指定的通信协议。所以在介绍使用驱动程序前,在下节内容中描述一下通信协议的问题。