ThingJS智慧粮仓源码分享

ThingJS_用户支持 5月前 755

如何使用thingjs制作智慧粮仓可视化项目?物联网可视化项目如何制作?制作好的物联网场景该怎么去开发?

在制作项目的时候,能不能做是一个问题,怎么去做又是一个问题,交付项目的时候能否让用户满意?能否在规定时间保质保量的交付项目,而三维可视化项目的制作,相比于普通项目来说更让人头大,它要有场景模型,还得有一定的交互功能,比如摄像机视角移动或者是跟随某个物体移动,同时还得采集数据,还得使用数据采集器(传感器),收集到数据并上传到数据库中,然后3D开发人员还得获取到这个数据,并且将有需要的数据处理成可以看见的图表或者是云图等等,总体来说,一个完整需求的三维可视化开发难度还是比较大的,这里,分享一个智慧粮仓的可视化解决方案。

本次要分享的ThingJS公开的可视化解决方案是“智慧粮仓”。

如何制作这个解决方案的呢?

第一步:搭建场景;

第二步:开发场景;

第三步:数据对接;

第四步:发布项目;


场景的搭建,通过3Dmax搭建相关特殊模型,通过3dmax上传插件上传到ThingJS的campusbuilder中,通过thingjs的campusbuilder园区搭建工具制作粮仓场景;


开发场景,第一点就是控制摄像机的视角,thingjs的视角也是camera摄像机来控制的,要开发项目,首先就是学会使用camera,然后才是各种功能,智慧粮仓中有着通栏,里面含有编号、温湿度、云图、能耗、视频、人车定位这几个功能,这些功能均在thingjs在线开发左侧的官方示例。


本次分享的智慧粮仓可视化解决方案中的数据都是写死的数据,可自行使用JavaScript语法,通过thingjs官方示例中的数据对接方式,获取数据并且放入到对于图表中。以下是四种数据对接中的ajax对接方式。

/**
* 说明:
* 通过CORS解决跨域问题
* 并将数据挂接到物体(car01)身上,与panel面板进行数据绑定
* 当温度>25℃时 car01变红
* 教程:ThingJS教程——>数据对接——>Ajax对接
* 难度:★★☆☆☆
*/
var app = new THING.App({
    url: 'https://www.thingjs.com/static/models/storehouse'
});

// 定时器
var timer;

app.on('load', function () {
    var car = app.query('car01')[0];

    // 物体身上创建monitorData对象 用于存储动态监控数据
    car.monitorData = {
        '温度': ''
    };

    updateData(car);
    createPanel(car);
})

/** ******************* 以下为ajax数据对接 ********************/
// 服务器程序端 通过设置 Access-Control-Allow-Origin 解决跨域问题
// 更多关于 CORS "跨域资源共享"(Cross-origin resource sharing)的技术细节 请自行搜索
// 请求传入参数为 { "id": id }
// 服务器返回的数据格式为 {"state":"success","data":{"id":"4967","temper":"15℃","humi":"59%","power":"20kWh"}}

function updateData(obj) {
    // 如果网站是 https 接口则对应 https 请求
    // 如果网站是 http 接口则对应 http 请求即可
    $.ajax({
        type: "get",
        url: "https://3dmmd.cn/getMonitorDataById",
        data: { "id": obj.id },
        dataType: "json", // 返回的数据类型 json
        success: function (d) {
            console.log(d);
            var temper = d.data.temper;

            // 设置物体身上的监控数据
            obj.setAttribute("monitorData/温度", temper);

            changeColor(obj);

            // 每隔3s 请求一次数据
            timer = setTimeout(function () {
                updateData(obj)
            }, 3000);
        }
    });
}

// 停止请求数据
function stopUpdate() {
    clearTimeout(timer);
}

function createPanel(obj) {
    // 界面组件
    var panel = new THING.widget.Panel({
        titleText: 'car01温度',
        hasTitle: true
    });

    var monitorControl = panel.addBoolean({ 'isOpen': true }, "isOpen").caption("监控开关");
    // 将物体的monitor对象中的数据 与 panel 进行绑定
    panel.add(obj.monitorData, '温度').name('温度');

    monitorControl.on('change', function (ev) {
        if (ev) {
            updateData(obj);
        }
        else {
            stopUpdate();
        }
    })
}

