概述
这一节讲述了如何通过JAVA客户端访问QuarkIoE, 开始先讲述了从通过访问数据连接到QuarkIoE到运程控制设备。 之后探讨了针对新的设备或者其他业务对象如何扩展QuarkIoE范围内的模型。 最后, 这一节还描述了为了控制设备产生的诊断消息的等级如何配置登陆服务。
连接QuarkIoE
从JAVA连接到QuarkIoE的根接口被称为 "Platform" (参见 "根接口" 在参考指南的REST 实现 的章节)。它也提供了访问"platform"的其他接口, 例如设备清单。 它可以以最简单的形式实例化如下:
Platform platform = new PlatformImpl("<<URL>>",
new QuarkioeCredentials("<<user>>", "<<password>>"));
举例:
Platform platform = new PlatformImpl("https://demos.quarkioe.com", new QuarkioeCredentials("myuser", "mypassword"));
如果你用JAVA客户端开发应用, 你需要先注册一个"application key"。 (通过在QuarkIoE系统管理应用里 "我的应用" , 或者通过 应用 API)。
new QuarkioeCredentials("<<tenant>>", "<<user>>", "<<password>>", "<<application key>>")
如果用于测试, 每个租户都订阅了用于演示的 "application key" "uL27no8nhvLlYmW1JIK1CA=="。 PlatformImpl的constructor也允许你指定回复参数"pageSize"的从服务器返回的object默认数量。
访问设备列表
下面的代码片段展示了JAVA如何获取一个访问设备列表的handle:
InventoryApi inventory = platform.getInventoryApi();
通过这个handle,你可以创建、检索和更新托管的对象。 例如,如果你想检索包含地理位置的所有对象, 可以用下面的代码:
InventoryFilter inventoryFilter = new InventoryFilter();
inventoryFilter.byFragmentType(Position.class);
ManagedObjectCollection moc = inventory.getManagedObjectsByFilter(inventoryFilter);
这将返回一个获取所有对象的请求 -- 并没有实际获取他们。在实际操作中, 这样一个对象列表可能是非常巨大的。因此,从服务器上是按"页"返回的。反复调用,得到所有的"页",可以用下面的代码:
for (ManagedObjectRepresentation mo : moc.get().allPages()) {
System.out.println(mo.getName());
}
创一个新的托管对象, 简单创建一个对象的本地描述并且发送到平台上。 下面的代码片段展示了如何创建一个新的带继电器的电表:
ManagedObjectRepresentation mo = new ManagedObjectRepresentation();
mo.setName("MyMeter-1");
Relay relay = new Relay();
mo.set(relay);
SinglePhaseElectricitySensor meter = new SinglePhaseElectricitySensor();
mo.set(meter);
// Set additional properties, e.g., tariff tables, ...
mo = inventory.create(mo);
System.out.println(mo.getId());
调用"create"返回的结果是某一版本的带一个唯一标识符的托管对象。
现在假设你准备存储额外的、和设备相关的自己的属性。这可以通过以java bean的形式创建一个新的"fragment" 来简单地实现。例如,假设你准备存储价目表以及表具。价目表分为白天和晚上,我们需要保存按夜间计费的时长:
public class Tariff {
public int getNightTariffStart() {
return nightTariffStart;
}
public void setNightTariffStart(int nightTariffStart) {
this.nightTariffStart = nightTariffStart;
}
public int getNightTariffEnd() {
return nightTariffEnd;
}
public void setNightTariffEnd(int nightTariffEnd) {
this.nightTariffEnd = nightTariffEnd;
}
private int nightTariffStart = 22;
private int nightTariffEnd = 6;
}
现在你可以简单添加计费信息在你的表具上:
Tariff tariff = new Tariff();
mo.set(tariff);
这将保存与表具相关的计费信息。 为了JAVA对象在JSON、REST之间转换,QuarkIoE使用了Svenson。 Svenson 文档 提供了更多关于如何让各自产生的JSON格式被接受的信息。
当创建自己的OSGi片段时,你需要让片段对QuarkIoE的客户端库是可见的。要做到这一点,将下列行加到bundle的包含这些片段的manifest文件中:
Eclipse-RegisterBuddy: com.nsn.cumulocity.model.core-model
在SDK中,最好保持你的域模型在一个单独的项目中。这样的话,你可以在agent和你的应用中共享域模型。
访问身份服务
一个设备通常都有一个技术上的标识符,agent需要知道用以连接设备。例如表具的编号、IP地址和REST URL。为了将这些标识符和将QuarkIoE的标识符关联起来,agent可以使用身份服务。为了创建关联, 需要创建一个类型为 "ExternalIDRepresentation"的对象,然后发回到平台。下面的代码片段展示了如何注册一个和REST URL关联的设备。从上面的例子中,假设"mo"是托管对象, "deviceUrl"是设备关联的REST URL字符串。
final String ASSET_TYPE = "com_cumulocity_idtype_AssetTag";
final String deviceUrl = "SAMPLE-A-239239232";
ExternalIDRepresentation externalIDGid = new ExternalIDRepresentation();
externalIDGid.setType(ASSET_TYPE);
externalIDGid.setExternalId(deviceUrl);
externalIDGid.setManagedObject(mo);
IdentityApi identityApi= platform.getIdentityApi();
identityApi.create(externalIDGid);
现在,如果你想要取回这个关联信息,你可以象下面查询身份服务:
ID id = new ID();
id.setType(ASSET_TYPE);
id.setValue(deviceUrl);
externalIDGid = identityApi.getExternalId(id);
返回的对象包含唯一的标识符和一个指向托管对象的链接。
访问事件和数据
访问事件和数据方式和上面讲的设备列表的方式很类似。下面的例子查询在过去的两周里设备的无线连接的信号强度,然后打印设备ID、数据采集的时间、收到的信号强度和错误比特率
MeasurementApi measurementApi = platform.getMeasurementApi();
MeasurementFilter measurementFilter = new MeasurementFilter();
Calendar cal = Calendar.getInstance();
Date toDate = cal.getTime();
cal.add(Calendar.DATE, -14);
Date fromDate = cal.getTime();
measurementFilter.byDate(fromDate, toDate);
measurementFilter.byFragmentType(SignalStrength.class);
MeasurementCollection mc = measurementApi.getMeasurementsByFilter(measurementFilter);
MeasurementCollectionRepresentation measurements = mc.get();
for (; measurements != null; measurements = mc.getNextPage(measurements)) {
for (MeasurementRepresentation measurement : measurements.getMeasurements()) {
SignalStrength signal = measurement.get(SignalStrength.class);
System.out.println(measurement.getSource().getId() + " " + measurement.getTime() + " " + signal.getRssiValue() + " " + signal.getBerValue());
}
}
控制设备
最后, "DeviceControlResource"允许你远程操控设备。包含两个方面:你可以在应用中创建发往设备的操作, 也可以查询从agent来的操作。
为了控制设备,设备必须在托管对象agent的"childDevices" 分层中。agent托管对象代表了在设备列表中你的agent。它可以通过一个片段"com_cumulocity_model_Agent"来识别。 这是QuarkIoE如何识别将操作发到何处来控制一个特定的设备。下面的代码演示了如何建立:
ManagedObjectRepresentation agent = new ManagedObjectRepresentation();
agent.set(new com.cumulocity.model.Agent()); // agents must include this fragment
// ... create agent in inventory
ManagedObjectRepresentation device = ...;
// ... create device in inventory
ManagedObjectReferenceRepresentation child2Ref = new ManagedObjectReferenceRepresentation();
child2Ref.setManagedObject(device);
inventory.getManagedObject(agent.getId()). addChildDevice(child2Ref);
例如, 假设你在应用中想要关闭一个表具的继电器。 和前面的例子类似,, 你需要创建一个本地执行的操作,然后发送给平台:
DeviceControlApi control = platform.getDeviceControlApi();
OperationRepresentation operation = new OperationRepresentation();
operation.setDeviceId(mo.getId());
relay.setRelayState(RelayState.OPEN);
operation.set(relay);
control.create(operation);
现在,你想从agent查询挂起的操作, 下面的代码需要被执行:
OperationFilter operationFilter = new OperationFilter();
operationFilter.byAgent(mo.getId().getValue());
operationFilter.byStatus(OperationStatus.PENDING);
OperationCollection oc = control.getOperationsByFilter(operationFilter);
而且,返回的结果根据它内容大小,可能会分成几页。
OperationCollectionRepresentation opCollectionRepresentation;
for (opCollectionRepresentation = oc.get(); opCollectionRepresentation != null; opCollectionRepresentation = oc.getNextPage(opCollectionRepresentation)) {
for (OperationRepresentation op : opCollectionRepresentation.getOperations()) {
System.out.println(op.getStatus());
}
}
实时特性
JAVA客户端库完全支持QuarkIoE的实时API。 例如,为了有人发送操作给agent立即得到通知,使用下面的代码:
Subscriber<GId, OperationRepresentation> subscriber = deviceControl.getNotificationsSubscriber();
subscriber.subscribe(agentId, new SubscriptionListener<GId, OperationRepresentation> {
public void onError(Subscription<GId> sub, Throwable e) {
logger.error("OperationDispatcher error!", e);
}
public void onNotification(Subscription<GId> sub, OperationRepresentation operation) {
// Execute the operation
}
});
"agentId" 是你设备列表的agentID。
可靠性
特别是在移动设备上,Internet连接可能不可靠。 为了支持这样的环境, JAVA客户端库支持本地缓存。这就是说你可以把数据传送给客户端库,不用管internet连接是否可用。连接可用,数据立即发送。否则,数据将被缓存直到连接可用。 API的"async"变量需要设置。例如发送告警,可以用:
AlarmApi alarmApi = platform.getAlarmApi();
Future future = alarmApi.createAsync(anAlarm);
"createAsync"方法立即返回。 无论什么时候被真正地执行,"Future"对象可以被用来确定请求的结果。
日志配置
在JAVA客户端SDK里登陆是通过带有logback 后台的slf4j 实现的。如何使用和配置登陆的详细描述,参见logback文档。
0.11开始的版本, 对所有的组件,默认的SDK日志等级设为"Error" ,意思是除非达到"Error",日志消息是被禁止发表的。 如果一起运行顺利,应该没有SDK产生的日志消息。 默认情况下,日志消息只发送到控制台。
提供一个新的日志配置文件可以修改默认的日志配置。 在这讨论两种方法:使用系统属性,通过文件的绝对路径; 通过一个OSGi片段。 需要注意这两个方法都会覆盖 默认的行为,而不是扩展。
通过系统属性配置
日志配置文件的绝对路径使用下面的系统属性来设置:
-Dlogback.configurationFile=/path/to/config.xml
使用Eclipse,这个属性添加在Run Configuration 窗口-> Arguments->VM Arguments 。
通过OSGi片段Bundle配置
更灵活的方式是提供一个可以和客户端一起部署的OSGi bundle来设置日志配置。Eclipse内部,这个片段可以象下面这样产生:
创建一个新的类型为 "Fragment Project" 的工程,命名为"LogConfig"
- Target to run with: an OSGi framework
- Host plugin ID: ch.qos.logback.classic
-
在新工程的根目录下添加一个名为 logback.xml 的文件。 这个文件包含日志配置的信息。
-
编辑MANIFEST.MF文件,象下面这样添加:
- 在 Build 页面添加 logback.xml 文件到 Binary Build
-
在Java客户端工程的 Run Configuration 对话框内 Bundles页面:
- 选择 LogConfig bundle
- 取消选择 com.nsn.cumulocity.platform-services.sdk.logging-config bundle
简单的日志设置
下面的例子演示了如何开启对单一组件的 debug-level 日志, 名为 "com.cumulocity.javaclient", 同时其他组件保持error-level日志。 下面的代码片段展示了如何创建日志,然后发送消息到日志里:
Logger logger = LoggerFactory.getLogger("com.cumulocity.javaclient");logger.debug("A debug message");
配置文件如下所示:
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder><pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern></encoder>
</appender>
<logger name="com.cumulocity.javaclient" level="debug"/>
<root level="error"><appender-ref ref="STDOUT" /></root>
</configuration>
代码运行后, 控制台会包含下面这样的消息:
21:52:02.790 [Start Level Event Dispatcher] DEBUG com.cumulocity.javaclient - A debug message