机械境

这里只是我的后花园,随性而写

问题

SourceTree 真是一个让人又爱又恨的产品,小问题不断,但是在免费的 Git 的 GUI 里面还算优秀的。因为 TortoiseGit 对 msys2 支持不好,我又回到 SourceTree 阵营了,SourceTree 为主, CLI 辅助。 因为 SourceTree 不能识别 msys2 的 Git,虽然其自带一个内嵌的 Git,这样就导致了我需要配置两份全局的 gitconfig。

解决方案

其实原因很简单,因为 SourceTree 要求 Git 所在目录必须同时有 bin 和 cmd 文件夹。听起来很 2 是吧?知道了原因要解决起来就很简单。

  1. 我的 msys2 安装在 d:\msys64,在 SourceTree 中选择 系统安装的 Git (工具 / 选项 / Git)

    在 AppData 中的 user.config 配置中 GitSystemPath 节点会自动识别出 msys2 的路径

    1
    2
    3
    <setting name="GitSystemPath" serializeAs="String">
    <value>D:\msys64\usr</value>
    </setting>
  2. 通过 mklink 建立一个链接

    D:\msys64\usr 建立链接
    mklink /D cmd bin

—EOF—

介绍

eDNA 是一个领导性的实时 / 历史数据系统。eDNA 采集、存储和展示大量的工程和运行信息。eDNA 将深入在整个企业范围内的数据采集上来,以极高的无损压缩方式存储起来,使得以时间序列频繁变化的数据能以原有的数据精度和时间精度在线保存多达几十年。eDNA 使基于运行状况的及时和准确的决策成为可能,极大地降低运行成本。eDNA 具有完全分布的体系结构,让正确的人在正确的时间做出正确的决定。eDNA 是一套实时的运行管理解决方案,它提供了对你的生产运行中无限制的观察和分析,使得你能够根据丰富的信息迅速地做出决策,你的一线生产能力将会大大提高。via 印步

API

eDNA 分为两种:

  • 常规 API:支持 C/C++/Visual Basic 等。这种方式需要自己组装报文,虽然有相关 API 函数支持,但是实际应用起来还是比较麻烦
  • EzDNA:封装了常用的操作,只需要引入一个头文件 (EzDnaApi.h) 即可。

注:两种方式都需要安装 eDNA 的 Client 程序。

示例代码

涉及到的 API 函数:

  • DNAGoodPointFormat 检查测点名称是否符合命名规范
  • DoesIdExist 检查测点是否存在
  • DNAGetRTFull 查询指定测点的实时数据
  • DNAGetHSFull 同上

注: DNAGetHSFull 查询到的时间精度是毫秒,如果对时间有要求,建议使用此函数。但是在实际使用过程中,发现此函数有时候会查询不到数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
int16_t getsnapshot()
{
edna_tag_t *tag;
int32_t index;
int32_t *buff_size;
int16_t counter;
int32_t ret;
int8_t log_tag[MAX_BUFF_SIZE];
int8_t szValue[EDNA_LEN];
int32_t ptTime;

int8_t szmS[EDNA_LEN];
uint16_t pusStatus;
int8_t szStatus[EDNA_LEN];
int8_t szDesc[EDNA_DESC_LEN];
int8_t szUnits[EDNA_LEN];

strncpy(log_tag, "getsnapshot", MAX_BUFF_SIZE);
// init parameters
// ....

for (index = 0; index < ptbl_data->point_size; index++, tag++)
{
// other stuff
// ...

if (tag->flag == E_EMPTY_TAG) continue;

// tests the string, validating it as a fully qualified eDNA point name
if (DNAGoodPointFormat(tag->name) == 0)
{
tag->flag = INVALID;
continue;
}

// check point name is exist
if (!DoesIdExist(tag->name))
{
tag->flag = NOT_EXIST;
continue;
}

// retrieves the value, time and status in their raw formats
ret = DNAGetRTFull(tag->name, &tag->value, szValue, EDNA_LEN, &ptTime, tag->ts,
EDNA_LEN, &pusStatus, szStatus, EDNA_LEN, szDesc,
EDNA_DESC_LEN, szUnits, EDNA_LEN);

#if 0

ret = DNAGetHSFull(tag->name, &tag->value, szValue, EDNA_LEN, &ptTime, tag->ts,
EDNA_LEN, &tag->ms, szmS, EDNA_LEN, &pusStatus, szStatus,
EDNA_LEN, szDesc, EDNA_DESC_LEN, szUnits, EDNA_LEN);
#endif


tag->flag = (ret == 0) ? NORMAL : OFFLINE;
}

return TRUE;
}

