在最开始设计时,主要是针对摄像头和网络门禁这种使用私有协议的网络设备,需要让驱动可以和设备进行数据交互,于是创建了Socket型驱动这个分支。
但在后续的应用中,我们就发现Socket型驱动其实和串口驱动,在协议解析上类似,但是编写方法不同,造成维护不一致。
并且后续我们又加入了 “虚拟网络串口” 这个功能,虚拟网络串口 可以将一个IP:port模拟成FsuOS系统中的一个串口,然后串口驱动就可以正常的收发,只是开关串口实际没有效果。
当前Socket型驱动,主要用于那些需要调用第三方SDK的的驱动,比如使用TCP-Modbus的驱动。
ModbusTcp协议支持
FsuOS SDK中集成了libmodbus开发库,用户可以直接使用modbus-tcp相关代码
#include <modbus/modbus.h>
#include <modbus/modbus-tcp.h>
class UnicomLL11: public UniDataDevice<UnicomLL11_Data_t,SMDSocketDevice, RT_UNICOMLL11>
类可以从SMDSocketDevice直接继承
在RefreshStatus里直接请求数据
pCtx = modbus_new_tcp(ip_.c_str(), port_);
int nRet = modbus_connect(pCtx);
if(-1 == modbus_read_registers(pCtx, 27-1, 12, regs))
BacNet协议支持 BacNet在楼宇自动化对接中比较常见,水泵,冷循环设备多提供此接口,FsuOS在2024-10-17号以后的版本提供此协议的支持。 使用方法:
#include "SMDBacNetDevice.hpp"
class BTBacnetLeng: public UniDataDevice<BTBacnetLeng_Data_t,SMDBacNetDevice, RT_BTBacnetLeng>
类需要从SMDBacNetDevice继承
逻辑参数:需要提供:ip, port:47808默认, device_id:对方设备ID
读取数据:
根据BACNET的规范,可以读,很多类型,一般设备导出的数据点表会注明类型
OBJECT_ANALOG_INPUT = 0,
OBJECT_ANALOG_OUTPUT = 1,
OBJECT_ANALOG_VALUE = 2,
OBJECT_BINARY_INPUT = 3,
OBJECT_BINARY_OUTPUT = 4,
OBJECT_BINARY_VALUE = 5,
10005 是属性ID,即objectId, PROP_PRESENT_VALUE 是要读取属性的值
nt r = Send_Read_Property_Request_Address((BACNET_OBJECT_TYPE)0, 10005, PROP_PRESENT_VALUE);
if(r){
int len = Receive();
if(len)
{
//这个appValue.type.Real是如果返回值是float型,就用这个读appValue就是从上面命令读取到的值
cData.v0[i] = appValue.type.Real;
std::cout<<"get 0 "<<v0addr[i]<<" value:"<<cData.v0[i]<<std::endl;
}else{
return false;
}
}else{
return false;
}
SNMP协议支持
FsuOS SDK中集成了net-snmp开发库,用户可以直接使用net-snmp相关代码,例如:snmp_sess_init,snmp_open,snmp_pdu_create,snmp_synch_response等
示例,海悟微模块是一款使用SNMP的智能设备,首先要包含net-snmp的头文件,使用我们提供的模板则已有包含代码.
#include "SMDSocketDevice.hpp"
#include "HaiWuWMK_interface.h"
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
class HaiWuWMK: public SMDSocketDevice
{
public:
HaiWuWMK();
~HaiWuWMK();
bool Init(std::weak_ptr<SMDController> controller,unsigned int data_id, const Json::Value& settingRoot) override;
bool RefreshStatus() override;
bool process_data(tcp::socket::native_handle_type fd, uint8_t *buffer, int size);
void RoundDone();
private:
netsnmp_session session, *ss = NULL;
netsnmp_pdu *pdu = NULL, *response = NULL;
netsnmp_variable_list *vars;
oid root[MAX_OID_LEN] = {1,3,6,1,4,1};
size_t rootlen = 6;
oid name[MAX_OID_LEN];
size_t name_length;
bool is_snmp_running_ = false;
HaiWuWMK_Data_t cData;
boost::posix_time::ptime lastTime;
};
实现代码
HaiWuWMK::HaiWuWMK()
{
device_type_ = "haiwuwmk";
snmp_sess_init(&session); /* initialize session */
session.version = SNMP_VERSION_2c;
//session.peername = "194.109.65.33";
session.community = (uint8_t*)"public";
session.community_len = strlen((const char*)session.community);
session.timeout = 10000000L;
session.retries = 1;
}
bool HaiWuWMK::Init ( std::weak_ptr<SMDController> controller, unsigned int data_id, const Json::Value& settingRoot )
{
SMDVDevice::Init ( controller, data_id, settingRoot );
//app settings
if ( settingRoot["appSetting"] != Json::nullValue ) {
if ( settingRoot["appSetting"].type() == Json::objectValue ) {
if ( settingRoot["appSetting"]["ip"] != Json::nullValue ) {
ip_ = settingRoot["appSetting"]["ip"].asString();
session.peername = (char*)ip_.c_str();
}
}
}
return true;
}
bool HaiWuWMK::RefreshStatus()
{
if(!is_snmp_running_)
{
is_snmp_running_ = true;
}else{
//要判断,多上卡住多长时间了
boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
boost::posix_time::time_duration diff = now - lastTime;
if( diff.total_seconds() > 30) {
//下一轮
printf("going to restart,will crash\n");
return false;
}else{
printf("abort going to restart,will crash\n");
return false;
}
}
std::cout<<"HaiWu "<<data_id_<<"RefrehsStatus"<<std::endl;
//CheckThresholdWork([this]{
std::async( std::launch::async,
[&]() {
lastTime = boost::posix_time::second_clock::local_time();
if (response){
snmp_free_pdu(response);
}
if(ss){
snmp_close(ss);
}
ss = snmp_open(&session);
if (ss == NULL) {
/*
* diagnose snmp_open errors with the input netsnmp_session pointer
*/
snmp_sess_perror("snmpbulkwalk", &session);
is_snmp_running_ = false;
return false;
}
ss->timeout = 10000000L;
memmove(name, root, rootlen * sizeof(oid));
name_length = rootlen;
int running = true;
int exitval = 0;
int status = STAT_ERROR;
while(running)
{
lastTime = boost::posix_time::second_clock::local_time();
pdu = snmp_pdu_create(SNMP_MSG_GETBULK);
pdu->non_repeaters = 1;
pdu->max_repetitions = 100; /* fill the packet */
snmp_add_null_var(pdu, name, name_length);
/*
* do the request
*/
status = snmp_synch_response(ss, pdu, &response);
if (status == STAT_SUCCESS && response != NULL) {
if (response->errstat == SNMP_ERR_NOERROR) {
/*
* check resulting variables
*/
for (vars = response->variables; vars;
vars = vars->next_variable) {
if ((vars->name_length < rootlen)
|| (memcmp(root, vars->name, rootlen * sizeof(oid))
!= 0)) {
/*
* not part of this subtree
*/
running = 0;
continue;
}
//这个地方解析数据
std::string newVal;
newVal.assign((const char*)vars->buf, vars->val_len);
if(vars->name[6] == 11 || vars->name[6] == 12)
{
switch(vars->name[7])
{
case 1:
cData.env[vars->name[6] - 11].online_status = atof(newVal.c_str());
break;
case 2:
cData.env[vars->name[6] - 11].temperature = atof(newVal.c_str());
break;
case 3:
cData.env[vars->name[6] - 11].humid = atof(newVal.c_str());
break;
}
}else if(vars->name[6] == 50)
{
switch(vars->name[7])
{
case 1:
cData.io_status = atof(newVal.c_str());
break;
case 2:
cData.smoke[0] = atof(newVal.c_str());//1,正常;0,告警
break;
case 3:
cData.smoke[1] = atof(newVal.c_str());//1,正常;0,告警
break;
case 4:
cData.fire_alarm = atof(newVal.c_str());//外部消防,0,正常;1,告警
break;
case 5:
cData.water = atof(newVal.c_str());//漏水,0,正常;1,告警
break;
case 13:
cData.beeper = atof(newVal.c_str());//峰鸣器,0,断开;1,告警
break;
case 14:
cData.open_win = atof(newVal.c_str());//天窗联动,0,保持;1,动作
break;
}
}else if(vars->name[6] == 179)
{
if(vars->name[7] == 1)
{
cData.cabinet_status = atof(newVal.c_str());
}else
{
cData.cabinet_value[vars->name[7]-2] = atof(newVal.c_str());
}
}else if(vars->name[6] == 302 || vars->name[6] == 303 || vars->name[6] == 304 || vars->name[6] == 305)
{
if(vars->name[7] == 1)
{
cData.ac[vars->name[6] - 302].online_status = atof(newVal.c_str());
}else
{
cData.ac[vars->name[6] - 302].value[vars->name[7] - 2] = atof(newVal.c_str());
}
}else if(vars->name[6] == 326 || vars->name[6] == 327)
{
cData.door[vars->name[6] - 326].value[vars->name[7] - 1] = atof(newVal.c_str());
}
if ((vars->type != SNMP_ENDOFMIBVIEW) &&
(vars->type != SNMP_NOSUCHOBJECT) &&
(vars->type != SNMP_NOSUCHINSTANCE)) {
/*
* not an exception value
*/
/*
* Check if last variable, and if so, save for next request.
*/
if (vars->next_variable == NULL) {
memmove(name, vars->name,
vars->name_length * sizeof(oid));
name_length = vars->name_length;
}
} else {
/*
* an exception value, so stop
*/
running = 0;
}
}
} else {
/*
* error in response, print it
*/
running = 0;
if (response->errstat == SNMP_ERR_NOSUCHNAME) {
printf("End of MIB\n");
} else {
fprintf(stderr, "Error in packet.\nReason: %s\n",
snmp_errstring(response->errstat));
exitval = 2;
}
}
} else if (status == STAT_TIMEOUT) {
fprintf(stderr, "Timeout: No Response from %s\n",
session.peername);
running = 0;
exitval = 1;
} else { /* status == STAT_ERROR */
//snmp_sess_perror("snmpbulkwalk", ss);
printf("snmp get error %d\n", status);
running = 0;
exitval = 1;
}
if (response){
snmp_free_pdu(response);
response = NULL;
}
}
snmp_close(ss);
ss = NULL;
if(exitval == 0)
{
RoundDone();
//return false;
}
is_snmp_running_ = false;
});
return false;
}