基于Web SCADA平台构建实时数字化产线 - 初篇

9 篇文章 2 订阅
6 篇文章 2 订阅

       如各位对Web SCADA平台及技术感兴趣,欢迎转发或私信我,大家共同学习,相互交流共同进步; 

       构建数字化产线是近几年国家推出两化融化后、智能制造等相应政策后的产物,传统的信息化、工业化相对独立的走了大几十年,目前迎来了两化的大融合,当然也是技术与行业发展的一个必然,其中组态系统一直存在于上层管理信息化与下层自动化之间,融合前组态(SCADA)系统大多以CS架构出现,其重点是关键生产工艺,大多数的使用场景是在车间中控室内,伴随着移动设备及相应WEB技术的不断成熟,基于Web技术的SCADA系统得到了快速发展,通过WEB SCADA平台能够实现的场景因其所在的企业内的位置就决定了其应用的广泛性,本篇重点关注如何通过WEB SCADA平台构建数字化产线,如下:

       当前我们讨论的数字化产线只是单纯意义的底层数据采集并通过采集后的数据驱动3D模型所实现的单向数字化产线,重点在展示;

       第一步:
       目前通用的做法是通过3D建模软件对产线上关键性的设备进行三维建模,这个过程如果需要将效果做的更加的真实,需要配合动画效果,将关键设备及所采集的数据与设备部件加以关联,制作出对应的动画效果,最终导出带有动画的GLB或GLTF格式的文件;

   第二步 :

   将GLB文件通过ThreeJS的GLTFLoader JS包进行引入,如下:

  <!--引入three.js三维引擎-->
  <script src="./scripts/three.js"></script>
  <!-- 引入global加载器 -->
  <script src="./scripts/GLTFLoader.js"></script>
  <!--引入轨道控件OrbitControls.js-->
  <script src="./scripts/OrbitControls.js"></script>
  <script type='text/javascript'src='system/scripts/igrX2.js'></script>

  loader.load('./glb/labeling_machine5.glb', function (glb) {
        glb.scene.position.set( 0,0,0)
        glb.scene.add(moonMassLabel);	
		scene.add(glb.scene);
   }, undefined, function ( error ) {
	   console.error( error );
   } );

第三步:

       将引入后的GLB动画文件,通过ThreeJS进行动画控制,如下:


mixer = new THREE.AnimationMixer( model );
console.log('mixer' + mixer);
idleAction = mixer.clipAction( animations[ 0 ] );
walkAction = mixer.clipAction( animations[ 3 ] );
runAction = mixer.clipAction( animations[ 1 ] );
actions = [ idleAction, walkAction, runAction ];

//控制其动画的播放
actions.forEach( function ( action ) {
   action.play();
} );

第四步:

     比较关键的一步是需要将底层采集到的数据与ThreeJS代码进行整合,实现整体的效果,IGX Web SCADA 平台已经将底层采集的数据封装为JS包,IGX Web SCADA附带了一些额外的专有功能或方法来简化编程工作,主要用于与服务器通信的脚本。setTag('TagName',Data)用于设置Tag 值,其中TagName 是字符串中的标签,Data 是保存值以设置到特定Tag 中的变量。getTag('TagName')用于获取Tag 值,它返回相关数据类型中的值。sleep(DELAY)用于保持脚本执行指定的时间,DELAY 以毫秒为单位。setTimeout()和setInterval()在大多数情况下可能无法在服务器端工作。所以此处通过getTag('TagName') 获取到设备部件相应的数据,通过变量与JS脚本进行融合使其插入动画; 

如下:

//控制其动画的播放
if (getTag('Tag') == 1)
{
    actions.forEach( function ( action ) {
       action.play();
    } );
}

关于对应设备数据标签的实时显示,通过引入2D文本渲染JS包,如下:

import { CSS2DRenderer, CSS2DObject } from './jsm/renderers/CSS2DRenderer.js';

并与IGX Web SCADA提供的igrX2.js中的getTag('tagName')相结合,实现定时的数据获取与展示,如下:

function setVal1(){
		var t;
		var para = getTag('part_a');
        t = setTimeout(function(){setVal1()},2000);

        document.getElementById("label_id").innerHTML= "</br><strong>一线灌装机</strong></br>" + "</br>实际速度(B/H):&nbsp;&nbsp;<font color='#87CEEB'>" + getTag('part_a').toFixed(2) + '</font></br></br>' + 
				"环缸压力(Bar):&nbsp;&nbsp; <font color='#FF4500'>" + getTag('part_b').toFixed(2) + '</font></br></br>' + 
				'操作小时数(H):&nbsp;&nbsp;' + getTag('part_c').toFixed(2) + '</br></br>' + 
				"供料温度(℃):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color='#EEE8AA'>" + getTag('part_c').toFixed(2) + '</font></br></br>' +
				"调节阀开度(%):&nbsp;&nbsp;&nbsp;&nbsp;<font color='#EEE8AA'>" + getTag('app.currentTime.second').toFixed(2)+ '</font></br></br>';
}

初步的实现效果如下:

 当前属于一条产线的一台设备的加载与展示,其细节仍需要地进行相应的优化,多台设备的加载方式与当前设备一致,动画环节我们在随后几篇文章中进行展示,请持续关注; 

整体代码发布如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
  <link rel="stylesheet" type="text/css" href="system/styles/igrX.css" />
  <title>pb展示</title>
  <!--引入three.js三维引擎-->
  <script src="./scripts/three.js"></script>
  <!-- 引入global加载器 -->
  <script src="./scripts/GLTFLoader.js"></script>
  <!--引入轨道控件OrbitControls.js-->
  <script src="./scripts/OrbitControls.js"></script>

	<style>
		.label {
			color: #FFF;
			font-family: sans-serif;
			padding: 2px;
			background: rgba( 0, 0, 0, .6 );
		}
	</style>
  