—EOF—

介绍

PI(Plant Information System)是由美国 OSIsoft 公司开发的一套基于 Client/Server 结构的商品化软件应用平台,是过程工业全厂信息集成的必然选择。作为工厂底层控制网络与上层管理信息系统网络连接的桥梁,PI 在工厂信息集成中扮演着特殊和重要的角色。更详细的介绍,可参考百度百科或者官方网站

登录数据库

要访问数据库,有两种方法,一种是通过用户名和密码登录,另一种是通过 PI Trust。官方是不建议使用密码登录的,所以在开发应用的时候,尽可能使用 PI Trust,但是国内的情况比较混乱,很多生产环境都还是使用第一次方式。

下面分别介绍两种登录方法的使用

用户名 / 密码登录

这种方式比较简单,Server 端几乎不需要任何配置,只要简单添加一个用户即可。

涉及到的 API 函数:

  • piut_setservernode 设置 PI Server 节点,一般就是 Server 的 IP 地址
  • piut_isconnected 是否已经和 Server 连接
  • piut_login 传入用户名 / 密码,根据返回值判断是否连接成功,如果需要写数据的话,需要注意写权限

在 PI 3.x 中,默认是不启用用户名 / 密码登录的,需要注意

PI Trust 登录

这种方式,是官方推荐做法,需要在 Server 段添加相应的 PI Trust 设置,可以设置 IP 地址 / Client 名称 / 进程名字等,配置非常灵活。

涉及到的 API 函数:

  • piut_setservernode 同上
  • piut_isconnected 同上
  • piut_setprocname 设置 Client 的进程名,需要和 Server 段设置一致
  • piut_connect 连接 Server

示例代码,功能就是根据配置参数自动判断连接方式,因为我们生产环境是个很老的现场,所以优先使用用户名 / 密码登录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
error_t connect_pi(server_info_t *server)
{
int32_t result;
int32_t valid;
int8_t version[SERVER_FIELD_SIZE];
int8_t log_tag[MAX_BUFF_SIZE];
int8_t pro_name[SERVER_FIELD_SIZE];

valid = PIREAD;

strncpy(log_tag, "connect pi", MAX_BUFF_SIZE);

if (piut_isconnected() == 0)
{
// retrieves the version number of the PI-API.
if (piut_getapiversion(version, sizeof(version)) == 0)
{
ftrace_log(log_tag, "PI-API version %s", version);
}

if (server->misc3 == NULL || strlen(server->misc3) == 0)
{
strncpy(pro_name, "pi_snap", SERVER_FIELD_SIZE);
}
else
{
strncpy(pro_name, server->misc3, SERVER_FIELD_SIZE);
}

// sets the active PI Server node where the data for the subsequent
// PI-API calls will be resolved
result = piut_setservernode(server->server_ip);

if (result)
{
ftrace_log(log_tag, "piut_setservernode: can not connect to %s", server->server_ip);
return E_CONNECT_FAILED;
}

if (server->usr_name != NULL && strlen(server->usr_name) > 0)
{
// establishes a user's access to PI System data based on a login to a configured user database
result = piut_login(server->usr_name, server->usr_pwd, &valid);

if (result == 0)
{
ftrace_log(log_tag, "piut_login: connect to %s successful, valid=%d", server->server_ip, valid);
return E_SUCCESS;
}

ferror_log(log_tag, "piut_login: connect to %s failed, ret=%d", server->server_ip, result);
}

//sets the global process name to the passed string
piut_setprocname(pro_name);
result = piut_connect(server->server_ip);

if (result)
{
ferror_log(log_tag, "piut_connect: connect to %s failed, ret=%d", server->server_ip, result);
return E_CONNECT_FAILED;
}
}

return E_SUCCESS;
}

