@marsleung
2020-03-25T10:48:25.000000Z
字数 13386
阅读 7841
triton
desktop
neptune
wince
本文将介绍,如何通过 Triton WinCE 把您的设备连接到 Neptune工业设备物联云平台,体验快速连接并传输数据的乐趣。
Triton WinCE 适用于您的控制程序或者您的逻辑程序运行于WinCE(x86)之上(最普遍的情况为您的控制器运行操作系统,并且您对操作系统有操作的权限),通过您的程序和Triton交互,实现设备连接和数据传输的功能。
适配开发语言:
- EVC (WinCE6)
- OtoStudio V2.3
- CoDeSys V2.3
交互方式为:
如果您使用的运行平台为Windows或者Linux,请参考以下文档:
STEP#01
访问 https://www.neptune-iiot.net, 点击右上角注册
;
STEP#02
填写注册信息,注册分为三个步骤:
a) 填写账户信息(使用邮件或者手机号码注册);
b) 验证账户信息(使用邮件或者手机号码接收验收激活邮件或者验证码);
以邮箱注册为例子,将收到上述邮件。点击激活邮箱
,跳转到团队创建界面。
c) 创建或者加入一个团队[说明1], 完成注册;
新建一个团队,输入一个您的团队名称,点击创建
;
加入一个团队,通过关键字搜索一个团队的名称,点击申请加入
[说明2]。
说明1:团队
团队的含义是一个域,用来为用户划定一个空间。用户创建团队时,会自动成为该团队的管理员,别的用户可以申请加入您的团队,您也可以在团队中邀请用户加入。
说明2:申请加入
当申请加入一个团队时,需要对方团队的管理员通过您的请求。请在申请加入后,耐心等待通过。
STEP#03 注册完成
注册完成后,页面跳转到登录界面。您可以使用您的账号密码进行登录。
本文档关注使用Triton将设备连接至Neptune,因此平台功能在本文档中不作过多介绍,平台功能的介绍将在其它文档中介绍。
在Neptune账号注册完成后,我们需要在Neptune中“创建一个设备”,这个“设备”将获得一个全平台唯一的序列号,并且和实体设备一一对应起来。
STEP#01
点击左侧菜单栏分组设备列表
,选中菜单设备
,右侧出现设备列表,此时设备列表显示为空。
点击列表上方创建
按钮,出现以下对话框:
STEP#02
为您的设备输入一个名称和描述,其它的栏位将会在平台使用教程中说明。
如果您的设备是固高控制器,序列号
请选择手动输入,输入控制器的UUID即可(UUID=字母U+15位控制器序列号),如果您的设备还没有烧录UUID,您可以: a)与我们的技术人员联系如何烧录UUID 或者 b)序列号选择自动生成;
如果您的设备并非固高的控制器,序列号
请选择自动生成,由Neptune为您的设备分配一个序列号。例如:
STEP#03
点击确认后,Neptune为该设备生成设备序列号
以及团队令牌
。上述信息是设备通过Triton能正确连接到Neptune的重要信息,后续仍可以在平台中查询得到。
STEP#04
再次返回到设备列表
,就可以看到您的设备已经添加到Neptune中了。
Triton WinCE
x86
https://www.neptune-iiot.net/triton-release/triton-wince-386.zip
Triton目前支持以下版本:
Windows(x86/x64)
、WinCE(x86)
、Linux(x86/x64/ARMv5以上/AArch64)
,请按实际情况选择适配版本。
STEP#01
将triton-wince-386.zip
解压到WinCE文件目录\Hard Disk
之下。
triton-wince-386.zip
解压后包含以下文件:
- Triton.exe: 主程序
- options.ini: 配置文件
- Resources:相关的资源目录
当Triton正常运行后,该目录还会自动产生一个Log
文件夹,用于存放日志文件。
STEP#02
在运行Triton前,需修改Triton的配置文件。请使用文本编辑器打开配置文件options.ini
:
#triton client config file
#服务器连接信息
[server]
#设备在Neptune中的<序列号>
uuid=
#Neptune用户的<团队令牌>
token=
#服务器地址
address=www.neptune-iiot.net
#服务器端口
port=9883
#本地网络配置
[network]
#managed=0: 由用户自行配置网络,不使用以下网卡配置;
#managed=1: 用以下网卡配置来配置本地网络;默认使用此项;
managed=1
#设备有两个网口时,建议一个网口用于连接Neptune,另一个网口用于本地开发调试使用;以下网卡1用于上网,网卡2用于本地调试。
#网卡1
#nic0_network_type可选项:dhcp (自动分配) / static(静态设置)
nic0_network_type=dhcp
nic0_ip_address=192.168.1.2
nic0_netmask=255.255.255.0
nic0_gateway=192.168.1.1
nic0_dns=192.168.1.1
#网卡2
#nic1_network_type可选项:dhcp (自动分配) / static(静态设置)
nic1_network_type=static
nic1_ip_address=192.168.0.2
nic1_netmask=255.255.255.0
nic1_gateway=
nic1_dns=
#自启动程序
[start_up]
#请填写程序的绝对路径,如:program0=\Hard Disk\CPAC\GRT.exe
program0=
program1=
program2=
您需要修改[server]
中的uuid
(设备序列号)以及token
(团队令牌),将您创建的设备的序列号及您团队的令牌填写在这里:
#服务器连接信息
[server]
#设备在Neptune中的<序列号>
uuid=6ed92f55a083f1038b005eb28ef0c760
#Neptune用户的<团队令牌>
token=05712fb7cf95547d3e0f2ad076c3db75
#服务器地址
address=www.neptune-iiot.net
#服务器端口
port=9883
修改完成后,保存options.ini
。
STEP#03
双击运行triton.exe
, 右下角任务栏出现triton的程序图标,并且在网络不可用或者配置文件内容填写错误时,显示无法连接图标。
如果网络可用且配置文件内容正确,triton将显示已链接图标。
STEP#04
建议创建Triton的自启动快捷方式,以便控制器重新启动时,Triton能够自动运行起来。
a) 定位到triton程序目录,右击triton.exe, 点击Copy(复制)
b) 移动到\Hard Disk\StartUp
目录,点击Paste Shortcut(粘贴为快捷方式)
c) 完成上述操作后,在控制器重启时,Triton也能够自动启动。
打开Neptune,设备列表,查看列表中的设备是否已经显示在线,在线表示Triton与Neptune已经成功通讯。如果依然显示为离线,请检查:
- Triton所在的计算机网络是否正常;
- options.ini中需要修改的两项内容是否正确填写;
Triton目前提供了以下数据交互的方式:
所有需要传输的数据都需要先在Neptune中先定义,Neptune中称为数据定义
,每一个设备需要维护一份数据定义
。如果数据不事先定义好,在数据传输时,平台会把无定义的数据丢弃。
STEP#01
从设备列表中找到您需要添加数据定义的设置,点击设备名称进入设备表单;
STEP#02
采用key/value
模式(即编号/值)规划好您需要传输的数据,您需要为每个需要传输的变量定义一个在本设备中唯一的编号,建议编写具有阅读性、便于理解的编号;比如我们要传输以下内容,编号规划如下:
- 温度:temp,数字
- 湿度:humidity, 数字
- 照度:illuminance, 数字
- 是否工作:is_running, 布尔
- 工作状态:status, 文本
STEP#03
将上述内容输入到数据定义
表格中;
a) 点击添加
,在对话框中逐项输入第2点中的范例数据[说明1]:
说明1
1. 数据ID由数字和字母组成,不能包含特殊字符: + - * / > < = % . \ & $ # @, 但可以包含 _ (下划线)。
2.数据ID
、名称
以及数据类型
为必填项,其中数据类型可选值为:数字
(不区分整形和浮点型)、字符
、布尔类型
;
b) 如果您的数据存在枚举型
(即用数值代表一个选项,例如:1->男,2->女),可以使用数据转义
,数据转义
会将得到的数据值,直接翻译为转义值
c) 全部添加完成后,效果如下:
STEP#04
每个设备都需要有这样一份数据定义
,首个设备需要按照上述步骤手动创建,创建后可以将其保存为模板
或者导出Excel
方便下次直接引用或者使用Excel文件导入,以减少重复的操作次数;
根据您使用不同的开发语言及平台,集成方式有所不同,请根据您的实际情况选择集成方法。
Triton WinCE Library - CPAC
cpac
https://www.neptune-iiot.net/triton-release/libs/triton-wince-386-cpac.zip
下载解压后,包含以下文件:
- triton_cpac.lib: OtoStudio的外部库,需要将其添加到您的工程中;
- triton_cpac.dll: triton_lib_cpac.lib调用,充当函数入口 ,相当于是头文件的作用;
- triton_lib.dll: 接口函数库,实现数据的收发的具体逻辑;
triton_cpac.lib包含结构体定义
该结构体表示数据从控制器发送至Neptune时,需要以此数据类型进行构造并发送;
TYPE TritonDataSend : (*发送结构体*)
STRUCT
iSize: UDINT; (*发送的变量组数*)
Payload: ARRAY[0..499] OF Payload;
END_STRUCT
END_TYPE
TYPE TritonDataRecv : (*接收结构体*)
STRUCT
iSize: UDINT; (*接收的变量组数*)
Payload: ARRAY[0..31] OF Payload;
END_STRUCT
END_TYPE
TYPE Payload : (*变量结构体*)
STRUCT
szKey: ARRAY[0..63] OF BYTE; (*数据ID,对应Neptune的数据ID*)
szValue: ARRAY[0..63] OF BYTE; (*数据值,对应的数据值*)
END_STRUCT
END_TYPE
TYPE TritonDataSendSampling : (*采样发送结构体*)
STRUCT
iSize: UDINT; (*变量组数*)
szTimeStart: ARRAY[0..63] OF BYTE; (*采样开始时间*)
szInterval: ARRAY[0..63] OF BYTE; (*采样间隔*)
Payload: ARRAY[0..199] OF PayloadSampling;
END_STRUCT
END_TYPE
TYPE PayloadSampling : (*采样变量结构体*)
STRUCT
szKey: ARRAY[0..63] OF BYTE; (*数据ID,对应Neptune的数据ID*)
szValue: ARRAY[0..254] OF BYTE; (*数据值,对应的数据值*)
END_STRUCT
END_TYPE
triton_cpac.dll包含五个接口函数
(*发送数据:普通*)
FUNCTION triton_send : INT
VAR_INPUT
Param: POINTER TO TritonDataSend;
END_VAR
VAR
END_VAR
(*接收数据:普通*)
FUNCTION triton_recv : INT
VAR_INPUT
Param: POINTER TO TritonDataRecv;
END_VAR
VAR
END_VAR
(*发送数据:采样*)
FUNCTION triton_send_sampling : INT
VAR_INPUT
Param: POINTER TO TritonDataSendSampling;
END_VAR
VAR
END_VAR
(*读取Triton运行状态*)
FUNCTION triton_status : INT
VAR_INPUT
END_VAR
VAR
END_VAR
(*读取设备UUID*)
FUNCTION triton_uuid : INT
VAR_INPUT
Param: POINTER TO STRING;
END_VAR
VAR
END_VAR
STEP#01
将动态链接库(.dll)放置到控制器CPAC目录下。
STEP#02
配置RTS3S.CFG文件
配置文件内容如下:
[IODRIVERLIST]
;MODULE0=3SSJA1000P
MODULE0=GT8003S
INITFCT0=IODrvInit
;INITFCT1=IODrvInit
[PLC]
DisableSerialProgramming=Yes
SETTINGS_FROM_CFG=Yes
;USE SYSINTR_TIMING AS TICK=No
CESchedulerSleepTillTick=Yes
Files=\hard disk\CPAC
BootMode=Run
MapPhysical0=Shm
MapPhysical1=Shm1
NoSetMemoryDivision=1
;UseIRQ10ForExternalEventTask=Yes
;CEGetTimeSyncWithTick=Yes
;DataSize=0x3000000
UseIRQ11ForExternalEventTask=Yes
[Googol]
UsePortAddress=0
UseIsaCard=0
IsaBaseAddress=0x300
IsaIrqNumber=9
RetainOffset=0x800
IOOffset=0x0
IOOputOffset=0x100
IODiagnosis=0x1f0
pIOOffset=0x0
pIOOputOffset=0x0
pIODiagnosis=0x0
ideaType=0
IOExtendType=0
[RETAIN]
;TYPE=Task
SIZE=0x300
[External Dlls]
CheckExtRef=Yes
Path0=triton_cpac
[VISUALIZATION]
TargetVisuLoadOnlyCurrentUsedBitmaps=Yes
FONT0=SUNFON.ttf
FONT1=MSYH.ttf
[SJA1000P]
SJAPCIScan=1
SJAInterruptTaskPriority=0
SJAOutputControlMode=0x1a
;SJASendTestMsgAfterInit=1
SJAPCIVendorId=0x11d4
SJAPCISubVendorId=0x0
SJAPCIDeviceId=0x1535
SJAPCISubsystemId=0x0
SJAXTALFrequency=24000000
;TargetVisuWindowBottom=300
;TargetVisuWindowLeft=0
;TargetVisuWindowRight=400
;TargetVisuWindowTop=0
在[External DLLs]
中增加cpac_gagent.lib作为调用项:
[External Dlls]
CheckExtRef=Yes
Path0=triton_cpac
STEP#03
添加triton_cpac.lib
到您的工程中:
STEP#04
编写您的OtoStudio程序,在适当的时机以适当的频率调用发送和接收方法即可。可参考教程中的示例程序。
PROGRAM PLC_PRG
VAR
iSize: INT;(*发送数据的组数*)
sTritonDataSend: TritonDataSend; (*发送的数据的结构体,结构体中发送数据的结构体中的szKey和szValue长度均为64个字节(由于内存长度限制)*)
sTritonDataRecv: TritonDataRecv;(*从Neptune获取的数据*)
aTempKey: ARRAY [0..99] OF string;
aTempNote: ARRAY [0..99] OF string;
strJsonData: ARRAY [0..9] OF JsonDataString;
(*
需要在<数据类型>中定义以下结构体:
TYPE JsonDataString :
STRUCT
szKey:STRING;
szValue:STRING;
END_STRUCT
END_TYPE
*)
loop: WORD := 1000; (* 1000ms给一次实时数据 *)
rt: INT;(*调用接口函数返回值*)
i: INT := 1;
Delay: TON;
irandom: INT;
baction_done: BOOL;
END_VAR
(*从Neptune获取数据*)
rtn:=triton_recv(ADR(sTritonDataRecv));
IF sTritonDataRecv.iSize<>0 THEN
FOR i:=0 TO 9 DO
SysMemCpy(ADR(strJsonData[i].szKey),ADR(sTritonDataRecv.payload[i].szKey),SIZEOF(sTritonDataRecv.payload[i].szKey));
SysMemCpy(ADR(strJsonData[i].szValue),ADR(sTritonDataRecv.payload[i].szValue),SIZEOF(sTritonDataRecv.payload[i].szValue));
END_FOR
END_IF
IF strJsonData[1].szKey='0100' THEN
bstrart_lift:=STRING_TO_BOOL(strJsonData[1].szValue);
baction_done:=TRUE;
END_IF
IF baction_done THEN
SysMemSet(ADR(strJsonData),0,SIZEOF(strJsonData));(*清空数据缓存区*)
baction_done:=FALSE;
END_IF
iSize:=0;
aTempKey[0]:='0100H';
aTempNote[0]:=INT_TO_STRING(irandom);
iSize:=iSize+1;
aTempKey[1]:='0101H';
aTempNote[1]:=REAL_TO_STRING(50);
iSize:=iSize+1;
aTempKey[2]:='0102H';
aTempNote[2]:=REAL_TO_STRING(25);
iSize:=iSize+1;
aTempKey[3]:='0103H';
aTempNote[3]:=REAL_TO_STRING(8);
iSize:=iSize+1;
aTempKey[4]:='0104H';
aTempNote[4]:=REAL_TO_STRING(40);
iSize:=iSize+1;
(*循环发送数据,采用的是计时器,每1S发送一次*)
Delay(in:=TRUE, PT:=WORD_TO_TIME(loop), Q=>, ET=>);
IF Delay.Q THEN
Delay(in:=FALSE);
(*给sTritonDataRecv赋值*)
sTritonDataSend.iSize:=iSize;(*发送数据的组数*)
FOR i:=0 TO iSize DO
SysMemCpy(ADR(sTritonDataSend.payload[i].szKey), ADR(aTempKey[i]), LEN(aTempKey[i]));(*发送的key有string转化成数组的byte(库要求的是byte的数组)*)
SysMemCpy(ADR(sTritonDataSend.payload[i].szValue), ADR(aTempNote[i]), LEN(aTempNote[i]));(*发送的value有string转化成数组的byte(库要求的是byte的数组)*)
END_FOR
rt := triton_send(ADR(sTritonDataSend));
SysMemSet(ADR(sTritonDataSend),0,SIZEOF(sTritonDataSend));(*清空数据缓存区*)
END_IF
Triton动态库包含一个库文件和一个头文件,请根据您使用的开发语言及平台下载对应的动态库:
Triton WinCE Library - EVC
evc
https://www.neptune-iiot.net/triton-release/libs/triton-wince-386-evc.zip
triton_lib_evc.h
内容如下:
#pragma once
#define MAX_LENGTH 64 // 数据key以及value字符串最大长度
#define MAX_SEND_PAIR 500 // 发送至平台的数据组(key+value)组数上限
#define MAX_RECEIVE_PAIR 10 // 从平台接收数据组(key+value)组数上限
#pragma pack(1)
typedef struct _TritonPayload
{
char szKey[MAX_LENGTH]; //数据ID:对应Neptune的数据定义
char szValue[MAX_LENGTH]; //数据值
}TritonPayload;
//数据传输结构
typedef struct _triton_send_data
{
unsigned int iSize; //数据组数
TritonPayload payload[MAX_SEND_PAIR];
_triton_send_data()
{
memset(this, 0, sizeof(*this));
}
} TritonDataSend;
//数据接收结构
typedef struct _triton_receive_data
{
unsigned int iSize; //数据组数
TritonPayload payload[MAX_RECEIVE_PAIR];
_triton_receive_data()
{
memset(this, 0, sizeof(*this));
}
} TritonDataRecv;
#pragma pack()
#ifdef TRITON_LIB_EXPORTS
#define TRITON_API extern "C" short __declspec(dllexport)
#else
#define TRITON_API extern "C" short __declspec(dllimport)
#endif
//发送数据
TRITON_API triton_send(TritonDataSend *pTritonDataSend);
//获取推送命令
TRITON_API triton_recv(TritonDataRecv *pTritonDataRecv);
- 需要发送的数据按结构体
TritonDataSend
构造,传入发送函数triton_send()
中;- 需要接收的数据按结构体
TritonDataRecv
构造,传入接收函数triton_recv()
中;
编写程序,在适当的时机以适当的频率调用发送和接收方法即可。可参考教程中的示例程序。
// triton_lib_test.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "triton_lib_evc.h"
#include <cstdlib>
#include <iostream>
#include <string>
using namespace std;
static char *TimeFormat() //Neptune时间格式
{
SYSTEMTIME st;
GetLocalTime(&st);
char szTime[64] = {0};
sprintf(szTime, "%04d%02d%02d%02d%02d%02d%03d", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
return szTime;
}
static DWORD WINAPI RecvDataFromNeptune(LPVOID pvParams)
{
while (1)
{
TritonDataRecv *sTritonDataRecv = new TritonDataRecv;
memset(TritonDataRecv, 0, sizeof(sTritonDataRecv));
int ret = triton_recv(sTritonDataRecv); //轮询Neptune是否有下发数据
if (ret != 0)
{
cout << TimeFormat().c_str() << "call triton_recv failed, errorcode: " << ret << endl;
}
else
{
if (sTritonDataRecv->size == 0)
{
Sleep(100);
continue;
}
cout << TimeFormat().c_str() << "receive data from Neptune: ";
for (int i = 0; i < sTritonDataRecv->size; i++)
{
cout << sTritonDataRecv->payload[i].szKey << " " << sTritonDataRecv->payload[i].szValue << "\t";
}
cout << endl;
}
delete sTritonDataRecv;
sTritonDataRecv = NULL;
Sleep(100);
}
return 0;
}
int _tmain(int argc, _TCHAR *argv[])
{
HANDLE hRecvData = CreateThread(NULL, 0, RecvDataFromNeptune, NULL, 0, 0); //使用线程检测是否收到Neptune下发数据
srand(GetTickCount());
int i_temp = 10;
while (i_temp-- > 0)
{
cout << endl;
TritonDataSend *sTritonDataSend = new TritonDataSend;
sTritonDataSend->size = 6;
for (int i = 0; i < sTritonDataSend->size; ++i)
{
sprintf(sTritonDataSend->payload[i].szKey, "%d", i + 1);
sprintf(sTritonDataSend->payload[i].szValue, "%.3f", (float)(rand() % 100) * 0.234); //生成一个随机value,用于测试
}
int rtn = triton_send(sTritonDataSend);
if (rtn == 0)
{
cout << TimeFormat().c_str() << "call triton_send success." << endl;
}
else
{
cout << TimeFormat().c_str() << "call triton_send fail." << endl;
return -1;
}
delete sTritonDataSend;
sTritonDataSend = NULL;
Sleep(1000);
}
cout << endl;
cout << "Press 'Enter' key exit..." << endl;
cin.get();
return 0;
}
结果测试将使用Neptune上的开发者面板进行,开发者面板的作用是查看平台数据的交互结果是否正确,以便提高调试效率,并非最终的数据应用方式。
STEP#01
在Neptune中打开需要调试的对象设备,同时请注意设备的状态需要是“在线”,如果设备不在线,请检查:
- Triton是否正确运行;
- 网络连接是否正确;
STEP#02
打开开发者面板
;
STEP#03
选择密令项[说明1] start_data
,并点击按钮推送密令到设备
,点击后Triton会接收到该密令,并将数据打印在开发者面板之上。
说明1
密令项指Neptune和Triton之间的内部沟通指令,用户也可以向设备推送自定义的沟通指令,以实现用户自定义的功能;
STEP#04
观察开发面板的数据输出窗口是否已经打印出您上传的数据,数据格式为json,您上发的数据位于data标签中,其它的键值为Neptune内部使用,用户不必关注。
STEP#05
数据上传测试完成。
STEP#01
打开开发者面板
;
STEP#02
编写向下推送的密令,向下发送的密令只需要符合json格式(即key/value格式)即可,内容没有特定的要求,只需要您的程序能够使用即可。比如,用户期望推送"300秒后关机"到设备上,让其执行关机,那么我们可以编写以下内容:
{
"action":"shutdown",
"delay":"300"
}
STEP#03
将上述密令粘贴到密令输入框入,然后点击推送。消息正确推送时,界面不会有任何的错误提示,如果有出现错误提示,按提示的内容检查即可。
STEP#04
数据推送下去后,按上述说明使用triton_recv()
进行读取,以下为程序输出结果:
这里看到,推送的数据都收到了,但数据里多了一项uuid,这一项是系统默认会带上的,用于标记数据是发给哪一个设备。
{
"action":"shutdown",
"delay":"300",
"uuid":"6ed92f55a083f1038b005eb28ef0c760"
}
此时我们再调用triton_recv()
方法,可以看到,所有数据取完后,Triton回复NODATA
:
STEP#05
数据下发测试完成。