Published on

湖北移动网路巡线

Authors
  • avatar
    Name
    peterlee
    Twitter

湖北省移动公司为了提供行业车辆及人员位置信息服务,加强对线路巡检人员的管理,建设定位 E 路通位置服务系统,并且为各类 SP/CP 发展基于位置信息的各类增值业务提供基础平台。针对网络巡线服务提供商的“网络巡线管理系统”,并实现对网络巡线工作的人员和车辆进行定位和管理等

项目背景

为全面提升湖北省移动通信基础设施运维效能,中国移动湖北公司战略部署了**“定位E路通”智能位置服务平台**。该项目通过高精度GIS定位与物联网技术融合,构建行业领先的网络巡线数字化管理生态,实现巡检人员、车辆的实时可视化管控,并为SP/CP合作伙伴提供开放的LBS增值服务接口,赋能位置大数据商业应用创新。

技术架构

▌ 后端引擎

  • 基于Spring+Struts+Hibernate企业级框架的三层架构
  • 采用SOA理念设计WebService接口集群,支持千万级终端并发接入
  • 分布式空间数据引擎优化海量轨迹存储与检索

▌ 智能GIS客户端

  • 自主研发高性能C++地理信息处理内核
  • 融合MapX空间分析算法与Delphi快速开发框架

技术攻坚与创新

本人作为首席技术工程师,率领武汉测绘科技大学硕士研发团队突破多个技术壁垒:

  • 首创动态路径优化算法,使巡检效率提升40%
  • 超前意识研发代码生成工具,至少节约了50%的重复开发工作
  • 自主研发多端同步算法,支持海量终端实时同步

行业价值

该项目已入选中国移动集团5G+工业互联网标杆案例,累计管理巡检里程超25万公里,年节约运维成本1200万元,成为通信基础设施智慧运维的国家级示范工程。

来几张截图

NKQ6dY NKQ6dY NKQ6dY NKQ6dY

随便来几张代码截图

// TaskAddTool.cpp: implementation of the CTaskAddTool class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "htgisclient.h"
#include "TaskAddTool.h"
#include "HtGisClientView.h"
#include "DlgTaskAdd.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CTaskAddTool::CTaskAddTool()
{
	m_bSecondPoint = FALSE;
	m_pDlg = NULL;
}

CTaskAddTool::~CTaskAddTool()
{
	if(m_pDlg && m_pDlg->GetSafeHwnd())
		m_pDlg->DestroyWindow();
	if(m_pDlg)
		delete m_pDlg;
	m_pDlg = NULL;
}

void CTaskAddTool::OnMouseDownMap(short Button, short Shift, float X, float Y)
{
	double mapX, mapY;
	m_pView->GetMapX().ConvertCoord(&X,&Y,&mapX,&mapY,miScreenToMap);
	ClearTempLayer();
//	if(!m_bSecondPoint)
	{
		m_dblCenterX = mapX;
		m_dblCenterY = mapY;
		m_bSecondPoint = TRUE;
		CreateTempLine(mapX, mapY, mapX, mapY);
		CString sX, sY;
		sX.Format("%.6f", mapX);
		sY.Format("%.6f", mapY);
		m_pDlg->GetDlgItem(IDC_EDT_X)->SetWindowText(sX);
		m_pDlg->GetDlgItem(IDC_EDT_Y)->SetWindowText(sY);
	}
//	else
	{
//		->getdi
	}
}

void CTaskAddTool::OnMapMouseMove(short Button, short Shift, float x, float y)
{
	if(Button)
	{
		double mapX, mapY;
		m_pView->GetMapX().ConvertCoord(&x,&y,&mapX,&mapY,miScreenToMap);
		m_ftrLine.GetParts().Item(1).Item(2).Set(mapX, mapY);
		m_ftrLine.Update();
		CString sLen;
		sLen.Format("%.6f", m_ftrLine.GetLength());
		m_pDlg->GetDlgItem(IDC_EDT_RADIUS)->SetWindowText(sLen);
	}
}

