智能家居通用管理平台(二)-软件架构设计

    智能家居通用管理平台,以后简称平台SHP。
    SHP的任务之一:怎样管理不同厂家设备系统;任务二,不同设备系统中的物理设备如何交互?这两个核心功能如何设计,才能减少管理程序对具体设备的依赖?自然想到了操作系统对硬件设备的管理方式。您设想对了,那就是使用设备驱动程序。通过驱动程序抽象设备的操作、数据和功能。只要按照统一的接口标准来编写设备驱动程序,所有符合该接口标准的设备,都可以被SHP操作。
    那么第二个任务如何设计?不同厂家的设备之间是相互不认识的。目前流行的UPnP协议(通用即插即用),是各种各样的智能设备、无线设备和个人电脑等实现遍布全球的对等网络连接(P2P)的结构。通过网关的地址转换,设备暴露自己的IP地址,从而实现TCP/IP通信交互。但对非智能设备,或者是使用RS232通信的设备,这种方式行不通。所以我们设计通用的监控机制,使用该机制来实现设备间的交互。先来看一个具体的例子,我们希望在冰箱的门半分钟未关时,SHP能及时发邮件到主人邮箱,并开启家里的音乐系统,使用语音提示来告知家人该情况的出现。
   首先我们下载并安装SHP,按说明书的操作配置好系统。运行界面如下:

    前三个设备的驱动,已经安装好了。智能冰箱模拟程序,在其他电脑上运行,清除上次配置后,会通过PNP机制自动连接上SHP。SHP包括两个核心程序,服务器程序SHS和设备监控程序SHM。对每个不同厂家的设备,SHS启动一个监控程序SHM。图中启动了四个监控程序。分别是:邮件发送监控程序,智能家居音乐系统监控程序,智能门锁、智能冰箱监控程序。接下来,我们设计“任务”,即当某些事件发生时,期望SHP来如何操作这些家庭设备,因为只有它知道所有的设备功能。

    下图是我们设计的一个叫“冰箱门打开了”的任务。

     可以按“手动执行”来测试一下结果。任务编号后,设置监控事件,即当检查到冰箱门未关报警信息时,SHP执行“冰箱门打开了”的任务。下图是该监控设置。

    我们在模拟冰箱程序中打开冰箱门,30秒后,SHP收到报警信号,您会在音乐系统的第一个播放器(共有12个播放器)里提到语音提示“冰箱门打开了,谁在家里?去关一下冰箱的门”,再检查您的邮箱,邮件也收到了。简单吧,任务机制的监控模式,实现了不同设备间的交互。
   下图是各子系统之间的通信示意图。

抽象后的软件通信结构图如下:

    这种通信方式,无需电商改变自己设备内部的工作方式,只需编写设备驱动程序即可,增加一些通信接口转换代码。
    SHS知晓设备驱动程序,通知SHM加载设备驱动程序去监控设备,SHS并不直接操控设备系统。设备驱动程序是关键,如何设计?先看一下系统解决方案,见下图。

    其中有个HomeLibrary的项目,定义了设备驱动开发的规范。展开后,在Device目录下看到有个叫ISmartHome.cs的文档,里面定义了设备的抽象接口,包括属性、方法、事件。见下图。

    ISmartHome.cs文档包含多个抽象接口,它们之间的关系见下图。

    说复杂也不复杂,但也不简单。ISmartHome接口定义了一个厂家的设备系统,其中可以包含多种不同的设备IHomeDevice,而IHomeDevice由六类不同的设备组成。目前看来,六类设备能很好的抽象家居设备系统。它们是数值量输入输出设备IDeviceDI, IDeviceDO;模拟量输入输出设备IDeviceAI, IDeviceAO;流输入输出设备IDeviceSI, IDeviceSO。理论上,这六类设备的组合可以描述任意复杂的设备。
    本节就介绍到此。下节介绍设备的通信方式和数据结构。下面是ISmartHome.cs文档的全部内容,免费开放。有兴趣的同行可以转载,但请注明来源。

