11287 </xsl:text> |
11316 </xsl:text> |
11288 <xsl:text>var has_watchdog = window.location.hash == "#watchdog"; |
11317 <xsl:text>var has_watchdog = window.location.hash == "#watchdog"; |
11289 </xsl:text> |
11318 </xsl:text> |
11290 <xsl:text> |
11319 <xsl:text> |
11291 </xsl:text> |
11320 </xsl:text> |
11292 <xsl:text>var ws_url = |
11321 <xsl:text>const dvgetters = { |
|
11322 </xsl:text> |
|
11323 <xsl:text> INT: (dv,offset) => [dv.getInt16(offset, true), 2], |
|
11324 </xsl:text> |
|
11325 <xsl:text> BOOL: (dv,offset) => [dv.getInt8(offset, true), 1], |
|
11326 </xsl:text> |
|
11327 <xsl:text> NODE: (dv,offset) => [dv.getInt8(offset, true), 1], |
|
11328 </xsl:text> |
|
11329 <xsl:text> REAL: (dv,offset) => [dv.getFloat32(offset, true), 4], |
|
11330 </xsl:text> |
|
11331 <xsl:text> STRING: (dv, offset) => { |
|
11332 </xsl:text> |
|
11333 <xsl:text> const size = dv.getInt8(offset); |
|
11334 </xsl:text> |
|
11335 <xsl:text> return [ |
|
11336 </xsl:text> |
|
11337 <xsl:text> String.fromCharCode.apply(null, new Uint8Array( |
|
11338 </xsl:text> |
|
11339 <xsl:text> dv.buffer, /* original buffer */ |
|
11340 </xsl:text> |
|
11341 <xsl:text> offset + 1, /* string starts after size*/ |
|
11342 </xsl:text> |
|
11343 <xsl:text> size /* size of string */ |
|
11344 </xsl:text> |
|
11345 <xsl:text> )), size + 1]; /* total increment */ |
|
11346 </xsl:text> |
|
11347 <xsl:text> } |
|
11348 </xsl:text> |
|
11349 <xsl:text>}; |
|
11350 </xsl:text> |
|
11351 <xsl:text> |
|
11352 </xsl:text> |
|
11353 <xsl:text>// Called on requestAnimationFrame, modifies DOM |
|
11354 </xsl:text> |
|
11355 <xsl:text>var requestAnimationFrameID = null; |
|
11356 </xsl:text> |
|
11357 <xsl:text>function animate() { |
|
11358 </xsl:text> |
|
11359 <xsl:text> let rearm = true; |
|
11360 </xsl:text> |
|
11361 <xsl:text> do{ |
|
11362 </xsl:text> |
|
11363 <xsl:text> if(page_fading == "pending" || page_fading == "forced"){ |
|
11364 </xsl:text> |
|
11365 <xsl:text> if(page_fading == "pending") |
|
11366 </xsl:text> |
|
11367 <xsl:text> svg_root.classList.add("fade-out-page"); |
|
11368 </xsl:text> |
|
11369 <xsl:text> page_fading = "in_progress"; |
|
11370 </xsl:text> |
|
11371 <xsl:text> if(page_fading_args.length) |
|
11372 </xsl:text> |
|
11373 <xsl:text> setTimeout(function(){ |
|
11374 </xsl:text> |
|
11375 <xsl:text> switch_page(...page_fading_args); |
|
11376 </xsl:text> |
|
11377 <xsl:text> },1); |
|
11378 </xsl:text> |
|
11379 <xsl:text> break; |
|
11380 </xsl:text> |
|
11381 <xsl:text> } |
|
11382 </xsl:text> |
|
11383 <xsl:text> |
|
11384 </xsl:text> |
|
11385 <xsl:text> // Do the page swith if pending |
|
11386 </xsl:text> |
|
11387 <xsl:text> if(page_switch_in_progress){ |
|
11388 </xsl:text> |
|
11389 <xsl:text> if(current_subscribed_page != current_visible_page){ |
|
11390 </xsl:text> |
|
11391 <xsl:text> switch_visible_page(current_subscribed_page); |
|
11392 </xsl:text> |
|
11393 <xsl:text> } |
|
11394 </xsl:text> |
|
11395 <xsl:text> |
|
11396 </xsl:text> |
|
11397 <xsl:text> page_switch_in_progress = false; |
|
11398 </xsl:text> |
|
11399 <xsl:text> |
|
11400 </xsl:text> |
|
11401 <xsl:text> if(page_fading == "in_progress"){ |
|
11402 </xsl:text> |
|
11403 <xsl:text> svg_root.classList.remove("fade-out-page"); |
|
11404 </xsl:text> |
|
11405 <xsl:text> page_fading = "off"; |
|
11406 </xsl:text> |
|
11407 <xsl:text> } |
|
11408 </xsl:text> |
|
11409 <xsl:text> } |
|
11410 </xsl:text> |
|
11411 <xsl:text> |
|
11412 </xsl:text> |
|
11413 <xsl:text> if(jumps_need_update) update_jumps(); |
|
11414 </xsl:text> |
|
11415 <xsl:text> |
|
11416 </xsl:text> |
|
11417 <xsl:text> |
|
11418 </xsl:text> |
|
11419 <xsl:text> pending_widget_animates.forEach(widget => widget._animate()); |
|
11420 </xsl:text> |
|
11421 <xsl:text> pending_widget_animates = []; |
|
11422 </xsl:text> |
|
11423 <xsl:text> rearm = false; |
|
11424 </xsl:text> |
|
11425 <xsl:text> } while(0); |
|
11426 </xsl:text> |
|
11427 <xsl:text> |
|
11428 </xsl:text> |
|
11429 <xsl:text> requestAnimationFrameID = null; |
|
11430 </xsl:text> |
|
11431 <xsl:text> |
|
11432 </xsl:text> |
|
11433 <xsl:text> if(rearm) requestHMIAnimation(); |
|
11434 </xsl:text> |
|
11435 <xsl:text>} |
|
11436 </xsl:text> |
|
11437 <xsl:text> |
|
11438 </xsl:text> |
|
11439 <xsl:text>function requestHMIAnimation() { |
|
11440 </xsl:text> |
|
11441 <xsl:text> if(requestAnimationFrameID == null){ |
|
11442 </xsl:text> |
|
11443 <xsl:text> requestAnimationFrameID = window.requestAnimationFrame(animate); |
|
11444 </xsl:text> |
|
11445 <xsl:text> } |
|
11446 </xsl:text> |
|
11447 <xsl:text>} |
|
11448 </xsl:text> |
|
11449 <xsl:text> |
|
11450 </xsl:text> |
|
11451 <xsl:text>// Message reception handler |
|
11452 </xsl:text> |
|
11453 <xsl:text>// Hash is verified and HMI values updates resulting from binary parsing |
|
11454 </xsl:text> |
|
11455 <xsl:text>// are stored until browser can compute next frame, DOM is left untouched |
|
11456 </xsl:text> |
|
11457 <xsl:text>function ws_onmessage(evt) { |
|
11458 </xsl:text> |
|
11459 <xsl:text> |
|
11460 </xsl:text> |
|
11461 <xsl:text> let data = evt.data; |
|
11462 </xsl:text> |
|
11463 <xsl:text> let dv = new DataView(data); |
|
11464 </xsl:text> |
|
11465 <xsl:text> let i = 0; |
|
11466 </xsl:text> |
|
11467 <xsl:text> try { |
|
11468 </xsl:text> |
|
11469 <xsl:text> for(let hash_int of hmi_hash) { |
|
11470 </xsl:text> |
|
11471 <xsl:text> if(hash_int != dv.getUint8(i)){ |
|
11472 </xsl:text> |
|
11473 <xsl:text> throw new Error("Hash doesn't match"); |
|
11474 </xsl:text> |
|
11475 <xsl:text> }; |
|
11476 </xsl:text> |
|
11477 <xsl:text> i++; |
|
11478 </xsl:text> |
|
11479 <xsl:text> }; |
|
11480 </xsl:text> |
|
11481 <xsl:text> |
|
11482 </xsl:text> |
|
11483 <xsl:text> while(i < data.byteLength){ |
|
11484 </xsl:text> |
|
11485 <xsl:text> let index = dv.getUint32(i, true); |
|
11486 </xsl:text> |
|
11487 <xsl:text> i += 4; |
|
11488 </xsl:text> |
|
11489 <xsl:text> let iectype = hmitree_types[index]; |
|
11490 </xsl:text> |
|
11491 <xsl:text> if(iectype != undefined){ |
|
11492 </xsl:text> |
|
11493 <xsl:text> let dvgetter = dvgetters[iectype]; |
|
11494 </xsl:text> |
|
11495 <xsl:text> let [value, bytesize] = dvgetter(dv,i); |
|
11496 </xsl:text> |
|
11497 <xsl:text> dispatch_value(index, value); |
|
11498 </xsl:text> |
|
11499 <xsl:text> i += bytesize; |
|
11500 </xsl:text> |
|
11501 <xsl:text> } else { |
|
11502 </xsl:text> |
|
11503 <xsl:text> throw new Error("Unknown index "+index); |
|
11504 </xsl:text> |
|
11505 <xsl:text> } |
|
11506 </xsl:text> |
|
11507 <xsl:text> }; |
|
11508 </xsl:text> |
|
11509 <xsl:text> |
|
11510 </xsl:text> |
|
11511 <xsl:text> // register for rendering on next frame, since there are updates |
|
11512 </xsl:text> |
|
11513 <xsl:text> } catch(err) { |
|
11514 </xsl:text> |
|
11515 <xsl:text> // 1003 is for "Unsupported Data" |
|
11516 </xsl:text> |
|
11517 <xsl:text> // ws.close(1003, err.message); |
|
11518 </xsl:text> |
|
11519 <xsl:text> |
|
11520 </xsl:text> |
|
11521 <xsl:text> // TODO : remove debug alert ? |
|
11522 </xsl:text> |
|
11523 <xsl:text> alert("Error : "+err.message+"\nHMI will be reloaded."); |
|
11524 </xsl:text> |
|
11525 <xsl:text> |
|
11526 </xsl:text> |
|
11527 <xsl:text> // force reload ignoring cache |
|
11528 </xsl:text> |
|
11529 <xsl:text> location.reload(true); |
|
11530 </xsl:text> |
|
11531 <xsl:text> } |
|
11532 </xsl:text> |
|
11533 <xsl:text>}; |
|
11534 </xsl:text> |
|
11535 <xsl:text> |
|
11536 </xsl:text> |
|
11537 <xsl:text>hmi_hash_u8 = new Uint8Array(hmi_hash); |
|
11538 </xsl:text> |
|
11539 <xsl:text> |
|
11540 </xsl:text> |
|
11541 <xsl:text>var ws = null; |
|
11542 </xsl:text> |
|
11543 <xsl:text> |
|
11544 </xsl:text> |
|
11545 <xsl:text>function send_blob(data) { |
|
11546 </xsl:text> |
|
11547 <xsl:text> if(ws && data.length > 0) { |
|
11548 </xsl:text> |
|
11549 <xsl:text> ws.send(new Blob([hmi_hash_u8].concat(data))); |
|
11550 </xsl:text> |
|
11551 <xsl:text> }; |
|
11552 </xsl:text> |
|
11553 <xsl:text>}; |
|
11554 </xsl:text> |
|
11555 <xsl:text> |
|
11556 </xsl:text> |
|
11557 <xsl:text>const typedarray_types = { |
|
11558 </xsl:text> |
|
11559 <xsl:text> INT: (number) => new Int16Array([number]), |
|
11560 </xsl:text> |
|
11561 <xsl:text> BOOL: (truth) => new Int8Array([truth]), |
|
11562 </xsl:text> |
|
11563 <xsl:text> NODE: (truth) => new Int8Array([truth]), |
|
11564 </xsl:text> |
|
11565 <xsl:text> REAL: (number) => new Float32Array([number]), |
|
11566 </xsl:text> |
|
11567 <xsl:text> STRING: (str) => { |
|
11568 </xsl:text> |
|
11569 <xsl:text> // beremiz default string max size is 128 |
|
11570 </xsl:text> |
|
11571 <xsl:text> str = str.slice(0,128); |
|
11572 </xsl:text> |
|
11573 <xsl:text> binary = new Uint8Array(str.length + 1); |
|
11574 </xsl:text> |
|
11575 <xsl:text> binary[0] = str.length; |
|
11576 </xsl:text> |
|
11577 <xsl:text> for(let i = 0; i < str.length; i++){ |
|
11578 </xsl:text> |
|
11579 <xsl:text> binary[i+1] = str.charCodeAt(i); |
|
11580 </xsl:text> |
|
11581 <xsl:text> } |
|
11582 </xsl:text> |
|
11583 <xsl:text> return binary; |
|
11584 </xsl:text> |
|
11585 <xsl:text> } |
|
11586 </xsl:text> |
|
11587 <xsl:text> /* TODO */ |
|
11588 </xsl:text> |
|
11589 <xsl:text>}; |
|
11590 </xsl:text> |
|
11591 <xsl:text> |
|
11592 </xsl:text> |
|
11593 <xsl:text>function send_reset() { |
|
11594 </xsl:text> |
|
11595 <xsl:text> send_blob(new Uint8Array([1])); /* reset = 1 */ |
|
11596 </xsl:text> |
|
11597 <xsl:text>}; |
|
11598 </xsl:text> |
|
11599 <xsl:text> |
|
11600 </xsl:text> |
|
11601 <xsl:text>var subscriptions = []; |
|
11602 </xsl:text> |
|
11603 <xsl:text> |
|
11604 </xsl:text> |
|
11605 <xsl:text>function subscribers(index) { |
|
11606 </xsl:text> |
|
11607 <xsl:text> let entry = subscriptions[index]; |
|
11608 </xsl:text> |
|
11609 <xsl:text> let res; |
|
11610 </xsl:text> |
|
11611 <xsl:text> if(entry == undefined){ |
|
11612 </xsl:text> |
|
11613 <xsl:text> res = new Set(); |
|
11614 </xsl:text> |
|
11615 <xsl:text> subscriptions[index] = [res,0]; |
|
11616 </xsl:text> |
|
11617 <xsl:text> }else{ |
|
11618 </xsl:text> |
|
11619 <xsl:text> [res, _ign] = entry; |
|
11620 </xsl:text> |
|
11621 <xsl:text> } |
|
11622 </xsl:text> |
|
11623 <xsl:text> return res |
|
11624 </xsl:text> |
|
11625 <xsl:text>} |
|
11626 </xsl:text> |
|
11627 <xsl:text> |
|
11628 </xsl:text> |
|
11629 <xsl:text>function get_subscription_period(index) { |
|
11630 </xsl:text> |
|
11631 <xsl:text> let entry = subscriptions[index]; |
|
11632 </xsl:text> |
|
11633 <xsl:text> if(entry == undefined) |
|
11634 </xsl:text> |
|
11635 <xsl:text> return 0; |
|
11636 </xsl:text> |
|
11637 <xsl:text> let [_ign, period] = entry; |
|
11638 </xsl:text> |
|
11639 <xsl:text> return period; |
|
11640 </xsl:text> |
|
11641 <xsl:text>} |
|
11642 </xsl:text> |
|
11643 <xsl:text> |
|
11644 </xsl:text> |
|
11645 <xsl:text>function set_subscription_period(index, period) { |
|
11646 </xsl:text> |
|
11647 <xsl:text> let entry = subscriptions[index]; |
|
11648 </xsl:text> |
|
11649 <xsl:text> if(entry == undefined){ |
|
11650 </xsl:text> |
|
11651 <xsl:text> subscriptions[index] = [new Set(), period]; |
|
11652 </xsl:text> |
|
11653 <xsl:text> } else { |
|
11654 </xsl:text> |
|
11655 <xsl:text> entry[1] = period; |
|
11656 </xsl:text> |
|
11657 <xsl:text> } |
|
11658 </xsl:text> |
|
11659 <xsl:text>} |
|
11660 </xsl:text> |
|
11661 <xsl:text> |
|
11662 </xsl:text> |
|
11663 <xsl:text>function reset_subscription_periods() { |
|
11664 </xsl:text> |
|
11665 <xsl:text> for(let index in subscriptions) |
|
11666 </xsl:text> |
|
11667 <xsl:text> subscriptions[index][1] = 0; |
|
11668 </xsl:text> |
|
11669 <xsl:text>} |
|
11670 </xsl:text> |
|
11671 <xsl:text> |
|
11672 </xsl:text> |
|
11673 <xsl:text>if(has_watchdog){ |
|
11674 </xsl:text> |
|
11675 <xsl:text> // artificially subscribe the watchdog widget to "/heartbeat" hmi variable |
|
11676 </xsl:text> |
|
11677 <xsl:text> // Since dispatch directly calls change_hmi_value, |
|
11678 </xsl:text> |
|
11679 <xsl:text> // PLC will periodically send variable at given frequency |
|
11680 </xsl:text> |
|
11681 <xsl:text> subscribers(heartbeat_index).add({ |
|
11682 </xsl:text> |
|
11683 <xsl:text> /* type: "Watchdog", */ |
|
11684 </xsl:text> |
|
11685 <xsl:text> frequency: 1, |
|
11686 </xsl:text> |
|
11687 <xsl:text> indexes: [heartbeat_index], |
|
11688 </xsl:text> |
|
11689 <xsl:text> new_hmi_value: function(index, value, oldval) { |
|
11690 </xsl:text> |
|
11691 <xsl:text> apply_hmi_value(heartbeat_index, value+1); |
|
11692 </xsl:text> |
|
11693 <xsl:text> } |
|
11694 </xsl:text> |
|
11695 <xsl:text> }); |
|
11696 </xsl:text> |
|
11697 <xsl:text>} |
|
11698 </xsl:text> |
|
11699 <xsl:text> |
|
11700 </xsl:text> |
|
11701 <xsl:text> |
|
11702 </xsl:text> |
|
11703 <xsl:text>var page_fading = "off"; |
|
11704 </xsl:text> |
|
11705 <xsl:text>var page_fading_args = "off"; |
|
11706 </xsl:text> |
|
11707 <xsl:text>function fading_page_switch(...args){ |
|
11708 </xsl:text> |
|
11709 <xsl:text> if(page_fading == "in_progress") |
|
11710 </xsl:text> |
|
11711 <xsl:text> page_fading = "forced"; |
|
11712 </xsl:text> |
|
11713 <xsl:text> else |
|
11714 </xsl:text> |
|
11715 <xsl:text> page_fading = "pending"; |
|
11716 </xsl:text> |
|
11717 <xsl:text> page_fading_args = args; |
|
11718 </xsl:text> |
|
11719 <xsl:text> |
|
11720 </xsl:text> |
|
11721 <xsl:text> requestHMIAnimation(); |
|
11722 </xsl:text> |
|
11723 <xsl:text> |
|
11724 </xsl:text> |
|
11725 <xsl:text>} |
|
11726 </xsl:text> |
|
11727 <xsl:text>document.body.style.backgroundColor = "black"; |
|
11728 </xsl:text> |
|
11729 <xsl:text> |
|
11730 </xsl:text> |
|
11731 <xsl:text>// subscribe to per instance current page hmi variable |
|
11732 </xsl:text> |
|
11733 <xsl:text>// PLC must prefix page name with "!" for page switch to happen |
|
11734 </xsl:text> |
|
11735 <xsl:text>subscribers(current_page_var_index).add({ |
|
11736 </xsl:text> |
|
11737 <xsl:text> frequency: 1, |
|
11738 </xsl:text> |
|
11739 <xsl:text> indexes: [current_page_var_index], |
|
11740 </xsl:text> |
|
11741 <xsl:text> new_hmi_value: function(index, value, oldval) { |
|
11742 </xsl:text> |
|
11743 <xsl:text> if(value.startsWith("!")) |
|
11744 </xsl:text> |
|
11745 <xsl:text> fading_page_switch(value.slice(1)); |
|
11746 </xsl:text> |
|
11747 <xsl:text> } |
|
11748 </xsl:text> |
|
11749 <xsl:text>}); |
|
11750 </xsl:text> |
|
11751 <xsl:text> |
|
11752 </xsl:text> |
|
11753 <xsl:text>function svg_text_to_multiline(elt) { |
|
11754 </xsl:text> |
|
11755 <xsl:text> return(Array.prototype.map.call(elt.children, x=>x.textContent).join("\n")); |
|
11756 </xsl:text> |
|
11757 <xsl:text>} |
|
11758 </xsl:text> |
|
11759 <xsl:text> |
|
11760 </xsl:text> |
|
11761 <xsl:text>function multiline_to_svg_text(elt, str, blank) { |
|
11762 </xsl:text> |
|
11763 <xsl:text> str.split('\n').map((line,i) => {elt.children[i].textContent = blank?"":line;}); |
|
11764 </xsl:text> |
|
11765 <xsl:text>} |
|
11766 </xsl:text> |
|
11767 <xsl:text> |
|
11768 </xsl:text> |
|
11769 <xsl:text>function switch_langnum(langnum) { |
|
11770 </xsl:text> |
|
11771 <xsl:text> langnum = Math.max(0, Math.min(langs.length - 1, langnum)); |
|
11772 </xsl:text> |
|
11773 <xsl:text> |
|
11774 </xsl:text> |
|
11775 <xsl:text> for (let translation of translations) { |
|
11776 </xsl:text> |
|
11777 <xsl:text> let [objs, msgs] = translation; |
|
11778 </xsl:text> |
|
11779 <xsl:text> let msg = msgs[langnum]; |
|
11780 </xsl:text> |
|
11781 <xsl:text> for (let obj of objs) { |
|
11782 </xsl:text> |
|
11783 <xsl:text> multiline_to_svg_text(obj, msg); |
|
11784 </xsl:text> |
|
11785 <xsl:text> obj.setAttribute("lang",langnum); |
|
11786 </xsl:text> |
|
11787 <xsl:text> } |
|
11788 </xsl:text> |
|
11789 <xsl:text> } |
|
11790 </xsl:text> |
|
11791 <xsl:text> return langnum; |
|
11792 </xsl:text> |
|
11793 <xsl:text>} |
|
11794 </xsl:text> |
|
11795 <xsl:text> |
|
11796 </xsl:text> |
|
11797 <xsl:text>// backup original texts |
|
11798 </xsl:text> |
|
11799 <xsl:text>for (let translation of translations) { |
|
11800 </xsl:text> |
|
11801 <xsl:text> let [objs, msgs] = translation; |
|
11802 </xsl:text> |
|
11803 <xsl:text> msgs.unshift(svg_text_to_multiline(objs[0])); |
|
11804 </xsl:text> |
|
11805 <xsl:text>} |
|
11806 </xsl:text> |
|
11807 <xsl:text> |
|
11808 </xsl:text> |
|
11809 <xsl:text>var lang_local_index = hmi_local_index("lang"); |
|
11810 </xsl:text> |
|
11811 <xsl:text>var langcode_local_index = hmi_local_index("lang_code"); |
|
11812 </xsl:text> |
|
11813 <xsl:text>var langname_local_index = hmi_local_index("lang_name"); |
|
11814 </xsl:text> |
|
11815 <xsl:text>subscribers(lang_local_index).add({ |
|
11816 </xsl:text> |
|
11817 <xsl:text> indexes: [lang_local_index], |
|
11818 </xsl:text> |
|
11819 <xsl:text> new_hmi_value: function(index, value, oldval) { |
|
11820 </xsl:text> |
|
11821 <xsl:text> let current_lang = switch_langnum(value); |
|
11822 </xsl:text> |
|
11823 <xsl:text> let [langname,langcode] = langs[current_lang]; |
|
11824 </xsl:text> |
|
11825 <xsl:text> apply_hmi_value(langcode_local_index, langcode); |
|
11826 </xsl:text> |
|
11827 <xsl:text> apply_hmi_value(langname_local_index, langname); |
|
11828 </xsl:text> |
|
11829 <xsl:text> switch_page(); |
|
11830 </xsl:text> |
|
11831 <xsl:text> } |
|
11832 </xsl:text> |
|
11833 <xsl:text>}); |
|
11834 </xsl:text> |
|
11835 <xsl:text> |
|
11836 </xsl:text> |
|
11837 <xsl:text>// returns en_US, fr_FR or en_UK depending on selected language |
|
11838 </xsl:text> |
|
11839 <xsl:text>function get_current_lang_code(){ |
|
11840 </xsl:text> |
|
11841 <xsl:text> return cache[langcode_local_index]; |
|
11842 </xsl:text> |
|
11843 <xsl:text>} |
|
11844 </xsl:text> |
|
11845 <xsl:text> |
|
11846 </xsl:text> |
|
11847 <xsl:text>function setup_lang(){ |
|
11848 </xsl:text> |
|
11849 <xsl:text> let current_lang = cache[lang_local_index]; |
|
11850 </xsl:text> |
|
11851 <xsl:text> let new_lang = switch_langnum(current_lang); |
|
11852 </xsl:text> |
|
11853 <xsl:text> if(current_lang != new_lang){ |
|
11854 </xsl:text> |
|
11855 <xsl:text> apply_hmi_value(lang_local_index, new_lang); |
|
11856 </xsl:text> |
|
11857 <xsl:text> } |
|
11858 </xsl:text> |
|
11859 <xsl:text>} |
|
11860 </xsl:text> |
|
11861 <xsl:text> |
|
11862 </xsl:text> |
|
11863 <xsl:text>setup_lang(); |
|
11864 </xsl:text> |
|
11865 <xsl:text> |
|
11866 </xsl:text> |
|
11867 <xsl:text>function update_subscriptions() { |
|
11868 </xsl:text> |
|
11869 <xsl:text> let delta = []; |
|
11870 </xsl:text> |
|
11871 <xsl:text> if(!ws) |
|
11872 </xsl:text> |
|
11873 <xsl:text> // dont' change subscriptions if not connected |
|
11874 </xsl:text> |
|
11875 <xsl:text> return; |
|
11876 </xsl:text> |
|
11877 <xsl:text> |
|
11878 </xsl:text> |
|
11879 <xsl:text> for(let index in subscriptions){ |
|
11880 </xsl:text> |
|
11881 <xsl:text> let widgets = subscribers(index); |
|
11882 </xsl:text> |
|
11883 <xsl:text> |
|
11884 </xsl:text> |
|
11885 <xsl:text> // periods are in ms |
|
11886 </xsl:text> |
|
11887 <xsl:text> let previous_period = get_subscription_period(index); |
|
11888 </xsl:text> |
|
11889 <xsl:text> |
|
11890 </xsl:text> |
|
11891 <xsl:text> // subscribing with a zero period is unsubscribing |
|
11892 </xsl:text> |
|
11893 <xsl:text> let new_period = 0; |
|
11894 </xsl:text> |
|
11895 <xsl:text> if(widgets.size > 0) { |
|
11896 </xsl:text> |
|
11897 <xsl:text> let maxfreq = 0; |
|
11898 </xsl:text> |
|
11899 <xsl:text> for(let widget of widgets){ |
|
11900 </xsl:text> |
|
11901 <xsl:text> let wf = widget.frequency; |
|
11902 </xsl:text> |
|
11903 <xsl:text> if(wf != undefined && maxfreq < wf) |
|
11904 </xsl:text> |
|
11905 <xsl:text> maxfreq = wf; |
|
11906 </xsl:text> |
|
11907 <xsl:text> } |
|
11908 </xsl:text> |
|
11909 <xsl:text> |
|
11910 </xsl:text> |
|
11911 <xsl:text> if(maxfreq != 0) |
|
11912 </xsl:text> |
|
11913 <xsl:text> new_period = 1000/maxfreq; |
|
11914 </xsl:text> |
|
11915 <xsl:text> } |
|
11916 </xsl:text> |
|
11917 <xsl:text> |
|
11918 </xsl:text> |
|
11919 <xsl:text> if(previous_period != new_period) { |
|
11920 </xsl:text> |
|
11921 <xsl:text> set_subscription_period(index, new_period); |
|
11922 </xsl:text> |
|
11923 <xsl:text> if(index <= last_remote_index){ |
|
11924 </xsl:text> |
|
11925 <xsl:text> delta.push( |
|
11926 </xsl:text> |
|
11927 <xsl:text> new Uint8Array([2]), /* subscribe = 2 */ |
|
11928 </xsl:text> |
|
11929 <xsl:text> new Uint32Array([index]), |
|
11930 </xsl:text> |
|
11931 <xsl:text> new Uint16Array([new_period])); |
|
11932 </xsl:text> |
|
11933 <xsl:text> } |
|
11934 </xsl:text> |
|
11935 <xsl:text> } |
|
11936 </xsl:text> |
|
11937 <xsl:text> } |
|
11938 </xsl:text> |
|
11939 <xsl:text> send_blob(delta); |
|
11940 </xsl:text> |
|
11941 <xsl:text>}; |
|
11942 </xsl:text> |
|
11943 <xsl:text> |
|
11944 </xsl:text> |
|
11945 <xsl:text>function send_hmi_value(index, value) { |
|
11946 </xsl:text> |
|
11947 <xsl:text> if(index > last_remote_index){ |
|
11948 </xsl:text> |
|
11949 <xsl:text> dispatch_value(index, value); |
|
11950 </xsl:text> |
|
11951 <xsl:text> |
|
11952 </xsl:text> |
|
11953 <xsl:text> if(persistent_indexes.has(index)){ |
|
11954 </xsl:text> |
|
11955 <xsl:text> let varname = persistent_indexes.get(index); |
|
11956 </xsl:text> |
|
11957 <xsl:text> document.cookie = varname+"="+value+"; max-age=3153600000"; |
|
11958 </xsl:text> |
|
11959 <xsl:text> } |
|
11960 </xsl:text> |
|
11961 <xsl:text> |
|
11962 </xsl:text> |
|
11963 <xsl:text> return; |
|
11964 </xsl:text> |
|
11965 <xsl:text> } |
|
11966 </xsl:text> |
|
11967 <xsl:text> |
|
11968 </xsl:text> |
|
11969 <xsl:text> let iectype = hmitree_types[index]; |
|
11970 </xsl:text> |
|
11971 <xsl:text> let tobinary = typedarray_types[iectype]; |
|
11972 </xsl:text> |
|
11973 <xsl:text> send_blob([ |
|
11974 </xsl:text> |
|
11975 <xsl:text> new Uint8Array([0]), /* setval = 0 */ |
|
11976 </xsl:text> |
|
11977 <xsl:text> new Uint32Array([index]), |
|
11978 </xsl:text> |
|
11979 <xsl:text> tobinary(value)]); |
|
11980 </xsl:text> |
|
11981 <xsl:text> |
|
11982 </xsl:text> |
|
11983 <xsl:text> // DON'T DO THAT unless read_iterator in svghmi.c modifies wbuf as well, not only rbuf |
|
11984 </xsl:text> |
|
11985 <xsl:text> // cache[index] = value; |
|
11986 </xsl:text> |
|
11987 <xsl:text>}; |
|
11988 </xsl:text> |
|
11989 <xsl:text> |
|
11990 </xsl:text> |
|
11991 <xsl:text>function apply_hmi_value(index, new_val) { |
|
11992 </xsl:text> |
|
11993 <xsl:text> // Similarly to previous comment, taking decision to update based |
|
11994 </xsl:text> |
|
11995 <xsl:text> // on cache content is bad and can lead to inconsistency |
|
11996 </xsl:text> |
|
11997 <xsl:text> /*let old_val = cache[index];*/ |
|
11998 </xsl:text> |
|
11999 <xsl:text> if(new_val != undefined /*&& old_val != new_val*/) |
|
12000 </xsl:text> |
|
12001 <xsl:text> send_hmi_value(index, new_val); |
|
12002 </xsl:text> |
|
12003 <xsl:text> return new_val; |
|
12004 </xsl:text> |
|
12005 <xsl:text>} |
|
12006 </xsl:text> |
|
12007 <xsl:text> |
|
12008 </xsl:text> |
|
12009 <xsl:text>const quotes = {"'":null, '"':null}; |
|
12010 </xsl:text> |
|
12011 <xsl:text> |
|
12012 </xsl:text> |
|
12013 <xsl:text>function eval_operation_string(old_val, opstr) { |
|
12014 </xsl:text> |
|
12015 <xsl:text> let op = opstr[0]; |
|
12016 </xsl:text> |
|
12017 <xsl:text> let given_val; |
|
12018 </xsl:text> |
|
12019 <xsl:text> if(opstr.length < 2) |
|
12020 </xsl:text> |
|
12021 <xsl:text> return undefined; |
|
12022 </xsl:text> |
|
12023 <xsl:text> if(opstr[1] in quotes){ |
|
12024 </xsl:text> |
|
12025 <xsl:text> if(opstr.length < 3) |
|
12026 </xsl:text> |
|
12027 <xsl:text> return undefined; |
|
12028 </xsl:text> |
|
12029 <xsl:text> if(opstr[opstr.length-1] == opstr[1]){ |
|
12030 </xsl:text> |
|
12031 <xsl:text> given_val = opstr.slice(2,opstr.length-1); |
|
12032 </xsl:text> |
|
12033 <xsl:text> } |
|
12034 </xsl:text> |
|
12035 <xsl:text> } else { |
|
12036 </xsl:text> |
|
12037 <xsl:text> given_val = Number(opstr.slice(1)); |
|
12038 </xsl:text> |
|
12039 <xsl:text> } |
|
12040 </xsl:text> |
|
12041 <xsl:text> let new_val; |
|
12042 </xsl:text> |
|
12043 <xsl:text> switch(op){ |
|
12044 </xsl:text> |
|
12045 <xsl:text> case "=": |
|
12046 </xsl:text> |
|
12047 <xsl:text> new_val = given_val; |
|
12048 </xsl:text> |
|
12049 <xsl:text> break; |
|
12050 </xsl:text> |
|
12051 <xsl:text> case "+": |
|
12052 </xsl:text> |
|
12053 <xsl:text> new_val = old_val + given_val; |
|
12054 </xsl:text> |
|
12055 <xsl:text> break; |
|
12056 </xsl:text> |
|
12057 <xsl:text> case "-": |
|
12058 </xsl:text> |
|
12059 <xsl:text> new_val = old_val - given_val; |
|
12060 </xsl:text> |
|
12061 <xsl:text> break; |
|
12062 </xsl:text> |
|
12063 <xsl:text> case "*": |
|
12064 </xsl:text> |
|
12065 <xsl:text> new_val = old_val * given_val; |
|
12066 </xsl:text> |
|
12067 <xsl:text> break; |
|
12068 </xsl:text> |
|
12069 <xsl:text> case "/": |
|
12070 </xsl:text> |
|
12071 <xsl:text> new_val = old_val / given_val; |
|
12072 </xsl:text> |
|
12073 <xsl:text> break; |
|
12074 </xsl:text> |
|
12075 <xsl:text> } |
|
12076 </xsl:text> |
|
12077 <xsl:text> return new_val; |
|
12078 </xsl:text> |
|
12079 <xsl:text>} |
|
12080 </xsl:text> |
|
12081 <xsl:text> |
|
12082 </xsl:text> |
|
12083 <xsl:text>var current_visible_page; |
|
12084 </xsl:text> |
|
12085 <xsl:text>var current_subscribed_page; |
|
12086 </xsl:text> |
|
12087 <xsl:text>var current_page_index; |
|
12088 </xsl:text> |
|
12089 <xsl:text>var page_node_local_index = hmi_local_index("page_node"); |
|
12090 </xsl:text> |
|
12091 <xsl:text>var page_switch_in_progress = false; |
|
12092 </xsl:text> |
|
12093 <xsl:text> |
|
12094 </xsl:text> |
|
12095 <xsl:text>function toggleFullscreen() { |
|
12096 </xsl:text> |
|
12097 <xsl:text> let elem = document.documentElement; |
|
12098 </xsl:text> |
|
12099 <xsl:text> |
|
12100 </xsl:text> |
|
12101 <xsl:text> if (!document.fullscreenElement) { |
|
12102 </xsl:text> |
|
12103 <xsl:text> elem.requestFullscreen().catch(err => { |
|
12104 </xsl:text> |
|
12105 <xsl:text> console.log("Error attempting to enable full-screen mode: "+err.message+" ("+err.name+")"); |
|
12106 </xsl:text> |
|
12107 <xsl:text> }); |
|
12108 </xsl:text> |
|
12109 <xsl:text> } else { |
|
12110 </xsl:text> |
|
12111 <xsl:text> document.exitFullscreen(); |
|
12112 </xsl:text> |
|
12113 <xsl:text> } |
|
12114 </xsl:text> |
|
12115 <xsl:text>} |
|
12116 </xsl:text> |
|
12117 <xsl:text> |
|
12118 </xsl:text> |
|
12119 <xsl:text>// prevents context menu from appearing on right click and long touch |
|
12120 </xsl:text> |
|
12121 <xsl:text>document.body.addEventListener('contextmenu', e => { |
|
12122 </xsl:text> |
|
12123 <xsl:text> toggleFullscreen(); |
|
12124 </xsl:text> |
|
12125 <xsl:text> e.preventDefault(); |
|
12126 </xsl:text> |
|
12127 <xsl:text>}); |
|
12128 </xsl:text> |
|
12129 <xsl:text> |
|
12130 </xsl:text> |
|
12131 <xsl:text>var screensaver_timer = null; |
|
12132 </xsl:text> |
|
12133 <xsl:text>function reset_screensaver_timer() { |
|
12134 </xsl:text> |
|
12135 <xsl:text> if(screensaver_timer){ |
|
12136 </xsl:text> |
|
12137 <xsl:text> window.clearTimeout(screensaver_timer); |
|
12138 </xsl:text> |
|
12139 <xsl:text> } |
|
12140 </xsl:text> |
|
12141 <xsl:text> screensaver_timer = window.setTimeout(() => { |
|
12142 </xsl:text> |
|
12143 <xsl:text> switch_page("ScreenSaver"); |
|
12144 </xsl:text> |
|
12145 <xsl:text> screensaver_timer = null; |
|
12146 </xsl:text> |
|
12147 <xsl:text> }, screensaver_delay*1000); |
|
12148 </xsl:text> |
|
12149 <xsl:text>} |
|
12150 </xsl:text> |
|
12151 <xsl:text>if(screensaver_delay) |
|
12152 </xsl:text> |
|
12153 <xsl:text> document.body.addEventListener('pointerdown', reset_screensaver_timer); |
|
12154 </xsl:text> |
|
12155 <xsl:text> |
|
12156 </xsl:text> |
|
12157 <xsl:text>function detach_detachables() { |
|
12158 </xsl:text> |
|
12159 <xsl:text> |
|
12160 </xsl:text> |
|
12161 <xsl:text> for(let eltid in detachable_elements){ |
|
12162 </xsl:text> |
|
12163 <xsl:text> let [element,parent] = detachable_elements[eltid]; |
|
12164 </xsl:text> |
|
12165 <xsl:text> parent.removeChild(element); |
|
12166 </xsl:text> |
|
12167 <xsl:text> } |
|
12168 </xsl:text> |
|
12169 <xsl:text>}; |
|
12170 </xsl:text> |
|
12171 <xsl:text> |
|
12172 </xsl:text> |
|
12173 <xsl:text>function switch_page(page_name, page_index) { |
|
12174 </xsl:text> |
|
12175 <xsl:text> if(page_switch_in_progress){ |
|
12176 </xsl:text> |
|
12177 <xsl:text> /* page switch already going */ |
|
12178 </xsl:text> |
|
12179 <xsl:text> /* TODO LOG ERROR */ |
|
12180 </xsl:text> |
|
12181 <xsl:text> return false; |
|
12182 </xsl:text> |
|
12183 <xsl:text> } |
|
12184 </xsl:text> |
|
12185 <xsl:text> page_switch_in_progress = true; |
|
12186 </xsl:text> |
|
12187 <xsl:text> |
|
12188 </xsl:text> |
|
12189 <xsl:text> if(page_name == undefined) |
|
12190 </xsl:text> |
|
12191 <xsl:text> page_name = current_subscribed_page; |
|
12192 </xsl:text> |
|
12193 <xsl:text> else if(page_index == undefined){ |
|
12194 </xsl:text> |
|
12195 <xsl:text> [page_name, page_index] = page_name.split('@') |
|
12196 </xsl:text> |
|
12197 <xsl:text> } |
|
12198 </xsl:text> |
|
12199 <xsl:text> |
|
12200 </xsl:text> |
|
12201 <xsl:text> let old_desc = page_desc[current_subscribed_page]; |
|
12202 </xsl:text> |
|
12203 <xsl:text> let new_desc = page_desc[page_name]; |
|
12204 </xsl:text> |
|
12205 <xsl:text> |
|
12206 </xsl:text> |
|
12207 <xsl:text> if(new_desc == undefined){ |
|
12208 </xsl:text> |
|
12209 <xsl:text> /* TODO LOG ERROR */ |
|
12210 </xsl:text> |
|
12211 <xsl:text> return false; |
|
12212 </xsl:text> |
|
12213 <xsl:text> } |
|
12214 </xsl:text> |
|
12215 <xsl:text> |
|
12216 </xsl:text> |
|
12217 <xsl:text> if(page_index == undefined) |
|
12218 </xsl:text> |
|
12219 <xsl:text> page_index = new_desc.page_index; |
|
12220 </xsl:text> |
|
12221 <xsl:text> else if(typeof(page_index) == "string") { |
|
12222 </xsl:text> |
|
12223 <xsl:text> let hmitree_node = hmitree_nodes[page_index]; |
|
12224 </xsl:text> |
|
12225 <xsl:text> if(hmitree_node !== undefined){ |
|
12226 </xsl:text> |
|
12227 <xsl:text> let [int_index, hmiclass] = hmitree_node; |
|
12228 </xsl:text> |
|
12229 <xsl:text> if(hmiclass == new_desc.page_class) |
|
12230 </xsl:text> |
|
12231 <xsl:text> page_index = int_index; |
|
12232 </xsl:text> |
|
12233 <xsl:text> else |
|
12234 </xsl:text> |
|
12235 <xsl:text> page_index = new_desc.page_index; |
|
12236 </xsl:text> |
|
12237 <xsl:text> } else { |
|
12238 </xsl:text> |
|
12239 <xsl:text> page_index = new_desc.page_index; |
|
12240 </xsl:text> |
|
12241 <xsl:text> } |
|
12242 </xsl:text> |
|
12243 <xsl:text> } |
|
12244 </xsl:text> |
|
12245 <xsl:text> |
|
12246 </xsl:text> |
|
12247 <xsl:text> if(old_desc){ |
|
12248 </xsl:text> |
|
12249 <xsl:text> old_desc.widgets.map(([widget,relativeness])=>widget.unsub()); |
|
12250 </xsl:text> |
|
12251 <xsl:text> } |
|
12252 </xsl:text> |
|
12253 <xsl:text> const new_offset = page_index == undefined ? 0 : page_index - new_desc.page_index; |
|
12254 </xsl:text> |
|
12255 <xsl:text> |
|
12256 </xsl:text> |
|
12257 <xsl:text> const container_id = page_name + (page_index != undefined ? page_index : ""); |
|
12258 </xsl:text> |
|
12259 <xsl:text> |
|
12260 </xsl:text> |
|
12261 <xsl:text> new_desc.widgets.map(([widget,relativeness])=>widget.sub(new_offset,relativeness,container_id)); |
|
12262 </xsl:text> |
|
12263 <xsl:text> |
|
12264 </xsl:text> |
|
12265 <xsl:text> update_subscriptions(); |
|
12266 </xsl:text> |
|
12267 <xsl:text> |
|
12268 </xsl:text> |
|
12269 <xsl:text> current_subscribed_page = page_name; |
|
12270 </xsl:text> |
|
12271 <xsl:text> current_page_index = page_index; |
|
12272 </xsl:text> |
|
12273 <xsl:text> let page_node; |
|
12274 </xsl:text> |
|
12275 <xsl:text> if(page_index != undefined){ |
|
12276 </xsl:text> |
|
12277 <xsl:text> page_node = hmitree_paths[page_index]; |
|
12278 </xsl:text> |
|
12279 <xsl:text> }else{ |
|
12280 </xsl:text> |
|
12281 <xsl:text> page_node = ""; |
|
12282 </xsl:text> |
|
12283 <xsl:text> } |
|
12284 </xsl:text> |
|
12285 <xsl:text> apply_hmi_value(page_node_local_index, page_node); |
|
12286 </xsl:text> |
|
12287 <xsl:text> |
|
12288 </xsl:text> |
|
12289 <xsl:text> jumps_need_update = true; |
|
12290 </xsl:text> |
|
12291 <xsl:text> |
|
12292 </xsl:text> |
|
12293 <xsl:text> requestHMIAnimation(); |
|
12294 </xsl:text> |
|
12295 <xsl:text> let [last_page_name, last_page_index] = jump_history[jump_history.length-1]; |
|
12296 </xsl:text> |
|
12297 <xsl:text> if(last_page_name != page_name || last_page_index != page_index){ |
|
12298 </xsl:text> |
|
12299 <xsl:text> jump_history.push([page_name, page_index]); |
|
12300 </xsl:text> |
|
12301 <xsl:text> if(jump_history.length > 42) |
|
12302 </xsl:text> |
|
12303 <xsl:text> jump_history.shift(); |
|
12304 </xsl:text> |
|
12305 <xsl:text> } |
|
12306 </xsl:text> |
|
12307 <xsl:text> |
|
12308 </xsl:text> |
|
12309 <xsl:text> apply_hmi_value(current_page_var_index, page_index == undefined |
|
12310 </xsl:text> |
|
12311 <xsl:text> ? page_name |
|
12312 </xsl:text> |
|
12313 <xsl:text> : page_name + "@" + hmitree_paths[page_index]); |
|
12314 </xsl:text> |
|
12315 <xsl:text> |
|
12316 </xsl:text> |
|
12317 <xsl:text> return true; |
|
12318 </xsl:text> |
|
12319 <xsl:text>}; |
|
12320 </xsl:text> |
|
12321 <xsl:text> |
|
12322 </xsl:text> |
|
12323 <xsl:text>function switch_visible_page(page_name) { |
|
12324 </xsl:text> |
|
12325 <xsl:text> |
|
12326 </xsl:text> |
|
12327 <xsl:text> let old_desc = page_desc[current_visible_page]; |
|
12328 </xsl:text> |
|
12329 <xsl:text> let new_desc = page_desc[page_name]; |
|
12330 </xsl:text> |
|
12331 <xsl:text> |
|
12332 </xsl:text> |
|
12333 <xsl:text> if(old_desc){ |
|
12334 </xsl:text> |
|
12335 <xsl:text> for(let eltid in old_desc.required_detachables){ |
|
12336 </xsl:text> |
|
12337 <xsl:text> if(!(eltid in new_desc.required_detachables)){ |
|
12338 </xsl:text> |
|
12339 <xsl:text> let [element, parent] = old_desc.required_detachables[eltid]; |
|
12340 </xsl:text> |
|
12341 <xsl:text> parent.removeChild(element); |
|
12342 </xsl:text> |
|
12343 <xsl:text> } |
|
12344 </xsl:text> |
|
12345 <xsl:text> } |
|
12346 </xsl:text> |
|
12347 <xsl:text> for(let eltid in new_desc.required_detachables){ |
|
12348 </xsl:text> |
|
12349 <xsl:text> if(!(eltid in old_desc.required_detachables)){ |
|
12350 </xsl:text> |
|
12351 <xsl:text> let [element, parent] = new_desc.required_detachables[eltid]; |
|
12352 </xsl:text> |
|
12353 <xsl:text> parent.appendChild(element); |
|
12354 </xsl:text> |
|
12355 <xsl:text> } |
|
12356 </xsl:text> |
|
12357 <xsl:text> } |
|
12358 </xsl:text> |
|
12359 <xsl:text> }else{ |
|
12360 </xsl:text> |
|
12361 <xsl:text> for(let eltid in new_desc.required_detachables){ |
|
12362 </xsl:text> |
|
12363 <xsl:text> let [element, parent] = new_desc.required_detachables[eltid]; |
|
12364 </xsl:text> |
|
12365 <xsl:text> parent.appendChild(element); |
|
12366 </xsl:text> |
|
12367 <xsl:text> } |
|
12368 </xsl:text> |
|
12369 <xsl:text> } |
|
12370 </xsl:text> |
|
12371 <xsl:text> |
|
12372 </xsl:text> |
|
12373 <xsl:text> svg_root.setAttribute('viewBox',new_desc.bbox.join(" ")); |
|
12374 </xsl:text> |
|
12375 <xsl:text> current_visible_page = page_name; |
|
12376 </xsl:text> |
|
12377 <xsl:text>}; |
|
12378 </xsl:text> |
|
12379 <xsl:text> |
|
12380 </xsl:text> |
|
12381 <xsl:text>/* From https://jsfiddle.net/ibowankenobi/1mmh7rs6/6/ */ |
|
12382 </xsl:text> |
|
12383 <xsl:text>function getAbsoluteCTM(element){ |
|
12384 </xsl:text> |
|
12385 <xsl:text> var height = svg_root.height.baseVal.value, |
|
12386 </xsl:text> |
|
12387 <xsl:text> width = svg_root.width.baseVal.value, |
|
12388 </xsl:text> |
|
12389 <xsl:text> viewBoxRect = svg_root.viewBox.baseVal, |
|
12390 </xsl:text> |
|
12391 <xsl:text> vHeight = viewBoxRect.height, |
|
12392 </xsl:text> |
|
12393 <xsl:text> vWidth = viewBoxRect.width; |
|
12394 </xsl:text> |
|
12395 <xsl:text> if(!vWidth || !vHeight){ |
|
12396 </xsl:text> |
|
12397 <xsl:text> return element.getCTM(); |
|
12398 </xsl:text> |
|
12399 <xsl:text> } |
|
12400 </xsl:text> |
|
12401 <xsl:text> var sH = height/vHeight, |
|
12402 </xsl:text> |
|
12403 <xsl:text> sW = width/vWidth, |
|
12404 </xsl:text> |
|
12405 <xsl:text> matrix = svg_root.createSVGMatrix(); |
|
12406 </xsl:text> |
|
12407 <xsl:text> matrix.a = sW; |
|
12408 </xsl:text> |
|
12409 <xsl:text> matrix.d = sH |
|
12410 </xsl:text> |
|
12411 <xsl:text> var realCTM = element.getCTM().multiply(matrix.inverse()); |
|
12412 </xsl:text> |
|
12413 <xsl:text> realCTM.e = realCTM.e/sW + viewBoxRect.x; |
|
12414 </xsl:text> |
|
12415 <xsl:text> realCTM.f = realCTM.f/sH + viewBoxRect.y; |
|
12416 </xsl:text> |
|
12417 <xsl:text> return realCTM; |
|
12418 </xsl:text> |
|
12419 <xsl:text>} |
|
12420 </xsl:text> |
|
12421 <xsl:text> |
|
12422 </xsl:text> |
|
12423 <xsl:text>function apply_reference_frames(){ |
|
12424 </xsl:text> |
|
12425 <xsl:text> const matches = svg_root.querySelectorAll("g[svghmi_x_offset]"); |
|
12426 </xsl:text> |
|
12427 <xsl:text> matches.forEach((group) => { |
|
12428 </xsl:text> |
|
12429 <xsl:text> let [x,y] = ["x", "y"].map((axis) => Number(group.getAttribute("svghmi_"+axis+"_offset"))); |
|
12430 </xsl:text> |
|
12431 <xsl:text> let ctm = getAbsoluteCTM(group); |
|
12432 </xsl:text> |
|
12433 <xsl:text> // zero translation part of CTM |
|
12434 </xsl:text> |
|
12435 <xsl:text> // to only apply rotation/skewing to offset vector |
|
12436 </xsl:text> |
|
12437 <xsl:text> ctm.e = 0; |
|
12438 </xsl:text> |
|
12439 <xsl:text> ctm.f = 0; |
|
12440 </xsl:text> |
|
12441 <xsl:text> let invctm = ctm.inverse(); |
|
12442 </xsl:text> |
|
12443 <xsl:text> let vect = new DOMPoint(x, y); |
|
12444 </xsl:text> |
|
12445 <xsl:text> let newvect = vect.matrixTransform(invctm); |
|
12446 </xsl:text> |
|
12447 <xsl:text> let transform = svg_root.createSVGTransform(); |
|
12448 </xsl:text> |
|
12449 <xsl:text> transform.setTranslate(newvect.x, newvect.y); |
|
12450 </xsl:text> |
|
12451 <xsl:text> group.transform.baseVal.appendItem(transform); |
|
12452 </xsl:text> |
|
12453 <xsl:text> ["x", "y"].forEach((axis) => group.removeAttribute("svghmi_"+axis+"_offset")); |
|
12454 </xsl:text> |
|
12455 <xsl:text> }); |
|
12456 </xsl:text> |
|
12457 <xsl:text>} |
|
12458 </xsl:text> |
|
12459 <xsl:text> |
|
12460 </xsl:text> |
|
12461 <xsl:text>// prepare SVG |
|
12462 </xsl:text> |
|
12463 <xsl:text>apply_reference_frames(); |
|
12464 </xsl:text> |
|
12465 <xsl:text>init_widgets(); |
|
12466 </xsl:text> |
|
12467 <xsl:text>detach_detachables(); |
|
12468 </xsl:text> |
|
12469 <xsl:text> |
|
12470 </xsl:text> |
|
12471 <xsl:text>// show main page |
|
12472 </xsl:text> |
|
12473 <xsl:text>switch_page(default_page); |
|
12474 </xsl:text> |
|
12475 <xsl:text> |
|
12476 </xsl:text> |
|
12477 <xsl:text>// initialize screensaver |
|
12478 </xsl:text> |
|
12479 <xsl:text>reset_screensaver_timer(); |
|
12480 </xsl:text> |
|
12481 <xsl:text> |
|
12482 </xsl:text> |
|
12483 <xsl:text>var reconnect_delay = 0; |
|
12484 </xsl:text> |
|
12485 <xsl:text>var periodic_reconnect_timer; |
|
12486 </xsl:text> |
|
12487 <xsl:text> |
|
12488 </xsl:text> |
|
12489 <xsl:text>// Once connection established |
|
12490 </xsl:text> |
|
12491 <xsl:text>function ws_onopen(evt) { |
|
12492 </xsl:text> |
|
12493 <xsl:text> // Work around memory leak with websocket on QtWebEngine |
|
12494 </xsl:text> |
|
12495 <xsl:text> // reconnect every hour to force deallocate websocket garbage |
|
12496 </xsl:text> |
|
12497 <xsl:text> if(window.navigator.userAgent.includes("QtWebEngine")){ |
|
12498 </xsl:text> |
|
12499 <xsl:text> if(periodic_reconnect_timer){ |
|
12500 </xsl:text> |
|
12501 <xsl:text> window.clearTimeout(periodic_reconnect_timer); |
|
12502 </xsl:text> |
|
12503 <xsl:text> } |
|
12504 </xsl:text> |
|
12505 <xsl:text> periodic_reconnect_timer = window.setTimeout(() => { |
|
12506 </xsl:text> |
|
12507 <xsl:text> ws.close(); |
|
12508 </xsl:text> |
|
12509 <xsl:text> periodic_reconnect_timer = null; |
|
12510 </xsl:text> |
|
12511 <xsl:text> }, 3600000); |
|
12512 </xsl:text> |
|
12513 <xsl:text> } |
|
12514 </xsl:text> |
|
12515 <xsl:text> |
|
12516 </xsl:text> |
|
12517 <xsl:text> // forget subscriptions remotely |
|
12518 </xsl:text> |
|
12519 <xsl:text> send_reset(); |
|
12520 </xsl:text> |
|
12521 <xsl:text> |
|
12522 </xsl:text> |
|
12523 <xsl:text> // forget earlier subscriptions locally |
|
12524 </xsl:text> |
|
12525 <xsl:text> reset_subscription_periods(); |
|
12526 </xsl:text> |
|
12527 <xsl:text> |
|
12528 </xsl:text> |
|
12529 <xsl:text> // update PLC about subscriptions and current page |
|
12530 </xsl:text> |
|
12531 <xsl:text> switch_page(); |
|
12532 </xsl:text> |
|
12533 <xsl:text> |
|
12534 </xsl:text> |
|
12535 <xsl:text> // at first try reconnect immediately |
|
12536 </xsl:text> |
|
12537 <xsl:text> reconnect_delay = 1; |
|
12538 </xsl:text> |
|
12539 <xsl:text>}; |
|
12540 </xsl:text> |
|
12541 <xsl:text> |
|
12542 </xsl:text> |
|
12543 <xsl:text>function ws_onclose(evt) { |
|
12544 </xsl:text> |
|
12545 <xsl:text> console.log("Connection closed. code:"+evt.code+" reason:"+evt.reason+" wasClean:"+evt.wasClean+" Reload in "+reconnect_delay+"ms."); |
|
12546 </xsl:text> |
|
12547 <xsl:text> ws = null; |
|
12548 </xsl:text> |
|
12549 <xsl:text> // reconect |
|
12550 </xsl:text> |
|
12551 <xsl:text> // TODO : add visible notification while waiting for reload |
|
12552 </xsl:text> |
|
12553 <xsl:text> window.setTimeout(create_ws, reconnect_delay); |
|
12554 </xsl:text> |
|
12555 <xsl:text> reconnect_delay += 500; |
|
12556 </xsl:text> |
|
12557 <xsl:text>}; |
|
12558 </xsl:text> |
|
12559 <xsl:text> |
|
12560 </xsl:text> |
|
12561 <xsl:text>var ws_url = |
11293 </xsl:text> |
12562 </xsl:text> |
11294 <xsl:text> window.location.href.replace(/^http(s?:\/\/[^\/]*)\/.*$/, 'ws$1/ws') |
12563 <xsl:text> window.location.href.replace(/^http(s?:\/\/[^\/]*)\/.*$/, 'ws$1/ws') |
11295 </xsl:text> |
12564 </xsl:text> |
11296 <xsl:text> + '?mode=' + (has_watchdog ? "watchdog" : "multiclient"); |
12565 <xsl:text> + '?mode=' + (has_watchdog ? "watchdog" : "multiclient"); |
11297 </xsl:text> |
12566 </xsl:text> |
11298 <xsl:text> |
12567 <xsl:text> |
11299 </xsl:text> |
12568 </xsl:text> |
11300 <xsl:text>var ws = new WebSocket(ws_url); |
12569 <xsl:text>function create_ws(){ |
11301 </xsl:text> |
12570 </xsl:text> |
11302 <xsl:text>ws.binaryType = 'arraybuffer'; |
12571 <xsl:text> ws = new WebSocket(ws_url); |
11303 </xsl:text> |
12572 </xsl:text> |
11304 <xsl:text> |
12573 <xsl:text> ws.binaryType = 'arraybuffer'; |
11305 </xsl:text> |
12574 </xsl:text> |
11306 <xsl:text>const dvgetters = { |
12575 <xsl:text> ws.onmessage = ws_onmessage; |
11307 </xsl:text> |
12576 </xsl:text> |
11308 <xsl:text> INT: (dv,offset) => [dv.getInt16(offset, true), 2], |
12577 <xsl:text> ws.onclose = ws_onclose; |
11309 </xsl:text> |
12578 </xsl:text> |
11310 <xsl:text> BOOL: (dv,offset) => [dv.getInt8(offset, true), 1], |
12579 <xsl:text> ws.onopen = ws_onopen; |
11311 </xsl:text> |
|
11312 <xsl:text> NODE: (dv,offset) => [dv.getInt8(offset, true), 1], |
|
11313 </xsl:text> |
|
11314 <xsl:text> REAL: (dv,offset) => [dv.getFloat32(offset, true), 4], |
|
11315 </xsl:text> |
|
11316 <xsl:text> STRING: (dv, offset) => { |
|
11317 </xsl:text> |
|
11318 <xsl:text> const size = dv.getInt8(offset); |
|
11319 </xsl:text> |
|
11320 <xsl:text> return [ |
|
11321 </xsl:text> |
|
11322 <xsl:text> String.fromCharCode.apply(null, new Uint8Array( |
|
11323 </xsl:text> |
|
11324 <xsl:text> dv.buffer, /* original buffer */ |
|
11325 </xsl:text> |
|
11326 <xsl:text> offset + 1, /* string starts after size*/ |
|
11327 </xsl:text> |
|
11328 <xsl:text> size /* size of string */ |
|
11329 </xsl:text> |
|
11330 <xsl:text> )), size + 1]; /* total increment */ |
|
11331 </xsl:text> |
|
11332 <xsl:text> } |
|
11333 </xsl:text> |
|
11334 <xsl:text>}; |
|
11335 </xsl:text> |
|
11336 <xsl:text> |
|
11337 </xsl:text> |
|
11338 <xsl:text>// Called on requestAnimationFrame, modifies DOM |
|
11339 </xsl:text> |
|
11340 <xsl:text>var requestAnimationFrameID = null; |
|
11341 </xsl:text> |
|
11342 <xsl:text>function animate() { |
|
11343 </xsl:text> |
|
11344 <xsl:text> let rearm = true; |
|
11345 </xsl:text> |
|
11346 <xsl:text> do{ |
|
11347 </xsl:text> |
|
11348 <xsl:text> if(page_fading == "pending" || page_fading == "forced"){ |
|
11349 </xsl:text> |
|
11350 <xsl:text> if(page_fading == "pending") |
|
11351 </xsl:text> |
|
11352 <xsl:text> svg_root.classList.add("fade-out-page"); |
|
11353 </xsl:text> |
|
11354 <xsl:text> page_fading = "in_progress"; |
|
11355 </xsl:text> |
|
11356 <xsl:text> if(page_fading_args.length) |
|
11357 </xsl:text> |
|
11358 <xsl:text> setTimeout(function(){ |
|
11359 </xsl:text> |
|
11360 <xsl:text> switch_page(...page_fading_args); |
|
11361 </xsl:text> |
|
11362 <xsl:text> },1); |
|
11363 </xsl:text> |
|
11364 <xsl:text> break; |
|
11365 </xsl:text> |
|
11366 <xsl:text> } |
|
11367 </xsl:text> |
|
11368 <xsl:text> |
|
11369 </xsl:text> |
|
11370 <xsl:text> // Do the page swith if pending |
|
11371 </xsl:text> |
|
11372 <xsl:text> if(page_switch_in_progress){ |
|
11373 </xsl:text> |
|
11374 <xsl:text> if(current_subscribed_page != current_visible_page){ |
|
11375 </xsl:text> |
|
11376 <xsl:text> switch_visible_page(current_subscribed_page); |
|
11377 </xsl:text> |
|
11378 <xsl:text> } |
|
11379 </xsl:text> |
|
11380 <xsl:text> |
|
11381 </xsl:text> |
|
11382 <xsl:text> page_switch_in_progress = false; |
|
11383 </xsl:text> |
|
11384 <xsl:text> |
|
11385 </xsl:text> |
|
11386 <xsl:text> if(page_fading == "in_progress"){ |
|
11387 </xsl:text> |
|
11388 <xsl:text> svg_root.classList.remove("fade-out-page"); |
|
11389 </xsl:text> |
|
11390 <xsl:text> page_fading = "off"; |
|
11391 </xsl:text> |
|
11392 <xsl:text> } |
|
11393 </xsl:text> |
|
11394 <xsl:text> } |
|
11395 </xsl:text> |
|
11396 <xsl:text> |
|
11397 </xsl:text> |
|
11398 <xsl:text> if(jumps_need_update) update_jumps(); |
|
11399 </xsl:text> |
|
11400 <xsl:text> |
|
11401 </xsl:text> |
|
11402 <xsl:text> |
|
11403 </xsl:text> |
|
11404 <xsl:text> pending_widget_animates.forEach(widget => widget._animate()); |
|
11405 </xsl:text> |
|
11406 <xsl:text> pending_widget_animates = []; |
|
11407 </xsl:text> |
|
11408 <xsl:text> rearm = false; |
|
11409 </xsl:text> |
|
11410 <xsl:text> } while(0); |
|
11411 </xsl:text> |
|
11412 <xsl:text> |
|
11413 </xsl:text> |
|
11414 <xsl:text> requestAnimationFrameID = null; |
|
11415 </xsl:text> |
|
11416 <xsl:text> |
|
11417 </xsl:text> |
|
11418 <xsl:text> if(rearm) requestHMIAnimation(); |
|
11419 </xsl:text> |
12580 </xsl:text> |
11420 <xsl:text>} |
12581 <xsl:text>} |
11421 </xsl:text> |
12582 </xsl:text> |
11422 <xsl:text> |
12583 <xsl:text> |
11423 </xsl:text> |
12584 </xsl:text> |
11424 <xsl:text>function requestHMIAnimation() { |
12585 <xsl:text>create_ws() |
11425 </xsl:text> |
|
11426 <xsl:text> if(requestAnimationFrameID == null){ |
|
11427 </xsl:text> |
|
11428 <xsl:text> requestAnimationFrameID = window.requestAnimationFrame(animate); |
|
11429 </xsl:text> |
|
11430 <xsl:text> } |
|
11431 </xsl:text> |
|
11432 <xsl:text>} |
|
11433 </xsl:text> |
|
11434 <xsl:text> |
|
11435 </xsl:text> |
|
11436 <xsl:text>// Message reception handler |
|
11437 </xsl:text> |
|
11438 <xsl:text>// Hash is verified and HMI values updates resulting from binary parsing |
|
11439 </xsl:text> |
|
11440 <xsl:text>// are stored until browser can compute next frame, DOM is left untouched |
|
11441 </xsl:text> |
|
11442 <xsl:text>ws.onmessage = function (evt) { |
|
11443 </xsl:text> |
|
11444 <xsl:text> |
|
11445 </xsl:text> |
|
11446 <xsl:text> let data = evt.data; |
|
11447 </xsl:text> |
|
11448 <xsl:text> let dv = new DataView(data); |
|
11449 </xsl:text> |
|
11450 <xsl:text> let i = 0; |
|
11451 </xsl:text> |
|
11452 <xsl:text> try { |
|
11453 </xsl:text> |
|
11454 <xsl:text> for(let hash_int of hmi_hash) { |
|
11455 </xsl:text> |
|
11456 <xsl:text> if(hash_int != dv.getUint8(i)){ |
|
11457 </xsl:text> |
|
11458 <xsl:text> throw new Error("Hash doesn't match"); |
|
11459 </xsl:text> |
|
11460 <xsl:text> }; |
|
11461 </xsl:text> |
|
11462 <xsl:text> i++; |
|
11463 </xsl:text> |
|
11464 <xsl:text> }; |
|
11465 </xsl:text> |
|
11466 <xsl:text> |
|
11467 </xsl:text> |
|
11468 <xsl:text> while(i < data.byteLength){ |
|
11469 </xsl:text> |
|
11470 <xsl:text> let index = dv.getUint32(i, true); |
|
11471 </xsl:text> |
|
11472 <xsl:text> i += 4; |
|
11473 </xsl:text> |
|
11474 <xsl:text> let iectype = hmitree_types[index]; |
|
11475 </xsl:text> |
|
11476 <xsl:text> if(iectype != undefined){ |
|
11477 </xsl:text> |
|
11478 <xsl:text> let dvgetter = dvgetters[iectype]; |
|
11479 </xsl:text> |
|
11480 <xsl:text> let [value, bytesize] = dvgetter(dv,i); |
|
11481 </xsl:text> |
|
11482 <xsl:text> dispatch_value(index, value); |
|
11483 </xsl:text> |
|
11484 <xsl:text> i += bytesize; |
|
11485 </xsl:text> |
|
11486 <xsl:text> } else { |
|
11487 </xsl:text> |
|
11488 <xsl:text> throw new Error("Unknown index "+index); |
|
11489 </xsl:text> |
|
11490 <xsl:text> } |
|
11491 </xsl:text> |
|
11492 <xsl:text> }; |
|
11493 </xsl:text> |
|
11494 <xsl:text> |
|
11495 </xsl:text> |
|
11496 <xsl:text> // register for rendering on next frame, since there are updates |
|
11497 </xsl:text> |
|
11498 <xsl:text> } catch(err) { |
|
11499 </xsl:text> |
|
11500 <xsl:text> // 1003 is for "Unsupported Data" |
|
11501 </xsl:text> |
|
11502 <xsl:text> // ws.close(1003, err.message); |
|
11503 </xsl:text> |
|
11504 <xsl:text> |
|
11505 </xsl:text> |
|
11506 <xsl:text> // TODO : remove debug alert ? |
|
11507 </xsl:text> |
|
11508 <xsl:text> alert("Error : "+err.message+"\nHMI will be reloaded."); |
|
11509 </xsl:text> |
|
11510 <xsl:text> |
|
11511 </xsl:text> |
|
11512 <xsl:text> // force reload ignoring cache |
|
11513 </xsl:text> |
|
11514 <xsl:text> location.reload(true); |
|
11515 </xsl:text> |
|
11516 <xsl:text> } |
|
11517 </xsl:text> |
|
11518 <xsl:text>}; |
|
11519 </xsl:text> |
|
11520 <xsl:text> |
|
11521 </xsl:text> |
|
11522 <xsl:text>hmi_hash_u8 = new Uint8Array(hmi_hash); |
|
11523 </xsl:text> |
|
11524 <xsl:text> |
|
11525 </xsl:text> |
|
11526 <xsl:text>function send_blob(data) { |
|
11527 </xsl:text> |
|
11528 <xsl:text> if(data.length > 0) { |
|
11529 </xsl:text> |
|
11530 <xsl:text> ws.send(new Blob([hmi_hash_u8].concat(data))); |
|
11531 </xsl:text> |
|
11532 <xsl:text> }; |
|
11533 </xsl:text> |
|
11534 <xsl:text>}; |
|
11535 </xsl:text> |
|
11536 <xsl:text> |
|
11537 </xsl:text> |
|
11538 <xsl:text>const typedarray_types = { |
|
11539 </xsl:text> |
|
11540 <xsl:text> INT: (number) => new Int16Array([number]), |
|
11541 </xsl:text> |
|
11542 <xsl:text> BOOL: (truth) => new Int16Array([truth]), |
|
11543 </xsl:text> |
|
11544 <xsl:text> NODE: (truth) => new Int16Array([truth]), |
|
11545 </xsl:text> |
|
11546 <xsl:text> REAL: (number) => new Float32Array([number]), |
|
11547 </xsl:text> |
|
11548 <xsl:text> STRING: (str) => { |
|
11549 </xsl:text> |
|
11550 <xsl:text> // beremiz default string max size is 128 |
|
11551 </xsl:text> |
|
11552 <xsl:text> str = str.slice(0,128); |
|
11553 </xsl:text> |
|
11554 <xsl:text> binary = new Uint8Array(str.length + 1); |
|
11555 </xsl:text> |
|
11556 <xsl:text> binary[0] = str.length; |
|
11557 </xsl:text> |
|
11558 <xsl:text> for(let i = 0; i < str.length; i++){ |
|
11559 </xsl:text> |
|
11560 <xsl:text> binary[i+1] = str.charCodeAt(i); |
|
11561 </xsl:text> |
|
11562 <xsl:text> } |
|
11563 </xsl:text> |
|
11564 <xsl:text> return binary; |
|
11565 </xsl:text> |
|
11566 <xsl:text> } |
|
11567 </xsl:text> |
|
11568 <xsl:text> /* TODO */ |
|
11569 </xsl:text> |
|
11570 <xsl:text>}; |
|
11571 </xsl:text> |
|
11572 <xsl:text> |
|
11573 </xsl:text> |
|
11574 <xsl:text>function send_reset() { |
|
11575 </xsl:text> |
|
11576 <xsl:text> send_blob(new Uint8Array([1])); /* reset = 1 */ |
|
11577 </xsl:text> |
|
11578 <xsl:text>}; |
|
11579 </xsl:text> |
|
11580 <xsl:text> |
|
11581 </xsl:text> |
|
11582 <xsl:text>var subscriptions = []; |
|
11583 </xsl:text> |
|
11584 <xsl:text> |
|
11585 </xsl:text> |
|
11586 <xsl:text>function subscribers(index) { |
|
11587 </xsl:text> |
|
11588 <xsl:text> let entry = subscriptions[index]; |
|
11589 </xsl:text> |
|
11590 <xsl:text> let res; |
|
11591 </xsl:text> |
|
11592 <xsl:text> if(entry == undefined){ |
|
11593 </xsl:text> |
|
11594 <xsl:text> res = new Set(); |
|
11595 </xsl:text> |
|
11596 <xsl:text> subscriptions[index] = [res,0]; |
|
11597 </xsl:text> |
|
11598 <xsl:text> }else{ |
|
11599 </xsl:text> |
|
11600 <xsl:text> [res, _ign] = entry; |
|
11601 </xsl:text> |
|
11602 <xsl:text> } |
|
11603 </xsl:text> |
|
11604 <xsl:text> return res |
|
11605 </xsl:text> |
|
11606 <xsl:text>} |
|
11607 </xsl:text> |
|
11608 <xsl:text> |
|
11609 </xsl:text> |
|
11610 <xsl:text>function get_subscription_period(index) { |
|
11611 </xsl:text> |
|
11612 <xsl:text> let entry = subscriptions[index]; |
|
11613 </xsl:text> |
|
11614 <xsl:text> if(entry == undefined) |
|
11615 </xsl:text> |
|
11616 <xsl:text> return 0; |
|
11617 </xsl:text> |
|
11618 <xsl:text> let [_ign, period] = entry; |
|
11619 </xsl:text> |
|
11620 <xsl:text> return period; |
|
11621 </xsl:text> |
|
11622 <xsl:text>} |
|
11623 </xsl:text> |
|
11624 <xsl:text> |
|
11625 </xsl:text> |
|
11626 <xsl:text>function set_subscription_period(index, period) { |
|
11627 </xsl:text> |
|
11628 <xsl:text> let entry = subscriptions[index]; |
|
11629 </xsl:text> |
|
11630 <xsl:text> if(entry == undefined){ |
|
11631 </xsl:text> |
|
11632 <xsl:text> subscriptions[index] = [new Set(), period]; |
|
11633 </xsl:text> |
|
11634 <xsl:text> } else { |
|
11635 </xsl:text> |
|
11636 <xsl:text> entry[1] = period; |
|
11637 </xsl:text> |
|
11638 <xsl:text> } |
|
11639 </xsl:text> |
|
11640 <xsl:text>} |
|
11641 </xsl:text> |
|
11642 <xsl:text> |
|
11643 </xsl:text> |
|
11644 <xsl:text>if(has_watchdog){ |
|
11645 </xsl:text> |
|
11646 <xsl:text> // artificially subscribe the watchdog widget to "/heartbeat" hmi variable |
|
11647 </xsl:text> |
|
11648 <xsl:text> // Since dispatch directly calls change_hmi_value, |
|
11649 </xsl:text> |
|
11650 <xsl:text> // PLC will periodically send variable at given frequency |
|
11651 </xsl:text> |
|
11652 <xsl:text> subscribers(heartbeat_index).add({ |
|
11653 </xsl:text> |
|
11654 <xsl:text> /* type: "Watchdog", */ |
|
11655 </xsl:text> |
|
11656 <xsl:text> frequency: 1, |
|
11657 </xsl:text> |
|
11658 <xsl:text> indexes: [heartbeat_index], |
|
11659 </xsl:text> |
|
11660 <xsl:text> new_hmi_value: function(index, value, oldval) { |
|
11661 </xsl:text> |
|
11662 <xsl:text> apply_hmi_value(heartbeat_index, value+1); |
|
11663 </xsl:text> |
|
11664 <xsl:text> } |
|
11665 </xsl:text> |
|
11666 <xsl:text> }); |
|
11667 </xsl:text> |
|
11668 <xsl:text>} |
|
11669 </xsl:text> |
|
11670 <xsl:text> |
|
11671 </xsl:text> |
|
11672 <xsl:text> |
|
11673 </xsl:text> |
|
11674 <xsl:text>var page_fading = "off"; |
|
11675 </xsl:text> |
|
11676 <xsl:text>var page_fading_args = "off"; |
|
11677 </xsl:text> |
|
11678 <xsl:text>function fading_page_switch(...args){ |
|
11679 </xsl:text> |
|
11680 <xsl:text> if(page_fading == "in_progress") |
|
11681 </xsl:text> |
|
11682 <xsl:text> page_fading = "forced"; |
|
11683 </xsl:text> |
|
11684 <xsl:text> else |
|
11685 </xsl:text> |
|
11686 <xsl:text> page_fading = "pending"; |
|
11687 </xsl:text> |
|
11688 <xsl:text> page_fading_args = args; |
|
11689 </xsl:text> |
|
11690 <xsl:text> |
|
11691 </xsl:text> |
|
11692 <xsl:text> requestHMIAnimation(); |
|
11693 </xsl:text> |
|
11694 <xsl:text> |
|
11695 </xsl:text> |
|
11696 <xsl:text>} |
|
11697 </xsl:text> |
|
11698 <xsl:text>document.body.style.backgroundColor = "black"; |
|
11699 </xsl:text> |
|
11700 <xsl:text> |
|
11701 </xsl:text> |
|
11702 <xsl:text>// subscribe to per instance current page hmi variable |
|
11703 </xsl:text> |
|
11704 <xsl:text>// PLC must prefix page name with "!" for page switch to happen |
|
11705 </xsl:text> |
|
11706 <xsl:text>subscribers(current_page_var_index).add({ |
|
11707 </xsl:text> |
|
11708 <xsl:text> frequency: 1, |
|
11709 </xsl:text> |
|
11710 <xsl:text> indexes: [current_page_var_index], |
|
11711 </xsl:text> |
|
11712 <xsl:text> new_hmi_value: function(index, value, oldval) { |
|
11713 </xsl:text> |
|
11714 <xsl:text> if(value.startsWith("!")) |
|
11715 </xsl:text> |
|
11716 <xsl:text> fading_page_switch(value.slice(1)); |
|
11717 </xsl:text> |
|
11718 <xsl:text> } |
|
11719 </xsl:text> |
|
11720 <xsl:text>}); |
|
11721 </xsl:text> |
|
11722 <xsl:text> |
|
11723 </xsl:text> |
|
11724 <xsl:text>function svg_text_to_multiline(elt) { |
|
11725 </xsl:text> |
|
11726 <xsl:text> return(Array.prototype.map.call(elt.children, x=>x.textContent).join("\n")); |
|
11727 </xsl:text> |
|
11728 <xsl:text>} |
|
11729 </xsl:text> |
|
11730 <xsl:text> |
|
11731 </xsl:text> |
|
11732 <xsl:text>function multiline_to_svg_text(elt, str, blank) { |
|
11733 </xsl:text> |
|
11734 <xsl:text> str.split('\n').map((line,i) => {elt.children[i].textContent = blank?"":line;}); |
|
11735 </xsl:text> |
|
11736 <xsl:text>} |
|
11737 </xsl:text> |
|
11738 <xsl:text> |
|
11739 </xsl:text> |
|
11740 <xsl:text>function switch_langnum(langnum) { |
|
11741 </xsl:text> |
|
11742 <xsl:text> langnum = Math.max(0, Math.min(langs.length - 1, langnum)); |
|
11743 </xsl:text> |
|
11744 <xsl:text> |
|
11745 </xsl:text> |
|
11746 <xsl:text> for (let translation of translations) { |
|
11747 </xsl:text> |
|
11748 <xsl:text> let [objs, msgs] = translation; |
|
11749 </xsl:text> |
|
11750 <xsl:text> let msg = msgs[langnum]; |
|
11751 </xsl:text> |
|
11752 <xsl:text> for (let obj of objs) { |
|
11753 </xsl:text> |
|
11754 <xsl:text> multiline_to_svg_text(obj, msg); |
|
11755 </xsl:text> |
|
11756 <xsl:text> obj.setAttribute("lang",langnum); |
|
11757 </xsl:text> |
|
11758 <xsl:text> } |
|
11759 </xsl:text> |
|
11760 <xsl:text> } |
|
11761 </xsl:text> |
|
11762 <xsl:text> return langnum; |
|
11763 </xsl:text> |
|
11764 <xsl:text>} |
|
11765 </xsl:text> |
|
11766 <xsl:text> |
|
11767 </xsl:text> |
|
11768 <xsl:text>// backup original texts |
|
11769 </xsl:text> |
|
11770 <xsl:text>for (let translation of translations) { |
|
11771 </xsl:text> |
|
11772 <xsl:text> let [objs, msgs] = translation; |
|
11773 </xsl:text> |
|
11774 <xsl:text> msgs.unshift(svg_text_to_multiline(objs[0])); |
|
11775 </xsl:text> |
|
11776 <xsl:text>} |
|
11777 </xsl:text> |
|
11778 <xsl:text> |
|
11779 </xsl:text> |
|
11780 <xsl:text>var lang_local_index = hmi_local_index("lang"); |
|
11781 </xsl:text> |
|
11782 <xsl:text>var langcode_local_index = hmi_local_index("lang_code"); |
|
11783 </xsl:text> |
|
11784 <xsl:text>var langname_local_index = hmi_local_index("lang_name"); |
|
11785 </xsl:text> |
|
11786 <xsl:text>subscribers(lang_local_index).add({ |
|
11787 </xsl:text> |
|
11788 <xsl:text> indexes: [lang_local_index], |
|
11789 </xsl:text> |
|
11790 <xsl:text> new_hmi_value: function(index, value, oldval) { |
|
11791 </xsl:text> |
|
11792 <xsl:text> let current_lang = switch_langnum(value); |
|
11793 </xsl:text> |
|
11794 <xsl:text> let [langname,langcode] = langs[current_lang]; |
|
11795 </xsl:text> |
|
11796 <xsl:text> apply_hmi_value(langcode_local_index, langcode); |
|
11797 </xsl:text> |
|
11798 <xsl:text> apply_hmi_value(langname_local_index, langname); |
|
11799 </xsl:text> |
|
11800 <xsl:text> switch_page(); |
|
11801 </xsl:text> |
|
11802 <xsl:text> } |
|
11803 </xsl:text> |
|
11804 <xsl:text>}); |
|
11805 </xsl:text> |
|
11806 <xsl:text> |
|
11807 </xsl:text> |
|
11808 <xsl:text>// returns en_US, fr_FR or en_UK depending on selected language |
|
11809 </xsl:text> |
|
11810 <xsl:text>function get_current_lang_code(){ |
|
11811 </xsl:text> |
|
11812 <xsl:text> return cache[langcode_local_index]; |
|
11813 </xsl:text> |
|
11814 <xsl:text>} |
|
11815 </xsl:text> |
|
11816 <xsl:text> |
|
11817 </xsl:text> |
|
11818 <xsl:text>function setup_lang(){ |
|
11819 </xsl:text> |
|
11820 <xsl:text> let current_lang = cache[lang_local_index]; |
|
11821 </xsl:text> |
|
11822 <xsl:text> let new_lang = switch_langnum(current_lang); |
|
11823 </xsl:text> |
|
11824 <xsl:text> if(current_lang != new_lang){ |
|
11825 </xsl:text> |
|
11826 <xsl:text> apply_hmi_value(lang_local_index, new_lang); |
|
11827 </xsl:text> |
|
11828 <xsl:text> } |
|
11829 </xsl:text> |
|
11830 <xsl:text>} |
|
11831 </xsl:text> |
|
11832 <xsl:text> |
|
11833 </xsl:text> |
|
11834 <xsl:text>setup_lang(); |
|
11835 </xsl:text> |
|
11836 <xsl:text> |
|
11837 </xsl:text> |
|
11838 <xsl:text>function update_subscriptions() { |
|
11839 </xsl:text> |
|
11840 <xsl:text> let delta = []; |
|
11841 </xsl:text> |
|
11842 <xsl:text> for(let index in subscriptions){ |
|
11843 </xsl:text> |
|
11844 <xsl:text> let widgets = subscribers(index); |
|
11845 </xsl:text> |
|
11846 <xsl:text> |
|
11847 </xsl:text> |
|
11848 <xsl:text> // periods are in ms |
|
11849 </xsl:text> |
|
11850 <xsl:text> let previous_period = get_subscription_period(index); |
|
11851 </xsl:text> |
|
11852 <xsl:text> |
|
11853 </xsl:text> |
|
11854 <xsl:text> // subscribing with a zero period is unsubscribing |
|
11855 </xsl:text> |
|
11856 <xsl:text> let new_period = 0; |
|
11857 </xsl:text> |
|
11858 <xsl:text> if(widgets.size > 0) { |
|
11859 </xsl:text> |
|
11860 <xsl:text> let maxfreq = 0; |
|
11861 </xsl:text> |
|
11862 <xsl:text> for(let widget of widgets){ |
|
11863 </xsl:text> |
|
11864 <xsl:text> let wf = widget.frequency; |
|
11865 </xsl:text> |
|
11866 <xsl:text> if(wf != undefined && maxfreq < wf) |
|
11867 </xsl:text> |
|
11868 <xsl:text> maxfreq = wf; |
|
11869 </xsl:text> |
|
11870 <xsl:text> } |
|
11871 </xsl:text> |
|
11872 <xsl:text> |
|
11873 </xsl:text> |
|
11874 <xsl:text> if(maxfreq != 0) |
|
11875 </xsl:text> |
|
11876 <xsl:text> new_period = 1000/maxfreq; |
|
11877 </xsl:text> |
|
11878 <xsl:text> } |
|
11879 </xsl:text> |
|
11880 <xsl:text> |
|
11881 </xsl:text> |
|
11882 <xsl:text> if(previous_period != new_period) { |
|
11883 </xsl:text> |
|
11884 <xsl:text> set_subscription_period(index, new_period); |
|
11885 </xsl:text> |
|
11886 <xsl:text> if(index <= last_remote_index){ |
|
11887 </xsl:text> |
|
11888 <xsl:text> delta.push( |
|
11889 </xsl:text> |
|
11890 <xsl:text> new Uint8Array([2]), /* subscribe = 2 */ |
|
11891 </xsl:text> |
|
11892 <xsl:text> new Uint32Array([index]), |
|
11893 </xsl:text> |
|
11894 <xsl:text> new Uint16Array([new_period])); |
|
11895 </xsl:text> |
|
11896 <xsl:text> } |
|
11897 </xsl:text> |
|
11898 <xsl:text> } |
|
11899 </xsl:text> |
|
11900 <xsl:text> } |
|
11901 </xsl:text> |
|
11902 <xsl:text> send_blob(delta); |
|
11903 </xsl:text> |
|
11904 <xsl:text>}; |
|
11905 </xsl:text> |
|
11906 <xsl:text> |
|
11907 </xsl:text> |
|
11908 <xsl:text>function send_hmi_value(index, value) { |
|
11909 </xsl:text> |
|
11910 <xsl:text> if(index > last_remote_index){ |
|
11911 </xsl:text> |
|
11912 <xsl:text> dispatch_value(index, value); |
|
11913 </xsl:text> |
|
11914 <xsl:text> |
|
11915 </xsl:text> |
|
11916 <xsl:text> if(persistent_indexes.has(index)){ |
|
11917 </xsl:text> |
|
11918 <xsl:text> let varname = persistent_indexes.get(index); |
|
11919 </xsl:text> |
|
11920 <xsl:text> document.cookie = varname+"="+value+"; max-age=3153600000"; |
|
11921 </xsl:text> |
|
11922 <xsl:text> } |
|
11923 </xsl:text> |
|
11924 <xsl:text> |
|
11925 </xsl:text> |
|
11926 <xsl:text> return; |
|
11927 </xsl:text> |
|
11928 <xsl:text> } |
|
11929 </xsl:text> |
|
11930 <xsl:text> |
|
11931 </xsl:text> |
|
11932 <xsl:text> let iectype = hmitree_types[index]; |
|
11933 </xsl:text> |
|
11934 <xsl:text> let tobinary = typedarray_types[iectype]; |
|
11935 </xsl:text> |
|
11936 <xsl:text> send_blob([ |
|
11937 </xsl:text> |
|
11938 <xsl:text> new Uint8Array([0]), /* setval = 0 */ |
|
11939 </xsl:text> |
|
11940 <xsl:text> new Uint32Array([index]), |
|
11941 </xsl:text> |
|
11942 <xsl:text> tobinary(value)]); |
|
11943 </xsl:text> |
|
11944 <xsl:text> |
|
11945 </xsl:text> |
|
11946 <xsl:text> // DON'T DO THAT unless read_iterator in svghmi.c modifies wbuf as well, not only rbuf |
|
11947 </xsl:text> |
|
11948 <xsl:text> // cache[index] = value; |
|
11949 </xsl:text> |
|
11950 <xsl:text>}; |
|
11951 </xsl:text> |
|
11952 <xsl:text> |
|
11953 </xsl:text> |
|
11954 <xsl:text>function apply_hmi_value(index, new_val) { |
|
11955 </xsl:text> |
|
11956 <xsl:text> // Similarly to previous comment, taking decision to update based |
|
11957 </xsl:text> |
|
11958 <xsl:text> // on cache content is bad and can lead to inconsistency |
|
11959 </xsl:text> |
|
11960 <xsl:text> /*let old_val = cache[index];*/ |
|
11961 </xsl:text> |
|
11962 <xsl:text> if(new_val != undefined /*&& old_val != new_val*/) |
|
11963 </xsl:text> |
|
11964 <xsl:text> send_hmi_value(index, new_val); |
|
11965 </xsl:text> |
|
11966 <xsl:text> return new_val; |
|
11967 </xsl:text> |
|
11968 <xsl:text>} |
|
11969 </xsl:text> |
|
11970 <xsl:text> |
|
11971 </xsl:text> |
|
11972 <xsl:text>const quotes = {"'":null, '"':null}; |
|
11973 </xsl:text> |
|
11974 <xsl:text> |
|
11975 </xsl:text> |
|
11976 <xsl:text>function eval_operation_string(old_val, opstr) { |
|
11977 </xsl:text> |
|
11978 <xsl:text> let op = opstr[0]; |
|
11979 </xsl:text> |
|
11980 <xsl:text> let given_val; |
|
11981 </xsl:text> |
|
11982 <xsl:text> if(opstr.length < 2) |
|
11983 </xsl:text> |
|
11984 <xsl:text> return undefined; |
|
11985 </xsl:text> |
|
11986 <xsl:text> if(opstr[1] in quotes){ |
|
11987 </xsl:text> |
|
11988 <xsl:text> if(opstr.length < 3) |
|
11989 </xsl:text> |
|
11990 <xsl:text> return undefined; |
|
11991 </xsl:text> |
|
11992 <xsl:text> if(opstr[opstr.length-1] == opstr[1]){ |
|
11993 </xsl:text> |
|
11994 <xsl:text> given_val = opstr.slice(2,opstr.length-1); |
|
11995 </xsl:text> |
|
11996 <xsl:text> } |
|
11997 </xsl:text> |
|
11998 <xsl:text> } else { |
|
11999 </xsl:text> |
|
12000 <xsl:text> given_val = Number(opstr.slice(1)); |
|
12001 </xsl:text> |
|
12002 <xsl:text> } |
|
12003 </xsl:text> |
|
12004 <xsl:text> let new_val; |
|
12005 </xsl:text> |
|
12006 <xsl:text> switch(op){ |
|
12007 </xsl:text> |
|
12008 <xsl:text> case "=": |
|
12009 </xsl:text> |
|
12010 <xsl:text> new_val = given_val; |
|
12011 </xsl:text> |
|
12012 <xsl:text> break; |
|
12013 </xsl:text> |
|
12014 <xsl:text> case "+": |
|
12015 </xsl:text> |
|
12016 <xsl:text> new_val = old_val + given_val; |
|
12017 </xsl:text> |
|
12018 <xsl:text> break; |
|
12019 </xsl:text> |
|
12020 <xsl:text> case "-": |
|
12021 </xsl:text> |
|
12022 <xsl:text> new_val = old_val - given_val; |
|
12023 </xsl:text> |
|
12024 <xsl:text> break; |
|
12025 </xsl:text> |
|
12026 <xsl:text> case "*": |
|
12027 </xsl:text> |
|
12028 <xsl:text> new_val = old_val * given_val; |
|
12029 </xsl:text> |
|
12030 <xsl:text> break; |
|
12031 </xsl:text> |
|
12032 <xsl:text> case "/": |
|
12033 </xsl:text> |
|
12034 <xsl:text> new_val = old_val / given_val; |
|
12035 </xsl:text> |
|
12036 <xsl:text> break; |
|
12037 </xsl:text> |
|
12038 <xsl:text> } |
|
12039 </xsl:text> |
|
12040 <xsl:text> return new_val; |
|
12041 </xsl:text> |
|
12042 <xsl:text>} |
|
12043 </xsl:text> |
|
12044 <xsl:text> |
|
12045 </xsl:text> |
|
12046 <xsl:text>var current_visible_page; |
|
12047 </xsl:text> |
|
12048 <xsl:text>var current_subscribed_page; |
|
12049 </xsl:text> |
|
12050 <xsl:text>var current_page_index; |
|
12051 </xsl:text> |
|
12052 <xsl:text>var page_node_local_index = hmi_local_index("page_node"); |
|
12053 </xsl:text> |
|
12054 <xsl:text>var page_switch_in_progress = false; |
|
12055 </xsl:text> |
|
12056 <xsl:text> |
|
12057 </xsl:text> |
|
12058 <xsl:text>function toggleFullscreen() { |
|
12059 </xsl:text> |
|
12060 <xsl:text> let elem = document.documentElement; |
|
12061 </xsl:text> |
|
12062 <xsl:text> |
|
12063 </xsl:text> |
|
12064 <xsl:text> if (!document.fullscreenElement) { |
|
12065 </xsl:text> |
|
12066 <xsl:text> elem.requestFullscreen().catch(err => { |
|
12067 </xsl:text> |
|
12068 <xsl:text> console.log("Error attempting to enable full-screen mode: "+err.message+" ("+err.name+")"); |
|
12069 </xsl:text> |
|
12070 <xsl:text> }); |
|
12071 </xsl:text> |
|
12072 <xsl:text> } else { |
|
12073 </xsl:text> |
|
12074 <xsl:text> document.exitFullscreen(); |
|
12075 </xsl:text> |
|
12076 <xsl:text> } |
|
12077 </xsl:text> |
|
12078 <xsl:text>} |
|
12079 </xsl:text> |
|
12080 <xsl:text> |
|
12081 </xsl:text> |
|
12082 <xsl:text>function prepare_svg() { |
|
12083 </xsl:text> |
|
12084 <xsl:text> // prevents context menu from appearing on right click and long touch |
|
12085 </xsl:text> |
|
12086 <xsl:text> document.body.addEventListener('contextmenu', e => { |
|
12087 </xsl:text> |
|
12088 <xsl:text> toggleFullscreen(); |
|
12089 </xsl:text> |
|
12090 <xsl:text> e.preventDefault(); |
|
12091 </xsl:text> |
|
12092 <xsl:text> }); |
|
12093 </xsl:text> |
|
12094 <xsl:text> |
|
12095 </xsl:text> |
|
12096 <xsl:text> for(let eltid in detachable_elements){ |
|
12097 </xsl:text> |
|
12098 <xsl:text> let [element,parent] = detachable_elements[eltid]; |
|
12099 </xsl:text> |
|
12100 <xsl:text> parent.removeChild(element); |
|
12101 </xsl:text> |
|
12102 <xsl:text> } |
|
12103 </xsl:text> |
|
12104 <xsl:text>}; |
|
12105 </xsl:text> |
|
12106 <xsl:text> |
|
12107 </xsl:text> |
|
12108 <xsl:text>function switch_page(page_name, page_index) { |
|
12109 </xsl:text> |
|
12110 <xsl:text> if(page_switch_in_progress){ |
|
12111 </xsl:text> |
|
12112 <xsl:text> /* page switch already going */ |
|
12113 </xsl:text> |
|
12114 <xsl:text> /* TODO LOG ERROR */ |
|
12115 </xsl:text> |
|
12116 <xsl:text> return false; |
|
12117 </xsl:text> |
|
12118 <xsl:text> } |
|
12119 </xsl:text> |
|
12120 <xsl:text> page_switch_in_progress = true; |
|
12121 </xsl:text> |
|
12122 <xsl:text> |
|
12123 </xsl:text> |
|
12124 <xsl:text> if(page_name == undefined) |
|
12125 </xsl:text> |
|
12126 <xsl:text> page_name = current_subscribed_page; |
|
12127 </xsl:text> |
|
12128 <xsl:text> else if(page_index == undefined){ |
|
12129 </xsl:text> |
|
12130 <xsl:text> [page_name, page_index] = page_name.split('@') |
|
12131 </xsl:text> |
|
12132 <xsl:text> } |
|
12133 </xsl:text> |
|
12134 <xsl:text> |
|
12135 </xsl:text> |
|
12136 <xsl:text> let old_desc = page_desc[current_subscribed_page]; |
|
12137 </xsl:text> |
|
12138 <xsl:text> let new_desc = page_desc[page_name]; |
|
12139 </xsl:text> |
|
12140 <xsl:text> |
|
12141 </xsl:text> |
|
12142 <xsl:text> if(new_desc == undefined){ |
|
12143 </xsl:text> |
|
12144 <xsl:text> /* TODO LOG ERROR */ |
|
12145 </xsl:text> |
|
12146 <xsl:text> return false; |
|
12147 </xsl:text> |
|
12148 <xsl:text> } |
|
12149 </xsl:text> |
|
12150 <xsl:text> |
|
12151 </xsl:text> |
|
12152 <xsl:text> if(page_index == undefined) |
|
12153 </xsl:text> |
|
12154 <xsl:text> page_index = new_desc.page_index; |
|
12155 </xsl:text> |
|
12156 <xsl:text> else if(typeof(page_index) == "string") { |
|
12157 </xsl:text> |
|
12158 <xsl:text> let hmitree_node = hmitree_nodes[page_index]; |
|
12159 </xsl:text> |
|
12160 <xsl:text> if(hmitree_node !== undefined){ |
|
12161 </xsl:text> |
|
12162 <xsl:text> let [int_index, hmiclass] = hmitree_node; |
|
12163 </xsl:text> |
|
12164 <xsl:text> if(hmiclass == new_desc.page_class) |
|
12165 </xsl:text> |
|
12166 <xsl:text> page_index = int_index; |
|
12167 </xsl:text> |
|
12168 <xsl:text> else |
|
12169 </xsl:text> |
|
12170 <xsl:text> page_index = new_desc.page_index; |
|
12171 </xsl:text> |
|
12172 <xsl:text> } else { |
|
12173 </xsl:text> |
|
12174 <xsl:text> page_index = new_desc.page_index; |
|
12175 </xsl:text> |
|
12176 <xsl:text> } |
|
12177 </xsl:text> |
|
12178 <xsl:text> } |
|
12179 </xsl:text> |
|
12180 <xsl:text> |
|
12181 </xsl:text> |
|
12182 <xsl:text> if(old_desc){ |
|
12183 </xsl:text> |
|
12184 <xsl:text> old_desc.widgets.map(([widget,relativeness])=>widget.unsub()); |
|
12185 </xsl:text> |
|
12186 <xsl:text> } |
|
12187 </xsl:text> |
|
12188 <xsl:text> const new_offset = page_index == undefined ? 0 : page_index - new_desc.page_index; |
|
12189 </xsl:text> |
|
12190 <xsl:text> |
|
12191 </xsl:text> |
|
12192 <xsl:text> const container_id = page_name + (page_index != undefined ? page_index : ""); |
|
12193 </xsl:text> |
|
12194 <xsl:text> |
|
12195 </xsl:text> |
|
12196 <xsl:text> new_desc.widgets.map(([widget,relativeness])=>widget.sub(new_offset,relativeness,container_id)); |
|
12197 </xsl:text> |
|
12198 <xsl:text> |
|
12199 </xsl:text> |
|
12200 <xsl:text> update_subscriptions(); |
|
12201 </xsl:text> |
|
12202 <xsl:text> |
|
12203 </xsl:text> |
|
12204 <xsl:text> current_subscribed_page = page_name; |
|
12205 </xsl:text> |
|
12206 <xsl:text> current_page_index = page_index; |
|
12207 </xsl:text> |
|
12208 <xsl:text> let page_node; |
|
12209 </xsl:text> |
|
12210 <xsl:text> if(page_index != undefined){ |
|
12211 </xsl:text> |
|
12212 <xsl:text> page_node = hmitree_paths[page_index]; |
|
12213 </xsl:text> |
|
12214 <xsl:text> }else{ |
|
12215 </xsl:text> |
|
12216 <xsl:text> page_node = ""; |
|
12217 </xsl:text> |
|
12218 <xsl:text> } |
|
12219 </xsl:text> |
|
12220 <xsl:text> apply_hmi_value(page_node_local_index, page_node); |
|
12221 </xsl:text> |
|
12222 <xsl:text> |
|
12223 </xsl:text> |
|
12224 <xsl:text> jumps_need_update = true; |
|
12225 </xsl:text> |
|
12226 <xsl:text> |
|
12227 </xsl:text> |
|
12228 <xsl:text> requestHMIAnimation(); |
|
12229 </xsl:text> |
|
12230 <xsl:text> jump_history.push([page_name, page_index]); |
|
12231 </xsl:text> |
|
12232 <xsl:text> if(jump_history.length > 42) |
|
12233 </xsl:text> |
|
12234 <xsl:text> jump_history.shift(); |
|
12235 </xsl:text> |
|
12236 <xsl:text> |
|
12237 </xsl:text> |
|
12238 <xsl:text> apply_hmi_value(current_page_var_index, page_index == undefined |
|
12239 </xsl:text> |
|
12240 <xsl:text> ? page_name |
|
12241 </xsl:text> |
|
12242 <xsl:text> : page_name + "@" + hmitree_paths[page_index]); |
|
12243 </xsl:text> |
|
12244 <xsl:text> |
|
12245 </xsl:text> |
|
12246 <xsl:text> return true; |
|
12247 </xsl:text> |
|
12248 <xsl:text>}; |
|
12249 </xsl:text> |
|
12250 <xsl:text> |
|
12251 </xsl:text> |
|
12252 <xsl:text>function switch_visible_page(page_name) { |
|
12253 </xsl:text> |
|
12254 <xsl:text> |
|
12255 </xsl:text> |
|
12256 <xsl:text> let old_desc = page_desc[current_visible_page]; |
|
12257 </xsl:text> |
|
12258 <xsl:text> let new_desc = page_desc[page_name]; |
|
12259 </xsl:text> |
|
12260 <xsl:text> |
|
12261 </xsl:text> |
|
12262 <xsl:text> if(old_desc){ |
|
12263 </xsl:text> |
|
12264 <xsl:text> for(let eltid in old_desc.required_detachables){ |
|
12265 </xsl:text> |
|
12266 <xsl:text> if(!(eltid in new_desc.required_detachables)){ |
|
12267 </xsl:text> |
|
12268 <xsl:text> let [element, parent] = old_desc.required_detachables[eltid]; |
|
12269 </xsl:text> |
|
12270 <xsl:text> parent.removeChild(element); |
|
12271 </xsl:text> |
|
12272 <xsl:text> } |
|
12273 </xsl:text> |
|
12274 <xsl:text> } |
|
12275 </xsl:text> |
|
12276 <xsl:text> for(let eltid in new_desc.required_detachables){ |
|
12277 </xsl:text> |
|
12278 <xsl:text> if(!(eltid in old_desc.required_detachables)){ |
|
12279 </xsl:text> |
|
12280 <xsl:text> let [element, parent] = new_desc.required_detachables[eltid]; |
|
12281 </xsl:text> |
|
12282 <xsl:text> parent.appendChild(element); |
|
12283 </xsl:text> |
|
12284 <xsl:text> } |
|
12285 </xsl:text> |
|
12286 <xsl:text> } |
|
12287 </xsl:text> |
|
12288 <xsl:text> }else{ |
|
12289 </xsl:text> |
|
12290 <xsl:text> for(let eltid in new_desc.required_detachables){ |
|
12291 </xsl:text> |
|
12292 <xsl:text> let [element, parent] = new_desc.required_detachables[eltid]; |
|
12293 </xsl:text> |
|
12294 <xsl:text> parent.appendChild(element); |
|
12295 </xsl:text> |
|
12296 <xsl:text> } |
|
12297 </xsl:text> |
|
12298 <xsl:text> } |
|
12299 </xsl:text> |
|
12300 <xsl:text> |
|
12301 </xsl:text> |
|
12302 <xsl:text> svg_root.setAttribute('viewBox',new_desc.bbox.join(" ")); |
|
12303 </xsl:text> |
|
12304 <xsl:text> current_visible_page = page_name; |
|
12305 </xsl:text> |
|
12306 <xsl:text>}; |
|
12307 </xsl:text> |
|
12308 <xsl:text> |
|
12309 </xsl:text> |
|
12310 <xsl:text>/* From https://jsfiddle.net/ibowankenobi/1mmh7rs6/6/ */ |
|
12311 </xsl:text> |
|
12312 <xsl:text>function getAbsoluteCTM(element){ |
|
12313 </xsl:text> |
|
12314 <xsl:text> var height = svg_root.height.baseVal.value, |
|
12315 </xsl:text> |
|
12316 <xsl:text> width = svg_root.width.baseVal.value, |
|
12317 </xsl:text> |
|
12318 <xsl:text> viewBoxRect = svg_root.viewBox.baseVal, |
|
12319 </xsl:text> |
|
12320 <xsl:text> vHeight = viewBoxRect.height, |
|
12321 </xsl:text> |
|
12322 <xsl:text> vWidth = viewBoxRect.width; |
|
12323 </xsl:text> |
|
12324 <xsl:text> if(!vWidth || !vHeight){ |
|
12325 </xsl:text> |
|
12326 <xsl:text> return element.getCTM(); |
|
12327 </xsl:text> |
|
12328 <xsl:text> } |
|
12329 </xsl:text> |
|
12330 <xsl:text> var sH = height/vHeight, |
|
12331 </xsl:text> |
|
12332 <xsl:text> sW = width/vWidth, |
|
12333 </xsl:text> |
|
12334 <xsl:text> matrix = svg_root.createSVGMatrix(); |
|
12335 </xsl:text> |
|
12336 <xsl:text> matrix.a = sW; |
|
12337 </xsl:text> |
|
12338 <xsl:text> matrix.d = sH |
|
12339 </xsl:text> |
|
12340 <xsl:text> var realCTM = element.getCTM().multiply(matrix.inverse()); |
|
12341 </xsl:text> |
|
12342 <xsl:text> realCTM.e = realCTM.e/sW + viewBoxRect.x; |
|
12343 </xsl:text> |
|
12344 <xsl:text> realCTM.f = realCTM.f/sH + viewBoxRect.y; |
|
12345 </xsl:text> |
|
12346 <xsl:text> return realCTM; |
|
12347 </xsl:text> |
|
12348 <xsl:text>} |
|
12349 </xsl:text> |
|
12350 <xsl:text> |
|
12351 </xsl:text> |
|
12352 <xsl:text>function apply_reference_frames(){ |
|
12353 </xsl:text> |
|
12354 <xsl:text> const matches = svg_root.querySelectorAll("g[svghmi_x_offset]"); |
|
12355 </xsl:text> |
|
12356 <xsl:text> matches.forEach((group) => { |
|
12357 </xsl:text> |
|
12358 <xsl:text> let [x,y] = ["x", "y"].map((axis) => Number(group.getAttribute("svghmi_"+axis+"_offset"))); |
|
12359 </xsl:text> |
|
12360 <xsl:text> let ctm = getAbsoluteCTM(group); |
|
12361 </xsl:text> |
|
12362 <xsl:text> // zero translation part of CTM |
|
12363 </xsl:text> |
|
12364 <xsl:text> // to only apply rotation/skewing to offset vector |
|
12365 </xsl:text> |
|
12366 <xsl:text> ctm.e = 0; |
|
12367 </xsl:text> |
|
12368 <xsl:text> ctm.f = 0; |
|
12369 </xsl:text> |
|
12370 <xsl:text> let invctm = ctm.inverse(); |
|
12371 </xsl:text> |
|
12372 <xsl:text> let vect = new DOMPoint(x, y); |
|
12373 </xsl:text> |
|
12374 <xsl:text> let newvect = vect.matrixTransform(invctm); |
|
12375 </xsl:text> |
|
12376 <xsl:text> let transform = svg_root.createSVGTransform(); |
|
12377 </xsl:text> |
|
12378 <xsl:text> transform.setTranslate(newvect.x, newvect.y); |
|
12379 </xsl:text> |
|
12380 <xsl:text> group.transform.baseVal.appendItem(transform); |
|
12381 </xsl:text> |
|
12382 <xsl:text> ["x", "y"].forEach((axis) => group.removeAttribute("svghmi_"+axis+"_offset")); |
|
12383 </xsl:text> |
|
12384 <xsl:text> }); |
|
12385 </xsl:text> |
|
12386 <xsl:text>} |
|
12387 </xsl:text> |
|
12388 <xsl:text> |
|
12389 </xsl:text> |
|
12390 <xsl:text>// Once connection established |
|
12391 </xsl:text> |
|
12392 <xsl:text>ws.onopen = function (evt) { |
|
12393 </xsl:text> |
|
12394 <xsl:text> apply_reference_frames(); |
|
12395 </xsl:text> |
|
12396 <xsl:text> init_widgets(); |
|
12397 </xsl:text> |
|
12398 <xsl:text> send_reset(); |
|
12399 </xsl:text> |
|
12400 <xsl:text> // show main page |
|
12401 </xsl:text> |
|
12402 <xsl:text> prepare_svg(); |
|
12403 </xsl:text> |
|
12404 <xsl:text> switch_page(default_page); |
|
12405 </xsl:text> |
|
12406 <xsl:text>}; |
|
12407 </xsl:text> |
|
12408 <xsl:text> |
|
12409 </xsl:text> |
|
12410 <xsl:text>ws.onclose = function (evt) { |
|
12411 </xsl:text> |
|
12412 <xsl:text> // TODO : add visible notification while waiting for reload |
|
12413 </xsl:text> |
|
12414 <xsl:text> console.log("Connection closed. code:"+evt.code+" reason:"+evt.reason+" wasClean:"+evt.wasClean+" Reload in 10s."); |
|
12415 </xsl:text> |
|
12416 <xsl:text> // TODO : re-enable auto reload when not in debug |
|
12417 </xsl:text> |
|
12418 <xsl:text> //window.setTimeout(() => location.reload(true), 10000); |
|
12419 </xsl:text> |
|
12420 <xsl:text> alert("Connection closed. code:"+evt.code+" reason:"+evt.reason+" wasClean:"+evt.wasClean+"."); |
|
12421 </xsl:text> |
|
12422 <xsl:text> |
|
12423 </xsl:text> |
|
12424 <xsl:text>}; |
|
12425 </xsl:text> |
12586 </xsl:text> |
12426 <xsl:text> |
12587 <xsl:text> |
12427 </xsl:text> |
12588 </xsl:text> |
12428 <xsl:text>const xmlns = "http://www.w3.org/2000/svg"; |
12589 <xsl:text>const xmlns = "http://www.w3.org/2000/svg"; |
12429 </xsl:text> |
12590 </xsl:text> |