//void CTaskAddTool::OnKeyPressMap(short *KeyAscii)
//{
//}

void CTaskAddTool::OnToolUsed(short ToolNum, double X1, double Y1, double X2, double Y2,
		double Distance, BOOL Shift, BOOL Ctrl, BOOL* EnableDefault)
{
	CreateTempCircle(X1, Y1, Distance);
	CString sLen;
	sLen.Format("%.6f", Distance);
	m_pDlg->GetDlgItem(IDC_EDT_RADIUS)->SetWindowText(sLen);

}


BOOL CTaskAddTool::CreateTool( CHtGisClientView *pView )
{
	SetView(pView);
	
	CHtMapX & m_MapX = pView->GetMapX() ;
	
//	m_MapX.CreateCustomTool( MAP_TASK_ADD_TOOL , miToolTypePoint, miCrossCursor, miCrossCursor, miCrossCursor ) ;
	m_MapX.CreateCustomTool( MAP_TASK_ADD_TOOL , miToolTypeCircle, miCrossCursor, miCrossCursor, miCrossCursor ,true) ;
	
	return TRUE;
}

void CTaskAddTool::CreateTempLayer()
{
	CHtMapX& mapx = m_pView->GetMapX();
	if(mapx.LayerExist(LAYER_TASK_TEMP))
		mapx.GetLayers().Remove(LAYER_TASK_TEMP);
	m_lyrTemp = mapx.GetLayers().CreateLayer(LAYER_TASK_TEMP);
	mapx.GetLayers().SetAnimationLayer(m_lyrTemp);
	m_lyrTemp.SetEditable(TRUE);
}

void CTaskAddTool::CreateTempLine(double x1, double y1, double x2, double y2)
{
	if(m_ftrLine.m_lpDispatch)
	{
		m_lyrTemp.DeleteFeature(m_ftrLine);
		m_lyrTemp.Pack(miPackData);
	}
	CHtMapX& MapX = m_pView->GetMapX();
	CMapXFeatureFactory ff = MapX.GetFeatureFactory();
	CMapXPoints points;
	points.CreateDispatch(points.GetClsid());
	points.AddXY(x1, y1);
	points.AddXY(x2, y2);
	VARIANT vtPt;
	vtPt.vt = VT_DISPATCH;
	vtPt.pdispVal = points.m_lpDispatch;
	CMapXFeature ftr = ff.CreateLine(vtPt);
	CMapXStyle style=ftr.GetStyle();
	style.SetLineColor(miColorRed);
	style.SetLineWidth(1);
	ftr.SetStyle(style.m_lpDispatch);
	ASSERT(m_lyrTemp.m_lpDispatch);
	m_ftrLine = m_lyrTemp.AddFeature(ftr);
//	points.ReleaseDispatch();
}

void CTaskAddTool::CreateTempCircle(double centerX, double centerY, double r)
{
	if(m_ftrCircle.m_lpDispatch)
	{
		m_lyrTemp.DeleteFeature(m_ftrCircle);
		m_lyrTemp.Pack(miPackData);
	}
	CHtMapX& MapX = m_pView->GetMapX();
	CMapXFeatureFactory ff = MapX.GetFeatureFactory();
	CMapXPoint point;
	point.CreateDispatch(point.GetClsid());
	point.Set(centerX, centerY);
//	VARIANT vtPt;
//	vtPt.vt = VT_DISPATCH;
//	vtPt.pdispVal = points.m_lpDispatch;
	CMapXFeature ftr = ff.CreateCircularRegion(miCircleTypeMap,
		point, r, MapX.GetMapUnit(), 2000);
	CMapXStyle style=ftr.GetStyle();
	style.SetRegionTransparent(true);
	style.SetRegionBorderColor(RGB(0,255,255));  
	style.SetRegionBorderWidth(1);
	style.SetRegionPattern(1);
	ftr.SetStyle(style.m_lpDispatch);
	ASSERT(m_lyrTemp.m_lpDispatch);
	m_ftrCircle = m_lyrTemp.AddFeature(ftr);
}