namespace HomeLibrary.Devices
{
/// <summary>
/// 智能家居接口声明
/// copyright by 吴志辉
/// writted on 2014-04-13
/// </summary>
public interface ISmartHome // 智能家居接口,实现此接口的智能家居程序,必须为SmartHome类,不要改成其他类名!
{
#region 属性
string HomeName { get; set; } //某家智能家居家庭名称:神龙城99-2902
string Password { get; set; }    //访问某智能家居需要的密码
string FileName { get; set; }    //某家智能家居信息存储文件
List<IHomeDevice> HomeDevices { get; set; }  //所有设备列表:最主要属性,管理所有设备
#endregion

void SaveToFile();   //保存所有属性到文件
void ReadFromFile(string filename);     //从文件读取数据
void InitComm(object[] commObjects); //用于初始化通信对象:传递多个通信对象,如串口,Tcpsocket等

int GetDeviceCount();   //返回SH中登记的设备数量
IHomeDevice MakeNewHomeDevice();//建立新设备
void GetAllDeviceInfo();//获取所有设备的方法,空实现,则需要人工登记设备。

//---------以下为数据更新方法,无某类设备的,空实现即可---------------//
#region
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输入状态的方法:厂家必须提供
#endregion
//---------以下为输出控制方法,无某类设备的,空实现即可---------------//
#region
/// <summary>
/// 接通或断开某个子设备的方法
/// </summary>
/// <param name="deviceID">设备号</param>
/// <param name="subID">子设备号</param>
/// <param name="On">on=true表示接通,false表示断开</param>
void SendDO(int deviceID, ushort subID, bool On); //对DO输出设备,必须实现输出控制

/// <summary>
/// 往子设备发送数据,脉冲输出设备,也看作是AO设备
/// </summary>
/// <param name="deviceID">设备号</param>
/// <param name="subID">子设备号</param>
/// <param name="value">发送的参数,一般约定:value=0表示停止工作,value>0:正向工作, value小于0:反方向工作
/// value的意义由具体设备定义,如对调光开关,50表示亮度百分比:半亮。 对步进电机,-20,表示反转20转;允许使用
/// 多个参数,便于满足各类设备工作,如调节脉冲宽度和速度,可能需要多个参数 </param>
void SendAO(int deviceID, ushort subID, float[] value);  //对AO输出设备,必须实现输出控制

/// <summary>
/// 往子设备发送字节流,特定应用软件可视为SO设备:如语音朗读器,短信发送程序
/// </summary>
/// <param name="deviceID">设备号</param>
/// <param name="subID">子设备号</param>
/// <param name="value">发送的字节流,具体意义由设备解释 </param>
void SendSO(int deviceID, ushort subID, byte[] value);  //对SO输出设备,必须实现输出控制
#endregion
void ProcessDeviceStateData(byte[] states);   //厂家实现的通用处理接收数据的方法
}

public enum DeviceType : byte   //智能家居设备类型定义:只有6种设备
{
  Unknow = 0, DO = 1, AO = 2, DI = 3, AI = 4, SO = 5, SI = 6   //数值量输入输出设备,模拟量输入输出设备
}

public enum StreamType : byte //字节流的类型,对流设备SO,SI需要定义
{
  Unknow, TEXT,IMAGE,AUDIO,FILE,STRINGJSON
}


/// <summary>
/// 说明:一种设备,只能包含6类设备中的一类,但该类设备可以包含多个同类子设备
/// 比如一个DO开关设备,可以包含10个子开关,每个子设备可以被独立操作;如果一个
/// 设备在物理上包含了几类设备,如一个传感器设备,包含了二个温度传感器和一个湿
/// 度传感器,则应该被登记为两个设备,但设备编号DeviceID是一样的,方便物理设备
/// 的嵌入式程序编写
/// </summary>
public interface IHomeDevice : IWriteReadInterface//某智能家居的设备定义接口,抽象设备公用属性描述
{
   int DeviceID { get; set; } //同一公司的设备唯一识别码:必须唯一, 一般公同一设备生产不了21亿台,
   //所谓同一设备或系统,是指采用同一“智能家居驱动程序”的设备或系统
   bool Used { get; set; }//设备能否被使用或禁止,被禁用的设备可以不受监控
   // DeviceType DeviceType { get; set; }    //设备类型:只能包含6类设备中的一类 :移到子设备中了:2014-05-21,吴志辉修改
string DeviceName { get; set; }                  //设备名称文本描述,如:调光开关
string PositionDescription { get; set; }        //设备位置信息描述:如客厅
string GetState(DeviceType DeviceType);//某子设备所有数据的文字描述,便于显示

IDeviceDO MakeNewDeviceDO();//建立新子设备
IDeviceDI MakeNewDeviceDI();//建立新子设备
IDeviceAO MakeNewDeviceAO();//建立新子设备
IDeviceAI MakeNewDeviceAI();//建立新子设备
IDeviceSO MakeNewDeviceSO();//建立新子设备
IDeviceSI MakeNewDeviceSI();//建立新子设备

int GetDODeviceCount();//返回DO子设备的数量
int GetDIDeviceCount();//返回DI子设备的数量
int GetAODeviceCount();//返回AO子设备的数量
int GetAIDeviceCount();//返回AI子设备的数量
int GetSODeviceCount();//返回SO子设备的数量
int GetSIDeviceCount();//返回SI子设备的数量

/// <summary>
/// 一个设备,可能包含几种不同类型的子设备列表,子设备编号的预定规则是:
/// 同类子设备的编号都从0开始。如一个开关设备:有10个DO开关子设备,4个
/// DI信号采集子设备,则DO子设备的编号是0--9, DI子设备的编号0-3.但他们
/// 的设备号是一样的。知道子设备编号和其类型,才能定位到具体的子设备!
/// 共有一下六种类型的子设备
/// </summary>
List<IDeviceDO> DODevices { get; set; }   //数值量输出子设备
List<IDeviceDI> DIDevices { get; set; }   //数值量输入子设备
List<IDeviceAO> AODevices { get; set; }   //模拟量输出子设备
List<IDeviceAI> AIDevices { get; set; }   //模拟量输入子设备
List<IDeviceSO> SODevices { get; set; }   //字节流输出子设备
List<IDeviceSI> SIDevices { get; set; }//字节流输入子设备
}
public interface IBaseDevice  //基础设备公共接口
{
DeviceType DeviceType { get; set; }//设备类型:只能包含6类设备中的一类
int ParentID { get; set; } //所属父设备的唯一识别码
ushort Id { get; set; }     //子设备编号:0---65535
int Tag { get; set; }        //用于特定设备的使用,可存储任何内容的“指针”,由厂家决定其存储内容
string UnitName { get; set; }  //数据单位的文本描述,如开关的“ON=true”的意思可以是“开”,“启动”,“运行”等
string FunctionDescription { get; set; }   //子设备功能描述:如客厅顶灯开关 
}

/// <summary>
/// 常闭开关:上电时断开,断电时接通
/// 常开开关:上电时接通,断电时断开
/// 这里“上电”,是指子设备的工作电源,参见PowerState
/// </summary>
public enum DOType : byte //DO开关类型
{
   Open, Close   //常开和常闭开关:
}

/// <summary>
/// DO开关设备的电源工作状态:即控制某子设备通断的工作电源
/// PowerON:子设备接通工作电源(如给继电器上电);PowerOFF:子设备断开工作电源
/// </summary>
public enum PowerState : byte //开关的电源工作状态
{
  PowerON, PowerOFF
}

public class DataChangedEventArgs<T> : EventArgs   //定义数据变化的事件类:包含两个数据:发送者和具体内容(泛型)
{
public DataChangedEventArgs(object sender, T data)
{
  Sender = sender;
Data = data;
}
public Object Sender { get; private set; }
public T Data { get; private set; }
}
public delegate void DigitalDataChanged(DataChangedEventArgs<bool> e);   // 数值量处理方法的代理
public delegate void AnalogDataChanged(DataChangedEventArgs<float> e);   // 模拟量处理方法的代理
public delegate void StreamDataChanged(DataChangedEventArgs<byte[]> e);  // 字节流处理方法的代理

//以下6种设备的定义
#region 数值量输出DO设备接口
public interface IDeviceDO : IBaseDevice, IWriteReadInterface  //设备之一:数值量输出DO设备接口,如各类开关设备
{
#region 添加新的属性接口
DOType DoType { get; set; }//开关类型:长开dtOpen
PowerState PowerState { get; set; }//开关电源状态,DoType和PowerState共同决定DO子设备的接通或断开状态ON
bool ON { get; } //true表示接通,false表示断开,有DoType和PowerState属性共同决定,是个计算字段

string PictureON { get; set; }//接通时的图形文件名
string PictureOFF { get; set; }   //断开时的图形文件名

#endregion

event DigitalDataChanged OnDigitalDataChanged;  //当数据变化时,通知应用程序的事件。

以下5类设备类似
}
#endregion

#region 模拟量输出AO设备接口
public interface IDeviceAO : IBaseDevice, IWriteReadInterface  //设备之二:模拟量输出设备接口,如调光开关,恒温器
{
   float AoValue { get; set; }   //子设备的当前值,厂家负责把AoValue值转换为与计量单位名称相符的值,可能没有用,但保留
  byte DotPlace { get; set; } //小数点位数
  string Picture { get; set; }//显示的图形文件名
  event AnalogDataChanged OnAnalogDataChanged;
}
#endregion

#region 数值量输入DI设备接口
public interface IDeviceDI : IBaseDevice, IWriteReadInterface  //设备之三:数值量输入设备接口,如门磁,红外传感器
{
   bool HasSignal { get; set; }   //子设备是否有信号 HasSignal=true
  string PictureON { get; set; }//有信号时的图形文件名
  string PictureOFF { get; set; }   //无信号时的图形文件名
  event DigitalDataChanged OnDigitalDataChanged;
}
#endregion

#region 模拟量输入AI设备接口
public interface IDeviceAI : IBaseDevice, IWriteReadInterface  //设备之四:模拟量输入设备接口,如压力传感器,温度传感器
{
  float AiValue { get; set; } //子设备的当前值,厂家负责把AiValume值转换为与计量单位名称相符的值
  byte DotPlace { get; set; } //小数点位数
  string Picture { get; set; }//显示的图形文件名
  event AnalogDataChanged OnAnalogDataChanged;
}
#endregion

#region 字节流输出设备接口
public interface IDeviceSO : IBaseDevice, IWriteReadInterface  //设备之5:字节流输出设备接口:如语音朗读器
{
  StreamType StreamType { get; set; }//流类型
  byte[] SoValue { get; set; }   //子设备输出的当前值
  string Picture { get; set; }   //显示的图形文件名
  event StreamDataChanged OnStreamDataChanged;  //当输出数据时,通知应用程序的事件。
}
#endregion

#region 字节流输入设备接口
public interface IDeviceSI : IBaseDevice, IWriteReadInterface  //设备之6:字节流输入设备接口:如摄像头
{
  StreamType StreamType { get; set; }//流类型
  byte[] SiValue { get; set; }   //子设备输入的当前值
  string Picture { get; set; }   //显示的图形文件名
  event StreamDataChanged OnStreamDataChanged;  //当有输入数据时,通知应用程序的事件。
}
#endregion  

public interface IWriteReadInterface   //读写对象的接口,方便对象的文件存储
{
  void WriteToStream(BinaryWriter bw);//把对象写入流中
  void ReadFromStream(BinaryReader br);   //从流中读取数据
}

}



  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值