From 05fcaeac890dc74b003f7327d5eacbccc796bc49 Mon Sep 17 00:00:00 2001 From: Matthew Date: Wed, 8 Nov 2023 13:02:52 +0800 Subject: [PATCH] =?UTF-8?q?GB2312=20=E8=BD=ACUTF8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/HTLogger.h | 12 +- include/HTMutex.h | 5 +- include/HTOpencvImg.h | 14 +- include/HTTestOpencv.h | 2 +- src/HTIEC104.cpp | 3103 +++++++++++++++++++++++++++++++++++++++- src/HTLogger.cpp | 8 + src/HTMemCacheData.cpp | 88 +- src/HTOpencvImg.cpp | 2608 ++++++++++++++++++++++++++++++++- src/HTPublic.cpp | 277 +++- src/HTTestOpencv.cpp | 1849 +++++++++++++++++++++++- src/HXIec104.cpp | 1572 +++++++++++++++++++- src/make.mak | 6 +- src/makefile | 6 +- 13 files changed, 9514 insertions(+), 36 deletions(-) diff --git a/include/HTLogger.h b/include/HTLogger.h index 859eff7..e6253df 100644 --- a/include/HTLogger.h +++ b/include/HTLogger.h @@ -19,11 +19,15 @@ extern "C" { // logger config parameter #ifdef _WIN32 - static const char *DEF_LOG_PATH_NAME = "..\\log" ; //日志文件目录名 - static const char *DEF_RUN_INFO_PATH = "..\\run" ; // 服务运行状态日志目录 + // static const char *DEF_LOG_PATH_NAME = "..\\log" ; //日志文件目录名 + // static const char *DEF_RUN_INFO_PATH = "..\\run" ; // 服务运行状态日志目录 + extern const char *DEF_LOG_PATH_NAME; // = "..\\log"; //日志文件目录名 + extern const char *DEF_RUN_INFO_PATH; // = "..\\run"; // 服务运行状态日志目录 #else - static const char *DEF_LOG_PATH_NAME = "../log" ; //日志文件目录名 - static const char *DEF_RUN_INFO_PATH = "../run" ; // 服务运行状态日志目录 + // static const char *DEF_LOG_PATH_NAME = "../log" ; //日志文件目录名 + // static const char *DEF_RUN_INFO_PATH = "../run" ; // 服务运行状态日志目录 + extern const char *DEF_LOG_PATH_NAME; // = "../log"; //日志文件目录名 + extern const char *DEF_RUN_INFO_PATH; // = "../run"; // 服务运行状态日志目录 #endif // 打印报文类型,日志 diff --git a/include/HTMutex.h b/include/HTMutex.h index 69b255b..7d83261 100644 --- a/include/HTMutex.h +++ b/include/HTMutex.h @@ -35,8 +35,11 @@ #endif #ifdef _WIN32 //for unix +#if (_MSC_VER < 1910) // older than VS2017 #define snprintf _snprintf -#endif +#endif // (_MSC_VER < 1910) + +#endif // _WIN32 #endif // end __HT_MUTEX_H diff --git a/include/HTOpencvImg.h b/include/HTOpencvImg.h index 51c9526..befa05b 100644 --- a/include/HTOpencvImg.h +++ b/include/HTOpencvImg.h @@ -16,13 +16,13 @@ #include #include "HTGlobal.h" //#include "opencv/cv.h" -#include "opencv2/opencv.hpp" -#include "opencv2/core/core.hpp" -#include "opencv2/highgui/highgui.hpp" -#include "opencv2/imgproc/imgproc.hpp" -#include "opencv2/imgcodecs.hpp" -#include "opencv2/ml.hpp" -#include "opencv2/objdetect.hpp" +#include +#include +#include +#include +#include +#include +#include //#define _HT_OPENCV_TEST_ENV // 测试环境,产生随机数作为分析表的数据 diff --git a/include/HTTestOpencv.h b/include/HTTestOpencv.h index 80d1c93..15b24fb 100644 --- a/include/HTTestOpencv.h +++ b/include/HTTestOpencv.h @@ -16,7 +16,7 @@ #include "HTGlobal.h" #include "opencv2/opencv.hpp" -#include "opencv/cv.h" +// #include "opencv/cv.h" #include "opencv2/core/core.hpp" #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" diff --git a/src/HTIEC104.cpp b/src/HTIEC104.cpp index 283684b..e1eb3b2 100644 --- a/src/HTIEC104.cpp +++ b/src/HTIEC104.cpp @@ -679,4 +679,3105 @@ static void showIEC104Conf() { int i = 0; vPrtLogMsg(LOG_DEBUG, RET_OK, "---> IEC104_CONF: iec_byq_count = %d, iec_break_count = %d", g_iec_conf.iec_byq_count, g_iec_conf.iec_break_count); - vPrtLogMsg(L \ No newline at end of file + vPrtLogMsg(LOG_DEBUG, RET_OK, "---> IEC104_CONF: yx_start_addr = %d-(0x%04x)", g_iec_conf.yx_start_addr, g_iec_conf.yx_start_addr); + vPrtLogMsg(LOG_DEBUG, RET_OK, "---> IEC104_CONF: yx_stop_addr = %d-(0x%04x)", g_iec_conf.yx_stop_addr, g_iec_conf.yx_stop_addr); + vPrtLogMsg(LOG_DEBUG, RET_OK, "---> IEC104_CONF: jb_start_addr = %d-(0x%04x)", g_iec_conf.jb_start_addr, g_iec_conf.jb_start_addr); + vPrtLogMsg(LOG_DEBUG, RET_OK, "---> IEC104_CONF: jb_stop_addr = %d-(0x%04x)", g_iec_conf.jb_stop_addr, g_iec_conf.jb_stop_addr); + vPrtLogMsg(LOG_DEBUG, RET_OK, "---> IEC104_CONF: yc_start_addr = %d-(0x%04x)", g_iec_conf.yc_start_addr, g_iec_conf.yc_start_addr); + vPrtLogMsg(LOG_DEBUG, RET_OK, "---> IEC104_CONF: yc_stop_addr = %d-(0x%04x)", g_iec_conf.yc_stop_addr, g_iec_conf.yc_stop_addr); + for (i = 0; i < (int)g_iec_conf.iec_byq_count; i++) + vPrtLogMsg(LOG_DEBUG, RET_OK, "---> IEC104_CONF: BYQ_%02d Eqm_code = %s [%d,%d,%d,%d---%d,%d,%d,%d]", i+1, g_iec_conf.pstByqCode[i].szEqmCode, + g_iec_conf.pstByqCode[i].uiHighVoltage, g_iec_conf.pstByqCode[i].uiHighCurrent, + g_iec_conf.pstByqCode[i].uiLowVoltage, g_iec_conf.pstByqCode[i].uiLowCurrent, + g_iec_conf.pstByqCode[i].uiCoolWaterPressEntry, g_iec_conf.pstByqCode[i].uiCoolWaterPressOuter, + g_iec_conf.pstByqCode[i].uiOilTemperature, g_iec_conf.pstByqCode[i].uiOilPosition); + + for (i = 0; i < (int)g_iec_conf.iec_break_count; i++) + vPrtLogMsg(LOG_DEBUG, RET_OK, "---> IEC104_CONF: GIS_%02d Eqm_code(A,B,C) = (%s,%s,%s [%d,%d])", + i+1, g_iec_conf.pstBrkCode[i].szEqmCodeA, g_iec_conf.pstBrkCode[i].szEqmCodeB, g_iec_conf.pstBrkCode[i].szEqmCodeC, + g_iec_conf.pstBrkCode[i].uiAbortVoltage, g_iec_conf.pstBrkCode[i].uiAbortCurrent); + + // 显示点表地址匹配关系map + vPrtLogMsg(LOG_DEBUG, RET_OK, "---> g_map_sadr: size= %d", g_map_sadr.size()); +} +void InitIECENV() +{ + memset(&g_IecCtrl, 0x00, sizeof(ST_IEC104_CTRL)); + g_IecCtrl.t0 = (int)g_TConfig.getTimeout0(); + g_IecCtrl.t1 = (int)g_TConfig.getTimeout1(); + g_IecCtrl.t2 = (int)g_TConfig.getTimeout2(); + g_IecCtrl.t3 = (int)g_TConfig.getTimeout3(); + g_IecCtrl.k = (int)g_TConfig.getK(); + g_IecCtrl.w = (int)g_TConfig.getW(); + g_IecCtrl.timer_S_Ackflag = true; + g_IecCtrl.timer_U_Testflag = true; // 初始化为真,如果过程中有超时,置为false + g_IecCtrl.m_gis_change = false; + g_IecCtrl.time_action = time(NULL); // 总召间隔时间更新 +} +void IEC104EnvLoad() +{ + mutex_create(g_list_pack_mutex); + mutex_create(g_list_dbset_mutex); + mutex_create(g_sendno_mutex); + mutex_create(g_list_origin_mutex); + mutex_create(g_list_pingce_mutex); + mutex_create(g_list_warn_mutex); + g_list_pack.clear(); + g_list_dbset.clear(); + g_list_origin.clear(); + g_list_pingce.clear(); + g_list_warn.clear(); + InitIECENV(); + iGetIEC104Conf(); + //InitIECPointTable(); +#ifdef _DEBUG + showIEC104Conf(); +#endif +} + +void IEC104EnvFree() +{ + mutex_close(g_list_pack_mutex); + mutex_close(g_list_dbset_mutex); + mutex_close(g_sendno_mutex); + mutex_close(g_list_origin_mutex); + mutex_close(g_list_pingce_mutex); + mutex_close(g_list_warn_mutex); + g_list_pack.clear(); + g_list_dbset.clear(); + g_list_origin.clear(); + g_list_pingce.clear(); + g_list_warn.clear(); +} + +// last action time. +void UpdateLastTime() +{ + mutex_lock(g_IecCtrl_mutex); + g_IecCtrl.last_time = time(NULL); + mutex_unlock(g_IecCtrl_mutex); +} + +// 添加报文到解析队列 +void AddParserList(unsigned char *pbuf, int bufLen) +{ + ST_RECVPKG stPack; + + ST_APCI *pstAPCI = (ST_APCI*)pbuf; + + memset(&stPack, 0x00, sizeof(ST_RECVPKG)); + + stPack.usISUType = (pstAPCI->cntl1 & 0x03); + if (stPack.usISUType == 0 || stPack.usISUType == 2) + { + stPack.usSendNo = 2 + ((pstAPCI->cntl2 << 8) | pstAPCI->cntl1); // 发送序号 + stPack.usRecvNo = (pstAPCI->cntl4 << 8) | pstAPCI->cntl3; // 接收序号 + } + else if (stPack.usISUType == 3) { + stPack.usRecvNo = (pstAPCI->cntl4 << 8) | pstAPCI->cntl3; // 接收序号 + } + + stPack.pszBuff = (unsigned char*)calloc(1, bufLen + 1); + if (stPack.pszBuff == NULL) + { + vPrtLogMsg(LOG_WARNG, RET_CALLOC_FAIL, "calloc failed."); + return; + } + memcpy(stPack.pszBuff, pbuf, bufLen); + stPack.iLength = bufLen; + + mutex_lock(g_list_pack_mutex); + g_list_pack.push_front(stPack); + mutex_unlock(g_list_pack_mutex); +} +//ST_IEC104_DATA +//添加到入库队列 +// pkgtype= 类型:1-运行工况 2:断路器数据 3:变压器负荷数据 +static void addDbSetList(char *pdata, int length, char ctype) +{ + int i = 0; + ST_DB_DATA stSetData; + + memset(&stSetData, 0x00, sizeof(ST_DB_DATA)); + stSetData.pData = (unsigned char*)calloc(1, length); + if (stSetData.pData == NULL) + { + vPrtLogMsg(LOG_WARNG, RET_CALLOC_FAIL, "calloc failed."); + return; + } + memcpy(stSetData.pData, pdata, length); + stSetData.ctype = (ctype & 0xff); //类型:1 - 运行工况 2 : 断路器数据 3 : 变压器负荷数据 + stSetData.iLength = length; + + mutex_lock(g_list_dbset_mutex); + g_list_dbset.push_front(stSetData); + mutex_unlock(g_list_dbset_mutex); +} + +// 添加告警数据队列 +static void addWarningTableList(ST_IECPOINT_TABLE &stData) +{ + int i = 0; + ST_IECPOINT_TABLE stSetData; + memset(&stSetData, 0x00, sizeof(ST_DB_DATA)); + memcpy(&stSetData, &stData, sizeof(ST_IECPOINT_TABLE)); + + mutex_lock(g_list_warn_mutex); + g_list_warn.push_front(stSetData); + mutex_unlock(g_list_warn_mutex); +} +// 添加断路器断开状态数据队列 +static void addGisDataList(ST_IECPOINT_TABLE &stData) +{ + int i = 0; + ST_IECPOINT_TABLE stSetData; + memset(&stSetData, 0x00, sizeof(ST_DB_DATA)); + memcpy(&stSetData, &stData, sizeof(ST_IECPOINT_TABLE)); + + mutex_lock(g_list_pingce_mutex); + g_list_pingce.push_front(stSetData); + mutex_unlock(g_list_pingce_mutex); +} +// 添加原始数据入库队列 +static void addOriginDataList(ST_IECPOINT_TABLE &stData) +{ + int i = 0; + ST_IECPOINT_TABLE stSetData; + + memset(&stSetData, 0x00, sizeof(ST_DB_DATA)); + memcpy(&stSetData, &stData, sizeof(ST_IECPOINT_TABLE)); + + mutex_lock(g_list_origin_mutex); + g_list_origin.push_front(stSetData); + mutex_unlock(g_list_origin_mutex); +} + +// 获取发送序列号 +void vAutoSendSeqNo(int b) +{ + mutex_lock(g_sendno_mutex); + if (g_IecCtrl.usSendNo > 16384) + g_IecCtrl.usSendNo = 0; + else g_IecCtrl.usSendNo += b; + mutex_unlock(g_sendno_mutex); +} + +// TX U-Format Message (APCI only) +int SendMsgFormatU(int cmd) +{ + int iRet = -1; + if (g_Tcp.tcpIsConnected(g_IecCtrl.sockid)) + { + ST_APCI header; + header.start = 0x68; + header.len = 0x04; + header.cntl1 = 0x03 | cmd; // U-Format + header.cntl2 = 0; + header.cntl3 = 0; // g_IecCtrl.usRecvNo & 0xFE; + header.cntl4 = 0; //(g_IecCtrl.usRecvNo >> 8) & 0xFF; + + iRet = g_Tcp.tcpSendBuffer(g_IecCtrl.sockid, (const char*)&header, sizeof(ST_APCI)); + if (iRet == ErrException) + { + vPrtLogMsg(LOG_ERROR, iRet, "send mesg failed, sockid:%d msg:%s", g_IecCtrl.sockid, strerror(errno)); + g_Tcp.clear_tcp_buffer(g_IecCtrl.sockid, MAX_SBUFF_TCP); + g_Tcp.tcpCloseSocket(g_IecCtrl.sockid); + g_IecCtrl.isConnect = false; + } + //g_IecCtrl.usSendNo++; + vPrtLogHex(LOG_PACK, g_IecCtrl.sockid, PRT_PACK_SEND, (unsigned char*)&header, sizeof(ST_APCI)); + } + return iRet; +} +// 发送S格式报文 +int SendMsgFormatS(unsigned short sendno) +{ + if (!g_IecCtrl.isConnect || g_IecCtrl.sockid <= 0) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "send S package, No socket connection, send cancel.sockid=%d isConn=%d", g_IecCtrl.sockid, g_IecCtrl.isConnect); + return 0; + } + + ST_APCI header; + int iRet = -1; + header.start = 0x68; + header.len = 0x04; + + header.cntl1 = 0x01; // S-Format + header.cntl2 = 0x00; //S格式确认包,cnt1=0x01, cnt2=0x00,是固定不变的。 + header.cntl3 = sendno & 0xFE; // 应答对端的接收序列号 + header.cntl4 = (sendno >> 8) & 0xFF; + + //header.cntl1 = (g_IecCtrl.RxCounter & 0xFE); // S-Format + //header.cntl2 = (g_IecCtrl.RxCounter >> 8) & 0xFF; //S格式确认包,cnt1=0x01, cnt2=0x00,是固定不变的。 + //header.cntl3 = (g_IecCtrl.TxCounter & 0xFE); // S-Format + //header.cntl4 = (g_IecCtrl.TxCounter >> 8) & 0xFF; //S格式确认包,cnt1=0x01, cnt2=0x00,是固定不变的。 + + + if ((iRet = g_Tcp.tcpSendBuffer(g_IecCtrl.sockid, (const char*)&header, sizeof(ST_APCI))) < 0) + { + vPrtLogMsg(LOG_WARNG, iRet, "send Format S failed with error: %s, errno=%d", strerror(errno), errno); + g_Tcp.clear_tcp_buffer(g_IecCtrl.sockid, MAX_SBUFF_TCP); + g_Tcp.tcpCloseSocket(g_IecCtrl.sockid); + g_IecCtrl.isConnect = false; + return iRet; + } + //g_IecCtrl.usSendNo++; + vPrtLogHex(LOG_PACK, g_IecCtrl.sockid, PRT_PACK_SEND, (unsigned char*)&header, sizeof(ST_APCI)); + return iRet; +} + +// TX I-Format Message (always contains ASDU) +int SendMsgFormatI(unsigned char *msgbuf, unsigned int len) +{ + if (!g_IecCtrl.isConnect || g_IecCtrl.sockid <= 0) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "send I package, No socket connection, send cancel.sockid=%d isConn=%d", g_IecCtrl.sockid, g_IecCtrl.isConnect); + return 0; + } + char buf[1024]; + int iRet = -1; + ST_APCI *header = (ST_APCI*)buf; + vAutoSendSeqNo(1); // 发送序列号增1 + header->start = 0x68; + header->len = 0x04 + len; + header->cntl1 = g_IecCtrl.usSendNo & 0xFE; // I-Format + header->cntl2 = ((g_IecCtrl.usSendNo >> 8) & 0xFF); + header->cntl3 = (g_IecCtrl.usRecvNo & 0xFE); // I-Format + header->cntl4 = ((g_IecCtrl.usRecvNo >> 8) & 0xFF); + + memcpy(buf + sizeof(ST_APCI), msgbuf, len); + if ((iRet = g_Tcp.tcpSendBuffer(g_IecCtrl.sockid, (const char*)buf, len + sizeof(ST_APCI))) < 0) + { + vPrtLogMsg(LOG_WARNG, iRet, "send Format I failed with error: %s, errno=%d", strerror(errno), errno); + g_Tcp.clear_tcp_buffer(g_IecCtrl.sockid, MAX_SBUFF_TCP); + g_Tcp.tcpCloseSocket(g_IecCtrl.sockid); + g_IecCtrl.isConnect = false; + return iRet; + } + // g_IecCtrl.usSendNo++; + vPrtLogHex(LOG_PACK, g_IecCtrl.sockid, PRT_PACK_SEND, (unsigned char*)buf, len + sizeof(ST_APCI)); + return iRet; +} +int Interrogate(int asdu, int group) +{ + ST_ASDU msg; + msg.header.type = 0x64; // interrogation command + msg.header.qual = 0x01; // number of elements + msg.header.tx_cause1 = 0x06; // activation + msg.header.tx_cause2 = 0x00; + msg.header.commom_asdu1 = asdu & 0xFF; + msg.header.commom_asdu2 = (asdu >> 8) & 0xFF; + // 貌似少了3个字节的信息体地址 + // group information + msg.data[0] = 0x00; + msg.data[1] = 0x00; + msg.data[2] = 0x00; + msg.data[3] = 20 + group; + return SendMsgFormatI((unsigned char*)&msg, 10); +} +/*************************************************************************** +** 函数名:thread_listen_proc 启动服务线程 +** 参数: 无 +** +** 返回: 无 +***************************************************************************/ +void* thread_listen_proc(void * arg) +{ + int server_fd, accept_fd; + + vPrtLogMsg(LOG_DEBUG, 0, "thread_listen_proc = %d startup...", GETTID()); + //从链路启动服务 + server_fd = g_Tcp.tcpOpenServer(g_TConfig.getLocalAddr(), g_TConfig.getLocalPort()); + if (server_fd <= 0) { + vPrtLogMsg(LOG_ERROR, server_fd, "Local server:%s:%d startup failed.", g_TConfig.getLocalAddr(), g_TConfig.getLocalPort()); + g_Running = 0; + return NULL; + } + g_Tcp.tcpSetServerfd(server_fd); + + mutex_lock(g_IecCtrl_mutex); + g_IecCtrl.listenid = server_fd; + mutex_unlock(g_IecCtrl_mutex); + + while (g_Running) + { + pthread_testcancels(); + accept_fd = g_Tcp.tcpAcceptSocket(server_fd); + if (accept_fd < 0) + { + mutex_lock(g_IecCtrl_mutex); + g_Tcp.tcpCloseSocket(g_IecCtrl.sockid); + g_IecCtrl.last_time = 0; + g_IecCtrl.sockid = -1; + g_IecCtrl.isConnect = false; + mutex_unlock(g_IecCtrl_mutex); + _SLEEP(1000); + // vPrtLogMsg(LOG_DEBUG, errno,"slave link waitting connect...accept_fd=%d", accept_fd); + continue; + } + mutex_lock(g_IecCtrl_mutex); + g_IecCtrl.last_time = time(NULL); + g_IecCtrl.last_yx_time = time(NULL); + g_IecCtrl.last_yc_time = time(NULL); + g_IecCtrl.sockid = accept_fd; + g_IecCtrl.isConnect = true; + mutex_unlock(g_IecCtrl_mutex); + + pthread_t thread_handle_minor_recv = -1; + pthread_t thread_handle_minor_active = -1; + _SLEEP(3000); // 给主链路时间,来处理登录应答. +#ifndef _WIN32 + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); // 创建一个分离子线程 +#endif + g_Tcp.tcpSetSockID(accept_fd); + vPrtLogMsg(LOG_DEBUG, errno, "connect link the recv socket fd is: %d", g_IecCtrl.sockid); + ht_pthread_create_background(&thread_handle_minor_recv, thread_recv_proc, NULL); + ht_pthread_create_background(&thread_handle_minor_active, thread_active_proc, NULL); +#ifndef _WIN32 + pthread_detach(thread_handle_minor_recv); + pthread_detach(thread_handle_minor_active); +#endif + _SLEEP(1000); + } + g_Tcp.tcpCloseSocket(server_fd); + vPrtLogMsg(LOG_DEBUG, errno, "shutdown accept addr=%s:%d, socket_id: %d", g_TConfig.getLocalAddr(), g_TConfig.getLocalPort(), server_fd); + return NULL; +} + +// 添加104子站服务不能连接的告警消息 +void vSetIEC104StationsWarnMesg() +{ + int i = 0; + char eqm_code[64] = { 0 }, szuuid[64] = { 0 }; + char szSql[DEF_BUFFER_1K] = { 0 }; + CDBMySQL *pdbHandle = CDBMySQL::Instance(); + if (!pdbHandle) { + return; + } + vGetHostTimeFmt(g_IecCtrl.m_iec_warn_time); + for (i = 0; i < (int)g_iec_conf.iec_byq_count; i++) + { + getuuid(szuuid, sizeof(szuuid)); + strcpy(eqm_code, (const char*)g_iec_conf.pstByqCode[i].szEqmCode); + snprintf(szSql,sizeof(szSql), "INSERT INTO busi_eqm_warning(id,eq_type,gz_type,warning_msg,warning_time,create_time," + "del_flag,site_id,main_tree_id,run_state) " + "VALUES('%s','1', '9', '104子站服务异常断开,请检查服务!', '%s', SYSDATE(), '1', '%s', '%s','0')", + szuuid,g_IecCtrl.m_iec_warn_time, g_iec_conf.site_id, eqm_code); + + vPrtLogMsg(LOG_DEBUG, RET_OK, "insert IECService Warning: %s", szSql); + if (!pdbHandle->InsertRecord((const char *)szSql)) { + vPrtLogMsg(LOG_ERROR, RET_FAIL, "INSERT INTO busi_eqm_warning message failed,SQL:%s", szSql); + } + + // 插入告警详细表 + char szSubUuid[64] = { 0 }; + char szWarninfo[512] = { 0 }; + getuuid(szSubUuid, sizeof(szSubUuid)); + memset(szSql, 0x00, sizeof(szSql)); + + snprintf(szWarninfo, sizeof(szWarninfo), + "{\"sadr\":\"\",\"thresholdValue\":\"\",\"currRealValue\":\"\",\"equipmentStatus\":\"\",\"collectDate\":\"%s\",\"ipaddr\":\"%s\",\"port\":\"%d\",\"msgInfo\":\"%s\",\"phase\":\"\"}", + g_IecCtrl.m_iec_warn_time, g_TConfig.getRemoteAddr(), g_TConfig.getRemotePort(),"IEC104服务链路"); + snprintf(szSql, sizeof(szSql), "INSERT INTO busi_eqm_warning_info(id,warning_id,warning_info) " + "VALUES('%s','%s','%s')", + szSubUuid, szuuid, szWarninfo); + if (!pdbHandle->InsertRecord((const char *)szSql)) { + vPrtLogMsg(LOG_ERROR, RET_FAIL, "INSERT INTO busi_eqm_warning_info message failed,SQL:%s", szSql); + } + } +} +// 删除104子站服务不能连接的告警消息 +void vDelIEC104StationsWarnMesg() +{ + int i = 0; + char eqm_code[64] = { 0 }; + char szSql[DEF_BUFFER_1K] = { 0 }; + CDBMySQL *pdbHandle = CDBMySQL::Instance(); + if (!pdbHandle) { + return; + } + for (i = 0; i < (int)g_iec_conf.iec_byq_count; i++) + { + strcpy(eqm_code, (const char*)g_iec_conf.pstByqCode[i].szEqmCode); + if (strlen(g_IecCtrl.m_iec_warn_time) > 0 ) + snprintf(szSql, sizeof(szSql), "update busi_eqm_warning set del_flag='2', deal_status='1',processing_time=sysdate(),deal_msg='system service auto resume.'," + "processing_user='system' where site_id='%s' and eq_type='1' and gz_type='9' and main_tree_id='%s' and warning_time='%s' and " + "warning_msg='104子站服务异常断开,请检查服务!' ", g_iec_conf.site_id, eqm_code, g_IecCtrl.m_iec_warn_time); + else + snprintf(szSql, sizeof(szSql), "update busi_eqm_warning set del_flag='2', deal_status='1',processing_time=sysdate(),deal_msg='system service auto resume.'," + "processing_user='system' where site_id='%s' and eq_type='1' and gz_type='9' and main_tree_id='%s' and " + "warning_msg='104子站服务异常断开,请检查服务!' ", g_iec_conf.site_id, eqm_code); + + vPrtLogMsg(LOG_DEBUG, RET_OK, "update IECService Warning: %s", szSql); + if (!pdbHandle->UpdateRecord((const char *)szSql)) { + vPrtLogMsg(LOG_ERROR, RET_FAIL, "update busi_eqm_warning message failed,SQL:%s", szSql); + } + } +} +/*************************************************************************** +** function name : thread_linkmgr_proc +** deacription : link manager thread +** parameter : none +** return code : NULL +***************************************************************************/ +void *thread_client_proc(void *arg) +{ + bool bWarn = true; + vPrtLogMsg(LOG_DEBUG, 0, "thread_client_proc = %d startup...", GETTID()); + g_IecCtrl.b_Signal = false; + g_IecCtrl.m_gis_fault_count = 0; +// g_IecCtrl.m_gis_count = 0; + while (g_Running) + { + pthread_testcancels(); + + if (g_Tcp.tcpIsConnected(g_IecCtrl.sockid) && g_IecCtrl.isConnect) { + _SLEEP(5000); // 5秒钟检查一次链路是否正常 + continue; + } + else if (g_IecCtrl.sockid > 0) + { + mutex_lock(g_IecCtrl_mutex); + g_Tcp.tcpCloseSocket(g_IecCtrl.sockid); + g_IecCtrl.last_time = 0; + g_IecCtrl.sockid = -1; + g_IecCtrl.isConnect = false; + InitIECENV(); + mutex_unlock(g_IecCtrl_mutex); + } + vPrtLogMsg(LOG_DEBUG, 0, "Connect to server:%s:%d start...", g_TConfig.getRemoteAddr(), g_TConfig.getRemotePort()); + //创建客户端 + g_IecCtrl.sockid = g_Tcp.tcpConnect(g_TConfig.getRemoteAddr(), g_TConfig.getRemotePort(), 30); // t0 = 30s + if (g_IecCtrl.sockid <= 0) { + vPrtLogMsg(LOG_ERROR, g_IecCtrl.sockid, "Connect to server:%s:%d failed.", g_TConfig.getRemoteAddr(), g_TConfig.getRemotePort()); + if (bWarn) { + vSetIEC104StationsWarnMesg(); + bWarn = false; + } + _SLEEP(5000); + continue; + } + mutex_lock(g_IecCtrl_mutex); + g_IecCtrl.last_time = time(NULL); + g_IecCtrl.isConnect = true; + mutex_unlock(g_IecCtrl_mutex); + SendMsgFormatU(CMD_STARTDT); // 发送激活命令 + pthread_t thread_handle_minor_recv = -1; + pthread_t thread_handle_minor_active = -1; + g_Internal_time = time(NULL); + //_SLEEP(3000) ; +#ifndef _WIN32 + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); // 创建一个分离子线程,接收数据线程 +#endif + vPrtLogMsg(LOG_DEBUG, errno, "connect link the recv socket fd is: %d", g_IecCtrl.sockid); + ht_pthread_create_background(&thread_handle_minor_recv, thread_recv_proc, NULL); + ht_pthread_create_background(&thread_handle_minor_active, thread_active_proc, NULL); +#ifndef _WIN32 + pthread_detach(thread_handle_minor_recv); + pthread_detach(thread_handle_minor_active); +#endif + _SLEEP(1000); + bWarn = true; + vDelIEC104StationsWarnMesg(); // 链路自动恢复后,处理链路告警:以处理 + } + g_Tcp.tcpCloseSocket(g_IecCtrl.sockid); + g_IecCtrl.isConnect = false; + vPrtLogMsg(LOG_DEBUG, errno, "shutdown thread_client_proc, server_addr=%s:%d, socket_id: %d", g_TConfig.getRemoteAddr(), g_TConfig.getRemotePort(), g_IecCtrl.sockid); + + return NULL; +} + +/*************************************************************************** +** function name : thread_recv_proc +** deacription : 104 parser thread +** parameter : none +** return code : NULL +***************************************************************************/ +void* thread_recv_proc(void * arg) +{ + unsigned char szBuf[MAX_SBUFF_TCP] = { 0 }; + int recv_buflen = 0, pkgLen = 0; + + vPrtLogMsg(LOG_DEBUG, 0, "thread_recv_proc = %d startup, sockid=%d...", GETTID(), g_IecCtrl.sockid); + + while (g_Running && g_IecCtrl.isConnect) + { + pthread_testcancels(); + memset(szBuf, 0x00, sizeof(szBuf)); + recv_buflen = -1; + pkgLen = 0; + recv_buflen = g_Tcp.tcpRecvBuffer(g_IecCtrl.sockid, (char*)szBuf, MAX_SBUFF_TCP, 5); // t1 = 15s + if (recv_buflen == ErrException || (recv_buflen == 0 && errno == 2)) { + vPrtLogMsg(LOG_WARNG, errno, "socket link exceptiond, do close sockid=%d,thread_recv_proc pthread_exit.", g_IecCtrl.sockid); + g_Tcp.clear_tcp_buffer(g_IecCtrl.sockid, MAX_SBUFF_TCP); + + mutex_lock(g_IecCtrl_mutex); + g_IecCtrl.isConnect = false; + g_IecCtrl.sockid = -1; + mutex_unlock(g_IecCtrl_mutex); + break; + } + else if (recv_buflen <= 0) { + vPrtLogMsg(LOG_WARNG, RET_OK,"socket link recv data length: %d,sockid=%d,errno=%d",recv_buflen,g_IecCtrl.sockid,errno); + //_SLEEP(g_TConfig.getTimeout1()); + continue; + } + + mutex_lock(g_IecCtrl_mutex); + g_IecCtrl.last_time = time(NULL); + mutex_unlock(g_IecCtrl_mutex); + + if (recv_buflen < (int)sizeof(ST_APCI)) // < 6byte + { + _SLEEP(1000); + continue; + } + if (recv_buflen >= (int)sizeof(ST_APCI) && (szBuf[0] & 0xff) == CMD_START_HEAD) // 0x68H + { + pkgLen = 1 + 1 + (szBuf[1] & 0xff); + if (recv_buflen < pkgLen) + continue; + } + AddParserList(szBuf, pkgLen); + vPrtLogHex(LOG_PACK, g_IecCtrl.sockid, PRT_PACK_RECV, (unsigned char*)szBuf, pkgLen); + g_Tcp.clear_tcp_buffer(g_IecCtrl.sockid, pkgLen); + } + vPrtLogMsg(LOG_DEBUG, RET_OK, "link exceptiond, thread_recv_proc pthread_exit."); + return NULL; +} + +//遥控执行 +void vYaoKongExe(unsigned char *msgbuf, unsigned int len) +{ + msgbuf[2] = 0x06; + msgbuf[9] = 0x01; + SendMsgFormatI(msgbuf, len); +} + +//遥控撤销 +void vYaoKongUnexe(unsigned char *msgbuf, unsigned int len) +{ + msgbuf[2] = 0x08; + SendMsgFormatI(msgbuf, len); +} + +// 遥信值入map +bool bSetPointTableValueYX(unsigned char v, unsigned int adr) +{ + bool is_state_opend = false; + unsigned char ovl = 0; + map::iterator m_pIter; + mutex_lock(g_map_iec_mutex); + m_pIter = g_map_iec.find(adr); + if (m_pIter != g_map_iec.end()) + { + if (((m_pIter->second).stype & 0xff) == 0x01) + { + (m_pIter->second).dtime = time(NULL)-1; + // 遥信量 + if ((v & 0xff) != ((m_pIter->second).cval & 0xff)) + { + ovl = ((m_pIter->second).cval & 0xff); // 上一次的状态值 + (m_pIter->second).cval = (v & 0xff); + g_IecCtrl.b_Signal = true; + g_IecCtrl.stime = time(NULL); + + // 根据配置文件GIS的点位配置,抓取断路器断开状态 + if (((ovl & 0xff) == YX_STAT_CLOSE) && ((v & 0xff) == YX_STAT_OPEND)) // 原状态位合1,且新状态位0,表示断路器断开一次 + { + addGisDataList(m_pIter->second); // 记录一次断开数据 + } + + if (((m_pIter->second).eqm_type & 0xff) == 0x01) + { + (m_pIter->second).wstate = getDevWorkState(m_pIter->second); // 刷新主变工作状态 + SethDevTimeStat((m_pIter->second).sys_code, (m_pIter->second).wstate); + vPrtLogMsg(LOG_DEBUG, RET_OK, "YX addr:%d(0x%04x) set val=%d, eqid=%s wstatue=%d", adr, adr, (m_pIter->second).cval, (m_pIter->second).eqm_code,(m_pIter->second).wstate); + (m_pIter->second).bfault = getDevFaultState(m_pIter->second); // 刷新主变事故状态 + } + + if (((m_pIter->second).eqm_type & 0xff) == 0x02) //刷新GIS开关开合状态 + { + flushDevCloseState((m_pIter->second).site_id, (m_pIter->second).eqm_code, (m_pIter->second).cval); + } + addOriginDataList(m_pIter->second); // 原始数据入库队列 + vPrtLogMsg(LOG_DEBUG, RET_OK, "YX addr:%d(0x%04x) set val=%d", adr, adr, (m_pIter->second).cval); + } + } + } + mutex_unlock(g_map_iec_mutex); + + return true; +} + +//遥测值入map +bool bSetPointTableValueYC(float v, unsigned int adr) +{ + map::iterator m_pIter; + double oldval = 0.0; + mutex_lock(g_map_iec_mutex); + m_pIter = g_map_iec.find(adr); + if (m_pIter != g_map_iec.end()) + { + if (((m_pIter->second).stype & 0xff) == 2) + { + // 遥测量 + oldval = (m_pIter->second).fval; + (m_pIter->second).dtime = time(NULL); + (m_pIter->second).fval = getChangeUnit((m_pIter->second).unit, (m_pIter->second).sysunit, v); // 单位转换 + if (((m_pIter->second).eqm_type & 0xff) == 1) + (m_pIter->second).wstate = cGetByqDeviceState((m_pIter->second).eqm_code); + else (m_pIter->second).wstate = cGetCurrentWorkState((m_pIter->second).eqm_code); // 获取工作状态 + + if (strlen((char*)(m_pIter->second).fieldname) > 0) { + addWarningTableList(m_pIter->second); + } +#ifdef _DEF_DB_CHANGERATE + #if 0 + if (g_IecCtrl.b_Signal) { + vPrtLogMsg(LOG_WARNG, RET_OK, "YC addr:%d(0x%04x) set val=%.4f, eqid=%s wstatue=%d", adr, adr, (m_pIter->second).fval, (m_pIter->second).eqm_code, (m_pIter->second).wstate); + addPingCeTableList(m_pIter->second); + g_IecCtrl.b_Signal = false; + } + else if (bCompare(oldval, (m_pIter->second).fval, (m_pIter->second).devrate) && !g_IecCtrl.b_Signal) + { // 计算偏差率 + //vPrtLogMsg(LOG_WARNG, RET_OK, "YC addr:%d(0x%04x) set val=%.4f-%.4f-%.4f, eqid=%s wstatue=%d", adr, adr, (m_pIter->second).fval,oldval,v, (m_pIter->second).eqm_code, (m_pIter->second).wstate); + vPrtLogMsg(LOG_WARNG, RET_OK, "bCompare olv=%.4f nlv=%.4f sadr=%d rate=%.2f",oldval,(m_pIter->second).fval, adr, (m_pIter->second).devrate); + addPingCeTableList(m_pIter->second); + } + + if ((time(NULL) - g_Internal_time) >= (g_iec_conf.save_internal)) { // 间隔时间保存数据 + vPrtLogMsg(LOG_WARNG, RET_OK, "(time(NULL) - g_Internal_time) =%d internal=%d", (time(NULL) - g_Internal_time), g_iec_conf.save_internal); + addPingCeTableList(m_pIter->second); + g_Internal_time = time(NULL); + } + #endif +#endif + if ((m_pIter->second).isget == 1 && bCompare(oldval, (m_pIter->second).fval, (m_pIter->second).devrate)) // 是否提取 + { + addOriginDataList(m_pIter->second); // 原始数据入库队列 + } + //vPrtLogMsg(LOG_DEBUG, RET_OK, "YC addr:%d(0x%04x) set val=%.4f", adr, adr, (m_pIter->second).fval); + } + } + mutex_unlock(g_map_iec_mutex); + return true; +} + +// I-Formar message decodification I格式帧解码 +// msgbuf : asdu_header + asdu_body +// len : asdu length +int DecodeMsgFormatI(unsigned char *msgbuf, unsigned int len, unsigned short sendno) +{ + ST_ASDU_HEADER *header = NULL; + ST_OBJVAL stObj[DEF_BUFFER_256]; + unsigned char num = 0, cause = 0, n = 0; + bool seq = false; + int asdu = 0, idx = 0; + unsigned int pos = 0; + unsigned int adr; + + //ASDU = type(1) + limit(1) + cause(2) + gaddr(2) + objaddr(3) + data(1) + data(1) + ... + memset(stObj, 0x00, sizeof(ST_OBJVAL)*DEF_BUFFER_256); + /* + 可变结构限定词 = VARIABLE STRUCTURE QUALIFIER :=CP8{ number、SQ } + number = N = 数目 := UI7[1..7]<0..127> + <0> :=应用服务数据单元不含信息对象 + <1..127>:=应用服务数据单元信息元素(单个信息元素或同类信息元素组合)的数目 + SQ = 单个或者顺序 := BS1[8]<0..1> + <0> :=寻址同一种类型的许多信息对象中单个的信息元素或者信息元素的集合 + <1> :=寻址 ASDU 单个信息对象中顺序的单个信息元素信息元素的同类集合 + SQ<0>和 N<0..127>∶ = 信息对象的数目 I (一个地址+一个元素)*N + SQ<1>和 N<0..127>∶ = 每个应用服务数据单元中单个对象的信息元素或者信息元素的 + 集合的数目 j (一个地址,后接连续 N 个元素) + SQ 位规定寻址后续信息对象或单个信息元素 / 信息元素集合的方法。 + SQ := 0 由信息对象地址寻址的单个信息元素或信息元素集合。应用服务数据单 + 元可以由一个或者多个同类的信息对象所组成。数目 N 是一个二进制数,它定义了信息对 + 象的数目。 + SQ := 1 单个信息元素或者信息元素同类集合的序列(即同一种格式测量值)由信 + 息对象地址来寻址(见 IEC 60870 - 5 - 3 中的 5.1.5), 信息对象地址是顺序单个信息元素或者信 + 息元素集合的第一个信息元素或者集合的地址。后续单个信息元素或者信息元素集合的地 + 址是从这个地址起顺序加 1。数目 N 是一个二进制数,它定义了单个信息元素或者信息元 + 素集合的数目。在顺序单个信息元素或者信息元素集合的情况下每个应用服务数据单元仅 + 安排一个信息对象. + */ + header = (ST_ASDU_HEADER*)msgbuf; + num = header->qual & 0x7F; // number of objects. 数据元素个数 + if (header->qual & 0x80) // sequence of objects ? 限定词,第一位:1=报文包含信息数据,0=不含信息数据 + seq = true; + /* + 传送原因 = CAUSE OF TRANSMISSION := CP16{ Cause, P / N, T, Originator Address(opt) } + 其中 Cause :=UI6[1..6]<0..63> + <0> := 未定义 + <1..63> :=传送原因序号 + <1..47> :=本配套标准的标准定义(兼容范围)见表 14 + <48..63> := 专用范围 + P/N ∶ = BS1[7]<0..1> + <0> ∶ = 肯定确认 + <1> ∶ = 否定确认 + T=test := BS1[8]<0..1> + <0>:=未试验 + <1>:=试验 + Originator address = 源发者地址 := UI8[9..16] + <0>:=缺省值 + <1..255> :=源发者地址号 + */ + cause = header->tx_cause1; // cause of tx.传送原因[1] + + /* 公共地址 :=UI16[1..16]<0..65535> + 其中 <0> :=未用 + <1..65534> :=站地址 + <65535> :=全局地址 */ + asdu = header->commom_asdu2 << 8; + asdu |= header->commom_asdu1; + if (asdu != g_iec_conf.iec_global_addr) { + vPrtLogMsg(LOG_ERROR, RET_FAIL, "Recv Common Address=0x%04x(%d), Dest Common Address=0x%04x(%d), Plase Checking Config!", asdu, asdu,g_iec_conf.iec_global_addr,g_iec_conf.iec_global_addr); + } + vPrtLogMsg(LOG_DEBUG, RET_OK, "ASDU Common Address=0x%04x(%d) TypeID=%d Count=%d Cause=%d(0x%02x) seq=%d", asdu, asdu, header->type, num, cause, cause, seq); + + // type identification + // 第一个八位位组为类型标识(图 11), 它定义了后续信息对象的结构、类型和格式。 + // 类型标识定义如下: + // 类型标识=TYPE IDENTIFICATION:=UI8[1..8]<1..255> + // bit 8 7 6 5 4 3 2 1 + // 信息对象是否带时标由标识类型的不同序号来区分。 + // 控制站将舍弃那些类型标识未被定义的应用服务数据单元 + // 类型标识值<0>未用,在本配套标准中定义了 1 至 127 的值,128 至 255 未定义。136 + // 至 255 可以由此标准的使用者彼此独立的进行定义,仅当使用具有类型标识号为 1 至 127 + // 的范围的应用服务数据单元才能达到全部互操作。 + switch (header->type & 0xff) + { + case 1: // Single-point information + SIQ104 *pi; + if (seq) // 有序数据,即信息体地址[3]+data[1]+data[1]+... + { + SP104 *ps = (SP104*)&msgbuf[sizeof(ST_ASDU_HEADER)]; + adr = ps->addr2 << 16 | ps->addr1 << 8 | ps->addr0; + vPrtLogMsg(LOG_DEBUG, RET_OK, "type=%d SP_%d: val:%d Valid: %d Blocked: %d", header->type, adr, ps->siq.spi, ps->siq.iv, ps->siq.bl); + bSetPointTableValueYX(ps->siq.spi, adr); + pos = sizeof(ST_ASDU_HEADER)+sizeof(SP104); + while (pos < len) + { + adr++; + pi = (SIQ104*)&msgbuf[pos]; + bSetPointTableValueYX(pi->spi, adr); + if (pi->spi) + vPrtLogMsg(LOG_DEBUG, RET_OK, "type=%d SP_%d: val:%d Valid: %d Blocked: %d", header->type, adr, pi->spi, pi->iv, pi->bl); + pos++; + } + if (cause == 3) SendMsgFormatS(sendno); // 突发上送之后发送S帧确认 + } + else { // 数据格式,即信息体地址[3]+data[1]+信息体地址[3]+data[1]+信息体地址[3]+data[1]+... + pos = sizeof(ST_ASDU_HEADER); + while (pos < len) + { + SP104 *ps = (SP104*)&msgbuf[pos]; + adr = ps->addr2 << 16 | ps->addr1 << 8 | ps->addr0; + bSetPointTableValueYX(ps->siq.spi, adr); + vPrtLogMsg(LOG_DEBUG, RET_OK, "type=%d SP_%d: val:%d Valid: %d Blocked: %d", header->type, adr, ps->siq.spi, ps->siq.iv, ps->siq.bl); + pos += sizeof(SP104); + } + } + break; + case 9: // 短整数,测量值,归一化值 + pos = sizeof(ST_ASDU_HEADER); + if (seq) + { // 连续的值序列,常见于总召报文,即:3位信息地址+[2字节的值+1字节的品质位]+[2字节的值+1字节的品质位]+[2字节的值+1字节的品质位]... + SH104 *pf = (SH104*)&msgbuf[pos]; + adr = pf->addr2 << 16 | pf->addr1 << 8 | pf->addr0; + short *val = (short*)&pf->val[0]; + bSetPointTableValueYC(*val, adr); + vPrtLogMsg(LOG_DEBUG, RET_OK, "type=%d SFP_%d(0x%04x): val:%d OFlow: %d Valid: %d Blocked: %d", header->type, adr, adr, *val, pf->qds.ov, pf->qds.iv, pf->qds.bl); + pos += sizeof(SH104); + adr++; + while (pos < len) + { + SHP104 *pfv = (SHP104*)&msgbuf[pos]; + short *val = (short*)&pfv->val[0]; + bSetPointTableValueYC(*val, adr); + if (*val) + vPrtLogMsg(LOG_DEBUG, RET_OK, "type=%d SFP_%d(0x%04x): val:%d OFlow: %d Valid: %d Blocked: %d", header->type, adr, adr, *val, pfv->qds.ov, pfv->qds.iv, pfv->qds.bl); + pos += sizeof(SHP104); + adr++; + } + } + else { // 当有变化量时,连续的值序列,即:3位信息地址+[2字节的值+1字节的品质位]+3位信息地址+[2字节的值+1字节的品质位]+... + while (pos < len) + { + SH104 *pf = (SH104*)&msgbuf[pos]; + adr = pf->addr2 << 16 | pf->addr1 << 8 | pf->addr0; + short *val = (short*)&pf->val[0]; + bSetPointTableValueYC(*val, adr); + vPrtLogMsg(LOG_DEBUG, RET_OK, "type=%d SFP_%d(0x%04x): val:%d OFlow: %d Valid: %d Blocked: %d", header->type, adr, adr, *val, pf->qds.ov, pf->qds.iv, pf->qds.bl); + pos += sizeof(SH104); + } + } + break; + case 13: // Measure value, short floating point value 短浮点数测量值 + pos = sizeof(ST_ASDU_HEADER); + if (seq) + { // 连续的值序列,常见于总召报文,即:3位信息地址+[4字节的值+1字节的品质位]+[4字节的值+1字节的品质位]+[4字节的值+1字节的品质位]... + SFP104 *pf = (SFP104*)&msgbuf[pos]; + adr = pf->addr2 << 16 | pf->addr1 << 8 | pf->addr0; + float *val = (float*)&pf->val[0]; + bSetPointTableValueYC(*val, adr); + vPrtLogMsg(LOG_DEBUG, RET_OK, "type=%d SFP_%d(0x%04x): val:%-.4f OFlow: %d Valid: %d Blocked: %d", header->type, adr, adr, *val, pf->qds.ov, pf->qds.iv, pf->qds.bl); + pos += sizeof(SFP104); + adr++; + while (pos < len) + { + SFP104V *pfv = (SFP104V*)&msgbuf[pos]; + float *val = (float*)&pfv->val[0]; + bSetPointTableValueYC(*val, adr); + vPrtLogMsg(LOG_DEBUG, RET_OK, "type=%d SFP_%d(0x%04x): val:%-.4f OFlow: %d Valid: %d Blocked: %d", header->type, adr, adr, *val, pfv->qds.ov, pfv->qds.iv, pfv->qds.bl); + pos += sizeof(SFP104V); + adr++; + } + } + else { // 当有变化量时,连续的值序列,即:3位信息地址+[4字节的值+1字节的品质位]+3位信息地址+[4字节的值+1字节的品质位]+... + while (pos < len) + { + SFP104 *pf = (SFP104*)&msgbuf[pos]; + adr = pf->addr2 << 16 | pf->addr1 << 8 | pf->addr0; + float *val = (float*)&pf->val[0]; + bSetPointTableValueYC(*val, adr); + vPrtLogMsg(LOG_DEBUG, RET_OK, "type=%d SFP_%d(0x%04x): val:%-.4f OFlow: %d Valid: %d Blocked: %d", header->type, adr, adr, *val, pf->qds.ov, pf->qds.iv, pf->qds.bl); + pos += sizeof(SFP104); + } + } + break; + case 30: // Single-point information with time tag CP56Time2a 带CP56时标的单点信息 + pos = sizeof(ST_ASDU_HEADER); + while (pos < len) + { + SP104_T *ps = (SP104_T*)&msgbuf[pos]; + adr = ps->addr2 << 16 | ps->addr1 << 8 | ps->addr0; + CP56Time *t = new CP56Time(ps->time); + char buf[32]; + t->GetTimeString(buf, sizeof(buf)); + bSetPointTableValueYX(ps->siq.spi, adr); + vPrtLogMsg(LOG_DEBUG, RET_OK, "type=%d SP_%d: val:%d Valid: %d Blocked: %d time:%s", header->type, adr, ps->siq.spi, ps->siq.iv, ps->siq.bl, buf); + pos += sizeof(SP104_T); + } + if (cause == 3) SendMsgFormatS(sendno); // 突发上送之后发送S帧确认 + break; + case 36: // Measure value, short float point value w/CP56Time2a 带CP56时标的短浮点测量值 + pos = sizeof(ST_ASDU_HEADER); + while (pos < len) + { + SFP104_T *pf = (SFP104_T*)&msgbuf[pos]; + adr = pf->addr2 << 16 | pf->addr1 << 8 | pf->addr0; + float *val = (float*)&pf->val[0]; + bSetPointTableValueYC(*val, adr); + CP56Time *t = new CP56Time(pf->time); + char buf[32]; + t->GetTimeString(buf, sizeof(buf)); + vPrtLogMsg(LOG_DEBUG, RET_OK, "type=%d SFP_%d: val:%-.4f OFlow: %d Valid: %d Blocked: %d time:%s", header->type, adr, *val, pf->qds.ov, pf->qds.iv, pf->qds.bl, buf); + pos += sizeof(SFP104_T); + } + break; + case 45: + if (cause == 7 && !g_IecCtrl.is_yk_ack) // 收到遥控返校 + { + vYaoKongExe(msgbuf, len);// 遥控执行 + g_IecCtrl.is_yk_ack = true; + } + else if (cause == 7 && g_IecCtrl.is_yk_ack) + { + vYaoKongUnexe(msgbuf, len); // 遥控撤销 + g_IecCtrl.is_yk_ack = false; + } + break; + case 70: //站端初始化结束 + pos = sizeof(ST_ASDU_HEADER); // 跳过ASDU的头,size=6byte + idx = 0; + while (pos < len) + { + memcpy(&stObj[idx].objaddr, msgbuf + pos, 3); // 信息对象地址,3字节 + stObj[idx].objaddr = ntohi(stObj[idx].objaddr); // 转高低位 + pos += 3; + if (pos >= len) break; + stObj[idx].objval = (msgbuf[pos] & 0xff); // 对象值, 0:电源合 1:电源分 + idx++; + pos++; + } + vPrtLogMsg(LOG_DEBUG, RET_OK, "End of Initialization"); + break; + case 100: // 总召唤命令 + if (cause == 7) // 激活确认 + vPrtLogMsg(LOG_DEBUG, RET_OK, "Recv Activation Confirmation, %d.", msgbuf[9] - 20); + else if (cause == 10) // 激活结束 + { + //SendMsgFormatS(sendno); // 总召唤结束发送S帧确认 + vPrtLogMsg(LOG_DEBUG, RET_OK, "Recv Activation Termination, %d", msgbuf[9] - 20); + } + else + vPrtLogMsg(LOG_DEBUG, RET_OK, "Recv Command:%d ,Unknown.", msgbuf[9] - 20); + break; + default: + vPrtLogMsg(LOG_WARNG, RET_OK, "Not Implemented!,type=%d", header->type); + break; + } + return RET_OK; +} +// 发送召唤激活 +int SendMsgFormatIAction(unsigned char cmd) +{ + int iRet = -1; + ST_IEC_APDU stPack; + + if (g_Tcp.tcpIsConnected(g_IecCtrl.sockid)) + { + memset(&stPack, 0x00, sizeof(ST_IEC_APDU)); + stPack.apci.start = 0x68; + stPack.apci.len = 0x0e; + stPack.apci.cntl1 = 0; // g_IecCtrl.usSendNo & 0xFF; + stPack.apci.cntl2 = 0;// (g_IecCtrl.usSendNo >> 8) & 0xFF; + stPack.apci.cntl3 = g_IecCtrl.usRecvNo & 0xFE; + stPack.apci.cntl4 = (g_IecCtrl.usRecvNo >> 8) & 0xFF; + stPack.asdu.header.type = cmd; + stPack.asdu.header.qual = 0x01; + stPack.asdu.header.tx_cause1 = 0x06; + stPack.asdu.header.tx_cause2 = 0x00; + stPack.asdu.header.commom_asdu1 = (g_iec_conf.iec_global_addr & 0xFF); + stPack.asdu.header.commom_asdu2 = (g_iec_conf.iec_global_addr >> 8) & 0xFF; + stPack.asdu.data[3] = 0x14; // 全召 + + iRet = g_Tcp.tcpSendBuffer(g_IecCtrl.sockid, (const char*)&stPack, stPack.apci.len + 2); + if (iRet == ErrException) + { + vPrtLogMsg(LOG_ERROR, iRet, "send mesg failed, sockid:%d msg:%s", g_IecCtrl.sockid, strerror(errno)); + g_Tcp.clear_tcp_buffer(g_IecCtrl.sockid, MAX_SBUFF_TCP); + g_Tcp.tcpCloseSocket(g_IecCtrl.sockid); + g_IecCtrl.isConnect = false; + } + g_IecCtrl.time_action = time(NULL); // 总召间隔时间更新 + vPrtLogHex(LOG_PACK, g_IecCtrl.sockid, PRT_PACK_SEND, (unsigned char*)&stPack, iRet); + } + return iRet; +} +void DecodeMsgFormatU(ST_APCI *p) +{ + switch (p->cntl1 & 0xfc) // 判断是否是U帧 + { + case CMD_STARTDT: // U启动 + SendMsgFormatU(CMD_STARTC); + break; + case CMD_STOPDT: // U停止 + SendMsgFormatU(CMD_STOPC); + break; + case CMD_TESTFR: // && !this->GetIsResponseUTest()) // U测试 + SendMsgFormatU(CMD_TESTC); + break; + case 0x08: // 启动确认位:=1 + SendMsgFormatIAction(CMD_CTL_64H); // 发送总召激活 + break; + default: + break; + } +} + +// S-Formar message decodification S格式帧解码 +void DecodeMsgFormatS(unsigned char *msgbuf, unsigned int len) +{ + int Index; + ST_IEC_APDU stPkg; + + memset(&stPkg, 0x00, sizeof(ST_IEC_APDU)); + + printf("\nFormatS"); + + Index = 0; + /* + NsIec104Struct.SendBuffer[0]=0x68; + NsIec104Struct.SendBuffer[1]=0x04; + NsIec104Struct.SendBuffer[2]=0x0b; + NsIec104Struct.SendBuffer[3]=0x00; + NsIec104Struct.SendBuffer[4]=0x00; + NsIec104Struct.SendBuffer[5]=0x00; + + NsIec104Struct.ReceiveHimNumber[0]=NsIec104Struct.ReceiveBuffer[2]; + NsIec104Struct.ReceiveHimNumber[1]=NsIec104Struct.ReceiveBuffer[3]; + + if((NsIec104Struct.SendLength = send(NsIec104Struct.NsNewSocketId,&NsIec104Struct.SendBuffer[0],6,0x0)) < 0) + { + perror ("Client Write Error"); + } + printf("\nwrite=%d",NsIec104Struct.SendLength); + */ +} +/*************************************************************************** +** function name : thread_parser_proc +** deacription : 104 parser thread +** parameter : none +** return code : NULL +***************************************************************************/ +void * thread_parser_proc(void * arg) +{ + int len = 0, j = 0; + ST_RECVPKG pData; + + vPrtLogMsg(LOG_DEBUG, 0, "thread_parser_proc = %d startup...", GETTID()); + while (g_Running) + { + pthread_testcancels(); + memset(&pData, 0x00, sizeof(ST_RECVPKG)); + mutex_lock(g_list_pack_mutex); + if (g_list_pack.empty() || g_list_pack.size() <= 0) { + mutex_unlock(g_list_pack_mutex); + _SLEEP(MAX_SLEEP_EMPTY * 10); + continue; + } + memcpy(&pData, &(g_list_pack.back()), sizeof(ST_RECVPKG)); // 由尾取出 + if (pData.pszBuff == NULL) { + mutex_unlock(g_list_pack_mutex); + _SLEEP(MAX_SLEEP_EMPTY * 10); + continue; + } + g_list_pack.pop_back(); // 由尾删除 + mutex_unlock(g_list_pack_mutex); + + // 解析104报文 + if (pData.iLength >= MIN_APDU_LENGTH && (pData.pszBuff[0] & 0xff) == CMD_START_HEAD) // 0x68H + { + ST_APCI *pstAPCI = (ST_APCI*)pData.pszBuff; + g_IecCtrl.usISUType = (pstAPCI->cntl1 & 0x03); + g_IecCtrl.usSendNo = (pstAPCI->cntl2 << 8) | pstAPCI->cntl1; // 发送序号 + g_IecCtrl.usRecvNo = (pstAPCI->cntl4 << 8) | pstAPCI->cntl3; // 接收序号 + + if (g_IecCtrl.usISUType == 0 || g_IecCtrl.usISUType == 2) // I Format package + { + g_IecCtrl.timer_S_Ackflag = false; // 收到I格式帧的时候置为false, 超时的时候置为true. + g_IecCtrl.RxCounter = g_IecCtrl.usSendNo++; + g_IecCtrl.LastAckTx = g_IecCtrl.usRecvNo; + SendMsgFormatS(pData.usSendNo); // 应答 + DecodeMsgFormatI(pData.pszBuff + sizeof(ST_APCI), pData.iLength - sizeof(ST_APCI), pData.usSendNo); // 解析报文 + } + else if (g_IecCtrl.usISUType == 1) // S Format package + { + //DecodeMsgFormatS(pData.pszBuff + sizeof(ST_APCI), pData.iLength - sizeof(ST_APCI)); + } + else if (g_IecCtrl.usISUType == 3) // U Format package + { + DecodeMsgFormatU(pstAPCI); // U Format package + g_IecCtrl.usRecvNo = (pstAPCI->cntl4 << 8) | pstAPCI->cntl3; + g_IecCtrl.LastAckTx = g_IecCtrl.usRecvNo; + } + g_IecCtrl.timer_U_Testflag = false; // 接收到I S U格式的帧都需要重置,表示t3内已经接收到报文 + } + free(pData.pszBuff); + pData.pszBuff = NULL; + } + return NULL; +} +// IEC原始数据入库 +void dbSet_iec_origin(char pszSql[][256], int count) +{ + CDBMySQL *pdbHandle = CDBMySQL::Instance(); + if (!pdbHandle) return; + vPrtLogMsg(LOG_DEBUG, RET_OK, "insert iec_origin_data start, cont: %d", count); + if (!pdbHandle->InsertRecordBitch(pszSql, count)) { + vPrtLogMsg(LOG_ERROR, RET_FAIL, "INSERT INTO iec_origin_data message failed."); + } + vPrtLogMsg(LOG_DEBUG, RET_OK, "insert iec_origin_data complater, insert top cont: %d", count); +} +/*************************************************************************** +** function name : thread_origin_proc +** deacription : 104 origin data save database thread +** parameter : none +** return code : NULL +***************************************************************************/ +void * thread_origin_proc(void * arg) +{ + int count = 0, n = 0; + ST_IECPOINT_TABLE pData; + char szSql[512] = { 0 }, szWaringTime[32] = { 0 }; + MYSQL *pMySql = NULL; + vPrtLogMsg(LOG_DEBUG, 0, "thread_origin_proc = %d startup...", GETTID()); + while (g_Running) + { + pthread_testcancels(); + memset(&pData, 0x00, sizeof(ST_IECPOINT_TABLE)); + mutex_lock(g_list_origin_mutex); + count = g_list_origin.size(); + n = 0; + if (g_list_origin.empty() || g_list_origin.size() <= 0) { + mutex_unlock(g_list_origin_mutex); + _SLEEP(MAX_SLEEP_EMPTY * 10); + continue; + } + //if (time(NULL) - g_IecCtrl.last_yc_time <= g_iec_conf.save_internal) // 超过设定保存时间间隔时 + //{ + // g_list_origin.clear(); + // mutex_unlock(g_list_origin_mutex); + // _SLEEP(MAX_SLEEP_EMPTY * 10); + // continue; + //} + //g_IecCtrl.last_yc_time = time(NULL); + + CDBMySQL *pdbHandle = CDBMySQL::Instance(); + if (!pdbHandle) { + mutex_unlock(g_list_origin_mutex); + _SLEEP(MAX_SLEEP_EMPTY * 5); + continue; + } + pMySql = pdbHandle->GetIdleMySql(); + if (pMySql == NULL) { + mutex_unlock(g_list_origin_mutex); + vPrtLogMsg(LOG_WARNG, RET_FAIL, "GetIdleMySql handle failed."); + _SLEEP(MAX_SLEEP_EMPTY); + continue; + } + pdbHandle->dbAutoCommit(pMySql, false); + for (int n = 0; n < (count > 500 ? 500 : count); n++) + { + memcpy(&pData, &(g_list_origin.back()), sizeof(ST_IECPOINT_TABLE)); // 由尾取出 + g_list_origin.pop_back(); // 由尾删除 + memset(szSql, 0x00, sizeof(szSql)); + vTranHostTimeFmt(pData.dtime, szWaringTime); + if ((pData.stype & 0xff) == 1) // 遥信 + { + snprintf(szSql, sizeof(szSql), "INSERT into iec_origin_data(id, site_id, site_tree_id,system_code,d_time,state,stype,sadr,sadrval) " + "VALUES(guuid(), '%s','%s', '%s','%s', '%d', '%d', '%d', '%d')", + pData.site_id, pData.eqm_code, pData.sys_code, szWaringTime, pData.wstate, pData.stype, pData.sadr, pData.cval); + } + else { // 遥测 + snprintf(szSql, sizeof(szSql), "INSERT into iec_origin_data(id, site_id, site_tree_id,system_code,d_time,state,stype,sadr,sadrval) " + "VALUES(guuid(), '%s','%s', '%s','%s', '%d','%d', '%d', '%.4f')", + pData.site_id, pData.eqm_code, pData.sys_code, szWaringTime, pData.wstate, pData.stype, pData.sadr, pData.fval); + } + pdbHandle->AddInsertRecord(pMySql, szSql); + } + pdbHandle->dbCommit(pMySql); + pdbHandle->dbAutoCommit(pMySql, true); + pdbHandle->SetIdleMysql(pMySql); + mutex_unlock(g_list_origin_mutex); + _SLEEP(MAX_SLEEP_EMPTY * 10); + } + return NULL; +} + +/*************************************************************************** +** function name : iGetSadrMatchRecord +** deacription : 104 addr point match list +** parameter : none +** return code : NULL +** relations table: ht_iec104.conf +***************************************************************************/ +int iGetSadrMatchRecord(unsigned int sadr, ST_SADR_MATCH &adrList) +{ + if(sadr <= 0) return 0 ; + map::iterator m_pIter; + mutex_lock(g_map_sadr_mutex); + m_pIter = g_map_sadr.find(sadr); + if (m_pIter == g_map_sadr.end()) + { + mutex_unlock(g_map_sadr_mutex); + return 0; + } + adrList.psadr = (ST_MATCH_LIST*)calloc(m_pIter->second.count, sizeof(ST_MATCH_LIST)); + if (adrList.psadr == NULL) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "calloc failed."); + mutex_unlock(g_map_sadr_mutex); + return 0; + } + adrList.count = (m_pIter->second).count; + adrList.eqm_type = (m_pIter->second).eqm_type; + for (int i = 0; i < (int)adrList.count; i++) + { + adrList.psadr[i].btype = (m_pIter->second).psadr[i].btype; + adrList.psadr[i].group = (m_pIter->second).psadr[i].group; + adrList.psadr[i].sadr = (m_pIter->second).psadr[i].sadr; + } + mutex_unlock(g_map_sadr_mutex); + + return adrList.count; +} +// 获取指定地址关系的组号记录 +static bool bGetSadrMatchRecord(int gorup, ST_SADR_MATCH &adrList) +{ + map::iterator m_pIter; + mutex_lock(g_map_sadr_mutex); + + for (m_pIter = g_map_sadr.begin(); m_pIter != g_map_sadr.end(); m_pIter++) + { + for (int i = 0; i < (int)(m_pIter->second).count; i++) + { + if ((int)((m_pIter->second).psadr[i].group) == gorup) + { + adrList.psadr = (ST_MATCH_LIST*)calloc((m_pIter->second).count, sizeof(ST_MATCH_LIST)); + if (adrList.psadr == NULL) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "calloc failed."); + mutex_unlock(g_map_sadr_mutex); + return false; + } + adrList.count = (m_pIter->second).count; + adrList.eqm_type = (m_pIter->second).eqm_type; + for (int i = 0; i < (int)adrList.count; i++) + { + adrList.psadr[i].btype = (m_pIter->second).psadr[i].btype; + adrList.psadr[i].group = (m_pIter->second).psadr[i].group; + adrList.psadr[i].sadr = (m_pIter->second).psadr[i].sadr; + } + mutex_unlock(g_map_sadr_mutex); + return true; + } + } + } + mutex_unlock(g_map_sadr_mutex); + return false; +} +// 提取数据 +static bool bGetValueBySadr(ST_IECPOINT_TABLE &stPoint) +{ + map::iterator m_pIter; + mutex_lock(g_map_iec_mutex); + m_pIter = g_map_iec.find(stPoint.sadr); + if (m_pIter == g_map_iec.end()) + { + mutex_unlock(g_map_iec_mutex); + return false; + } + memcpy(&stPoint, &m_pIter->second, sizeof(ST_IECPOINT_TABLE)); + mutex_unlock(g_map_iec_mutex); + return true; +} + +//初始化运行工况数据 +void initRunState(ST_IEC_BYQREAD_TABLE &stRunState, int dInitValue) +{ + stRunState.dcurJkyl[0] = dInitValue; // 冷却水进口压力值 + stRunState.dcurCkyl[0] = dInitValue; // 冷却水出口压力值 + stRunState.dCoolWaterTempEntry[0] = dInitValue; // 冷却水进口温度 + stRunState.dCoolWaterTempOuter[0] = dInitValue; // 冷却水出口温度 + + stRunState.dOilPressEntry[0] = dInitValue; // 变压器油进口油压力(MPa) + stRunState.dOilPressOuter[0] = dInitValue; // 变压器油出口油压力(MPa) + stRunState.dOilTempEntry[0] = dInitValue; // 变压器油进口温度 + stRunState.dOilTempOuter[0] = dInitValue; // 变压器油出口温度 + + stRunState.dOilTemperature[0] = dInitValue; //主变本体油温点位 + stRunState.dOilPosition[0] = dInitValue; //主变本体油位点位 + stRunState.dOilTopTemp[0] = dInitValue; //主变顶层油温点位 + stRunState.dOilPillowLevel[0] = dInitValue; //主变油枕油位点位 + stRunState.dWindingTemp[0] = dInitValue; //主变绕组温度点位 +} +//// 运行工况数据有效性转换 +//char * double2String(double value, double dInitValue, char *pStr, int iStrLen) +//{ +// memset(pStr, 0x00, iStrLen); +// if (value != dInitValue) sprintf(pStr, "%.4f", value); +// return pStr; +//} + +void calcHigthVolCurr(double &vol, double &curr) +{ + double dvol = vol; + double dcurr = curr; + vPrtLogMsg(LOG_WARNG, RET_OK, "vol = %.4f cur=%.4f", vol,curr); + //double dHvol = ((525000 * 1.732) / 3); // 高压侧电压=((额定电压*根号3)/3)=单相的电压值 + double dHvol = (525000 / 1.732); // 高压侧电压=((额定电压*根号3)/3)=单相的电压值 + curr = (dvol*dcurr) / dHvol; // 高压侧电流=(低压侧单相低压*低压侧单相电流)/高压侧电压 + vol = dHvol; +} + +// 组织变压器评测数据,iec_bydwork_param +static void vBuildByqWorkParam(ST_IECPOINT_TABLE &stIec,ST_SADR_MATCH &adrList) +{ + double dInitValue = -9999.0f; + ST_IEC_BYQWORK_TABLE stWork; + ST_IEC_BYQREAD_TABLE stRunState; + ST_IEC_GISBREAK_TABLE stGis; + // 数据入库 + char szSql[DEF_BUFFER_1K] = { 0 }; + char sTime[24] = { 0 }, eTime[24] = { 0 }; + + memset(&stWork, 0x00, sizeof(ST_IEC_BYQWORK_TABLE)); + memset(&stGis, 0x00, sizeof(ST_IEC_GISBREAK_TABLE)); + memset(&stRunState, 0x00, sizeof(ST_IEC_BYQREAD_TABLE)); + initRunState(stRunState, 0x00); + + CDBMySQL *pdbHandle = CDBMySQL::Instance(); + if (!pdbHandle) return; + int group = 0; + time_t st = 0; + //vPrtLogMsg(LOG_WARNG, RET_OK, "---------adrList.count:%d----------",adrList.count); + for (int i = 0; i < (int)adrList.count; i++) + { + memset(&stIec, 0x00, sizeof(ST_IECPOINT_TABLE)); + if (adrList.psadr[i].sadr <= 0) continue; // 0时,表示104配置文件中没有此点表地址 + stIec.sadr = adrList.psadr[i].sadr ; + if (false == bGetValueBySadr(stIec)) continue; + if (adrList.psadr[i].sadr == stIec.sadr) + { + group = adrList.psadr[i].group; + switch (group) + { + case 1: // 主变高压侧电流电压组 + case 2: // 主变低压侧电流电压组 + if (adrList.psadr[i].btype == 1) stWork.current = stIec.fval; + else if (adrList.psadr[i].btype == 2) stWork.voltage = stIec.fval; + if(strlen((const char *)stWork.eqm_code) <=0) + strncpy((char *)stWork.eqm_code, (char *)stIec.eqm_code, strlen((const char *)stIec.eqm_code)); + if (strlen((const char *)stWork.site_id) <= 0) + strncpy((char *)stWork.site_id, (char *)stIec.site_id, strlen((const char *)stIec.site_id)); + if (strlen((const char *)stWork.system_code) <= 0) + strncpy((char *)stWork.system_code, (char *)stIec.sys_code, strlen((const char *)stIec.sys_code)); + stWork.state = cGetByqDeviceState(stIec.eqm_code); // 获取工作状态 + if (st <= 0) + { + st = GetDevTimeStat(stIec.sys_code, &stWork.state) ; + vTranHostTimeFmt(st, sTime); + } + vTranHostTimeFmt(st, sTime); + g_IecCtrl.stime = time(NULL); + vTranHostTimeFmt(g_IecCtrl.stime, eTime); +#ifdef _DEF_HMF_VOLTAGE + // 根据低压侧的电压电流及额定高压侧电压,计算高压侧电流 + if (((stWork.state & 0xff) == 1) && (stWork.voltage >0 && stWork.current > 0)) { + calcHigthVolCurr(stWork.voltage, stWork.current); // 高压侧电压=((额定电压*根号3)/3)=单相的电压值 + } +#endif + //if (g_IecCtrl.stime > 0) { + // if (strlen(sTime) <= 0) + // { + // vTranHostTimeFmt(g_IecCtrl.stime, sTime); + // g_IecCtrl.stime = time(NULL); + // vTranHostTimeFmt(g_IecCtrl.stime, eTime); + // } + //} + //else { + // g_IecCtrl.stime = time(NULL); + // if (strlen(sTime) <= 0) vTranHostTimeFmt(g_IecCtrl.stime, sTime); + // vTranHostTimeFmt(time(NULL), eTime); + //} + stWork.is_fault = cGetByqDeviceFaultState(stIec.eqm_code); // 获取事故状态 + break; + case 3: // 主变运行工况组 + if (adrList.psadr[i].btype == 1) sprintf(stRunState.dcurJkyl,"%.4f",stIec.fval); // 进口水压力 + else if (adrList.psadr[i].btype == 2) sprintf(stRunState.dcurCkyl, "%.4f", stIec.fval); // 出口水压力 + else if (adrList.psadr[i].btype == 3) sprintf(stRunState.dOilPosition, "%.4f", stIec.fval); // 主变油位数 + else if (adrList.psadr[i].btype == 4) sprintf(stRunState.dOilTemperature, "%.4f", stIec.fval); // 主变油温数 + + else if (adrList.psadr[i].btype == 5) sprintf(stRunState.dCoolWaterTempEntry,"%.4f",stIec.fval); // 进口水温度 + else if (adrList.psadr[i].btype == 6) sprintf(stRunState.dCoolWaterTempOuter, "%.4f", stIec.fval); // 出口水温度 + else if (adrList.psadr[i].btype == 7) sprintf(stRunState.dOilPressEntry, "%.4f", stIec.fval); // 进口油压力 + else if (adrList.psadr[i].btype == 8) sprintf(stRunState.dOilPressOuter, "%.4f", stIec.fval); // 出口油压力 + + else if (adrList.psadr[i].btype == 9) sprintf(stRunState.dOilTempEntry, "%.4f", stIec.fval); // 进口油温度 + else if (adrList.psadr[i].btype == 10) sprintf(stRunState.dOilTempOuter, "%.4f", stIec.fval); // 出口油温度 + else if (adrList.psadr[i].btype == 11) sprintf(stRunState.dOilPillowLevel, "%.4f", stIec.fval);// 油枕油位 + else if (adrList.psadr[i].btype == 12) sprintf(stRunState.dOilTopTemp, "%.4f", stIec.fval); // 顶层油温度 + else if (adrList.psadr[i].btype == 13) sprintf(stRunState.dWindingTemp, "%.4f", stIec.fval); // 绕组温度 + + if (stRunState.chkTime <= 0) stRunState.chkTime = stIec.dtime; + else stRunState.chkTime = (stRunState.chkTime < stIec.dtime ? stRunState.chkTime : stIec.dtime); + + if (strlen((const char *)stRunState.eqm_code) <= 0) + strncpy((char *)stRunState.eqm_code, (char *)stIec.eqm_code, strlen((const char *)stIec.eqm_code)); + if (strlen((const char *)stRunState.site_id) <= 0) + strncpy((char *)stRunState.site_id, (char *)stIec.site_id, strlen((const char *)stIec.site_id)); + if (strlen((const char *)stRunState.system_code) <= 0) + strncpy((char *)stRunState.system_code, (char *)stIec.sys_code, strlen((const char *)stIec.sys_code)); + stRunState.wstate = cGetByqDeviceState(stIec.eqm_code); // 获取工作状态; + break; + case 4: // 断路器电流电压组 + if (adrList.psadr[i].btype == 1) stGis.abort_current = stIec.fval; // 断开电流 + else if (adrList.psadr[i].btype == 2) stGis.abort_voltage = stIec.fval; // 断开电压 + if (strlen((const char *)stGis.eqm_code) <= 0) + strncpy((char *)stGis.eqm_code, (char *)stIec.eqm_code, strlen((const char *)stIec.eqm_code)); + if (strlen((const char *)stGis.site_id) <= 0) + strncpy((char *)stGis.site_id, (char *)stIec.site_id, strlen((const char *)stIec.site_id)); + if (strlen((const char *)stGis.system_code) <= 0) + strncpy((char *)stGis.system_code, (char *)stIec.sys_code, strlen((const char *)stIec.sys_code)); + if (stGis.d_time <= 0) stGis.d_time = stIec.dtime; + else stGis.d_time = (stGis.d_time < stIec.dtime ? stGis.d_time : stIec.dtime); + // stGis.wstate = cGetByqDeviceState(stIec.eqm_code); // 获取工作状态; + stGis.wstate = cGetCurrentWorkState(stIec.eqm_code); + stGis.faild_rate = 0; + stGis.is_fault = stIec.bfault; + stGis.is_close = 1; // getGisOpenCloseState(stIec.site_id, stIec.eqm_code); + break; + default:; + } + } + } + // for test + //vGetHostTimeFmtBeforBay(sTime, 1); + //_SLEEP(1000); + //vGetHostTimeFmtBeforBay(eTime, 1); + // + switch (group) + { + case 1: // 主变高压侧电流电压组 + case 2: // 主变低压侧电流电压组 + snprintf(szSql, sizeof(szSql), "INSERT INTO iec_bydwork_param(id,site_id,eqm_code,system_code,stype,start_time,stop_time," + "higth_voltage,higth_current, record_date, ins_date) " + "VALUES(guuid(),'%s', '%s','%s', '%d', '%s', '%s', %.4f, %.4f, SYSDATE(), SYSDATE())", + stWork.site_id, stWork.eqm_code, stWork.system_code, stWork.state, sTime, eTime, stWork.voltage, stWork.current); + vPrtLogMsg(LOG_DEBUG, RET_OK, "iec_bydwork_param: %s", szSql); + break; + case 3: // 主变运行工况组 + // 原始数据入库 + vTranHostTimeFmt(stRunState.chkTime, sTime); + memset(szSql, 0x00, sizeof(szSql)); + snprintf(szSql, sizeof(szSql), + "INSERT INTO transformer_run_status(id, main_tree_id, system_code, work_status, d_time, oil_temperature, oil_position," + "cooling_entry_pressure, cooling_exit_pressure, coolingwater_inlet_temperature,coolingwater_outlet_temperature," + "circulating_oil_inlet_pressure,circulating_oil_outlet_pressure,top_oil_temperature,oilpillow_oillevel," + "winding_temperature,circulating_oil_inlet_temperature,circulating_oil_out_temperature,site_id) " + // "VALUES(guuid(),'%s','%s','%d','%s', '%.4f', '%.4f', '%.4f', '%.4f','%.4f', '%.4f', '%.4f', '%.4f','%.4f', '%.4f', '%.4f','%s')", + "VALUES(guuid(),'%s','%s','%d','%s', '%s', '%s', '%s', '%s','%s', '%s', '%s', '%s','%s', '%s', '%s','%s','%s','%s')", + stRunState.eqm_code, stRunState.system_code, stRunState.wstate, sTime, + stRunState.dOilTemperature, + stRunState.dOilPosition, + stRunState.dcurJkyl, + stRunState.dcurCkyl, + stRunState.dCoolWaterTempEntry, + stRunState.dCoolWaterTempOuter, + stRunState.dOilPressEntry, + stRunState.dOilPressOuter, + stRunState.dOilTopTemp, + stRunState.dOilPillowLevel, + stRunState.dWindingTemp, + stRunState.dOilTempEntry, + stRunState.dOilTempOuter, + stRunState.site_id); + vPrtLogMsg(LOG_DEBUG, RET_OK, "transformer_run_status: %s", szSql); + break; + case 4: // 断路器电流电压组 + memset(szSql, 0x00, sizeof(szSql)); + vTranHostTimeFmt(stGis.d_time, sTime); + snprintf(szSql, sizeof(szSql), "INSERT INTO iec_breaker_param(id,site_id,eqm_code,system_code,d_time,work_state,abort_current,abort_voltage,faild_rate," + "is_fault,is_break,ins_date) " + "VALUES(guuid(),'%s', '%s', '%s','%s','%d',%.4f, %.4f, %.4f, '%d', '%d',SYSDATE())", + stGis.site_id, stGis.eqm_code, stGis.system_code, sTime, stGis.wstate, stGis.abort_current, stGis.abort_voltage, stGis.faild_rate, + stGis.is_fault, stGis.is_close); + vPrtLogMsg(LOG_DEBUG, RET_OK, "iec_breaker_param: %s", szSql); + break; + default:; + } + if (!pdbHandle->InsertRecord((const char *)szSql)) { + vPrtLogMsg(LOG_ERROR, RET_FAIL, "INSERT INTO SQL message failed,SQL:%s", szSql); + } + //vPrtLogMsg(LOG_WARNG, RET_FAIL, "INSERT INTO SQL message failed,SQL:%s", szSql); +} + +// 保存断路器断开数据记录 +static void saveGisData(ST_BREAK_EQM_CODE &stGisRecord, unsigned char cState, time_t dtime) +{ + ST_IEC_GISBREAK_TABLE stGis; + ST_IECPOINT_TABLE stIec; + char szSql[DEF_BUFFER_1K] = { 0 }, szTime[32] = { 0 }; + + CDBMySQL *pdbHandle = CDBMySQL::Instance(); + if (!pdbHandle) return; + + memset(&stGis, 0x00, sizeof(ST_IEC_GISBREAK_TABLE)); + memset(&stIec, 0x00, sizeof(ST_IECPOINT_TABLE)); + + stIec.sadr = stGisRecord.uiAbortCurrent; + if (true == bGetValueBySadr(stIec)) // 获取电流值 + stGis.abort_current = stIec.fval; + + memset(&stIec, 0x00, sizeof(ST_IECPOINT_TABLE)); + stIec.sadr = stGisRecord.uiAbortVoltage; + if (true == bGetValueBySadr(stIec)) // 获取电压值 + stGis.abort_voltage = stIec.fval; + + strncpy((char *)stGis.eqm_code, (char *)stIec.eqm_code, strlen((const char *)stIec.eqm_code)); + strncpy((char *)stGis.site_id, (char *)stIec.site_id, strlen((const char *)stIec.site_id)); + strncpy((char *)stGis.system_code, (char *)stIec.sys_code, strlen((const char *)stIec.sys_code)); + stGis.wstate = cGetCurrentWorkState(stIec.eqm_code); + stGis.faild_rate = 0; + stGis.is_fault = 0; + stGis.is_close = cState; + + vTranHostTimeFmt(dtime, szTime); + + snprintf(szSql, sizeof(szSql), "INSERT INTO iec_breaker_param(id,site_id,eqm_code,system_code,d_time,work_state,abort_current,abort_voltage,faild_rate," + "is_fault,is_break,ins_date) " + "VALUES(guuid(),'%s', '%s', '%s','%s','%d',%.4f, %.4f, %.4f, '%d', '%d',SYSDATE())", + stGis.site_id, stGis.eqm_code, stGis.system_code, szTime, stGis.wstate, stGis.abort_current, stGis.abort_voltage, stGis.faild_rate, + stGis.is_fault, stGis.is_close); + if (!pdbHandle->InsertRecord((const char *)szSql)) { + vPrtLogMsg(LOG_ERROR, RET_FAIL, "INSERT INTO SQL message failed,SQL:%s", szSql); + } +} + +/*************************************************************************** +** function name : thread_gis_hold_proc +** deacription : 记录断路器断开时的电流电压及状态 +** parameter : none +** return code : NULL +** relations table: iec_break_param +***************************************************************************/ +void * thread_gis_hold_proc(void * arg) +{ + ST_IECPOINT_TABLE pData; + ST_BREAK_EQM_CODE stGisRecord; + map::iterator m_pIter; + vPrtLogMsg(LOG_DEBUG, 0, "thread_gis_hold_proc = %d startup...", GETTID()); + while (g_Running) + { + pthread_testcancels(); + memset(&pData, 0x00, sizeof(ST_IECPOINT_TABLE)); + mutex_lock(g_list_pingce_mutex); + if (g_list_pingce.empty() || g_list_pingce.size() <= 0) { + mutex_unlock(g_list_pingce_mutex); + _SLEEP(MAX_SLEEP_EMPTY * 10); + continue; + } + memcpy(&pData, &(g_list_pingce.back()), sizeof(ST_IECPOINT_TABLE)); // 由尾取出 + g_list_pingce.pop_back(); // 由尾删除 + mutex_unlock(g_list_pingce_mutex); + + // 根据遥信的点位地址,获取该点对应的电流电压点位及其数据 + mutex_lock(g_map_gis_state_mutex); + memset(&stGisRecord, 0x00, sizeof(ST_BREAK_EQM_CODE)); + + m_pIter = g_map_gis_state.find(pData.sadr); + if (m_pIter == g_map_gis_state.end()) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "yx_asdr=%d can not config info, do not nothing", pData.sadr); + continue; + } + memcpy(&stGisRecord, &(m_pIter->second), sizeof(ST_BREAK_EQM_CODE)); + mutex_lock(g_map_gis_state_mutex); + + saveGisData(stGisRecord, pData.cval, pData.dtime); + } + return NULL; +} + +/*************************************************************************** +** function name : thread_pingce_proc +** deacription : 104 pingce data save database thread +** parameter : none +** return code : NULL +** relations table: iec_bydwork_param,iec_breaker_param,transformer_run_status +***************************************************************************/ +#ifdef _DEF_DB_CHANGERATE +void * thread_pingce_proc(void * arg) +{ + ST_IECPOINT_TABLE pData; + ST_SADR_MATCH adrList; + vPrtLogMsg(LOG_DEBUG, 0, "thread_pingce_proc = %d startup...", GETTID()); + while (g_Running) + { + pthread_testcancels(); + #if 0 + memset(&pData, 0x00, sizeof(ST_IECPOINT_TABLE)); + mutex_lock(g_list_pingce_mutex); + if (g_list_pingce.empty() || g_list_pingce.size() <= 0) { + mutex_unlock(g_list_pingce_mutex); + _SLEEP(MAX_SLEEP_EMPTY * 10); + continue; + } + memcpy(&pData, &(g_list_pingce.back()), sizeof(ST_IECPOINT_TABLE)); // 由尾取出 + g_list_pingce.pop_back(); // 由尾删除 + mutex_unlock(g_list_pingce_mutex); + #endif + // 发生信号有变化且发生时间>0时,立即提取数据,加入数据入库队列中 + if (!g_IecCtrl.b_Signal && (time(NULL) - g_Internal_time < g_iec_conf.save_internal)) { // 超过设定保存时间间隔时 + _SLEEP(100); + continue; + } + if (!g_IecCtrl.isConnect) { + _SLEEP(100); + continue; + } + g_IecCtrl.b_Signal = false; + g_Internal_time = time(NULL); + + memset(&pData, 0x00, sizeof(ST_IECPOINT_TABLE)); + memset(&adrList, 0x00, sizeof(ST_SADR_MATCH)); + for (int i = 0; i < (int)g_iec_conf.iec_byq_count; i++) + { + for(int group = 2; group <= 3; group++) + { + switch(group) + { + case 1: // + //pData.sadr = g_iec_conf.pstByqCode[i].uiHighCurrent; //uiLowCurrent + //break; + case 2: + //pData.sadr = g_iec_conf.pstByqCode[i].uiLowCurrent; //uiLowCurrent + + if (1 == cGetByqDeviceState(g_iec_conf.pstByqCode[i].szEqmCode)) // 获取工作状态 + pData.sadr = g_iec_conf.pstByqCode[i].uiHighCurrent; //uiHighCurrent 发电时取高压侧电流电压 + else pData.sadr = g_iec_conf.pstByqCode[i].uiLowCurrent; //uiLowCurrent 抽水/空闲时取低压侧电流电压 + break; + case 3: + pData.sadr = g_iec_conf.pstByqCode[i].uiWindingTemp; //uiLowCurrent + break; + default: { + pData.sadr = 0 ; + continue; + } + } + if (iGetSadrMatchRecord(pData.sadr, adrList) > 0) + { + vBuildByqWorkParam(pData, adrList); + if (adrList.psadr) + { + free((void *)adrList.psadr); + adrList.psadr = NULL; + } + } + pData.sadr = 0 ; + } + } + for (int i = 0; i < (int)g_iec_conf.iec_break_count; i++) + { + pData.sadr = g_iec_conf.pstBrkCode[i].uiAbortCurrent; + if (iGetSadrMatchRecord(pData.sadr, adrList) > 0) + { + vBuildByqWorkParam(pData, adrList); + if (adrList.psadr) + { + free((void *)adrList.psadr); + adrList.psadr = NULL; + } + } + pData.sadr = 0; + } + } + return NULL; +} +#else +/*************************************************************************** +** function name : thread_pingce_proc +** deacription : 104 pingce data save database thread +** parameter : none +** return code : NULL +** relations table: iec_bydwork_param,iec_breaker_param,transformer_run_status +***************************************************************************/ +void * thread_pingce_proc(void * arg) +{ + ST_IECPOINT_TABLE pData; + ST_SADR_MATCH adrList; + time_t interTime = time(NULL); + _SLEEP(3000); + vPrtLogMsg(LOG_DEBUG, 0, "thread_pingce_proc = %d startup...", GETTID()); + while (g_Running) + { + pthread_testcancels(); + + // 发生信号有变化且发生时间>0时,立即提取数据,加入数据入库队列中 + if (!g_IecCtrl.b_Signal && (time(NULL) - interTime < g_iec_conf.save_internal)) { // 超过设定保存时间间隔时 + _SLEEP(100); + continue; + } + if (!g_IecCtrl.isConnect) { + _SLEEP(100); + continue; + } + g_IecCtrl.b_Signal = false; + interTime = time(NULL); + for (int i = 1; i < 5; i++) + { + memset(&adrList, 0x00, sizeof(ST_SADR_MATCH)); + if (!bGetSadrMatchRecord(i, adrList)) continue; + vPrtLogMsg(LOG_WARNG, RET_FAIL, "get addr match relations, group=%d", i); + vBuildByqWorkParam(pData, adrList); + if (adrList.psadr) + { + free((void *)adrList.psadr); + adrList.psadr = NULL; + } + } + } + return NULL; +} +#endif +/*************************************************************************** +** function name : thread_warn_proc +** deacription : 104 warn data save database thread +** parameter : none +** return code : NULL +***************************************************************************/ +void * thread_warn_proc(void * arg) +{ + int count = 0, n = 0; + double warn_val = 0.0f; // 告警值 + bool isWarn = false; + ST_IECPOINT_TABLE pData; + char szSql[1024] = { 0 }, szWaringTime[32] = { 0 }; + char szWarninfo[256] = { 0 }, szMsg[24] = { 0 }; // 告警信息 + vPrtLogMsg(LOG_DEBUG, 0, "thread_warn_proc = %d startup...", GETTID()); + while (g_Running) + { + pthread_testcancels(); + memset(&pData, 0x00, sizeof(ST_IECPOINT_TABLE)); + mutex_lock(g_list_warn_mutex); + count = g_list_warn.size(); + n = 0; + if (g_list_warn.empty() || g_list_warn.size() <= 0) { + mutex_unlock(g_list_warn_mutex); + _SLEEP(MAX_SLEEP_EMPTY * 10); + continue; + } + + CDBMySQL *pdbHandle = CDBMySQL::Instance(); + if (!pdbHandle) { + mutex_unlock(g_list_warn_mutex); + _SLEEP(MAX_SLEEP_EMPTY * 5); + continue; + } + isWarn = false; + memcpy(&pData, &(g_list_warn.back()), sizeof(ST_IECPOINT_TABLE)); // 由尾取出 + g_list_warn.pop_back(); // 由尾删除 + mutex_unlock(g_list_warn_mutex); + + memset(szSql, 0x00, sizeof(szSql)); + + // 获取告警值 + warn_val = getThresholdValuse((const char*)pData.eqm_code, (const char*)pData.fieldname, pData.wstate); + if (warn_val <= 0) + { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "Noset asdr=%d warn_val=%.4f nval=%.4f eqm_code=%s wstate=%d warn_field=%s", + pData.sadr, warn_val, pData.fval, pData.eqm_code, pData.wstate, pData.fieldname); + continue; // 告警值为0时,无效 + } + vPrtLogMsg(LOG_DEBUG, RET_OK, "findmatch warninfo, asdr=%d warn_val=%.4f nval=%.4f eqm_code=%s wstate=%d warn_field=%s", + pData.sadr, warn_val, pData.fval, pData.eqm_code, pData.wstate, pData.fieldname); + // 判断是否有告警 + if (pData.warnFlag == 1) // 大于预警值时报警 + { + if (pData.fval > warn_val && pData.fval != HT_INVALID_VALUE) { + isWarn = true; + vTranHostTimeFmt(pData.dtime, szWaringTime); + strcpy(szMsg, "超标"); + } + } + else if (pData.warnFlag == 2) // 小于预警值时报警 + { + if (pData.fval < warn_val && pData.fval != HT_INVALID_VALUE) { + isWarn = true; + vTranHostTimeFmt(pData.dtime, szWaringTime); + strcpy(szMsg, "未达标"); + } + } + if (isWarn) + { + char szUuid[64] = { 0 }; + getuuid(szUuid, sizeof(szUuid)); + // 插入告警表 + snprintf(szSql, sizeof(szSql), "INSERT INTO busi_eqm_warning(id,eq_type,gz_type,warning_msg,warning_time,create_time,del_flag,site_id,main_tree_id,run_state) " + "VALUES('%s','1', '9', '%s%s:%.4f', '%s', SYSDATE(), '1', '%s', '%s','%d')", + szUuid, pData.poidesc, szMsg, pData.fval, szWaringTime, pData.site_id, pData.eqm_code,pData.wstate); + if (!pdbHandle->InsertRecord((const char *)szSql)) { + vPrtLogMsg(LOG_ERROR, RET_FAIL, "INSERT INTO busi_eqm_warning message failed,SQL:%s", szSql); + } + vPrtLogMsg(LOG_DEBUG, RET_OK, "INSERT INTO busi_eqm_warning message,SQL:%s", szSql); + + // 插入告警详细表 + char szSubUuid[64] = { 0 }; + getuuid(szSubUuid, sizeof(szSubUuid)); + memset(szSql, 0x00, sizeof(szSql)); + + snprintf(szWarninfo, sizeof(szWarninfo), + "{\"sadr\":\"%d\",\"thresholdValue\":\"%.4f\",\"currRealValue\":\"%.4f\",\"equipmentStatus\":\"%d\",\"collectDate\":\"%s\",\"ipaddr\":\"\",\"port\":\"\",\"msgInfo\":\"%s\",\"phase\":\"\"}", + pData.sadr, warn_val, pData.fval,pData.wstate, szWaringTime,pData.poidesc); + snprintf(szSql, sizeof(szSql), "INSERT INTO busi_eqm_warning_info(id,warning_id,warning_info) VALUES('%s','%s','%s')",szSubUuid, szUuid, szWarninfo); + if (!pdbHandle->InsertRecord((const char *)szSql)) { + vPrtLogMsg(LOG_ERROR, RET_FAIL, "INSERT INTO busi_eqm_warning_info message failed,SQL:%s", szSql); + } + vPrtLogMsg(LOG_DEBUG, RET_OK, "INSERT INTO busi_eqm_warning_info message,SQL:%s", szSql); + } + } + return NULL; +} + +// 负荷数据数据入库 +void dbSet_iec_bydwork_param(ST_IEC_BYQWORK_TABLE *pstWork) +{ + /* + bool isChange = false; // 数据是否和最近一次的发生变化了 + map::iterator m_pIter; + if (!pstWork->eqm_code && strlen((char*)pstWork->eqm_code) <= 0) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "dbSet_iec_bydwork_param: eqm_code is null,please check config file or coding."); + return; + } + + mutex_lock(g_map_byq_mutex); + m_pIter = g_map_byq.find((char*)pstWork->eqm_code); + if (m_pIter != g_map_byq.end()) + { + if (pstWork->current > 0 && pstWork->current != (m_pIter->second).stLastData.current) + { + isChange = true; // 数据是否和最近一次的发生变化了 + (m_pIter->second).stLastData.current = pstWork->current; //覆盖最近一次的数据 + } + else pstWork->current = (m_pIter->second).stLastData.current; // 沿用最近一次的数据 + + if (pstWork->voltage > 0 && pstWork->voltage != (m_pIter->second).stLastData.voltage) + { + isChange = true; // 数据是否和最近一次的发生变化了 + (m_pIter->second).stLastData.voltage = pstWork->voltage; + } + else pstWork->voltage = (m_pIter->second).stLastData.voltage; + + if ((pstWork->stype & 0xff) > 0x00) { + if ((pstWork->stype & 0xff) == (m_pIter->second).stLastData.stype) + pstWork->start_time = (m_pIter->second).stLastData.start_time; // 状态未发生变化,则开始时间沿用之前的时间 + else { + (m_pIter->second).stLastData.stype = (pstWork->stype & 0xff); // 状态发生了变化 + pstWork->start_time = time(NULL); + (m_pIter->second).stLastData.start_time = time(NULL); + isChange = true; + } + } + else { + pstWork->stype = (m_pIter->second).stLastData.stype; // 状态=0时,无效值,沿用上一次的状态。 + pstWork->start_time = (m_pIter->second).stLastData.start_time; + } +#ifdef _SITE_ID_TYPE_INT + if (pstWork->site_id <= 0) pstWork->site_id = (m_pIter->second).SiteID; +#else + if (strlen((const char*)pstWork->site_id) <= 0) + strcpy((char*)pstWork->site_id , (const char*)((m_pIter->second).SiteID)); +#endif + pstWork->stop_time = time(NULL); + */ + // if (isChange) // 数据未有变化,无需入库操作 +// if ((pstWork->state & 0xff) != 0x03) +// { +// // 数据入库 +// char szSql[DEF_BUFFER_1K] = { 0 }; +// char sTime[21] = { 0 }, eTime[21] = { 0 }; +// CDBMySQL *pdbHandle = CDBMySQL::Instance(); +// if (!pdbHandle) return; +// +// vTranHostTimeFmt(pstWork->start_time, sTime); +// vTranHostTimeFmt(pstWork->stop_time, eTime); +//#ifdef _SITE_ID_TYPE_INT +// sprintf(szSql, "INSERT INTO iec_bydwork_param(site_id,eqm_code,stype,start_time,stop_time," +// "higth_voltage,higth_current, record_date, ins_date) " +// "VALUES(%d, '%s', %d, '%s', '%s', %.2f, %.2f, SYSDATE(), SYSDATE())", +// pstWork->site_id, pstWork->eqm_code, pstWork->state, sTime, eTime, pstWork->voltage, pstWork->current); +//#else +// snprintf(szSql, sizeof(szSql), "INSERT INTO iec_bydwork_param(site_id,eqm_code,stype,start_time,stop_time," +// "higth_voltage,higth_current, record_date, ins_date) " +// "VALUES('%s', '%s', %d, '%s', '%s', %.2f, %.2f, SYSDATE(), SYSDATE())", +// pstWork->site_id, pstWork->eqm_code, pstWork->state, sTime, eTime, pstWork->voltage, pstWork->current); +//#endif +// vPrtLogMsg(LOG_DEBUG, RET_OK, "iec_bydwork_param: %s", szSql); +// if (!pdbHandle->InsertRecord((const char *)szSql)) { +// vPrtLogMsg(LOG_ERROR, RET_FAIL, "INSERT INTO iec_bydwork_param message failed,SQL:%s", szSql); +// } +// } + //} + //mutex_unlock(g_map_byq_mutex); + return; +} +// 运行工况数据入库 +//void dbSet_transformer_read_table(ST_IEC_BYQREAD_TABLE *pstRung) +//{ +// bool isChange = false; // 数据是否和最近一次的发生变化了 +// bool bcurYWei = false, bcurYWen = false, bcurJkyl = false, bcurCkyl = false; +// map::iterator m_pIter; +// if (!pstRung->eqm_code && strlen((char*)pstRung->eqm_code) <= 0) { +// vPrtLogMsg(LOG_WARNG, RET_FAIL, "dbSet_transformer_read_table: eqm_code is null,please check config file or coding."); +// return; +// } +// mutex_lock(g_map_byq_mutex); +// m_pIter = g_map_byq.find((char*)pstRung->eqm_code); +// if (m_pIter != g_map_byq.end()) +// { /* +//#ifdef _SITE_ID_TYPE_INT +// if (pstRung->site_id <= 0) +// pstRung->site_id = (m_pIter->second).SiteID; +//#else +// if (strlen((const char*)pstRung->site_id) <= 0) +// strcpy((char*)pstRung->site_id, (const char*)((m_pIter->second).SiteID)); +//#endif +// if (pstRung->dcurYWei > 0 && pstRung->dcurYWei != (m_pIter->second).stLastData.dcurYWei) { +// (m_pIter->second).stLastData.dcurYWei = pstRung->dcurYWei; +// isChange = true; +// } +// else pstRung->dcurYWei = (m_pIter->second).stLastData.dcurYWei; +// +// if (pstRung->dcurYWen > 0 && pstRung->dcurYWen != (m_pIter->second).stLastData.dcurYWen) { +// (m_pIter->second).stLastData.dcurYWen = pstRung->dcurYWen; //覆盖最近一次的数据 +// isChange = true; // 发生变化了,需入库 +// } +// else pstRung->dcurYWen = (m_pIter->second).stLastData.dcurYWen; +// +// if (pstRung->dcurJkyl > 0 && pstRung->dcurJkyl != (m_pIter->second).stLastData.dcurJkyl) { +// (m_pIter->second).stLastData.dcurJkyl = pstRung->dcurJkyl; //覆盖最近一次的数据 +// isChange = true; // 发生变化了,需入库 +// } +// else pstRung->dcurJkyl = (m_pIter->second).stLastData.dcurJkyl; +// +// if (pstRung->dcurCkyl > 0 && pstRung->dcurCkyl != (m_pIter->second).stLastData.dcurCkyl) { +// (m_pIter->second).stLastData.dcurCkyl = pstRung->dcurCkyl; //覆盖最近一次的数据 +// isChange = true; // 发生变化了,需入库 +// } +// else pstRung->dcurCkyl = (m_pIter->second).stLastData.dcurCkyl; +// if (!isChange) return; // 数据未有变化,无需入库操作 +// */ +// vPrtLogMsg(LOG_DEBUG, RET_OK, "eqm_code:%s Warn_YWei:%.2f Warn_YWen:%.2f Warn_Ckyl:%.2f Warn_Jkyl:%.2f", +// pstRung->eqm_code, (m_pIter->second).dYWei, (m_pIter->second).dYWen, (m_pIter->second).dCkyl, (m_pIter->second).dJkyl); +// vPrtLogMsg(LOG_DEBUG, RET_OK, "eqm_code:%s Curr_YWei:%.2f Curr_YWen:%.2f Curr_Ckyl:%.2f Curr_Jkyl:%.2f", +// pstRung->eqm_code, pstRung->dcurYWei, pstRung->dcurYWen, pstRung->dcurCkyl, pstRung->dcurJkyl); +// // 告警判断 +// if (pstRung->dcurYWei > (m_pIter->second).dYWei) bcurYWei = true; +// if (pstRung->dcurYWen > (m_pIter->second).dYWen) bcurYWen = true; +// if (pstRung->dcurJkyl < (m_pIter->second).dJkyl) bcurJkyl = true; // 压力值小于预警值时报警 +// if (pstRung->dcurCkyl < (m_pIter->second).dCkyl) bcurCkyl = true; // 压力值小于预警值时报警 +// +// // 数据入库 +// char szSql[DEF_BUFFER_1K] = { 0 }, szTime[21] = { 0 }; +// CDBMySQL *pdbHandle = CDBMySQL::Instance(); +// if (!pdbHandle) { +// mutex_unlock(g_map_byq_mutex); +// return; +// } +// // 实时在线缓存表更新数据 +// snprintf(szSql, sizeof(szSql), "update transformer_read_table_cache set d_time=SYSDATE(),oil_temperature='%.2f'," +// "oil_position = '%.2f', cooling_entry_pressure = '%.2f', cooling_exit_pressure = '%.2f', site_eqm_code = '%s' " +// "where site_id = '%s' and eqmid = '%s'", +// pstRung->dcurYWen, pstRung->dcurYWei, pstRung->dcurJkyl, pstRung->dcurCkyl, pstRung->eqm_code, pstRung->site_id, pstRung->eqm_code); +// vPrtLogMsg(LOG_DEBUG, RET_OK, "transformer_read_table_cache: %s", szSql); +// if (!pdbHandle->UpdateRecord((const char *)szSql)) { +// vPrtLogMsg(LOG_ERROR, RET_FAIL, "UPDATE transformer_read_table_cache failed,SQL:%s", szSql); +// memset(szSql, 0x00, sizeof(szSql)); +// snprintf(szSql, sizeof(szSql), "INSERT INTO transformer_read_table_cache(eqmid,d_time,oil_temperature,oil_position,cooling_entry_pressure," +// "cooling_exit_pressure,site_id,site_eqm_code) " +// "VALUES('%s',SYSDATE(), '%.2f', '%.2f', '%.2f', '%.2f', '%s','%s')", +// pstRung->eqm_code, pstRung->dcurYWen, pstRung->dcurYWei, pstRung->dcurJkyl, pstRung->dcurCkyl, pstRung->site_id, pstRung->eqm_code); +// vPrtLogMsg(LOG_DEBUG, RET_OK, "transformer_read_table_cache: %s", szSql); +// if (!pdbHandle->InsertRecord((const char *)szSql)) { +// vPrtLogMsg(LOG_ERROR, RET_FAIL, "INSERT INTO transformer_read_table_cache message failed,SQL:%s", szSql); +// } +// } +// // 原始数据入库 +// memset(szSql, 0x00, sizeof(szSql)); +// snprintf(szSql, sizeof(szSql), "INSERT INTO transformer_read_table(eqmid,d_time,oil_temperature,oil_position,cooling_entry_pressure," +// "cooling_exit_pressure,site_id) " +// "VALUES('%s',SYSDATE(), '%.2f', '%.2f', '%.2f', '%.2f', '%s')", +// pstRung->eqm_code, pstRung->dcurYWen, pstRung->dcurYWei, pstRung->dcurJkyl, pstRung->dcurCkyl, pstRung->site_id); +// vPrtLogMsg(LOG_DEBUG, RET_OK, "transformer_read_table: %s", szSql); +// if (!pdbHandle->InsertRecord((const char *)szSql)) { +// vPrtLogMsg(LOG_ERROR, RET_FAIL, "INSERT INTO transformer_read_table message failed,SQL:%s", szSql); +// } +// vTranHostTimeFmt(pstRung->chkTime, szTime); +// +// // 报警入库 +// if (bcurYWei) { // 油位告警 +//#ifdef _SITE_ID_TYPE_INT +// sprintf(szSql, "INSERT INTO busi_eqm_warning(eq_type,gz_type,warning_msg,warning_time,create_time,del_flag,site_id," +// "eqm_code) " +// "VALUES('1', '9', '主变油位超标:%.2f(L)', '%s', SYSDATE(), '0', '%d', '%s')", +// pstRung->dcurYWei, szTime, pstRung->site_id, pstRung->eqm_code); +//#else +// snprintf(szSql, sizeof(szSql), "INSERT INTO busi_eqm_warning(eq_type,gz_type,warning_msg,warning_time,create_time,del_flag,site_id," +// "eqm_code) " +// "VALUES('1', '9', '主变油位超标:%.2f(L)', '%s', SYSDATE(), '0', '%s', '%s')", +// pstRung->dcurYWei, szTime, pstRung->site_id, pstRung->eqm_code); +//#endif +// vPrtLogMsg(LOG_DEBUG, RET_OK, "dbInsertWaring: %s", szSql); +// if (!pdbHandle->InsertRecord((const char *)szSql)) { +// vPrtLogMsg(LOG_ERROR, RET_FAIL, "INSERT INTO busi_eqm_warning message failed,SQL:%s", szSql); +// } +// } +// if (bcurYWen) { // 油温告警 +//#ifdef _SITE_ID_TYPE_INT +// sprintf(szSql, "INSERT INTO busi_eqm_warning(eq_type,gz_type,warning_msg,warning_time,create_time,del_flag,site_id," +// "eqm_code) " +// "VALUES('1', '9', '主变油温超标:%.2f(℃)', '%s', SYSDATE(), '0', '%d', '%s')", +// pstRung->dcurYWen, szTime, pstRung->site_id, pstRung->eqm_code); +//#else +// snprintf(szSql, sizeof(szSql), "INSERT INTO busi_eqm_warning(eq_type,gz_type,warning_msg,warning_time,create_time,del_flag,site_id," +// "eqm_code) " +// "VALUES('1', '9', '主变油温超标:%.2f(℃)', '%s', SYSDATE(), '0', '%s', '%s')", +// pstRung->dcurYWen, szTime, pstRung->site_id, pstRung->eqm_code); +//#endif +// vPrtLogMsg(LOG_DEBUG, RET_OK, "dbInsertWaring: %s", szSql); +// if (!pdbHandle->InsertRecord((const char *)szSql)) { +// vPrtLogMsg(LOG_ERROR, RET_FAIL, "INSERT INTO busi_eqm_warning message failed,SQL:%s", szSql); +// } +// } +// if (bcurJkyl) { // 进口压力告警 +//#ifdef _SITE_ID_TYPE_INT +// sprintf(szSql, "INSERT INTO busi_eqm_warning(eq_type,gz_type,warning_msg,warning_time,create_time,del_flag,site_id," +// "eqm_code) " +// "VALUES('1', '9', '主变冷却水进口压力未达标:%.2f(MPa)', '%s', SYSDATE(), '0', '%d', '%s')", +// pstRung->dcurJkyl, szTime, pstRung->site_id, pstRung->eqm_code); +//#else +// snprintf(szSql, sizeof(szSql), "INSERT INTO busi_eqm_warning(eq_type,gz_type,warning_msg,warning_time,create_time,del_flag,site_id," +// "eqm_code) " +// "VALUES('1', '9', '主变冷却水进口压力未达标:%.2f(MPa)', '%s', SYSDATE(), '0', '%s', '%s')", +// pstRung->dcurJkyl, szTime, pstRung->site_id, pstRung->eqm_code); +//#endif +// vPrtLogMsg(LOG_DEBUG, RET_OK, "dbInsertWaring: %s", szSql); +// if (!pdbHandle->InsertRecord((const char *)szSql)) { +// vPrtLogMsg(LOG_ERROR, RET_FAIL, "INSERT INTO busi_eqm_warning message failed,SQL:%s", szSql); +// } +// } +// if (bcurCkyl) { // 出口压力告警 +//#ifdef _SITE_ID_TYPE_INT +// sprintf(szSql, "INSERT INTO busi_eqm_warning(eq_type,gz_type,warning_msg,warning_time,create_time,del_flag,site_id," +// "eqm_code) " +// "VALUES('1', '9', '主变冷却水出口压力未达标:%.2f(MPa)', '%s', SYSDATE(), '0', '%d', '%s')", +// pstRung->dcurCkyl, szTime, pstRung->site_id, pstRung->eqm_code); +//#else +// snprintf(szSql, sizeof(szSql), "INSERT INTO busi_eqm_warning(eq_type,gz_type,warning_msg,warning_time,create_time,del_flag,site_id," +// "eqm_code) " +// "VALUES('1', '9', '主变冷却水出口压力未达标:%.2f(MPa)', '%s', SYSDATE(), '0', '%s', '%s')", +// pstRung->dcurCkyl, szTime, pstRung->site_id, pstRung->eqm_code); +//#endif +// vPrtLogMsg(LOG_DEBUG, RET_OK, "dbInsertWaring: %s", szSql); +// if (!pdbHandle->InsertRecord((const char *)szSql)) { +// vPrtLogMsg(LOG_ERROR, RET_FAIL, "INSERT INTO busi_eqm_warning message failed,SQL:%s", szSql); +// } +// } +// } +// mutex_unlock(g_map_byq_mutex); +// return; +//} + +//void dbSet_iec_breaker_param(ST_IEC_GISBREAK_TABLE *pData) +//{ +// // 数据入库 +// char szSql[DEF_BUFFER_1K] = { 0 }; +// char sTime[21] = { 0 }, eTime[21] = { 0 }; +// CDBMySQL *pdbHandle = CDBMySQL::Instance(); +// if (!pdbHandle) return; +// +// vTranHostTimeFmt(pData->d_time, sTime); +//#ifdef _SITE_ID_TYPE_INT +// snprintf(szSql,sizeof(szSql), "INSERT INTO iec_breaker_param(site_id,gis_code,eqm_code,abort_current,abort_voltage,faild_rate," +// "record_date,ins_date, is_fault,is_break) " +// "VALUES(%d, '%s', '%s', %.2f, %.2f, %.2f, '%s',SYSDATE(), '%d','%d')", +// pData->site_id, pData->eqm_code, pData->eqm_code, pData->abort_current, pData->abort_voltage, pData->faild_rate, +// sTime, pData->is_fault,pData->is_close); +//#else +// snprintf(szSql, sizeof(szSql), "INSERT INTO iec_breaker_param(site_id,gis_code,eqm_code,abort_current,abort_voltage,faild_rate," +// "record_date,ins_date, is_fault,is_break) " +// "VALUES('%s', '%s', '%s', %.2f, %.2f, %.2f, '%s',SYSDATE(), '%d','%d')", +// pData->site_id, pData->eqm_code, pData->eqm_code, pData->abort_current, pData->abort_voltage, pData->faild_rate, +// sTime, pData->is_fault, pData->is_close); +//#endif +// vPrtLogMsg(LOG_DEBUG, RET_OK, "iec_breaker_param: %s", szSql); +// if (!pdbHandle->InsertRecord((const char *)szSql)) { +// vPrtLogMsg(LOG_ERROR, RET_FAIL, "INSERT INTO iec_breaker_param message failed,SQL:%s", szSql); +// } +// return; +//} + + +// 刷新设备运行状态:1:发电、2:抽水、3:空闲 +unsigned char getDevWorkState(ST_IECPOINT_TABLE &stIec) +{ + bool b_byq_close = false, b_byq_fd = false, b_byq_cs = false; + unsigned char cstate = 0; + int s = 0; //对于一个主变设备的状态检索,最多找到3个遥信量即可,不必检索所有的map列表 + unsigned char szMEqmCode[DEF_EQM_CODE_SIZE] = { 0 }; // 主变ID + + // 先根据设备类型区分出是主变设备/GIS设备 + if ((stIec.eqm_type & 0xff) != 1) // GIS设备 + { + //根据隶属关系表,获取主设备id + if (!bGetMasterEqmCodeBySubEqmCode(stIec.site_id, stIec.eqm_code, szMEqmCode)) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "from s_eqm_code:%s find m_eqm_code, not found,please check device build relations...", stIec.eqm_code); + return 0x03; // 默认置为空闲状态 + } + } + else { + strcpy((char*)szMEqmCode, (const char*)stIec.eqm_code); // 主变ID + } + + //根据主变设备ID,找出其遥信地址,然后依据遥信量(3个)确定设备的工作状态 + map::iterator m_pIter; + for (m_pIter = g_map_iec.begin(); ((m_pIter != g_map_iec.end()) && (s < 3)); m_pIter++) + { + if (((m_pIter->second).stype & 0xff) != 1) continue; // 确定遥信量 + if (((m_pIter->second).eqm_type & 0xff) != 1) continue; // 变压器设备 + if (0 != stringncasecmp((const char*)((m_pIter->second).site_id), (const char*)stIec.site_id, strlen((const char*)stIec.site_id))) continue; + if (0 != stringncasecmp((const char*)((m_pIter->second).eqm_code), (const char*)szMEqmCode, strlen((const char*)szMEqmCode))) continue; + switch ((m_pIter->second).isget) + { + case 1: // 1号发电机出口开关合位 + if (((m_pIter->second).cval & 0xff) == 0x01) b_byq_close = true; + s++; + break; + case 2: // 1号机组发电换相刀闸合位 + if (((m_pIter->second).cval & 0xff) == 0x01) b_byq_fd = true; + s++; + break; + case 3: // 1号机组抽水换相刀闸合位 + if (((m_pIter->second).cval & 0xff) == 0x01) b_byq_cs = true; + s++; + break; + default:; + } + } + // 判断开关量 + if (b_byq_close) + { + if (b_byq_fd) cstate = 0x01; // 发电状态 + else if (b_byq_cs) cstate = 0x02; // 抽水状态 + else cstate = 0x03; // 空闲状态 + } + else cstate = 0x03; // 空闲状态 + flushDevWorkState(stIec.site_id, szMEqmCode, cstate); + vSetByqDeviceState(szMEqmCode, cstate); + return cstate; +} + +// 主变设备运行事故状态: 4:机组电气事故 +unsigned char getDevFaultState(ST_IECPOINT_TABLE &stIec) +{ + unsigned char isFault = 0; + map::iterator m_pIter; + + if (stIec.eqm_type != 1) return 0; // 只有主变有事故状态. + for (m_pIter = g_map_iec.begin(); m_pIter != g_map_iec.end(); m_pIter++) + { + if ((m_pIter->second).stype != 1) continue; // 不是遥信量 + if (0 != stringncasecmp((const char*)(m_pIter->second).site_id, (const char*)stIec.site_id, strlen((const char*)stIec.site_id))) continue; + if (0 != stringncasecmp((const char*)(m_pIter->second).eqm_code, (const char*)stIec.eqm_code, strlen((const char*)stIec.eqm_code))) continue; + switch ((m_pIter->second).isget) + { + case 4: // 1号机组电气事故,1:事故 0:正常 + if (((m_pIter->second).cval & 0xff) == 0x01) { + isFault = 1; + } + default:; + } + } + flushFaultState(stIec.site_id, stIec.eqm_code, isFault); + vSetByqDeviceFaultState(stIec.eqm_code, isFault); + return isFault; +} + + +// 提取运行工况数据 +//static void dbGet_transformer_read_table(ST_IEC_BYQREAD_TABLE &stRung, ST_IEC_BYQWORK_TABLE &stFuHe) +//{ +// bool b_byq_close = false, b_byq_fd = false, b_byq_cs = false; +// //bool b_gis_close = false; +// float hCurr = 0, lcurr = 0, hvol = 0, lvol = 0; +// map::iterator m_pIter; +// mutex_lock(g_map_iec_mutex); +// stFuHe.real_time = time(NULL); +// //stFuHe.start_time = g_IecCtrl.stime > 0 ? g_IecCtrl.stime : time(NULL); // 开始时间 +// stRung.chkTime = time(NULL); +// for (m_pIter = g_map_iec.begin(); m_pIter != g_map_iec.end(); m_pIter++) +// { +// if (memcmp((m_pIter->second).eqm_code, stRung.eqm_code, strlen((char*)stRung.eqm_code))) continue; +// if (0 != stringncasecmp((const char*)(m_pIter->second).site_id, (const char*)stRung.site_id, strlen((const char*)stRung.site_id))) continue; +// switch ((m_pIter->second).sadr) +// { +// case 1: case 5: case 9: case 13: // 1号发电机出口开关合位 +// if( ( (m_pIter->second).cval & 0xff) == 0x01) b_byq_close = true; +// break; +// case 2: case 6: case 10: case 14: // 1号机组发电换相刀闸合位 +// if (((m_pIter->second).cval & 0xff) == 0x01) b_byq_fd = true; +// break; +// case 3: case 7:case 11:case 15: // 1号机组抽水换相刀闸合位 +// if (((m_pIter->second).cval & 0xff) == 0x01) b_byq_cs = true; +// break; +// case 4: case 8: case 12: case 16: // 1号机组电气事故 +// stFuHe.is_fault = ((m_pIter->second).cval & 0xff); +// break; +// // // GIS的遥信数据 +// //case 17: case 18: case 19: // GIS开关合位B相 0:分 1:合 +// // if (((m_pIter->second).cval & 0xff) == 0x01) b_gis_close = true; +// // break; +// +// // 以下是遥测量 +// case 16385: case 16406: case 16427: case 16448: //主变高压侧A相电流(A) +// break; +// case 16386: case 16407: case 16428: case 16449: //主变高压侧B相电流(A) +// hCurr = (m_pIter->second).fval; +// break; +// case 16387: case 16408: case 16429: case 16450: //主变高压侧C相电流(A) +// break; +// +// case 16388: case 16409: case 16430: case 16451: //主变高压侧A相电压(kv) +// break; +// case 16389: case 16410: case 16431: case 16452: //主变高压侧B相电压(kv) +// hvol = ((m_pIter->second).fval * 1000); +// stFuHe.voltage = ((m_pIter->second).fval * 1000); +// break; +// case 16390: case 16411: case 16432: case 16453: //主变高压侧C相电压(kv) +// break; +// +// case 16391: case 16412: case 16433: case 16454: //机组出口A相电压(kv) +// break; +// case 16392: case 16413: case 16434: case 16455: //机组出口B相电压(kv) +// lvol = ((m_pIter->second).fval * 1000); +// break; +// case 16393: case 16414: case 16435: case 16456: //机组出口C相电压(kv) +// break; +// case 16394: case 16415: case 16436: case 16457: //机组出口A相电流(A) +// break; +// case 16395: case 16416: case 16437: case 16458: //机组出口B相电流(A) +// lcurr = (m_pIter->second).fval; +// break; +// case 16396: case 16417: case 16438: case 16459: //机组出口C相电流(A) +// break; +// case 16397: case 16418: case 16439: case 16460: //主变高压侧上层油温(℃) +// stRung.dcurYWen = (m_pIter->second).fval; +// break; +// case 16398: case 16419: case 16440: case 16461: //主变绕组温度(℃) +// break; +// case 16399: case 16420: case 16441: case 16462: //主变冷却系统进口水压(MPa) +// stRung.dcurJkyl = (m_pIter->second).fval; +// break; +// case 16400: case 16421: case 16442: case 16463: //主变冷却系统出口水压(MPa) +// stRung.dcurCkyl = (m_pIter->second).fval; +// break; +// case 16401: case 16422: case 16443: case 16464: //主变冷却器进口水温(℃) +// break; +// case 16402: case 16423: case 16444: case 16465: //主变冷却器出口水温(℃) +// break; +// case 16403: case 16424: case 16445: case 16466: //主变冷却系统油泵进口油温(℃) +// break; +// case 16404: case 16425: case 16446: case 16467: //主变冷却系统油泵出口油温(℃) +// break; +// case 16405: case 16426: case 16447: case 16468: //主变油位值(待定?)(L) +// stRung.dcurYWei = (m_pIter->second).fval; +// break; +// default: +// //vPrtLogMsg(LOG_WARNG, RET_OK, "this YX addr:%d(0x%04x) not define.", adr, adr); +// ; +// } // end switch +// }// end for +// // mutex_unlock(g_map_iec_mutex); +// +// // 判断开关量 +// if (b_byq_close) { +// if (b_byq_fd) stFuHe.state = 0x01; // 发电状态 +// else if (b_byq_cs) stFuHe.state = 0x02; // 抽水状态 +// else stFuHe.state = 0x03; // 空闲状态 +// } +// else stFuHe.state = 0x03; // 空闲状态 +// //m_pIter->second.wstate = stFuHe.state; +// +// mutex_unlock(g_map_iec_mutex); +// switch (stFuHe.state & 0xff) +// { +// case 0x02: // 抽水状态 +// stFuHe.current = lcurr; // 低压侧电流 +// stFuHe.voltage = lvol; // 低压侧电压 +// break; +// case 0x03: // 空闲状态,数据无需入库 +// g_IecCtrl.stime = 0; // 时间置0 +// g_IecCtrl.etime = 0; // 时间置0 +// break; +// case 0x01: // 发电状态 +// stFuHe.current = hCurr; // 高压侧电流 +// stFuHe.voltage = hvol; // 高压侧电压 +// break; +// default:; +// } +// stFuHe.stop_time = time(NULL); // 结束时间 +//} +// +//// 根据断路器的分组号,获取断路器的开关状态位 +////eqm_code : 遥信地址所配置的开关量,获取其各(A,B,C)相的开关量状态位 +//// 配置文件中的各断路器A相,点表配置其开关量,入参eqm_code位A相的编号 +////unsigned char cGetGisCloseStatusByEqmCodeA(unsigned int site_id, unsigned char *eqm_code) +//unsigned char cGetGisCloseStatusByEqmCodeA(htype site_id, unsigned char *eqm_code) +//{ +// unsigned char cStat = 0; +// map::iterator m_pIter; +// mutex_lock(g_map_iec_mutex); +// for (m_pIter = g_map_iec.begin(); m_pIter != g_map_iec.end(); m_pIter++) +// { +// if (memcmp((m_pIter->second).eqm_code, eqm_code, strlen((char*)eqm_code))) continue; +//#ifdef _SITE_ID_TYPE_INT +// if ((m_pIter->second).site_id != site_id) continue; +//#else +// if (stringncasecmp((const char*)(m_pIter->second).site_id, (const char*)site_id, strlen((const char*)site_id) != 0)) continue; +//#endif +// switch ((m_pIter->second).sadr) +// { +// case 17: case 18: case 19: // GIS开关合位B相 0:分 1:合 +// cStat = ((m_pIter->second).cval & 0xff); +// //if (((m_pIter->second).cval & 0xff) == 0x01) b_gis_close = true; +// break; +// default:; +// } +// } +// mutex_unlock(g_map_iec_mutex); +// return cStat; +//} +//void dbGet_gis_breaker_table(ST_IEC_GISBREAK_TABLE &stGis) +//{ +// //bool b_gis_close = false, b_gis_fault = false; +// +// map::iterator m_pIter; +// mutex_lock(g_map_iec_mutex); +// stGis.d_time = time(NULL); +// for (m_pIter = g_map_iec.begin(); m_pIter != g_map_iec.end(); m_pIter++) +// { +// if (memcmp((m_pIter->second).eqm_code, stGis.eqm_code, strlen((char*)stGis.eqm_code))) continue; +//#ifdef _SITE_ID_TYPE_INT +// if((m_pIter->second).site_id != stGis.site_id) continue; +//#else +// if (stringncasecmp((const char*)(m_pIter->second).site_id, (const char*)stGis.site_id, strlen((const char*)stGis.site_id) != 0)) continue; +//#endif +// switch ((m_pIter->second).sadr) +// { +// case 4: case 8: case 12: case 16: // 1号机组电气事故 +// stGis.is_fault = ((m_pIter->second).cval & 0xff); +// break; +// // GIS的遥信数据 +// case 17: case 18: case 19: // GIS开关合位B相 0:分 1:合 +// stGis.is_close = ((m_pIter->second).cval & 0xff); +// //if (((m_pIter->second).cval & 0xff) == 0x01) b_gis_close = true; +// break; +// +// // 以下是遥测量 +// case 16469: // 5001 - 1#电缆线A相电流(A) +// case 16470: // 5001 - 1#电缆线B相电流(A) +// case 16471: // 5001 - 1#电缆线C相电流(A) +// stGis.abort_current = (m_pIter->second).fval; +// break; +// case 16472: // 5003 - 1#电缆线A相电流(A) +// case 16473: // 5003 - 1#电缆线B相电流(A) +// case 16474: // 5003 - 1#电缆线C相电流(A) +// stGis.abort_current = (m_pIter->second).fval; +// break; +// case 16475: // 5051-A相电压(Kv) +// case 16476: // 5051-B相电压(Kv) +// case 16477: // 5051-C相电压(Kv) +// stGis.abort_voltage = ((m_pIter->second).fval * 1000); +// break; +// case 16478: // 5051-A相电流(A) +// case 16479: // 5051-B相电流(A) +// case 16480: // 5051-C相电流(A) +// stGis.abort_current = (m_pIter->second).fval; +// break; +// default: +// //vPrtLogMsg(LOG_WARNG, RET_OK, "this YX addr:%d(0x%04x) not define.", adr, adr); +// stGis.is_close = 0x01; //默认为合状态。 +// break; +// } // end switch +// }// end for +// if (stGis.abort_voltage <= 0) { +// m_pIter = g_map_iec.find(16476); // 默认取本设备的电压值,5051-B相电压(Kv) +// if (m_pIter != g_map_iec.end()) +// stGis.abort_voltage = ((m_pIter->second).fval * 1000); +// } +// mutex_unlock(g_map_iec_mutex); +// //if (g_IecCtrl.m_gis_count == 0) +// // g_IecCtrl.m_gis_count = 1; +// stGis.faild_rate = 0; // (float)((g_IecCtrl.m_gis_fault_count / g_IecCtrl.m_gis_count) * 100); +//} + +/*************************************************************************** +** function name : thread_setdb_proc +** deacription : db opration thread and mysql insert... +** parameter : none +** return code : NULL +***************************************************************************/ +//void *thread_setdb_proc(void *arg) +//{ +// vPrtLogMsg(LOG_DEBUG, 0, "thread_setdb_proc = %d startup...", GETTID()); + //ST_DB_DATA stdbSet; + + //while (g_Running) + //{ + // pthread_testcancels(); + // memset(&stdbSet, 0x00, sizeof(ST_DB_DATA)); + // mutex_lock(g_list_dbset_mutex); + // if (g_list_dbset.empty() || g_list_dbset.size() <= 0) { + // mutex_unlock(g_list_dbset_mutex); + // _SLEEP(MAX_SLEEP_EMPTY * 10); + // continue; + // } + // memcpy(&stdbSet, &(g_list_dbset.back()), sizeof(ST_DB_DATA)); // 由尾取出 + // if (stdbSet.pData == NULL) { + // mutex_unlock(g_list_dbset_mutex); + // _SLEEP(MAX_SLEEP_EMPTY * 10); + // continue; + // } + // g_list_dbset.pop_back(); // 由尾删除 + // mutex_unlock(g_list_dbset_mutex); + + // // 根据不同的数据类型分别入库 + // switch (stdbSet.ctype) + // { + // case 1: // 1:运行工况数据入库 transformer_read_table + // dbSet_transformer_read_table((ST_IEC_BYQREAD_TABLE*)stdbSet.pData); + // //dbSet_transformer_read_table_cache((ST_IEC_BYQREAD_TABLE*)stdbSet.pData); + // break; + // case 2: // 2:GIS负荷数据入库 iec_breaker_param + // dbSet_iec_breaker_param((ST_IEC_GISBREAK_TABLE*)stdbSet.pData); + // break; + // case 3: // 3:负荷数据入库 iec_bydwork_param + // dbSet_iec_bydwork_param((ST_IEC_BYQWORK_TABLE*)stdbSet.pData); + // break; + // default: + // break; + // } // end switch + // free(stdbSet.pData); + // stdbSet.pData = NULL; + //} +// return NULL; +//} +/*************************************************************************** +** function name : thread_getdata_proc +** deacription : from g_iec_map get data and add g_list_dbset. +** parameter : none +** return code : NULL +***************************************************************************/ +//void *thread_getdata_proc(void *arg) +//{ +// vPrtLogMsg(LOG_DEBUG, 0, "thread_getdata_proc = %d startup...", GETTID()); +// int i = 0; +// g_IecCtrl.lastTime = time(NULL); +// _SLEEP(1000 * 5); +// while (g_Running) +// { +// pthread_testcancels(); +// +// // 发生信号有变化且发生时间>0时,立即提取数据,加入数据入库队列中 +// if (!((g_IecCtrl.b_Signal || g_IecCtrl.m_gis_change) || (time(NULL) - g_IecCtrl.lastTime) > (60 * 5))) +// { +// _SLEEP(100); +// continue; +// } +// if (!g_IecCtrl.isConnect) { +// _SLEEP(100); +// continue; +// } +// +// //g_IecCtrl.stime = 0; +// g_IecCtrl.lastTime = time(NULL); +// for (i = 0; i < (int)g_iec_conf.iec_byq_count; i++) +// { +// // 1:运行工况数据入库 transformer_read_table +// ST_IEC_BYQREAD_TABLE stRung; +// ST_IEC_BYQWORK_TABLE stFuHe; +// memset(&stRung, 0x00, sizeof(ST_IEC_BYQREAD_TABLE)); +// memset(&stFuHe, 0x00, sizeof(ST_IEC_BYQWORK_TABLE)); +// strcpy((char*)stRung.site_id, (const char*)g_iec_conf.site_id); +// strcpy((char*)stFuHe.site_id, (const char*)g_iec_conf.site_id); +// strcpy((char*)stRung.eqm_code, (const char*)g_iec_conf.pstByqCode[i].szEqmCode); +// strcpy((char*)stFuHe.eqm_code, (const char*)g_iec_conf.pstByqCode[i].szEqmCode); +// if (g_iec_conf.pstByqCode[i].stime <= 0) { +// g_iec_conf.pstByqCode[i].stime = time(NULL)-10; +// stFuHe.start_time = time(NULL) - 10; +// } +// else { +// stFuHe.start_time = g_iec_conf.pstByqCode[i].etime>0 ? g_iec_conf.pstByqCode[i].etime:time(NULL); +// g_iec_conf.pstByqCode[i].stime = g_iec_conf.pstByqCode[i].etime; +// } +// dbGet_transformer_read_table(stRung, stFuHe); +// // 类型:1-运行工况 2:断路器数据 3:变压器负荷数据 +// addDbSetList((char*)&stRung, sizeof(ST_IEC_BYQREAD_TABLE), 0x01); +// //if (stFuHe.current < 100) { +// // vPrtLogMsg(LOG_WARNG, RET_OK, "%s: stype=%d,curr_val=%.2f(A) < 100(A),curr_vol:%.2f(V),not save.", +// // stFuHe.eqm_code, (stFuHe.stype & 0xff), stFuHe.current, stFuHe.voltage); +// // continue; +// //} +// if ((stFuHe.state & 0xff) == 0x01 || (stFuHe.state & 0xff) == 0x02) { // 空闲状态时的数据不入库。2019-01-08 +// g_iec_conf.pstByqCode[i].etime = time(NULL); +// stFuHe.stop_time = g_iec_conf.pstByqCode[i].etime; +// addDbSetList((char*)&stFuHe, sizeof(ST_IEC_BYQWORK_TABLE), 0x03); +// } +// if ((stFuHe.state & 0xff) == 0x03) { +// g_iec_conf.pstByqCode[i].stime = time(NULL); // g_iec_conf.pstByqCode[i].etime; +// g_iec_conf.pstByqCode[i].etime = time(NULL); +// } +// } +// +// //if (g_IecCtrl.b_Signal || g_IecCtrl.m_gis_change) // 有主变的开关变化或gis的开关变化时。 +// { +// for (i = 0; i < (int)g_iec_conf.iec_break_count; i++) +// { +// // 2:GIS负荷数据入库 iec_breaker_param +// ST_IEC_GISBREAK_TABLE stGis; +// memset(&stGis, 0x00, sizeof(ST_IEC_GISBREAK_TABLE)); +// strcpy((char*)stGis.site_id, (const char*)g_iec_conf.site_id); +// strcpy((char*)stGis.eqm_code, (const char*)g_iec_conf.pstBrkCode[i].szEqmCodeA); +// dbGet_gis_breaker_table(stGis); +// // 类型:1-运行工况 2:断路器数据 3:变压器负荷数据 +// //if ((stGis.is_close & 0xff) == 0x00) +// addDbSetList((char*)&stGis, sizeof(ST_IEC_GISBREAK_TABLE), 0x02); +// +// memset(&stGis, 0x00, sizeof(ST_IEC_GISBREAK_TABLE)); +//#ifdef _SITE_ID_TYPE_INT +// stGis.site_id = g_iec_conf.iec_site_id; +//#else +// strcpy((char*)stGis.site_id, (const char*)g_iec_conf.site_id); +//#endif +// strcpy((char*)stGis.eqm_code, (const char*)g_iec_conf.pstBrkCode[i].szEqmCodeB); +// dbGet_gis_breaker_table(stGis); +// stGis.is_close = cGetGisCloseStatusByEqmCodeA(stGis.site_id, g_iec_conf.pstBrkCode[i].szEqmCodeA); +// // 类型:1-运行工况 2:断路器数据 3:变压器负荷数据 +// //if ((stGis.is_close & 0xff) == 0x00) +// addDbSetList((char*)&stGis, sizeof(ST_IEC_GISBREAK_TABLE), 0x02); +// +// memset(&stGis, 0x00, sizeof(ST_IEC_GISBREAK_TABLE)); +//#ifdef _SITE_ID_TYPE_INT +// stGis.site_id = g_iec_conf.iec_site_id; +//#else +// strcpy((char*)stGis.site_id, (const char*)g_iec_conf.site_id); +//#endif +// strcpy((char*)stGis.eqm_code, (const char*)g_iec_conf.pstBrkCode[i].szEqmCodeC); +// dbGet_gis_breaker_table(stGis); +// stGis.is_close = cGetGisCloseStatusByEqmCodeA(stGis.site_id, g_iec_conf.pstBrkCode[i].szEqmCodeA); +// // 类型:1-运行工况 2:断路器数据 3:变压器负荷数据 +// //if ((stGis.is_close & 0xff) == 0x00) +// addDbSetList((char*)&stGis, sizeof(ST_IEC_GISBREAK_TABLE), 0x02); +// } +// g_IecCtrl.m_gis_change = false; +// } +// g_IecCtrl.b_Signal = false; +// _SLEEP(1000); +// } +// return NULL; +//} +/*************************************************************************** +** function name : dbGetFaildMaxValue +** deacription : link active thread +** parameter : none +** return code : NULL +***************************************************************************/ +//double dbGetFaildMaxValue(const char *site_id, const char *eqm_code) +//{ +// double dValue = 0.0; +// char szSql[1024] = { 0 }; +// +// CDBMySQL *pdbHandle = CDBMySQL::Instance(); +// //SELECT a.site_id,b.site_code,a.eqm_code, v.ywei,v.ywen, v.jkyl,v.ckyl FROM busi_site_tree a, transformer_threshold v,busi_offline_transformer_brand b +// //WHERE a.site_id=v.site_id AND a.status = 'A' AND a.type = 'a' AND b.status='A' AND a.eqm_code = v.eqm_code AND a.eqm_code = b.equipment_code AND b.equipment_code=v.eqm_code; +// sprintf(szSql, "select site_id, eqm_code, higth_voltage, higth_current from iec_bydwork_param where site_id='%s' and eqm_code='%s'", site_id, eqm_code); +// printf("=====%s\n", szSql); +// MYSQL_RES *res = pdbHandle->SelectRecord(szSql); +// if (!res) return 0.0; +// +// MYSQL_ROW row = NULL; +// while (row = pdbHandle->GetRecord(res)) { +// printf("site_id = %s eqm_code=%s type=%s\n", row[0], row[1], row[2]); +// } +// pdbHandle->FreeRecord(res); +// printf("\n"); +// return dValue; +//} + + +/*************************************************************************** +** function name : thread_Timer_proc +** deacription : Timer_proc thread +** parameter : none +** return code : NULL +***************************************************************************/ +void *thread_Timer_proc(void *arg) +{ + vPrtLogMsg(LOG_DEBUG, 0, "thread_Timer_proc = %d startup...", GETTID()); + while (g_Running && g_IecCtrl.isConnect) + { + // Sleep(1000); + // 间隔1s + time_t timeOrigin = time(NULL); + while ((time(NULL) - timeOrigin) < 1000) _SLEEP(1000); + + if (!g_IecCtrl.isConnect) // t0时间内连接未建立则重新连接并发送U启动帧激活数据传输 + { + // time-out for reconnection. + g_IecCtrl.timer_t0++; // t0++ + if (g_IecCtrl.timer_t0 > g_IecCtrl.t0) // TCP连接建立的超时时间(超时重新连接) + { + g_IecCtrl.timer_t0 = 0; + g_IecCtrl.isConnect = false; + //if (master->OpenLink(master->RemoteHost, master->RemotePort) == EOK) + //SendMsgFormatU(CMD_STARTDT); + } + } + else + { + // maximun number of frames received before master send ACK. W + if ((g_IecCtrl.RxCounter - g_IecCtrl.LastAckRx) >= g_IecCtrl.w * 2) + // Send_S_Msg(); + // time-out of waiting ACK to Sended APDU o TestFR + g_IecCtrl.timer_Confirm++; + // t1:RTU(服务器)端启动U格式测试过程后等待U格式测试应答的超时时间(超时处理:断开连接) + if (g_IecCtrl.timer_Confirm > g_IecCtrl.timer_t1) + { + if (g_IecCtrl.TxCounter - g_IecCtrl.LastAckTx > 6) + { + g_Tcp.tcpCloseSocket(g_IecCtrl.sockid); + g_IecCtrl.isConnect = false; + } + // g_IecCtrl.timer_Confirmflag = true; + } + + // time-out for no data to send, send S-frame to ack data received. OK + g_IecCtrl.timer_t2++; + // t2规定接收方在接收到I格式报文后,若经过t2时间未再收到新的I格式报文, + // 则必须向发送方发送S格式帧对已经接收到的I格式报文进行认可,显然t2必须小于t1。 + if (g_IecCtrl.timer_t2 > g_IecCtrl.t2) // (T2 < T1). S格式的超时时间 + { + g_IecCtrl.timer_t2 = 0; + if (g_IecCtrl.timer_S_Ackflag) // 在收到I格式报文的时候置timer_S_Ack为0 + SendMsgFormatS(g_IecCtrl.usSendNo); + g_IecCtrl.timer_S_Ackflag = true; + } + + // time-out for idle status, send test frame to check link state. OK + // t3规定调度端或子站RTU端每接收一帧 I帧、S帧或者U帧将重新触发计时器t3,若在t3内未接收到任何报文,将向对方发送测试链路帧 + g_IecCtrl.timer_t3++; + if (g_IecCtrl.timer_t3 > g_IecCtrl.t3) // 没有实际的数据交换时,任何一端启动U格式测试过程的最大间隔时间 (发送测试帧) + { + g_IecCtrl.timer_t3 = 0; + if (g_IecCtrl.timer_U_Testflag) // 接收到I S U帧都将timer_U_Test置为0 + SendMsgFormatU(CMD_TESTFR); + g_IecCtrl.timer_U_Testflag = true; + } + } + } + return NULL; +} + +/*************************************************************************** +** function name : thread_active_proc +** deacription : link active thread +** parameter : none +** return code : NULL +***************************************************************************/ +void *thread_active_proc(void *arg) +{ + vPrtLogMsg(LOG_DEBUG, 0, "thread_active_proc = %d startup...", GETTID()); + ST_APCI header; + while (g_Running && g_IecCtrl.isConnect) + { + if (time(NULL) - g_IecCtrl.last_time > g_TConfig.getTimeout3()) { + header.start = 0x68; + header.len = 0x04; + vAutoSendSeqNo(1); // 发送序列号增1 + header.cntl1 = g_IecCtrl.usSendNo & 0xFF; // S-Format + header.cntl2 = (g_IecCtrl.usSendNo >> 8) & 0xFF; + header.cntl3 = g_IecCtrl.usRecvNo & 0xFE; + header.cntl4 = (g_IecCtrl.usRecvNo >> 8) & 0xFF; + + g_Tcp.tcpSendBuffer(g_IecCtrl.sockid, (const char*)&header, 6); + vPrtLogHex(LOG_PACK, g_IecCtrl.sockid, PRT_PACK_SEND, (unsigned char*)&header, sizeof(ST_APCI)); + mutex_lock(g_IecCtrl_mutex); + g_IecCtrl.last_time = time(NULL); + mutex_unlock(g_IecCtrl_mutex); + } + //vPrtLogHex(LOG_PACK, g_IecCtrl.sockid, PRT_PACK_SEND, (unsigned char*)CMD_ACTIVE_PACKAGE, sizeof(ST_APCI)); + //vPrtListCount(); + _SLEEP(1000); + } + return NULL; +} + +// 获取最近一次变压器的数据 +//static void dbGetIECBYQLastData(ST_BYQ_CACHE &pstByq) +//{ +// char szSql[256] = { 0 }; +// time_t stime = 0; +// CDBMySQL *pdbHandle = CDBMySQL::Instance(); +// if (!pdbHandle) return; +// // 获取最近一次的电压、电流... +//#ifdef _SITE_ID_TYPE_INT +// sprintf(szSql, "select stype,start_time,stop_time,higth_voltage,higth_current from iec_bydwork_param where " +// "eqm_code = '%s' and site_id=%d ORDER BY id desc limit 1", pstByq.szEqmCode, pstByq.SiteID); +//#else +// snprintf(szSql, sizeof(szSql), "select stype,start_time,stop_time,higth_voltage,higth_current from iec_bydwork_param where " +// "eqm_code = '%s' and site_id='%s' ORDER BY id desc limit 1", pstByq.szEqmCode, pstByq.SiteID); +//#endif +// MYSQL_RES *res = pdbHandle->SelectRecord(szSql); +// if (!res) return; +// +// MYSQL_ROW row = NULL; +// if (row = pdbHandle->GetRecord(res)) +// { +// pstByq.stLastData.stype = atoi(row[0]); +// +// if (row[1] && strlen(row[1]) > 0) { +// memset(szSql, 0x00, sizeof(szSql)); +// strncpy(szSql, (const char*)row[1], strlen(row[1])); +// pstByq.stLastData.start_time = strTimeFmt2int(szSql); +// } +// +// if (row[2] && strlen(row[2]) > 0) { +// memset(szSql, 0x00, sizeof(szSql)); +// strncpy(szSql, (const char*)row[2], strlen(row[2])); +// pstByq.stLastData.stop_time = strTimeFmt2int(szSql); +// } +// (row[3] && strlen(row[3])) ? (pstByq.stLastData.voltage = atof((const char*)row[3])) : 0; +// (row[4] && strlen(row[4])) ? (pstByq.stLastData.current = atof((const char*)row[4])) : 0; +// +// } +// pdbHandle->FreeRecord(res); +// +// // 获取最近一次的油位,油温,进出口压力值 +//#ifdef _SITE_ID_TYPE_INT +// sprintf(szSql, "select oil_temperature,oil_position,cooling_entry_pressure,cooling_exit_pressure " +// "from transformer_read_table where " +// "eqmid = '%s' and site_id=%d ORDER BY id desc limit 1", pstByq.szEqmCode, pstByq.SiteID); +//#else +// snprintf(szSql, sizeof(szSql), "select oil_temperature,oil_position,cooling_entry_pressure,cooling_exit_pressure " +// "from transformer_read_table where " +// "eqmid = '%s' and site_id='%s' ORDER BY id desc limit 1", pstByq.szEqmCode, pstByq.SiteID); +//#endif +// res = pdbHandle->SelectRecord(szSql); +// if (!res) return; +// +// row = NULL; +// if (row = pdbHandle->GetRecord(res)) +// { +// pstByq.stLastData.dcurYWen = atof(row[0]); +// pstByq.stLastData.dcurYWei = atof(row[1]); +// pstByq.stLastData.dcurJkyl = atof(row[2]); +// pstByq.stLastData.dcurCkyl = atof(row[3]); +// } +// pdbHandle->FreeRecord(res); +// return; +//} +//// 获取gis数据 +//static void dbGetLastDataByEqmCode(unsigned char *pEqmcode) +//{ +// int i = 0; +// char szSql[256] = { 0 }; +// string strKey = ""; +// time_t stime = 0; +// CDBMySQL *pdbHandle = CDBMySQL::Instance(); +// if (!pdbHandle) return; +// +// snprintf(szSql, sizeof(szSql), "select site_id,abort_voltage,abort_current,faild_rate,is_fault,record_date from iec_breaker_param " +// "where eqm_code = '%s' ORDER BY id desc limit 1", pEqmcode); +// +// MYSQL_RES *res = pdbHandle->SelectRecord(szSql); +// if (!res) return; +// strKey = (char*)pEqmcode; +// MYSQL_ROW row = NULL; +// if (row = pdbHandle->GetRecord(res)) +// { +// ST_GIS_IEC104_DATA stGis; +// memset(&stGis, 0x00, sizeof(ST_GIS_IEC104_DATA)); +// +// strcpy((char*)stGis.szEqmCode, (char*)pEqmcode); +//#ifdef _SITE_ID_TYPE_INT +// (row[0] && strlen(row[0])) ? (stGis.SiteID = atoi((const char*)row[0])) : 0; // site_id +//#else +// if (row[0] && strlen(row[0]) > 0) { +// strncpy((char*)stGis.SiteID, row[0], strlen(row[0])); +// } +//#endif +// (row[1] && strlen(row[1])) ? (stGis.stLastData.voltage = atof((const char*)row[1])) : 0; +// (row[2] && strlen(row[2])) ? (stGis.stLastData.current = atof((const char*)row[2])) : 0; +// (row[3] && strlen(row[3])) ? (stGis.stLastData.failed_rate = atof((const char*)row[3])) : 0; +// (row[4] && strlen(row[4])) ? (stGis.stLastData.is_fault = atoi((const char*)row[4])) : 0; +// if (row[5] && strlen(row[5]) > 0) { +// memset(szSql, 0x00, sizeof(szSql)); +// strncpy(szSql, (const char*)row[5], strlen(row[5])); +// stGis.stLastData.record_date = strTimeFmt2int(szSql); +// } +// mutex_lock(g_map_gis_104_mutex); +// g_map_gis_104.insert(map::value_type(strKey, stGis)); +// mutex_unlock(g_map_gis_104_mutex); +// } +// pdbHandle->FreeRecord(res); +// return; +//} +// 获取最近一次断路器的数据 +//static void dbGetIECGISLastData() +//{ +// int i = 0; +// for (i = 0; i < (int)g_iec_conf.iec_break_count; i++) +// { +// dbGetLastDataByEqmCode(g_iec_conf.pstBrkCode[i].szEqmCodeA); +// dbGetLastDataByEqmCode(g_iec_conf.pstBrkCode[i].szEqmCodeB); +// dbGetLastDataByEqmCode(g_iec_conf.pstBrkCode[i].szEqmCodeC); +// } +// return; +//} +// 打印GIS最近一次104上传数据 +//static void vPrtIECGISLastData() +//{ +// map::iterator m_pIter; +// m_pIter = g_map_gis_104.begin(); +// vPrtLogMsg(LOG_DEBUG, RET_OK, "---------Print GIS Last Data ----------"); +// while (m_pIter != g_map_gis_104.end()) +// { +//#ifdef _SITE_ID_TYPE_INT +// vPrtLogMsg(LOG_DEBUG, RET_OK, "%-4d %-10s %.3f %.3f %.3f %1d %u", (m_pIter->second).SiteID, (m_pIter->second).szEqmCode, +//#else +// vPrtLogMsg(LOG_DEBUG, RET_OK, "%-s %.3f %.3f %.3f %1d %u", (m_pIter->second).szEqmCode, +//#endif +// (m_pIter->second).stLastData.voltage, (m_pIter->second).stLastData.current, +// (m_pIter->second).stLastData.failed_rate, (m_pIter->second).stLastData.is_fault, (m_pIter->second).stLastData.record_date); +// m_pIter++; +// } +// vPrtLogMsg(LOG_DEBUG, RET_OK, "---------- gis_last_data_size:%d-----------", g_map_gis_104.size()); +// return; +//} + +// 设置主变的油位信息体地址 +bool bSetBYQPointValueByAddr(const char *pszEqmCode, float value) +{ + unsigned int sdr = 0; + for (int i = 0; i < (int)g_iec_conf.iec_byq_count; i++) + { + if (0 == stringncasecmp(pszEqmCode, (const char*)(g_iec_conf.pstByqCode[i].szEqmCode), strlen(pszEqmCode))) + { + g_IecCtrl.m_gis_change = true; // 通知,需保存一条记录 + switch (i) + { + case 0: + return bSetPointTableValueYC(value, (unsigned int)16405); + case 1: + return bSetPointTableValueYC(value, (unsigned int)16426); + case 2: + return bSetPointTableValueYC(value, (unsigned int)16447); + case 3: + return bSetPointTableValueYC(value, (unsigned int)16468); + default: + break; + } + } + } + return false; +} + + +//// 获取站点主变设备对应关系及阈值 +//bool bGetByqCacheRelation(unsigned char *pszEqmCode, ST_BYQ_CACHE *pstData) +//{ +// if (!pszEqmCode) return false; +// map::iterator m_pIter; +// mutex_lock(g_map_byq_mutex); +// m_pIter = g_map_byq.find((char*)pszEqmCode); +// if (m_pIter != g_map_byq.end()) +// { +// memcpy(pstData, &(m_pIter->second), sizeof(ST_BYQ_CACHE)); +// //g_map_request_mec_rsp.erase(m_pIter) ; +// mutex_unlock(g_map_byq_mutex); +// return true; +// } +// mutex_unlock(g_map_byq_mutex); +// vPrtLogMsg(LOG_WARNG, RET_OK, "Can't found EqmCode:%s on BYQ cache relation table,map_size:%d", pszEqmCode, g_map_byq.size()); +// return false; +//} + +/*************************************************************************** +** function name : dbGetGISCacheData +** deacription : get gis define value & site_id-->site_code relation. +** parameter : none +** return code : NULL +***************************************************************************/ +//void dbGetGISCacheData(void) +//{ +// char szSql[1024] = { 0 }; +// +// CDBMySQL *pdbHandle = CDBMySQL::Instance(); +// if (!pdbHandle) return; +// +// snprintf(szSql, sizeof(szSql), "SELECT c.site_id,b.des, b.cac_number, b.eqm_code, c.picval FROM gis_sbjc a, gis_tree b, gis_threshold c " +// "WHERE a.status = 'A' AND a.type = '6' AND b.type = '6' AND a.id = b.eqm_id AND b.eqm_code = c.eqm_code"); +// +// MYSQL_RES *res = pdbHandle->SelectRecord(szSql); +// if (!res) return; +// +// MYSQL_ROW row = NULL; +// while (row = pdbHandle->GetRecord(res)) +// { +// ST_GIS_CACHE stGis; +// string strKey = ""; +// memset(&stGis, 0x00, sizeof(ST_GIS_CACHE)); +//#ifdef _STIE_ID_TYPE_INT +// stGis.SiteID = atoi(row[0]); +//#else +// if (row[0] && strlen(row[0]) > 0) { +// strncpy((char*)stGis.SiteID, (const char*)row[0], strlen(row[0])); +// } +//#endif +// if (row[1] && strlen(row[1]) > 0) { +// strncpy((char*)stGis.name_des, (const char*)row[1], sizeof(stGis.name_des)); +// } +// if (row[2] && strlen(row[2]) > 0) { +// strncpy((char*)stGis.szSersorID, (const char*)row[2], sizeof(stGis.szSersorID)); +// strKey = (char*)stGis.szSersorID; +// } +// if (row[3] && strlen(row[3]) > 0) { +// strncpy((char*)stGis.szEqmCodeID, (const char*)row[3], sizeof(stGis.szEqmCodeID)); +// } +// (row[4] && strlen(row[4])) ? (stGis.dNum = atof((const char*)row[4])) : 0; +// +// mutex_lock(g_map_gis_mutex); +// g_map_gis.insert(map::value_type(strKey, stGis)); +// mutex_unlock(g_map_gis_mutex); +// } +// pdbHandle->FreeRecord(res); +// vPrtLogMsg(LOG_DEBUG, RET_OK, "---get_gis_size:%d---", g_map_gis.size()); +// return; +//} + +// 获取GIS设备对应关系及阈值 +//bool bGetGisCacheRelation(unsigned char *pszSersorid, ST_GIS_CACHE *pstData) +//{ +// if (!pszSersorid) return false; +// map::iterator m_pIter; +// mutex_lock(g_map_gis_mutex); +// m_pIter = g_map_gis.find((char*)pszSersorid); +// if (m_pIter != g_map_gis.end()) +// { +// memcpy(pstData, &(m_pIter->second), sizeof(ST_GIS_CACHE)); +// //g_map_request_mec_rsp.erase(m_pIter) ; +// mutex_unlock(g_map_gis_mutex); +// return true; +// } +// mutex_unlock(g_map_gis_mutex); +// vPrtLogMsg(LOG_WARNG, RET_OK, "Can't found SersorID:%s on GIS cache relation table,map_size:%d", pszSersorid, g_map_gis.size()); +// return false; +//} +// 打印GIS缓存数据 +//void vPrtGisCacheRelation(void) +//{ +// map::iterator m_pIter; +// m_pIter = g_map_gis.begin(); +// vPrtLogMsg(LOG_DEBUG, RET_OK, "---------Print GIS Relation----------"); +// while (m_pIter != g_map_gis.end()) +// { +// // c.site_id, b.des, b.cac_number, b.eqm_code, c.tpsb_dl +//#ifdef _SITE_ID_TYPE_INT +// vPrtLogMsg(LOG_DEBUG, RET_OK, "%-4d %-.5f %18s %16s", (m_pIter->second).SiteID, +//#else +// vPrtLogMsg(LOG_DEBUG, RET_OK, "%-.5f %18s %16s", +//#endif +// (m_pIter->second).dNum, (m_pIter->second).szSersorID, (m_pIter->second).szEqmCodeID); +// m_pIter++; +// } +// vPrtLogMsg(LOG_DEBUG, RET_OK, "---------- GIS_size:%d-----------", g_map_gis.size()); +// return; +//} + +/*************************************************************************** +** function name : dbGetBLQCacheData +** deacription : get BLQ define value & site_id-->site_code relation. +** parameter : none +** return code : NULL +***************************************************************************/ +//void dbGetBLQCacheData(void) +//{ +// char szSql[1024] = { 0 }; +// +// CDBMySQL *pdbHandle = CDBMySQL::Instance(); +// if (!pdbHandle) return; +// +// snprintf(szSql, sizeof(szSql), "SELECT c.site_id,b.des, b.cac_number, b.eqm_code, c.tpsb_dl FROM arrester_sbjc a, arrester_tree b, arrester_threshold c " +// "WHERE a.status = 'A' AND a.type = '5' AND b.type = '5' AND a.id = b.eqm_id AND b.eqm_code = c.eqm_code"); +// +// MYSQL_RES *res = pdbHandle->SelectRecord(szSql); +// if (!res) return; +// +// MYSQL_ROW row = NULL; +// while (row = pdbHandle->GetRecord(res)) +// { +// ST_BLQ_CACHE stBlq; +// string strKey = ""; +// memset(&stBlq, 0x00, sizeof(ST_BLQ_CACHE)); +//#ifdef _SITE_ID_TYPE_INT +// stBlq.SiteID = atoi(row[0]); +//#else +// if (row[0] && strlen(row[0]) > 0) { +// strncpy((char*)stBlq.SiteID, (const char*)row[0], strlen(row[0])); +// } +//#endif +// if (row[1] && strlen(row[1]) > 0) { +// strncpy((char*)stBlq.name_des, (const char*)row[1], sizeof(stBlq.name_des)); +// } +// if (row[2] && strlen(row[2]) > 0) { +// strncpy((char*)stBlq.szSersorID, (const char*)row[2], sizeof(stBlq.szSersorID)); +// strKey = (char*)stBlq.szSersorID; +// } +// if (row[3] && strlen(row[3]) > 0) { +// strncpy((char*)stBlq.szEqmCodeID, (const char*)row[3], sizeof(stBlq.szEqmCodeID)); +// } +// (row[4] && strlen(row[4])) ? (stBlq.dMaxDL = atof((const char*)row[4])) : 0; +// +// mutex_lock(g_map_blq_mutex); +// g_map_blq.insert(map::value_type(strKey, stBlq)); +// mutex_unlock(g_map_blq_mutex); +// } +// pdbHandle->FreeRecord(res); +// vPrtLogMsg(LOG_DEBUG, RET_OK, "---get_BLQ_size:%d---", g_map_blq.size()); +// return; +//} + +// 获取BLQ设备对应关系及阈值 +//bool bGetBlqCacheRelation(unsigned char *pszSersorid, ST_BLQ_CACHE *pstData) +//{ +// if (!pszSersorid) return false; +// map::iterator m_pIter; +// mutex_lock(g_map_blq_mutex); +// m_pIter = g_map_blq.find((char*)pszSersorid); +// if (m_pIter != g_map_blq.end()) +// { +// memcpy(&pstData, &(m_pIter->second), sizeof(ST_BYQ_CACHE)); +// //g_map_request_mec_rsp.erase(m_pIter) ; +// mutex_unlock(g_map_blq_mutex); +// return true; +// } +// mutex_unlock(g_map_blq_mutex); +// vPrtLogMsg(LOG_WARNG, RET_OK, "Can't found SersorID:%s on BLQ cache relation table,map_size:%d", pszSersorid, g_map_blq.size()); +// return false; +//} +// 打印BLQ缓存数据 +//void vPrtBlqCacheRelation(void) +//{ +// map::iterator m_pIter; +// m_pIter = g_map_blq.begin(); +// vPrtLogMsg(LOG_DEBUG, RET_OK, "---------Print BLQ Relation----------"); +// while (m_pIter != g_map_blq.end()) +// { +// // c.site_id, b.des, b.cac_number, b.eqm_code, c.tpsb_dl +//#ifdef _SITE_ID_TYPE_INT +// vPrtLogMsg(LOG_DEBUG, RET_OK, "%-4d %-.5f %18s %16s %s", (m_pIter->second).SiteID, +//#else +// vPrtLogMsg(LOG_DEBUG, RET_OK, "%-.5f %18s %16s %s", +//#endif +// (m_pIter->second).dMaxDL, (m_pIter->second).szSersorID, (m_pIter->second).szEqmCodeID, +// (m_pIter->second).name_des); +// m_pIter++; +// } +// vPrtLogMsg(LOG_DEBUG, RET_OK, "---------- BLQ_size:%d-----------", g_map_blq.size()); +// return; +//} + +// 统计发电、抽水时间 +void vIECWorkTime() +{ + char szSql[1024] = { 0 }; + + // 统计一天的发电时长(分钟) + snprintf(szSql, sizeof(szSql), "select sum(TIMESTAMPDIFF(MINUTE,start_time,stop_time)) from iec_bydwork_param " + "where eqm_code='SBBH1BYQ' and stype=1 " + "and ins_date BETWEEN '2019-01-09 00:00:00' and '2019-01-09 23:59:59'"); + + //统计平均电压,电流 + snprintf(szSql, sizeof(szSql), "select count(*),sum(higth_voltage),sum(higth_current),avg(higth_voltage),avg(higth_current) " + "from iec_bydwork_param where eqm_code = 'SBBH1BYQ' and stype = 1 " + "and ins_date >= '2019-01-09 00:00:00' and ins_date <= '2019-01-09 23:59:59' "); + // select count(*), sum(temper), avg(temper) + // from busi_temper_cac where type = 'A相出线' + // and dtime >= '2019-01-09 00:00:00' and dtime <= '2019-01-09 23:59:59'; + /* + select count(*), sum(temper), avg(temper) + from busi_temper_cac where type = 'B相出线' + and dtime >= '2019-01-09 00:00:00' and dtime <= '2019-01-09 23:59:59' + and eqm_code = 'SBBH31CWG'; --and sensor_id = 'JXHPD001A10000112'; + + select count(*), sum(temper), avg(temper) + from busi_temper_cac where type = 'B相出线' + and dtime >= '2019-01-09 00:00:00' and dtime <= '2019-01-09 23:59:59' + and eqm_code = 'SBBH166CWG'; --and sensor_id = 'JXHPD001A10000112'; + + */ + +} diff --git a/src/HTLogger.cpp b/src/HTLogger.cpp index 31d21c3..eb87d04 100644 --- a/src/HTLogger.cpp +++ b/src/HTLogger.cpp @@ -29,6 +29,14 @@ static const char *_FILE_="HTLogger.cpp"; static char g_logger_time[15] ; // 日志文件名称时间(YYYYMMDD) +#ifdef _WIN32 +static const char *DEF_LOG_PATH_NAME = "..\\log"; //日志文件目录名 +static const char *DEF_RUN_INFO_PATH = "..\\run"; // 服务运行状态日志目录 +#else +const char *DEF_LOG_PATH_NAME = "../log"; //日志文件目录名 +const char *DEF_RUN_INFO_PATH = "../run"; // 服务运行状态日志目录 +#endif + /* mutex define */ static mutex mutex_Debug ; //跟踪日志 static FILE *g_fp; diff --git a/src/HTMemCacheData.cpp b/src/HTMemCacheData.cpp index 4eeea38..a27d952 100644 --- a/src/HTMemCacheData.cpp +++ b/src/HTMemCacheData.cpp @@ -791,4 +791,90 @@ unsigned char getGisOpenCloseState(unsigned char *site_id, unsigned char *s_eqm_ { if (0 != stringncasecmp( (const char*)site_id, - (const \ No newline at end of file + (const char*)((m_pIter->second).site_id), + strlen((const char*)(m_pIter->second).site_id))) continue; + isClose = (m_pIter->second).bClose ; + } + } + mutex_unlock(g_map_relation_mutex); + return isClose; +} + +// 从主设备关联关系中,获取pszEqm_code设备的运行状态 +unsigned char cGetCurrentWorkState(unsigned char *pszEqm_code) +{ + unsigned char wstate[3] = { 0 }; + map::iterator m_pIter; + mutex_lock(g_map_relation_mutex); + m_pIter = g_map_relation.find((char*)pszEqm_code); + if (m_pIter != g_map_relation.end()) + { // pszEqm_code 为gis/ce/blq ID时 + for (int c = 0; c < (int)g_map_relation.count((char*)pszEqm_code); c++, m_pIter++) + { + //if (0 != stringncasecmp( + // (const char*)pszSite_id, + // (const char*)((m_pIter->second).site_id), + // strlen((const char*)(m_pIter->second).site_id))) continue; + if (((m_pIter->second).wstate & 0xff) == 1){ + wstate[0] = 1; + break; + } + else if(((m_pIter->second).wstate & 0xff) == 2){ + wstate[1] = 2; + } + else wstate[2] = 3; + } + } + else { + // 当以主键(从属设备ID)没有找到时,以主变设备ID再查找。 + for (m_pIter = g_map_relation.begin(); m_pIter != g_map_relation.end(); m_pIter++) + { + //if (0 != stringncasecmp( + // (const char*)pszSite_id, + // (const char*)((m_pIter->second).site_id), + // strlen((const char*)(m_pIter->second).site_id))) continue; + if (0 != stringncasecmp( + (const char*)pszEqm_code, + (const char*)((m_pIter->second).m_eqm_code), + strlen((const char*)(m_pIter->second).m_eqm_code))) continue; + if (((m_pIter->second).wstate & 0xff) == 1){ + wstate[0] = 1; + break; + } + else if (((m_pIter->second).wstate & 0xff) == 2){ + wstate[1] = 2; + } + else wstate[2] = 3; + } + } + mutex_unlock(g_map_relation_mutex); + return (wstate[0] == 1 ? wstate[0] : (wstate[1] == 2 ? wstate[1] : (wstate[2] == 3 ? wstate[2] : 3))); +} +/*************************************************************************** +** function name : thread_cache_proc +** deacription : data cache thread +** parameter : none +** return code : NULL +***************************************************************************/ +void *thread_cache_proc(void *arg) // 同步内存数据线程 +{ + while (g_Running) + { + vLoadByqDeviceStateTable(); + vLoadBYQThresholdTable(false); + vLoadDeviceRelationsTable(); + vLoadIECPointTable(); + vLoadImageThreshold(); + //vPrtGisCacheRelation(); + //vPrtBlqCacheRelation(); + vPrtByqDeviceStateTable(); + vPrtBYQThresholdTable(); + vPrtDeviceRelationsTable(); + vPrtIECPointTable(); + vPrtTimeStat(); + vPrtImageThreshold(); + _SLEEP(1000 * 30); + } + return NULL; +} + diff --git a/src/HTOpencvImg.cpp b/src/HTOpencvImg.cpp index b72b27c..29c1146 100644 --- a/src/HTOpencvImg.cpp +++ b/src/HTOpencvImg.cpp @@ -886,19 +886,2603 @@ void colorReduce(cv::Mat & inputImage) c = inputImage.at(i, j)[2]; if (a > 180) inputImage.at(i, j)[0] = 0; if (b < 50) inputImage.at(i, j)[1] = 255; - if (c *data++ = *data & mask + div / 2; - else if (image.channels() == 3) { - *data++ = *data & mask + div / 2; - *data++ = *data & mask + div / 2; - *data++ = *data & mask + div / 2; + if (c < 120) inputImage.at(i, j)[2] = 0; + } + } +} +// 遍历所有像素点2 +Mat inverseColor4(cv::Mat &srcImage) +{ + // 初始化原图像迭代器 + MatIterator_ srcIterStart = srcImage.begin(); + MatIterator_ srcIterEnd = srcImage.end(); + + while (srcIterStart != srcIterEnd) + { + (*srcIterStart)[0] = 255 - (*srcIterStart)[0]; + (*srcIterStart)[1] = 255 - (*srcIterStart)[1]; + (*srcIterStart)[2] = 255 - (*srcIterStart)[2]; + srcIterStart++; + } + return srcImage; +} + +// 读取仪表指针数值 +double dReadPointValues(ST_OPENCV_CONF *stConf, const char *jpg_file) +{ + double val = 0.0; + Mat temp, dst, gray; //src-->temp-->dst(src为原图;temp是src经canny提取边缘的图, + //用于下面霍夫变换;dst是最终结果图,要在temp灰度图的基础上变为彩色图才能呈现画线效果) + //rows=行 rols=列 + + Mat src = imread(jpg_file, 1); //读取图片到mat + if (!src.data) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "imread picture file :%s failed", jpg_file); + return -1; + } + showImg("src:", src); + colorReduce7(src); + showImg("cvtColor dst:", dst); + vPrtLogMsg(LOG_DEBUG, RET_OK, "imread picture file %s size:[%dx%d],channel:%d", jpg_file, src.cols, src.rows, src.channels()); + + Canny(src, temp, 20, 80, 3); //提取边缘(如果不边缘提取就会浪费巨大时间) + //showImg("Canny temp:", temp); + cvtColor(temp, dst, CV_GRAY2BGR);//将边缘提取的灰度图转换为BGR图便于画线 CV_GRAY2BGR CV_BGR2GRAY + colorReduce7(src); + showImg("cvtColor dst:", dst); + //储存检测圆的容器 + vector circles; + + //调用Hough变换检测圆 + //参数为:待检测图像,检测结果,检测方法(这个参数唯一),累加器的分辨率,两个圆间的距离,canny门限的上限(下限自动设为上限的一半),圆心所需要的最小的投票数,最大和最小半径 + HoughCircles(temp, circles, CV_HOUGH_GRADIENT, 2, 50, 200, 100, 100, 300); + if (circles.size() == 0) HoughCircles(temp, circles, CV_HOUGH_GRADIENT, 2, temp.rows / 5, 150, 350, 50, 200); + if (circles.size() == 0) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "picture file :%s can't find circles point. return.", jpg_file); + return -1.0; + } + showImg("HoughCircles TEMP:", temp); + //找出圆盘(因为最大的不一定是的,所以加了几个限制条件) + int pos = 0; + int max = -1; + for (size_t i = 0; i < circles.size(); i++) + { + Vec3f f = circles[i]; + if (f[2]>max && f[0] + f[2] < temp.rows && f[0] - f[2] >= 0 && f[1] + f[2]0) + { + max = (int)f[2]; + pos = i; + } + } + + Point center((int)circles[pos][0], (int)circles[pos][1]); //找到的圆心 + int radius = (int)circles[pos][2]; //找到的半径 + circle(dst, center, radius, Scalar(255, 0, 0), 2); // BGR=blue + showImg("HoughCircles dst:", dst); + double vv = 0; + list list_MyLine; + vector lines2; //线段检测 + HoughLinesP(temp, lines2, 1, CV_PI / 180, 50, 50, 10); + if (lines2.size() == 0) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "file :%s can't find line. return.", jpg_file); + return -1.0; + } + + vPrtLogMsg(LOG_DEBUG, RET_OK, "file :%s find Cirles=%d,line=%d", jpg_file, (int)circles.size(), (int)lines2.size()); + for (size_t i = 0; i < lines2.size(); i++) + { + Vec4i l = lines2[i]; + Point A(l[0], l[1]), B(l[2], l[3]); + vv = DistancetoSegment(center, A, B); + //if (vv < stConf->max_pointer_len)//根据圆心到指针的距离阈值滤掉其他线段 + if (vv < 30)//根据圆心到指针的距离阈值滤掉其他线段 + { + bool down = (A.y + B.y - 2 * center.y>0);//判断长的在过圆心的水平线上部还是下部 + if (A.x == B.x) //斜率为无穷的情况 + { + list_MyLine.push_back(MyLine(i, 90 + (down ? 180 : 0), (int)Length(Point(A.x - B.x, A.y - B.y)))); } - } // end of row + else if (A.y == B.y) //水平的情况 + { + list_MyLine.push_back(MyLine(i, A.x + B.x - 2 * center.x > 0 ? 0 : 180, (int)Length(Point(A.x - B.x, A.y - B.y)))); + } + else + { + if (down){ + if (A.y > center.y) + list_MyLine.push_back(MyLine(i, 360 - (int)(atan2(A.y - B.y, A.x - B.x) * 180 / PI), (int)Length(Point(A.x - B.x, A.y - B.y)))); + else + list_MyLine.push_back(MyLine(i, 360 - (int)(atan2(B.y - A.y, B.x - A.x) * 180 / PI), (int)Length(Point(A.x - B.x, A.y - B.y)))); + } + else { + if (A.y < center.y) + list_MyLine.push_back(MyLine(i, abs((int)(atan2(A.y - B.y, A.x - B.x) * 180 / PI)), (int)Length(Point(A.x - B.x, A.y - B.y)))); + else + list_MyLine.push_back(MyLine(i, abs((int)(atan2(B.y - A.y, B.x - A.x) * 180 / PI)), (int)Length(Point(A.x - B.x, A.y - B.y)))); + } + } + //line(dst, A, B, Scalar(0, 0, i * 20 + 40), 2, CV_AA); + line(dst, A, B, Scalar(0, 255, 0), 2, CV_AA); //gree + } + } + showImg("dst", dst); + + //根据角度区分所属指针 + int now_k, pre_k = 720;//当前线段的角度和前一个线段的角度 + int num = 0;//指针数(可能为2或3) + int Du[3] = { 0 };//3个指针的度数(每组的平均) + int Le[3] = { 0 };//Le[i]=Le_ping[i]*0.2+le_max[i]*0.8; + int Le_ping[3] = { 0 };//3个指针的长度(每组的平均) + int Le_max[3] = { 0 };//3个指针的长度(每组区最大的) + int t_num = 0;//每组的数量(求平均用) + + MyLine now_Line; + list_MyLine.push_back(MyLine(99, 888, 0));//向其中插入一个右边界这样方便处理 + list_MyLine.sort(); + while (!list_MyLine.empty()) + { + now_Line = list_MyLine.front(); + now_k = now_Line.k; + //if (abs(now_k - pre_k)>5)//两个角度之差小于5°的算是同一类 + if (abs(now_k - pre_k) > 10)//两个角度之差小于10°的算是同一类 + { + if (num != 0) //对本组的度数和长度求平均 + { + Du[num - 1] /= t_num; + Le_ping[num - 1] /= t_num; + Le[num - 1] = (int)(Le_ping[num - 1] * 0.2 + Le_max[num - 1] * 0.8); + } + if (now_k == 888) break;//右边界直接跳出 + t_num = 0;//重新统计下一组 + num++; //组数增加1 + if (num > 3) break; + } + t_num++;//组内多一条线 + Du[num - 1] += now_Line.k; + Le_ping[num - 1] += now_Line.l; + if (now_Line.l > Le_max[num - 1]) Le_max[num - 1] = now_Line.l; + //now_Line.print(); + vPrtLogMsg(LOG_DEBUG, RET_OK, "Point id: %d k: %d° len: %d", now_Line.id, now_Line.k, now_Line.l); + //printf("Point id: %d k: %d° len: %d\n", now_Line.id, now_Line.k, now_Line.l); + list_MyLine.pop_front(); + pre_k = now_k; + } + + int t, duval = 0; + for (int i = 0; i < num - 1; i++) + { + for (int j = i + 1; j < num; j++) + { + if (Du[i] < Du[j]) + { + t = Le[i], Le[i] = Le[j], Le[j] = t; + t = Du[i], Du[i] = Du[j], Du[j] = t; + }//if end + }//for end + }//for end + + // 根据配置文件的仪表类型,查找指针度数对应的读数 + + //printf("\nIn Point id: num:%d k: %d° len: %d\n", num, Du[0], Le[0]); + + for (int n = 0; n < num; n++) { + val = dGetReadValueOf3ex5050(Du[n], (double)stConf->base_angle, Le[n]); + if (val <= 0 || val > 30) continue; + break; } + //if (val <= 0 && num && num <= 3 ) { + // printf("get val over, to tyr get...du=%d\n", Du[1]); + // val = dGetReadValueOf3ex5050(Du[1], (double)stConf->base_angle, Le[1]); + //} + //if (val > 30) { + // printf("get val over, to tyr get...du=%d\n", Du[2]); + // val = dGetReadValueOf3ex5050(Du[2], (double)stConf->base_angle, Le[2]); + //} + vPrtLogMsg(LOG_DEBUG, RET_OK, "File:%s Point=%d === read number is: %.3f", jpg_file, num, val); + return val; } -void colorReduce(cv::Mat & inputImage) +// 读取仪表指针数值 +double dReadPointValue_3EX5050(ST_OPENCV_CONF *stConf, const char *jpg_file) { - // 鍙傛暟鍑嗗 - int a, b, c; - int rowNumber = inputImage.rows; - int colNumber = inputImage.cols*inputImage.channels(); - if (inputImage.isContinuous()) { // 鍒ゆ柇鏁版嵁鍦ㄥ唴瀛樻槸鍚﹁繛缁瓨鍌ㄧ殑銆 \ No newline at end of file + double val = 0.0; + Mat temp, dst, gray; //src-->temp-->dst(src为原图;temp是src经canny提取边缘的图, + //用于下面霍夫变换;dst是最终结果图,要在temp灰度图的基础上变为彩色图才能呈现画线效果) + //rows=行 rols=列 + + Mat src = imread(jpg_file, 0); //读取图片到mat + if (!src.data) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "imread picture file :%s failed", jpg_file); + return -1; + } + vPrtLogMsg(LOG_DEBUG, RET_OK, "imread picture file %s size:[%dx%d],channel:%d", jpg_file, src.cols, src.rows, src.channels()); + Canny(src, temp, 20, 80, 3); //提取边缘(如果不边缘提取就会浪费巨大时间) + cvtColor(temp, dst, CV_GRAY2BGR);//将边缘提取的灰度图转换为BGR图便于画线 CV_GRAY2BGR CV_BGR2GRAY + //储存检测圆的容器 + vector circles; + + //调用Hough变换检测圆 + //参数为:待检测图像,检测结果,检测方法(这个参数唯一),累加器的分辨率,两个圆间的距离,canny门限的上限(下限自动设为上限的一半),圆心所需要的最小的投票数,最大和最小半径 + //HoughCircles(temp, circles, CV_HOUGH_GRADIENT, 2, 50, 200, 100, 100, 300); + HoughCircles(temp, circles, CV_HOUGH_GRADIENT, 2, 50, 200, 50, 100, 200); + if (circles.size() == 0) HoughCircles(temp, circles, CV_HOUGH_GRADIENT, 2, temp.rows / 5, 150, 350, 50, 200); + if (circles.size() == 0) HoughCircles(temp, circles, CV_HOUGH_GRADIENT, 2, temp.rows / 6, 100, 300, 50, 200); + //if (circles.size() == 0) HoughCircles(temp, circles, CV_HOUGH_GRADIENT, 2, temp.rows / 7, 80, 240, 50, 200); + //if (circles.size() == 0) HoughCircles(temp, circles, CV_HOUGH_GRADIENT, 2, temp.rows / 4, 50, 110, 50, 200); + //if (circles.size() == 0) HoughCircles(temp, circles, CV_HOUGH_GRADIENT, 2, temp.rows / 4, 150, 300, 50, 100); + //if (circles.size() == 0) HoughCircles(temp, circles, CV_HOUGH_GRADIENT, 2, temp.rows / 4, 100, 200, 40, 80); + //if (circles.size() == 0) HoughCircles(temp, circles, CV_HOUGH_GRADIENT, 2, temp.rows / 4, 80, 160, 30, 70); + //if (circles.size() == 0) HoughCircles(temp, circles, CV_HOUGH_GRADIENT, 2, temp.rows / 4, 50, 100, 20, 60); + if (circles.size() == 0) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "picture file :%s can't find circles point. return.", jpg_file); + return -1.0; + } + showImg("HoughCircles TEMP:", temp); + //找出圆盘(因为最大的不一定是的,所以加了几个限制条件) + int pos = 0; + int max = -1; + for (size_t i = 0; i < circles.size(); i++) + { + Vec3f f = circles[i]; + if (f[2]>max && f[0] + f[2] < temp.rows && f[0] - f[2] >= 0 && f[1] + f[2]0) + { + max = (int)f[2]; + pos = i; + } + } + + Point center((int)circles[pos][0], (int)circles[pos][1]); //找到的圆心 + int radius = (int)circles[pos][2]; //找到的半径 + circle(dst, center, radius, Scalar(255, 0, 0), 2); // BGR=blue + showImg("HoughCircles dst:", dst); + double vv = 0; + list list_MyLine; + vector lines2; //线段检测 + HoughLinesP(temp, lines2, 1, CV_PI / 180, 50, 50, 10); + if (lines2.size() == 0) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "file :%s can't find line. return.", jpg_file); + return -1.0; + } + + vPrtLogMsg(LOG_DEBUG, RET_OK, "file :%s find Cirles=%d,line=%d", jpg_file, (int)circles.size(), (int)lines2.size()); + for (size_t i = 0; i < lines2.size(); i++) + { + Vec4i l = lines2[i]; + Point A(l[0], l[1]), B(l[2], l[3]); + vv = DistancetoSegment(center, A, B); + //if (vv < stConf->max_pointer_len)//根据圆心到指针的距离阈值滤掉其他线段 + if (vv < 80)//根据圆心到指针的距离阈值滤掉其他线段 + { + bool down = (A.y + B.y - 2 * center.y>0);//判断长的在过圆心的水平线上部还是下部 + if (A.x == B.x) //斜率为无穷的情况 + { + //list_MyLine.push_back(MyLine(i, 90 + (down ? 180 : 0), (int)Length(Point(A.x - B.x, A.y - B.y)))); + } + else if (A.y == B.y) //水平的情况 + { + //list_MyLine.push_back(MyLine(i, A.x + B.x - 2 * center.x > 0 ? 0 : 180, (int)Length(Point(A.x - B.x, A.y - B.y)))); + } + else + { + if (down){ + if (A.y > center.y) + list_MyLine.push_back(MyLine(i, 360 - (int)(atan2(A.y - B.y, A.x - B.x) * 180 / PI), (int)Length(Point(A.x - B.x, A.y - B.y)))); + else + list_MyLine.push_back(MyLine(i, 360 - (int)(atan2(B.y - A.y, B.x - A.x) * 180 / PI), (int)Length(Point(A.x - B.x, A.y - B.y)))); + } + else { + if (A.y < center.y) + list_MyLine.push_back(MyLine(i, abs((int)(atan2(A.y - B.y, A.x - B.x) * 180 / PI)), (int)Length(Point(A.x - B.x, A.y - B.y)))); + else + list_MyLine.push_back(MyLine(i, abs((int)(atan2(B.y - A.y, B.x - A.x) * 180 / PI)), (int)Length(Point(A.x - B.x, A.y - B.y)))); + } + } + //line(dst, A, B, Scalar(0, 0, i * 20 + 40), 2, CV_AA); + line(dst, A, B, Scalar(0, 255, 0), 2, CV_AA); //gree + } + //line(dst, A, B, Scalar(0, 255, 0), 2, CV_AA); //gree + } + showImg("dst", dst); + + //根据角度区分所属指针 + int now_k, pre_k = 720;//当前线段的角度和前一个线段的角度 + int num = 0;//指针数(可能为2或3) + int Du[10] = { 0 };//3个指针的度数(每组的平均) + int Le[10] = { 0 };//Le[i]=Le_ping[i]*0.2+le_max[i]*0.8; + int Le_ping[10] = { 0 };//3个指针的长度(每组的平均) + int Le_max[10] = { 0 };//3个指针的长度(每组区最大的) + int t_num = 0;//每组的数量(求平均用) + + MyLine now_Line; + list_MyLine.push_back(MyLine(99, 888, 0));//向其中插入一个右边界这样方便处理 + list_MyLine.sort(); + while (!list_MyLine.empty()) + { + now_Line = list_MyLine.front(); + now_k = now_Line.k; + if (abs(now_k - pre_k)>5)//两个角度之差小于5°的算是同一类 + //if (abs(now_k - pre_k) > 10)//两个角度之差小于10°的算是同一类 + { + if (num != 0) //对本组的度数和长度求平均 + { + Du[num - 1] /= t_num; + Le_ping[num - 1] /= t_num; + Le[num - 1] = (int)(Le_ping[num - 1] * 0.2 + Le_max[num - 1] * 0.8); + } + if (now_k == 888) break;//右边界直接跳出 + t_num = 0;//重新统计下一组 + num++; //组数增加1 + if (num > 10) break; + } + t_num++;//组内多一条线 + Du[num - 1] += now_Line.k; + Le_ping[num - 1] += now_Line.l; + if (now_Line.l > Le_max[num - 1]) Le_max[num - 1] = now_Line.l; + //now_Line.print(); + //vPrtLogMsg(LOG_DEBUG, RET_OK, "Point id: %d k: %d° len: %d", now_Line.id, now_Line.k, now_Line.l); + //printf("Point id: %d k: %d° len: %d\n", now_Line.id, now_Line.k, now_Line.l); + list_MyLine.pop_front(); + pre_k = now_k; + } + + int t, duval = 0; + for (int i = 0; i < num - 1; i++) + { + for (int j = i + 1; j < num; j++) + { + if (Du[i] <= 0) continue; // 过滤0°的角 + if (Du[i] < Du[j]) + { + t = Le[i], Le[i] = Le[j], Le[j] = t; + t = Du[i], Du[i] = Du[j], Du[j] = t; + }//if end + }//for end + }//for end + + // 根据配置文件的仪表类型,查找指针度数对应的读数 + + //printf("\nIn Point id: num:%d k: %d° len: %d\n", num, Du[0], Le[0]); + + double dRetVal = stConf->dInitVal; + double dChaVal = dRetVal; + for (int n = 0; n < num; n++) { + val = dGetReadValueOf3ex5050(Du[n], (double)stConf->base_angle, Le[n]); + if (abs(val - stConf->dInitVal) < dChaVal) + { + dRetVal = val; + dChaVal = abs(val - stConf->dInitVal); + } + vPrtLogMsg(LOG_DEBUG, RET_OK, "File:%s Point=%d(%d:%d) === read number is: %.3f", jpg_file, num, Du[n],Le[n],val); + } + //if (dRetVal <= 0) dRetVal = 1.0f; + vPrtLogMsg(LOG_DEBUG, RET_OK, "File:%s Point=%d === read number is: %.3f", jpg_file, num, dRetVal); + return dRetVal; +} + +// 读取仪表指针数值 +double dReadPointValue_JSY10(ST_OPENCV_CONF *stConf, const char *jpg_file) +{ + double val = 0.0; + Mat temp, dst, gray; //src-->temp-->dst(src为原图;temp是src经canny提取边缘的图, + //用于下面霍夫变换;dst是最终结果图,要在temp灰度图的基础上变为彩色图才能呈现画线效果) + //rows=行 rols=列 + + Mat src = imread(jpg_file, 0); //读取图片到mat + if (!src.data) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "imread picture file :%s failed", jpg_file); + return -1; + } + vPrtLogMsg(LOG_DEBUG, RET_OK, "imread picture file %s size:[%dx%d]", jpg_file, src.cols, src.rows); + //Canny(src, temp, 10, src.cols/4, 3); //提取边缘(如果不边缘提取就会浪费巨大时间) + Canny(src, temp, 10, 120, 3); //提取边缘(如果不边缘提取就会浪费巨大时间) + cvtColor(temp, dst, CV_GRAY2BGR);//将边缘提取的灰度图转换为BGR图便于画线 + + //储存检测圆的容器 + vector circles; + + //调用Hough变换检测圆 + //参数为:待检测图像,检测结果,检测方法(这个参数唯一),累加器的分辨率,两个圆间的距离,canny门限的上限(下限自动设为上限的一半),圆心所需要的最小的投票数,最大和最小半径 + //while (!circles.size()) + //HoughCircles(temp, circles, CV_HOUGH_GRADIENT, 2, temp.rows/6, 100, 100, 20, 200); + HoughCircles(temp, circles, CV_HOUGH_GRADIENT, 2, 50, 200, 100, 100, 300); + if (circles.size() == 0) HoughCircles(temp, circles, CV_HOUGH_GRADIENT, 2, temp.rows / 5, 150, 350, 50, 200); + if (circles.size() == 0) HoughCircles(temp, circles, CV_HOUGH_GRADIENT, 2, temp.rows / 6, 100, 300, 50, 200); + if (circles.size() == 0) HoughCircles(temp, circles, CV_HOUGH_GRADIENT, 2, temp.rows / 7, 80, 240, 50, 200); + if (circles.size() == 0) HoughCircles(temp, circles, CV_HOUGH_GRADIENT, 2, temp.rows / 4, 50, 110, 50, 200); + if (circles.size() == 0) HoughCircles(temp, circles, CV_HOUGH_GRADIENT, 2, temp.rows / 4, 150, 300, 50, 100); + if (circles.size() == 0) HoughCircles(temp, circles, CV_HOUGH_GRADIENT, 2, temp.rows / 4, 100, 200, 40, 80); + if (circles.size() == 0) HoughCircles(temp, circles, CV_HOUGH_GRADIENT, 2, temp.rows / 4, 80, 160, 30, 70); + if (circles.size() == 0) HoughCircles(temp, circles, CV_HOUGH_GRADIENT, 2, temp.rows / 4, 50, 100, 20, 60); + if (circles.size() == 0) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "picture file :%s can't find circles point. return.", jpg_file); + return -1.0; + } + + //找出圆盘(因为最大的不一定是的,所以加了几个限制条件) + int pos = 0; + int max = -1; + for (size_t i = 0; i < circles.size(); i++) + { + Vec3f f = circles[i]; + if (f[2]>max && f[0] + f[2] < temp.rows && f[0] - f[2] >= 0 && f[1] + f[2]0) + { + max = (int)f[2]; + pos = i; + } + } + + Point center((int)circles[pos][0], (int)circles[pos][1]); //找到的圆心 + int radius = (int)circles[pos][2]; //找到的半径 + circle(dst, center, radius, Scalar(255, 0, 0), 2); // BGR=blue + showImg("Cirles:", dst); + double vv = 0; + list list_MyLine; + vector lines2; //线段检测 + HoughLinesP(temp, lines2, 1, CV_PI / 180, 50, 50, 10); + //HoughLinesP(temp, lines2, 1, CV_PI / 180, 100, 30, 10); + if (lines2.size() == 0) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "file :%s can't find line. return.", jpg_file); + return -1.0; + } + vPrtLogMsg(LOG_DEBUG, RET_OK, "file :%s find Cirles=%d,line=%d", jpg_file, (int)circles.size(), (int)lines2.size()); + // printf("lines2 = %d\n", (int)lines2.size()); + for (size_t i = 0; i < lines2.size(); i++) + { + Vec4i l = lines2[i]; + Point A(l[0], l[1]), B(l[2], l[3]); + vv = DistancetoSegment(center, A, B); + //if (vv < stConf->max_pointer_len)//根据圆心到指针的距离阈值滤掉其他线段 + if (vv < 30)//根据圆心到指针的距离阈值滤掉其他线段 + { + bool down = (A.y + B.y - 2 * center.y>0);//判断长的在过圆心的水平线上部还是下部 + if (A.x == B.x) //斜率为无穷的情况 + { + list_MyLine.push_back(MyLine(i, 90 + (down ? 180 : 0), (int)Length(Point(A.x - B.x, A.y - B.y)))); + } + else if (A.y == B.y) //水平的情况 + { + list_MyLine.push_back(MyLine(i, A.x + B.x - 2 * center.x > 0 ? 0 : 180, (int)Length(Point(A.x - B.x, A.y - B.y)))); + } + else + { + if (down){ + if (A.y > center.y) + list_MyLine.push_back(MyLine(i, 360 - (int)(atan2(A.y - B.y, A.x - B.x) * 180 / PI), (int)Length(Point(A.x - B.x, A.y - B.y)))); + else + list_MyLine.push_back(MyLine(i, 360 - (int)(atan2(B.y - A.y, B.x - A.x) * 180 / PI), (int)Length(Point(A.x - B.x, A.y - B.y)))); + } + else { + if (A.y < center.y) + list_MyLine.push_back(MyLine(i, abs((int)(atan2(A.y - B.y, A.x - B.x) * 180 / PI)), (int)Length(Point(A.x - B.x, A.y - B.y)))); + else + list_MyLine.push_back(MyLine(i, abs((int)(atan2(B.y - A.y, B.x - A.x) * 180 / PI)), (int)Length(Point(A.x - B.x, A.y - B.y)))); + } + } + //line(dst, A, B, Scalar(0, 0, i * 20 + 40), 2, CV_AA); + line(dst, A, B, Scalar(0, 255, 0), 2, CV_AA); //gree + } + } + showImg("dst", dst); + + //根据角度区分所属指针 + int now_k, pre_k = 720;//当前线段的角度和前一个线段的角度 + int num = 0;//指针数(可能为2或3) + int Du[3] = { 0 };//3个指针的度数(每组的平均) + int Le[3] = { 0 };//Le[i]=Le_ping[i]*0.2+le_max[i]*0.8; + int Le_ping[3] = { 0 };//3个指针的长度(每组的平均) + int Le_max[3] = { 0 };//3个指针的长度(每组区最大的) + int t_num = 0;//每组的数量(求平均用) + + MyLine now_Line; + list_MyLine.push_back(MyLine(99, 888, 0));//向其中插入一个右边界这样方便处理 + list_MyLine.sort(); + while (!list_MyLine.empty()) + { + now_Line = list_MyLine.front(); + now_k = now_Line.k; + //if (abs(now_k - pre_k)>5)//两个角度之差小于5°的算是同一类 + if (abs(now_k - pre_k) > 10)//两个角度之差小于10°的算是同一类 + { + if (num != 0) //对本组的度数和长度求平均 + { + Du[num - 1] /= t_num; + Le_ping[num - 1] /= t_num; + Le[num - 1] = (int)(Le_ping[num - 1] * 0.2 + Le_max[num - 1] * 0.8); + } + if (now_k == 888) break;//右边界直接跳出 + t_num = 0;//重新统计下一组 + num++; //组数增加1 + if (num > 3) break; + } + t_num++;//组内多一条线 + Du[num - 1] += now_Line.k; + Le_ping[num - 1] += now_Line.l; + if (now_Line.l > Le_max[num - 1]) Le_max[num - 1] = now_Line.l; + //now_Line.print(); + vPrtLogMsg(LOG_DEBUG, RET_OK, "Point id: %d k: %d° len: %d", now_Line.id, now_Line.k, now_Line.l); + //printf("Point id: %d k: %d° len: %d\n", now_Line.id, now_Line.k, now_Line.l); + list_MyLine.pop_front(); + pre_k = now_k; + } + + int t, duval = 0; + for (int i = 0; i < num - 1; i++) + { + for (int j = i + 1; j < num; j++) + { + //if (Le[i]>Le[j]) + //{ + // t = Le[i], Le[i] = Le[j], Le[j] = t; + // t = Du[i], Du[i] = Du[j], Du[j] = t; + //}//if end + if (Du[i] < Du[j]) + { + t = Le[i], Le[i] = Le[j], Le[j] = t; + t = Du[i], Du[i] = Du[j], Du[j] = t; + }//if end + }//for end + }//for end + + // 根据配置文件的仪表类型,查找指针度数对应的读数 + + double dRetVal = stConf->dInitVal; + double dChaVal = 1; + for (int n = 0; n < num; n++) { + val = dGetReadValueOfJSY10(Du[n], (double)stConf->base_angle, Le[n]); + if (abs(val - stConf->dInitVal) < dChaVal) + { + dRetVal = val; + dChaVal = abs(val - stConf->dInitVal); + } + } + + //printf("\nIn Point id: num:%d k: %d° len: %d\n", num, Du[0], Le[0]); + + vPrtLogMsg(LOG_DEBUG, RET_OK, "File:%s Point=%d === read number is: %.3f", jpg_file, num, dRetVal); + return dRetVal; +} + +// 读取仪表指针数值 +double dReadPointValue_YZF250TH(ST_OPENCV_CONF *stConf, const char *jpg_file) +{ + double val = 0.0; + Mat temp, dst, gray; //src-->temp-->dst(src为原图;temp是src经canny提取边缘的图, + //用于下面霍夫变换;dst是最终结果图,要在temp灰度图的基础上变为彩色图才能呈现画线效果) + //rows=行 rols=列 + + Mat src = imread(jpg_file, 0); //读取图片到mat + if (!src.data) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "imread picture file :%s failed", jpg_file); + return -1; + } + vPrtLogMsg(LOG_DEBUG, RET_OK, "imread picture file %s size:[%dx%d],channel:%d", jpg_file, src.cols, src.rows, src.channels()); + //Canny(src, temp, 20, 80, 3); //提取边缘(如果不边缘提取就会浪费巨大时间) + Canny(src, temp, 22, 120, 3); //提取边缘(如果不边缘提取就会浪费巨大时间) + cvtColor(temp, dst, CV_GRAY2BGR);//将边缘提取的灰度图转换为BGR图便于画线 CV_GRAY2BGR CV_BGR2GRAY + + showImg("HoughCircles TEMP:", src); + //储存检测圆的容器 + vector circles; + + //调用Hough变换检测圆 + //参数为:待检测图像,检测结果,检测方法(这个参数唯一),累加器的分辨率,两个圆间的距离,canny门限的上限(下限自动设为上限的一半),圆心所需要的最小的投票数,最大和最小半径 + HoughCircles(temp, circles, CV_HOUGH_GRADIENT, 1, 50, 200, 100, src.rows / 4, src.rows/2); + //HoughCircles(temp, circles, CV_HOUGH_GRADIENT, 2, 50, 200, 50, 100, 200); + //if (circles.size() == 0) HoughCircles(temp, circles, CV_HOUGH_GRADIENT, 2, temp.rows / 5, 150, 350, 50, 200); + //if (circles.size() == 0) HoughCircles(temp, circles, CV_HOUGH_GRADIENT, 2, temp.rows / 6, 100, 300, 50, 200); + if (circles.size() == 0) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "picture file :%s can't find circles point. return.", jpg_file); + return -1.0; + } + showImg("HoughCircles TEMP:", temp); + //找出圆盘(因为最大的不一定是的,所以加了几个限制条件) + int pos = 0; + int max = -1; + for (size_t i = 0; i < circles.size(); i++) + { + Vec3f f = circles[i]; + if (f[2]>max && f[0] + f[2] < temp.rows && f[0] - f[2] >= 0 && f[1] + f[2]0) + { + max = (int)f[2]; + pos = i; + } + } + + Point center((int)circles[pos][0], (int)circles[pos][1]); //找到的圆心 + int radius = (int)circles[pos][2]; //找到的半径 + circle(dst, center, radius, Scalar(255, 0, 0), 2); // BGR=blue + showImg("HoughCircles dst:", dst); + double vv = 0; + list list_MyLine; + vector lines2; //线段检测 + /*CV_EXPORTS_W void HoughLinesP(InputArray image, OutputArray lines, + double rho, double theta, int threshold, + double minLineLength = 0, double maxLineGap = 0);*/ + HoughLinesP(temp, lines2, 1, CV_PI / 180, 50, 50, 10); + if (lines2.size() == 0) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "file :%s can't find line. return.", jpg_file); + return -1.0; + } + + vPrtLogMsg(LOG_DEBUG, RET_OK, "file :%s find Cirles=%d,line=%d", jpg_file, (int)circles.size(), (int)lines2.size()); + for (size_t i = 0; i < lines2.size(); i++) + { + Vec4i l = lines2[i]; + Point A(l[0], l[1]), B(l[2], l[3]); + + vv = DistancetoSegment(center, A, B); + //if (vv < stConf->max_pointer_len)//根据圆心到指针的距离阈值滤掉其他线段 + if (vv < 80)//根据圆心到指针的距离阈值滤掉其他线段 + { + bool down = (A.y + B.y - 2 * center.y>0);//判断长的在过圆心的水平线上部还是下部 + if (A.x == B.x) //斜率为无穷的情况 + { + list_MyLine.push_back(MyLine(i, 90 + (down ? 180 : 0), (int)Length(Point(A.x - B.x, A.y - B.y)))); + } + else if (A.y == B.y) //水平的情况 + { + list_MyLine.push_back(MyLine(i, A.x + B.x - 2 * center.x > 0 ? 0 : 180, (int)Length(Point(A.x - B.x, A.y - B.y)))); + } + else + { + if (down){ + if (A.y > center.y) + list_MyLine.push_back(MyLine(i, 360 - (int)(atan2(A.y - B.y, A.x - B.x) * 180 / PI), (int)Length(Point(A.x - B.x, A.y - B.y)))); + else + list_MyLine.push_back(MyLine(i, 360 - (int)(atan2(B.y - A.y, B.x - A.x) * 180 / PI), (int)Length(Point(A.x - B.x, A.y - B.y)))); + } + else { + if (A.y < center.y) + list_MyLine.push_back(MyLine(i, abs((int)(atan2(A.y - B.y, A.x - B.x) * 180 / PI)), (int)Length(Point(A.x - B.x, A.y - B.y)))); + else + list_MyLine.push_back(MyLine(i, abs((int)(atan2(B.y - A.y, B.x - A.x) * 180 / PI)), (int)Length(Point(A.x - B.x, A.y - B.y)))); + } + } + line(dst, A, B, Scalar(0, 0, i * 20 + 40), 2, CV_AA); + //line(dst, A, B, Scalar(0, 255, 0), 2, CV_AA); //gree + } + } + showImg("dst", dst); + + //根据角度区分所属指针 + int now_k, pre_k = 720;//当前线段的角度和前一个线段的角度 + int num = 0;//指针数(可能为2或3) + int Du[10] = { 0 };//3个指针的度数(每组的平均) + int Le[10] = { 0 };//Le[i]=Le_ping[i]*0.2+le_max[i]*0.8; + int Le_ping[10] = { 0 };//3个指针的长度(每组的平均) + int Le_max[10] = { 0 };//3个指针的长度(每组区最大的) + int t_num = 0;//每组的数量(求平均用) + + MyLine now_Line; + list_MyLine.push_back(MyLine(99, 888, 0));//向其中插入一个右边界这样方便处理 + list_MyLine.sort(); + while (!list_MyLine.empty()) + { + now_Line = list_MyLine.front(); + now_k = now_Line.k; + //if (abs(now_k - pre_k)>5)//两个角度之差小于5°的算是同一类 + if (abs(now_k - pre_k) > 10)//两个角度之差小于10°的算是同一类 + { + if (num != 0) //对本组的度数和长度求平均 + { + Du[num - 1] /= t_num; + Le_ping[num - 1] /= t_num; + Le[num - 1] = (int)(Le_ping[num - 1] * 0.2 + Le_max[num - 1] * 0.8); + } + if (now_k == 888) break;//右边界直接跳出 + t_num = 0;//重新统计下一组 + num++; //组数增加1 + if (num > 10) break; + } + t_num++;//组内多一条线 + Du[num - 1] += now_Line.k; + Le_ping[num - 1] += now_Line.l; + if (now_Line.l > Le_max[num - 1]) Le_max[num - 1] = now_Line.l; + //now_Line.print(); + //vPrtLogMsg(LOG_DEBUG, RET_OK, "Point id: %d k: %d° len: %d", now_Line.id, now_Line.k, now_Line.l); + //printf("Point id: %d k: %d° len: %d\n", now_Line.id, now_Line.k, now_Line.l); + list_MyLine.pop_front(); + pre_k = now_k; + } + + int t, duval = 0; + for (int i = 0; i\n"; + //if (num == 2) + for (int n = 0; n < num ; n++) { + // val = dGetReadValueOfYZFTH(Du[n], (double)stConf->base_angle, Le[n]); + //printf("time is: %2d:%2d\n", (360 - Du[n] + 90) % 360 / 30, (360 - Du[n+1] + 90) % 360 / 6); + printf("=====>> %2d -- %.3f \n", Du[n], val); + } + //else if (num == 3) + // printf("time is: %2d:%2d:%2d\n", (360 - Du[0] + 90) % 360 / 30, (360 - Du[1] + 90) % 360 / 6, (360 - Du[2] + 90) % 360 / 6); + //printf("\nIn Point id: num:%d k: %d° len: %d\n", num, Du[0], Le[0]); + + double dRetVal = stConf->dInitVal; + printf("InitVal = %f\n", dRetVal); + //double dChaVal = dRetVal; + //for (int n = 0; n < num; n++) { + // val = dGetReadValueOfYZFTH(Du[n], (double)stConf->base_angle, Le[n]); + // if (abs(val - stConf->dInitVal) < dChaVal) + // { + // dRetVal = val; + // dChaVal = abs(val - stConf->dInitVal); + // } + // vPrtLogMsg(LOG_DEBUG, RET_OK, "File:%s Point=%d(%d:%d) === read number is: %.3f", jpg_file, num, Du[n], Le[n], val); + //} + ////if (dRetVal <= 0) dRetVal = 1.0f; + //vPrtLogMsg(LOG_DEBUG, RET_OK, "File:%s Point=%d === read number is: %.3f", jpg_file, num, dRetVal); + return dRetVal; +} + +//double Angle(Point cen, Point first, Point second) +//{ +// const double M_PI = 3.1415926535897; +// +// double ma_x = first.x - cen.x; +// double ma_y = first.y - cen.y; +// double mb_x = second.x - cen.x; +// double mb_y = second.y - cen.y; +// double v1 = (ma_x * mb_x) + (ma_y * mb_y); +// double ma_val = sqrt(ma_x * ma_x + ma_y * ma_y); +// double mb_val = sqrt(mb_x * mb_x + mb_y * mb_y); +// double cosM = v1 / (ma_val * mb_val); +// double angleAMB = acos(cosM) * 180 / M_PI; +// return angleAMB; +//} +/************************************************************************ +*函数名: get_point_angle +* +*函数作用: 已知2个坐标点,求从 0------->x 逆时针需旋转多少角度到该位置 +* +* | +* | +* | +* | +*------------------------------------> x +* | 0 +* | +* | +* | +* v +* y +* +*函数参数: +*CvPoint2D32f pointO - 起点 +*CvPoint2D32f pointA - 终点 +* +*函数返回值: +*double 向量OA,从 0------->x 逆时针需旋转多少角度到该位置 +**************************************************************************/ +double get_point_angle(cv::Point pointO, cv::Point pointA) +{ + double angle = 0; + cv::Point point; + double temp; + + point = cv::Point((pointA.x - pointO.x), (pointA.y - pointO.y)); + + if ((0 == point.x) && (0 == point.y)) + { + return 0; + } + + if (0 == point.x) + { + angle = 90; + return angle; + } + + if (0 == point.y) + { + angle = 0; + return angle; + } + + temp = fabsf(float(point.y) / float(point.x)); + temp = atan(temp); + temp = temp * 180 / CV_PI; + + if ((0point.x) && (0point.y)) + { + angle = temp; + return angle; + } + + if ((0>point.x) && (0>point.y)) + { + angle = 180 - temp; + return angle; + } + + printf("sceneDrawing :: getAngle error!"); + return -1; +} +// 一号主变冷却器进油温度表 +//HNHFT001Q01000106 一号主变冷却器进油温度表 150℃ rtsp://admin:shxy@12345@192.168.1.65:554/cam/realmonitor?channel=1&subtype=0 +double dReadPointValue_Wss411_150_T1(ST_OPENCV_CONF *stConf, const char *jpg_file) +{ + double val = stConf->dInitVal; + double threadload = 38; + vector circles; + int radius; + Mat src_gray, disp, detected_edges; + Mat src = imread(jpg_file); + if (!src.data) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "imread picture file :%s failed", jpg_file); + return -1; + } + /// 创建与src同类型和大小的矩阵(dst) + disp.create(src.size(), src.type()); + + cvtColor(src, src_gray, CV_BGR2GRAY); + while (threadload) + { + // 使用 3x3内核降噪 + blur(src_gray, detected_edges, Size(3, 3)); + + // 运行Canny算子 + Canny(detected_edges, detected_edges, threadload, threadload * 3, 3); + disp = Scalar::all(0); + src.copyTo(disp, detected_edges); // 将src经过detected_edges掩码(屏蔽)后输出dst. + HoughCircles(detected_edges, circles, CV_HOUGH_GRADIENT, + 1, //dp,累加器图像的分辨率,增大则分辨率变小 + 100, //minDist,很重要的一个参数,告诉两个圆之间的距离的最小距离,如果已知一副图像,可以先行计 + //算出符合自己需要的两个圆之间的最小距离。 + 150, //param1,canny算法的阈值上限,下限为一半(即100以上为边缘点,50以下抛弃,中间视是否相连而定) + 100, //param2,决定成圆的多寡 ,一个圆上的像素超过这个阈值,则成圆,否则丢弃 + 230, //minRadius,最小圆半径,这个可以通过图片确定你需要的圆的区间范围 + 460 //maxRadius,最大圆半径 + ); + + if (circles.size() <= 0) { + threadload--; + continue; + } + break; + } + if (circles.size() <= 0) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "file :%s,can not found circles.", jpg_file); + disp.release(); // 释放空间 + return stConf->dInitVal; + } + //找出圆盘(因为最大的不一定是的,所以加了几个限制条件) + int pos = 0; + int max = -1; + for (size_t i = 0; i < circles.size(); i++) + { + Vec3f f = circles[i]; + if (f[2]>max && f[0] + f[2] < detected_edges.rows && f[0] - f[2] >= 0 && f[1] + f[2]0) + { + max = (int)f[2]; + pos = i; + } + } + Point center(cvRound(circles[pos][0]), cvRound(circles[pos][1])); + line(disp, center, center, Scalar(0, 0, 255), 4); // 画圆心 + radius = cvRound(circles[pos][2]); + circle(disp, center, radius, Scalar(255, 20, 0), 2); + showImg("circle:", disp); + // 找线段 + vector lines2; + int Len = 0, p = -1; + double imin = 10.0f; + HoughLinesP(detected_edges, lines2, 1, CV_PI / 180, 50, 50, 10); + while (true) + { + for (int n = 0; n < (int)lines2.size(); n++) + { + Point A(lines2[n][0], lines2[n][1]), B(lines2[n][2], lines2[n][3]); // 一条线段的2头坐标点 + Point C((int)circles[0][0], (int)circles[0][1]); // 圆心点坐标 + double vv = DistancetoSegment(C, A, B); + Len = (int)Length(Point(A.x - B.x, A.y - B.y)); + if (Len > 120 && vv < imin) { + line(disp, A, B, Scalar(0, 255, 0), 2, CV_AA); + if (vv < imin) { // 找到离圆心最近的一条线 + p = n; + imin = vv; + } + } + } + //showImg("line2:", disp); + if (p < 0 && imin < radius) { + imin += 1; continue; + } + else break; + } + if (p >= 0) + { + Point A(lines2[p][0], lines2[p][1]), B(lines2[p][2], lines2[p][3]); // 一条线段的2头坐标点 + double du = get_point_angle(B, A); + Len = (int)Length(Point(A.x - B.x, A.y - B.y)); + // val = (du * 10) / (270); + val = dGetReadValueOf_WSS150(du, stConf->base_angle, Len); + if (val < 0) val = stConf->dInitVal; + vPrtLogMsg(LOG_DEBUG, RET_OK, "File:%s Anagle: %.2f° len: %d val:%.2f", jpg_file, du, Len, val); + } +#ifdef _IMG_MIDILL_SAVE + string files = jpg_file; + imwrite(files + ".jpg", disp); +#endif + showImg("line2:", disp); + disp.release(); + return val; +} +double dReadPointValue_Wss411_150_T2(ST_OPENCV_CONF *stConf, const char *jpg_file) +{ + double val = stConf->dInitVal; + double threadload = 53; + vector circles; + int radius; + Mat src_gray, disp, detected_edges; + Mat src = imread(jpg_file); + if (!src.data) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "imread picture file :%s failed", jpg_file); + return -1; + } + /// 创建与src同类型和大小的矩阵(dst) + disp.create(src.size(), src.type()); + + cvtColor(src, src_gray, CV_BGR2GRAY); + while (threadload) + { + // 使用 3x3内核降噪 + blur(src_gray, detected_edges, Size(3, 3)); + + // 运行Canny算子 + Canny(detected_edges, detected_edges, threadload, threadload * 3, 3); + disp = Scalar::all(0); + src.copyTo(disp, detected_edges); // 将src经过detected_edges掩码(屏蔽)后输出dst. + HoughCircles(detected_edges, circles, CV_HOUGH_GRADIENT, + 1, //dp,累加器图像的分辨率,增大则分辨率变小 + 100, //minDist,很重要的一个参数,告诉两个圆之间的距离的最小距离,如果已知一副图像,可以先行计 + //算出符合自己需要的两个圆之间的最小距离。 + 150, //param1,canny算法的阈值上限,下限为一半(即100以上为边缘点,50以下抛弃,中间视是否相连而定) + 100, //param2,决定成圆的多寡 ,一个圆上的像素超过这个阈值,则成圆,否则丢弃 + 230, //minRadius,最小圆半径,这个可以通过图片确定你需要的圆的区间范围 + 460 //maxRadius,最大圆半径 + ); + + if (circles.size() <= 0) { + threadload--; + continue; + } + break; + } + if (circles.size() <= 0) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "file :%s,can not found circles.", jpg_file); + disp.release(); // 释放空间 + return stConf->dInitVal; + } + //找出圆盘(因为最大的不一定是的,所以加了几个限制条件) + int pos = 0; + int max = -1; + for (size_t i = 0; i < circles.size(); i++) + { + Vec3f f = circles[i]; + if (f[2]>max && f[0] + f[2] < detected_edges.rows && f[0] - f[2] >= 0 && f[1] + f[2]0) + { + max = (int)f[2]; + pos = i; + } + } + Point center(cvRound(circles[pos][0]), cvRound(circles[pos][1])); + line(disp, center, center, Scalar(0, 0, 255), 4); // 画圆心 + radius = cvRound(circles[pos][2]); + circle(disp, center, radius, Scalar(255, 20, 0), 2); + showImg("circle:", disp); + // 找线段 + vector lines2; + int Len = 0, p = -1; + double imin = 14.0f; + HoughLinesP(detected_edges, lines2, 1, CV_PI / 180, 50, 50, 10); + while (true) + { + for (int n = 0; n < (int)lines2.size(); n++) + { + Point A(lines2[n][0], lines2[n][1]), B(lines2[n][2], lines2[n][3]); // 一条线段的2头坐标点 + Point C((int)circles[0][0], (int)circles[0][1]); // 圆心点坐标 + double vv = DistancetoSegment(C, A, B); + Len = (int)Length(Point(A.x - B.x, A.y - B.y)); + if (Len > 125 && vv < imin) { + line(disp, A, B, Scalar(0, 255, 0), 2, CV_AA); + if (vv < imin) { // 找到离圆心最近的一条线 + p = n; + imin = vv; + } + } + } + //showImg("line2:", disp); + if (p < 0 && imin < radius) { + imin += 1; continue; + } + else break; + } + if (p >= 0) + { + Point A(lines2[p][0], lines2[p][1]), B(lines2[p][2], lines2[p][3]); // 一条线段的2头坐标点 + double du = get_point_angle(B, A); + Len = (int)Length(Point(A.x - B.x, A.y - B.y)); + // val = (du * 10) / (270); + val = dGetReadValueOf_WSS150(du, stConf->base_angle, Len); + if (val < 0) val = stConf->dInitVal; + vPrtLogMsg(LOG_DEBUG, RET_OK, "File:%s Anagle: %.2f° len: %d val:%.2f", jpg_file, du, Len, val); + } +#ifdef _IMG_MIDILL_SAVE + string files = jpg_file; + imwrite(files + ".jpg", disp); +#endif + showImg("line2:", disp); + disp.release(); + return val; +} +// 3号主变进口油温表分析 +double dReadPointValue_Wss411_150_T3(ST_OPENCV_CONF *stConf, const char *jpg_file) +{ + double val = stConf->dInitVal; + double threadload = 50; //35 + vector circles; + int radius; + Mat src_gray, disp, detected_edges; + Mat src = imread(jpg_file); + if (!src.data) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "imread picture file :%s failed", jpg_file); + return -1; + } + /// 创建与src同类型和大小的矩阵(dst) + disp.create(src.size(), src.type()); + + cvtColor(src, src_gray, CV_BGR2GRAY); + while (threadload) + { + // 使用 3x3内核降噪 + blur(src_gray, detected_edges, Size(3, 3)); + + // 运行Canny算子 + Canny(detected_edges, detected_edges, threadload, threadload * 3, 3); + disp = Scalar::all(0); + src.copyTo(disp, detected_edges); // 将src经过detected_edges掩码(屏蔽)后输出dst. + HoughCircles(detected_edges, circles, CV_HOUGH_GRADIENT, + 1, //dp,累加器图像的分辨率,增大则分辨率变小 + 100, //minDist,很重要的一个参数,告诉两个圆之间的距离的最小距离,如果已知一副图像,可以先行计 + //算出符合自己需要的两个圆之间的最小距离。 + 200, //param1,canny算法的阈值上限,下限为一半(即100以上为边缘点,50以下抛弃,中间视是否相连而定) + 100, //param2,决定成圆的多寡 ,一个圆上的像素超过这个阈值,则成圆,否则丢弃 + 120, //minRadius,最小圆半径,这个可以通过图片确定你需要的圆的区间范围 + 320 //maxRadius,最大圆半径 + ); + + if (circles.size() <= 0) { + threadload--; + continue; + } + break; + } + if (circles.size() <= 0) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "file :%s,can not found circles.", jpg_file); + disp.release(); // 释放空间 + return stConf->dInitVal; + } + //找出圆盘(因为最大的不一定是的,所以加了几个限制条件) + int pos = 0; + int max = -1; + for (size_t i = 0; i < circles.size(); i++) + { + Vec3f f = circles[i]; + if (f[2]>max && f[0] + f[2] < detected_edges.rows && f[0] - f[2] >= 0 && f[1] + f[2]0) + { + max = (int)f[2]; + pos = i; + } + } + Point center(cvRound(circles[pos][0]), cvRound(circles[pos][1])); + line(disp, center, center, Scalar(0, 0, 255), 4); // 画圆心 + radius = cvRound(circles[pos][2]); + circle(disp, center, radius, Scalar(255, 20, 0), 2); + showImg("circle:", disp); + // 找线段 + vector lines2; + int Len = 0, p = -1; + double imin = 10.0f; + HoughLinesP(detected_edges, lines2, 1, CV_PI / 180, 50, 50, 10); + while (true) + { + for (int n = 0; n < (int)lines2.size(); n++) + { + Point A(lines2[n][0], lines2[n][1]), B(lines2[n][2], lines2[n][3]); // 一条线段的2头坐标点 + Point C((int)circles[0][0], (int)circles[0][1]); // 圆心点坐标 + double vv = DistancetoSegment(C, A, B); + Len = (int)Length(Point(A.x - B.x, A.y - B.y)); + if (Len > 125 && vv < imin) { + line(disp, A, B, Scalar(0, 255, 0), 2, CV_AA); + if (vv < imin) { // 找到离圆心最近的一条线 + p = n; + imin = vv; + } + } + } + //showImg("line2:", disp); + if (p < 0 && imin < radius) { + imin += 1; continue; + } + else break; + } + if (p >= 0) + { + Point A(lines2[p][0], lines2[p][1]), B(lines2[p][2], lines2[p][3]); // 一条线段的2头坐标点 + double du = get_point_angle(B, A); + Len = (int)Length(Point(A.x - B.x, A.y - B.y)); + // val = (du * 10) / (270); + val = dGetReadValueOf_WSS150(du, stConf->base_angle, Len); + if (val < 0) val = stConf->dInitVal; + vPrtLogMsg(LOG_DEBUG, RET_OK, "File:%s Anagle: %.2f° len: %d val:%.2f", jpg_file, du, Len, val); + } +#ifdef _IMG_MIDILL_SAVE + string files = jpg_file; + imwrite(files + ".jpg", disp); +#endif + showImg("line2:", disp); + disp.release(); + return val; +} +// 1#主变出口油压力表1.6MPa +double dReadPointValue_OrcPress_16_T1(ST_OPENCV_CONF *stConf, const char *jpg_file) +{ + double val = stConf->dInitVal; + double threadload = 30; + vector circles; + int radius; + Mat src_gray, disp, detected_edges; + Mat src = imread(jpg_file); + if (!src.data) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "imread picture file :%s failed", jpg_file); + return -1; + } + /// 创建与src同类型和大小的矩阵(dst) + disp.create(src.size(), src.type()); + + cvtColor(src, src_gray, CV_BGR2GRAY); + while (threadload) + { + // 使用 3x3内核降噪 + blur(src_gray, detected_edges, Size(3, 3)); + // 运行Canny算子 + Canny(detected_edges, detected_edges, threadload, threadload * 3, 3); + disp = Scalar::all(0); + src.copyTo(disp, detected_edges); // 将src经过detected_edges掩码(屏蔽)后输出dst. + HoughCircles(detected_edges, circles, CV_HOUGH_GRADIENT, + 1, //dp,累加器图像的分辨率,增大则分辨率变小 + 100, //minDist,很重要的一个参数,告诉两个圆之间的距离的最小距离,如果已知一副图像,可以先行计 + //算出符合自己需要的两个圆之间的最小距离。 + 250, //param1,canny算法的阈值上限,下限为一半(即100以上为边缘点,50以下抛弃,中间视是否相连而定) + 200, //param2,决定成圆的多寡 ,一个圆上的像素超过这个阈值,则成圆,否则丢弃 + 220, //minRadius,最小圆半径,这个可以通过图片确定你需要的圆的区间范围 + 440 //maxRadius,最大圆半径 + ); + if (circles.size() <= 0) { + threadload--; + continue; + } + break; + } + if (circles.size() <= 0) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "file :%s,can not found circles.", jpg_file); + disp.release(); // 释放空间 + return stConf->dInitVal; + } + //找出圆盘(因为最大的不一定是的,所以加了几个限制条件) + int pos = 0; + int max = -1; + for (size_t i = 0; i < circles.size(); i++) + { + Vec3f f = circles[i]; + if (f[2]>max && f[0] + f[2] < detected_edges.rows && f[0] - f[2] >= 0 && f[1] + f[2]0) + { + max = (int)f[2]; + pos = i; + } + } + Point center(cvRound(circles[pos][0]), cvRound(circles[pos][1])); + line(disp, center, center, Scalar(0, 0, 255), 4); // 画圆心 + radius = cvRound(circles[pos][2]); + circle(disp, center, radius, Scalar(255, 20, 0), 2); + showImg("circle:", disp); + // 找线段 + vector lines2; + int Len = 0, p = -1; + double imin = 10.0f; + HoughLinesP(detected_edges, lines2, 1, CV_PI / 180, 50, 50, 10); + while (true) { + for (int n = 0; n < (int)lines2.size(); n++) + { + Point A(lines2[n][0], lines2[n][1]), B(lines2[n][2], lines2[n][3]); // 一条线段的2头坐标点 + Point C((int)circles[0][0], (int)circles[0][1]); // 圆心点坐标 + double vv = DistancetoSegment(C, A, B); + Len = (int)Length(Point(A.x - B.x, A.y - B.y)); + if (Len > 250 && vv < imin) { + line(disp, A, B, Scalar(0, 255, 0), 2, CV_AA); + if (vv < imin) { // 找到离圆心最近的一条线 + p = n; + imin = vv; + } + } + } + if (p < 0 && imin < radius) { + imin += 1; continue; + } + else break; + } + showImg(jpg_file, disp); + if (p >= 0) + { + Point A(lines2[p][0], lines2[p][1]), B(lines2[p][2], lines2[p][3]); // 一条线段的2头坐标点 + double du = get_point_angle(B, A) + 2.7f; + //val = (d u * 10) / (248); + Len = (int)Length(Point(A.x - B.x, A.y - B.y)); + val = dGetReadValueOfOCR17NI12MO2_16(du, stConf->base_angle, Len); + if (val < 0) val = stConf->dInitVal; + vPrtLogMsg(LOG_DEBUG, RET_OK, "File:%s Anagle: %.2f° len: %d val:%.2f", jpg_file, du, Len, val); + } +#ifdef _IMG_MIDILL_SAVE + string files = jpg_file; + imwrite(files + ".jpg", disp); +#endif + showImg(jpg_file, disp); + disp.release(); + return val; +} +// 1#主变出口油压力表2.5MPa +double dReadPointValue_OrcPress_25_T2(ST_OPENCV_CONF *stConf, const char *jpg_file) +{ + double val = stConf->dInitVal; + double threadload = 77; + vector circles; + int radius; + Mat src_gray, disp, detected_edges; + Mat src = imread(jpg_file); + if (!src.data) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "imread picture file :%s failed", jpg_file); + return -1; + } + /// 创建与src同类型和大小的矩阵(dst) + disp.create(src.size(), src.type()); + + cvtColor(src, src_gray, CV_BGR2GRAY); + while (threadload) + { + // 使用 3x3内核降噪 + blur(src_gray, detected_edges, Size(3, 3)); + // 运行Canny算子 + Canny(detected_edges, detected_edges, threadload, threadload * 3, 3); + disp = Scalar::all(0); + src.copyTo(disp, detected_edges); // 将src经过detected_edges掩码(屏蔽)后输出dst. + HoughCircles(detected_edges, circles, CV_HOUGH_GRADIENT, + 1, //dp,累加器图像的分辨率,增大则分辨率变小 + 100, //minDist,很重要的一个参数,告诉两个圆之间的距离的最小距离,如果已知一副图像,可以先行计 + //算出符合自己需要的两个圆之间的最小距离。 + 150, //param1,canny算法的阈值上限,下限为一半(即100以上为边缘点,50以下抛弃,中间视是否相连而定) + 100, //param2,决定成圆的多寡 ,一个圆上的像素超过这个阈值,则成圆,否则丢弃 + 230, //minRadius,最小圆半径,这个可以通过图片确定你需要的圆的区间范围 + 460 //maxRadius,最大圆半径 + ); + if (circles.size() <= 0) { + threadload--; + continue; + } + break; + } + if (circles.size() <= 0) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "file :%s,can not found circles.", jpg_file); + disp.release(); // 释放空间 + return stConf->dInitVal; + } + //找出圆盘(因为最大的不一定是的,所以加了几个限制条件) + int pos = 0; + int max = -1; + for (size_t i = 0; i < circles.size(); i++) + { + Vec3f f = circles[i]; + if (f[2]>max && f[0] + f[2] < detected_edges.rows && f[0] - f[2] >= 0 && f[1] + f[2]0) + { + max = (int)f[2]; + pos = i; + } + } + Point center(cvRound(circles[pos][0]), cvRound(circles[pos][1])); + line(disp, center, center, Scalar(0, 0, 255), 4); // 画圆心 + radius = cvRound(circles[pos][2]); + circle(disp, center, radius, Scalar(255, 20, 0), 2); + showImg("circle:", disp); + // 找线段 + vector lines2; + int Len = 0, p = -1; + double imin = 26.0f; + HoughLinesP(detected_edges, lines2, 1, CV_PI / 180, 50, 50, 10); + while (true) { + for (int n = 0; n < (int)lines2.size(); n++) + { + Point A(lines2[n][0], lines2[n][1]), B(lines2[n][2], lines2[n][3]); // 一条线段的2头坐标点 + Point C((int)circles[0][0], (int)circles[0][1]); // 圆心点坐标 + double vv = DistancetoSegment(C, A, B); + Len = (int)Length(Point(A.x - B.x, A.y - B.y)); + if (Len > 120 && vv < imin) { + line(disp, A, B, Scalar(0, 255, 0), 2, CV_AA); + if (vv < imin) { // 找到离圆心最近的一条线 + p = n; + imin = vv; + } + } + } + if (p < 0 && imin < radius) { + imin += 1; continue; + } + else break; + } + showImg(jpg_file, disp); + if (p >= 0) + { + Point A(lines2[p][0], lines2[p][1]), B(lines2[p][2], lines2[p][3]); // 一条线段的2头坐标点 + double du = get_point_angle(B, A) ; + //val = (du * 10) / (248); + Len = (int)Length(Point(A.x - B.x, A.y - B.y)); + val = dGetReadValueOfOCR17NI12MO2_25(du, stConf->base_angle, Len); + if (val < 0) val = stConf->dInitVal; + vPrtLogMsg(LOG_DEBUG, RET_OK, "File:%s Anagle: %.2f° len: %d val:%.2f", jpg_file, du, Len, val); + } +#ifdef _IMG_MIDILL_SAVE + string files = jpg_file; + imwrite(files + ".jpg", disp); +#endif + showImg(jpg_file, disp); + disp.release(); + return val; +} +// 3#主变出口油压力表2.5MPa +double dReadPointValue_OrcPress_25_T3(ST_OPENCV_CONF *stConf, const char *jpg_file) +{ + double val = stConf->dInitVal; + double threadload = 48; + vector circles; + int radius; + Mat src_gray, disp, detected_edges; + Mat src = imread(jpg_file); + if (!src.data) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "imread picture file :%s failed", jpg_file); + return -1; + } + /// 创建与src同类型和大小的矩阵(dst) + disp.create(src.size(), src.type()); + + cvtColor(src, src_gray, CV_BGR2GRAY); + while (threadload) + { + // 使用 3x3内核降噪 + blur(src_gray, detected_edges, Size(3, 3)); + // 运行Canny算子 + Canny(detected_edges, detected_edges, threadload, threadload * 3, 3); + disp = Scalar::all(0); + src.copyTo(disp, detected_edges); // 将src经过detected_edges掩码(屏蔽)后输出dst. + HoughCircles(detected_edges, circles, CV_HOUGH_GRADIENT, + 1, //dp,累加器图像的分辨率,增大则分辨率变小 + 100, //minDist,很重要的一个参数,告诉两个圆之间的距离的最小距离,如果已知一副图像,可以先行计 + //算出符合自己需要的两个圆之间的最小距离。 + 200, //param1,canny算法的阈值上限,下限为一半(即100以上为边缘点,50以下抛弃,中间视是否相连而定) + 100, //param2,决定成圆的多寡 ,一个圆上的像素超过这个阈值,则成圆,否则丢弃 + 150, //minRadius,最小圆半径,这个可以通过图片确定你需要的圆的区间范围 + 320 //maxRadius,最大圆半径 + ); + if (circles.size() <= 0) { + threadload--; + continue; + } + break; + } + if (circles.size() <= 0) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "file :%s,can not found circles.", jpg_file); + disp.release(); // 释放空间 + return stConf->dInitVal; + } + //找出圆盘(因为最大的不一定是的,所以加了几个限制条件) + int pos = 0; + int max = -1; + for (size_t i = 0; i < circles.size(); i++) + { + Vec3f f = circles[i]; + if (f[2]>max && f[0] + f[2] < detected_edges.rows && f[0] - f[2] >= 0 && f[1] + f[2]0) + { + max = (int)f[2]; + pos = i; + } + } + Point center(cvRound(circles[pos][0]), cvRound(circles[pos][1])); + line(disp, center, center, Scalar(0, 0, 255), 4); // 画圆心 + radius = cvRound(circles[pos][2]); + circle(disp, center, radius, Scalar(255, 20, 0), 2); + showImg("circle:", disp); + // 找线段 + vector lines2; + int Len = 0, p = -1; + double imin = 10.0f; + HoughLinesP(detected_edges, lines2, 1, CV_PI / 180, 50, 50, 10); + while (true) { + for (int n = 0; n < (int)lines2.size(); n++) + { + Point A(lines2[n][0], lines2[n][1]), B(lines2[n][2], lines2[n][3]); // 一条线段的2头坐标点 + Point C((int)circles[0][0], (int)circles[0][1]); // 圆心点坐标 + double vv = DistancetoSegment(C, A, B); + Len = (int)Length(Point(A.x - B.x, A.y - B.y)); + if (Len > 165 && vv < imin) { + line(disp, A, B, Scalar(0, 255, 0), 2, CV_AA); + if (vv < imin) { // 找到离圆心最近的一条线 + p = n; + imin = vv; + } + } + } + if (p < 0 && imin < radius) { + imin += 1; continue; + } + else break; + } + showImg(jpg_file, disp); + if (p >= 0) + { + Point A(lines2[p][0], lines2[p][1]), B(lines2[p][2], lines2[p][3]); // 一条线段的2头坐标点 + double du = get_point_angle(B, A); + //val = (du * 10) / (248); + Len = (int)Length(Point(A.x - B.x, A.y - B.y)); + val = dGetReadValueOfOCR17NI12MO2_25(du, stConf->base_angle, Len); + if (val < 0) val = stConf->dInitVal; + vPrtLogMsg(LOG_DEBUG, RET_OK, "File:%s Anagle: %.2f° len: %d val:%.2f", jpg_file, du, Len, val); + } +#ifdef _IMG_MIDILL_SAVE + string files = jpg_file; + imwrite(files + ".jpg", disp); +#endif + showImg(jpg_file, disp); + disp.release(); + return val; +} +// 3#主变出口油压力表2.5MPa +double dReadPointValue_OrcPress_25_T4(ST_OPENCV_CONF *stConf, const char *jpg_file) +{ + double val = stConf->dInitVal; + double threadload = 48; + vector circles; + int radius; + Mat src_gray, disp, detected_edges; + Mat src = imread(jpg_file); + if (!src.data) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "imread picture file :%s failed", jpg_file); + return -1; + } + /// 创建与src同类型和大小的矩阵(dst) + disp.create(src.size(), src.type()); + + cvtColor(src, src_gray, CV_BGR2GRAY); + while (threadload) + { + // 使用 3x3内核降噪 + blur(src_gray, detected_edges, Size(3, 3)); + // 运行Canny算子 + Canny(detected_edges, detected_edges, threadload, threadload * 3, 3); + disp = Scalar::all(0); + src.copyTo(disp, detected_edges); // 将src经过detected_edges掩码(屏蔽)后输出dst. + HoughCircles(detected_edges, circles, CV_HOUGH_GRADIENT, + 1, //dp,累加器图像的分辨率,增大则分辨率变小 + 100, //minDist,很重要的一个参数,告诉两个圆之间的距离的最小距离,如果已知一副图像,可以先行计 + //算出符合自己需要的两个圆之间的最小距离。 + 200, //param1,canny算法的阈值上限,下限为一半(即100以上为边缘点,50以下抛弃,中间视是否相连而定) + 100, //param2,决定成圆的多寡 ,一个圆上的像素超过这个阈值,则成圆,否则丢弃 + 150, //minRadius,最小圆半径,这个可以通过图片确定你需要的圆的区间范围 + 320 //maxRadius,最大圆半径 + ); + if (circles.size() <= 0) { + threadload--; + continue; + } + break; + } + if (circles.size() <= 0) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "file :%s,can not found circles.", jpg_file); + disp.release(); // 释放空间 + return stConf->dInitVal; + } + //找出圆盘(因为最大的不一定是的,所以加了几个限制条件) + int pos = 0; + int max = -1; + for (size_t i = 0; i < circles.size(); i++) + { + Vec3f f = circles[i]; + if (f[2]>max && f[0] + f[2] < detected_edges.rows && f[0] - f[2] >= 0 && f[1] + f[2]0) + { + max = (int)f[2]; + pos = i; + } + } + Point center(cvRound(circles[pos][0]), cvRound(circles[pos][1])); + line(disp, center, center, Scalar(0, 0, 255), 4); // 画圆心 + radius = cvRound(circles[pos][2]); + circle(disp, center, radius, Scalar(255, 20, 0), 2); + showImg("circle:", disp); + // 找线段 + vector lines2; + int Len = 0, p = -1; + double imin = 10.0f; + HoughLinesP(detected_edges, lines2, 1, CV_PI / 180, 50, 50, 10); + while (true) { + for (int n = 0; n < (int)lines2.size(); n++) + { + Point A(lines2[n][0], lines2[n][1]), B(lines2[n][2], lines2[n][3]); // 一条线段的2头坐标点 + Point C((int)circles[0][0], (int)circles[0][1]); // 圆心点坐标 + double vv = DistancetoSegment(C, A, B); + Len = (int)Length(Point(A.x - B.x, A.y - B.y)); + if (Len > 165 && vv < imin) { + line(disp, A, B, Scalar(0, 255, 0), 2, CV_AA); + if (vv < imin) { // 找到离圆心最近的一条线 + p = n; + imin = vv; + } + } + } + if (p < 0 && imin < radius) { + imin += 1; continue; + } + else break; + } + showImg(jpg_file, disp); + if (p >= 0) + { + Point A(lines2[p][0], lines2[p][1]), B(lines2[p][2], lines2[p][3]); // 一条线段的2头坐标点 + double du = get_point_angle(B, A); + //val = (du * 10) / (248); + Len = (int)Length(Point(A.x - B.x, A.y - B.y)); + val = dGetReadValueOfOCR17NI12MO2_25(du, stConf->base_angle, Len); + if (val < 0) val = stConf->dInitVal; + vPrtLogMsg(LOG_DEBUG, RET_OK, "File:%s Anagle: %.2f° len: %d val:%.2f", jpg_file, du, Len, val); + } +#ifdef _IMG_MIDILL_SAVE + string files = jpg_file; + imwrite(files + ".jpg", disp); +#endif + showImg(jpg_file, disp); + disp.release(); + return val; +} +// 主变1油表 +double dReadPointValue_Wss411_100_T1(ST_OPENCV_CONF *stConf, const char *jpg_file) +{ + double val = stConf->dInitVal; + double threadload = 52; + vector circles; + int radius; + Mat src_gray, disp, detected_edges; + Mat src = imread(jpg_file); + if (!src.data) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "imread picture file :%s failed", jpg_file); + return -1; + } + /// 创建与src同类型和大小的矩阵(dst) + disp.create(src.size(), src.type()); + + cvtColor(src, src_gray, CV_BGR2GRAY); + while (threadload) + { + // 使用 3x3内核降噪 + blur(src_gray, detected_edges, Size(3, 3)); + // 运行Canny算子 + Canny(detected_edges, detected_edges, threadload, threadload * 3, 3); + disp = Scalar::all(0); + src.copyTo(disp, detected_edges); // 将src经过detected_edges掩码(屏蔽)后输出dst. + HoughCircles(detected_edges, circles, CV_HOUGH_GRADIENT, + 1, //dp,累加器图像的分辨率,增大则分辨率变小 + 100, //minDist,很重要的一个参数,告诉两个圆之间的距离的最小距离,如果已知一副图像,可以先行计 + //算出符合自己需要的两个圆之间的最小距离。 + 150, //param1,canny算法的阈值上限,下限为一半(即100以上为边缘点,50以下抛弃,中间视是否相连而定) + 100, //param2,决定成圆的多寡 ,一个圆上的像素超过这个阈值,则成圆,否则丢弃 + 220, //minRadius,最小圆半径,这个可以通过图片确定你需要的圆的区间范围 + 440 //maxRadius,最大圆半径 + ); + if (circles.size() <= 0) { + threadload--; + continue; + } + break; + } + if (circles.size() <= 0) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "file :%s,can not found circles.", jpg_file); + disp.release(); // 释放空间 + return stConf->dInitVal; + } + //找出圆盘(因为最大的不一定是的,所以加了几个限制条件) + int pos = 0; + int max = -1; + for (size_t i = 0; i < circles.size(); i++) + { + Vec3f f = circles[i]; + if (f[2]>max && f[0] + f[2] < detected_edges.rows && f[0] - f[2] >= 0 && f[1] + f[2]0) + { + max = (int)f[2]; + pos = i; + } + } + Point center(cvRound(circles[pos][0]), cvRound(circles[pos][1])); + line(disp, center, center, Scalar(0, 0, 255), 4); // 画圆心 + radius = cvRound(circles[pos][2]); + circle(disp, center, radius, Scalar(255, 20, 0), 2); + showImg("circle:", disp); + // 找线段 + vector lines2; + int Len = 0, p = -1; + double imin = 10.0f; + HoughLinesP(detected_edges, lines2, 1, CV_PI / 180, 50, 50, 10); + while (true) { + for (int n = 0; n < (int)lines2.size(); n++) + { + Point A(lines2[n][0], lines2[n][1]), B(lines2[n][2], lines2[n][3]); // 一条线段的2头坐标点 + Point C((int)circles[0][0], (int)circles[0][1]); // 圆心点坐标 + double vv = DistancetoSegment(C, A, B); + Len = (int)Length(Point(A.x - B.x, A.y - B.y)); + if (Len > 210 && vv < imin) { + line(disp, A, B, Scalar(0, 255, 0), 2, CV_AA); + if (vv < imin) { // 找到离圆心最近的一条线 + p = n; + imin = vv; + } + } + } + if (p < 0 && imin < radius) { + imin += 1; continue; + } + else break; + } + if (p >= 0) { + Point A(lines2[p][0], lines2[p][1]), B(lines2[p][2], lines2[p][3]); // 一条线段的2头坐标点 + double du = get_point_angle(B, A);// +2.7f; + Len = (int)Length(Point(A.x - B.x, A.y - B.y)); + val = dReadPointValue_Wss411_100(du, stConf->base_angle, Len); + if (val < 0) val = stConf->dInitVal; + vPrtLogMsg(LOG_DEBUG, RET_OK, "File:%s Anagle: %.2f° len: %d val:%.2f", jpg_file, du, Len, val); + } +#ifdef _IMG_MIDILL_SAVE + string files = jpg_file; + imwrite(files + ".jpg", disp); +#endif + showImg("line2:", disp); + disp.release(); + return val; +} +// 主变1油表 +double dReadPointValue_Wss411_100_T1_2(ST_OPENCV_CONF *stConf, const char *jpg_file) +{ + double val = stConf->dInitVal; + double threadload = 53; + vector circles; + int radius; + Mat src_gray, disp, detected_edges; + Mat src = imread(jpg_file); + if (!src.data) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "imread picture file :%s failed", jpg_file); + return -1; + } + /// 创建与src同类型和大小的矩阵(dst) + disp.create(src.size(), src.type()); + + cvtColor(src, src_gray, CV_BGR2GRAY); + while (threadload) + { + // 使用 3x3内核降噪 + blur(src_gray, detected_edges, Size(3, 3)); + // 运行Canny算子 + Canny(detected_edges, detected_edges, threadload, threadload * 3, 3); + disp = Scalar::all(0); + src.copyTo(disp, detected_edges); // 将src经过detected_edges掩码(屏蔽)后输出dst. + HoughCircles(detected_edges, circles, CV_HOUGH_GRADIENT, + 1, //dp,累加器图像的分辨率,增大则分辨率变小 + 100, //minDist,很重要的一个参数,告诉两个圆之间的距离的最小距离,如果已知一副图像,可以先行计 + //算出符合自己需要的两个圆之间的最小距离。 + 150, //param1,canny算法的阈值上限,下限为一半(即100以上为边缘点,50以下抛弃,中间视是否相连而定) + 100, //param2,决定成圆的多寡 ,一个圆上的像素超过这个阈值,则成圆,否则丢弃 + 200, //minRadius,最小圆半径,这个可以通过图片确定你需要的圆的区间范围 + 440 //maxRadius,最大圆半径 + ); + if (circles.size() <= 0) { + threadload--; + continue; + } + break; + } + if (circles.size() <= 0) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "file :%s,can not found circles.", jpg_file); + disp.release(); // 释放空间 + return stConf->dInitVal; + } + //找出圆盘(因为最大的不一定是的,所以加了几个限制条件) + int pos = 0; + int max = -1; + for (size_t i = 0; i < circles.size(); i++) + { + Vec3f f = circles[i]; + if (f[2]>max && f[0] + f[2] < detected_edges.rows && f[0] - f[2] >= 0 && f[1] + f[2]0) + { + max = (int)f[2]; + pos = i; + } + } + Point center(cvRound(circles[pos][0]), cvRound(circles[pos][1])); + line(disp, center, center, Scalar(0, 0, 255), 4); // 画圆心 + radius = cvRound(circles[pos][2]); + circle(disp, center, radius, Scalar(255, 20, 0), 2); + showImg("circle:", disp); + // 找线段 + vector lines2; + int Len = 0, p = -1; + double imin = 40.0f; + HoughLinesP(detected_edges, lines2, 1, CV_PI / 180, 50, 50, 10); + while (true) { + for (int n = 0; n < (int)lines2.size(); n++) + { + Point A(lines2[n][0], lines2[n][1]), B(lines2[n][2], lines2[n][3]); // 一条线段的2头坐标点 + Point C((int)circles[0][0], (int)circles[0][1]); // 圆心点坐标 + double vv = DistancetoSegment(C, A, B); + Len = (int)Length(Point(A.x - B.x, A.y - B.y)); + if (Len > 150 && vv < imin) { + line(disp, A, B, Scalar(0, 255, 0), 2, CV_AA); + if (vv < imin) { // 找到离圆心最近的一条线 + p = n; + imin = vv; + } + } + } + if (p < 0 && imin < radius) { + imin += 1; continue; + } + else break; + } + if (p >= 0) { + Point A(lines2[p][0], lines2[p][1]), B(lines2[p][2], lines2[p][3]); // 一条线段的2头坐标点 + double du = get_point_angle(B, A);// +2.7f; + Len = (int)Length(Point(A.x - B.x, A.y - B.y)); + val = dReadPointValue_Wss411_100(du, stConf->base_angle, Len); + if (val < 0) val = stConf->dInitVal; + vPrtLogMsg(LOG_DEBUG, RET_OK, "File:%s Anagle: %.2f° len: %d val:%.2f", jpg_file, du, Len, val); + } +#ifdef _IMG_MIDILL_SAVE + string files = jpg_file; + imwrite(files + ".jpg", disp); +#endif + showImg("line2:", disp); + disp.release(); + return val; +} +// 主变1油表 +double dReadPointValue_Wss411_100_T2(ST_OPENCV_CONF *stConf, const char *jpg_file) +{ + double val = stConf->dInitVal; + double threadload = 18; + vector circles; + int radius; + Mat src_gray, disp, detected_edges; + Mat src = imread(jpg_file); + if (!src.data) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "imread picture file :%s failed", jpg_file); + return -1; + } + /// 创建与src同类型和大小的矩阵(dst) + disp.create(src.size(), src.type()); + + cvtColor(src, src_gray, CV_BGR2GRAY); + while (threadload) + { + // 使用 3x3内核降噪 + blur(src_gray, detected_edges, Size(3, 3)); + // 运行Canny算子 + Canny(detected_edges, detected_edges, threadload, threadload * 3, 3); + disp = Scalar::all(0); + src.copyTo(disp, detected_edges); // 将src经过detected_edges掩码(屏蔽)后输出dst. + HoughCircles(detected_edges, circles, CV_HOUGH_GRADIENT, + 1, //dp,累加器图像的分辨率,增大则分辨率变小 + 150, //minDist,很重要的一个参数,告诉两个圆之间的距离的最小距离,如果已知一副图像,可以先行计 + //算出符合自己需要的两个圆之间的最小距离。 + 210, //param1,canny算法的阈值上限,下限为一半(即100以上为边缘点,50以下抛弃,中间视是否相连而定) + 200, //param2,决定成圆的多寡 ,一个圆上的像素超过这个阈值,则成圆,否则丢弃 + 200, //minRadius,最小圆半径,这个可以通过图片确定你需要的圆的区间范围 + 400 //maxRadius,最大圆半径 + ); + if (circles.size() <= 0) { + threadload--; + continue; + } + break; + } + if (circles.size() <= 0) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "file :%s,can not found circles.", jpg_file); + disp.release(); // 释放空间 + return stConf->dInitVal; + } + //找出圆盘(因为最大的不一定是的,所以加了几个限制条件) + int pos = 0; + int max = -1; + for (size_t i = 0; i < circles.size(); i++) + { + Vec3f f = circles[i]; + if (f[2]>max && f[0] + f[2] < detected_edges.rows && f[0] - f[2] >= 0 && f[1] + f[2]0) + { + max = (int)f[2]; + pos = i; + } + } + Point center(cvRound(circles[pos][0]), cvRound(circles[pos][1])); + line(disp, center, center, Scalar(0, 0, 255), 4); // 画圆心 + radius = cvRound(circles[pos][2]); + circle(disp, center, radius, Scalar(255, 20, 0), 2); + showImg("circle:", disp); + // 找线段 + vector lines2; + int Len = 0, p = -1; + double imin = 10.0f; + HoughLinesP(detected_edges, lines2, 1, CV_PI / 180, 50, 50, 10); + while (true) { + for (int n = 0; n < (int)lines2.size(); n++) + { + Point A(lines2[n][0], lines2[n][1]), B(lines2[n][2], lines2[n][3]); // 一条线段的2头坐标点 + Point C((int)circles[0][0], (int)circles[0][1]); // 圆心点坐标 + double vv = DistancetoSegment(C, A, B); + Len = (int)Length(Point(A.x - B.x, A.y - B.y)); + if (Len > 190 && vv < imin) { + line(disp, A, B, Scalar(0, 255, 0), 2, CV_AA); + if (vv < imin) { // 找到离圆心最近的一条线 + p = n; + imin = vv; + } + } + } + if (p < 0 && imin < radius) { + imin += 1; continue; + } + else break; + } + if (p >= 0) { + Point A(lines2[p][0], lines2[p][1]), B(lines2[p][2], lines2[p][3]); // 一条线段的2头坐标点 + double du = get_point_angle(B, A);// +2.7f; + Len = (int)Length(Point(A.x - B.x, A.y - B.y)); + val = dReadPointValue_Wss411_100(du, stConf->base_angle, Len); + if (val < 0) val = stConf->dInitVal; + vPrtLogMsg(LOG_DEBUG, RET_OK, "File:%s Anagle: %.2f° len: %d val:%.2f", jpg_file, du, Len, val); + } +#ifdef _IMG_MIDILL_SAVE + string files = jpg_file; + imwrite(files + ".jpg", disp); +#endif + showImg("line2:", disp); + disp.release(); + return val; +} +// 主变2油表 +double dReadPointValue_Wss411_100_T3(ST_OPENCV_CONF *stConf, const char *jpg_file) +{ + double val = stConf->dInitVal; + double threadload = 35; + vector circles; + int radius; + Mat src_gray, disp, detected_edges; + Mat src = imread(jpg_file); + if (!src.data) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "imread picture file :%s failed", jpg_file); + return -1; + } + /// 创建与src同类型和大小的矩阵(dst) + disp.create(src.size(), src.type()); + cvtColor(src, src_gray, CV_BGR2GRAY); + + while (threadload) + { + // 使用 3x3内核降噪 + blur(src_gray, detected_edges, Size(3, 3)); + // 运行Canny算子 + Canny(detected_edges, detected_edges, threadload, threadload * 3, 3); + disp = Scalar::all(0); + src.copyTo(disp, detected_edges); // 将src经过detected_edges掩码(屏蔽)后输出dst. + HoughCircles(detected_edges, circles, CV_HOUGH_GRADIENT, + 1, //dp,累加器图像的分辨率,增大则分辨率变小 + 100, //minDist,很重要的一个参数,告诉两个圆之间的距离的最小距离,如果已知一副图像,可以先行计 + //算出符合自己需要的两个圆之间的最小距离。 + 220, //param1,canny算法的阈值上限,下限为一半(即100以上为边缘点,50以下抛弃,中间视是否相连而定) + 200, //param2,决定成圆的多寡 ,一个圆上的像素超过这个阈值,则成圆,否则丢弃 + 230, //minRadius,最小圆半径,这个可以通过图片确定你需要的圆的区间范围 + 460 //maxRadius,最大圆半径 + ); + if (circles.size() <= 0) { + threadload--; + continue; + } + break; + } + if (circles.size() <= 0) { + vPrtLogMsg(LOG_WARNG, RET_FAIL, "file :%s,can not found circles.", jpg_file); + disp.release(); // 释放空间 + return stConf->dInitVal; + } + //找出圆盘(因为最大的不一定是的,所以加了几个限制条件) + int pos = 0; + int max = -1; + for (size_t i = 0; i < circles.size(); i++) + { + Vec3f f = circles[i]; + if (f[2]>max && f[0] + f[2] < detected_edges.rows && f[0] - f[2] >= 0 && f[1] + f[2]0) + { + max = (int)f[2]; + pos = i; + } + } + Point center(cvRound(circles[pos][0]), cvRound(circles[pos][1])); + line(disp, center, center, Scalar(0, 0, 255), 4); // 画圆心 + radius = cvRound(circles[pos][2]); + circle(disp, center, radius, Scalar(255, 20, 0), 2); + showImg("circle:", disp); + // 找线段 + vector lines2; + int Len = 0, p = -1; + double imin = 10.0f; + HoughLinesP(detected_edges, lines2, 1, CV_PI / 180, 50, 50, 10); + while (true) { + for (int n = 0; n < (int)lines2.size(); n++) + { + Point A(lines2[n][0], lines2[n][1]), B(lines2[n][2], lines2[n][3]); // 一条线段的2头坐标点 + Point C((int)circles[0][0], (int)circles[0][1]); // 圆心点坐标 + double vv = DistancetoSegment(C, A, B); + Len = (int)Length(Point(A.x - B.x, A.y - B.y)); + if (Len > 180 && vv < imin) { + line(disp, A, B, Scalar(0, 255, 0), 2, CV_AA); + if (vv < imin) { // 找到离圆心最近的一条线 + p = n; + imin = vv; + } + } + } + if (p < 0 && imin < radius) { + imin += 1; continue; + } + else break; + } + if (p >= 0) { + Point A(lines2[p][0], lines2[p][1]), B(lines2[p][2], lines2[p][3]); // 一条线段的2头坐标点 + double du = get_point_angle(B, A) - 2.7f; + //val = (du * 10) / 205; + Len = (int)Length(Point(A.x - B.x, A.y - B.y)); + val = dReadPointValue_Wss411_100(du, stConf->base_angle, Len); + vPrtLogMsg(LOG_DEBUG, RET_OK, "File:%s Anagle: %.2f° len: %d val:%.2f", jpg_file, du, Len, val); + } +#ifdef _IMG_MIDILL_SAVE + string files = jpg_file; + imwrite(files + ".jpg", disp); +#endif + showImg("line2:", disp); + disp.release(); + return val; +} +/* 根据配置文件的仪表单位,转为数据库表字段的单位 +# ------------------------------------------------------------------- +# 仪表单位类型及单位定义对应关系表 +# ---------------------------------------- +# 类型 | 单位定义 | +# -------------+-------------------------+ +# meter_type | meter_units | +# -------------+-------------------+-----+ +# | 0 | 1 | 2 | 3 | +# = 0 +-------------------+-----+ +# (电流) | 微安 | 毫安 | 安 |千安 | +# +-------------------+-----+ +# | μA | mA | A | kA | +#--------------+-------------------+-----+ +# = 1 | 帕 | 百帕 | 千帕 |兆帕 | +# (压力) +-------------------+-----+ +# | Pa | HPa | KPa | MPa | +#--------------+-------------------+-----+ +# = 2 | 微升 | 毫升 | 升 |千升 | +# (容量) +-------------------+-----+ +# | μL | mL | L | kL | +#--------------+-------------------+-----+ +# = 3 | 摄氏 | 华氏 | | | +# (温度) +-------------------+-----+ +# | ℃ | ℉ | | | +#--------------+-------------------+-----+ +# = 4 | 0 | | | | +# (计数器) +-------------------+-----+ +# | 次 | | | | +********************************************************************/ +double dChangeUnits(unsigned char *sersorid, double inVal) +{ + map::iterator m_pIter; + unsigned char meter_type, meter_unit; + + string strKey = (char*)sersorid; + m_pIter = g_conf_img.find(strKey); + + if (m_pIter == g_conf_img.end()) return inVal; // 没找到配置的单位,则按读到的数据返回即可。 + meter_type = (m_pIter->second.meter_type & 0xff); + meter_unit = (m_pIter->second.meter_units & 0xff); + switch (meter_type) + { + case 0: // 电流表,统一为mA + if (meter_unit == 0) //微安μA + inVal = (inVal / 1000); + else if (meter_unit == 1) + return inVal; //毫安mA + else if (meter_unit == 2) //安A + inVal = (inVal * 1000); + else if (meter_unit == 3) //千安kA + inVal = (inVal * 1000 * 1000); + break; + case 1: // 压力表,统一为MPa + if (meter_unit == 0) //帕Pa + inVal = (inVal / 1000 / 1000); + else if (meter_unit == 1) //百帕HPa + inVal = (inVal / 1000 / 100); + else if (meter_unit == 2) //千帕KPa + inVal = (inVal / 1000); + else if (meter_unit == 3) //兆帕MPa + return inVal; + break; + case 2: // 容量表/油位表...,0:缺油 1:正常,两种状态即可 + break; + case 3: // 温度表,统一为摄氏度° + if (meter_unit == 0) //度℃ + return inVal; + if (meter_unit == 1) //华氏度 + inVal = (inVal - 32 / 1.800f); + break; + case 4: // 计数器,放电次数 + break; + default:; // 未知表类型 + } + return inVal; +} + +// 获取sersorid对应的告警阈值 +// +//static double dGetWarningValue(const char *sersorid, ST_BLQ_CACHE &stBlq) +//{ +// if (sersorid == NULL || strlen(sersorid) <=0) return 0.0f; +// +// string strKey = sersorid; +// map::iterator m_pIter; +// mutex_lock(g_map_blq_mutex); +// m_pIter = g_map_blq.find(strKey); +// if(m_pIter == g_map_blq.end()) +// { +// mutex_unlock(g_map_blq_mutex); +// vPrtLogMsg(LOG_WARNG, RET_FAIL, "Can't find sersorid:%s for set warning value.",sersorid); +// return 0.0f; +// } +// memcpy(&stBlq, &(m_pIter->second), sizeof(ST_BLQ_CACHE)); +// mutex_unlock(g_map_blq_mutex); +// return stBlq.dMaxDL; +//} + +// 告警数据入库 +static void dbInsertImgageWarning(ST_IMG_THRESHOLD &stCache, double dval, double threshold, unsigned char wstate, unsigned char eqm_type, char *pWarnTime) +{ + char szSql[DEF_BUFFER_1K] = { 0 }, szFormatTime[32] = { 0 }; + char szUuid[64] = { 0 }; + char szWarninfo[DEF_BUFFER_256] = { 0 }; + + vFormatTimes(pWarnTime, szFormatTime); // yyyymmddhhmiss-->yyyy-mm-dd hh24:mi:ss + CDBMySQL *pdbHandle = CDBMySQL::Instance(); + if (!pdbHandle) return; + + getuuid(szUuid, sizeof(szUuid)); + + snprintf(szSql, sizeof(szSql),"INSERT INTO busi_eqm_warning(id,eq_type,gz_type,warning_msg,warning_time,create_time,del_flag,site_id, main_tree_id, sensor_id,run_state) " + "VALUES('%s','%d', '11', '%s:%.4f', '%s', SYSDATE(), '1', '%s', '%s', '%s','%d')", szUuid,eqm_type,stCache.desc, dval, pWarnTime, stCache.site_id, stCache.eqm_code, stCache.sensor_id,wstate); + vPrtLogMsg(LOG_DEBUG, RET_OK, "dbInsertWaring: %s", szSql); + if (!pdbHandle->InsertRecord((const char *)szSql)) { + vPrtLogMsg(LOG_ERROR, RET_FAIL, "INSERT INTO busi_eqm_warning message failed,SQL:%s", szSql); + return; + } + snprintf(szWarninfo, sizeof(szWarninfo), + "{\"sadr\":\"\",\"thresholdValue\":\"%.4f\",\"currRealValue\":\"%.4f\",\"equipmentStatus\":\"%d\",\"collectDate\":\"%s\",\"ipaddr\":\"\",\"port\":\"\",\"msgInfo\":\"%s\",\"phase\":\"%c\"}", + threshold, dval, wstate, szFormatTime, stCache.desc, stCache.phase); + snprintf(szSql, sizeof(szSql), "INSERT INTO busi_eqm_warning_info(id,warning_id,warning_info) VALUES(guuid(),'%s','%s')", szUuid, szWarninfo); + + vPrtLogMsg(LOG_DEBUG, RET_OK, "dbInsertWaring: %s", szSql); + if (!pdbHandle->InsertRecord((const char *)szSql)) { + vPrtLogMsg(LOG_ERROR, RET_FAIL, "INSERT INTO busi_eqm_warning message failed,SQL:%s", szSql); + return; + } +} + +//BLQ入库仪表解析数据 +static void dbBLQInsertData(ST_IMG_THRESHOLD &stBlqCache, double dval, const char *filepath, char *pTime, int wstate) +{ + char szSql[DEF_BUFFER_1K] = { 0 }; + char szHbaseKey[DEF_BUFFER_128] = { 0 }; // hbase同步key. + char szSortTime[24] = { 0 }; + + memcpy(szSortTime, pTime, strlen(pTime)); + strSwap(szSortTime, strlen(szSortTime)); + + CDBMySQL *pdbHandle = CDBMySQL::Instance(); + if (!pdbHandle) return; + + sprintf(szHbaseKey, "%s_%s_%s_%s", szSortTime, stBlqCache.sensor_id, pTime, stBlqCache.eqm_code); + sprintf(szSql, "INSERT INTO arrester_read_table(id, site_id, system_code, sensor_code,dlval,time_stamp,work_state,main_tree_id,picpath,hbase_rowkey) " + "VALUES(guuid(),'%s', '%s', '%s', '%.4f','%s','%d','%s','%s','%s')", stBlqCache.site_id, stBlqCache.system_code, stBlqCache.sensor_id, + dval, pTime, wstate, stBlqCache.eqm_code, filepath, szHbaseKey); + + vPrtLogMsg(LOG_DEBUG, RET_OK, "dbBLQInsertData: %s", szSql); + if (!pdbHandle->InsertRecord((const char *)szSql)) { + vPrtLogMsg(LOG_ERROR, RET_FAIL, "INSERT INTO arrester_read_table failed,SQL:%s", szSql); + return; + } +} + +//入库仪表解析数据 +static void dbGISInsertData(ST_IMG_THRESHOLD &stGisCache, double dval, const char *filepath, char *pTime, int wstate) +{ + char szSql[DEF_BUFFER_1K] = { 0 }; + char szHbaseKey[DEF_BUFFER_128] = { 0 }; // hbase同步key. + char szSortTime[24] = { 0 }; + + memcpy(szSortTime, pTime, strlen(pTime)); + strSwap(szSortTime, strlen(szSortTime)); + + CDBMySQL *pdbHandle = CDBMySQL::Instance(); + if (!pdbHandle) return; + + sprintf(szHbaseKey, "%s_%s_%s_%s", szSortTime, stGisCache.sensor_id, pTime, stGisCache.eqm_code); + sprintf(szSql, "INSERT INTO gis_read_table(id,site_id, system_code, sensor_code,time_stamp,dataval,picpath,main_tree_id,work_state,hbase_rowkey) " + "VALUES(guuid(),'%s', '%s', '%s','%s', '%.4f','%s','%s','%d','%s')", stGisCache.site_id, stGisCache.system_code, + stGisCache.sensor_id, pTime, dval, filepath, stGisCache.eqm_code,wstate,szHbaseKey); + vPrtLogMsg(LOG_DEBUG, RET_OK, "dbGISInsertData: %s", szSql); + if (!pdbHandle->InsertRecord((const char *)szSql)) { + vPrtLogMsg(LOG_ERROR, RET_FAIL, "INSERT INTO gis_read_table failed,SQL:%s", szSql); + return; + } +} +//入库仪表解析数据 +static void dbBYQInsertData(ST_IMG_THRESHOLD &stCache, double dval, const char *filepath, char *pTime, int wstate) +{ + // 数据入库 + char szSql[DEF_BUFFER_1K] = { 0 }, szTime[21] = { 0 }; + char szHbaseKey[DEF_BUFFER_128] = { 0 }; // hbase同步key. + char szSortTime[24] = { 0 }; + + memcpy(szSortTime, pTime, strlen(pTime)); + strSwap(szSortTime, strlen(szSortTime)); + + CDBMySQL *pdbHandle = CDBMySQL::Instance(); + if (!pdbHandle) { + mutex_unlock(g_map_byq_mutex); + return; + } + // 原始数据入库 + sprintf(szHbaseKey, "%s_%s_%s_%s", szSortTime, stCache.sensor_id, pTime, stCache.eqm_code); + memset(szSql, 0x00, sizeof(szSql)); + sprintf(szSql, "INSERT INTO transformer_read_table(id,site_id,main_tree_id,sensor_code,system_code,d_time,dataval,work_state,picpath,hbase_rowkey) " + "VALUES(guuid(),'%s','%s', '%s', '%s','%s','%.4f', '%d', '%s','%s')", + stCache.site_id, stCache.eqm_code, stCache.sensor_id, stCache.system_code, pTime, dval, wstate, filepath, szHbaseKey); + vPrtLogMsg(LOG_DEBUG, RET_OK, "transformer_read_table: %s", szSql); + if (!pdbHandle->InsertRecord((const char *)szSql)) { + vPrtLogMsg(LOG_ERROR, RET_FAIL, "INSERT INTO transformer_read_table message failed,SQL:%s", szSql); + } +} + +// 在一段时间内,统计GIS放电的次数 +//static int iGetGisFangDianCount(ST_GIS_CACHE &stGis) +//{ +// int count = 0; +// char szSql[1024] = { 0 }, sTime[24] = { 0 }, eTime[24] = { 0 }; +// +// // 取一天的起始和结束时间 +// vGeOneDayTimeFmt(sTime, eTime); +// CDBMySQL *pdbHandle = CDBMySQL::Instance(); +// if (!pdbHandle) return 0; +//#ifdef _SITE_ID_TYPE_INT +// sprintf(szSql, "SELECT SUM(fdcs) FROM gis_read_table WHERE site_id='%u' AND sersorid='%s' AND eqm_code='%s' " +// "AND DATE(d_time) BETWEEN '%s' AND '%s'", stGis.SiteID, (char*)stGis.szSersorID, (char*)stGis.szEqmCodeID, sTime, eTime); +//#else +// sprintf(szSql, "SELECT SUM(fdcs) FROM gis_read_table WHERE site_id='%s' AND sersorid='%s' AND eqm_code='%s' " +// "AND DATE(d_time) BETWEEN '%s' AND '%s'", stGis.SiteID, (char*)stGis.szSersorID, (char*)stGis.szEqmCodeID, sTime, eTime); +//#endif +// vPrtLogMsg(LOG_DEBUG, RET_OK, "iGetGisFangDianCount:SQL[%s]", szSql); +// MYSQL_RES *res = pdbHandle->SelectRecord(szSql); +// if (!res) return 0; +// +// MYSQL_ROW row = NULL; +// if (row = pdbHandle->GetRecord(res)) +// { +// (row[0] && strlen(row[0])) ? (count = atoi((const char*)row[0])) : 0; +// } +// pdbHandle->FreeRecord(res); +// +// return count; +//} + + +// 是否存在告警 +unsigned char isImageWaring(ST_OPENCV_CONF *stConf, double dValue, char *pszCreateTime, ST_IMG_THRESHOLD &stThresdhold) +{ + // 检查是否有阈值告警 + // 获取工作状态 + unsigned char ws = cGetCurrentWorkState(stConf->eqm_code); + if (ws <= 0) { + vPrtLogMsg(LOG_WARNG, RET_OK, "get device:%s status, can not found bind relations ...", (char*)stConf->eqm_code); + strcpy((char*)stThresdhold.site_id, (const char*)stConf->site_id); + strcpy((char*)stThresdhold.eqm_code, (const char*)stConf->eqm_code); + strcpy((char*)stThresdhold.system_code, (const char*)stConf->sys_code); + strcpy((char*)stThresdhold.sensor_id, (const char*)stConf->sersorid); + return ws; // 默认返回空闲状态 + } + unsigned char eqm_type = 0; + //获取该状态下的阈值 + if (getImageThresholdValuse((const char*)stConf->sersorid, stThresdhold)) + { + if (stThresdhold.eqm_type == 1) eqm_type = 1; // 避雷器 + else if (stThresdhold.eqm_type == 2) eqm_type = 2; // GIS + else if (stThresdhold.eqm_type == 3) eqm_type = 4; // 测温 + else if (stThresdhold.eqm_type == 4) eqm_type = 3; // 避雷器 + //判断是否告警 + switch (ws) + { + case 1: + if (stConf->meter_type == 1){ // 压力时,小于阈值报警 + if (dValue < stThresdhold.value[0] && stThresdhold.value[0] != HT_INVALID_VALUE) + dbInsertImgageWarning(stThresdhold, dValue, stThresdhold.value[0], ws, eqm_type, pszCreateTime); + } + else if (dValue > stThresdhold.value[0] && stThresdhold.value[0] != HT_INVALID_VALUE) + dbInsertImgageWarning(stThresdhold, dValue, stThresdhold.value[0], ws, eqm_type, pszCreateTime); + break; + case 2: + if (stConf->meter_type == 1){ // 压力时,小于阈值报警 + if (dValue < stThresdhold.value[1] && stThresdhold.value[1] != HT_INVALID_VALUE) + dbInsertImgageWarning(stThresdhold, dValue, stThresdhold.value[1], ws, eqm_type, pszCreateTime); + } + else if (dValue > stThresdhold.value[1] && stThresdhold.value[1] != HT_INVALID_VALUE) + dbInsertImgageWarning(stThresdhold, dValue, stThresdhold.value[1], ws, eqm_type, pszCreateTime); + break; + case 3: + if (stConf->meter_type == 1){ // 压力时,小于阈值报警 + if (dValue < stThresdhold.value[2] && stThresdhold.value[2] != HT_INVALID_VALUE) + dbInsertImgageWarning(stThresdhold, dValue, stThresdhold.value[2], ws, eqm_type, pszCreateTime); + } + else if (dValue > stThresdhold.value[2] && stThresdhold.value[2] != HT_INVALID_VALUE) + dbInsertImgageWarning(stThresdhold, dValue, stThresdhold.value[2], ws, eqm_type, pszCreateTime); + break; + default:; + } + } + else { + vPrtLogMsg(LOG_WARNG, RET_OK, "get device:%s sensorid:%s thresdhold, can not found setting threldhold!", (char*)stConf->eqm_code, (char*)stConf->sersorid); + strcpy((char*)stThresdhold.site_id, (const char*)stConf->site_id); + strcpy((char*)stThresdhold.eqm_code, (const char*)stConf->eqm_code); + strcpy((char*)stThresdhold.system_code, (const char*)stConf->sys_code); + strcpy((char*)stThresdhold.sensor_id, (const char*)stConf->sersorid); + } + return ws; +} +// 分析避雷器图片 +static void vBLQParserPicture(ST_OPENCV_CONF *stConf) +{ + char szPath[MAX_PATH] = { 0 }, szFile[MAX_PATH] = { 0 }, szExt[8] = { 0 }, szCreateTime[24] = { 0 }; + int filecount = 0; + double dWarnVal = 0.0; + ST_IMG_THRESHOLD stThresdhold; + std::vector vecFileList; + double dValue = 0.0; // 指针读数 + + filecount = iGetImgFileList((const char*)stConf->img_path, vecFileList); // 扫描文件列表 + // 逐个文件分析 + for (int n = 0; n < filecount; n++) + { + // 目录转为Linux格式 + Window2UnixPath((char*)vecFileList[n].c_str()); + + // 必须在截图之前,获取文件的创建时间,作为告警发生时间。 + if (getFileCreateTime((char*)vecFileList[n].c_str(), szCreateTime) < stConf->img_min_size) + { + vPrtLogMsg(LOG_WARNG, RET_OK, "file:%s size not enjon,process next file...", (char*)vecFileList[n].c_str()); + continue; // 获取原文件创建时间 + } + vPrtLogMsg(LOG_DEBUG, RET_OK, "img_file: %s ctime:%s", vecFileList[n].c_str(), szCreateTime); + +#ifdef _HT_OPENCV_TEST_ENV + dValue = getRandByTime(); // for test + //vGetHostTimeFmt(szCreateTime); + vGetHostTime(szCreateTime); + //vGetHostTimeFmtBeforBay(szCreateTime, 1); +#else + // 截图处理 + vCutImageSave(stConf, (char*)vecFileList[n].c_str()); + + // 分析图片 + dValue = dReadPointValue_3EX5050(stConf, vecFileList[n].c_str()); + if (dValue < 0) continue; +#endif + + // 单位转换,转为与数据库表字段相同的单位 + dValue = dChangeUnits(stConf->sersorid, dValue); + + // 检查是否有阈值告警 + memset(&stThresdhold, 0x00, sizeof(ST_IMG_THRESHOLD)); + unsigned char ws = isImageWaring(stConf, dValue, szCreateTime, stThresdhold); + // 获取工作状态 +#ifndef _HT_OPENCV_TEST_ENV + // 备份文件,分析完成的图片移到备份目录下. + bBakFilename((char*)vecFileList[n].c_str(), (char*)stConf->img_bak); +#endif + // 分解出目录、文件、及扩展名 + bSplitFilenameByFullPath((char*)vecFileList[n].c_str(), szPath, szFile, szExt); + + // 备份后的文件路径及文件名 + sprintf(szPath, "%s/%s.%s", stConf->img_bak, szFile, szExt); + + // 数据入库 + dbBLQInsertData(stThresdhold, dValue, (const char*)szPath, szCreateTime, ws); + } + return; +} + + +// 分析GIS图片,GIS没有放电次数的阈值报警,只有放电电流阈值报警了。 +static void vGISParserPicture(ST_OPENCV_CONF *stConf) +{ + char szPath[MAX_PATH] = { 0 }, szFile[MAX_PATH] = { 0 }, szExt[8] = { 0 }, szCreateTime[24] = { 0 }; + char szSrcFileName[MAX_PATH] = { 0 }; + int filecount = 0, iType = 0; + ST_IMG_THRESHOLD stThresdhold; + vector vecFileList; + double dValue = 0; // 指针读数 + + filecount = iGetImgFileList((const char*)stConf->img_path, vecFileList); // 扫描文件列表 + + // 逐个文件分析 + for (int n = 0; n < filecount; n++) + { + // 目录转为Linux格式 + memset(szSrcFileName, 0x00, sizeof(szSrcFileName)); + memset(szCreateTime, 0x00, sizeof(szCreateTime)); + strncpy(szSrcFileName, vecFileList[n].c_str(), vecFileList[n].length()); + Window2UnixPath(szSrcFileName); + + // 必须在截图之前,获取文件的创建时间,作为告警发生时间。 + if (getFileCreateTime(szSrcFileName, szCreateTime) < stConf->img_min_size) { + vPrtLogMsg(LOG_WARNG, RET_OK, "file:%s size not enjon,process next file...", szSrcFileName); + continue; // 获取原文件创建时间,返回值小于图片文件最大字节时,说明本文件尚未完整保存。 + } +#ifdef _HT_OPENCV_TEST_ENV + dValue = getRandByTime(); // for test + //vGetHostTimeFmt(szCreateTime); + vGetHostTime(szCreateTime); + //vGetHostTimeFmtBeforBay(szCreateTime, 1); +#else + // 截图处理 + vCutImageSave(stConf, szSrcFileName); + + // 分析图片 + dValue = dReadPointValue_JSY10(stConf, szSrcFileName); + if (dValue < 0) continue; +#endif + // 单位转换,转为与数据库表字段相同的单位 + dValue = dChangeUnits(stConf->sersorid, dValue); + + // 检查是否有放电电流阈值告警, 当前值大于预警值,则插入告警信息 + memset(&stThresdhold, 0x00, sizeof(ST_IMG_THRESHOLD)); + unsigned char ws = isImageWaring(stConf, dValue, szCreateTime, stThresdhold); +#ifndef _HT_OPENCV_TEST_ENV + // 备份文件,分析完成的图片移到备份目录下. + bBakFilename(szSrcFileName, (char*)stConf->img_bak); +#endif + // 分解出目录、文件、及扩展名 + bSplitFilenameByFullPath(szSrcFileName, szPath, szFile, szExt); + + // 备份后的文件路径及文件名 + sprintf(szPath, "%s/%s.%s", stConf->img_bak, szFile, szExt); + + // 数据入库 + dbGISInsertData(stThresdhold, dValue, (const char*)szPath, szCreateTime, ws); + } +} + +// 分析BYQ油位图片。 +static void vBYQParserPicture(ST_OPENCV_CONF *stConf) +{ + char szPath[MAX_PATH] = { 0 }, szFile[MAX_PATH] = { 0 }, szExt[8] = { 0 }, szCreateTime[24] = { 0 }; + char szSrcFileName[MAX_PATH] = { 0 }; + int filecount = 0, iType = 0; + ST_IMG_THRESHOLD stThresdhold; + vector vecFileList; + double dValue = 0; // 指针读数 + + filecount = iGetImgFileList((const char*)stConf->img_path, vecFileList); // 扫描文件列表 + + // 逐个文件分析 + for (int n = 0; n < filecount; n++) + { + // 目录转为Linux格式 + memset(szSrcFileName, 0x00, sizeof(szSrcFileName)); + memset(szCreateTime, 0x00, sizeof(szCreateTime)); + strncpy(szSrcFileName, vecFileList[n].c_str(), vecFileList[n].length()); + Window2UnixPath(szSrcFileName); + + // 必须在截图之前,获取文件的创建时间,作为告警发生时间。 + if (getFileCreateTime(szSrcFileName, szCreateTime) < stConf->img_min_size) { + vPrtLogMsg(LOG_WARNG, RET_OK, "file:%s size not enjon,process next file...", szSrcFileName); + continue; // 获取原文件创建时间,返回值小于图片文件最大字节时,说明本文件尚未完整保存。 + } +#ifdef _HT_OPENCV_TEST_ENV + dValue = getRandByTime(); // for test + //vGetHostTimeFmt(szCreateTime); + vGetHostTime(szCreateTime); + //vGetHostTimeFmtBeforBay(szCreateTime,1); +#else + // 截图处理 + vCutImageSave(stConf, szSrcFileName); + // 分析图片 + if (stringncasecmp((const char*)stConf->sys_code, (const char*)"5GST1QLZJ6ORXMK0U", 17) == 0) + { // 1#主变图片分析 + if (stringncasecmp((const char*)stConf->table_type, (const char*)"WSS-411-150", 11) == 0) + dValue = dReadPointValue_Wss411_150_T1(stConf, szSrcFileName); // #1进口油温仪表分析 + else if (stringncasecmp((const char*)stConf->table_type, (const char*)"OCR17NI12MO2-16", 15) == 0) + dValue = dReadPointValue_OrcPress_16_T1(stConf, szSrcFileName); // #1主变出水口压力仪表分析 + else if (stringncasecmp((const char*)stConf->table_type, (const char*)"WSS-411-100-1", 13) == 0) + dValue = dReadPointValue_Wss411_100_T1(stConf, szSrcFileName); // #1进出口水温仪表分析 + else if (stringncasecmp((const char*)stConf->table_type, (const char*)"WSS-411-100-2", 13) == 0) + dValue = dReadPointValue_Wss411_100_T1_2(stConf, szSrcFileName); // #1进出口水温仪表分析 + else { + vPrtLogMsg(LOG_WARNG, RET_OK, "table_type=[%s] not find devices type,pls check [htimg.conf]", stConf->table_type); + return; + } + } + else if (stringncasecmp((const char*)stConf->sys_code, "BOLBJMZFOYMSMCUIO", 17) == 0) //|| + { // 2#主变图片分析 + if (stringncasecmp((const char*)stConf->table_type, (const char*)"WSS-411-150", 11) == 0) + dValue = dReadPointValue_Wss411_150_T2(stConf, szSrcFileName); // #2进口油温仪表分析 + else if (stringncasecmp((const char*)stConf->table_type, (const char*)"OCR17NI12MO2-25", 15) == 0) + dValue = dReadPointValue_OrcPress_25_T2(stConf, szSrcFileName); // #2主变冷却出水压力 + else if (stringncasecmp((const char*)stConf->table_type, (const char*)"WSS-411-100", 11) == 0) + dValue = dReadPointValue_Wss411_100_T2(stConf, szSrcFileName); // #2进出口水温仪表分析 + else { + vPrtLogMsg(LOG_WARNG, RET_OK, "table_type=[%s] not find devices type,pls check [htimg.conf]", stConf->table_type); + return; + } + } + else if (stringncasecmp((const char*)stConf->sys_code, "YYKL3BWFOWB0K8H2K", 17) == 0) + { // 3#主变图片分析 + if (stringncasecmp((const char*)stConf->table_type, (const char*)"WSS-411-150", 11) == 0) + dValue = dReadPointValue_Wss411_150_T3(stConf, szSrcFileName); // #3进口油温仪表分析 + else if (stringncasecmp((const char*)stConf->table_type, (const char*)"OCR17NI12MO2-25", 15) == 0) + dValue = dReadPointValue_OrcPress_25_T3(stConf, szSrcFileName); // #3主变冷却出水压力 + else if (stringncasecmp((const char*)stConf->table_type, (const char*)"WSS-411-100", 11) == 0) + dValue = dReadPointValue_Wss411_100_T3(stConf, szSrcFileName); // #3进出口水温仪表分析 + else { + vPrtLogMsg(LOG_WARNG, RET_OK, "table_type=[%s] not find devices type,pls check [htimg.conf]", stConf->table_type); + return; + } + } + else if (stringncasecmp((const char*)stConf->sys_code, "1PH197OOZMW7DTMS8", 17) == 0) + { // 4#主变图片分析 + if (stringncasecmp((const char*)stConf->table_type, (const char*)"WSS-411-150", 11) == 0) + dValue = dReadPointValue_Wss411_150_T1(stConf, szSrcFileName); // #4进口油温仪表分析 + else if (stringncasecmp((const char*)stConf->table_type, (const char*)"OCR17NI12MO2-25", 15) == 0) + dValue = dReadPointValue_OrcPress_25_T4(stConf, szSrcFileName); // #4主变冷却出水压力 + else if (stringncasecmp((const char*)stConf->table_type, (const char*)"WSS-411-100", 11) == 0) + dValue = dReadPointValue_Wss411_100_T1(stConf, szSrcFileName); // #4进出口水温仪表分析 + else { + vPrtLogMsg(LOG_WARNG, RET_OK, "table_type=[%s] not find devices type,pls check [htimg.conf]", stConf->table_type); + return; + } + } + else { + vPrtLogMsg(LOG_WARNG, RET_OK, "sys_code=[%s] not find devices code,pls check [htimg.conf]", stConf->eqm_code); + return; + } + if (dValue < 0) continue; +#endif + // 单位转换,转为与数据库表字段相同的单位 + dValue = dChangeUnits(stConf->sersorid, dValue); + + // 检查是否有阈值告警, 当前值大于预警值,则插入告警信息 + memset(&stThresdhold, 0x00, sizeof(ST_IMG_THRESHOLD)); + unsigned char ws = isImageWaring(stConf, dValue, szCreateTime, stThresdhold); +#ifndef _HT_OPENCV_TEST_ENV + // 备份文件,分析完成的图片移到备份目录下. + bBakFilename(szSrcFileName, (char*)stConf->img_bak); +#endif + // 分解出目录、文件、及扩展名 + bSplitFilenameByFullPath(szSrcFileName, szPath, szFile, szExt); + + // 备份后的文件路径及文件名 + snprintf(szPath,sizeof(szPath), "%s/%s.%s", stConf->img_bak, szFile, szExt); + + // 数据入库 + dbBYQInsertData(stThresdhold, dValue, (const char*)szPath, szCreateTime, ws); + _SLEEP(100); + } +} + +/*************************************************************************** +** function name : thread_opencv_proc +** deacription : opencv img parser thread +** parameter : none +** return code : NULL +***************************************************************************/ +void* thread_opencv_proc(void * arg) +{ + ST_OPENCV_CONF stConf; + map::iterator m_pIter; + char szCmd[512] = { 0 }; + + vPrtLogMsg(LOG_DEBUG, 0, "thread_opencv_proc = %d startup...", GETTID()); + // 获取配置文件数据 + if (!iGetOpencvConf()) { + vPrtLogMsg(LOG_ERROR, errno, "get opencv config failed, thread_opecv_proc exit."); + return NULL; + } + + vPrtLogMsg(LOG_DEBUG, RET_OK, "get opencv config complating...size:%d", g_conf_img.size()); + _SLEEP(500); + while (g_Running && g_conf_img.size()) + { + // 扫描图片目录,获取识别的图片文件列表 + pthread_testcancels(); + + for (m_pIter = g_conf_img.begin(); m_pIter != g_conf_img.end(); m_pIter++) + { + memset(&stConf, 0x00, sizeof(ST_OPENCV_CONF)); + memcpy(&stConf, &(m_pIter->second), sizeof(ST_OPENCV_CONF)); + vPrtLogMsg(LOG_DEBUG, 0, "--> No=%d BEGIN sersorid: %s file List.", stConf.seqno, stConf.sersorid); + if ((stConf.sersorid[4] & 0xff) == 'T' || stConf.eqm_type[0] == 'T' ) { // BYQ图片处理,油位表读数 + vBYQParserPicture(&stConf); + } + + if ((stConf.sersorid[4] & 0xff) == 'G' || stConf.eqm_type[0] == 'G') { // GIS图片处理,放电次数 + vGISParserPicture(&stConf); + } + + if ((stConf.sersorid[4] & 0xff) == 'B' || stConf.eqm_type[0] == 'B') { // 避雷器图片处理,放电电流 + vBLQParserPicture(&stConf); + } + vPrtLogMsg(LOG_DEBUG, 0, "--> No=%d ENDED sersorid: %s file List.", stConf.seqno, stConf.sersorid); + //_SLEEP(1000 * 5); +#ifndef _HT_OPENCV_TEST_ENV + // 备份文件,分析完成的图片移到备份目录下. + //bBakFilename(szSrcFileName, (char*)stConf->img_bak); + memset(szCmd, 0x00, sizeof(szCmd)); +#ifdef _WIN32 + sprintf(szCmd, "move %s ../spic/", (char*)stConf.img_path); +#else + sprintf(szCmd, "mv %s /appdata/sftp/spic/", (char*)stConf.img_path); +#endif + system(szCmd); +#endif + } + _SLEEP(1000 * 60); + } + vPrtLogMsg(LOG_DEBUG, RET_OK, "link exceptiond, thread_opencv_proc pthread_exit."); + return NULL; +} + diff --git a/src/HTPublic.cpp b/src/HTPublic.cpp index 822b155..2057695 100644 --- a/src/HTPublic.cpp +++ b/src/HTPublic.cpp @@ -1009,4 +1009,279 @@ Note : 本函数入参为网络字节序列 *************************************************************************/ unsigned int int64To32(utint64 u64) { - unsigned int hi = (unsigned int)(( u64 >> 32) & 0xFFFFFFFF \ No newline at end of file + unsigned int hi = (unsigned int)(( u64 >> 32) & 0xFFFFFFFF); + return hi; +} + +// YYYY-MM-DD HH:MI:SS +void LocalTime2Bcd(char *pLocalTime, char *pBcd) +{ + char szDate[5]={0}; + char tmpTime[13] = {0}; + + if(pLocalTime == NULL || pBcd == NULL) + return; + memcpy(tmpTime, pLocalTime+2, 2); + memcpy(tmpTime+2, pLocalTime+5, 2); + memcpy(tmpTime+4, pLocalTime+8, 2); + memcpy(tmpTime+6, pLocalTime+11, 2); + memcpy(tmpTime+8, pLocalTime+14, 2); + memcpy(tmpTime+10, pLocalTime+17, 2); + + asc_to_bcd((unsigned char*)tmpTime, 12, (unsigned char*)pBcd); + return; +} + +/************************************************************************* +Function : 检查字符串结束符 +Param in : + psz : source string + length : input string length, max 128(Byte) +Param out : none +Return Code : valid string length +*************************************************************************/ +int getStringLen(unsigned char *pstr, int length) +{ + unsigned char szTmp[128+1] = {0} ; + int i = 0; + memcpy(szTmp, pstr, length); + while(i < length) { + if((szTmp[i] & 0xff) != 0x00) { + length -= i; + memcpy(pstr, szTmp+i, length); + break; + } + i++; + } + pstr[length] = 0x00; + while(--length) { + if((pstr[length] & 0xff) != 0x00 ) return length+1; + } + return length; +} +int getHexString(unsigned char *str, int length, unsigned char *pstr) +{ + int iLen = 0, iOff = 0; + char *p = (char*)str; + while ( *p == 0x00 ) { + p++; + length--; + } + memcpy( (char*)str, p,length ); + str[length] = 0x00; + while(iLen < length) { + iOff = (iLen * 2); + sprintf((char*)pstr+iOff, "%.2X", (str[iLen] & 0xff)); + iLen++; + } + return (length * 2); +} +/************************************************************************* +Function : 32位整数转二进制字符串 +Param in : + num : source uint +Param out : + pBin : object strings.(min length = 32) +Return Code : none +*************************************************************************/ +void uint32_binstr(unsigned int num, char* pBin) +{ + int i; + if(pBin == NULL) return ; + for( i=0; i < (int)(sizeof(int)*8); i++) + { + pBin[(sizeof(int)*8-1)-i] = (char)(((num >> i) & 1) + '0'); + } + pBin[(sizeof(int)*8)] = 0x00; // 结束符 + return; +} + +/************************************************************************* +Function : 64位整数转二进制字符串 +Param in : + num : source uint +Param out : + pBin : object strings.(min length = 64) +Return Code : none +*************************************************************************/ +void uint64_binstr(utint64 num, char* pBin) +{ + int i; + if(pBin == NULL) return ; + for(i=0; i < (int)(sizeof(utint64)*8); i++) + { + pBin[(sizeof(utint64)*8-1)-i] = (char)(((num >> i) & 1) + '0'); + } + pBin[(sizeof(utint64)*8)] = 0x00; // 结束符 + return; +} +/************************************************************************* +Function : 二进制字符串转32位整数 +Param in : + pBin : source bin string(eg. 1010101001111), need '\0' end. +Param out : + : none +Return Code : uint32 interger +*************************************************************************/ +unsigned int binstr_uint32(char *pBin) +{ + unsigned int n = 0; + int i, len = 0 ; + + if(pBin == NULL) return 0 ; + len = strlen(pBin) ; + if(len > (int)(sizeof(int)*8)) + len = (int)(sizeof(int)*8) ; + for(i=0; i < len; i++) { + n = n * 2 + (pBin[i] - '0'); + } + return n; +} +/************************************************************************* +Function : 字符串反转 +Param in : + str : source string(eg. 1010101001111), need '\0' end. + len : source length +Param out : + : str +Return Code : str, null is faild +*************************************************************************/ +char* strSwap(char* str,int len) +{ + if(len <= 1) return str; + char temp = *str; + *str = *(str+len-1); + *(str+len-1) = temp; + return (strSwap( str+1, len-2)-1); +} +/************************************************************************* +Function : 二进制字符串转64位整数 +Param in : + pBin : source bin string(eg. 1010101001111), need '\0' end. +Param out : + : none +Return Code : utint64 interger +*************************************************************************/ +utint64 binstr_uint64(char *pBin) +{ + utint64 n = 0; + int i, len = 0 ; + + if(pBin == NULL) return 0 ; + len = strlen(pBin) ; + if(len > (int)(sizeof(utint64)*8)) + len = (int)(sizeof(utint64)*8); + for(i=0; i < len; i++) { + n = n * 2 + (pBin[i] - '0'); + } + return n; +} + +/************************************************************************* +Function : 将Linux文件路径转为Windows的路径格式, 仅测试用 +Param in : + pszPath: source path +Param out : + : pszBootPath +Return Code : none +*************************************************************************/ +void Unix2WindowPath(char *pszPath) +{ + // /mnt/fileserver/gathergps/10122045/p_gathergps_20130703123031.dat + char *p = NULL; + int i = 0; + if (pszPath == NULL) return; + p = pszPath; + while(p[i] != 0x00) + { + if( p[i] == '/') p[i] = '\\'; + i++; + } + p[i] = 0x00; +} +/************************************************************************* +Function : 将Windows文件路径转为Linux的路径格式, 仅测试用 +Param in : pszPath -- source path +Param out : + : pszPath +Return Code : none +*************************************************************************/ +void Window2UnixPath(char *pszPath) +{ + char *p = NULL; + int i = 0; + if (pszPath == NULL) return; + p = pszPath; + while (p[i] != 0x00) { + if (p[i] == '\\') p[i] = '/'; + i++; + } + p[i] = 0x00; +} + +int stringcasecmp(const char *ps1, const char *ps2) +{ +#ifdef _WIN32 + //return strcmpi(ps1, ps2); + return _strcmpi(ps1,ps2); +#else + return strcasecmp(ps1,ps2); +#endif +} + +int stringncasecmp(const char *ps1, const char *ps2, int length) +{ +#ifdef _WIN32 + //return strnicmp(ps1,ps2,length); + return _strnicmp(ps1,ps2,length); +#else + return strncasecmp(ps1,ps2,length); +#endif +} + +/************************************************************************* +Function : 获取uuid +Param in : +Param out : +: pszPath +Return Code : none +*************************************************************************/ +int getuuid(char *str, int inlen ) +{ + memset(str, 0x00, inlen); +#if defined(WIN32)||defined(WINCE)||defined(WIN64) + GUID guid; + if (!CoCreateGuid(&guid)) + { + snprintf(str,inlen, + "%08x%04x%04x%02x%02x%02x%02x%02x%02x%02x%02x", //小写 + guid.Data1, guid.Data2, guid.Data3, + guid.Data4[0], guid.Data4[1], guid.Data4[2], + guid.Data4[3], guid.Data4[4], guid.Data4[5], + guid.Data4[6], guid.Data4[7]); + } +#else + uuid_t uu; + uuid_generate(uu); + //uuid_generate_time(uu); + //uuid_generate_tims_safe(uu); + uuid_unparse(uu, str); + strMiddleTrim((unsigned char*)str); +#endif + return strlen(str); +} + + +/************************************************************************* +Function : 获取1-100内的随机数 +Param in : +Param out : +: pszPath +Return Code : none +*************************************************************************/ +double getRandByTime() +{ + srand((unsigned int)time(NULL)); + return (double)((double)(rand()%100 + 1) / (double)(RAND_MAX / rand())); +} + diff --git a/src/HTTestOpencv.cpp b/src/HTTestOpencv.cpp index 24f10fc..ec014ea 100644 --- a/src/HTTestOpencv.cpp +++ b/src/HTTestOpencv.cpp @@ -1 +1,1848 @@ -锘 \ No newline at end of file +/**************************************************************************** +** File name : HTOpencvImg.cpp +** Description : define 104 worker thread group +** Create date : 2018.09.01 +** Auther by : Liuyx +** Version info : V1.0.01 +** Copyrigth By: xi'an huatek, Inc Co., Ltd +** Update record: +** DATE AUTHER DESC +** ------------------------------------------------------------------------- +** 2018.09.01 Liuyx first build +****************************************************************************/ +#include "HTGlobal.h" +#include "HTTestOpencv.h" + + +using namespace cv; +static const char *_FILE_ = "HTTestOpencv.cpp"; + +#ifndef ERROR +#define ERROR -1 +#endif + +string hehe[100] = { "time_0.jpg", "time_1.jpg", "time_2.jpg", "time_3.jpg", "time_4.jpg", +"time_5.jpg", "time_6.jpg", "time_7.jpg", "time_8.jpg", "time_9.jpg", }; + +//----------------------------------------------------------------------------- +//平面几何相关函数http://www.cnblogs.com/zjutlitao/p/3243883.html +//----------------------------------------------------------------------------- + +// file exist +bool isExistFile(const char *filename) +{ + FILE *fp = NULL; + fp = fopen(filename, "rb"); + if (fp == NULL) return false; + fclose(fp); + return true; +} + + + +////求向量的夹角 +//double Angle(Point A, Point B) +//{ +// return acos(Dot(A, B) / Length(A) / Length(B)); +//} +// +////点到直线的距离 +//double DistanceToLine(Point P, Point A, Point B) +//{ +// Point v1 = B - A, v2 = P - A; +// return fabs(Cross(v1, v2)) / Length(v1);//如果不加绝对值是带有方向的距离 +//} + + + +//度数转换 +double DegreeTrans(double theta) +{ + double res = theta / CV_PI * 180; + return res; +} + +//逆时针旋转图像degree角度(原尺寸) +void rotateImage(Mat src, Mat& img_rotate, double degree) +{ + //旋转中心为图像中心 + Point2f center; + center.x = float(src.cols / 2.0); + center.y = float(src.rows / 2.0); + int length = 0; + length = (int)sqrt(src.cols*src.cols + src.rows*src.rows); + //计算二维旋转的仿射变换矩阵 + Mat M = getRotationMatrix2D(center, degree, 1); + warpAffine(src, img_rotate, M, Size(length, length), 1, 0, Scalar(255, 255, 255));//仿射变换,背景色填充为白色 + showImg("旋转后:", img_rotate); +} + +//通过霍夫变换计算角度 +// +double CalcDegree(const Mat &srcImage, Mat &dst) +{ + Mat midImage, dstImage; + + Canny(srcImage, midImage, 50, 200, 3); + cvtColor(midImage, dstImage, CV_GRAY2BGR); + + //通过霍夫变换检测直线 + vector lines; + HoughLines(midImage, lines, 1, CV_PI / 180, 300, 0, 0);//第5个参数就是阈值,阈值越大,检测精度越高 + //cout << lines.size() << endl; + + //由于图像不同,阈值不好设定,因为阈值设定过高导致无法检测直线,阈值过低直线太多,速度很慢 + //所以根据阈值由大到小设置了三个阈值,如果经过大量试验后,可以固定一个适合的阈值。 + + if (!lines.size()) + { + HoughLines(midImage, lines, 1, CV_PI / 180, 200, 0, 0); + } + //cout << lines.size() << endl; + + if (!lines.size()) + { + HoughLines(midImage, lines, 1, CV_PI / 180, 150, 0, 0); + } + //cout << lines.size() << endl; + if (!lines.size()) + { + cout << "没有检测到直线!" << endl; + return -1; + } + float sum = 0; + //依次画出每条线段 + for (size_t i = 0; i < lines.size(); i++) + { + float rho = lines[i][0]; + float theta = lines[i][1]; + Point pt1, pt2; + //cout << theta << endl; + double a = cos(theta), b = sin(theta); + double x0 = a * rho, y0 = b * rho; + pt1.x = cvRound(x0 + 1000 * (-b)); + pt1.y = cvRound(y0 + 1000 * (a)); + pt2.x = cvRound(x0 - 1000 * (-b)); + pt2.y = cvRound(y0 - 1000 * (a)); + //只选角度最小的作为旋转角度 + sum += theta; + line(dstImage, pt1, pt2, Scalar(55, 100, 195), 1, CV_AA); //Scalar函数用于调节线段颜色 + showImg("直线探测效果图", dstImage); + } + float average = sum / lines.size(); //对所有角度求平均,这样做旋转效果会更好 + cout << "average theta:" << average << endl; + double angle = DegreeTrans(average) - 90; + rotateImage(dstImage, dst, angle); + showImg("直线探测效果图2", dstImage); + return angle; +} +// 图片矫正 +void ImageRecify(const char* pInFileName) +{ + double degree; + Mat src = imread(pInFileName); + showImg("原始图", src); + int srcWidth, srcHight; + srcWidth = src.cols; + srcHight = src.rows; + cout << srcWidth << " " << srcHight << endl; + Mat dst; + src.copyTo(dst); + //倾斜角度矫正 + degree = CalcDegree(src, dst); + if (degree == ERROR) + { + cout << "矫正失败!" << endl; + return; + } + rotateImage(src, dst, degree); + cout << "angle:" << degree << endl; + showImg("旋转调整后", dst); + + Mat resulyImage = dst(Rect(0, 0, srcWidth, srcHight)); + showImg("裁剪之后", resulyImage); + //imwrite("recified.jpg", resulyImage); +} +/****************倾斜校正子程序*****************/ +//函数名称:IplImage *Rotate(IplImage *RowImage) +//功能:对每行数字进行倾斜校正 +//入口参数:行图像RowImage +//出口参数:旋转后的图像RotateRow +//https://blog.csdn.net/ZhtSunday/article/details/52094745 +/********************************************/ +void ImgRotate(const char *img_file) +{ + IplImage * RowImage = cvLoadImage(img_file); + //建立储存边缘检测结果图像canImage + IplImage *canImage = cvCreateImage(cvGetSize(RowImage), IPL_DEPTH_8U, 1); + //进行边缘检测 + cvCanny(RowImage, canImage, 30, 200, 3); + //进行hough变换 + CvMemStorage *storage = cvCreateMemStorage(); + CvSeq *lines = NULL; + + lines = cvHoughLines2(canImage, storage, CV_HOUGH_STANDARD, 1, CV_PI / 180, 20, 0, 0); + //统计与竖直夹角<30度的直线个数以及其夹角和 + int numLine = 0; + float sumAng = 0.0; + for (int i = 0; i < lines->total; i++) + { + float *line = (float *)cvGetSeqElem(lines, i); + float theta = line[1]; //获取角度 为弧度制 + if (theta < 30 * CV_PI / 180 || (CV_PI - theta) < 30 * CV_PI / 180) + { + numLine++; + sumAng = sumAng + theta; + } + } + //计算出平均倾斜角,anAng为角度制 + double avAng = ((sumAng / numLine) * 180) / CV_PI; + + //获取二维旋转的仿射变换矩阵 + CvPoint2D32f center; + center.x = float(RowImage->width / 2.0); + center.y = float(RowImage->height / 2.0); + float m[6]; + CvMat M = cvMat(2, 3, CV_32F, m); + cv2DRotationMatrix(center, avAng, 1, &M); + //建立输出图像RotateRow + double a = sin(sumAng / numLine); + double b = cos(sumAng / numLine); + int width_rotate = int(RowImage->height*fabs(a) + RowImage->width*fabs(b)); + int height_rotate = int(RowImage->width*fabs(a) + RowImage->height*fabs(b)); + IplImage *RotateRow = cvCreateImage(cvSize(width_rotate, height_rotate), IPL_DEPTH_8U, 1); + //变换图像,并用黑色填充其余值 + m[2] += (width_rotate - RowImage->width) / 2; + m[5] += (height_rotate - RowImage->height) / 2; + + cvWarpAffine(RowImage, RotateRow, &M, CV_INTER_LINEAR + CV_WARP_FILL_OUTLIERS, cvScalarAll(0)); // 这有异常报出 + //释放 + + cvReleaseImage(&canImage); + cvReleaseMemStorage(&storage); + + cvNamedWindow("Roter:"); + cvShowImage("Roter", RotateRow); + return; // RotateRow; +} +//旋转图像内容不变,尺寸相应变大 +//https ://blog.csdn.net/xiaowei_cqu/article/details/7616044 +IplImage* rotateImage1(IplImage* img, int degree) { + double angle = degree * CV_PI / 180.; // 弧度 + double a = sin(angle), b = cos(angle); + int width = img->width; + int height = img->height; + int width_rotate = int(height * fabs(a) + width * fabs(b)); + int height_rotate = int(width * fabs(a) + height * fabs(b)); + //旋转数组map + // [ m0 m1 m2 ] ===> [ A11 A12 b1 ] + // [ m3 m4 m5 ] ===> [ A21 A22 b2 ] + float map[6]; + CvMat map_matrix = cvMat(2, 3, CV_32F, map); + // 旋转中心 + CvPoint2D32f center = cvPoint2D32f(width / 2, height / 2); + cv2DRotationMatrix(center, degree, 1.0, &map_matrix); + map[2] += (width_rotate - width) / 2; + map[5] += (height_rotate - height) / 2; + IplImage* img_rotate = cvCreateImage(cvSize(width_rotate, height_rotate), 8, 3); + //对图像做仿射变换 + //CV_WARP_FILL_OUTLIERS - 填充所有输出图像的象素。 + //如果部分象素落在输入图像的边界外,那么它们的值设定为 fillval. + //CV_WARP_INVERSE_MAP - 指定 map_matrix 是输出图像到输入图像的反变换, + cvWarpAffine(img, img_rotate, &map_matrix, CV_INTER_LINEAR | CV_WARP_FILL_OUTLIERS, cvScalarAll(0)); + return img_rotate; +} + + +//旋转图像内容不变,尺寸相应变大 +//https ://blog.csdn.net/xiaowei_cqu/article/details/7616044 +IplImage* rotateImage2(IplImage* img, int degree) +{ + double angle = degree * CV_PI / 180.; + double a = sin(angle), b = cos(angle); + int width = img->width, height = img->height; + + //旋转后的新图尺寸 + int width_rotate = int(height * fabs(a) + width * fabs(b)); + int height_rotate = int(width * fabs(a) + height * fabs(b)); + IplImage* img_rotate = cvCreateImage(cvSize(width_rotate, height_rotate), img->depth, img->nChannels); + cvZero(img_rotate); + + //保证原图可以任意角度旋转的最小尺寸 + int tempLength = (int)(sqrt((double)width * width + (double)height *height) + 10); + int tempX = (tempLength + 1) / 2 - width / 2; + int tempY = (tempLength + 1) / 2 - height / 2; + IplImage* temp = cvCreateImage(cvSize(tempLength, tempLength), img->depth, img->nChannels); + cvZero(temp); + + //将原图复制到临时图像tmp中心 + cvSetImageROI(temp, cvRect(tempX, tempY, width, height)); + cvCopy(img, temp, NULL); + cvResetImageROI(temp); + + //旋转数组map + // [ m0 m1 m2 ] ===> [ A11 A12 b1 ] + // [ m3 m4 m5 ] ===> [ A21 A22 b2 ] + double m[6]; + int w = temp->width; + int h = temp->height; + m[0] = b; + m[1] = a; + m[3] = -m[1]; + m[4] = m[0]; + // 将旋转中心移至图像中间 + m[2] = w * 0.5f; + m[5] = h * 0.5f; + CvMat M = cvMat(2, 3, CV_32F, m); + cvGetQuadrangleSubPix(temp, img_rotate, &M); + cvReleaseImage(&temp); + return img_rotate; +} + +// 仿照matlab,自适应求高低两个门限 +// https://blog.csdn.net/debug__boy/article/details/8179730 +void _AdaptiveFindThreshold(CvMat *dx, CvMat *dy, double *low, double *high) +{ + CvSize size; + IplImage *imge = 0; + int i, j; + CvHistogram *hist; + int hist_size = 255; + float range_0[] = { 0, 256 }; + float* ranges[] = { range_0 }; + double PercentOfPixelsNotEdges = 0.7; + size = cvGetSize(dx); + imge = cvCreateImage(size, IPL_DEPTH_32F, 1); + // 计算边缘的强度, 并存于图像中 + float maxv = 0; + for (i = 0; i < size.height; i++) + { + const short* _dx = (short*)(dx->data.ptr + dx->step*i); + const short* _dy = (short*)(dy->data.ptr + dy->step*i); + float* _image = (float *)(imge->imageData + imge->widthStep*i); + for (j = 0; j < size.width; j++) + { + _image[j] = (float)(abs(_dx[j]) + abs(_dy[j])); + maxv = maxv < _image[j] ? _image[j] : maxv; + + } + } + if (maxv == 0) { + *high = 0; + *low = 0; + cvReleaseImage(&imge); + return; + } + + // 计算直方图 + range_0[1] = maxv; + hist_size = (int)(hist_size > maxv ? maxv : hist_size); + hist = cvCreateHist(1, &hist_size, CV_HIST_ARRAY, ranges, 1); + cvCalcHist(&imge, hist, 0, NULL); + int total = (int)(size.height * size.width * PercentOfPixelsNotEdges); + float sum = 0; + int icount = hist->mat.dim[0].size; + + float *h = (float*)cvPtr1D(hist->bins, 0); + for (i = 0; i < icount; i++) + { + sum += h[i]; + if (sum > total) + break; + } + // 计算高低门限 + *high = (i + 1) * maxv / hist_size; + *low = *high * 0.4; + cvReleaseImage(&imge); + cvReleaseHist(&hist); +} + +// 根据图片,自动获取边缘高低阈值 +// https://blog.csdn.net/debug__boy/article/details/8179730 +//void AdaptiveFindThreshold(const CvArr* image, double *low, double *high, int aperture_size) +void AdaptiveFindThreshold(cv::Mat& image, double *low, double *high, int aperture_size) +{ + //cv::Mat src = cv::cvarrToMat(image); + const int cn = image.channels(); + cv::Mat dx(image.rows, image.cols, CV_16SC(cn)); + cv::Mat dy(image.rows, image.cols, CV_16SC(cn)); + + cv::Sobel(image, dx, CV_16S, 1, 0, aperture_size, 1, 0, cv::BORDER_REPLICATE); + cv::Sobel(image, dy, CV_16S, 0, 1, aperture_size, 1, 0, cv::BORDER_REPLICATE); + + CvMat _dx = dx, _dy = dy; + _AdaptiveFindThreshold(&_dx, &_dy, low, high); + +} +// 识别仪表盘读数 +double dReadPointerParserImg(const char *img_file) +{ + int i = 0; + if (!img_file && strlen(img_file) <= 0) { + return -1; + } + + //src-->gray-->dst(src为原图;gaus是经过高斯模糊平滑后的图,gray是gaus经canny提取边缘的图, + //用于下面霍夫变换;dst是最终结果图,要在gray灰度图的基础上变为彩色图才能呈现画线效果) + Mat src, gray, dst, srcImage; + + src = imread(img_file); //读取图片到mat + // showImg("原始图", src); + srcImage = src; + /* + // 霍夫圆变换 + cvtColor(srcImage, midImage, CV_BGR2GRAY); // 转为灰度图 + GaussianBlur(midImage, midImage, Size(9, 9), 2, 2); // 模糊 + + vector circles; + HoughCircles(midImage, circles, CV_HOUGH_GRADIENT, 1, 30, 115, 90, 0, 0); + printf("HoughCircles return resault == %d\n", circles.size()); + */ + + // 霍夫圆变换 + cvtColor(src, gray, CV_BGR2GRAY); //转为单通道的灰度图 + GaussianBlur(gray, gray, Size(9, 9), 2, 2); // 高斯模糊平滑 + + //储存检测圆的容器 + std::vector circles; + //调用Hough变换检测圆 + //参数为:待检测图像gray,检测结果circles,检测方法(这个参数唯一),累加器的分辨率2, + //两个圆间的距离50,canny门限的上限(下限自动设为上限的一半),圆心所需要的最小的投票数,最大和最小半径 + HoughCircles(gray, circles, CV_HOUGH_GRADIENT, 1, 30, 115, 90, 0, 0); + printf("HoughCircles return resault == %d\n", (int)circles.size()); + + if (circles.size() <= 0) { + printf("HoughCircles return resault == %d\n", (int)circles.size()); + return -1; + } + //找出圆盘(因为最大的不一定是的,所以加了几个限制条件) + int pos = 0; + int max = -1; + for (size_t i = 0; i < circles.size(); i++) + { + Vec3f f = circles[i]; + if (f[2] > max && f[0] + f[2] < gray.rows && f[0] - f[2] >= 0 && f[1] + f[2] < gray.cols && f[1] - f[2]>0) + { + max = (int)f[2]; + pos = i; + } + } + + //Point center; + //if (circles.size() > 0) { + // 找到圆心 + Point center((int)circles[pos][0], (int)circles[pos][1]); + // 找到的半径 + int radius = (int)circles[pos][2]; + + // 绘制圆心 + circle(src, center, 3, Scalar(0, 255, 0), -1, 8, 0); + + // 绘制圆轮廓 + circle(src, center, radius, Scalar(255), 2); + //} + // 效果图 + //showImg("霍夫圆变换", src); + /*******************************************************************************/ + // 霍夫线变换 + Canny(src, gray, 100, 300, 3); // 边缘检测 + cvtColor(gray, dst, CV_GRAY2BGR); // 转为RGB图 + + vector lines; + // 检测直线,最小投票为100,线条不短于50,间隙不小于10 + HoughLinesP(gray, lines, 1, CV_PI / 180, 100, 50, 10); + printf("HoughLinesP return resault == %d\n", (int)lines.size()); + list list_MyLine; + for (size_t i = 0; i < lines.size(); i++) + { + Vec4i l = lines[i]; + Point A(l[0], l[1]), B(l[2], l[3]); + if (DistancetoSegment(center, A, B) < 30)//根据圆心到指针的距离阈值滤掉其他线段 + { + bool down = (A.y + B.y - 2 * center.y > 0);//判断长的在过圆心的水平线上部还是下部 + if (A.x == B.x) //斜率为无穷的情况 + { + list_MyLine.push_back(MyLine(i, 90 + (down ? 180 : 0), (int)Length(Point(A.x - B.x, A.y - B.y)))); + } + else if (A.y == B.y) //水平的情况 + { + list_MyLine.push_back(MyLine(i, A.x + B.x - 2 * center.x > 0 ? 0 : 180, (int)Length(Point(A.x - B.x, A.y - B.y)))); + } + else + { + if (down) { + if (A.y > center.y) + list_MyLine.push_back(MyLine(i, 360 - (int)(atan2(A.y - B.y, A.x - B.x) * 180 / PI), (int)Length(Point(A.x - B.x, A.y - B.y)))); + else + list_MyLine.push_back(MyLine(i, 360 - (int)(atan2(B.y - A.y, B.x - A.x) * 180 / PI), (int)Length(Point(A.x - B.x, A.y - B.y)))); + } + else { + if (A.y < center.y) + list_MyLine.push_back(MyLine(i, abs((int)(atan2(A.y - B.y, A.x - B.x) * 180 / PI)), (int)Length(Point(A.x - B.x, A.y - B.y)))); + else + list_MyLine.push_back(MyLine(i, abs((int)(atan2(B.y - A.y, B.x - A.x) * 180 / PI)), (int)Length(Point(A.x - B.x, A.y - B.y)))); + } + } + //line(dst, A, B, Scalar(0, 0, i * 20 + 40), 2, CV_AA); + line(dst, A, B, Scalar(186, 88, 255), 1, CV_AA); + } + } + + //根据角度区分所属指针 + int now_k, pre_k = 720;//当前线段的角度和前一个线段的角度 + int num = 0;//指针数(可能为2或3) + int Du[3] = { 0 };//3个指针的度数(每组的平均) + int Le[3] = { 0 };//Le[i]=Le_ping[i]*0.2+le_max[i]*0.8; + int Le_ping[3] = { 0 };//3个指针的长度(每组的平均) + int Le_max[3] = { 0 };//3个指针的长度(每组区最大的) + int t_num = 0;//每组的数量(求平均用) + + MyLine now_Line; + list_MyLine.push_back(MyLine(99, 888, 0));//向其中插入一个右边界这样方便处理 + list_MyLine.sort(); + while (!list_MyLine.empty()) + { + now_Line = list_MyLine.front(); + now_k = now_Line.k; + if (abs(now_k - pre_k) > 10)//两个角度之差小于10°的算是同一类 + { + if (num != 0) //对本组的度数和长度求平均 + { + Du[num - 1] /= t_num; + Le_ping[num - 1] /= t_num; + Le[num - 1] = (int)(Le_ping[num - 1] * 0.2 + Le_max[num - 1] * 0.8); + } + if (now_k == 888) break;//右边界直接跳出 + t_num = 0;//重新统计下一组 + num++;//组数增加1 + cout << "---------------------------\n";//输出分割线 + } + t_num++;//组内多一条线 + Du[num - 1] += now_Line.k; + Le_ping[num - 1] += now_Line.l; + if (now_Line.l > Le_max[num - 1]) Le_max[num - 1] = now_Line.l; + now_Line.print(); + list_MyLine.pop_front(); + pre_k = now_k; + } + cout << "---------------------------\n\n"; + + cout << "---------------------------\n"; + int t; + for (int i = 0; i < num - 1; i++) + { + for (int j = i + 1; j < num; j++) + { + if (Le[i] > Le[j]) + { + t = Le[i], Le[i] = Le[j], Le[j] = t; + t = Du[i], Du[i] = Du[j], Du[j] = t; + }//if end + }//for end + }//for end + + char s[3][10] = { "point1:", "point2:", "point3:" }; + + for (int i = 0; i < num; i++) + printf("%s k: %3d° l: %3d\n", s[i], Du[i], Le[i]); + cout << "---------------------------\n"; + if (num == 1) + printf("read is: %5.2f\n", (float)abs(((360 - Du[0] + 90) % 360 / 30))); + if (num == 2) + printf("read is: %5.2f %5.2f\n", (float)((360 - Du[0] + 90) % 360 / 30), (float)((360 - Du[1] + 90) % 360 / 6)); + else if (num == 3) + printf("read is: %5.2f %5.2f %5.2f\n", (float)((360 - Du[0] + 90) % 360 / 30), (float)((360 - Du[1] + 90) % 360 / 6), (float)((360 - Du[2] + 90) % 360 / 6)); + + cout << "---------------------------\n"; + + showImg("src", src); + showImg("dst", dst); + + return 0; +} +// 霍夫圆、线变换 +double dReadValue(const char *img_file) +{ + Mat srcImage = imread(img_file); + Mat midImage, dstImage; + + // 显示原始图 + //showImg("原始图", srcImage); + + // 霍夫圆变换 + cvtColor(srcImage, midImage, CV_BGR2GRAY); // 转为灰度图 + GaussianBlur(midImage, dstImage, Size(9, 9), 2, 2); // 模糊 + + vector circles; + HoughCircles(midImage, circles, CV_HOUGH_GRADIENT, 1, 30, 115, 90, 0, 0); + printf("HoughCircles return resault == %d\n", (int)circles.size()); + // 找出圆盘 + int pos = 0; + int max = -1; + for (size_t i = 0; i < circles.size(); i++) + { + Vec3f f = circles[i]; + if (f[2] > max && f[0] + f[2] < midImage.rows && f[0] - f[2] >= 0 && f[1] + f[2] < midImage.cols && f[1] - f[2] > 0) + { + max = (int)f[2]; + pos = i; + } + } + + if (circles.size() > 0) + { + // 找到圆心 + Point center((int)circles[pos][0], (int)circles[pos][1]); + // 找到的半径 + int radius = (int)circles[pos][2]; + + // 绘制圆心 + circle(srcImage, center, 3, Scalar(0, 255, 0), -1, 8, 0); + + // 绘制圆轮廓 + circle(srcImage, center, radius, Scalar(255), 2); + } + + // 效果图 + //showImg("霍夫圆变换", srcImage); + + // 霍夫线变换 + Canny(srcImage, midImage, 100, 300, 3); // 边缘检测 + cvtColor(midImage, dstImage, CV_GRAY2BGR); // 转为灰度图 + + vector lines; + // 检测直线,最小投票为100,线条不短于50,间隙不小于10 + HoughLinesP(midImage, lines, 1, CV_PI / 180, 100, 50, 10); + printf("HoughLinesP return resault == %d\n", (int)lines.size()); + for (size_t i = 0; i < lines.size(); i++) + { + Vec4i l = lines[i]; + Point pt1(l[0], l[1]); + Point pt2(l[2], l[3]); + line(dstImage, pt1, pt2, Scalar(186, 88, 255), 1, CV_AA); + } + + // showImg("边缘检测图", midImage); + + //showImg("霍夫线变换", dstImage); + return 0; +} + +//******************双阈值处理************************* +//第一个参数imageInput输入和输出的的Sobel梯度幅值图像; +//第二个参数lowThreshold是低阈值 +//第三个参数highThreshold是高阈值 +// 指定一个低阈值A,一个高阈值B,一般取B为图像整体灰度级分布的70%,且B为1.5到2倍大小的A; +// 灰度值大于B的,置为255,灰度值小于A的,置为0; +//****************************************************** +void DoubleThreshold(Mat &imageIput, double lowThreshold, double highThreshold) +{ + for (int i = 0; i < imageIput.rows; i++) + { + for (int j = 0; j < imageIput.cols; j++) + { + //printf("[%d:%d] = %d\n", i, j, imageIput.at(i, j)); + + if (imageIput.at(i, j) > highThreshold) + { + imageIput.at(i, j) = 255; + } + if (imageIput.at(i, j) < lowThreshold) + { + imageIput.at(i, j) = 0; + } + } + } +} + + +/* +生成高斯卷积核 kernel +*/ +void Gaussian_kernel(int kernel_size, int sigma, Mat &kernel) +{ + double pi = 3.1415926f; + int m = kernel_size / 2; + + kernel = Mat(kernel_size, kernel_size, CV_32FC1); + float s = (float)(2 * sigma*sigma); + for (int i = 0; i < kernel_size; i++) + { + for (int j = 0; j < kernel_size; j++) + { + int x = i - m; + int y = j - m; + double w = exp(-(x*x + y * y) / s); + kernel.at(i, j) = (int)(w / (pi*s)); + } + } +} + +/* +计算梯度值和方向 +imageSource 原始灰度图 +imageX X方向梯度图像 +imageY Y方向梯度图像 +gradXY 该点的梯度幅值 +pointDirection 梯度方向角度 +*/ +void GradDirection(const Mat imageSource, Mat &imageX, Mat &imageY, Mat &gradXY, Mat &theta) +{ + imageX = Mat::zeros(imageSource.size(), CV_32SC1); + imageY = Mat::zeros(imageSource.size(), CV_32SC1); + gradXY = Mat::zeros(imageSource.size(), CV_32SC1); + theta = Mat::zeros(imageSource.size(), CV_32SC1); + + int rows = imageSource.rows; + int cols = imageSource.cols; + + int stepXY = imageX.step; + int step = imageSource.step; + /* + Mat.step参数指图像的一行实际占用的内存长度, + 因为opencv中的图像会对每行的长度自动补齐(8的倍数), + 编程时尽量使用指针,指针读写像素是速度最快的,使用at函数最慢。 + */ + uchar *PX = imageX.data; + uchar *PY = imageY.data; + uchar *P = imageSource.data; + uchar *XY = gradXY.data; + for (int i = 1; i < rows - 1; i++) + { + for (int j = 1; j < cols - 1; j++) + { + int a00 = P[(i - 1)*step + j - 1]; + int a01 = P[(i - 1)*step + j]; + int a02 = P[(i - 1)*step + j + 1]; + + int a10 = P[i*step + j - 1]; + int a11 = P[i*step + j]; + int a12 = P[i*step + j + 1]; + + int a20 = P[(i + 1)*step + j - 1]; + int a21 = P[(i + 1)*step + j]; + int a22 = P[(i + 1)*step + j + 1]; + + double gradY = double(a02 + 2 * a12 + a22 - a00 - 2 * a10 - a20); + double gradX = double(a00 + 2 * a01 + a02 - a20 - 2 * a21 - a22); + + //PX[i*stepXY + j*(stepXY / step)] = abs(gradX); + //PY[i*stepXY + j*(stepXY / step)] = abs(gradY); + + imageX.at(i, j) = (int)abs(gradX); + imageY.at(i, j) = (int)abs(gradY); + if (gradX == 0) + { + gradX = 0.000000000001; + } + theta.at(i, j) = (int)(atan(gradY / gradX)*57.3); + theta.at(i, j) = (theta.at(i, j) + 360) % 360; + gradXY.at(i, j) = (int)sqrt(gradX*gradX + gradY * gradY); + //XY[i*stepXY + j*(stepXY / step)] = sqrt(gradX*gradX + gradY*gradY); + } + + } + convertScaleAbs(imageX, imageX); + convertScaleAbs(imageY, imageY); + convertScaleAbs(gradXY, gradXY); + +} + +/* +局部非极大值抑制 +沿着该点梯度方向,比较前后两个点的幅值大小,若该点大于前后两点,则保留, +若该点小于前后两点任意一点,则置为0; +imageInput 输入得到梯度图像 +imageOutput 输出的非极大值抑制图像 +theta 每个像素点的梯度方向角度 +imageX X方向梯度 +imageY Y方向梯度 +*/ +void NonLocalMaxValue(const Mat imageInput, Mat &imageOutput, const Mat &theta, const Mat &imageX, const Mat &imageY) +{ + imageOutput = imageInput.clone(); + + + int cols = imageInput.cols; + int rows = imageInput.rows; + + for (int i = 1; i < rows - 1; i++) + { + for (int j = 1; j < cols - 1; j++) + { + if (0 == imageInput.at(i, j))continue; + + int g00 = imageInput.at(i - 1, j - 1); + int g01 = imageInput.at(i - 1, j); + int g02 = imageInput.at(i - 1, j + 1); + + int g10 = imageInput.at(i, j - 1); + int g11 = imageInput.at(i, j); + int g12 = imageInput.at(i, j + 1); + + int g20 = imageInput.at(i + 1, j - 1); + int g21 = imageInput.at(i + 1, j); + int g22 = imageInput.at(i + 1, j + 1); + + int direction = theta.at(i, j); //该点梯度的角度值 + int g1 = 0; + int g2 = 0; + int g3 = 0; + int g4 = 0; + double tmp1 = 0.0; //保存亚像素点插值得到的灰度数 + double tmp2 = 0.0; + double weight = fabs((double)imageY.at(i, j) / (double)imageX.at(i, j)); + + if (weight == 0)weight = 0.0000001; + if (weight > 1) + { + weight = 1 / weight; + } + if ((0 <= direction && direction < 45) || 180 <= direction && direction < 225) + { + tmp1 = g10 * (1 - weight) + g20 * (weight); + tmp2 = g02 * (weight)+g12 * (1 - weight); + } + if ((45 <= direction && direction < 90) || 225 <= direction && direction < 270) + { + tmp1 = g01 * (1 - weight) + g02 * (weight); + tmp2 = g20 * (weight)+g21 * (1 - weight); + } + if ((90 <= direction && direction < 135) || 270 <= direction && direction < 315) + { + tmp1 = g00 * (weight)+g01 * (1 - weight); + tmp2 = g21 * (1 - weight) + g22 * (weight); + } + if ((135 <= direction && direction < 180) || 315 <= direction && direction < 360) + { + tmp1 = g00 * (weight)+g10 * (1 - weight); + tmp2 = g12 * (1 - weight) + g22 * (weight); + } + + if (imageInput.at(i, j) < tmp1 || imageInput.at(i, j) < tmp2) + { + imageOutput.at(i, j) = 0; + } + } + } + +} + + +/* +连接处理: +灰度值介于A和B之间的,考察该像素点临近的8像素是否有灰度值为255的, +若没有255的,表示这是一个孤立的局部极大值点,予以排除,置为0; +若有255的,表示这是一个跟其他边缘有“接壤”的可造之材,置为255, +之后重复执行该步骤,直到考察完之后一个像素点。 + +其中的邻域跟踪算法,从值为255的像素点出发找到周围满足要求的点,把满足要求的点设置为255, +然后修改i,j的坐标值,i,j值进行回退,在改变后的i,j基础上继续寻找255周围满足要求的点。 +当所有连接255的点修改完后,再把所有上面所说的局部极大值点置为0;(算法可以继续优化)。 + +参数1,imageInput:输入和输出的梯度图像 +参数2,lowTh:低阈值 +参数3,highTh:高阈值 +*/ +void DoubleThresholdLink(Mat &imageInput, double lowTh, double highTh) +{ + int cols = imageInput.cols; + int rows = imageInput.rows; + + for (int i = 1; i < rows - 1; i++) + { + for (int j = 1; j < cols - 1; j++) + { + double pix = imageInput.at(i, j); + if (pix != 255)continue; + bool change = false; + for (int k = -1; k <= 1; k++) + { + for (int u = -1; u <= 1; u++) + { + if (k == 0 && u == 0)continue; + double temp = imageInput.at(i + k, j + u); + if (temp >= lowTh && temp <= highTh) + { + imageInput.at(i + k, j + u) = 255; + change = true; + } + } + } + if (change) + { + if (i > 1)i--; + if (j > 2)j -= 2; + + } + } + } + + for (int i = 0; i < rows; i++) + { + for (int j = 0; j < cols; j++) + { + if (imageInput.at(i, j) != 255) + { + imageInput.at(i, j) = 0; + } + } + } +} + + +/* +Canny边缘检测主要包括: + +图像的灰度化; + +图像的高斯滤波,来平滑图像,同时消除和降低图像噪声的影响; + +计算出每一个像素点位置的梯度(X方向梯度、Y方向梯度、已经该点的梯度幅值)和方向角度;Y方向和X方向梯度的比值,得出梯度方向,X梯度的平方和+Y梯度的平方和的值,再进行求平方得到该点的梯度幅值。(Sobel算子等) + +局部非极大值抑制处理;梯度方向垂直于边缘方向,在梯度方向上进行非极大值抑制可以细化边缘,在梯度方向上比较该点前后两个点的梯度的大小,如果大于两个点则保留,小于任意一个点则置为0。 + +双阈值处理和连接处理;指定高低阈值,然后高阈值直接赋值为255,低阈值为0,中间的值进行连接处理。如果中间的值八邻域内有255,则该值也变为255,也就是说255往周围进行扩张,收集边缘加闭合边缘。 +*/ +int HTCanny(char *filename) +{ + Mat image = imread(filename, 0); + showImg("origin image", image); + + //转换为灰度图 + Mat grayImage; + //cvtColor(image, grayImage, CV_RGB2GRAY); + cvtColor(image, grayImage, CV_GRAY2BGR); + + imshow("灰度图", grayImage); + + + + //计算XY方向梯度 + Mat imageX, imageY, imageXY; + Mat theta; + GradDirection(grayImage, imageX, imageY, imageXY, theta); + imshow("计算XY方向梯度", imageXY); + + //对梯度幅值进行非极大值抑制 + Mat localImage; + NonLocalMaxValue(imageXY, localImage, theta, imageX, imageY); + imshow("对梯度幅值进行非极大值抑制", localImage); + + //双阈值算法检测和边缘连接 + DoubleThreshold(localImage, 30, 100); + DoubleThresholdLink(localImage, 30, 100); + imshow("双阈值算法检测和边缘连接", localImage); + + Mat temMat; + vector lines2; + Canny(image, temMat, 30, 100); + HoughLinesP(temMat, lines2, 1, CV_PI / 180, 50, 30, 10); + printf("lines2 = %d\n", (int)lines2.size()); + for (size_t i = 0; i < lines2.size(); i++) + { + Vec4i l = lines2[i]; + Point A(l[0], l[1]), B(l[2], l[3]); + line(temMat, A, B, Scalar(255, 0, 0), 2, CV_AA); + } + imshow("线条监测", temMat); + + waitKey(0); + return 0; +} + +/************************************************************************/ + +////////////////////////////////////////////////////////////////// +//函数功能:用向量来做COSα=两向量之积/两向量模的乘积求两条线段夹角 +//输入: 线段3个点坐标pt1,pt2,pt0,最后一个参数为公共点 +//输出: 线段夹角,单位为角度 +////////////////////////////////////////////////////////////////// +double angle(CvPoint* pt1, CvPoint* pt2, CvPoint* pt0) +{ + double dx1 = pt1->x - pt0->x; + double dy1 = pt1->y - pt0->y; + double dx2 = pt2->x - pt0->x; + double dy2 = pt2->y - pt0->y; + double angle_line = (dx1*dx2 + dy1 * dy2) / sqrt((dx1*dx1 + dy1 * dy1)*(dx2*dx2 + dy2 * dy2) + 1e-10);//余弦值 + return acos(angle_line) * 180 / 3.141592653; +} +////////////////////////////////////////////////////////////////// +//函数功能:采用多边形检测,通过约束条件寻找矩形 +//输入: img 原图像 +// storage 存储 +// minarea,maxarea 检测矩形的最小/最大面积 +// minangle,maxangle 检测矩形边夹角范围,单位为角度 +//输出: 矩形序列 +////////////////////////////////////////////////////////////////// +CvSeq* findSquares4(IplImage* img, CvMemStorage* storage, int minarea, int maxarea, int minangle, int maxangle, int(&temp)[30]) +{ + CvSeq* contours;//边缘 + int N = 6; //阈值分级 + CvSize sz = cvSize(img->width & -2, img->height & -2); + IplImage* timg = cvCloneImage(img);//拷贝一次img + IplImage* gray = cvCreateImage(sz, 8, 1); //img灰度图 + IplImage* pyr = cvCreateImage(cvSize(sz.width / 2, sz.height / 2), 8, 3); //金字塔滤波3通道图像中间变量 + IplImage* tgray = cvCreateImage(sz, 8, 1);; + CvSeq* result; + double s, t; + int sk = 0; + CvSeq* squares = cvCreateSeq(0, sizeof(CvSeq), sizeof(CvPoint), storage); + + cvSetImageROI(timg, cvRect(0, 0, sz.width, sz.height)); + //金字塔滤波 + cvPyrDown(timg, pyr, 7); + cvPyrUp(pyr, timg, 7); + //在3个通道中寻找矩形 + for (int c = 0; c < 3; c++) //对3个通道分别进行处理 + { + cvSetImageCOI(timg, c + 1); + cvCopy(timg, tgray, 0); //依次将BGR通道送入tgray + for (int l = 0; l < N; l++) + { + //不同阈值下二值化 + cvThreshold(tgray, gray, 75, 250, CV_THRESH_BINARY); + cvShowImage("111", gray); + cvFindContours(gray, storage, &contours, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0)); + while (contours) + { //多边形逼近 + result = cvApproxPoly(contours, sizeof(CvContour), storage, CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0); + + //如果是凸四边形并且面积在范围内 + if (result->total == 4 && fabs(cvContourArea(result, CV_WHOLE_SEQ)) > minarea && fabs(cvContourArea(result, CV_WHOLE_SEQ)) < maxarea && cvCheckContourConvexity(result)) + { + + s = 0; + //判断每一条边 + for (int i = 0; i < 5; i++) + { + if (i >= 2) + { //角度 + t = fabs(angle((CvPoint*)cvGetSeqElem(result, i), (CvPoint*)cvGetSeqElem(result, i - 2), (CvPoint*)cvGetSeqElem(result, i - 1))); + s = s > t ? s : t; + } + } + //这里的S为直角判定条件 单位为角度 + if (s > minangle && s < maxangle) + { + for (int i = 0; i < 4; i++) + cvSeqPush(squares, (CvPoint*)cvGetSeqElem(result, i)); + CvRect rect = cvBoundingRect(contours, 1); // 获取矩形边界框 + CvPoint p1; + p1 = cvPoint(rect.x + rect.width / 2, rect.y + rect.height / 2); //矩形中心坐标 + std::cout << "X:" << p1.x << "Y:" << p1.y << std::endl; + } + } + contours = contours->h_next; + } + } + std::cout << "圆的数量是" << sk << std::endl; + temp[26] = sk; + + + sk = 0; + } + cvReleaseImage(&gray); + cvReleaseImage(&pyr); + cvReleaseImage(&tgray); + cvReleaseImage(&timg); + + return squares; +} + +////////////////////////////////////////////////////////////////// +//函数功能:画出所有矩形 +//输入: img 原图像 +// squares 矩形序列 +// wndname 窗口名称 +//输出: 图像中标记矩形 +////////////////////////////////////////////////////////////////// +void drawSquares(IplImage* img, CvSeq* squares, const char* wndname) +{ + CvSeqReader reader; + IplImage* cpy = cvCloneImage(img); + CvPoint pt[4]; + int i; + cvStartReadSeq(squares, &reader, 0); + for (i = 0; i < squares->total; i += 4) + { + CvPoint* rect = pt; + int count = 4; + memcpy(pt, reader.ptr, squares->elem_size); + CV_NEXT_SEQ_ELEM(squares->elem_size, reader); + memcpy(pt + 1, reader.ptr, squares->elem_size); + CV_NEXT_SEQ_ELEM(squares->elem_size, reader); + memcpy(pt + 2, reader.ptr, squares->elem_size); + CV_NEXT_SEQ_ELEM(squares->elem_size, reader); + memcpy(pt + 3, reader.ptr, squares->elem_size); + CV_NEXT_SEQ_ELEM(squares->elem_size, reader); + //cvPolyLine( cpy, &rect, &count, 1, 1, CV_RGB(0,255,0), 3, CV_AA, 0 ); + cvPolyLine(cpy, &rect, &count, 1, 1, CV_RGB(rand() & 255, rand() & 255, rand() & 255), 1, CV_AA, 0);//彩色绘制 + } + cvShowImage("22", cpy); + cvReleaseImage(&cpy); +} + +void SendMessageOne(char *rtsp_url) +{ + String rtsp_addr = rtsp_url; + + VideoCapture capture(rtsp_addr); + + //开起摄像头 + //capture.open(0); + Mat edges; //定义转化的灰度图 + const char* winn = "1111"; + if (!capture.isOpened()) + { + namedWindow("【效果图】", CV_WINDOW_NORMAL); + } + while (1) + { + int Y = 0, J = 0; + Mat frame; + capture >> frame; + IplImage img0 = frame; + + //Mat E = frame(Range(1, 320), Range(1, 240)); + Mat E = frame(Range(1, 160), Range(1, 240)); + cvtColor(frame, edges, CV_BGR2GRAY); + //高斯滤波 + GaussianBlur(edges, edges, Size(7, 7), 2, 2); + std::vector circles;//存储每个圆的位置信息 + //霍夫圆 + HoughCircles(edges, circles, CV_HOUGH_GRADIENT, 1.5, 5, 100, 240, 0, 50); + for (size_t i = 0; i < circles.size(); i++) + { + Point center(cvRound(circles[i][0]), cvRound(circles[i][1])); + int radius = cvRound(circles[i][2]); + //std::cout << "圆的X是" << circles[i][0] << "圆的Y是" << circles[i][1] << std:: endl; + //绘制圆轮廓 + circle(frame, center, radius, Scalar(155, 50, 255), 3, 8, 0); + int R = frame.at(cvRound(circles[i][1]), cvRound(circles[i][0]))[2];//R + int G = frame.at(cvRound(circles[i][1]), cvRound(circles[i][0]))[1];//G + int B = frame.at(cvRound(circles[i][1]), cvRound(circles[i][0]))[0];//B + int num = R + G + B; + std::cout << "圆心颜色是" << num << std::endl; + } + + imshow("【效果图】", frame); + if ('q' == waitKey(30)) break; + } +} + +/**************************************************/ + +using namespace cv; +using namespace std; + +cv::Mat img; +bool select_flag = false; +cv::Rect m_select; +cv::Point origin; +int ROI_count; + +void onMouseRectPicking(int event, int x, int y, int, void*) +{ + if (select_flag) + { + m_select.x = MIN(origin.x, x);//不一定要等鼠标弹起才计算矩形框,而应该在鼠标按下开始到弹起这段时间实时计算所选矩形框 + m_select.y = MIN(origin.y, y); + m_select.width = abs(x - origin.x);//算矩形宽度和高度 + m_select.height = abs(y - origin.y); + m_select &= cv::Rect(0, 0, img.cols, img.rows);//保证所选矩形框在视频显示区域之内 + } + if (event == CV_EVENT_LBUTTONDOWN) + { + select_flag = true; //鼠标按下的标志赋真值 + origin = cv::Point(x, y); //保存下来单击捕捉到的点 + m_select = cv::Rect(x, y, 0, 0); //这里一定要初始化,宽和高为(0,0)是因为在opencv中Rect矩形框类内的点是包含左上角那个点的,但是不含右下角那个点 + } + else if (event == CV_EVENT_LBUTTONUP) + { + select_flag = false; + ROI_count++; + } +} + +// 鼠标选取区域,实现截图功能并保持图片 +void vCutPicture(char *filename) +{ + + img = imread(filename); + bool stop = false; + + cv::namedWindow("capframe", CV_WINDOW_AUTOSIZE); + cv::setMouseCallback("capframe", onMouseRectPicking, 0); + + char pic_name[40]; + ROI_count = 0; + + while (!stop) + { + img = imread(filename); + cv::rectangle(img, m_select, cv::Scalar(255, 0, 0), 2, 8, 0); // 画矩形框 + cv::imshow("capframe", img); + + if ((m_select.x != 0) && (m_select.y != 0) && (m_select.width != 0) && (m_select.height != 0)) + { + sprintf(pic_name, "ROI_%d.jpg", ROI_count); + Mat ROI = img(m_select); + imshow("ROI_WIN", ROI); + imwrite(pic_name, ROI); + } + char key = cv::waitKey(30); + if (key == 'q') stop = true; + } + waitKey(0); + return; +} + +////////////////////////////////// +//检测直线方法 +int iCheckLine(char *filename) +{ + int i; + IplImage* src = cvLoadImage(filename, 0); + IplImage* dst; + IplImage* color_dst; + + CvMemStorage* storage = cvCreateMemStorage(0); + CvSeq* lines = 0; + + if (!src) return -1; + dst = cvCreateImage(cvGetSize(src), 8, 1); + color_dst = cvCreateImage(cvGetSize(src), 8, 3); + cvCanny(src, dst, 50, 200, 3); + cvCvtColor(dst, color_dst, CV_GRAY2BGR); +#if 0 + + lines = cvHoughLines2(dst, storage, CV_HOUGH_STANDARD, 1, CV_PI / 180, 100, 0, 0); + for (i = 0; i < MIN(lines->total, 100); i++) + { + float* line = (float*)cvGetSeqElem(lines, i); + float rho = line[0]; + float theta = line[1]; + CvPoint pt1, pt2; + double a = cos(theta), b = sin(theta); + double x0 = a * rho, y0 = b * rho; + pt1.x = cvRound(x0 + 1000 * (-b)); + pt1.y = cvRound(y0 + 1000 * (a)); + pt2.x = cvRound(x0 - 1000 * (-b)); + pt2.y = cvRound(y0 - 1000 * (a)); + cvLine(color_dst, pt1, pt2, CV_RGB(255, 0, 0), 3, CV_AA, 0); + } +#else + lines = cvHoughLines2(dst, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI / 180, 50, 50, 10); + for (i = 0; i < lines->total; i++) + { + CvPoint* line = (CvPoint*)cvGetSeqElem(lines, i); + cvLine(color_dst, line[0], line[1], CV_RGB(255, 0, 0), 3, CV_AA, 0); + } + +#endif + cvNamedWindow("Source", 1); + cvShowImage("Source", src); + cvNamedWindow("Hough", 1); + cvShowImage("Hough", color_dst); + cvReleaseMemStorage(&storage); + cvWaitKey(0); + return 0; +} +//检测圆方法 +int iCheckCircles(char *filename) +{ + IplImage* img; + if (img = cvLoadImage(filename, 1)) + { + IplImage* gray = cvCreateImage(cvGetSize(img), 8, 1); + CvMemStorage* storage = cvCreateMemStorage(0); + + cvCvtColor(img, gray, CV_BGR2GRAY); + cvSmooth(gray, gray, CV_GAUSSIAN, 9, 9); // smooth it, otherwise a lot of false circles may be detected + CvSeq* circles = cvHoughCircles(gray, storage, CV_HOUGH_GRADIENT, 2, gray->height / 5, 200, 100); + int i; + for (i = 0; i < circles->total; i++) + { + float* p = (float*)cvGetSeqElem(circles, i); + cvCircle(img, cvPoint(cvRound(p[0]), cvRound(p[1])), 3, CV_RGB(0, 255, 0), -1, 8, 0); + cvCircle(img, cvPoint(cvRound(p[0]), cvRound(p[1])), cvRound(p[2]), CV_RGB(0, 255, 0), 3, 8, 0); + } + cvNamedWindow("circles", 1); + cvShowImage("circles", img); + cvReleaseImage(&gray); + cvReleaseMemStorage(&storage); + } + return 0; +} + +//检测矩形代码: +/* +在程序里找寻矩形 +*/ +#ifdef _CH_ +#pragma package +#endif + + +IplImage* imgfx = 0; +CvMemStorage* storage = 0; +static const char* names[] = { "pic1.png", "pic2.png", "pic3.png", "pic4.png", "pic5.png", "pic6.png", 0 }; +int thresh = 50; +// helper function: +// finds a cosine of angle between vectors +// from pt0->pt1 and from pt0->pt2 +//double angle(CvPoint* pt1, CvPoint* pt2, CvPoint* pt0) +//{ +// double dx1 = pt1->x - pt0->x; +// double dy1 = pt1->y - pt0->y; +// double dx2 = pt2->x - pt0->x; +// double dy2 = pt2->y - pt0->y; +// return (dx1*dx2 + dy1*dy2) / sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10); +//} + +// returns sequence of squares detected on the image. +// the sequence is stored in the specified memory storage +CvSeq* findSquares4(IplImage* img, CvMemStorage* storage) +{ + CvSeq* contours; + int i, c, l, N = 11; + CvSize sz = cvSize(img->width & -2, img->height & -2); + IplImage* timg = cvCloneImage(img); // make a copy of input image + IplImage* gray = cvCreateImage(sz, 8, 1); + IplImage* pyr = cvCreateImage(cvSize(sz.width / 2, sz.height / 2), 8, 3); + IplImage* tgray; + CvSeq* result; + double s, t; + // create empty sequence that will contain points - + // 4 points per square (the square's vertices) + CvSeq* squares = cvCreateSeq(0, sizeof(CvSeq), sizeof(CvPoint), storage); + + // select the maximum ROI in the image + // with the width and height divisible by 2 + cvSetImageROI(timg, cvRect(0, 0, sz.width, sz.height)); + + // down-scale and upscale the image to filter out the noise + cvPyrDown(timg, pyr, 7); + cvPyrUp(pyr, timg, 7); + tgray = cvCreateImage(sz, 8, 1); + + // find squares in every color plane of the image + for (c = 0; c < 3; c++) + { + // extract the c-th color plane + cvSetImageCOI(timg, c + 1); + cvCopy(timg, tgray, 0); + + // try several threshold levels + for (l = 0; l < N; l++) + { + // hack: use Canny instead of zero threshold level. + // Canny helps to catch squares with gradient shading   + if (l == 0) + { + // apply Canny. Take the upper threshold from slider + // and set the lower to 0 (which forces edges merging) + cvCanny(tgray, gray, 0, thresh, 5); + // dilate canny output to remove potential + // holes between edge segments + cvDilate(gray, gray, 0, 1); + } + else + { + // apply threshold if l!=0: + //     tgray(x,y) = gray(x,y) < (l+1)*255/N ? 255 : 0 + cvThreshold(tgray, gray, (l + 1) * 255 / N, 255, CV_THRESH_BINARY); + } + + // find contours and store them all as a list + cvFindContours(gray, storage, &contours, sizeof(CvContour), + CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0)); + + // test each contour + while (contours) + { + // approximate contour with accuracy proportional + // to the contour perimeter + result = cvApproxPoly(contours, sizeof(CvContour), storage, + CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0); + // square contours should have 4 vertices after approximation + // relatively large area (to filter out noisy contours) + // and be convex. + // Note: absolute value of an area is used because + // area may be positive or negative - in accordance with the + // contour orientation + if (result->total == 4 && + fabs(cvContourArea(result, CV_WHOLE_SEQ)) > 1000 && + cvCheckContourConvexity(result)) + { + s = 0; + + for (i = 0; i < 5; i++) + { + // find minimum angle between joint + // edges (maximum of cosine) + if (i >= 2) + { + t = fabs(angle( + (CvPoint*)cvGetSeqElem(result, i), + (CvPoint*)cvGetSeqElem(result, i - 2), + (CvPoint*)cvGetSeqElem(result, i - 1))); + s = s > t ? s : t; + } + } + + // if cosines of all angles are small + // (all angles are ~90 degree) then write quandrange + // vertices to resultant sequence + if (s < 0.3) + for (i = 0; i < 4; i++) + cvSeqPush(squares, + (CvPoint*)cvGetSeqElem(result, i)); + } + + // take the next contour + contours = contours->h_next; + } + } + } + + // release all the temporary images + cvReleaseImage(&gray); + cvReleaseImage(&pyr); + cvReleaseImage(&tgray); + cvReleaseImage(&timg); + + return squares; +} + +// the function draws all the squares in the image +void drawSquares(IplImage* img, CvSeq* squares) +{ + CvSeqReader reader; + IplImage* cpy = cvCloneImage(img); + int i; + CvPoint pt[4]; + // initialize reader of the sequence + cvStartReadSeq(squares, &reader, 0); + + // read 4 sequence elements at a time (all vertices of a square) + for (i = 0; i < squares->total; i += 4) + { + CvPoint* rect = pt; + int count = 4; + + // read 4 vertices + memcpy(pt, reader.ptr, squares->elem_size); + CV_NEXT_SEQ_ELEM(squares->elem_size, reader); + memcpy(pt + 1, reader.ptr, squares->elem_size); + CV_NEXT_SEQ_ELEM(squares->elem_size, reader); + memcpy(pt + 2, reader.ptr, squares->elem_size); + CV_NEXT_SEQ_ELEM(squares->elem_size, reader); + memcpy(pt + 3, reader.ptr, squares->elem_size); + CV_NEXT_SEQ_ELEM(squares->elem_size, reader); + + // draw the square as a closed polyline + cvPolyLine(cpy, &rect, &count, 1, 1, CV_RGB(255, 255, 255), 3, CV_AA, 0); + } + + // show the resultant image + cvShowImage("Square Detection Demo", cpy); + cvReleaseImage(&cpy); +} + +void on_trackbar(int a) +{ + if (imgfx) + drawSquares(imgfx, findSquares4(imgfx, storage)); +} + +// 检测矩形 +int iCheckFangXiang(char *filename) +{ + IplImage* img0 = 0; + int c; + // create memory storage that will contain all the dynamic data + storage = cvCreateMemStorage(0); + + // load i-th image + img0 = cvLoadImage(filename, 1); + cvShowImage("原图", img0); + if (!img0) + { + printf("Couldn't load %s/n", filename); + return 0; + } + imgfx = cvCloneImage(img0); + + // create window and a trackbar (slider) with parent "image" and set callback + // (the slider regulates upper threshold, passed to Canny edge detector) + //cvNamedWindow("Square Detection Demo1",1); + cvCreateTrackbar("canny thresh", "Square Detection Demo2", &thresh, 1000, on_trackbar); + + // force the image processing + on_trackbar(0); + + // wait for key. + // Also the function cvWaitKey takes care of event processing + c = cvWaitKey(0); + // release both images + cvReleaseImage(&imgfx); + cvReleaseImage(&img0); + // clear memory storage - reset free space position + cvClearMemStorage(storage); + + cvDestroyWindow("Square Detection Demo3"); + + return 0; +} + + + +/////////////////////////////////////////////////////////////////////////////////// +double Angles(Point cen, Point first, Point second) +{ + double M_PIs = 3.1415926535897; + + double ma_x = first.x - cen.x; + double ma_y = first.y - cen.y; + double mb_x = second.x - cen.x; + double mb_y = second.y - cen.y; + double v1 = (ma_x * mb_x) + (ma_y * mb_y); + double ma_val = sqrt(ma_x * ma_x + ma_y * ma_y); + double mb_val = sqrt(mb_x * mb_x + mb_y * mb_y); + double cosM = v1 / (ma_val * mb_val); + double angleAMB = acos(cosM) * 180 / M_PIs; + return angleAMB; +} + +/************************************************************************ +*函数名: get_point_angle +* +*函数作用: 已知2个坐标点,求从 0------->x 逆时针需旋转多少角度到该位置 +* +* | +* | +* | +* | +*------------------------------------> x +* | 0 +* | +* | +* | +* v +* y +* +*函数参数: +*CvPoint2D32f pointO - 起点 +*CvPoint2D32f pointA - 终点 +* +*函数返回值: +*double 向量OA,从 0------->x 逆时针需旋转多少角度到该位置 +**************************************************************************/ + +double get_point_angles(CvPoint pointO, CvPoint pointA) +{ + double angle = 0; + CvPoint point; + double temp; + + point = cvPoint((pointA.x - pointO.x), (pointA.y - pointO.y)); + + if ((0 == point.x) && (0 == point.y)) + { + return 0; + } + + if (0 == point.x) + { + angle = 90; + return angle; + } + + if (0 == point.y) + { + angle = 0; + return angle; + } + + temp = fabsf(float(point.y) / float(point.x)); + temp = atan(temp); + temp = temp * 180 / CV_PI; + + if ((0 < point.x) && (0 < point.y)) + { + angle = 360 - temp; + return angle; + } + + if ((0 > point.x) && (0 < point.y)) + { + angle = 360 - (180 - temp); + return angle; + } + + if ((0 < point.x) && (0 > point.y)) + { + angle = temp; + return angle; + } + + if ((0 > point.x) && (0 > point.y)) + { + angle = 180 - temp; + return angle; + } + + printf("sceneDrawing :: getAngle error!"); + return -1; +} + +Mat src, src_gray; +Mat dst, detected_edges; + +int edgeThresh = 1; +int lowThreshold = 77; +int max_lowThreshold = 100; +int ratio = 3; +int kernel_size = 3; + +vector circles; +Point g_Center(0, 0); +int g_radius = 0; +/** +* @函数 CannyThreshold +* @简介: trackbar 交互回调 - Canny阈值输入比例1:3 +*/ +void CannyThreshold(int, void*) +{ + int radius = 0; + int iMaxLimit = 0; + double min = 0; + string window_name = "Edge Map"; + /// 使用 3x3内核降噪 + //while (lowThreshold > 0) { + blur(src_gray, detected_edges, Size(3, 3)); + + /// 运行Canny算子 + Canny(detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size); + //printf("LowThresold : %d -- %d \n", lowThreshold, lowThreshold*ratio); + /// 使用 Canny算子输出边缘作为掩码显示原图像,声明一个三通道图像,像素值全为0,用来将霍夫变换检测出的圆画在上面 + dst = Scalar::all(0); + + src.copyTo(dst, detected_edges); + HoughCircles(detected_edges, circles, CV_HOUGH_GRADIENT, + 1, //dp,累加器图像的分辨率,增大则分辨率变小 + 100, // 100, //minDist,很重要的一个参数,告诉两个圆之间的距离的最小距离,如果已知一副图像,可以先行计 + //算出符合自己需要的两个圆之间的最小距离。 + 150, //param1,canny算法的阈值上限,下限为一半(即100以上为边缘点,50以下抛弃,中间视是否相连而定) + 100, //param2,决定成圆的多寡 ,一个圆上的像素超过这个阈值,则成圆,否则丢弃 + 230, //minRadius,最小圆半径,这个可以通过图片确定你需要的圆的区间范围 + 460 //maxRadius,最大圆半径 + ); + //imshow(window_name, dst); + // if (circles.size() <= 0) { + // lowThreshold--; + // continue; + //} + // break; + //} + if (circles.size() <= 0) { + printf("Can not circles, return, thred=%d\n", lowThreshold); + return; + } + for (size_t i = 0; i < circles.size(); i++)//把霍夫变换检测出的圆画出来 + { + Point center(cvRound(circles[i][0]), cvRound(circles[i][1])); + radius = cvRound(circles[i][2]); + g_Center = center; + g_radius = radius; + line(dst, center, center, Scalar(0, 0, 255), 8); + //circle(dst, center, 0, Scalar(0, 255, 0), -1, 8, 0); + int a = cvRound(circles[i][0]); + int b = cvRound(circles[i][1]); + circle(dst, center, radius, Scalar(255, 0, 0), 2); + //printf("low=%.4f max=%d\n", lowThreshold, iMaxLimit); + printf("No%d - {%d} %4d %dx%d %d %d %d\n", i + 1, lowThreshold, + (int)circles.size(), a, b, radius, dst.cols, dst.rows); + //在控制台输出圆心坐标和半径 + } + imshow(window_name, dst); +#if 1 + // 找线段 + vector lines2; + int Len = 0, iCirLen = 0; + HoughLinesP(detected_edges, lines2, 1, CV_PI / 180, 50, 50, 10); + double vv = 0.0f; + double fmin = 26.0f; + int p = 0; + //while (p <= 0) { + // min++; + for (int n = 0; n < (int)lines2.size(); n++) + { + // minangle = 60 maxangle = 320 最大刻度量 10,theta = 220 + Point A(lines2[n][0], lines2[n][1]), B(lines2[n][2], lines2[n][3]); // 一条线段的2头坐标点 + Point C((int)circles[0][0], (int)circles[0][1]); // 圆心点坐标 + double vv = DistancetoSegment(C, A, B); + Len = (int)Length(Point(A.x - B.x, A.y - B.y)); + //printf("Len=%d vv = %.4f\n", Len, vv); + if (Len > 150 && vv < fmin) { + fmin = vv; + line(dst, A, B, Scalar(0, 255, 0), 2, CV_AA); + printf("OK_Len=%d vv = %.4f\n", Len, vv); + imshow(window_name, dst); + } + //iCirLen = (int)Length(Point((int)C.x - B.x, (int)C.y - B.y)); + + //if (Len > g_radius) // && vv < 13.0f) { + //if (Len > 100 && vv < 10.0f) { + // line(dst, A, B, Scalar(0, 255, 0), 2, CV_AA); + // min = vv; + // p = n; + // // + // //int x_angle = B.x - A.x; + // //int y_angle = A.y - B.y; + // //if (x_angle <= 0) continue; + + // //double theta = atan(tan(y_angle / x_angle)); + // //double final_theta = 0; + // //// 左右分 + // ////if ((x_angle > 0 && y_angle > 0) || (x_angle > 0 && y_angle < 0)) + // //// final_theta = 270 - theta; + // ////if ((x_angle < 0 && y_angle >0) || (x_angle < 0 && y_angle < 0)) + // //// final_theta = 90 - theta; + // ////上下分 + // //if ((x_angle > 0 && y_angle > 0) || (x_angle < 0 && y_angle > 0)) + // // final_theta = 360 - theta; + // //if ((x_angle > 0 && y_angle <0) || (x_angle < 0 && y_angle < 0)) + // // final_theta = 180 - theta; + + // // minangle = 48, maxangle = 320 最大刻度量 10 + // double du = get_point_angles(B, A); + // //double val = (final_theta - 48) * 1.6f / (270 - 24); + // //double val = dGetReadValueOfYZFTH(du, 360, Len); + // double val = (du * 10) / (270); + // //printf_s(" = %.2f° %.2f %.2f %.2f %.2f [%d]\n", Angles(C, B, A), val, final_theta, theta, vv, Len); + // //printf_s(" = %.2f° %.2f %.2f %.2f %.2f [%d]\n", get_point_angles(B,A), val, final_theta, theta, vv, Len); + // printf(" = %.2f° [%.2f] %.2f [%d]\n", get_point_angles(B, A), val, vv, Len); + //} + + + } + printf("OK_Len=%d vv = %.4f\n", Len, fmin); + //} +#endif + imshow(window_name, dst); +} + +// 检测线段 +void vHoughLines(int, void*) +{ + string window_name = "Edge Map"; + // 找线段 + vector lines2; + int Len = 0, iCirLen = 0; + HoughLinesP(detected_edges, lines2, 1, CV_PI / 180, 50, 50, 10); + for (int n = 0; n < (int)lines2.size(); n++) + { + // minangle = 60 maxangle = 320 最大刻度量 10,theta = 220 + Point A(lines2[n][0], lines2[n][1]), B(lines2[n][2], lines2[n][3]); // 一条线段的2头坐标点 + Point C((int)circles[0][0], (int)circles[0][1]); // 圆心点坐标 + double vv = DistancetoSegment(C, A, B); + + + Len = (int)Length(Point(A.x - B.x, A.y - B.y)); + iCirLen = (int)Length(Point((int)C.x - B.x, (int)C.y - B.y)); + + if (Len > g_radius && vv < 10.0f) { + line(dst, A, B, Scalar(0, 255, 0), 2, CV_AA); + // + int x_angle = B.x - C.x; + int y_angle = C.y - B.y; + if (x_angle <= 0) continue; + + double theta = atan(tan(y_angle / x_angle)); + double final_theta = 0; + // 左右分 + //if ((x_angle > 0 && y_angle > 0) || (x_angle > 0 && y_angle < 0)) + // final_theta = 270 - theta; + //if ((x_angle < 0 && y_angle >0) || (x_angle < 0 && y_angle < 0)) + // final_theta = 90 - theta; + //上下分 + if ((x_angle > 0 && y_angle > 0) || (x_angle < 0 && y_angle > 0)) + final_theta = 360 - theta; + if ((x_angle > 0 && y_angle < 0) || (x_angle < 0 && y_angle < 0)) + final_theta = 180 - theta; + + double val = (final_theta - 60) * 10 / (320 - 60); + //double val = (final_theta - 48) * 10 / (270 - 24); + printf("%.2f %.2f %.2f %.2f %.2f\n", Angles(C, A, B), val, final_theta, theta, vv); + } + } + imshow(window_name, dst); +} + +// 滑动窗口检测图片圆和线段 +void HTImgAnalys(const char *img_file) +{ + char *window_name = NULL; + /// 装载图像 + src = imread(img_file); + //AdaptiveFindThreshold(src, (double*)&lowThreshold, (double*)&max_lowThreshold, 3); + //AdaptiveFindThreshold(src, &lowThreshold, &max_lowThreshold, 3); + printf("lowThreshold = %f max_lowThreshold=%f\n", lowThreshold, max_lowThreshold); + window_name = (char*)img_file; + + if (!src.data) + { + perror("error"); + return; + } + printf("width x higth = %dx%d\n", src.cols, src.rows); + printf("No. Ltld Htld cirs center radius w_cols h_rows Angles value final_theta theta vv Len\n"); + + // 创建与src同类型和大小的矩阵(dst) + dst.create(src.size(), src.type()); + + // 原图像转换为灰度图像 + cvtColor(src, src_gray, CV_BGR2GRAY); + + // 创建显示窗口 + namedWindow(window_name, CV_WINDOW_AUTOSIZE); + + // 创建trackbar + createTrackbar("Min Threshold:", window_name, &lowThreshold, max_lowThreshold, CannyThreshold); + // createTrackbar("Min Threshold:", window_name, (int*)&low, max, CannyThreshold); + + // 显示图像 + CannyThreshold(0, 0); + //vHoughLines(0, 0); + + // 等待用户反应 + waitKey(0); + dst.release(); +} + +// 上海自动化仪表股份有限公司OCr17Ni12Mo2压力表,min~max=0-1.6MPa +void vInitPress_OCR17NI12MO2_16() +{ + // radio, du, val + float initdu = 225.0f; //max 315-1.6, 225-0.0 + float du = 225.0f; + float val = 0; + int i = 0; + for (i = 0; i < 80; i++) + { + printf("{%d,%.2f,%.2f},\n", 183, du, val); + du -= 5.40f; + if (du < 0) du = 360 - du; + val += 0.05f; + } + printf("{%d,%.2f,%.2f},\n", 180, du, val); +} + +// 上海自动化仪表股份有限公司OCr17Ni12Mo2压力表,min~max=0-2.5MPa +void vInitPress_OCR17NI12MO2_25() +{ + +} diff --git a/src/HXIec104.cpp b/src/HXIec104.cpp index 40d4ded..8c5449b 100644 --- a/src/HXIec104.cpp +++ b/src/HXIec104.cpp @@ -909,4 +909,1574 @@ unsigned short RealNumber; if(NsIec104Struct.YkCellAction==NS_CLOSE) NsIec104Struct.SendBuffer[Index++]=0x82; else - \ No newline at end of file + NsIec104Struct.YkCellAction=0xff; + + /* Repair Length */ + NsIec104Struct.SendBuffer[1]=Index-2; + if((NsIec104Struct.SendLength=send(NsIec104Struct.NsNewSocketId,&NsIec104Struct.SendBuffer[0],Index,0x0))<0) + { + perror ("Client FirstWrite Error"); + } +} + +static void NsIec104SendYkExeConfirm(void) +{ + + +unsigned short YkObject,Index; +unsigned char *P,i; +unsigned short RealNumber; + + + /* YkExe Confirm */ + Index=0; + NsIec104Struct.SendBuffer[Index++]=0x68; + NsIec104Struct.SendBuffer[Index++]=0x00; + + RealNumber=(NsIec104Struct.SendMeNumber<<1); + P=(unsigned char *)&RealNumber; + NsIec104Struct.SendBuffer[Index++]=P[0]; /* send number */ + NsIec104Struct.SendBuffer[Index++]=P[1]; + NsIec104Struct.SendMeNumber=(NsIec104Struct.SendMeNumber+1)%65535; + + + NsIec104Struct.SendBuffer[Index++]=NsIec104Struct.ReceiveHimNumber[0]; /* receive number */ + NsIec104Struct.SendBuffer[Index++]=NsIec104Struct.ReceiveHimNumber[1]; + + NsIec104Struct.SendBuffer[Index++]=NsIec104Struct.YkYtType; /* asdu type */ + NsIec104Struct.SendBuffer[Index++]=1; /* information object count */ + + /* REASONBYTE now is two */ + NsIec104Struct.SendBuffer[Index++]=0x07; /* reason */ + NsIec104Struct.SendBuffer[Index++]=0x00; /* reason */ + + + for(i=0;i>=6; + NsIec104Struct.PulseGroup=NsIec104Struct.Qcc&0x3f; + + switch(NsIec104Struct.FreezeSign) + { + case 0: + NsIec104ProcessPulseData(); + break; + case 1: + case 2: + case 3: + NsIec104ProcessPulseAck(); + break; + } +} + +static void NsIec104ProcessPulseGroup(void) +{ +unsigned short General,Index; +unsigned char *P,start; +unsigned short i,j,RealNumber,YmLib; + + + for(start=0;start<2;start++) + { + Index=0; + NsIec104Struct.SendBuffer[Index++]=0x68; + NsIec104Struct.SendBuffer[Index++]=0x00; + + RealNumber=(NsIec104Struct.SendMeNumber<<1); + P=(unsigned char *)&RealNumber; + NsIec104Struct.SendBuffer[Index++]=P[0]; /* send number */ + NsIec104Struct.SendBuffer[Index++]=P[1]; + NsIec104Struct.SendMeNumber=(NsIec104Struct.SendMeNumber+1)%65535; + + + NsIec104Struct.SendBuffer[Index++]=NsIec104Struct.ReceiveHimNumber[0]; /* receive number */ + NsIec104Struct.SendBuffer[Index++]=NsIec104Struct.ReceiveHimNumber[1]; + + NsIec104Struct.SendBuffer[Index++]=15; /* asdu type */ + NsIec104Struct.SendBuffer[Index++]=16; /* information object count */ + + /* REASONBYTE now is two */ + NsIec104Struct.SendBuffer[Index++]=37+NsIec104Struct.PulseGroup; /* reason */ + NsIec104Struct.SendBuffer[Index++]=0x0; /* reason */ + + + for(i=0;i=NS_IEC104_GROUP_BEGIN&&NsIec104Struct.AsduQualifier<=NS_IEC104_GROUP_END) + { + WhichGroup=NsIec104Struct.ReceiveBuffer[NS_IEC_104_GROUP_BYTE]; + WhichGroup-=21; + switch(WhichGroup) + { + + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + NsIec104ProcessYxGroup(WhichGroup); + break; + case 8: + case 9: + case 10: + case 11: + NsIec104ProcessYcGroup(WhichGroup); + break; + case 12: /* location information */ + NsIec104DefaultGroup(WhichGroup); + break; + case 13: /* BCD Information */ + NsIec104DefaultGroup(WhichGroup); + break; + case 14: /* cell state */ + NsIec104DefaultGroup(WhichGroup); + break; + case 15: /* backup use */ + NsIec104DefaultGroup(WhichGroup); + break; + default: + NsIec104DefaultGroup(WhichGroup); + break; + } + + } + +} + +static void NsIec104DefaultGroup(unsigned char Group) +{ +unsigned short General,Index; +unsigned char *P; +unsigned short i,j,RealNumber; + + + /* First Send Confirm */ + Index=0; + NsIec104Struct.SendBuffer[Index++]=0x68; + NsIec104Struct.SendBuffer[Index++]=0x0e; + + RealNumber=(NsIec104Struct.SendMeNumber<<1); + P=(unsigned char *)&RealNumber; + NsIec104Struct.SendBuffer[Index++]=P[0]; /* send number */ + NsIec104Struct.SendBuffer[Index++]=P[1]; + NsIec104Struct.SendMeNumber=(NsIec104Struct.SendMeNumber+1)%65535; + + + + NsIec104Struct.SendBuffer[Index++]=NsIec104Struct.ReceiveHimNumber[0]; /* receive number */ + NsIec104Struct.SendBuffer[Index++]=NsIec104Struct.ReceiveHimNumber[1]; + + + NsIec104Struct.SendBuffer[Index++]=100; + NsIec104Struct.SendBuffer[Index++]=1; + + for(i=0;i< NS_IEC_104_REASONBYTE;i++) + NsIec104Struct.SendBuffer[Index++]=0x07; + + for(i=0;i>g)&0x01; + NsIec104Struct.SendBuffer[Index++]=YxBit; + } + } + /* Repair Length */ + NsIec104Struct.SendBuffer[1]=Index-2; + if((NsIec104Struct.SendLength=send(NsIec104Struct.NsNewSocketId,&NsIec104Struct.SendBuffer[0],Index,0x0))<0) + { + perror ("Client FirstWrite Error"); + } + } + + taskDelay(6); + Index=0; + NsIec104Struct.SendBuffer[Index++]=0x68; + NsIec104Struct.SendBuffer[Index++]=0x0e; + + RealNumber=(NsIec104Struct.SendMeNumber<<1); + P=(unsigned char *)&RealNumber; + NsIec104Struct.SendBuffer[Index++]=P[0]; /* send number */ + NsIec104Struct.SendBuffer[Index++]=P[1]; + NsIec104Struct.SendMeNumber=(NsIec104Struct.SendMeNumber+1)%65535; + + + NsIec104Struct.SendBuffer[Index++]=NsIec104Struct.ReceiveHimNumber[0]; /* receive number */ + NsIec104Struct.SendBuffer[Index++]=NsIec104Struct.ReceiveHimNumber[1]; + + NsIec104Struct.SendBuffer[Index++]=100; + NsIec104Struct.SendBuffer[Index++]=1; + + for(i=0;i< NS_IEC_104_REASONBYTE;i++) + NsIec104Struct.SendBuffer[Index++]=10; + + for(i=0;i>g)&0x01; + NsIec104Struct.GroupSendBuffer[GroupIndex][Index++]=YxBit; + } + } + + /* + Debug Yx Message + for(j=0;j21 sigle yc asdu type */ + NsIec104Struct.GroupSendBuffer[GroupIndex][Index++]=NS_IEC_104_ONCE_YC|0x80; /* information object count */ + + NsIec104Struct.GroupSendBuffer[GroupIndex][Index++]=20; /* reason */ + NsIec104Struct.GroupSendBuffer[GroupIndex][Index++]=0x0; /* reason */ + + + for(i=0;i0) + { + if((NsIec104Struct.SendLength = send(NsIec104Struct.NsNewSocketId,&NsIec104Struct.SendBuffer[0],6,0x0)) < 0) + { + perror ("Client Write Error"); + } + /* + printf("\ns:"); + for (i = 0; i < 6; i++) + printf("%4x",NsIec104Struct.SendBuffer[i]); + printf("\n"); + */ + } +} + + + +static void NsIec104Reset(void) +{ + +NsIec104Struct.ReceiveLength=0; +NsIec104Struct.ReceiveIndexLength=0; +NsIec104Struct.ReceiveIndex=0; +NsIec104Struct.HasSendNumber=0; + +} + + + + +static void NsIec104CycleCount(void) +{ +unsigned short i; +unsigned long Nap,NowSecond; + + + + getforsecond(&NowSecond); + for(i=0;i= NsIec104Cycle[i].TimeNap) + { + NsIec104Cycle[i].TimeSign= ON; + getforsecond(&NsIec104Cycle[i].LastTime); + } + } +} +#endif +/* +void debug104(void) +{ +unsigned char Buff[256]; +short i; + + for(i=0;i<200;i++) + Buff[i]=0; + + for(i=0;i<96;i+=2) + Buff[i]=i+100; + + NsPutDd1Lib(NS_YC_DATA,0,0,&Buff[0],48); + + for(i=0;i<12;i++) + Buff[i]=0x0f; + NsPutDd1Lib(NS_YX_DATA,0,0,&Buff[0],12); +} + +static void Iec104testCosSoe(void) { + struct tm NowTime; + static i = 0; + static unsigned char st = 0; + NS_COS_FORMAT CosValue; + NS_SOE_FORMAT SoeValue; + + getvxTime(&NowTime); +for(i=0;i<10;i++){ + SoeValue.SendNumber = i; + SoeValue.KgNumber = i; + SoeValue.CellNumber = 0; + SoeValue.RealAddress = 1; + SoeValue.Type = 2; + SoeValue.State = st; + + SoeValue.Year = 2003; + SoeValue.Month = 7; + SoeValue.Day = 22; + SoeValue.Hour = 15; + + SoeValue.Minute = 00; + SoeValue.Second = i; + SoeValue.Msecond = 888; + + NsPutNetSoeLib(&SoeValue); + NsPutDd1SoeLib(&SoeValue); + NsPutDd2SoeLib(&SoeValue); + NsPutDd3SoeLib(&SoeValue); + NsPutAllSoeLib(&SoeValue); + + CosValue.SendNumber =i; + CosValue.KgNumber = i; + CosValue.CellNumber = 0; + CosValue.RealAddress = 1; + CosValue.Type = 2; + CosValue.State = st; + + NsPutNetCosLib(&CosValue); + NsPutDd1CosLib(&CosValue); + NsPutDd2CosLib(&CosValue); + NsPutDd3CosLib(&CosValue); + NsPutAllCosLib(&CosValue); + +} + if (st == 0) st = 1; + else st = 0; +} +*/ \ No newline at end of file diff --git a/src/make.mak b/src/make.mak index 1ff9819..61ed11d 100644 --- a/src/make.mak +++ b/src/make.mak @@ -9,14 +9,14 @@ CC = g++ -MYSQL_PATH = /usr/local/mysql-5.5.61 +MYSQL_LIB_PATH = /usr/lib64/mysql MYSQL_LIB_FILE = mysqlclient #CFLAGS = -c -Werror -g -D_REENTRANT -Wformat -Wsign-compare -O3 -ansi CFLAGS = -c -Werror -g -D_DEBUG -D_REENTRANT -Wformat -Wsign-compare -O3 -ansi -LIBS = -L$(MYSQL_PATH)/lib -l$(MYSQL_LIB_FILE) `pkg-config --libs opencv` -lnsl -lpthread -lm -lc -lstdc++ +LIBS = -L$(MYSQL_LIB_PATH) -L/usr/local/lib64 -l$(MYSQL_LIB_FILE) -lopencv_core -lopencv_imgproc -lnsl -lpthread -lm -lc -lstdc++ -INCPATH = -I../include -I$(MYSQL_PATH)/include `pkg-config --cflags opencv` +INCPATH = -I../include -I$(MYSQL_PATH)/include # global file OBJS_PATH = ../obj diff --git a/src/makefile b/src/makefile index d7aed10..2bac75b 100644 --- a/src/makefile +++ b/src/makefile @@ -10,14 +10,14 @@ CC = g++ #MYSQL_PATH = /usr/local/mysql-5.5.43 -MYSQL_PATH = /usr/local/mysql-5.7.28 +MYSQL_LIB_PATH = /usr/lib64/mysql MYSQL_LIB_FILE = mysqlclient #CFLAGS = -c -Werror -g -D_REENTRANT -Wformat -Wsign-compare -O3 -ansi CFLAGS = -c -Werror -g -D_DEBUG -D_REENTRANT -Wformat -Wsign-compare -O3 -ansi -LIBS = -L$(MYSQL_PATH)/lib -l$(MYSQL_LIB_FILE) `pkg-config --libs opencv` -lnsl -luuid -lpthread -lm -lc -lstdc++ +LIBS = -L$(MYSQL_LIB_PATH) -L/usr/local/lib64 -l$(MYSQL_LIB_FILE) -lopencv_core -lopencv_imgproc -lopencv_highgui -lopencv_videoio -lopencv_imgcodecs -lz -ldl -lpng -lnsl -luuid -lpthread -lm -lc -lstdc++ -INCPATH = -I../include -I$(MYSQL_PATH)/include `pkg-config --cflags opencv` +INCPATH = -I../include -I$(MYSQL_PATH)/include # global file OBJS_PATH = ../obj