初始化测点信息

连接上 Server 之后,需要根据测点信息,转换成数据库中对应的 id,方便后面的操作

涉及到的 API 函数:

  • pipt_findpoint 转换测点名称为测点号 (必须)
  • pipt_pointtypex 根据测点号查询对应的测点类型
  • pipt_displaydigits 根据测点号查询显示位数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
static int16_t init_pi_tag(pi_tag_t *tag)
{
int32_t result;
int8_t log_tag[MAX_BUFF_SIZE];
strncpy(log_tag, "init_pi_tag", MAX_BUFF_SIZE);

// gets the point number for the given tagname
result = pipt_findpoint(tag->name, &tag->point_id);

if (result)
{
ftrace_log(log_tag, "fetch point[%s] id failed.", tag->name);
tag->stat = NOT_EXIST;
return FALSE;
}

// gets the data type code for the passed point number
result = pipt_pointtypex(tag->point_id, &tag->pt_typex);

if (result)
{
ftrace_log(log_tag, "fetch point[%s] typex failed.", tag->name);
tag->stat = INVALID;
return FALSE;
}

// gets the display digits attribute for the passed point number
result = pipt_displaydigits(tag->point_id, &tag->display_prec);

if (result)
{
ftrace_log(log_tag, "fetch point[%s] display digits failed.", tag->name);
tag->display_prec = -5;
}

tag->stat = NORMAL;
tag->rval = (float64_t) 0.0;
tag->istat = 0;
tag->ival = 0;
tag->flag = 0;

ftrace_log(log_tag, "name:%s, id:%d, dis_prec: %d,type: %d", tag->name, tag->point_id, tag->display_prec, (int32_t)tag->pt_typex);

switch (tag->pt_typex)
{
case PI_Type_PIstring:
case PI_Type_blob:
case PI_Type_PItimestamp:
if (tag->bsize == 0)
{
/* Skip allocation if a subsequent run. */
if ((tag->bval = (void *)malloc(BVALUE_BUFF)) == NULL)
{
error_log(log_tag, "malloc bval failed.");
destory();
return E_MALLOC_FAILED;
}
}

tag->bsize = (tag->bsize > BVALUE_BUFF - 1) ? tag->bsize : BVALUE_BUFF - 1;
memset(tag->bval, 0, (size_t)(tag->bsize + 1));
break;

case PI_Type_int16:
case PI_Type_int32:
tag->bsize = 0;
break;

case PI_Type_digital:
tag->bsize = 0;
break;

default:/* floats, PI2 */
tag->bsize = 0;
break;
} /* End switch */

return TRUE;
}

查询实时数据

从快照中查询数据库,可以通过 pisn_getsnapshot 或者 pisn_getsnapshotx 查询,具体可查考 API 文档。

注: pisn_getsnapshot 获取的时间只到秒级,如果对时间精度要求比较高,需要使用 pisn_getsnapshotx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
int16_t getsnapshot()
{
// init parameters etc.
// ...

for (index = 0; index < ptbl_data->point_size; index++, tag++)
{
// check server connection etc
// ...
tag->rval = 0;
tag->istat = 0;

result = pisn_getsnapshot(tag->point_id, &tag->rval, &tag->istat, &tag->time);

if (result == 0)
{
ftrace_log(log_tag, "%s[0x%p] pisn_getsnapshot, rval=%f,ival=%d,time=%d", tag->name, tag, tag->rval, tag->istat, tag->time);
}
else if (result == -1)
{
tag->stat = NOT_EXIST;
ftrace_log(log_tag, "%s pisn_getsnapshot not exist, result=%d.", tag->name, result);
}
else
{
tag->stat = INVALID;
ftrace_log(log_tag, "%s pisn_getsnapshot failed, result=%d.", tag->name, result);
}
}

return TRUE;
}

—EOF—

0%