preface
The development of the Industrial Internet of Things in China is in full swing. The construction of network infrastructure and the urgent need for industrial upgrading provide great opportunities for the development of the industrial Internet of Things. At present, There are two forms of development in China’s industrial Internet of Things enterprises: one is the layout of large communication and IT enterprises; On the one hand, traditional industrial software and industrial network enterprises spontaneously extend from product provider to solution provider. What is a skirt house? A podium is a low-rise building attached to and integrated with the main building. The Demo of this article is aimed at the skirt house, but there are many similar parts in the industrial monitoring system, such as animation, click switch, click hide, fault display, switch, data display and so on, are some of the more common functions. Therefore, I will make a record of these contents in this Demo. I also met some problems in this Demo, and I will share with you how to solve them.
http://www.hightopo.com/demo/annexMonitor/
Code implementation
We will implement the following parts of the Demo:
- 3D scene construction
- The rotation of impeller in the scene
- The rise and fall of the water level of a container
- Impeller rotation fault display
- Turn animation on/off
- Light on/off
- Click to switch model
- Click hide/Show properties window
- Text content/color transformations look a lot, but they’re actually quite easy to implement, with 200+ lines of code.
3D scene construction
2 lines of code for building the foundation of 3d scene:
var g3d = new ht.graph3d.Graph3dView(); // Hightopo 3D component (3D scene foundation) g3d.addtodom (); // Add the 3D group to the bodyCopy the code
HT components are generally embedded BorderPane (https://hightopo.com/guide/guide/core/borderpane/ht-borderpane-guide.html), SplitView (https://hightopo.com/gui DE/guide/core/splitview/ht – splitview – guide. HTML) and TabView (https://hightopo.com/guide/guide/core/tabview/ht-tabview-guide.html), and other containers in use, The outermost HT component requires the user to manually add the underlying div element returned by getView() to the DOM element of the page. It is important to note that when the parent container size changes, if the parent container is BorderPane, SplitView and other predefined HT container components, The HT container will automatically recursively call the invalidate function of the child component to notify the update. However, if the parent container is a native HTML element, the HT component cannot know that it needs to be updated. Therefore, the outermost HT component usually needs to listen for the window size change event and call the invalidate function of the outermost component to update. For ease of loading and filling Windows for the outermost components, all components of HT have addToDOM functions that implement the following logic, where iv is short for invalidate:
addToDOM = function(){var self = this, view = self.getView(),// get the component's bottom div style = view.style; document.body.appendChild(view); // Add the component's underlying div to the body. Left ='0'; // Ht sets position for all components to absolute. Right ='0';
style.top = '0';
style.bottom = '0';
window.addEventListener('resize'.function () { self.iv(); }, false); // Window size change event, call refresh function}Copy the code
Next we need to add various models to the scene. Generating the model in code is extremely painful. We put the entire scene model into a JSON file and use the HT.default. xhrLoad method to convert the JSON to 3D scene and display it on the interface:
var dm = g3d.dm(); Ht.default.xhrload (hT.default.xhrLoad (hT.default.xhrLoad))'scenes/system.json'.function(text) { dm.deserialize(text); // Pass the function's text (json) argument to the deserialize deserialize method, which adds elements from the JSON content to the dataModel data container for display}Copy the code
The ht.default. xhrLoad method is an asynchronous method to load JSON files. The first parameter is the incoming JSON file, and the path is relative to the HTML file. The second parameter is the callback function, which does the operation after the incoming JSON file is parsed. This method is asynchronous loading, so if you need to obtain or operate data in the DM data container, you need to write the code of obtaining/operation after dm.deserialize(text) method, because there are nodes in the DM data container at this time.
After serializing the JSON file to the DM data container, the interface appears as follows:
The background of the whole scene in the figure above was added by code in the later stage. From the previous addToDOM function, we can know that we can get the underlying DIV of HT 3D component through getView method, so it is not difficult to add a background image on this div. The rest of the 3D model is deserialized from JSON.
The rotation of impeller in the scene
Of course, it is not the whole model that rotates, but the “wheel” in the middle that rotates. This requires the designer to separate this part out when creating the model. Then I give this part a tag with a unique identifier of “Yelun”, which can be obtained through dmp.getDatabyTag (‘ Yelun ‘). Then animate the node with rotation.
HT scheduling in the process is, first through the DataModel (https://hightopo.com/guide/guide/core/datamodel/ht-datamodel-guide.html) to add scheduling tasks, DataModel will traverse all DataModel primions callback the action function of the scheduling task when the time interval specified by the scheduling task arrives. In this function, corresponding property modification can be made to the incoming Data primions to achieve animation effect.
According to the above description of scheduling tasks, we know that adding scheduling tasks to THE DM data container will traverse the whole data container, which may not be felt when there is little content in the data container, but when there is much content in the data container and the model is heavy, it is necessary to filter the DM data container. Moreover, if multiple scheduling tasks are added to traverse the entire data container, the performance requirements on the computer can be imagined. At the beginning, I omitted the filtering of THE DM data container. Because the scene was not large, I didn’t feel it at first. Later, after the lighting was added, it was very heavy, and there was a problem immediately.
Therefore, the action method passes a data value in the parameter object passed by the scheduling task, which is used to set the object of the current animation. If it is not this object, it can return it without doing anything:
var task = [];
var yelun = dm.getDataByTag('yelun'); Task. yelunTask = {interval: 100,// Animation duration action:function(data) {// Animation contentif(data ! == yelun)return; // Set the X-axis rotation of the Yelun node to the current X-axis rotation value plus math.pi /12 Yelun.setrotationx (Yelun.getrotationx () + math.pi /12); } } dm.addScheduleTask(task.yelunTask); // Add the scheduling task to the data containerCopy the code
The rise and fall of the water level of a container
The action of the yelunTask scheduling task in the previous section should be changed. The action of the yelunTask scheduling task in the previous section should be changed. Since the above code only operates on the Yelun node, we need to operate on the water container as well. Firstly, get the container containing water, and set the unique tag of this node as “cylinder” :
var cylinder = dm.getDataByTag('cylinder'); Then change the action section of the schedule: action:function(data) {
if(! (data === yelun || data === cylinder))return; // Yelun.setrotationx (yelun.getrotationx () + math.pi /12); // The container level changesif(cylinder.getTall() === 100) { cylinder.setTall(0); // When the container water level reaches the value of 100, reset to 0}else cylinder.setTall(cylinder.getTall() + 1);
}
Copy the code
Impeller rotation fault display
Since there was no data transmission, I had to fabricate the data for the fault information. I created a random integer number within 10 to determine whether the value was 1. If it was 1, THE normal operation icon would be transformed into an alarm icon. At the same time, I also set the dm data container to add/remove scheduling tasks to control the current impeller rotation/stop and whether the water level of the container changes:
var alarm = dm.getDataByTag('alarm'); // Obtain the alarm icon nodesetInterval(function() {
var random = Math.floor(Math.random()*5);
if (random === 1) {
alarm.s('shape3d.image'.'Symbols/telecom/fault 2.json'); Dm.removescheduletask (task.yeluntask); // Set the alarm icon to fault. // Add the animation of the impeller to}else {
alarm.s('shape3d.image'.'Symbols/telecom/normal 2.json'); Dm.addscheduletask (task.yeluntask); // Set the alarm icon to Normal. }}, 1000);Copy the code
Turn animation on/off
We already mentioned how to turn animation on/off in the previous section. Here we use the form form to manually turn animation on and off (note: only the “flow switch” in the first line is described here).
First, we need to create a formPane form components (https://hightopo.com/guide/guide/plugin/form/ht-form-guide.html), add rows of data in the form components, Here I use the checkbox to open and close the animation. The value changes only true and false. This is a better choice in this case. Then set the animation on (add) or off (remove) by listening for the checkbox change event.
functioncreateForm(task) { var form = new ht.widget.FormPane(); // Create the form component object form.setwidth (160); // Set the width of the form component form.setheight (90); // Set the height of the form component // Set the style property of the underlying div of the form component form.getView().style.right ='10px';
form.getView().style.top = '10px';
form.getView().style.background = 'rgba (255, 255, 255, 0.2)';
form.getView().style.borderRadius = '5px'; document.body.appendChild(form.getView()); Form. addRow([// add a line of data to the form {checkBox: {// CheckBox class, HT encapsulates this in the form to actually create an ht.widget.CheckBox component label:'Flow switch'// Set the checkbox text content labelColor:'#fff'// Set the checkbox text color to selected:true,// Sets whether onValueChanged is checked for this checkbox:function() {// listen for value change eventsif(this.isSelected()) dm.addScheduleTask(task.arrowTask); // If the checkBox is checked, add animation (open water valve)elsedm.removeScheduleTask(task.arrowTask); // If the checkBox is not checked, remove the animation (close the water valve)}}}], [0.1]); // Set the width of the column in the row datareturn form;
}
Copy the code
AddRow method in the above code is not clear, please refer to the following instructions:
AddRow (Items, Widths, height, params) adds a row of components
- Items are an array of elements, which can be strings, component parameter information described in JSON format, HTML elements, or null
- Widths is an array of information about the width of each element. A width value greater than 1 represents a fixed absolute value, a width value less than or equal to 1 represents a relative value, or a combination of 80+0.3
- Height refers to the information of row height. The value greater than 1 represents the fixed absolute value, while the value less than or equal to 1 represents the relative value. It can also be a combination of 80+0.3
- Params are additional arguments in JSON format, such as inserting row indexes and row borders or background colors, such as {index: 2, background: ‘yellow’, borderColor: ‘red’} The arrowTask mentioned in the above code is the animation scheduling task added to the “arrow” flow in the scene. You can directly operate whether the DM adds/removes the animation scheduling task by controlling whether the checkbox in the form is checked.
Light on/off
Controls lighting on and off, again via the checkbox on the form. It is generally recommended not to use lighting, rendering is too inefficient, here is just for effect to add a note.
First we need to create a “lamp” node, and then set the lamp type, color, lamp range, and so on by setting the style property setStyle:
Var light = new ht.light (); / / create a Lantern Festival point (inherited from ht. Node) (https://hightopo.com/guide/guide/core/lighting/ht-lighting-guide.html) light. P3 ([15, 120, 50]); // Set the location of this object light.settag ('light'); Dm.add (light); // Add this node to the DM data container to display light.s({// set the style properties of this nodesetStyle 简写为 s
'light.type': 'point'// Set the lamp type'light.color': 'RGB (252252149).// Set the lamp color'light.range': 1400,// Set the lamp range'3d.visible': false// Make this node invisible on 3D});Copy the code
Then add a line to the form to control the light switch and the light color function:
// form. AddRow ([// add a line {id:'lightDisabled'Form. getItemById checkBox: {// checkBox component label:'Lights on and lights off',// Set the checkbox text content labelColor:'#fff',// Set the text color of the checkbox to selected:true,// Set the checkbox to check whether onValueChanged is selected:function() {// Listen for the value change event dm.getDatabyTag ('light').s('light.disabled',! this.getValue()); // Get the light point and set whether to turn off the light effect. The light. Disabled property defaults tofalse, can be set totrue}}}, {colorPicker: {// colorPicker component value:'RGB (252252149).,// set the current value instant:true,// If the model is in the live state, change the model value onValueChanged in real time:function() {// Listen for the value change event dm.getDatabyTag ('light').s('light.color', enclosing the getValue ()) / / set the color of the lamp to the currently selected color}}}], [0.1, 0.1]);Copy the code
Click to switch model
HT will be listening to the packaging to mi events (https://hightopo.com/guide/guide/core/3d/ht-3d-guide.html#ref_interactionlistener), mi method has a variety of events, Here we need to listen for the clickData event by checking whether the event type e.Kinind is clickData and then setting the model for the node:
var waterPump6 = dm.getDataByTag('pump 06'); // Get waterpump6.s ({set the style attribute for this node'note': 'Click me toggle model'// Set the text content of the annotation'note.transparent': true// Set the annotation to be transparent in 3D'note.t3': [0, 0, -50],// Set the annotation offset in 3D'note.reverse.flip': true// set whether the back of the annotation shows the positive content}); g3d.mi(function(e) {// Listen for events on 3D componentsif(e.kind === 'clickData') {// Click node event // Model click toggleif (e.data === waterPump6 && e.data.s('shape3d') = = ='Models/Pump. json') e.data.s('shape3d'.'models/fengji.json'); // Set the shape3D style property of the click nodeelse if (e.data === waterPump6 && e.data.s('shape3d') = = ='models/fengji.json') e.data.s('shape3d'.'Models/Pump. json'); // Set the shape3D style property of the click node}});Copy the code
The HT setting model is implemented by setting the node’s style property Node.setStyle (abbreviated node.s) to shape3D.
Click hide/Show properties window
If (e.type === ‘clickData’) {if (e.type === ‘clickData’) {if (e.type === ‘clickData’);
var waterPump5 = dm.getDataByTag('pump 05');
waterPump6.s({
'note': 'Click me toggle model'.'note.transparent': true.'note.t3': [0, 0, - 50].'note.reverse.flip': true
});
g3d.mi(function(e) {
if(e.kind === 'clickData') {// Model click toggleif (e.data === waterPump6 && e.data.s('shape3d') = = ='Models/Pump. json') e.data.s('shape3d'.'models/fengji.json');
else if (e.data === waterPump6 && e.data.s('shape3d') = = ='models/fengji.json') e.data.s('shape3d'.'Models/Pump. json'); // Model click to hide/show properties windowif(e.data === waterPump5) {// Check whether the pixel clicked is waterPump5if(giveWater.s('3d.visible'GiveWater. S () {// Check whether the current property window is displayed.'3d.visible'.false); // Make the properties window invisible e.data.s('note'.'Click I show properties window'); // Change the display in the annotation}else {
giveWater.s('3d.visible'.true); // Set the properties window to see e.data.s('note'.'Click I hide properties window')// Change the content displayed in the annotation}}}});Copy the code
Text content/color transformation
Obtain the node of the corresponding property window in the scene through tag. This node is a panel, equivalent to a hexahedron with six sides, and this node type has only one side. And by setting the attribute shape3d. Image set of pictures on this node to tooltips. Json vector icon (https://hightopo.com/guide/guide/core/vector/ht-vector-guide.html). Vector is short for vector graphics in Hightopo (HT). Raster bitmaps, such as PNG and JPG, describe graphics by storing the color information of each pixel. Images in this way will appear blurred graphics, thick lines and jagged lines when they are stretched and enlarged or reduced. Vector images, on the other hand, describe images with points, lines, and polygons, so they can be zoomed in and out with consistent accuracy. HT vector graphics also has a very important feature, that is, it can bind any part of the vector graphics. That is to say, we can draw only one of the five graphs in the figure above, and change the text and numerical content on the graph through data binding.
Data binding in vector ICONS can be used for production kanban in industry, data display in large screens, and so on, all in an efficient way to integrate products.
Vector graphics data binding can write an article to elaborate, here will not mention, we go to the official website to view the “vector manual” and “data binding manual”, the explanation is more detailed.
After obtaining the corresponding node, Through the node. A method can get and set the attribute of data binding (https://hightopo.com/guide/guide/core/databinding/ht-databinding-guide.html#ref_vector), Here we bind the text content “label” and the value “value” and the value color “valueColor” :
var billboardArray = []; Var temperature1 = dM.getDatabyTag ('Return water temperature 1'); // Get the tag as"Return water temperature 1"The node billboardArray. Push (temperature1); var temperature2 = dm.getDataByTag('Return water temperature 2');
billboardArray.push(temperature2);
var returnPress = dm.getDataByTag('Backwater pressure');
billboardArray.push(returnPress);
var givePress = dm.getDataByTag('Water supply pressure');
billboardArray.push(givePress);
var giveTemp = dm.getDataByTag('Water supply temperature');
billboardArray.push(giveTemp);
var giveWater = dm.getDataByTag('Water flow'); billboardArray.push(giveWater); // BillboardarRay.foreach (function(billboard) {
billboard.a('label', billboard.getTag()); // Set the data binding attribute to label to the current node tag content}); // Text label number transform + color transform Changes the value of the bound icon propertysetInterval(function() {
billboardArray.forEach(function(billboard) {
var random = Math.random()*100;
billboard.a('value', random.toFixed(2)); // Set "value content color" in the iconif (random > 70 && random <= 80) billboard.a('valueColor'.'#00FFFF');
else if (random > 80 && random <= 90) billboard.a('valueColor'.'#FFA000');
else if (random > 90) billboard.a('valueColor'.'#FF0000');
else billboard.a('valueColor'.' ');
});
}, 1000);
Copy the code