</head>
<body>
  <div id="junying"></div>
  

	<!-- Import maps polyfill -->
	<!-- Remove this when import maps will be widely supported -->
	<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>

	<script type="importmap">
		{
			"imports": {
				"three": "./build/three.module.js"
			}
		}
	</script>
	
		<!-- 加载模型文件 -->
	<!-- <script src="./scripts/3dmodel_KRONES.js"></script> -->
	
	<script type="module">
		
		import { OrbitControls } from './jsm/controls/OrbitControls.js';
		import { CSS2DRenderer, CSS2DObject } from './jsm/renderers/CSS2DRenderer.js';
		import { GUI } from './jsm/libs/lil-gui.module.min.js';
		
			let gui;
			let camera, scene, renderer, labelRenderer;
			
			let junying = document.getElementById("junying");
			
			const layers = {
				'Toggle Name': function () {
					camera.layers.toggle( 0 );
				},
				'Toggle Mass': function () {
					camera.layers.toggle( 1 );
				},
				'Enable All': function () {
					camera.layers.enableAll();
				},
				'Disable All': function () {
					camera.layers.disableAll();
				}
			}

			const clock = new THREE.Clock();
			const textureLoader = new THREE.TextureLoader();

			let moon;

			init();
			animate();
			setVal1();	

			function init() {
				const EARTH_RADIUS = 1;
				const MOON_RADIUS = 1.27;

				camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 1000);
				camera.position.set( 100,40,120 );
				camera.layers.enableAll();
				//camera.layers.toggle( 0.1 );
				scene = new THREE.Scene();
				
				scene.background = new THREE.Color( 0xb0b0b0 );
				const hemiLight = new THREE.HemisphereLight( 0xffffff, 0x444444 );
				hemiLight.position.set( 0, 200, 0 );
				scene.add( hemiLight );

				const directionalLight = new THREE.DirectionalLight( 0xffffff );
				directionalLight.position.set( 0, 200, 100 );
				directionalLight.castShadow = true;
				directionalLight.shadow.camera.top = 180;
				directionalLight.shadow.camera.bottom = - 100;
				directionalLight.shadow.camera.left = - 120;
				directionalLight.shadow.camera.right = 120;
				scene.add( directionalLight );

				const ground = new THREE.Mesh( new THREE.PlaneGeometry( 2000, 2000 ), new THREE.MeshPhongMaterial( { color: 0x999999, depthWrite: false } ) );
				ground.rotation.x = - Math.PI / 2;
				ground.receiveShadow = true;
				scene.add( ground );

				const grid = new THREE.GridHelper( 2000, 20, 0x000000, 0x000000 );
				grid.material.opacity = 0.2;
				grid.material.transparent = true;
				scene.add( grid );

				// 加载模型
				var loader = new THREE.GLTFLoader();
				
				const moonMassDiv = document.createElement( 'div' );
				moonMassDiv.className = 'label';
				moonMassDiv.id="label_id";
				moonMassDiv.textContent = '7.342e22 kg';
				moonMassDiv.style.marginTop = '2px';
				moonMassDiv.style.width='154px';
				moonMassDiv.style.height='167px';
				moonMassDiv.style.border = '1px solid #000';
				const moonMassLabel = new CSS2DObject( moonMassDiv );
				moonMassLabel.position.set( 10, 2 * 22, 0 );
				//glb.add( moonMassLabel );
				moonMassLabel.layers.set( 1 );
				
				loader.load('./glb/labeling_machine5.glb', function (glb) {
					glb.scene.position.set( 0,0,0)
					glb.scene.add(moonMassLabel);	
					scene.add(glb.scene);

				}, undefined, function ( error ) {
					console.error( error );
				} );

				//
				renderer = new THREE.WebGLRenderer();
				renderer.setPixelRatio( window.devicePixelRatio );
				renderer.setSize( window.innerWidth, window.innerHeight );
				document.body.appendChild( renderer.domElement );

				labelRenderer = new CSS2DRenderer();
				labelRenderer.setSize( window.innerWidth, window.innerHeight );
				labelRenderer.domElement.style.position = 'absolute';
				labelRenderer.domElement.style.top = '0px';
				document.body.appendChild( labelRenderer.domElement );

				const controls = new OrbitControls( camera, labelRenderer.domElement );
				controls.minDistance = 0;
				controls.maxDistance = Infinity;

				//
				window.addEventListener( 'resize', onWindowResize );
				initGui();
			}

			function onWindowResize() {
				camera.aspect = window.innerWidth / window.innerHeight;
				camera.updateProjectionMatrix();
				renderer.setSize( window.innerWidth, window.innerHeight );
				labelRenderer.setSize( window.innerWidth, window.innerHeight );
			}

			function animate() {
				requestAnimationFrame( animate );
				const elapsed = clock.getElapsedTime();
				renderer.render( scene, camera );
				labelRenderer.render( scene, camera );
			}

			function initGui() {
				gui = new GUI();

				gui.add( layers, 'Toggle Name' );
				gui.add( layers, 'Toggle Mass' );
				gui.add( layers, 'Enable All' );
				gui.add( layers, 'Disable All' );
			}

			function setVal1(){
				var t;
				var para = getTag('part_a');
        		t = setTimeout(function(){setVal1()},2000);

				document.getElementById("label_id").innerHTML= "</br><strong>一线灌装机</strong></br>" + 
				"</br>实际速度(B/H):&nbsp;&nbsp;<font color='#87CEEB'>" + getTag('part_a').toFixed(2) + '</font></br></br>' + 
				"环缸压力(Bar):&nbsp;&nbsp; <font color='#FF4500'>" + getTag('part_b').toFixed(2) + '</font></br></br>' + 
				'操作小时数(H):&nbsp;&nbsp;' + getTag('part_c').toFixed(2) + '</br></br>' + 
				"供料温度(℃):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color='#EEE8AA'>" + getTag('part_c').toFixed(2) + '</font></br></br>' +
				"调节阀开度(%):&nbsp;&nbsp;&nbsp;&nbsp;<font color='#EEE8AA'>" + getTag('app.currentTime.second').toFixed(2)+ '</font></br></br>';
			}
		</script>
	</script>
  
  <style>
    body { margin: 0;overflow: hidden;}
  </style>
</body>
</html>

<script type='text/javascript'src='system/scripts/igrX2.js'></script>

       如各位对Web SCADA平台及技术感兴趣,欢迎转发或私信我,大家共同学习,相互交流共同进步; 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WebScada

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值