物联网平台 +Web 组态
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

701 lines
21 KiB

2 years ago
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="com.lp.cfg.ProConfig"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
<!DOCTYPE html>
<html>
<head>
<%@ include file="/WEB-INF/oss/iot/common/variable.jsp"%>
<%@ include file="/WEB-INF/oss/iot/common/variable_js.jsp"%>
<%@ include file="/WEB-INF/oss/iot/common/resource_lib.jsp"%>
<script type="text/javascript" src="http://api.map.baidu.com/api?v=2.0&ak=<%=ProConfig.Map.BAIDU_MAP_KEY%>"></script>
<style type="text/css">
#map {width: 100%;height: 100%;overflow: hidden;margin:0;font-family:"微软雅黑";}
.mapContainer{
height: 550px;width:100%;
}
.index_theLeft ul {
width: 100%;
padding: 0px;
margin: 0px;
overflow: auto;
}
.mapContainer{
height: 550px;width:100%;
}
.app-container{
background: #f2f2f2;width: 100%;border-top:1px solid #ecf0f5;
}
.box-white {
width: 97%;
min-width: 897px;
padding-bottom: 0;
margin: 0 auto;
}
.box {
padding: 10px;
padding-top:1px;
border-left: 1px solid #d2d6de;
border-right: 1px solid #d2d6de;
border-bottom: 1px solid #d2d6de;
}
.box {
border: 0!important;
box-shadow: 0 0 2px 1px rgba(13,5,9,.08)!important;
}
.box {
position: relative;
border-radius: 3px;
background: #fff;
border-top: 3px solid #d2d6de;
margin-bottom: 20px;
width: 100%;
box-shadow: 0 1px 1px rgba(0,0,0,.1);
}
.sceneList-info{
padding-top: 10px;
padding-left: 10px;
}
.generateStyle ,.infosgs {
position: absolute;
top: 18px;
right: 10px;
cursor: pointer;
}
.webui-popover{
z-index: 19991015;
}
.showbtn{
position: absolute;
top: 22px;
right: 56px;
background: #4CAF50;
color: #fff;
padding: 2px 4px;
border-radius: 3px;
cursor: pointer;
}
.input-map{
background-size: 5% ;
}
.BMap_cpyCtrl {
display: none;
}
.pro-span{
background: #4caf50;
padding: 3px 6px;
color: #fff;
border-radius: 4px;
}
.blue-span{
background: #3499da;
padding: 3px 6px;
color: #fff;
border-radius: 4px;
}
.red-span{
background: #f44336;
padding: 3px 6px;
color: #fff;
border-radius: 4px;
}
.pro-span{
background: #4caf50;
padding: 3px 6px;
color: #fff;
border-radius: 4px;
}
</style>
</head>
<body>
<div>
<!-- 下方部位 -->
<div>
<div class="mapContainer" >
<div class="app-container">
<div class="container-layout sceneList-info">
<div class="box join-device-box">
<div class="search-container jui-search_table" data-options="submit:'#searchbtns',
table:'#init-table'">
<span class="titleName">设备管理</span>
<input type-name="name" placeholder="设备名称" class="input input-self" />
<button id="searchbtns" class="button ml-20 bg-iot"><span class="icon-search"></span> 查询</button>
</div>
<div style="margin-top:10px;">
<div id="init-table" type-option="table" config-option="url:'/admin/page/node',
param:{},
columns:[
{'name':'序号','type':'seq','value':'seq','width':'7%'},
{'name':'设备名称','value':'name',limit:15,'width':'15%'},
{'name':'设备号','value':'device_code',limit:15,'width':'15%'},
{'name':'设备状态','type':'self',value_callback:'statusFunc2','value':'iot_node_status','width':'8%' },
{'name':'上传周期','value':'frequency','width':'9%' },
{'name':'所属项目','value':'scene_name',limit:10,'width':'12%'},
{'name':'操作','type':'self','value_callback':'value_function','width':'20%'}]" >
</div>
</div>
</div>
</div>
</div>
</div>
<div style="display:none;padding:20px;" type-option="form_submit" id="bindNode"
config-option=" 'url':'/node/bind',
'submit':'.bind-Btn',
'success':'sunccessReturn' ">
<input type-name="scene_id" class="scene_id" type="hidden" >
<div class="form-line">
<span class="red">*</span>&nbsp;<span>设备号:</span>
<div class="pb-10 pt-10" style="position: relative;">
<input placeholder="输入设备号SN" validate="empty:设备号不能为空" type="text" maxlength="62" type-name="device_code" class="input input-big clear-v" >
</div>
</div>
<div class="form-line clearfix">
<button onclick="closeAll();" class="button border-gray ml-10 button-big float-right">取消</button>
<button class="bind-Btn button ml-20 bg-iot button-big float-right">保存</button>
</div>
</div>
<div style="display:none;padding:20px;" type-option="form_submit" id="addCopyNode"
config-option=" 'url':'/node',
'submit':'.add-copy-Btn',
'success':'sunccessReturn' ">
<input type-name="scene_id" class="scene_id" type="hidden" >
<div class="form-line">
<span class="red">*</span>&nbsp;<span>设备名称:</span>
<div class="pb-10 pt-10">
<input placeholder="输入设备名称" validate="empty" type-name="name" class="input input-big clear-v" >
</div>
</div>
<div class="form-line">
<span class="red">*</span>&nbsp;<span>设备号:</span>
<div class="pb-10 pt-10" style="position: relative;">
<input placeholder="输入设备号SN" validate="empty:设备号不能为空" type="text" maxlength="62" type-name="device_code" class="input input-big clear-v" >
<img class="generateStyle" onclick="generateCode(this)" src="<%=basePath%>/image/oss/iot/generate.png">
<span class="showbtn" onclick="showHexInfo(this)" >HEX格式</span>
</div>
</div>
<div class="form-line">
<span class="red">*</span>&nbsp;<span>同类型设备号:</span>
<div class="pb-10 pt-10">
<input placeholder="输入同类型 设备号" validate="empty" type-name="copy_device_code" class="input device_copy input-big clear-v" >
</div>
</div>
<div class="form-line clearfix">
<button onclick="closeAll();" class="button border-gray ml-10 button-big float-right">取消</button>
<button class="add-copy-Btn button ml-20 bg-iot button-big float-right">保存</button>
</div>
</div>
<div style="display:none;padding:20px;" type-option="form_submit" id="modifyNode"
config-option=" 'url':'/node','method':'put',
'submit':'.modify-Btn',
'success':'sunccessReturn' ">
<div id="detail_get" config-option=" 'url':'/node?id=$#init-id$' , 'success':'successbtn' ">
<input type="hidden" type-name="id" id="init-id" >
<input type-name="scene_id" class="scene_id" type="hidden" >
<div class="form-line">
<span class="red">*</span>&nbsp;<span>设备名称:</span>
<div class="pb-10 pt-10">
<input placeholder="设备名称" validate="empty" type-name="name" class="input input-big" >
</div>
</div>
<div class="form-line">
<span class="red">*</span>&nbsp;<span>设备号:</span>
<div class="pb-10 pt-10" style="position: relative;" >
<input placeholder="输入设备号SN" readonly="readonly" type="text" maxlength="62" validate="empty" type-name="device_code" class="input input-big" >
<span></span>
<span class="showbtn" style="right: 20px;" onclick="showHexInfo(this)" >HEX格式</span>
</div>
</div>
<div class="form-line">
<div class="float-left" style="width: 48%; ">
<span class="red">*</span>&nbsp;<span>设备通讯协议:</span>
<div class="pb-10 pt-10" style="position: relative;">
<select type-name="iot_node_type" onchange="nodeTypeChange(this)" validate="empty" type-option="selecter"
config-option="'method':'GET','url':'/dictionary/81','key':'code','value':'name','type':'list' "
class="input input-big input-select modify_node_type " ></select>
</div>
</div>
<div class="float-left" style="width: 48%;margin-left: 4%;" >
<span class="red">*</span>&nbsp;<span>数据协议:</span>
<div class="pb-10 pt-10" style="position: relative;">
<select type-name="iot_protocal_category" onchange="protocalChange(this)" validate="empty" class="input input-big input-select nodetypeselect" ></select>
</div>
</div>
</div>
<div class="form-line infos-container hide">
<span>配置参数:</span>
<div class="pb-10 pt-10" style="position: relative;">
<input placeholder="按照协议填写,可不填" readonly="readonly" style="padding-right: 50px;" type-name="infos" class="input input-big infos infos-edit" >
<img class="infosgs self-icon" tag='参数配置' onclick="generateCode12(this)" src="<%=basePath%>/image/oss/iot/settings.png">
</div>
</div>
<div class="form-line">
<span>经纬度:</span>
<div class="pb-10 pt-10">
<input placeholder="设置设备默认经纬度" type-name="lonLat" class="input input-big input-map" style="background-position-x: 524px;" onclick="showMap(this)" >
</div>
</div>
<div class="form-line">
<div class="float-left" style="width: 49%;">
<span>上传周期[ 秒 ]:</span>
<div class="pb-10 pt-10">
<input placeholder="填写0,则不进行监控,单位s" value="30" validate="empty" type-name="frequency" class="input input-big" >
</div>
</div>
<div class="float-left" style="width: 49%; margin-left: 2%; ">
<span>排序:</span>
<div class="pb-10 pt-10">
<input placeholder="显示顺序" type="number" oninput="if(value.length>5) value=value.slice(0,5)" validate="empty" type-name="seq" class="input input-big" >
</div>
</div>
</div>
<div class="form-line clearfix">
<button onclick="closeAll();" class="button border-gray ml-10 button-big float-right">取消</button>
<button class="modify-Btn button ml-20 bg-iot button-big float-right">保存</button>
</div>
</div>
</div>
<div id="map_container" class="hide">
<div style="height: 350px;width: 100%;">
<div id="map"></div>
</div>
</div>
<div id="template_config" class="hide">
<div class="form-line"style="padding: 10px;">
<table class="table table-bordered">
<thead class="lpro-thead">
<tr>
<td>从机地址</td>
<td>功能码</td>
<td>寄存器起始地址</td>
<td>读取长度</td>
<td style="width: 120px;">操作</td>
</tr>
</thead>
<tbody class="tbody infoslist">
<tr>
<td>
<input id="address" class="input">
</td>
<td>
<select class="input" id="fcode" style="width: 90px;">
<option value='01' >01</option>
<option value='02' >02</option>
<option value='03' >03</option>
<option value='04' >04</option>
<option value='05' >05</option>
<option value='06' >06</option>
</select>
</td>
<td>
<input class="input" id="startaddress">
</td>
<td>
<input class="input" id="length">
</td>
<td>
<button onclick="addInfos()" class="button small-button bg-iot">增加</button>
</td>
</tr>
</tbody>
</table>
<div class="form-line clearfix" style="padding-top: 20px;">
<button onclick="saveInfo();" class="button ml-20 bg-iot button-big float-right">保存确认</button>
</div>
</div>
</div>
<div class="grant_auth hide">
<div style="padding: 30px;">
<div class="form-line" >
<span class="red">*</span>&nbsp;<span>项目名称:</span>
<div class="pb-10 pt-10">
<select validate="empty:请选择场景名称" config-option=" url:'/admin/page/scene?paged=1&pageSize=300',key:'id',value:'name',init_value:'请选择项目', data :{} " class="input input-big input-select jui-selecter sceneinfoid" ></select>
</div>
</div>
<div class="mt-20 clearfix">
<button onclick="closeAll();" class="button border-gray ml-10 button-big float-right">取消</button>
<button onclick="saveSceneInfo();" class="saveScene button ml-20 bg-iot button-big float-right">保存</button>
</div>
</div>
</div>
</div>
</div>
</body>
<script type="text/javascript">
var lt ;
function showMap(obj){
lt = openWindow("地图获取","#map_container","600px");
if(! validater.empty( $(obj).val() ) ){
// 如果当前已有定位,则将定位显示到地图上
theLocation2($(obj).val().split(",")[0],$(obj).val().split(",")[1]);
}
}
function showHexInfo(obj){
var val = stringToHex($(obj).prev().prev().val()) ;
layer.alert(val, {
title: 'HEX设备码格式',
closeBtn: 0
});
}
var map ;
function theLocation2(lon,lat){
map.clearOverlays();
var icon = new BMap.Icon('<%=basePath%>/image/oss/iot/location2.png', new BMap.Size(32, 32), {
anchor: new BMap.Size(32, 32)
});
var mkr = new BMap.Marker(new BMap.Point(lon,lat), {
icon: icon
});
var new_point = new BMap.Point(lon,lat);
map.addOverlay(mkr);
// 如果已有定位,则地图以改中心点居中
setTimeout(function(){
map.panTo(new_point);
},200);
}
function value_function(data,seq){
return "<button type='button' onclick='t_edit(this,"+data.id+")' class='layui-btn layui-btn-xs layui-btn-normal'>编辑</button>"+
"<button type='button' onclick='t_editSensor(this,"+data.id+")' class='layui-btn layui-btn-xs layui-btn-normal'>属性</button>"+
"<button type='button' onclick='LoknodeInfo(this,"+data.id+")' class='layui-btn layui-btn-xs layui-btn-normal'>详情</button>"+
"<button type='button' onclick='t_generateScene(this,"+data.id+")' class='layui-btn layui-btn-xs layui-btn-normal'>迁移</button>";
}
var tmp_node_id = -1 ;
function t_generateScene(obj,id){
tmp_node_id = id ;
openWindow("设备迁移",".grant_auth","400px");
}
function LoknodeInfo(obj,s){
// 打开窗口查看设备详情
layer.open({
type: 2,
title: '设备详情',
shadeClose: true,
shade: 0.7,
maxmin: true, //开启最大化最小化按钮
area: ['100%', '100%'],
content: "<%=basePath%>/page/devicedetail/index.html?id="
+ s+"&ucode=${user.user_key}"
});
}
function t_editSensor(obj,id){
// 打开窗口查看传感器列表
layer.open({
type: 2,
title: '传感器列表',
shadeClose: true,
shade: 0.7,
maxmin: true, //开启最大化最小化按钮
area: ['900px', '90%'],
content: "<%=basePath%>/service/iot/sensors_manger?node_id="
+ id
});
}
function saveSceneInfo(){
if($(".sceneinfoid").val() != null && $(".sceneinfoid").val() != "" ){
commonAjax('PUT',localUrl+'/node.json', {scene_id:$(".sceneinfoid").val() , id:tmp_node_id },function(data){
if(isOK(data)){
tip("成功");
setTimeout(function(){
$("#init-table").tableAdaptor();
closeAll() ;
}, 500);
}
});
}else{
tip("请选择迁移到的项目名称");
}
}
//设备协议
function func_type_name(obj){
if(obj == 83 ){
return "<span class='pro-span'>TCP</span>" ;
}else if(obj == 84) {
return "<span class='pro-span'>MQTT</span>" ;
}
}
function sunccessReturn(data){
if( data.status == status_code.OK ){
tip("成功");
setTimeout(function(){
closeAll();
$("#init-table").tableAdaptor();
},1000)
}else{
tip(data.statusMsg);
}
}
function t_edit(obj,id){
writeVal("init-id",id);
manual_init_detail_get($("#detail_get"));
setTimeout( function() {
openWindow("编辑设备","#modifyNode","600px");
}, 150);
}
function t_delete(obj,id){
lcomfirm('是否确定删除该设备?',function(){
commonAjax("DELETE",localUrl+"/node?id="+id, "",function(){
tip("删除成功");
setTimeout(function(){
closeAll();
$("#init-table").tableAdaptor();
},1000)
});
});
}
function randomWord1(randomFlag, min, max) {
let str = "",
range = min,
arr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
if (randomFlag) {
range = Math.round(Math.random() * (max - min)) + min;// 任意长度
}
for (var i = 0; i < range; i++) {
pos = Math.round(Math.random() * (arr.length - 1));
str += arr[pos];
}
return str;
}
function generateCode(obj){
$(obj).prev().val(randomWord1(true,6,28));
}
$(function(){
// 百度地图API功能
map = new BMap.Map("map"); // 创建Map实例
map.setMapStyle({
styleJson: [{
"featureType": "road",//道路
"elementType": "all",
"stylers": {
"color": "#ffffff",
"visibility": "off"
}
},
{
"featureType": "building",
"elementType": "all",
"stylers": {
"visibility": "off"
}
},
{
"featureType": "poilabel",//景点
"elementType": "all",
"stylers": {
"visibility": "off"
}
},
{
"featureType": "manmade",//学院
"elementType": "all",
"stylers": {
"visibility": "off"
}
}
]
});
map.centerAndZoom('南京', 9); // 初始化地图,设置中心点坐标和地图级别
//添加地图类型控件
map.addControl(new BMap.MapTypeControl({
mapTypes:[
BMAP_NORMAL_MAP,
BMAP_HYBRID_MAP
]}));
map.enableScrollWheelZoom(true); //开启鼠标滚轮缩放
map.addEventListener("click", showInfo);
function showInfo(e){
theLocation(e.point.lng , e.point.lat);
}
function theLocation(lon,lat){
map.clearOverlays();
var icon = new BMap.Icon('<%=basePath%>/image/oss/iot/location2.png', new BMap.Size(32, 32), {
anchor: new BMap.Size(32, 32)
});
var mkr = new BMap.Marker(new BMap.Point(lon,lat), {
icon: icon
});
var new_point = new BMap.Point(lon,lat);
map.addOverlay(mkr);
tip("标注成功");
gic("lonLat").val(lon+","+lat);
setTimeout(function() {
layer.close(lt) ;
}, 1000);
}
})
function stringToHex(str){
var val="";
for(var i = 0; i < str.length; i++){
if(val == "")
val = str.charCodeAt(i).toString(16).toUpperCase();
else
val += str.charCodeAt(i).toString(16).toUpperCase();
}
return val;
}
var aindex ;
function generateCode12(obj){
$(".aaa").remove();
try{
if( $(".infos-edit").val() != '' ){
var data = $.parseJSON( $(".infos-edit").val() );
for(var i=0;i< data.length;i++){
$(".infoslist").append( reHtmlVal(data[i].address,data[i].fcode,data[i].saddr,data[i].length ) );
}
}
aindex = openWindow("ModbusRTU批量读配置【可不填】","#template_config","700px");
}catch(a){
}
}
function addInfos(){
var a = $("#address").val();
var b = $("#fcode").val();
var c = $("#startaddress").val();
var d = $("#length").val();
if(a == "" || c=="" || d==""){
tip("请填写完整");
return ;
}
$(".infoslist").append( reHtmlVal(a,b,c,d ) );
$("#address").val('');
$("#fcode").val('');
$("#startaddress").val('');
$("#length").val('');
}
function reHtmlVal(a,b,c,d){
return "<tr class='aaa'><td>"+a+"</td><td>"+b+"</td><td>"+c+"</td><td>"+d+"</td><td> <button onclick='removeinfo(this)' style='color:red;' class='button'>删除</button> </td> </tr>" ;
}
function removeinfo(obj){
$(obj).parent().parent().remove();
}
//设备状态
function statusFunc2(obj){
if(obj == 16 ){
return "<span class='blue-span'>在线</span>" ;
}else if(obj == 17) {
return "<span class='red-span'>已离线</span>" ;
}else if(obj == 18) {
return "<span class='grey-span'>未连接</span>" ;
}
}
function saveInfo(){
var dataall = [] ;
for(var i=0; i < $(".aaa").length ; i++ ){
dataall.push({
address:$(".aaa").eq(i).children().eq(0).html() ,
fcode:$(".aaa").eq(i).children().eq(1).html() ,
saddr:$(".aaa").eq(i).children().eq(2).html() ,
length:$(".aaa").eq(i).children().eq(3).html()
});
}
$(".infos").val(JSON.stringify(dataall));
layer.close(aindex);
}
function nodeTypeChange(obj, default_value){
var s = '' ;
if( default_value != null ){
s+= default_value ;
}
if( $(obj).val() == '83' ){
// TCP
init_select({ 'method':'GET','url':'/dictionary/300','init_value':'请选择设备协议','key':'dictionary_name','value':'name','type':'list','default_value':s } ,$(".nodetypeselect") );
}else if( $(obj).val() == '84' ){
// MQTT
init_select({'method':'GET','url':'/dictionary?ids=305','init_value':'请选择设备协议','key':'dictionary_name','value':'name','type':'list','default_value':s } ,$(".nodetypeselect") );
}else{
$(".nodetypeselect").val('');
}
}
function protocalChange(obj){
if( $(obj).val() == 'ProtocalModbus' ){
$(".infos-container").show();
}else{
$(".infos-container").hide();
}
}
function successbtn(data){
if(data.data.iot_protocal_category == 'ProtocalModbus'){
$(".infos-container").show();
}else{
$(".infos-container").hide();
}
nodeTypeChange( $(".modify_node_type"), data.data.iot_protocal_category ) ;
}
$(function(){
$(document).delegate(".limiter","click",function(){
layer.tips($(this).attr("tag") , this, {
tips: [1, '#4682B4']
});
})
})
</script>
</html>