// 如果温度>25 改变颜色
function changeColor(obj) {
    var temper = obj.getAttribute("monitorData/温度");
    var value = temper.substr(0, temper.indexOf("℃"));
    if (value > 25) {
        obj.style.color = 'rgb(255,0,0)';
    }
    else {
        obj.style.color = null;
    }
}



开发场景需要通过thingjs的app对象引入我们搭建的园区场景:

//-----------------------------------------------------------------------------
// 应用入口
var toolBarState = true;
var startFps = false;
var fpsControl = null;

var app = new THING.App({
   container: "div3d",
   skyBox: 'BlueSky',
   url: "models/silohouse",
   ak: "app_test_key",
   sucess: function () {
       app.camera.position = [98.5, 176.3, 218.5];
       app.camera.target = [19.7, -47.8, -22.5];
   }
});


其余的就是添加页面元素以及通过URL的方式将模型加载到在线开发中,制作出对应的tab表格,完整的智慧粮仓代码如下:

document.title = 'Demo-粮仓管理';
/**
 * 仓库封装类
 */
function SiloHouse(obj) {
    this.name = obj.name;
    this.obj = obj;
    obj.siloHouse = this;
    this.height = obj.size[1];
    this.roof = obj.query('/gaizi')[0];
    this.roof.initPos = this.roof.position; // 保存盖子的初始位置
    this.temper = this.humi = this.power = this.store = "";
    this.info = null;
    this.heatMap = null;
    this.panel = null;
    this.ui = null;
    this.setupEvents();
    this.simulateData();
}
// 几个粮仓的静态变量
SiloHouse.current = null;       // 正在选中的粮仓
SiloHouse.currentOpen = null;   // 正在打开的粮仓
SiloHouse.summeryPanel = null;  // 注意统计信息只有一个面板,是静态变量
// 选择
SiloHouse.prototype.select = function () {
    this.obj.style.outlineColor = 0x0000FF;
    this.showSummery(true);
}
SiloHouse.prototype.unselect = function () {
    this.obj.style.outlineColor = null;
    this.showSummery(false);
}
// 屋顶
SiloHouse.prototype.openRoof = function () {
    this.roof.initPos = this.roof.position; // 保存盖子的初始位置
    var pos = this.obj.upPosition(80);
    this.roof.moveTo({ 'position': pos, 'time': 300 });
}
SiloHouse.prototype.resetRoof = function () {
    var pos = this.roof.initPos;
    this.roof.moveTo({ 'position': pos, 'time': 300 });
    this.destroyHeatmap(); // 关闭房顶要确认云图删除
}
// 事件
SiloHouse.prototype.setupEvents = function (obj) {
    var that = this;
    var obj = this.obj;
    // 单击
    obj.on('singleclick', function () {
        if (SiloHouse.current)
            SiloHouse.current.unselect();
        SiloHouse.current = that;
        SiloHouse.current.select();
    });
    // 双击
    obj.on('dblclick', function () {
        if (SiloHouse.currentOpen == that)
            return;
        // 取消选中的
        if (SiloHouse.current) {
            SiloHouse.current.unselect();
            SiloHouse.current = null;
        }
        // 取消上一次打开的
        if (SiloHouse.currentOpen)
            SiloHouse.currentOpen.resetRoof();
        SiloHouse.currentOpen = that;
        // 打开屋顶
        that.openRoof();
        // 摄像机跳转
        var obj = SiloHouse.currentOpen.obj;
        app.camera.flyTo({//飞到
            offset: [0, 70, -30],
            target: obj,
            time: 1000,	// 耗时毫秒
            complete: function () {
                if (toolBar.data.cloud == true)
                    SiloHouse.currentOpen.createHeatmap();
            }
        });
    });
}
// 模拟数据
SiloHouse.prototype.simulateData = function (obj) {
    var that = this;
    this.info = {
        "基本信息": {
            "品种": Math.ceil(Math.random() * 2) == 1 ? "小麦" : "玉米",
            "库存数量": Math.ceil(Math.random() * 9000) + "",
            "报关员": Math.ceil(Math.random() * 2) == 1 ? "张三" : "李四",
            "入库时间": Math.ceil(Math.random() * 2) == 1 ? "11:24" : "19:02",
            "用电量": Math.ceil(Math.random() * 100) + "",
            "单仓核算": "无"
        },
        "粮情信息": {
            "仓房温度": Math.ceil(Math.random() * 27 + 25) + "",
            "粮食温度": Math.ceil(Math.random() * 25 + 20) + "",
        },
        "报警信息": {
            "火灾": "无",
            "虫害": "无"
        }
    };
    // 模拟间隔刷新的数据
    var simuTime = Math.ceil(1000 + Math.random() * 1000);
    setInterval(function () {
        that.temper = Math.ceil(20 + Math.random() * 10) + "℃"; // 温度
        that.humi = Math.ceil(30 + Math.random() * 10) + "%"; // 湿度
        that.power = Math.ceil(Math.random() * 20) + "kWh"; // 能耗
    }, simuTime);
}
// 头顶界面
SiloHouse.prototype.createUI = function (width) {
    width = width || 120;
    // 创建widget (动态绑定数据用)
    var panel = new THING.widget.Panel({
        template: 'default',
        cornerType: 's2c5',
        width: width.toString() + "px",
        isClose: false,
        opacity: 0.8,
        media: true
    });
    this.panel = panel;
    // 创建obj ui (跟随物体用)
    var ui = app.create({
        type: 'UI',
        parent: this.obj,
        el: panel.domElement,
        offset: [0, this.height, 0],
        pivot: [0.3, 0]
    });
    this.ui = ui;
    return panel;
}
SiloHouse.prototype.showUI = function (uiName, boolValue) {
    if (this.panel || this.ui)
        this.hideUI();
    if (boolValue) {
        if (uiName == 'number') {
            this.createUI().add(this.obj, 'name').name('编号');
        } else if (uiName == 'temper') {
            this.createUI().add(this, uiName).name('温度');
        } else if (uiName == 'humi') {
            this.createUI().add(this, uiName).name('湿度');
        } else if (uiName == 'power') {
            this.createUI(150).add(this, uiName).name('能耗');
        }
    }
}
SiloHouse.prototype.hideUI = function () {
    if (this.panel) {
        this.panel.destroy();
        this.panel = null;
    }
    if (this.ui) {
        this.ui.destroy();
        this.ui = null;
    }
}
// 云图相关
SiloHouse.prototype.createHeatmap = function () {
    this.heatMap = app.create({
        type: "Heatmap",
        width: this.obj.size[0],
        height: this.obj.size[2],
        minValue: 15,
        maxValue: 45,
        radius: 1.2
    });
    this.heatMap.randomData();
    this.heatMap.position = this.obj.position;
    this.heatMap.moveY(this.height + 1);
    this.heatMap.rotateX(90);
}
SiloHouse.prototype.destroyHeatmap = function () {
    if (!this.heatMap)
        return;
    this.heatMap.destroy();
    this.heatMap = null;
}
// 统计信息 (处理全局唯一一个面板)
SiloHouse.prototype.showSummery = function (boolValue) {
    if (SiloHouse.summeryPanel) {
        SiloHouse.summeryPanel.destroy();
        SiloHouse.summeryPanel = null;
    }
    if (boolValue) {
        SiloHouse.summeryPanel = new THING.widget.Panel({
            template: 'default',
            name: this.name,
            isClose: true,
            isDrag: true,
            isRetract: true,
            hasTitle: true,
            width: "325px",
            media: true
        });
        SiloHouse.summeryPanel.setZIndex(999999);//设置ui排序
        SiloHouse.summeryPanel.addTab(this.info);
        SiloHouse.summeryPanel.setPosition({ left: 300, top: 50 });
    }
}
// ----------------------------------------------------------------------------
// 摄像头封装类
function VideoCamera(obj) {
    this.obj = obj;
    this.videoFrame = null;
    var that = this;
    this.marker = app.create({
        type: "Marker",
        offset: [0, 3.5, 0],
        size: 23,
        url: "https://www.thingjs.com/static/images/sliohouse/videocamera3.png",
        parent: obj
    });
    this.marker.visible = false;
    this.marker.on('click', function () {
        that.showVideoFrame();
    });
}
VideoCamera.prototype.showUI = function (boolValue) {
    this.marker.visible = boolValue;
}
VideoCamera.prototype.showVideoFrame = function () {
    if (this.videoFrame) {
        this.videoFrame.destroy();
        this.videoFrame = null;
    }
    this.videoFrame = new THING.widget.Panel({
        template: 'default',
        name: this.obj.name,
        isClose: true,
        isDrag: true,
        hasTitle: true,
        width: "450px",
        media: true
    });
    var ui2data = { iframe: true };
    var videoUrlList = ["https://gctxyc.liveplay.myqcloud.com/gc/ljgcdyhxgjt_1/index.m3u8?contentid=2820180516001", "http://gcdnc.v.dwion.com/gc/ljgcwglytylxs_1/index.m3u8?contentid=2820180516001", "https://gctxyc.liveplay.myqcloud.com/gc/hswlf_1/index.m3u8?contentid=2820180516001"];//大研花巷观景,万古楼遥望玉龙雪山,黄山卧云峰
    this.videoFrame.addIframe(ui2data, 'iframe').name(" ").iframeUrl('https://www.thingjs.com/demos/player/player.html?url=' + videoUrlList[parseInt((videoUrlList.length) * Math.random())]).setHeight('321px');
    this.videoFrame.setPosition({ left: app.domElement.offsetWidth - this.videoFrame.domElement.offsetWidth - 100, top: 100 });// ui位置默认在 右上角   
    this.videoFrame.setZIndex(999999);
    var that = this;
    this.videoFrame.on('close', function () {
        if (that.videoFrame) {
            that.videoFrame.destroy();
            that.videoFrame = null;
        }
    });
}
// ----------------------------------------------------------------------------
// 卡车封装类
function Truck(obj) {
    this.obj = obj;
    this.info = { "车牌": "京A12345", "公司": "北京优锘科技有限公司", "状态": "出库", "仓房": "1号", "状态": "过磅" };
}
Truck.prototype.createUI = function (width) {
    // 创建widget (动态绑定数据用)
    var panel = new THING.widget.Panel({
        cornerType: 'polyline',
        width: "350px",
        isClose: false,
        opacity: 0.8,
        media: true
    });
    for (var key in this.info)
        panel.add(this.info, key);
    this.panel = panel;
    // 创建obj ui (跟随物体用)
    var ui = app.create({
        type: 'UI',
        parent: this.obj,
        el: panel.domElement,
        offset: [0, this.height, 0],
        pivot: [0, 1.45]
    });
    this.ui = ui;
    return panel;
}
Truck.prototype.showUI = function (boolValue) {
    if (this.ui || this.panel)
        this.hideUI();
    if (boolValue)
        this.createUI();
}
Truck.prototype.hideUI = function (width) {
    this.panel.destroy();
    this.panel = null;
    this.ui.destroy();
    this.ui = null;
}
//-----------------------------------------------------------------------------
// 应用入口
var toolBarState = true;
var startFps = false;
var fpsControl = null;
var app = new THING.App({
    container: "div3d",
    skyBox: 'BlueSky',
    url: "models/silohouse",
    ak: "app_test_key",
    sucess: function () {
        app.camera.position = [98.5, 176.3, 218.5];
        app.camera.target = [19.7, -47.8, -22.5];
    }
});
// 加载完成
app.on('load', function () {
    init();
    init_gui();
});
var siloHouseList = [];
var videoCameraList = [];
var truckList = [];
function init() {
    // 摄像机
    app.camera.flyTo({
        time: 1500,
        position: [-182.16900300883736, 53.24677728392183, 72.21965470775368],
        target: [-68.1412926741533, -18.16319203074775, -23.30416731768694]
    });
    // 设置效果
    app.setPostEffect({
        enable: true,
        antialias: {
            enable: true,
            type: 'FXAA',
        },
        colorAdjustment: {
            enable: true,
            brightness: 0,
            contrast: 1.15,
            exposure: 0,
            gamma: 1,
            saturation: 1.3
        },
        screenSpaceAmbientOcclusion: {
            enable: false
        }
    });
    app.setLighting({
        ambientLight: {
            intensity: 0.4,
            color: '#FFFFFF',
        },
        mainLight: {
            shadow: true,
            intensity: 0.6,
            color: '#FFFFFF',
            alpha: 45,
            beta: 0,
        },
        secondaryLight: {
            shadow: false,
            intensity: 0,
            color: '#FFFFFF',
            alpha: 0,
            beta: 0,
        }
    });
    // 粮仓
    app.query("[物体类型=粮仓]").forEach(function (obj) {
        var siloHouse = new SiloHouse(obj);
        siloHouseList.push(siloHouse);
    });
    // 摄像头
    app.query("[物体类型=摄像头]").forEach(function (obj) {
        videoCameraList.push(new VideoCamera(obj));
    });
    // 卡车 
    create_truck();
    app.query("[物体类型=卡车]").forEach(function (obj) {
        truckList.push(new Truck(obj));
    });
    // ----------------------------------------------------------------------------------
    // 单击 如果没拾取到,则取消上次选择的粮仓
    app.on('singleclick', function (event) {
        if (event.object == null || event.object.attr('物体类型') != '粮仓') {
            if (SiloHouse.current) {
                SiloHouse.current.unselect();
                SiloHouse.current = null;
            }
        }
    });
    // 双击 如果没pick到,则取消上次打开的粮仓 
    app.on('dblclick', function (event) {
        if (event.object == null || event.object.attr('物体类型') != '粮仓') {
            if (SiloHouse.currentOpen) {
                SiloHouse.currentOpen.resetRoof();
                SiloHouse.currentOpen = null;
            }
        }
    });
    // 右键 则取消上次打开的粮仓 
    var mouseDownPos = null;
    app.on('mousedown', function (event) {
        if (event.button == 2)
            mouseDownPos = [event.x, event.y];
    });
    app.on('click', function (event) {
        if (event.button == 2 && Math.getDistance(mouseDownPos, [event.x, event.y]) < 4) { // 小于4像素执行click事件
            if (SiloHouse.currentOpen) {
                SiloHouse.currentOpen.resetRoof();
                SiloHouse.currentOpen = null;
            }
        }
    });
    // 屏蔽鼠标右键系统菜单
    document.body.oncontextmenu = function (evt) {
        evt = evt || event;
        evt.returnValue = false;
        return false;
    };
    // 第一人称
    fpsControl = new THING.WalkControl({
        enableKeyRotate: true, walkSpeed: 0.02,turnSpeed: 0.25, gravity: 29.8, eyeHeight: 1.6,jumpSpeed: 10
    });
}
// ----------------------------------------------------------------------------------
// 定位相关,演示只创建一个卡车
var positionList = [];// 人车定位相关
var truckInfo = { "车牌": "京A12345", "公司": "北京优锘科技有限公司", "状态": "出库", "仓房": "1号", "状态": "过磅" };
var wayPointList = ["L109", "L110", "L104", "L103", "L102", "L108", "L109", "L118", "L119", "L112", "L111", "L117", "L118"];
function create_truck() {
    // 生成path,从场景中物体取得位置
    var path = [];
    for (var i = 0; i < wayPointList.length; i++) {
        var pObj = app.query(wayPointList[i])[0];
        if (!pObj)
            continue;
        path.push(pObj.position);
    }
    // 创建卡车并行走路径
    truck = app.create({
        type: 'Thing',
        name: "truck",
        url: "https://www.thingjs.com/static/models/truck"
    });
    truck.movePath({
        'orientToPath': true,
        'orientToPathDegree': 180,
        'path': path,
        'speed': 20,
        'delayTime': 500,
        'lerp': false,
        'loop': true
    });
    truck.attr('物体类型', '卡车');
}
// ----------------------------------------------------------------------------
// 界面相关
var toolBar = null;
function init_gui() {//ui 初始化
    var baseURL = "https://www.thingjs.com/static/images/sliohouse/";
    toolBar = new THING.widget.Banner({ template: 'default' ,column: 'left'});
    toolBar.data = { number: false, temper: false, humi: false, power: false, store: false, video: false, cloud: false, location: false };
    var img0 = toolBar.addImageBoolean(toolBar.data, 'number').caption('仓库编号').imgUrl(baseURL + 'warehouse_code.png');
    var img1 = toolBar.addImageBoolean(toolBar.data, 'temper').caption('温度检测').imgUrl(baseURL + 'temperature.png');
    var img2 = toolBar.addImageBoolean(toolBar.data, 'humi').caption('湿度检测').imgUrl(baseURL + 'humidity.png');
    var img3 = toolBar.addImageBoolean(toolBar.data, 'power').caption('能耗统计').imgUrl(baseURL + 'statistics.png');
    var img4 = toolBar.addImageBoolean(toolBar.data, 'store').caption('粮食储量').imgUrl(baseURL + 'cereals_reserves.png');
    var img5 = toolBar.addImageBoolean(toolBar.data, 'video').caption('视频监控').imgUrl(baseURL + 'video.png');
    var img6 = toolBar.addImageBoolean(toolBar.data, 'cloud').caption('温度云图').imgUrl(baseURL + 'cloud.png');
    var img7 = toolBar.addImageBoolean(toolBar.data, 'location').caption('人车定位').imgUrl(baseURL + 'orientation.png');
    img0.on('change', function (boolValue) { onChangeImageButton('number', boolValue); });
    img1.on('change', function (boolValue) { onChangeImageButton('temper', boolValue); });
    img2.on('change', function (boolValue) { onChangeImageButton('humi', boolValue); });
    img3.on('change', function (boolValue) { onChangeImageButton('power', boolValue); });
    img4.on('change', function (boolValue) { onChangeImageButton('store', boolValue); });
    img5.on('change', function (boolValue) { onChangeImageButton('video', boolValue); });
    img6.on('change', function (boolValue) { onChangeImageButton('cloud', boolValue); });
    img7.on('change', function (boolValue) { onChangeImageButton('location', boolValue); });
}
// 处理工具条按钮
function onChangeImageButton(key, boolValue) {
    // 更新界面绑定对象,其中排除 云图 和 人车定位
    if (boolValue) {
        for (var elem in toolBar.data) {
            if (elem == "cloud" || elem == "location" || elem == key)
                continue;
            toolBar.data[elem] = false;
        }
    }
    // 分类别处理
    if (key == "cloud") { // 云图
        if (!boolValue) {
            if (SiloHouse.currentOpen)
                SiloHouse.currentOpen.destroyHeatmap();
        } else {
            if (SiloHouse.currentOpen && app.camera.flying == false)
                SiloHouse.currentOpen.createHeatmap();
        }
    } else if (key == "location") { // 人车定位
        truckList.forEach(function (tr) {
            tr.showUI(boolValue);
        });
    } else if (key == "video") { // 视频监控
        videoCameraList.forEach(function (vc) {
            vc.showUI(boolValue);
        });
    } else if (key == "store") { // 储量
        siloHouseList.forEach(function (siloHouse) {
            siloHouse.hideUI();
            siloHouse.obj.visible = !boolValue;
        });
    } else { // 其他粮仓UI显示
        siloHouseList.forEach(function (siloHouse) {
            siloHouse.showUI(key, boolValue);
        });
    }
}
function changeFPS(start) {
    if (start) {
        app.addControl(fpsControl);
    } else {
        app.removeControl(fpsControl);
    }
}


最新回复 (0)

你可以在 登录 or 注册 后,对此帖发表评论!

返回