525 <xsl:value-of select="@id"/> |
525 <xsl:value-of select="@id"/> |
526 <xsl:text> |
526 <xsl:text> |
527 </xsl:text> |
527 </xsl:text> |
528 </xsl:for-each> |
528 </xsl:for-each> |
529 </xsl:template> |
529 </xsl:template> |
530 <xsl:template match="/"> |
530 <xsl:template mode="hmi_elements" match="svg:*"> |
531 <xsl:comment> |
531 <xsl:variable name="widget" select="func:parselabel(@inkscape:label)/widget"/> |
532 <xsl:text>Made with SVGHMI. https://beremiz.org</xsl:text> |
532 <xsl:variable name="eltid" select="@id"/> |
533 </xsl:comment> |
533 <xsl:text> "</xsl:text> |
534 <xsl:comment> |
534 <xsl:value-of select="@id"/> |
535 <xsl:text> |
535 <xsl:text>": { |
536 </xsl:text> |
536 </xsl:text> |
537 <xsl:text>debug_hmitree: |
537 <xsl:text> type: "</xsl:text> |
538 </xsl:text> |
538 <xsl:value-of select="$widget/@type"/> |
539 <xsl:call-template name="debug_hmitree"/> |
539 <xsl:text>", |
540 <xsl:text> |
540 </xsl:text> |
541 </xsl:text> |
541 <xsl:text> args: [ |
542 </xsl:comment> |
542 </xsl:text> |
543 <xsl:comment> |
543 <xsl:for-each select="$widget/arg"> |
544 <xsl:text> |
544 <xsl:text> "</xsl:text> |
545 </xsl:text> |
545 <xsl:value-of select="@value"/> |
546 <xsl:text>debug_geometry: |
|
547 </xsl:text> |
|
548 <xsl:call-template name="debug_geometry"/> |
|
549 <xsl:text> |
|
550 </xsl:text> |
|
551 </xsl:comment> |
|
552 <xsl:comment> |
|
553 <xsl:text> |
|
554 </xsl:text> |
|
555 <xsl:text>debug_detachables: |
|
556 </xsl:text> |
|
557 <xsl:call-template name="debug_detachables"/> |
|
558 <xsl:text> |
|
559 </xsl:text> |
|
560 </xsl:comment> |
|
561 <xsl:comment> |
|
562 <xsl:text> |
|
563 </xsl:text> |
|
564 <xsl:text>debug_unlink: |
|
565 </xsl:text> |
|
566 <xsl:call-template name="debug_unlink"/> |
|
567 <xsl:text> |
|
568 </xsl:text> |
|
569 </xsl:comment> |
|
570 <html xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/1999/xhtml"> |
|
571 <head/> |
|
572 <body style="margin:0;overflow:hidden;"> |
|
573 <xsl:copy-of select="$result_svg"/> |
|
574 <script> |
|
575 <xsl:call-template name="scripts"/> |
|
576 </script> |
|
577 </body> |
|
578 </html> |
|
579 </xsl:template> |
|
580 <xsl:template name="scripts"> |
|
581 <xsl:text>//(function(){ |
|
582 </xsl:text> |
|
583 <xsl:text> |
|
584 </xsl:text> |
|
585 <xsl:text>id = idstr => document.getElementById(idstr); |
|
586 </xsl:text> |
|
587 <xsl:text> |
|
588 </xsl:text> |
|
589 <xsl:text>var hmi_hash = [</xsl:text> |
|
590 <xsl:value-of select="$hmitree/@hash"/> |
|
591 <xsl:text>]; |
|
592 </xsl:text> |
|
593 <xsl:text>var hmi_widgets = { |
|
594 </xsl:text> |
|
595 <xsl:for-each select="$hmi_elements"> |
|
596 <xsl:variable name="widget" select="func:parselabel(@inkscape:label)/widget"/> |
|
597 <xsl:variable name="eltid" select="@id"/> |
|
598 <xsl:text> "</xsl:text> |
|
599 <xsl:value-of select="@id"/> |
|
600 <xsl:text>": { |
|
601 </xsl:text> |
|
602 <xsl:text> type: "</xsl:text> |
|
603 <xsl:value-of select="$widget/@type"/> |
|
604 <xsl:text>", |
|
605 </xsl:text> |
|
606 <xsl:text> args: [ |
|
607 </xsl:text> |
|
608 <xsl:for-each select="$widget/arg"> |
|
609 <xsl:text> "</xsl:text> |
|
610 <xsl:value-of select="@value"/> |
|
611 <xsl:text>"</xsl:text> |
|
612 <xsl:if test="position()!=last()"> |
|
613 <xsl:text>,</xsl:text> |
|
614 </xsl:if> |
|
615 <xsl:text> |
|
616 </xsl:text> |
|
617 </xsl:for-each> |
|
618 <xsl:text> ], |
|
619 </xsl:text> |
|
620 <xsl:text> indexes: [ |
|
621 </xsl:text> |
|
622 <xsl:for-each select="$widget/path"> |
|
623 <xsl:choose> |
|
624 <xsl:when test="not(@index)"> |
|
625 <xsl:message terminate="no"> |
|
626 <xsl:text>Widget </xsl:text> |
|
627 <xsl:value-of select="$widget/@type"/> |
|
628 <xsl:text> id="</xsl:text> |
|
629 <xsl:value-of select="$eltid"/> |
|
630 <xsl:text>" : No match for path "</xsl:text> |
|
631 <xsl:value-of select="@value"/> |
|
632 <xsl:text>" in HMI tree</xsl:text> |
|
633 </xsl:message> |
|
634 </xsl:when> |
|
635 <xsl:otherwise> |
|
636 <xsl:text> </xsl:text> |
|
637 <xsl:value-of select="@index"/> |
|
638 <xsl:if test="position()!=last()"> |
|
639 <xsl:text>,</xsl:text> |
|
640 </xsl:if> |
|
641 <xsl:text> |
|
642 </xsl:text> |
|
643 </xsl:otherwise> |
|
644 </xsl:choose> |
|
645 </xsl:for-each> |
|
646 <xsl:text> ], |
|
647 </xsl:text> |
|
648 <xsl:text> element: id("</xsl:text> |
|
649 <xsl:value-of select="@id"/> |
|
650 <xsl:text>"), |
|
651 </xsl:text> |
|
652 <xsl:apply-templates mode="widget_defs" select="$widget"> |
|
653 <xsl:with-param name="hmi_element" select="."/> |
|
654 </xsl:apply-templates> |
|
655 <xsl:text> }</xsl:text> |
|
656 <xsl:if test="position()!=last()"> |
|
657 <xsl:text>,</xsl:text> |
|
658 </xsl:if> |
|
659 <xsl:text> |
|
660 </xsl:text> |
|
661 </xsl:for-each> |
|
662 <xsl:text>} |
|
663 </xsl:text> |
|
664 <xsl:text> |
|
665 </xsl:text> |
|
666 <xsl:text>var heartbeat_index = </xsl:text> |
|
667 <xsl:value-of select="$indexed_hmitree/*[@hmipath = '/HEARTBEAT']/@index"/> |
|
668 <xsl:text>; |
|
669 </xsl:text> |
|
670 <xsl:text> |
|
671 </xsl:text> |
|
672 <xsl:text>var hmitree_types = [ |
|
673 </xsl:text> |
|
674 <xsl:for-each select="$indexed_hmitree/*"> |
|
675 <xsl:text> /* </xsl:text> |
|
676 <xsl:value-of select="@index"/> |
|
677 <xsl:text> </xsl:text> |
|
678 <xsl:value-of select="@hmipath"/> |
|
679 <xsl:text> */ "</xsl:text> |
|
680 <xsl:value-of select="substring(local-name(), 5)"/> |
|
681 <xsl:text>"</xsl:text> |
546 <xsl:text>"</xsl:text> |
682 <xsl:if test="position()!=last()"> |
547 <xsl:if test="position()!=last()"> |
683 <xsl:text>,</xsl:text> |
548 <xsl:text>,</xsl:text> |
684 </xsl:if> |
549 </xsl:if> |
685 <xsl:text> |
550 <xsl:text> |
686 </xsl:text> |
551 </xsl:text> |
687 </xsl:for-each> |
552 </xsl:for-each> |
688 <xsl:text>] |
553 <xsl:text> ], |
689 </xsl:text> |
554 </xsl:text> |
690 <xsl:text> |
555 <xsl:text> indexes: [ |
691 </xsl:text> |
556 </xsl:text> |
692 <xsl:text>var detachable_elements = { |
557 <xsl:for-each select="$widget/path"> |
693 </xsl:text> |
558 <xsl:choose> |
694 <xsl:for-each select="$detachable_elements"> |
559 <xsl:when test="not(@index)"> |
695 <xsl:text> "</xsl:text> |
560 <xsl:message terminate="no"> |
696 <xsl:value-of select="@id"/> |
561 <xsl:text>Widget </xsl:text> |
697 <xsl:text>":[id("</xsl:text> |
562 <xsl:value-of select="$widget/@type"/> |
698 <xsl:value-of select="@id"/> |
563 <xsl:text> id="</xsl:text> |
699 <xsl:text>"), id("</xsl:text> |
564 <xsl:value-of select="$eltid"/> |
700 <xsl:value-of select="../@id"/> |
565 <xsl:text>" : No match for path "</xsl:text> |
701 <xsl:text>")]</xsl:text> |
566 <xsl:value-of select="@value"/> |
702 <xsl:if test="position()!=last()"> |
567 <xsl:text>" in HMI tree</xsl:text> |
703 <xsl:text>,</xsl:text> |
568 </xsl:message> |
704 </xsl:if> |
569 </xsl:when> |
705 <xsl:text> |
570 <xsl:otherwise> |
706 </xsl:text> |
571 <xsl:text> </xsl:text> |
|
572 <xsl:value-of select="@index"/> |
|
573 <xsl:if test="position()!=last()"> |
|
574 <xsl:text>,</xsl:text> |
|
575 </xsl:if> |
|
576 <xsl:text> |
|
577 </xsl:text> |
|
578 </xsl:otherwise> |
|
579 </xsl:choose> |
707 </xsl:for-each> |
580 </xsl:for-each> |
708 <xsl:text>} |
581 <xsl:text> ], |
709 </xsl:text> |
582 </xsl:text> |
710 <xsl:text> |
583 <xsl:text> element: id("</xsl:text> |
711 </xsl:text> |
584 <xsl:value-of select="@id"/> |
712 <xsl:text>var page_desc = { |
585 <xsl:text>"), |
713 </xsl:text> |
586 </xsl:text> |
714 <xsl:apply-templates mode="page_desc" select="$hmi_pages"/> |
587 <xsl:apply-templates mode="widget_defs" select="$widget"> |
715 <xsl:text>} |
588 <xsl:with-param name="hmi_element" select="."/> |
716 </xsl:text> |
589 </xsl:apply-templates> |
717 <xsl:text> |
590 <xsl:text> }</xsl:text> |
718 </xsl:text> |
591 <xsl:if test="position()!=last()"> |
719 <xsl:text>var default_page = "</xsl:text> |
592 <xsl:text>,</xsl:text> |
720 <xsl:value-of select="$default_page"/> |
593 </xsl:if> |
721 <xsl:text>"; |
594 <xsl:text> |
722 </xsl:text> |
|
723 <xsl:text>var svg_root = id("</xsl:text> |
|
724 <xsl:value-of select="/svg:svg/@id"/> |
|
725 <xsl:text>"); |
|
726 </xsl:text> |
|
727 <xsl:text>// svghmi.js |
|
728 </xsl:text> |
|
729 <xsl:text> |
|
730 </xsl:text> |
|
731 <xsl:text>var cache = hmitree_types.map(_ignored => undefined); |
|
732 </xsl:text> |
|
733 <xsl:text>var updates = {}; |
|
734 </xsl:text> |
|
735 <xsl:text> |
|
736 </xsl:text> |
|
737 <xsl:text>function dispatch_value_to_widget(widget, index, value, oldval) { |
|
738 </xsl:text> |
|
739 <xsl:text> try { |
|
740 </xsl:text> |
|
741 <xsl:text> let idx = widget.offset ? index - widget.offset : index; |
|
742 </xsl:text> |
|
743 <xsl:text> let idxidx = widget.indexes.indexOf(idx); |
|
744 </xsl:text> |
|
745 <xsl:text> let d = widget.dispatch; |
|
746 </xsl:text> |
|
747 <xsl:text> console.log(index, idx, idxidx, value); |
|
748 </xsl:text> |
|
749 <xsl:text> if(typeof(d) == "function" && idxidx == 0){ |
|
750 </xsl:text> |
|
751 <xsl:text> d.call(widget, value, oldval); |
|
752 </xsl:text> |
|
753 <xsl:text> } |
|
754 </xsl:text> |
|
755 <xsl:text> else if(typeof(d) == "object" && d.length >= idxidx){ |
|
756 </xsl:text> |
|
757 <xsl:text> d[idxidx].call(widget, value, oldval); |
|
758 </xsl:text> |
|
759 <xsl:text> } |
|
760 </xsl:text> |
|
761 <xsl:text> /* else dispatch_0, ..., dispatch_n ? */ |
|
762 </xsl:text> |
|
763 <xsl:text> /*else { |
|
764 </xsl:text> |
|
765 <xsl:text> throw new Error("Dunno how to dispatch to widget at index = " + index); |
|
766 </xsl:text> |
|
767 <xsl:text> }*/ |
|
768 </xsl:text> |
|
769 <xsl:text> } catch(err) { |
|
770 </xsl:text> |
|
771 <xsl:text> console.log(err); |
|
772 </xsl:text> |
|
773 <xsl:text> } |
|
774 </xsl:text> |
|
775 <xsl:text>} |
|
776 </xsl:text> |
|
777 <xsl:text> |
|
778 </xsl:text> |
|
779 <xsl:text>function dispatch_value(index, value) { |
|
780 </xsl:text> |
|
781 <xsl:text> let widgets = subscribers[index]; |
|
782 </xsl:text> |
|
783 <xsl:text> |
|
784 </xsl:text> |
|
785 <xsl:text> let oldval = cache[index]; |
|
786 </xsl:text> |
|
787 <xsl:text> cache[index] = value; |
|
788 </xsl:text> |
|
789 <xsl:text> |
|
790 </xsl:text> |
|
791 <xsl:text> if(widgets.size > 0) { |
|
792 </xsl:text> |
|
793 <xsl:text> for(let widget of widgets){ |
|
794 </xsl:text> |
|
795 <xsl:text> dispatch_value_to_widget(widget, index, value, oldval); |
|
796 </xsl:text> |
|
797 <xsl:text> } |
|
798 </xsl:text> |
|
799 <xsl:text> } |
|
800 </xsl:text> |
|
801 <xsl:text>}; |
|
802 </xsl:text> |
|
803 <xsl:text> |
|
804 </xsl:text> |
|
805 <xsl:text>function init_widgets() { |
|
806 </xsl:text> |
|
807 <xsl:text> Object.keys(hmi_widgets).forEach(function(id) { |
|
808 </xsl:text> |
|
809 <xsl:text> let widget = hmi_widgets[id]; |
|
810 </xsl:text> |
|
811 <xsl:text> let init = widget.init; |
|
812 </xsl:text> |
|
813 <xsl:text> if(typeof(init) == "function"){ |
|
814 </xsl:text> |
|
815 <xsl:text> try { |
|
816 </xsl:text> |
|
817 <xsl:text> init.call(widget); |
|
818 </xsl:text> |
|
819 <xsl:text> } catch(err) { |
|
820 </xsl:text> |
|
821 <xsl:text> console.log(err); |
|
822 </xsl:text> |
|
823 <xsl:text> } |
|
824 </xsl:text> |
|
825 <xsl:text> } |
|
826 </xsl:text> |
|
827 <xsl:text> }); |
|
828 </xsl:text> |
|
829 <xsl:text>}; |
|
830 </xsl:text> |
|
831 <xsl:text> |
|
832 </xsl:text> |
|
833 <xsl:text>// Open WebSocket to relative "/ws" address |
|
834 </xsl:text> |
|
835 <xsl:text>var ws = new WebSocket(window.location.href.replace(/^http(s?:\/\/[^\/]*)\/.*$/, 'ws$1/ws')); |
|
836 </xsl:text> |
|
837 <xsl:text>ws.binaryType = 'arraybuffer'; |
|
838 </xsl:text> |
|
839 <xsl:text> |
|
840 </xsl:text> |
|
841 <xsl:text>const dvgetters = { |
|
842 </xsl:text> |
|
843 <xsl:text> INT: (dv,offset) => [dv.getInt16(offset, true), 2], |
|
844 </xsl:text> |
|
845 <xsl:text> BOOL: (dv,offset) => [dv.getInt8(offset, true), 1], |
|
846 </xsl:text> |
|
847 <xsl:text> STRING: (dv, offset) => { |
|
848 </xsl:text> |
|
849 <xsl:text> size = dv.getInt8(offset); |
|
850 </xsl:text> |
|
851 <xsl:text> return [ |
|
852 </xsl:text> |
|
853 <xsl:text> String.fromCharCode.apply(null, new Uint8Array( |
|
854 </xsl:text> |
|
855 <xsl:text> dv.buffer, /* original buffer */ |
|
856 </xsl:text> |
|
857 <xsl:text> offset + 1, /* string starts after size*/ |
|
858 </xsl:text> |
|
859 <xsl:text> size /* size of string */ |
|
860 </xsl:text> |
|
861 <xsl:text> )), size + 1]; /* total increment */ |
|
862 </xsl:text> |
|
863 <xsl:text> } |
|
864 </xsl:text> |
|
865 <xsl:text>}; |
|
866 </xsl:text> |
|
867 <xsl:text> |
|
868 </xsl:text> |
|
869 <xsl:text>// Apply updates recieved through ws.onmessage to subscribed widgets |
|
870 </xsl:text> |
|
871 <xsl:text>function apply_updates() { |
|
872 </xsl:text> |
|
873 <xsl:text> for(let index in updates){ |
|
874 </xsl:text> |
|
875 <xsl:text> // serving as a key, index becomes a string |
|
876 </xsl:text> |
|
877 <xsl:text> // -> pass Number(index) instead |
|
878 </xsl:text> |
|
879 <xsl:text> dispatch_value(Number(index), updates[index]); |
|
880 </xsl:text> |
|
881 <xsl:text> delete updates[index]; |
|
882 </xsl:text> |
|
883 <xsl:text> } |
|
884 </xsl:text> |
|
885 <xsl:text>} |
|
886 </xsl:text> |
|
887 <xsl:text> |
|
888 </xsl:text> |
|
889 <xsl:text>// Called on requestAnimationFrame, modifies DOM |
|
890 </xsl:text> |
|
891 <xsl:text>var requestAnimationFrameID = null; |
|
892 </xsl:text> |
|
893 <xsl:text>function animate() { |
|
894 </xsl:text> |
|
895 <xsl:text> // Do the page swith if any one pending |
|
896 </xsl:text> |
|
897 <xsl:text> if(current_subscribed_page != current_visible_page){ |
|
898 </xsl:text> |
|
899 <xsl:text> switch_visible_page(current_subscribed_page); |
|
900 </xsl:text> |
|
901 <xsl:text> } |
|
902 </xsl:text> |
|
903 <xsl:text> apply_updates(); |
|
904 </xsl:text> |
|
905 <xsl:text> requestAnimationFrameID = null; |
|
906 </xsl:text> |
|
907 <xsl:text>} |
|
908 </xsl:text> |
|
909 <xsl:text> |
|
910 </xsl:text> |
|
911 <xsl:text>function requestHMIAnimation() { |
|
912 </xsl:text> |
|
913 <xsl:text> if(requestAnimationFrameID == null){ |
|
914 </xsl:text> |
|
915 <xsl:text> requestAnimationFrameID = window.requestAnimationFrame(animate); |
|
916 </xsl:text> |
|
917 <xsl:text> } |
|
918 </xsl:text> |
|
919 <xsl:text>} |
|
920 </xsl:text> |
|
921 <xsl:text> |
|
922 </xsl:text> |
|
923 <xsl:text>// Message reception handler |
|
924 </xsl:text> |
|
925 <xsl:text>// Hash is verified and HMI values updates resulting from binary parsing |
|
926 </xsl:text> |
|
927 <xsl:text>// are stored until browser can compute next frame, DOM is left untouched |
|
928 </xsl:text> |
|
929 <xsl:text>ws.onmessage = function (evt) { |
|
930 </xsl:text> |
|
931 <xsl:text> |
|
932 </xsl:text> |
|
933 <xsl:text> let data = evt.data; |
|
934 </xsl:text> |
|
935 <xsl:text> let dv = new DataView(data); |
|
936 </xsl:text> |
|
937 <xsl:text> let i = 0; |
|
938 </xsl:text> |
|
939 <xsl:text> try { |
|
940 </xsl:text> |
|
941 <xsl:text> for(let hash_int of hmi_hash) { |
|
942 </xsl:text> |
|
943 <xsl:text> if(hash_int != dv.getUint8(i)){ |
|
944 </xsl:text> |
|
945 <xsl:text> throw new Error("Hash doesn't match"); |
|
946 </xsl:text> |
|
947 <xsl:text> }; |
|
948 </xsl:text> |
|
949 <xsl:text> i++; |
|
950 </xsl:text> |
|
951 <xsl:text> }; |
|
952 </xsl:text> |
|
953 <xsl:text> |
|
954 </xsl:text> |
|
955 <xsl:text> while(i < data.byteLength){ |
|
956 </xsl:text> |
|
957 <xsl:text> let index = dv.getUint32(i, true); |
|
958 </xsl:text> |
|
959 <xsl:text> i += 4; |
|
960 </xsl:text> |
|
961 <xsl:text> let iectype = hmitree_types[index]; |
|
962 </xsl:text> |
|
963 <xsl:text> if(iectype != undefined){ |
|
964 </xsl:text> |
|
965 <xsl:text> let dvgetter = dvgetters[iectype]; |
|
966 </xsl:text> |
|
967 <xsl:text> let [value, bytesize] = dvgetter(dv,i); |
|
968 </xsl:text> |
|
969 <xsl:text> updates[index] = value; |
|
970 </xsl:text> |
|
971 <xsl:text> i += bytesize; |
|
972 </xsl:text> |
|
973 <xsl:text> } else { |
|
974 </xsl:text> |
|
975 <xsl:text> throw new Error("Unknown index "+index); |
|
976 </xsl:text> |
|
977 <xsl:text> } |
|
978 </xsl:text> |
|
979 <xsl:text> }; |
|
980 </xsl:text> |
|
981 <xsl:text> // register for rendering on next frame, since there are updates |
|
982 </xsl:text> |
|
983 <xsl:text> requestHMIAnimation(); |
|
984 </xsl:text> |
|
985 <xsl:text> } catch(err) { |
|
986 </xsl:text> |
|
987 <xsl:text> // 1003 is for "Unsupported Data" |
|
988 </xsl:text> |
|
989 <xsl:text> // ws.close(1003, err.message); |
|
990 </xsl:text> |
|
991 <xsl:text> |
|
992 </xsl:text> |
|
993 <xsl:text> // TODO : remove debug alert ? |
|
994 </xsl:text> |
|
995 <xsl:text> alert("Error : "+err.message+"\nHMI will be reloaded."); |
|
996 </xsl:text> |
|
997 <xsl:text> |
|
998 </xsl:text> |
|
999 <xsl:text> // force reload ignoring cache |
|
1000 </xsl:text> |
|
1001 <xsl:text> location.reload(true); |
|
1002 </xsl:text> |
|
1003 <xsl:text> } |
|
1004 </xsl:text> |
|
1005 <xsl:text>}; |
|
1006 </xsl:text> |
|
1007 <xsl:text> |
|
1008 </xsl:text> |
|
1009 <xsl:text> |
|
1010 </xsl:text> |
|
1011 <xsl:text>function send_blob(data) { |
|
1012 </xsl:text> |
|
1013 <xsl:text> if(data.length > 0) { |
|
1014 </xsl:text> |
|
1015 <xsl:text> ws.send(new Blob([new Uint8Array(hmi_hash)].concat(data))); |
|
1016 </xsl:text> |
|
1017 <xsl:text> }; |
|
1018 </xsl:text> |
|
1019 <xsl:text>}; |
|
1020 </xsl:text> |
|
1021 <xsl:text> |
|
1022 </xsl:text> |
|
1023 <xsl:text>const typedarray_types = { |
|
1024 </xsl:text> |
|
1025 <xsl:text> INT: (number) => new Int16Array([number]), |
|
1026 </xsl:text> |
|
1027 <xsl:text> BOOL: (truth) => new Int16Array([truth]), |
|
1028 </xsl:text> |
|
1029 <xsl:text> STRING: (str) => { |
|
1030 </xsl:text> |
|
1031 <xsl:text> // beremiz default string max size is 128 |
|
1032 </xsl:text> |
|
1033 <xsl:text> str = str.slice(0,128); |
|
1034 </xsl:text> |
|
1035 <xsl:text> binary = new Uint8Array(str.length + 1); |
|
1036 </xsl:text> |
|
1037 <xsl:text> binary[0] = str.length; |
|
1038 </xsl:text> |
|
1039 <xsl:text> for(var i = 0; i < str.length; i++){ |
|
1040 </xsl:text> |
|
1041 <xsl:text> binary[i+1] = str.charCodeAt(i); |
|
1042 </xsl:text> |
|
1043 <xsl:text> } |
|
1044 </xsl:text> |
|
1045 <xsl:text> return binary; |
|
1046 </xsl:text> |
|
1047 <xsl:text> } |
|
1048 </xsl:text> |
|
1049 <xsl:text> /* TODO */ |
|
1050 </xsl:text> |
|
1051 <xsl:text>}; |
|
1052 </xsl:text> |
|
1053 <xsl:text> |
|
1054 </xsl:text> |
|
1055 <xsl:text>function send_reset() { |
|
1056 </xsl:text> |
|
1057 <xsl:text> send_blob(new Uint8Array([1])); /* reset = 1 */ |
|
1058 </xsl:text> |
|
1059 <xsl:text>}; |
|
1060 </xsl:text> |
|
1061 <xsl:text> |
|
1062 </xsl:text> |
|
1063 <xsl:text>// subscription state, as it should be in hmi server |
|
1064 </xsl:text> |
|
1065 <xsl:text>// hmitree indexed array of integers |
|
1066 </xsl:text> |
|
1067 <xsl:text>var subscriptions = hmitree_types.map(_ignored => 0); |
|
1068 </xsl:text> |
|
1069 <xsl:text> |
|
1070 </xsl:text> |
|
1071 <xsl:text>// subscription state as needed by widget now |
|
1072 </xsl:text> |
|
1073 <xsl:text>// hmitree indexed array of Sets of widgets objects |
|
1074 </xsl:text> |
|
1075 <xsl:text>var subscribers = hmitree_types.map(_ignored => new Set()); |
|
1076 </xsl:text> |
|
1077 <xsl:text> |
|
1078 </xsl:text> |
|
1079 <xsl:text>// artificially subscribe the watchdog widget to "/heartbeat" hmi variable |
|
1080 </xsl:text> |
|
1081 <xsl:text>// Since dispatch directly calls change_hmi_value, |
|
1082 </xsl:text> |
|
1083 <xsl:text>// PLC will periodically send variable at given frequency |
|
1084 </xsl:text> |
|
1085 <xsl:text>subscribers[heartbeat_index].add({ |
|
1086 </xsl:text> |
|
1087 <xsl:text> /* type: "Watchdog", */ |
|
1088 </xsl:text> |
|
1089 <xsl:text> frequency: 1, |
|
1090 </xsl:text> |
|
1091 <xsl:text> indexes: [heartbeat_index], |
|
1092 </xsl:text> |
|
1093 <xsl:text> dispatch: function(value) { |
|
1094 </xsl:text> |
|
1095 <xsl:text> // console.log("Heartbeat" + value); |
|
1096 </xsl:text> |
|
1097 <xsl:text> change_hmi_value(heartbeat_index, "+1"); |
|
1098 </xsl:text> |
|
1099 <xsl:text> } |
|
1100 </xsl:text> |
|
1101 <xsl:text>}); |
|
1102 </xsl:text> |
|
1103 <xsl:text> |
|
1104 </xsl:text> |
|
1105 <xsl:text>function update_subscriptions() { |
|
1106 </xsl:text> |
|
1107 <xsl:text> let delta = []; |
|
1108 </xsl:text> |
|
1109 <xsl:text> for(let index = 0; index < subscribers.length; index++){ |
|
1110 </xsl:text> |
|
1111 <xsl:text> let widgets = subscribers[index]; |
|
1112 </xsl:text> |
|
1113 <xsl:text> |
|
1114 </xsl:text> |
|
1115 <xsl:text> // periods are in ms |
|
1116 </xsl:text> |
|
1117 <xsl:text> let previous_period = subscriptions[index]; |
|
1118 </xsl:text> |
|
1119 <xsl:text> |
|
1120 </xsl:text> |
|
1121 <xsl:text> // subscribing with a zero period is unsubscribing |
|
1122 </xsl:text> |
|
1123 <xsl:text> let new_period = 0; |
|
1124 </xsl:text> |
|
1125 <xsl:text> if(widgets.size > 0) { |
|
1126 </xsl:text> |
|
1127 <xsl:text> let maxfreq = 0; |
|
1128 </xsl:text> |
|
1129 <xsl:text> for(let widget of widgets) |
|
1130 </xsl:text> |
|
1131 <xsl:text> if(maxfreq < widget.frequency) |
|
1132 </xsl:text> |
|
1133 <xsl:text> maxfreq = widget.frequency; |
|
1134 </xsl:text> |
|
1135 <xsl:text> |
|
1136 </xsl:text> |
|
1137 <xsl:text> if(maxfreq != 0) |
|
1138 </xsl:text> |
|
1139 <xsl:text> new_period = 1000/maxfreq; |
|
1140 </xsl:text> |
|
1141 <xsl:text> } |
|
1142 </xsl:text> |
|
1143 <xsl:text> |
|
1144 </xsl:text> |
|
1145 <xsl:text> if(previous_period != new_period) { |
|
1146 </xsl:text> |
|
1147 <xsl:text> subscriptions[index] = new_period; |
|
1148 </xsl:text> |
|
1149 <xsl:text> delta.push( |
|
1150 </xsl:text> |
|
1151 <xsl:text> new Uint8Array([2]), /* subscribe = 2 */ |
|
1152 </xsl:text> |
|
1153 <xsl:text> new Uint32Array([index]), |
|
1154 </xsl:text> |
|
1155 <xsl:text> new Uint16Array([new_period])); |
|
1156 </xsl:text> |
|
1157 <xsl:text> } |
|
1158 </xsl:text> |
|
1159 <xsl:text> } |
|
1160 </xsl:text> |
|
1161 <xsl:text> send_blob(delta); |
|
1162 </xsl:text> |
|
1163 <xsl:text>}; |
|
1164 </xsl:text> |
|
1165 <xsl:text> |
|
1166 </xsl:text> |
|
1167 <xsl:text>function send_hmi_value(index, value) { |
|
1168 </xsl:text> |
|
1169 <xsl:text> let iectype = hmitree_types[index]; |
|
1170 </xsl:text> |
|
1171 <xsl:text> let tobinary = typedarray_types[iectype]; |
|
1172 </xsl:text> |
|
1173 <xsl:text> send_blob([ |
|
1174 </xsl:text> |
|
1175 <xsl:text> new Uint8Array([0]), /* setval = 0 */ |
|
1176 </xsl:text> |
|
1177 <xsl:text> new Uint32Array([index]), |
|
1178 </xsl:text> |
|
1179 <xsl:text> tobinary(value)]); |
|
1180 </xsl:text> |
|
1181 <xsl:text> |
|
1182 </xsl:text> |
|
1183 <xsl:text> cache[index] = value; |
|
1184 </xsl:text> |
|
1185 <xsl:text>}; |
|
1186 </xsl:text> |
|
1187 <xsl:text> |
|
1188 </xsl:text> |
|
1189 <xsl:text>function change_hmi_value(index, opstr) { |
|
1190 </xsl:text> |
|
1191 <xsl:text> let op = opstr[0]; |
|
1192 </xsl:text> |
|
1193 <xsl:text> let given_val = opstr.slice(1); |
|
1194 </xsl:text> |
|
1195 <xsl:text> let old_val = cache[index] |
|
1196 </xsl:text> |
|
1197 <xsl:text> let new_val; |
|
1198 </xsl:text> |
|
1199 <xsl:text> switch(op){ |
|
1200 </xsl:text> |
|
1201 <xsl:text> case "=": |
|
1202 </xsl:text> |
|
1203 <xsl:text> eval("new_val"+opstr); |
|
1204 </xsl:text> |
|
1205 <xsl:text> break; |
|
1206 </xsl:text> |
|
1207 <xsl:text> case "+": |
|
1208 </xsl:text> |
|
1209 <xsl:text> case "-": |
|
1210 </xsl:text> |
|
1211 <xsl:text> case "*": |
|
1212 </xsl:text> |
|
1213 <xsl:text> case "/": |
|
1214 </xsl:text> |
|
1215 <xsl:text> if(old_val != undefined) |
|
1216 </xsl:text> |
|
1217 <xsl:text> new_val = eval("old_val"+opstr); |
|
1218 </xsl:text> |
|
1219 <xsl:text> break; |
|
1220 </xsl:text> |
|
1221 <xsl:text> } |
|
1222 </xsl:text> |
|
1223 <xsl:text> if(new_val != undefined && old_val != new_val) |
|
1224 </xsl:text> |
|
1225 <xsl:text> send_hmi_value(index, new_val); |
|
1226 </xsl:text> |
|
1227 <xsl:text> return new_val; |
|
1228 </xsl:text> |
|
1229 <xsl:text>} |
|
1230 </xsl:text> |
|
1231 <xsl:text> |
|
1232 </xsl:text> |
|
1233 <xsl:text>var current_visible_page; |
|
1234 </xsl:text> |
|
1235 <xsl:text>var current_subscribed_page; |
|
1236 </xsl:text> |
|
1237 <xsl:text> |
|
1238 </xsl:text> |
|
1239 <xsl:text>function prepare_svg() { |
|
1240 </xsl:text> |
|
1241 <xsl:text> for(let eltid in detachable_elements){ |
|
1242 </xsl:text> |
|
1243 <xsl:text> let [element,parent] = detachable_elements[eltid]; |
|
1244 </xsl:text> |
|
1245 <xsl:text> parent.removeChild(element); |
|
1246 </xsl:text> |
|
1247 <xsl:text> } |
|
1248 </xsl:text> |
|
1249 <xsl:text>}; |
|
1250 </xsl:text> |
|
1251 <xsl:text> |
|
1252 </xsl:text> |
|
1253 <xsl:text>function switch_page(page_name, page_index) { |
|
1254 </xsl:text> |
|
1255 <xsl:text> if(current_subscribed_page != current_visible_page){ |
|
1256 </xsl:text> |
|
1257 <xsl:text> /* page switch already going */ |
|
1258 </xsl:text> |
|
1259 <xsl:text> /* TODO LOG ERROR */ |
|
1260 </xsl:text> |
|
1261 <xsl:text> return; |
|
1262 </xsl:text> |
|
1263 <xsl:text> } else if(page_name == current_visible_page){ |
|
1264 </xsl:text> |
|
1265 <xsl:text> /* already in that page */ |
|
1266 </xsl:text> |
|
1267 <xsl:text> /* TODO LOG ERROR */ |
|
1268 </xsl:text> |
|
1269 <xsl:text> return; |
|
1270 </xsl:text> |
|
1271 <xsl:text> } |
|
1272 </xsl:text> |
|
1273 <xsl:text> switch_subscribed_page(page_name, page_index); |
|
1274 </xsl:text> |
|
1275 <xsl:text>}; |
|
1276 </xsl:text> |
|
1277 <xsl:text> |
|
1278 </xsl:text> |
|
1279 <xsl:text>function* chain(a,b){ |
|
1280 </xsl:text> |
|
1281 <xsl:text> yield* a; |
|
1282 </xsl:text> |
|
1283 <xsl:text> yield* b; |
|
1284 </xsl:text> |
|
1285 <xsl:text>}; |
|
1286 </xsl:text> |
|
1287 <xsl:text> |
|
1288 </xsl:text> |
|
1289 <xsl:text>function switch_subscribed_page(page_name, page_index) { |
|
1290 </xsl:text> |
|
1291 <xsl:text> let old_desc = page_desc[current_subscribed_page]; |
|
1292 </xsl:text> |
|
1293 <xsl:text> let new_desc = page_desc[page_name]; |
|
1294 </xsl:text> |
|
1295 <xsl:text> |
|
1296 </xsl:text> |
|
1297 <xsl:text> if(new_desc == undefined){ |
|
1298 </xsl:text> |
|
1299 <xsl:text> /* TODO LOG ERROR */ |
|
1300 </xsl:text> |
|
1301 <xsl:text> return; |
|
1302 </xsl:text> |
|
1303 <xsl:text> } |
|
1304 </xsl:text> |
|
1305 <xsl:text> |
|
1306 </xsl:text> |
|
1307 <xsl:text> if(page_index == undefined){ |
|
1308 </xsl:text> |
|
1309 <xsl:text> page_index = new_desc.page_index; |
|
1310 </xsl:text> |
|
1311 <xsl:text> } |
|
1312 </xsl:text> |
|
1313 <xsl:text> |
|
1314 </xsl:text> |
|
1315 <xsl:text> if(old_desc){ |
|
1316 </xsl:text> |
|
1317 <xsl:text> for(let widget of old_desc.absolute_widgets){ |
|
1318 </xsl:text> |
|
1319 <xsl:text> /* remove subsribers */ |
|
1320 </xsl:text> |
|
1321 <xsl:text> for(let index of widget.indexes){ |
|
1322 </xsl:text> |
|
1323 <xsl:text> subscribers[index].delete(widget); |
|
1324 </xsl:text> |
|
1325 <xsl:text> } |
|
1326 </xsl:text> |
|
1327 <xsl:text> } |
|
1328 </xsl:text> |
|
1329 <xsl:text> for(let widget of old_desc.relative_widgets){ |
|
1330 </xsl:text> |
|
1331 <xsl:text> /* remove subsribers */ |
|
1332 </xsl:text> |
|
1333 <xsl:text> for(let index of widget.indexes){ |
|
1334 </xsl:text> |
|
1335 <xsl:text> let idx = widget.offset ? index + widget.offset : index; |
|
1336 </xsl:text> |
|
1337 <xsl:text> subscribers[idx].delete(widget); |
|
1338 </xsl:text> |
|
1339 <xsl:text> } |
|
1340 </xsl:text> |
|
1341 <xsl:text> /* lose the offset */ |
|
1342 </xsl:text> |
|
1343 <xsl:text> delete widget.offset; |
|
1344 </xsl:text> |
|
1345 <xsl:text> } |
|
1346 </xsl:text> |
|
1347 <xsl:text> } |
|
1348 </xsl:text> |
|
1349 <xsl:text> for(let widget of new_desc.absolute_widgets){ |
|
1350 </xsl:text> |
|
1351 <xsl:text> /* add widget's subsribers */ |
|
1352 </xsl:text> |
|
1353 <xsl:text> for(let index of widget.indexes){ |
|
1354 </xsl:text> |
|
1355 <xsl:text> subscribers[index].add(widget); |
|
1356 </xsl:text> |
|
1357 <xsl:text> } |
|
1358 </xsl:text> |
|
1359 <xsl:text> } |
|
1360 </xsl:text> |
|
1361 <xsl:text> var new_offset = page_index == undefined ? 0 : page_index - new_desc.page_index; |
|
1362 </xsl:text> |
|
1363 <xsl:text> for(let widget of new_desc.relative_widgets){ |
|
1364 </xsl:text> |
|
1365 <xsl:text> /* set the offset because relative */ |
|
1366 </xsl:text> |
|
1367 <xsl:text> widget.offset = new_offset; |
|
1368 </xsl:text> |
|
1369 <xsl:text> /* add widget's subsribers */ |
|
1370 </xsl:text> |
|
1371 <xsl:text> for(let index of widget.indexes){ |
|
1372 </xsl:text> |
|
1373 <xsl:text> subscribers[index + new_offset].add(widget); |
|
1374 </xsl:text> |
|
1375 <xsl:text> } |
|
1376 </xsl:text> |
|
1377 <xsl:text> } |
|
1378 </xsl:text> |
|
1379 <xsl:text> |
|
1380 </xsl:text> |
|
1381 <xsl:text> update_subscriptions(); |
|
1382 </xsl:text> |
|
1383 <xsl:text> |
|
1384 </xsl:text> |
|
1385 <xsl:text> current_subscribed_page = page_name; |
|
1386 </xsl:text> |
|
1387 <xsl:text> |
|
1388 </xsl:text> |
|
1389 <xsl:text> requestHMIAnimation(); |
|
1390 </xsl:text> |
|
1391 <xsl:text>} |
|
1392 </xsl:text> |
|
1393 <xsl:text> |
|
1394 </xsl:text> |
|
1395 <xsl:text>function switch_visible_page(page_name) { |
|
1396 </xsl:text> |
|
1397 <xsl:text> |
|
1398 </xsl:text> |
|
1399 <xsl:text> let old_desc = page_desc[current_visible_page]; |
|
1400 </xsl:text> |
|
1401 <xsl:text> let new_desc = page_desc[page_name]; |
|
1402 </xsl:text> |
|
1403 <xsl:text> |
|
1404 </xsl:text> |
|
1405 <xsl:text> if(old_desc){ |
|
1406 </xsl:text> |
|
1407 <xsl:text> for(let eltid in old_desc.required_detachables){ |
|
1408 </xsl:text> |
|
1409 <xsl:text> if(!(eltid in new_desc.required_detachables)){ |
|
1410 </xsl:text> |
|
1411 <xsl:text> let [element, parent] = old_desc.required_detachables[eltid]; |
|
1412 </xsl:text> |
|
1413 <xsl:text> parent.removeChild(element); |
|
1414 </xsl:text> |
|
1415 <xsl:text> } |
|
1416 </xsl:text> |
|
1417 <xsl:text> } |
|
1418 </xsl:text> |
|
1419 <xsl:text> for(let eltid in new_desc.required_detachables){ |
|
1420 </xsl:text> |
|
1421 <xsl:text> if(!(eltid in old_desc.required_detachables)){ |
|
1422 </xsl:text> |
|
1423 <xsl:text> let [element, parent] = new_desc.required_detachables[eltid]; |
|
1424 </xsl:text> |
|
1425 <xsl:text> parent.appendChild(element); |
|
1426 </xsl:text> |
|
1427 <xsl:text> } |
|
1428 </xsl:text> |
|
1429 <xsl:text> } |
|
1430 </xsl:text> |
|
1431 <xsl:text> }else{ |
|
1432 </xsl:text> |
|
1433 <xsl:text> for(let eltid in new_desc.required_detachables){ |
|
1434 </xsl:text> |
|
1435 <xsl:text> let [element, parent] = new_desc.required_detachables[eltid]; |
|
1436 </xsl:text> |
|
1437 <xsl:text> parent.appendChild(element); |
|
1438 </xsl:text> |
|
1439 <xsl:text> } |
|
1440 </xsl:text> |
|
1441 <xsl:text> } |
|
1442 </xsl:text> |
|
1443 <xsl:text> |
|
1444 </xsl:text> |
|
1445 <xsl:text> for(let widget of chain(new_desc.absolute_widgets,new_desc.relative_widgets)){ |
|
1446 </xsl:text> |
|
1447 <xsl:text> for(let index of widget.indexes){ |
|
1448 </xsl:text> |
|
1449 <xsl:text> /* dispatch current cache in newly opened page widgets */ |
|
1450 </xsl:text> |
|
1451 <xsl:text> let cached_val = cache[index]; |
|
1452 </xsl:text> |
|
1453 <xsl:text> if(cached_val != undefined) |
|
1454 </xsl:text> |
|
1455 <xsl:text> dispatch_value_to_widget(widget, index, cached_val, cached_val); |
|
1456 </xsl:text> |
|
1457 <xsl:text> } |
|
1458 </xsl:text> |
|
1459 <xsl:text> } |
|
1460 </xsl:text> |
|
1461 <xsl:text> |
|
1462 </xsl:text> |
|
1463 <xsl:text> svg_root.setAttribute('viewBox',new_desc.bbox.join(" ")); |
|
1464 </xsl:text> |
|
1465 <xsl:text> current_visible_page = page_name; |
|
1466 </xsl:text> |
|
1467 <xsl:text>}; |
|
1468 </xsl:text> |
|
1469 <xsl:text> |
|
1470 </xsl:text> |
|
1471 <xsl:text> |
|
1472 </xsl:text> |
|
1473 <xsl:text>// Once connection established |
|
1474 </xsl:text> |
|
1475 <xsl:text>ws.onopen = function (evt) { |
|
1476 </xsl:text> |
|
1477 <xsl:text> init_widgets(); |
|
1478 </xsl:text> |
|
1479 <xsl:text> send_reset(); |
|
1480 </xsl:text> |
|
1481 <xsl:text> // show main page |
|
1482 </xsl:text> |
|
1483 <xsl:text> prepare_svg(); |
|
1484 </xsl:text> |
|
1485 <xsl:text> switch_page(default_page); |
|
1486 </xsl:text> |
|
1487 <xsl:text>}; |
|
1488 </xsl:text> |
|
1489 <xsl:text> |
|
1490 </xsl:text> |
|
1491 <xsl:text>ws.onclose = function (evt) { |
|
1492 </xsl:text> |
|
1493 <xsl:text> // TODO : add visible notification while waiting for reload |
|
1494 </xsl:text> |
|
1495 <xsl:text> console.log("Connection closed. code:"+evt.code+" reason:"+evt.reason+" wasClean:"+evt.wasClean+" Reload in 10s."); |
|
1496 </xsl:text> |
|
1497 <xsl:text> // TODO : re-enable auto reload when not in debug |
|
1498 </xsl:text> |
|
1499 <xsl:text> //window.setTimeout(() => location.reload(true), 10000); |
|
1500 </xsl:text> |
|
1501 <xsl:text> alert("Connection closed. code:"+evt.code+" reason:"+evt.reason+" wasClean:"+evt.wasClean+"."); |
|
1502 </xsl:text> |
|
1503 <xsl:text> |
|
1504 </xsl:text> |
|
1505 <xsl:text>}; |
|
1506 </xsl:text> |
|
1507 <xsl:text>//})(); |
|
1508 </xsl:text> |
595 </xsl:text> |
1509 </xsl:template> |
596 </xsl:template> |
1510 <xsl:template name="defs_by_labels"> |
597 <xsl:template name="defs_by_labels"> |
1511 <xsl:param name="labels" select="''"/> |
598 <xsl:param name="labels" select="''"/> |
1512 <xsl:param name="mandatory" select="'yes'"/> |
599 <xsl:param name="mandatory" select="'yes'"/> |
1534 <xsl:text>"), |
621 <xsl:text>"), |
1535 </xsl:text> |
622 </xsl:text> |
1536 </xsl:otherwise> |
623 </xsl:otherwise> |
1537 </xsl:choose> |
624 </xsl:choose> |
1538 </xsl:for-each> |
625 </xsl:for-each> |
|
626 </xsl:template> |
|
627 <xsl:template match="/"> |
|
628 <xsl:comment> |
|
629 <xsl:text>Made with SVGHMI. https://beremiz.org</xsl:text> |
|
630 </xsl:comment> |
|
631 <xsl:comment> |
|
632 <xsl:text> |
|
633 </xsl:text> |
|
634 <xsl:text>debug_hmitree: |
|
635 </xsl:text> |
|
636 <xsl:call-template name="debug_hmitree"/> |
|
637 <xsl:text> |
|
638 </xsl:text> |
|
639 </xsl:comment> |
|
640 <xsl:comment> |
|
641 <xsl:text> |
|
642 </xsl:text> |
|
643 <xsl:text>debug_geometry: |
|
644 </xsl:text> |
|
645 <xsl:call-template name="debug_geometry"/> |
|
646 <xsl:text> |
|
647 </xsl:text> |
|
648 </xsl:comment> |
|
649 <xsl:comment> |
|
650 <xsl:text> |
|
651 </xsl:text> |
|
652 <xsl:text>debug_detachables: |
|
653 </xsl:text> |
|
654 <xsl:call-template name="debug_detachables"/> |
|
655 <xsl:text> |
|
656 </xsl:text> |
|
657 </xsl:comment> |
|
658 <xsl:comment> |
|
659 <xsl:text> |
|
660 </xsl:text> |
|
661 <xsl:text>debug_unlink: |
|
662 </xsl:text> |
|
663 <xsl:call-template name="debug_unlink"/> |
|
664 <xsl:text> |
|
665 </xsl:text> |
|
666 </xsl:comment> |
|
667 <html xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/1999/xhtml"> |
|
668 <head/> |
|
669 <body style="margin:0;overflow:hidden;"> |
|
670 <xsl:copy-of select="$result_svg"/> |
|
671 <script> |
|
672 <xsl:call-template name="scripts"/> |
|
673 </script> |
|
674 </body> |
|
675 </html> |
|
676 </xsl:template> |
|
677 <xsl:template name="scripts"> |
|
678 <xsl:text>//(function(){ |
|
679 </xsl:text> |
|
680 <xsl:text> |
|
681 </xsl:text> |
|
682 <xsl:text>id = idstr => document.getElementById(idstr); |
|
683 </xsl:text> |
|
684 <xsl:text> |
|
685 </xsl:text> |
|
686 <xsl:text>var hmi_hash = [</xsl:text> |
|
687 <xsl:value-of select="$hmitree/@hash"/> |
|
688 <xsl:text>]; |
|
689 </xsl:text> |
|
690 <xsl:text>var hmi_widgets = { |
|
691 </xsl:text> |
|
692 <xsl:apply-templates mode="hmi_elements" select="$hmi_elements"/> |
|
693 <xsl:text>} |
|
694 </xsl:text> |
|
695 <xsl:text> |
|
696 </xsl:text> |
|
697 <xsl:text>var heartbeat_index = </xsl:text> |
|
698 <xsl:value-of select="$indexed_hmitree/*[@hmipath = '/HEARTBEAT']/@index"/> |
|
699 <xsl:text>; |
|
700 </xsl:text> |
|
701 <xsl:text> |
|
702 </xsl:text> |
|
703 <xsl:text>var hmitree_types = [ |
|
704 </xsl:text> |
|
705 <xsl:for-each select="$indexed_hmitree/*"> |
|
706 <xsl:text> /* </xsl:text> |
|
707 <xsl:value-of select="@index"/> |
|
708 <xsl:text> </xsl:text> |
|
709 <xsl:value-of select="@hmipath"/> |
|
710 <xsl:text> */ "</xsl:text> |
|
711 <xsl:value-of select="substring(local-name(), 5)"/> |
|
712 <xsl:text>"</xsl:text> |
|
713 <xsl:if test="position()!=last()"> |
|
714 <xsl:text>,</xsl:text> |
|
715 </xsl:if> |
|
716 <xsl:text> |
|
717 </xsl:text> |
|
718 </xsl:for-each> |
|
719 <xsl:text>] |
|
720 </xsl:text> |
|
721 <xsl:text> |
|
722 </xsl:text> |
|
723 <xsl:text>var detachable_elements = { |
|
724 </xsl:text> |
|
725 <xsl:for-each select="$detachable_elements"> |
|
726 <xsl:text> "</xsl:text> |
|
727 <xsl:value-of select="@id"/> |
|
728 <xsl:text>":[id("</xsl:text> |
|
729 <xsl:value-of select="@id"/> |
|
730 <xsl:text>"), id("</xsl:text> |
|
731 <xsl:value-of select="../@id"/> |
|
732 <xsl:text>")]</xsl:text> |
|
733 <xsl:if test="position()!=last()"> |
|
734 <xsl:text>,</xsl:text> |
|
735 </xsl:if> |
|
736 <xsl:text> |
|
737 </xsl:text> |
|
738 </xsl:for-each> |
|
739 <xsl:text>} |
|
740 </xsl:text> |
|
741 <xsl:text> |
|
742 </xsl:text> |
|
743 <xsl:text>var page_desc = { |
|
744 </xsl:text> |
|
745 <xsl:apply-templates mode="page_desc" select="$hmi_pages"/> |
|
746 <xsl:text>} |
|
747 </xsl:text> |
|
748 <xsl:text> |
|
749 </xsl:text> |
|
750 <xsl:text>var default_page = "</xsl:text> |
|
751 <xsl:value-of select="$default_page"/> |
|
752 <xsl:text>"; |
|
753 </xsl:text> |
|
754 <xsl:text>var svg_root = id("</xsl:text> |
|
755 <xsl:value-of select="/svg:svg/@id"/> |
|
756 <xsl:text>"); |
|
757 </xsl:text> |
|
758 <xsl:text>// svghmi.js |
|
759 </xsl:text> |
|
760 <xsl:text> |
|
761 </xsl:text> |
|
762 <xsl:text>var cache = hmitree_types.map(_ignored => undefined); |
|
763 </xsl:text> |
|
764 <xsl:text>var updates = {}; |
|
765 </xsl:text> |
|
766 <xsl:text> |
|
767 </xsl:text> |
|
768 <xsl:text>function dispatch_value_to_widget(widget, index, value, oldval) { |
|
769 </xsl:text> |
|
770 <xsl:text> try { |
|
771 </xsl:text> |
|
772 <xsl:text> let idx = widget.offset ? index - widget.offset : index; |
|
773 </xsl:text> |
|
774 <xsl:text> let idxidx = widget.indexes.indexOf(idx); |
|
775 </xsl:text> |
|
776 <xsl:text> let d = widget.dispatch; |
|
777 </xsl:text> |
|
778 <xsl:text> console.log(index, idx, idxidx, value); |
|
779 </xsl:text> |
|
780 <xsl:text> if(typeof(d) == "function" && idxidx == 0){ |
|
781 </xsl:text> |
|
782 <xsl:text> d.call(widget, value, oldval); |
|
783 </xsl:text> |
|
784 <xsl:text> } |
|
785 </xsl:text> |
|
786 <xsl:text> else if(typeof(d) == "object" && d.length >= idxidx){ |
|
787 </xsl:text> |
|
788 <xsl:text> d[idxidx].call(widget, value, oldval); |
|
789 </xsl:text> |
|
790 <xsl:text> } |
|
791 </xsl:text> |
|
792 <xsl:text> /* else dispatch_0, ..., dispatch_n ? */ |
|
793 </xsl:text> |
|
794 <xsl:text> /*else { |
|
795 </xsl:text> |
|
796 <xsl:text> throw new Error("Dunno how to dispatch to widget at index = " + index); |
|
797 </xsl:text> |
|
798 <xsl:text> }*/ |
|
799 </xsl:text> |
|
800 <xsl:text> } catch(err) { |
|
801 </xsl:text> |
|
802 <xsl:text> console.log(err); |
|
803 </xsl:text> |
|
804 <xsl:text> } |
|
805 </xsl:text> |
|
806 <xsl:text>} |
|
807 </xsl:text> |
|
808 <xsl:text> |
|
809 </xsl:text> |
|
810 <xsl:text>function dispatch_value(index, value) { |
|
811 </xsl:text> |
|
812 <xsl:text> let widgets = subscribers[index]; |
|
813 </xsl:text> |
|
814 <xsl:text> |
|
815 </xsl:text> |
|
816 <xsl:text> let oldval = cache[index]; |
|
817 </xsl:text> |
|
818 <xsl:text> cache[index] = value; |
|
819 </xsl:text> |
|
820 <xsl:text> |
|
821 </xsl:text> |
|
822 <xsl:text> if(widgets.size > 0) { |
|
823 </xsl:text> |
|
824 <xsl:text> for(let widget of widgets){ |
|
825 </xsl:text> |
|
826 <xsl:text> dispatch_value_to_widget(widget, index, value, oldval); |
|
827 </xsl:text> |
|
828 <xsl:text> } |
|
829 </xsl:text> |
|
830 <xsl:text> } |
|
831 </xsl:text> |
|
832 <xsl:text>}; |
|
833 </xsl:text> |
|
834 <xsl:text> |
|
835 </xsl:text> |
|
836 <xsl:text>function init_widgets() { |
|
837 </xsl:text> |
|
838 <xsl:text> Object.keys(hmi_widgets).forEach(function(id) { |
|
839 </xsl:text> |
|
840 <xsl:text> let widget = hmi_widgets[id]; |
|
841 </xsl:text> |
|
842 <xsl:text> let init = widget.init; |
|
843 </xsl:text> |
|
844 <xsl:text> if(typeof(init) == "function"){ |
|
845 </xsl:text> |
|
846 <xsl:text> try { |
|
847 </xsl:text> |
|
848 <xsl:text> init.call(widget); |
|
849 </xsl:text> |
|
850 <xsl:text> } catch(err) { |
|
851 </xsl:text> |
|
852 <xsl:text> console.log(err); |
|
853 </xsl:text> |
|
854 <xsl:text> } |
|
855 </xsl:text> |
|
856 <xsl:text> } |
|
857 </xsl:text> |
|
858 <xsl:text> }); |
|
859 </xsl:text> |
|
860 <xsl:text>}; |
|
861 </xsl:text> |
|
862 <xsl:text> |
|
863 </xsl:text> |
|
864 <xsl:text>// Open WebSocket to relative "/ws" address |
|
865 </xsl:text> |
|
866 <xsl:text>var ws = new WebSocket(window.location.href.replace(/^http(s?:\/\/[^\/]*)\/.*$/, 'ws$1/ws')); |
|
867 </xsl:text> |
|
868 <xsl:text>ws.binaryType = 'arraybuffer'; |
|
869 </xsl:text> |
|
870 <xsl:text> |
|
871 </xsl:text> |
|
872 <xsl:text>const dvgetters = { |
|
873 </xsl:text> |
|
874 <xsl:text> INT: (dv,offset) => [dv.getInt16(offset, true), 2], |
|
875 </xsl:text> |
|
876 <xsl:text> BOOL: (dv,offset) => [dv.getInt8(offset, true), 1], |
|
877 </xsl:text> |
|
878 <xsl:text> STRING: (dv, offset) => { |
|
879 </xsl:text> |
|
880 <xsl:text> size = dv.getInt8(offset); |
|
881 </xsl:text> |
|
882 <xsl:text> return [ |
|
883 </xsl:text> |
|
884 <xsl:text> String.fromCharCode.apply(null, new Uint8Array( |
|
885 </xsl:text> |
|
886 <xsl:text> dv.buffer, /* original buffer */ |
|
887 </xsl:text> |
|
888 <xsl:text> offset + 1, /* string starts after size*/ |
|
889 </xsl:text> |
|
890 <xsl:text> size /* size of string */ |
|
891 </xsl:text> |
|
892 <xsl:text> )), size + 1]; /* total increment */ |
|
893 </xsl:text> |
|
894 <xsl:text> } |
|
895 </xsl:text> |
|
896 <xsl:text>}; |
|
897 </xsl:text> |
|
898 <xsl:text> |
|
899 </xsl:text> |
|
900 <xsl:text>// Apply updates recieved through ws.onmessage to subscribed widgets |
|
901 </xsl:text> |
|
902 <xsl:text>function apply_updates() { |
|
903 </xsl:text> |
|
904 <xsl:text> for(let index in updates){ |
|
905 </xsl:text> |
|
906 <xsl:text> // serving as a key, index becomes a string |
|
907 </xsl:text> |
|
908 <xsl:text> // -> pass Number(index) instead |
|
909 </xsl:text> |
|
910 <xsl:text> dispatch_value(Number(index), updates[index]); |
|
911 </xsl:text> |
|
912 <xsl:text> delete updates[index]; |
|
913 </xsl:text> |
|
914 <xsl:text> } |
|
915 </xsl:text> |
|
916 <xsl:text>} |
|
917 </xsl:text> |
|
918 <xsl:text> |
|
919 </xsl:text> |
|
920 <xsl:text>// Called on requestAnimationFrame, modifies DOM |
|
921 </xsl:text> |
|
922 <xsl:text>var requestAnimationFrameID = null; |
|
923 </xsl:text> |
|
924 <xsl:text>function animate() { |
|
925 </xsl:text> |
|
926 <xsl:text> // Do the page swith if any one pending |
|
927 </xsl:text> |
|
928 <xsl:text> if(current_subscribed_page != current_visible_page){ |
|
929 </xsl:text> |
|
930 <xsl:text> switch_visible_page(current_subscribed_page); |
|
931 </xsl:text> |
|
932 <xsl:text> } |
|
933 </xsl:text> |
|
934 <xsl:text> apply_updates(); |
|
935 </xsl:text> |
|
936 <xsl:text> requestAnimationFrameID = null; |
|
937 </xsl:text> |
|
938 <xsl:text>} |
|
939 </xsl:text> |
|
940 <xsl:text> |
|
941 </xsl:text> |
|
942 <xsl:text>function requestHMIAnimation() { |
|
943 </xsl:text> |
|
944 <xsl:text> if(requestAnimationFrameID == null){ |
|
945 </xsl:text> |
|
946 <xsl:text> requestAnimationFrameID = window.requestAnimationFrame(animate); |
|
947 </xsl:text> |
|
948 <xsl:text> } |
|
949 </xsl:text> |
|
950 <xsl:text>} |
|
951 </xsl:text> |
|
952 <xsl:text> |
|
953 </xsl:text> |
|
954 <xsl:text>// Message reception handler |
|
955 </xsl:text> |
|
956 <xsl:text>// Hash is verified and HMI values updates resulting from binary parsing |
|
957 </xsl:text> |
|
958 <xsl:text>// are stored until browser can compute next frame, DOM is left untouched |
|
959 </xsl:text> |
|
960 <xsl:text>ws.onmessage = function (evt) { |
|
961 </xsl:text> |
|
962 <xsl:text> |
|
963 </xsl:text> |
|
964 <xsl:text> let data = evt.data; |
|
965 </xsl:text> |
|
966 <xsl:text> let dv = new DataView(data); |
|
967 </xsl:text> |
|
968 <xsl:text> let i = 0; |
|
969 </xsl:text> |
|
970 <xsl:text> try { |
|
971 </xsl:text> |
|
972 <xsl:text> for(let hash_int of hmi_hash) { |
|
973 </xsl:text> |
|
974 <xsl:text> if(hash_int != dv.getUint8(i)){ |
|
975 </xsl:text> |
|
976 <xsl:text> throw new Error("Hash doesn't match"); |
|
977 </xsl:text> |
|
978 <xsl:text> }; |
|
979 </xsl:text> |
|
980 <xsl:text> i++; |
|
981 </xsl:text> |
|
982 <xsl:text> }; |
|
983 </xsl:text> |
|
984 <xsl:text> |
|
985 </xsl:text> |
|
986 <xsl:text> while(i < data.byteLength){ |
|
987 </xsl:text> |
|
988 <xsl:text> let index = dv.getUint32(i, true); |
|
989 </xsl:text> |
|
990 <xsl:text> i += 4; |
|
991 </xsl:text> |
|
992 <xsl:text> let iectype = hmitree_types[index]; |
|
993 </xsl:text> |
|
994 <xsl:text> if(iectype != undefined){ |
|
995 </xsl:text> |
|
996 <xsl:text> let dvgetter = dvgetters[iectype]; |
|
997 </xsl:text> |
|
998 <xsl:text> let [value, bytesize] = dvgetter(dv,i); |
|
999 </xsl:text> |
|
1000 <xsl:text> updates[index] = value; |
|
1001 </xsl:text> |
|
1002 <xsl:text> i += bytesize; |
|
1003 </xsl:text> |
|
1004 <xsl:text> } else { |
|
1005 </xsl:text> |
|
1006 <xsl:text> throw new Error("Unknown index "+index); |
|
1007 </xsl:text> |
|
1008 <xsl:text> } |
|
1009 </xsl:text> |
|
1010 <xsl:text> }; |
|
1011 </xsl:text> |
|
1012 <xsl:text> // register for rendering on next frame, since there are updates |
|
1013 </xsl:text> |
|
1014 <xsl:text> requestHMIAnimation(); |
|
1015 </xsl:text> |
|
1016 <xsl:text> } catch(err) { |
|
1017 </xsl:text> |
|
1018 <xsl:text> // 1003 is for "Unsupported Data" |
|
1019 </xsl:text> |
|
1020 <xsl:text> // ws.close(1003, err.message); |
|
1021 </xsl:text> |
|
1022 <xsl:text> |
|
1023 </xsl:text> |
|
1024 <xsl:text> // TODO : remove debug alert ? |
|
1025 </xsl:text> |
|
1026 <xsl:text> alert("Error : "+err.message+"\nHMI will be reloaded."); |
|
1027 </xsl:text> |
|
1028 <xsl:text> |
|
1029 </xsl:text> |
|
1030 <xsl:text> // force reload ignoring cache |
|
1031 </xsl:text> |
|
1032 <xsl:text> location.reload(true); |
|
1033 </xsl:text> |
|
1034 <xsl:text> } |
|
1035 </xsl:text> |
|
1036 <xsl:text>}; |
|
1037 </xsl:text> |
|
1038 <xsl:text> |
|
1039 </xsl:text> |
|
1040 <xsl:text> |
|
1041 </xsl:text> |
|
1042 <xsl:text>function send_blob(data) { |
|
1043 </xsl:text> |
|
1044 <xsl:text> if(data.length > 0) { |
|
1045 </xsl:text> |
|
1046 <xsl:text> ws.send(new Blob([new Uint8Array(hmi_hash)].concat(data))); |
|
1047 </xsl:text> |
|
1048 <xsl:text> }; |
|
1049 </xsl:text> |
|
1050 <xsl:text>}; |
|
1051 </xsl:text> |
|
1052 <xsl:text> |
|
1053 </xsl:text> |
|
1054 <xsl:text>const typedarray_types = { |
|
1055 </xsl:text> |
|
1056 <xsl:text> INT: (number) => new Int16Array([number]), |
|
1057 </xsl:text> |
|
1058 <xsl:text> BOOL: (truth) => new Int16Array([truth]), |
|
1059 </xsl:text> |
|
1060 <xsl:text> STRING: (str) => { |
|
1061 </xsl:text> |
|
1062 <xsl:text> // beremiz default string max size is 128 |
|
1063 </xsl:text> |
|
1064 <xsl:text> str = str.slice(0,128); |
|
1065 </xsl:text> |
|
1066 <xsl:text> binary = new Uint8Array(str.length + 1); |
|
1067 </xsl:text> |
|
1068 <xsl:text> binary[0] = str.length; |
|
1069 </xsl:text> |
|
1070 <xsl:text> for(var i = 0; i < str.length; i++){ |
|
1071 </xsl:text> |
|
1072 <xsl:text> binary[i+1] = str.charCodeAt(i); |
|
1073 </xsl:text> |
|
1074 <xsl:text> } |
|
1075 </xsl:text> |
|
1076 <xsl:text> return binary; |
|
1077 </xsl:text> |
|
1078 <xsl:text> } |
|
1079 </xsl:text> |
|
1080 <xsl:text> /* TODO */ |
|
1081 </xsl:text> |
|
1082 <xsl:text>}; |
|
1083 </xsl:text> |
|
1084 <xsl:text> |
|
1085 </xsl:text> |
|
1086 <xsl:text>function send_reset() { |
|
1087 </xsl:text> |
|
1088 <xsl:text> send_blob(new Uint8Array([1])); /* reset = 1 */ |
|
1089 </xsl:text> |
|
1090 <xsl:text>}; |
|
1091 </xsl:text> |
|
1092 <xsl:text> |
|
1093 </xsl:text> |
|
1094 <xsl:text>// subscription state, as it should be in hmi server |
|
1095 </xsl:text> |
|
1096 <xsl:text>// hmitree indexed array of integers |
|
1097 </xsl:text> |
|
1098 <xsl:text>var subscriptions = hmitree_types.map(_ignored => 0); |
|
1099 </xsl:text> |
|
1100 <xsl:text> |
|
1101 </xsl:text> |
|
1102 <xsl:text>// subscription state as needed by widget now |
|
1103 </xsl:text> |
|
1104 <xsl:text>// hmitree indexed array of Sets of widgets objects |
|
1105 </xsl:text> |
|
1106 <xsl:text>var subscribers = hmitree_types.map(_ignored => new Set()); |
|
1107 </xsl:text> |
|
1108 <xsl:text> |
|
1109 </xsl:text> |
|
1110 <xsl:text>// artificially subscribe the watchdog widget to "/heartbeat" hmi variable |
|
1111 </xsl:text> |
|
1112 <xsl:text>// Since dispatch directly calls change_hmi_value, |
|
1113 </xsl:text> |
|
1114 <xsl:text>// PLC will periodically send variable at given frequency |
|
1115 </xsl:text> |
|
1116 <xsl:text>subscribers[heartbeat_index].add({ |
|
1117 </xsl:text> |
|
1118 <xsl:text> /* type: "Watchdog", */ |
|
1119 </xsl:text> |
|
1120 <xsl:text> frequency: 1, |
|
1121 </xsl:text> |
|
1122 <xsl:text> indexes: [heartbeat_index], |
|
1123 </xsl:text> |
|
1124 <xsl:text> dispatch: function(value) { |
|
1125 </xsl:text> |
|
1126 <xsl:text> // console.log("Heartbeat" + value); |
|
1127 </xsl:text> |
|
1128 <xsl:text> change_hmi_value(heartbeat_index, "+1"); |
|
1129 </xsl:text> |
|
1130 <xsl:text> } |
|
1131 </xsl:text> |
|
1132 <xsl:text>}); |
|
1133 </xsl:text> |
|
1134 <xsl:text> |
|
1135 </xsl:text> |
|
1136 <xsl:text>function update_subscriptions() { |
|
1137 </xsl:text> |
|
1138 <xsl:text> let delta = []; |
|
1139 </xsl:text> |
|
1140 <xsl:text> for(let index = 0; index < subscribers.length; index++){ |
|
1141 </xsl:text> |
|
1142 <xsl:text> let widgets = subscribers[index]; |
|
1143 </xsl:text> |
|
1144 <xsl:text> |
|
1145 </xsl:text> |
|
1146 <xsl:text> // periods are in ms |
|
1147 </xsl:text> |
|
1148 <xsl:text> let previous_period = subscriptions[index]; |
|
1149 </xsl:text> |
|
1150 <xsl:text> |
|
1151 </xsl:text> |
|
1152 <xsl:text> // subscribing with a zero period is unsubscribing |
|
1153 </xsl:text> |
|
1154 <xsl:text> let new_period = 0; |
|
1155 </xsl:text> |
|
1156 <xsl:text> if(widgets.size > 0) { |
|
1157 </xsl:text> |
|
1158 <xsl:text> let maxfreq = 0; |
|
1159 </xsl:text> |
|
1160 <xsl:text> for(let widget of widgets) |
|
1161 </xsl:text> |
|
1162 <xsl:text> if(maxfreq < widget.frequency) |
|
1163 </xsl:text> |
|
1164 <xsl:text> maxfreq = widget.frequency; |
|
1165 </xsl:text> |
|
1166 <xsl:text> |
|
1167 </xsl:text> |
|
1168 <xsl:text> if(maxfreq != 0) |
|
1169 </xsl:text> |
|
1170 <xsl:text> new_period = 1000/maxfreq; |
|
1171 </xsl:text> |
|
1172 <xsl:text> } |
|
1173 </xsl:text> |
|
1174 <xsl:text> |
|
1175 </xsl:text> |
|
1176 <xsl:text> if(previous_period != new_period) { |
|
1177 </xsl:text> |
|
1178 <xsl:text> subscriptions[index] = new_period; |
|
1179 </xsl:text> |
|
1180 <xsl:text> delta.push( |
|
1181 </xsl:text> |
|
1182 <xsl:text> new Uint8Array([2]), /* subscribe = 2 */ |
|
1183 </xsl:text> |
|
1184 <xsl:text> new Uint32Array([index]), |
|
1185 </xsl:text> |
|
1186 <xsl:text> new Uint16Array([new_period])); |
|
1187 </xsl:text> |
|
1188 <xsl:text> } |
|
1189 </xsl:text> |
|
1190 <xsl:text> } |
|
1191 </xsl:text> |
|
1192 <xsl:text> send_blob(delta); |
|
1193 </xsl:text> |
|
1194 <xsl:text>}; |
|
1195 </xsl:text> |
|
1196 <xsl:text> |
|
1197 </xsl:text> |
|
1198 <xsl:text>function send_hmi_value(index, value) { |
|
1199 </xsl:text> |
|
1200 <xsl:text> let iectype = hmitree_types[index]; |
|
1201 </xsl:text> |
|
1202 <xsl:text> let tobinary = typedarray_types[iectype]; |
|
1203 </xsl:text> |
|
1204 <xsl:text> send_blob([ |
|
1205 </xsl:text> |
|
1206 <xsl:text> new Uint8Array([0]), /* setval = 0 */ |
|
1207 </xsl:text> |
|
1208 <xsl:text> new Uint32Array([index]), |
|
1209 </xsl:text> |
|
1210 <xsl:text> tobinary(value)]); |
|
1211 </xsl:text> |
|
1212 <xsl:text> |
|
1213 </xsl:text> |
|
1214 <xsl:text> cache[index] = value; |
|
1215 </xsl:text> |
|
1216 <xsl:text>}; |
|
1217 </xsl:text> |
|
1218 <xsl:text> |
|
1219 </xsl:text> |
|
1220 <xsl:text>function change_hmi_value(index, opstr) { |
|
1221 </xsl:text> |
|
1222 <xsl:text> let op = opstr[0]; |
|
1223 </xsl:text> |
|
1224 <xsl:text> let given_val = opstr.slice(1); |
|
1225 </xsl:text> |
|
1226 <xsl:text> let old_val = cache[index] |
|
1227 </xsl:text> |
|
1228 <xsl:text> let new_val; |
|
1229 </xsl:text> |
|
1230 <xsl:text> switch(op){ |
|
1231 </xsl:text> |
|
1232 <xsl:text> case "=": |
|
1233 </xsl:text> |
|
1234 <xsl:text> eval("new_val"+opstr); |
|
1235 </xsl:text> |
|
1236 <xsl:text> break; |
|
1237 </xsl:text> |
|
1238 <xsl:text> case "+": |
|
1239 </xsl:text> |
|
1240 <xsl:text> case "-": |
|
1241 </xsl:text> |
|
1242 <xsl:text> case "*": |
|
1243 </xsl:text> |
|
1244 <xsl:text> case "/": |
|
1245 </xsl:text> |
|
1246 <xsl:text> if(old_val != undefined) |
|
1247 </xsl:text> |
|
1248 <xsl:text> new_val = eval("old_val"+opstr); |
|
1249 </xsl:text> |
|
1250 <xsl:text> break; |
|
1251 </xsl:text> |
|
1252 <xsl:text> } |
|
1253 </xsl:text> |
|
1254 <xsl:text> if(new_val != undefined && old_val != new_val) |
|
1255 </xsl:text> |
|
1256 <xsl:text> send_hmi_value(index, new_val); |
|
1257 </xsl:text> |
|
1258 <xsl:text> return new_val; |
|
1259 </xsl:text> |
|
1260 <xsl:text>} |
|
1261 </xsl:text> |
|
1262 <xsl:text> |
|
1263 </xsl:text> |
|
1264 <xsl:text>var current_visible_page; |
|
1265 </xsl:text> |
|
1266 <xsl:text>var current_subscribed_page; |
|
1267 </xsl:text> |
|
1268 <xsl:text> |
|
1269 </xsl:text> |
|
1270 <xsl:text>function prepare_svg() { |
|
1271 </xsl:text> |
|
1272 <xsl:text> for(let eltid in detachable_elements){ |
|
1273 </xsl:text> |
|
1274 <xsl:text> let [element,parent] = detachable_elements[eltid]; |
|
1275 </xsl:text> |
|
1276 <xsl:text> parent.removeChild(element); |
|
1277 </xsl:text> |
|
1278 <xsl:text> } |
|
1279 </xsl:text> |
|
1280 <xsl:text>}; |
|
1281 </xsl:text> |
|
1282 <xsl:text> |
|
1283 </xsl:text> |
|
1284 <xsl:text>function switch_page(page_name, page_index) { |
|
1285 </xsl:text> |
|
1286 <xsl:text> if(current_subscribed_page != current_visible_page){ |
|
1287 </xsl:text> |
|
1288 <xsl:text> /* page switch already going */ |
|
1289 </xsl:text> |
|
1290 <xsl:text> /* TODO LOG ERROR */ |
|
1291 </xsl:text> |
|
1292 <xsl:text> return; |
|
1293 </xsl:text> |
|
1294 <xsl:text> } else if(page_name == current_visible_page){ |
|
1295 </xsl:text> |
|
1296 <xsl:text> /* already in that page */ |
|
1297 </xsl:text> |
|
1298 <xsl:text> /* TODO LOG ERROR */ |
|
1299 </xsl:text> |
|
1300 <xsl:text> return; |
|
1301 </xsl:text> |
|
1302 <xsl:text> } |
|
1303 </xsl:text> |
|
1304 <xsl:text> switch_subscribed_page(page_name, page_index); |
|
1305 </xsl:text> |
|
1306 <xsl:text>}; |
|
1307 </xsl:text> |
|
1308 <xsl:text> |
|
1309 </xsl:text> |
|
1310 <xsl:text>function* chain(a,b){ |
|
1311 </xsl:text> |
|
1312 <xsl:text> yield* a; |
|
1313 </xsl:text> |
|
1314 <xsl:text> yield* b; |
|
1315 </xsl:text> |
|
1316 <xsl:text>}; |
|
1317 </xsl:text> |
|
1318 <xsl:text> |
|
1319 </xsl:text> |
|
1320 <xsl:text>function switch_subscribed_page(page_name, page_index) { |
|
1321 </xsl:text> |
|
1322 <xsl:text> let old_desc = page_desc[current_subscribed_page]; |
|
1323 </xsl:text> |
|
1324 <xsl:text> let new_desc = page_desc[page_name]; |
|
1325 </xsl:text> |
|
1326 <xsl:text> |
|
1327 </xsl:text> |
|
1328 <xsl:text> if(new_desc == undefined){ |
|
1329 </xsl:text> |
|
1330 <xsl:text> /* TODO LOG ERROR */ |
|
1331 </xsl:text> |
|
1332 <xsl:text> return; |
|
1333 </xsl:text> |
|
1334 <xsl:text> } |
|
1335 </xsl:text> |
|
1336 <xsl:text> |
|
1337 </xsl:text> |
|
1338 <xsl:text> if(page_index == undefined){ |
|
1339 </xsl:text> |
|
1340 <xsl:text> page_index = new_desc.page_index; |
|
1341 </xsl:text> |
|
1342 <xsl:text> } |
|
1343 </xsl:text> |
|
1344 <xsl:text> |
|
1345 </xsl:text> |
|
1346 <xsl:text> if(old_desc){ |
|
1347 </xsl:text> |
|
1348 <xsl:text> for(let widget of old_desc.absolute_widgets){ |
|
1349 </xsl:text> |
|
1350 <xsl:text> /* remove subsribers */ |
|
1351 </xsl:text> |
|
1352 <xsl:text> for(let index of widget.indexes){ |
|
1353 </xsl:text> |
|
1354 <xsl:text> subscribers[index].delete(widget); |
|
1355 </xsl:text> |
|
1356 <xsl:text> } |
|
1357 </xsl:text> |
|
1358 <xsl:text> } |
|
1359 </xsl:text> |
|
1360 <xsl:text> for(let widget of old_desc.relative_widgets){ |
|
1361 </xsl:text> |
|
1362 <xsl:text> /* remove subsribers */ |
|
1363 </xsl:text> |
|
1364 <xsl:text> for(let index of widget.indexes){ |
|
1365 </xsl:text> |
|
1366 <xsl:text> let idx = widget.offset ? index + widget.offset : index; |
|
1367 </xsl:text> |
|
1368 <xsl:text> subscribers[idx].delete(widget); |
|
1369 </xsl:text> |
|
1370 <xsl:text> } |
|
1371 </xsl:text> |
|
1372 <xsl:text> /* lose the offset */ |
|
1373 </xsl:text> |
|
1374 <xsl:text> delete widget.offset; |
|
1375 </xsl:text> |
|
1376 <xsl:text> } |
|
1377 </xsl:text> |
|
1378 <xsl:text> } |
|
1379 </xsl:text> |
|
1380 <xsl:text> for(let widget of new_desc.absolute_widgets){ |
|
1381 </xsl:text> |
|
1382 <xsl:text> /* add widget's subsribers */ |
|
1383 </xsl:text> |
|
1384 <xsl:text> for(let index of widget.indexes){ |
|
1385 </xsl:text> |
|
1386 <xsl:text> subscribers[index].add(widget); |
|
1387 </xsl:text> |
|
1388 <xsl:text> } |
|
1389 </xsl:text> |
|
1390 <xsl:text> } |
|
1391 </xsl:text> |
|
1392 <xsl:text> var new_offset = page_index == undefined ? 0 : page_index - new_desc.page_index; |
|
1393 </xsl:text> |
|
1394 <xsl:text> for(let widget of new_desc.relative_widgets){ |
|
1395 </xsl:text> |
|
1396 <xsl:text> /* set the offset because relative */ |
|
1397 </xsl:text> |
|
1398 <xsl:text> widget.offset = new_offset; |
|
1399 </xsl:text> |
|
1400 <xsl:text> /* add widget's subsribers */ |
|
1401 </xsl:text> |
|
1402 <xsl:text> for(let index of widget.indexes){ |
|
1403 </xsl:text> |
|
1404 <xsl:text> subscribers[index + new_offset].add(widget); |
|
1405 </xsl:text> |
|
1406 <xsl:text> } |
|
1407 </xsl:text> |
|
1408 <xsl:text> } |
|
1409 </xsl:text> |
|
1410 <xsl:text> |
|
1411 </xsl:text> |
|
1412 <xsl:text> update_subscriptions(); |
|
1413 </xsl:text> |
|
1414 <xsl:text> |
|
1415 </xsl:text> |
|
1416 <xsl:text> current_subscribed_page = page_name; |
|
1417 </xsl:text> |
|
1418 <xsl:text> |
|
1419 </xsl:text> |
|
1420 <xsl:text> requestHMIAnimation(); |
|
1421 </xsl:text> |
|
1422 <xsl:text>} |
|
1423 </xsl:text> |
|
1424 <xsl:text> |
|
1425 </xsl:text> |
|
1426 <xsl:text>function switch_visible_page(page_name) { |
|
1427 </xsl:text> |
|
1428 <xsl:text> |
|
1429 </xsl:text> |
|
1430 <xsl:text> let old_desc = page_desc[current_visible_page]; |
|
1431 </xsl:text> |
|
1432 <xsl:text> let new_desc = page_desc[page_name]; |
|
1433 </xsl:text> |
|
1434 <xsl:text> |
|
1435 </xsl:text> |
|
1436 <xsl:text> if(old_desc){ |
|
1437 </xsl:text> |
|
1438 <xsl:text> for(let eltid in old_desc.required_detachables){ |
|
1439 </xsl:text> |
|
1440 <xsl:text> if(!(eltid in new_desc.required_detachables)){ |
|
1441 </xsl:text> |
|
1442 <xsl:text> let [element, parent] = old_desc.required_detachables[eltid]; |
|
1443 </xsl:text> |
|
1444 <xsl:text> parent.removeChild(element); |
|
1445 </xsl:text> |
|
1446 <xsl:text> } |
|
1447 </xsl:text> |
|
1448 <xsl:text> } |
|
1449 </xsl:text> |
|
1450 <xsl:text> for(let eltid in new_desc.required_detachables){ |
|
1451 </xsl:text> |
|
1452 <xsl:text> if(!(eltid in old_desc.required_detachables)){ |
|
1453 </xsl:text> |
|
1454 <xsl:text> let [element, parent] = new_desc.required_detachables[eltid]; |
|
1455 </xsl:text> |
|
1456 <xsl:text> parent.appendChild(element); |
|
1457 </xsl:text> |
|
1458 <xsl:text> } |
|
1459 </xsl:text> |
|
1460 <xsl:text> } |
|
1461 </xsl:text> |
|
1462 <xsl:text> }else{ |
|
1463 </xsl:text> |
|
1464 <xsl:text> for(let eltid in new_desc.required_detachables){ |
|
1465 </xsl:text> |
|
1466 <xsl:text> let [element, parent] = new_desc.required_detachables[eltid]; |
|
1467 </xsl:text> |
|
1468 <xsl:text> parent.appendChild(element); |
|
1469 </xsl:text> |
|
1470 <xsl:text> } |
|
1471 </xsl:text> |
|
1472 <xsl:text> } |
|
1473 </xsl:text> |
|
1474 <xsl:text> |
|
1475 </xsl:text> |
|
1476 <xsl:text> for(let widget of chain(new_desc.absolute_widgets,new_desc.relative_widgets)){ |
|
1477 </xsl:text> |
|
1478 <xsl:text> for(let index of widget.indexes){ |
|
1479 </xsl:text> |
|
1480 <xsl:text> /* dispatch current cache in newly opened page widgets */ |
|
1481 </xsl:text> |
|
1482 <xsl:text> let cached_val = cache[index]; |
|
1483 </xsl:text> |
|
1484 <xsl:text> if(cached_val != undefined) |
|
1485 </xsl:text> |
|
1486 <xsl:text> dispatch_value_to_widget(widget, index, cached_val, cached_val); |
|
1487 </xsl:text> |
|
1488 <xsl:text> } |
|
1489 </xsl:text> |
|
1490 <xsl:text> } |
|
1491 </xsl:text> |
|
1492 <xsl:text> |
|
1493 </xsl:text> |
|
1494 <xsl:text> svg_root.setAttribute('viewBox',new_desc.bbox.join(" ")); |
|
1495 </xsl:text> |
|
1496 <xsl:text> current_visible_page = page_name; |
|
1497 </xsl:text> |
|
1498 <xsl:text>}; |
|
1499 </xsl:text> |
|
1500 <xsl:text> |
|
1501 </xsl:text> |
|
1502 <xsl:text> |
|
1503 </xsl:text> |
|
1504 <xsl:text>// Once connection established |
|
1505 </xsl:text> |
|
1506 <xsl:text>ws.onopen = function (evt) { |
|
1507 </xsl:text> |
|
1508 <xsl:text> init_widgets(); |
|
1509 </xsl:text> |
|
1510 <xsl:text> send_reset(); |
|
1511 </xsl:text> |
|
1512 <xsl:text> // show main page |
|
1513 </xsl:text> |
|
1514 <xsl:text> prepare_svg(); |
|
1515 </xsl:text> |
|
1516 <xsl:text> switch_page(default_page); |
|
1517 </xsl:text> |
|
1518 <xsl:text>}; |
|
1519 </xsl:text> |
|
1520 <xsl:text> |
|
1521 </xsl:text> |
|
1522 <xsl:text>ws.onclose = function (evt) { |
|
1523 </xsl:text> |
|
1524 <xsl:text> // TODO : add visible notification while waiting for reload |
|
1525 </xsl:text> |
|
1526 <xsl:text> console.log("Connection closed. code:"+evt.code+" reason:"+evt.reason+" wasClean:"+evt.wasClean+" Reload in 10s."); |
|
1527 </xsl:text> |
|
1528 <xsl:text> // TODO : re-enable auto reload when not in debug |
|
1529 </xsl:text> |
|
1530 <xsl:text> //window.setTimeout(() => location.reload(true), 10000); |
|
1531 </xsl:text> |
|
1532 <xsl:text> alert("Connection closed. code:"+evt.code+" reason:"+evt.reason+" wasClean:"+evt.wasClean+"."); |
|
1533 </xsl:text> |
|
1534 <xsl:text> |
|
1535 </xsl:text> |
|
1536 <xsl:text>}; |
|
1537 </xsl:text> |
|
1538 <xsl:text>//})(); |
|
1539 </xsl:text> |
1539 </xsl:template> |
1540 </xsl:template> |
1540 <xsl:template mode="widget_defs" match="widget[@type='Display']"> |
1541 <xsl:template mode="widget_defs" match="widget[@type='Display']"> |
1541 <xsl:param name="hmi_element"/> |
1542 <xsl:param name="hmi_element"/> |
1542 <xsl:text> frequency: 5, |
1543 <xsl:text> frequency: 5, |
1543 </xsl:text> |
1544 </xsl:text> |