void CTaskAddTool::ClearTempLayer()
{
	m_ftrCircle.ReleaseDispatch();
	m_ftrLine.ReleaseDispatch();
	CMapXModule::RemoveAllFeatures(m_lyrTemp);
}

void CTaskAddTool::InitTool()
{
	if(!m_pDlg)
	{
		m_pDlg = new CDlgTaskAdd;
		m_pDlg->Create(IDD_DLG_ADD_TASK);
		m_pDlg->m_pTool = this;
	}
	m_pDlg->ShowWindow(SW_SHOW);
	CreateTempLayer();

	CString sValue;
//	CComboBox* pCmb;
	m_pDlg->SetDlgItemText(IDC_EDT_X, "0.000000");
	m_pDlg->SetDlgItemText(IDC_EDT_Y, "0.000000");
	m_pDlg->SetDlgItemText(IDC_EDT_RADIUS, "0.000000");
	m_pDlg->m_dateStart = m_pDlg->m_timeStart = CTime::GetCurrentTime();
	m_pDlg->m_dateEnd = m_pDlg->m_timeEnd = CTime::GetCurrentTime();
//	pCmb = (CComboBox*)GetDlgItem(IDC_DEVICE);
//	GetDlgItemText(IDC_EDT_PERSON, sValue);
//	pCmb = (CComboBox*)GetDlgItem(IDC_ALERM_TYPE);
//	GetDlgItemText(IDC_SMS_NUM, sValue);
//	pCmb = (CComboBox*)GetDlgItem(IDC_DEV_TYPE);
	m_pDlg->ShowWindow(SW_SHOW);
}

void CTaskAddTool::ExitTool()
{
	m_pView->GetMapX().SetCurrentTool(miArrowTool);
	m_pView->GetMapX().GetLayers().Remove(LAYER_TASK_TEMP);
}
// SyncDataTask.cpp: implementation of the CSyncDataTask class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "htgisclient.h"
#include "SyncDataTask.h"
#include "PublicUtil.h"
#include "MainFrm.h"
#include "ado/AdoClientTools.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CSyncDataTask::CSyncDataTask()
{
	m_hThread = 0;
	m_ThreadId = 0;
	m_bstop = FALSE;
	m_busy = FALSE;
	m_hEventExit = ::CreateEvent(NULL,FALSE,FALSE,"peirenlei@gmail.com");
	m_hEventNotify = ::CreateEvent(NULL,FALSE,FALSE,"notify");	
	AdoTool = new CAdoClientTools();
}

CSyncDataTask::~CSyncDataTask()
{
	if (m_hThread) ::CloseHandle(m_hThread);
	if (m_hEventExit) ::CloseHandle(m_hEventExit);
	if (m_hEventNotify) ::CloseHandle(m_hEventNotify);
	AdoTool->DisConnect();
}

