@marsleung
2020-03-25T10:48:25.000000Z
字数 13386
阅读 8188
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=dhcpnic0_ip_address=192.168.1.2nic0_netmask=255.255.255.0nic0_gateway=192.168.1.1nic0_dns=192.168.1.1#网卡2#nic1_network_type可选项:dhcp (自动分配) / static(静态设置)nic1_network_type=staticnic1_ip_address=192.168.0.2nic1_netmask=255.255.255.0nic1_gateway=nic1_dns=#自启动程序[start_up]#请填写程序的绝对路径,如:program0=\Hard Disk\CPAC\GRT.exeprogram0=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 : (*发送结构体*)STRUCTiSize: UDINT; (*发送的变量组数*)Payload: ARRAY[0..499] OF Payload;END_STRUCTEND_TYPETYPE TritonDataRecv : (*接收结构体*)STRUCTiSize: UDINT; (*接收的变量组数*)Payload: ARRAY[0..31] OF Payload;END_STRUCTEND_TYPETYPE Payload : (*变量结构体*)STRUCTszKey: ARRAY[0..63] OF BYTE; (*数据ID,对应Neptune的数据ID*)szValue: ARRAY[0..63] OF BYTE; (*数据值,对应的数据值*)END_STRUCTEND_TYPETYPE TritonDataSendSampling : (*采样发送结构体*)STRUCTiSize: UDINT; (*变量组数*)szTimeStart: ARRAY[0..63] OF BYTE; (*采样开始时间*)szInterval: ARRAY[0..63] OF BYTE; (*采样间隔*)Payload: ARRAY[0..199] OF PayloadSampling;END_STRUCTEND_TYPETYPE PayloadSampling : (*采样变量结构体*)STRUCTszKey: ARRAY[0..63] OF BYTE; (*数据ID,对应Neptune的数据ID*)szValue: ARRAY[0..254] OF BYTE; (*数据值,对应的数据值*)END_STRUCTEND_TYPE
triton_cpac.dll包含五个接口函数
(*发送数据:普通*)FUNCTION triton_send : INTVAR_INPUTParam: POINTER TO TritonDataSend;END_VARVAREND_VAR(*接收数据:普通*)FUNCTION triton_recv : INTVAR_INPUTParam: POINTER TO TritonDataRecv;END_VARVAREND_VAR(*发送数据:采样*)FUNCTION triton_send_sampling : INTVAR_INPUTParam: POINTER TO TritonDataSendSampling;END_VARVAREND_VAR(*读取Triton运行状态*)FUNCTION triton_status : INTVAR_INPUTEND_VARVAREND_VAR(*读取设备UUID*)FUNCTION triton_uuid : INTVAR_INPUTParam: POINTER TO STRING;END_VARVAREND_VAR
STEP#01
将动态链接库(.dll)放置到控制器CPAC目录下。

STEP#02
配置RTS3S.CFG文件

配置文件内容如下:
[IODRIVERLIST];MODULE0=3SSJA1000PMODULE0=GT8003SINITFCT0=IODrvInit;INITFCT1=IODrvInit[PLC]DisableSerialProgramming=YesSETTINGS_FROM_CFG=Yes;USE SYSINTR_TIMING AS TICK=NoCESchedulerSleepTillTick=YesFiles=\hard disk\CPACBootMode=RunMapPhysical0=ShmMapPhysical1=Shm1NoSetMemoryDivision=1;UseIRQ10ForExternalEventTask=Yes;CEGetTimeSyncWithTick=Yes;DataSize=0x3000000UseIRQ11ForExternalEventTask=Yes[Googol]UsePortAddress=0UseIsaCard=0IsaBaseAddress=0x300IsaIrqNumber=9RetainOffset=0x800IOOffset=0x0IOOputOffset=0x100IODiagnosis=0x1f0pIOOffset=0x0pIOOputOffset=0x0pIODiagnosis=0x0ideaType=0IOExtendType=0[RETAIN];TYPE=TaskSIZE=0x300[External Dlls]CheckExtRef=YesPath0=triton_cpac[VISUALIZATION]TargetVisuLoadOnlyCurrentUsedBitmaps=YesFONT0=SUNFON.ttfFONT1=MSYH.ttf[SJA1000P]SJAPCIScan=1SJAInterruptTaskPriority=0SJAOutputControlMode=0x1a;SJASendTestMsgAfterInit=1SJAPCIVendorId=0x11d4SJAPCISubVendorId=0x0SJAPCIDeviceId=0x1535SJAPCISubsystemId=0x0SJAXTALFrequency=24000000;TargetVisuWindowBottom=300;TargetVisuWindowLeft=0;TargetVisuWindowRight=400;TargetVisuWindowTop=0
在[External DLLs]中增加cpac_gagent.lib作为调用项:
[External Dlls]CheckExtRef=YesPath0=triton_cpac
STEP#03
添加triton_cpac.lib到您的工程中:

STEP#04
编写您的OtoStudio程序,在适当的时机以适当的频率调用发送和接收方法即可。可参考教程中的示例程序。
PROGRAM PLC_PRGVARiSize: 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 :STRUCTszKey:STRING;szValue:STRING;END_STRUCTEND_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 THENFOR i:=0 TO 9 DOSysMemCpy(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_FOREND_IFIF strJsonData[1].szKey='0100' THENbstrart_lift:=STRING_TO_BOOL(strJsonData[1].szValue);baction_done:=TRUE;END_IFIF baction_done THENSysMemSet(ADR(strJsonData),0,SIZEOF(strJsonData));(*清空数据缓存区*)baction_done:=FALSE;END_IFiSize:=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 THENDelay(in:=FALSE);(*给sTritonDataRecv赋值*)sTritonDataSend.iSize:=iSize;(*发送数据的组数*)FOR i:=0 TO iSize DOSysMemCpy(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_FORrt := 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
数据下发测试完成。