传感器 - 本机(动手实验)
Windows 7 传感器 & 定位平台
Windows 7传感器 & 定位平台可以使你的应用程序更加适应当前的环境,并且能够改变它们的外观,体验或者行为。这有一些例子:
• 阳光明媚的时候,你在户外使用一个移动PC(比如,笔记本电脑或者平板电脑),那么应用程序可能会增加亮度,对比度来增加屏幕的易读性。
• 应用程序可能会提供一些特定的信息,比如附近的餐馆。
• 应用程序可能会像游戏控制器一样使用3D加速计和按钮。
• 应用程序也可能会使用人体传感器来改变Messenger的登录状态。
图例 1
可调MSDN阅读器,使用了环境光线感应器来调整对比度,尺寸和颜色饱和度
传感器&定位平台,对所有的解决方案都有很多优点:
• 硬件-独立:不再需要为一个独立的提供商的API进行学习和投资;所有的传感器类型控制起来都非常相似。
• 隐私:Microsoft意识到,传感器和本地数据都是私人的,个人的验证信息,所有的传感器在默认情况下都是关闭的。你可以通过控制面板随时打开/关闭传感器。应用程序将会提示你一个安全的允许的UI用户界面,来打开指定的传感器。
• 应用程序共享:多个应用程序可以同时使用同一个传感器的数据。
• 定位简单化:定位API能够使你不需要关心获取信息的特殊结构,就能得到所需要的位置。比如。GPS,发射塔或者WIFI热点。定位API将自动选择可用的最正确的传感器数据。另外,你也不需要去实现类似于NMEA的GPS协议了。
目的
在本次动手实验中,你将了解到如何在你的应用程序中,整合Windows 7的传感器支持,包括:
• 检测已连接的传感器
• 检测传感器状态
• 如果所连接的传感器没有开启,那么向用户提出一个许可
• 处理传感器的数据事件
系统需求
若完成此实验,你必须需要以下组件:
• Microsoft Visual Studio 2008 SP1
• Windows 7 Windows 7 SDK
• 安装了驱动的Windows 7兼容环境光线传感器硬件,或者虚拟环境光线传感器
练习: 根据环境光线的强烈程度调整字体
在本次练习中,你需要修改一个Win32 MFC应用程序,以便于可以根据环境光线感应器所感应到得不同的光线强度级别,来调整相应的字体。基本上大部分的应用程序用户界面都已经实现了;你只需要在缺少的部分与Windows 7 的传感器API进行交互。那么最终结果类似于下面的图片:
开始本次练习之前,请在Visual Studio中打开AmbientLightAware解决方案(在HOL的根目录中)。
这是一个简单而标准的MFC应用程序。花一两分钟去浏览一下包含示例的应用程序的C++文件(.cpp/.h)。为了方便你,请确认任务列表工具窗口是可见的(在View菜单中,选择任务列表),并且在工具窗口复选框选择注释–你将看到这个练习中的每一个任务中的TODO事项。运行这个应用程序,使你对它有所熟悉。
注意
以简短和简单为目的,示例应用程序没有展示最好的MFC UI用户界面,也没有为面向对象开发和COM开发展现出最好的设计标准。
任务 1 —添加环境光线感知
在这个任务中,你需要添加一些支持的类,并且修改你的应用程序,使其能够根据一个或多个环境光线传感器感应到的平均光线明视度,来调整字体。
1. 右键点击AmbientLightAware项目,然后点击属性
a. 转向Linker --> Input --> Additional dependencies.
b. 向列表中添加 sensorsapi.lib 。
c. 点击OK关闭对话框。
2. 找到StdAfx.h文件,然后将下面的代码取消注释:
C++
#include <Sensors.h>
#include <SensorAPI.h>
3. 右键点击AmbientLightAware项目,然后点击Add --> Existing Item...
a. 如果你不在项目目录中,那么就导航到项目目录中。
b. 选择下面的文件(按住Ctrl选择):
• AmbientLightAwareSensorManagerEvents.cpp
• AmbientLightAwareSensorManagerEvents.h
• AmbientLightAwareSensorEvents.cpp
• AmbientLightAwareSensorEvents.h
c. 点击Add 按钮。
传感器API是由IsensorManager对象构成,IsensorManager对象可以用来对现有的传感器根据它们的类型,类别或者ID进行查询。你也可以向其他的用户发送请求,来要求使用它们的传感器。除此之外,你还可以当新的传感器变为可用时,通知自己。
CambientLightAwareSensorManagerEvents类提供了两个功能:
• 它包含了与传感器管理器间的交互代码
• It also implements the OnSensorEnter (new sensor becomes available) event它还实现了OnSensorEnter(当新的传感器变为可用)事件。
从传感器管理器中,你可以获取到Isensor对象,它用来表示独立的传感器。你可以查询一个传感器的状态,并且获取或者设置其他的属性,比如制造厂商,型号,精度和更新周期。你也可以请求一个包含传感器所感应到的结果度量的数据报表。你同样可以注册一些事件(当传感器变为不可用,状态更改,有新的数据报表,一般事件)。
CambientLightAwareSensorEvents类提供了两个功能:
• 它包含了与环境光线传感器进行交互的代码
• 它同样实现了上述的传感器事件
4. 导航到AmbientLightAwareSensorManagerEvents.cpp文件的顶部。
5. 检查构造函数,AddRef, Release 和 QueryInterface方法。它们仅仅是标准的COM样本代码。
6. 将Initialize()方法取消注释。
a. 这段代码创建了一个传感器管理器对象(等效于CoCreateInstance)。
b. SetEventSink的调用,是为我们订阅OnSensorEnter事件(实现这个事件的类)。
c. 通过调用GetSensorsByType,我们可以枚举出所有类型为SENSOR_TYPE_AMBIENT_LIGHT的传感器。每一个传感器都拥有三个GUID:类别,类型和ID。类别用来表示这个传感器是用来衡量什么的(例如,环境),类型是用来表示传感器是如何进行衡量的(比如,温度,湿度)。ID则是传感器的唯一标识。ID可能是永久的(如果在传感器上的序列号是“烧录”上的),或者是非永久的。
d. SENSOR_TYPE_AMBIENT_LIGHT定义了一个GUID地图。在sensors.h中,有很多预先定义好的传感器类型。你可以在SENSOR_TYPE_AMBIENT_LIGHT定义上右键点击,然后选择Go To Definition去查看这些类型。
e. 我们通过调用ISensorManager::RequestPermissions()来请求使用环境光线传感器,特别是父HWND,传感器集合,还有是否需要在用户选择该如何去做之前,阻止该方法。这些操作会显示下面的对话框:
如果用户拒绝开启一个传感器,那么后续的调用RequestPermissions的方法将不会再次显示这个UI用户界面。
f. GetSensorsByType将返回一个IsensorCollection对象,这个对象中的元素数量由GetCount()决定,并且可以使用GetAt()来访问。
g. 每个传感器,我们都需要调用AddSensor方法,这个方法我们后面将会实现。
h. 由于我们希望强制生成最新的数据报告,所以我们需要调用传感器事件类中的GetSensorsData()方法。否则,传感器在亮度变化不是很大的情况下,不会产生数据报告。
7. 将AddSensor()方法取消注释。
a. Isensor中的SetEventSink方法,将为我们注册CambientLightAwareSensorEvents对象,这个对象是我们在这个指定的传感器事件(状态变更,传感器已分离/不可用,或者新的数据报告)的构造函数中创建的
b. 传感器对象添加到m_Sensors CatlMap 。
8. 将两个RemoveSensor()方法取消注释。
a. 将包含Isensor*的方法,取消注释。
当应用程序需要退订指定传感器时,需要调用这个方法。当应用程序关闭的时候,将会调用这个方法。
b. 将包含REFSENSOR_ID的方法取消注释。
当传感器被强制移除时(比如,当硬件断开连接时),传感器事件类(CAmbientLightAwareSensorEvents)将调用这个方法。这个方法能得到一个GUID而不是一个ISensor*,因为当传感器被移除后,将不能继续访问。
9. 将UnInitiliaze()方法取消注释。这个方法将取消事先已经订阅到传感器的所有处理的事件。它同样也会取消订阅传感器管理器事件。这个方法将会在关闭的时候被调用。
10. 将OnSensorEnter()方法取消注释。
a. 这个方法将实现IsensorManagerEvents。它将在新传感器变为可用时被调用(比如,物理连接)
b. 当Initialize()管理一些已经在应用程序启动时就可用的传感器时,这个方法将会管理传感器,使其稍后才变为可用。
c. 这个方法还会请求传感器获取其状态,以便于判断当前它是否已经可用,并且请求一个数据报告。
11. 导航到 AmbientLightAwareSensorEvents.cpp 文件的顶部。
12. 检查构造函数,AddRef, Release 和 QueryInterface方法。他们仅仅是标准COM样本代码。
13. 检查m_mapLux。它是一个(浮点型SENSOR_ID)的CatlMap。这个地图将跟踪每一个传感器的最新的环境光线值的记录(Lux单元)。这其中的每个传感器的每个值,都是独立保存的,所以他们才能在后期进行平均计算。
14. 将OnLeave()方法取消注释。这个方法是实现IsensorEvents和当传感器变为不可用时调用的(断开连接)。
a. 这个方法将调用我们之前已经实现了的方法CAmbientLightAwareSensorManagerEvents::RemoveSensor()。
b. 这个方法将删除被移除的传感器的最新存储的环境光线值,那么这些被删除的值在后面进行平均计算时,就不会被计算在内。UpdateLux()方法是用来反映计算出的光线亮度平均值和更新UI用户界面的。
15. 将OnStateChanged()方法取消注释。这个方法实现了IsensorEvents,并且当一个传感器的状态发生变化时,将被调用。当传感器已经准备就绪,那么会提供一个数据报告,并且UI用户界面也会被更新。
a. 即使传感器是连接着的,它也可能不能使用(SENSOR_STATE_READY)。这个状态将告诉你为什么不能使用的原因。
b. 另外一个重要的状态是SENSOR_STATE_ACCESS_DENIED,它表示能够使用当前传感器的权限,还没有在控制面板中进行设置。当你设置了权限后,这儿状态将更改。你也可以调用ISensorManager::RequestPermissions来显示一个权限对话框。
c. 你可以调用ISensor::GetState()方法,来查询所有的状态。
16. 将OnDataUpdated()方法取消注释。这个方法将实现IsensorEvents,并且当传感器有新的可用的数据报告时将被调用。一些传感器将定期进行更新,其他的当数据的值发生了很大的变化才会进行更新。在一些传感器中,你可以通过设置SetProperties()中的SENSOR_PROPERTY_CHANGE_SENSITIVITY属性,来控制传感器的敏感度。
17. 将OnEvent()取消注释。这个方法实现了IsensorEvents,并且当其它事件,包括OnLeave(), OnStateChanged() 和 OnData()激发时会被调用。
a. 你可以通过事件的GUID对事件进行区分。
b. 这个事件存在的意义,就在于它能允许传感器自定义用户事件(例如,GPS传感器可能需要有卫星发现/丢失事件)。在这里,我们没有自定义事件,所以这个方法总是会返回S_OK。
18. 将UpdateLux()方法取消注释。这个方法将重复访问m_mapLux 地图和计算平均值。它会调用CambientLightAwareDlg类中的一个同名方法。
19. 将UpdateData(ISensor* pSensor)方法取消注释。注意这里还有一个同样名称,只是重载参数不同的方法。
a. 无论何时我们需要手动请求一个新的数据报告时,这个方法都会贯穿整个代码。
b. 这个方法调用ISensor::GetData()生成一个IdataReport对象。这个对象将被当做第二个参数,在UpdateData调用时重载。它还将保存代码的副本。
20. 将GetSensorData(ISensor *pSensor, ISensorDataReport *pDataReport)方法取消注释。这个方法将实现IsensorEvents,并且在当传感器有新的可用的数据时被调用。
a. 传感器的数据报告由一个或多个数据(区域)构成。它们可以是由多个类型的PROPVARIANT来支持。
b. 你可以右键点击SENSOR_DATA_TYPE_LIGHT_LEVEL_LUX,然后选择转到定义,去查看预定的属性。这些属性在sensors.h文件,按照传感器类别组合在一起。紧邻每一个属性的注释,是其指定的数据类型。
c. 你可以通过调用GetSensorValues()方法,来决定在数据报告中需要显示那些数据,这个方法将返回一个键值的集合。
d. 如果传感器支持调用ISensor::SupportsDataField()来给出数据,那么你也可以来决定其优先级。
e. 使用PropVariantInit 和 PropVariantClear 方法来初始化成PROPVARIANT。
f. 这个方法将更新m_mapLux集合中的传感器的亮度值的集合,然后调用UpdateLux()重新计算,并且更新UI用户界面。
21. 导航到AmbientLightAwareDlg.h文件的顶部。将CleanUp() 和 UpdateLux()方法的声明取消注释。
22. 导航到文件的底部。将m_pSensorManagerEvents 和 m_lfLogFont的类成员取消注释。
23. 导航到 AmbientLightAwareDlg.cpp文件的顶部。将下面的 #include 语句取消注释:
C++
#include "AmbientLightAwareSensorEvents.h"
#include "AmbientLightAwareSensorManagerEvents.h"
24. 导航到 AmbientLightAwareDlg 构造函数。将初始化的m_pSensorManagerEvents 和 m_lfLogFont取消注释。
25. 取消 CleanUp()方法的注释。这个方法将在关闭的时候被调用。
26. 将InitAmbientLightAware()方法取消注释。这个方法将在启动时被调用,来初始化相关的传感器。
27. 导航到 OnInitDialog(),将调用InitAmbientLightAware()的方法取消注释。
28. 将 UpdateLux()方法取消注释。这个方法决定了在当前的亮度下所显示的字体。它将更新UI用户界面的成员(传感器的平均亮度)和UI用户界面。一个适当的应用程序将可以实现低通过滤器来避开可能会引起用户烦躁的频繁的字体更新。
29. 导航到 OnPaint(),将负责改变字体的IDC_STATIC_SAMPLE静态控制代码取消注释。
30. 导航到 AmbientLightAware.cpp文件的顶部。将下面的 #include 代码取消注释:
(C++)
#include "AmbientLightAwareSensorEvents.h"
#include "AmbientLightAwareSensorManagerEvents.h"
31. 导航到 InitInstance()。将调用 CoInitializeEx(), CoUninitialize() 和 dlg.CleanUp()方法的代码取消注释。
32. 编译并运行应用程序。改变亮度值,然后查看文本的变大和缩小。
摘要
在这个实验中国,你已经体会到了使用Windows 7 传感器API,向一个现有的应用程序中整合传感器的支持。
积累一个成熟的支持Windows 7的应用程序,可能不仅仅需要本次试验中所做的工作,但是,现在你已经可以有充足的准备去自己解决了。