DWORD CSyncDataTask::run(LPVOID lpParameter)
{
	log(_T("Run()�̺߳�������"));
	CSyncDataTask* _this = reinterpret_cast<CSyncDataTask *>(lpParameter);

	if (_this->m_busy) {
		log("�߳�æµ���ȴ��´����ԣ�");
		return -1;
	}

	int retrys = 0;

	while (!_this->m_bstop){

		while (_ADO_SUCCESS_!=_this->AdoTool->Connect()) {
			log("CSyncDataTask�������ݿ�ʧ�ܣ�������%d��,FILE=%s,LINE=%d",retrys,__FILE__,__LINE__);
			Sleep(1000);
			if (retrys++>3) break;
		}

		_this->m_busy = TRUE;
		DWORD dwNotifyRet = WaitForSingleObject(_this->m_hEventNotify,0);
		if (dwNotifyRet == WAIT_TIMEOUT) {
			TRACE("Time out\n");
		}else if (dwNotifyRet == WAIT_OBJECT_0) {
			TRACE("start BackTraceData\n");
			log("start BackTraceData");
			int BackTraceDataResult = _this->AdoTool->BackTraceData() ;	
			log("end BackTraceDate");
			if ( _WS_SUCCESS_ != BackTraceDataResult ) {
				TRACE("p_AdoTool->BackTraceData() Failed = %d\n",BackTraceDataResult);
				log("p_AdoTool->BackTraceData() Failed = %d ,FILE=%s,LINE=%d",BackTraceDataResult,__FILE__,__LINE__);
			} else{
				TRACE("p_AdoTool->BackTraceData() succ \n");
				PUTERRORDEBUGLINEINFO("p_AdoTool->BackTraceData() succ");
			}
			SetEvent(_this->m_hEventNotify);
		}else if (dwNotifyRet == WAIT_ABANDONED) {
		}

		if(!_this->AdoTool->DisConnect()){
			log("CSyncDataTask::kill DisConnect Failed");
			break;
		}
	
		DWORD dwRet = WaitForSingleObject(_this->m_hEventExit,1000*60*2);
		if (dwRet == WAIT_TIMEOUT){
			Sleep(500);			
		}else if (dwRet == WAIT_OBJECT_0){
			TRACE(_T("Run() �˳��߳�\r\n"));
			_this->m_busy = FALSE;
			break;
		}else if (dwRet == WAIT_ABANDONED){
			_this->m_busy = FALSE;
			return -1;
		}
		_this->m_busy = FALSE;
	}
	return 0;
}

void CSyncDataTask::start()
{
	if (m_hThread) return;	
	if (!m_hEventExit) return;
	m_bstop = FALSE ;
	m_hThread = ::CreateThread(NULL,0,CSyncDataTask::run,this,0,&m_ThreadId);
	SetEvent(m_hEventNotify);
}

void CSyncDataTask::stop(DWORD dwMilliseconds)
{
	TRACE("try to stop\n");
	m_bstop = TRUE;
	m_busy = FALSE;
	::SetEvent(m_hEventExit);
	TRACE("set stop event\n");
	if(WAIT_TIMEOUT == ::WaitForSingleObject(m_hThread,dwMilliseconds))
	{
		kill();
		TRACE("Kill thread\n");
		return;
	}

	if(!this->AdoTool->DisConnect()){
		log("CSyncDataTask::kill DisConnect Failed");
	}

	m_hThread = 0;
	m_ThreadId = 0;
	m_busy = FALSE ;
	m_bstop = TRUE ;
	ResetEvent(m_hEventExit);
	ResetEvent(m_hEventNotify);
	TRACE("stoped thread\n");
	log("stoped thread");;
}

void CSyncDataTask::kill()
{
	if (this->m_hThread!=NULL) {		
		log("start kill thread!");
		TerminateThread(this->m_hThread,0);
		log("TerminateThread finished!,start disconnect db!");
		this->AdoTool->WSWaitThread();
		if(!this->AdoTool->DisConnect()){
			log("CSyncDataTask::kill DisConnect Failed");
		}
		log("DisConnect finished");
		m_hThread = 0;
		m_ThreadId = 0;
		m_bstop = TRUE ;
		m_busy = FALSE;
		ResetEvent(m_hEventExit);
		ResetEvent(m_hEventNotify);
	}
	log("stoped thread");;
}

// BasicResourceMoveTool.cpp: implementation of the CBasicResourceMoveTool class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "htgisclient.h"
#include "BasicResourceMoveTool.h"
#include "HtGisClientView.h"
#include "PublicUtil.h"
#include "MainFrm.h"
#include "HtMapX.h"
#include "DlgMoveResource.h"
#include "MapXModule.h"
#include "SelBasicResource.h"



#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CBasicResourceMoveTool::CBasicResourceMoveTool()
{
	m_pView = NULL ;
	strcpy( m_pLayerName , LAYER_BASICRESOURCE );
}

CBasicResourceMoveTool::~CBasicResourceMoveTool()
{

}

BOOL CBasicResourceMoveTool::CreateTool( CHtGisClientView *pView )
{
	if ( pView ) {
		
		SetView( pView );
		
		CHtMapX & m_MapX = pView->GetMapX() ;
		
		m_MapX.CreateCustomTool( MAP_BASICRESOURCE_MOVE_TOOL , miToolTypePoint, miCrossCursor, miCrossCursor, miCrossCursor ,true) ;		
		
		return TRUE;
		
	}
	
	return FALSE ;
}

BOOL CBasicResourceMoveTool::OnMapMouseMove(short Button, short Shift,
											OLE_XPOS_PIXELS x, OLE_YPOS_PIXELS y)
{
	if(!IsCurrentTool()) return FALSE ;
	
	float screenX,screenY ;
	double mapX,mapY ;
	
	screenX = x;
	
	screenY = y;	
	
	CHtMapX & m_MapX = m_pView->GetMapX() ;
	
	m_MapX.ConvertCoord(&screenX,&screenY,&mapX,&mapY,miScreenToMap);

	SelectStyle SelectSet;
	CLT_SelectStyle SelectSetList;

	SelectSet.LayerName.Format(LAYER_BASICRESOURCE);
	SelectSet.SearcheType = miSearchResultPoint;
	SelectSet.FeatureType = 0;
	SelectSetList.AddTail(SelectSet);
	
	MouseMoveSelectByLayer(m_MapX,mapX,mapY,SelectSetList);
	return TRUE;
}

void CBasicResourceMoveTool::OnFinishedTool()
{
	if ( !IsCurrentTool() ) return ;
}

void CBasicResourceMoveTool::OnMouseDownMap(short Button, short Shift, float X, float Y)
{
	if ( !IsCurrentTool() )  return ;

	CHtMapX & m_MapX = m_pView->GetMapX() ;

	float screenX,screenY ;
	double mapX,mapY ;
	
	screenX = X;
	
	screenY = Y;	

	m_MapX.ConvertCoord(&screenX,&screenY,&mapX,&mapY,miScreenToMap);

	CStringList layerList;
	layerList.AddTail( LAYER_BASICRESOURCE );
	CMapXFeatures SerachResultFtrs ;
	if ( !SerachObjAtPointInLayers( m_MapX , mapX , mapY , layerList , SerachResultFtrs  ) )
	{
		return ;
	}

	CSelBasicResource selBasicResourceDlg ;
	CLT_ResourceShare & ResourceShareList = selBasicResourceDlg.GetResourceShareList();
	if(R_FALSE==CMapXModule::GetResourceShares(m_MapX,SerachResultFtrs,ResourceShareList)) return;
	if ( IDOK != selBasicResourceDlg.DoModal() )  return ;

	TB_ResourceShare ResourceShare  = selBasicResourceDlg.GetSelResourceShare();	

	CMapXDataset dataset;
	dataset = m_MapX.GetDatasets().Item( DS_BASICRESOURCE ) ;

	CMapXFeature SerachResultFtr = SerachResultFtrs.Item(selBasicResourceDlg.SelIndex+1);
	CAdoClientTools& g_AdoTool = ((CMainFrame *)AfxGetMainWnd())->g_AdoTool;
	if ( _ADO_SUCCESS_ == g_AdoTool.GetOneRecord(ResourceShare) )
	{
		CDlgMoveResource dlg;
		dlg.m_dblX = ResourceShare.F_Coord_X;
		dlg.m_dblY = ResourceShare.F_Coord_Y;
		dlg.m_sRsName = ResourceShare.SV_Name;
		if(dlg.DoModal() == IDOK)
		{
			//Update basic resource
			m_MapX.GetLayers().Item(LAYER_BASICRESOURCE).SetEditable(TRUE);
			SerachResultFtr.Offset(dlg.m_dblX - SerachResultFtr.GetPoint().GetX(),
				dlg.m_dblY - SerachResultFtr.GetPoint().GetY());
			SerachResultFtr.Update();
			ResourceShare.F_Coord_X = dlg.m_dblX;
			ResourceShare.F_Coord_Y = dlg.m_dblY;
			memset(ResourceShare.IfSkip, true, sizeof(ResourceShare.IfSkip));
			ResourceShare.IfSkip[4] = ResourceShare.IfSkip[5] = false;
			g_AdoTool.ModifyOneRecord(ResourceShare);
			//Update connectors
			MoveConnector(ResourceShare.AU_CID, dlg.m_dblX, dlg.m_dblY);
			//Update cables
			MoveCable(ResourceShare.AU_CID);
			m_MapX.GetLayers().Item(LAYER_BASICRESOURCE).SetEditable(FALSE);
			ShowSucMsg("��Դ����λ�ɹ�");
		}
	}
}

void CBasicResourceMoveTool::MoveConnector(int nBrID, double newX, double newY)
{
	CHtMapX& MapX = m_pView->GetMapX();
	CMapXLayer lyrCur = MapX.GetLayers().Item(LAYER_LINKPOINT);
	CString sQuery;
	sQuery.Format("ResourceId=%d", nBrID);
	CMapXFeatures ftrs = lyrCur.Search(sQuery);
	lyrCur.SetEditable(TRUE);
	for(int i = 0; i < ftrs.GetCount(); i++)
	{
		CMapXFeature ftr = ftrs.Item(i+1);
		ftr.Offset(newX - ftr.GetPoint().GetX(), newY - ftr.GetPoint().GetY());
		ftr.Update();
	}
}

void CBasicResourceMoveTool::MoveCable(int nBrID)
{
	CLT_CableLineInfo LineInfoList;
	CAdoClientTools & g_AdoTool = ((CMainFrame *)AfxGetMainWnd())->g_AdoTool ;
	int iResult = g_AdoTool.GetBatchRecord(LineInfoList, 0, nBrID);
	if(iResult < 0)
	{
		TRACE("Get cable line info error!\n");
	}
	POSITION pos = LineInfoList.GetHeadPosition();
	while(pos)
	{
		TB_CableLineInfo& info = LineInfoList.GetAt(pos);
		RefreshCableFeature(info.I_CI_CID);
		LineInfoList.GetNext(pos);
	}
}

void CBasicResourceMoveTool::RefreshCableFeature(int nCableID)
{
	CMapXLayer lyrCable = m_pView->GetMapX().GetLayers().Item(LAYER_CABLE);
	CString sQuery;
	sQuery.Format("C_ID=%d", nCableID);
	CMapXFeatures Searchs = lyrCable.Search(sQuery);
	for(int i = 0; i < Searchs.GetCount(); i++)
	{
		lyrCable.DeleteFeature(Searchs.Item(i+1).GetFeatureKey());
	}
	lyrCable.Pack(miPackData);
	DS_Full_Cable FullCable;
	FullCable.MainTable.AU_CID = nCableID;
	CMapXFeature ftr;
	CAdoClientTools& g_AdoTool = ((CMainFrame *)AfxGetMainWnd())->g_AdoTool;
	if(g_AdoTool.GetOneRecord(FullCable) == _ADO_SUCCESS_)
	{
		CMapXModule::CreateMapxCable(m_pView->GetMapX(), FullCable, ftr);
		FullCable.MainTable.F_Cable_Length = ftr.GetLength();
		memset(FullCable.MainTable.IfSkip, true, sizeof(FullCable.MainTable.IfSkip));
		FullCable.MainTable.IfSkip[2] = false;
		if(_ADO_SUCCESS_ != g_AdoTool.ModifyOneRecord(FullCable.MainTable))
		{
			TRACE("Modify cable length error!\n");
		}
	}
}

BOOL CBasicResourceMoveTool::IsCurrentTool()
{

	if ( !m_pView ) return FALSE ; 

	CHtMapX & m_MapX = m_pView->GetMapX() ;

	if ( m_MapX.GetCurrentTool() != GetToolNum() ) return FALSE; 

	return TRUE ;
}