622 </xsl:text> |
622 </xsl:text> |
623 </xsl:otherwise> |
623 </xsl:otherwise> |
624 </xsl:choose> |
624 </xsl:choose> |
625 </xsl:for-each> |
625 </xsl:for-each> |
626 </xsl:template> |
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> |
|
1540 </xsl:template> |
|
1541 <xsl:template mode="widget_defs" match="widget[@type='Display']"> |
|
1542 <xsl:param name="hmi_element"/> |
|
1543 <xsl:text> frequency: 5, |
|
1544 </xsl:text> |
|
1545 <xsl:text> dispatch: function(value) { |
|
1546 </xsl:text> |
|
1547 <xsl:choose> |
|
1548 <xsl:when test="$hmi_element[self::svg:text]"> |
|
1549 <xsl:text> this.element.textContent = String(value); |
|
1550 </xsl:text> |
|
1551 </xsl:when> |
|
1552 <xsl:otherwise> |
|
1553 <xsl:message terminate="no"> |
|
1554 <xsl:text>Display widget as a group not implemented</xsl:text> |
|
1555 </xsl:message> |
|
1556 </xsl:otherwise> |
|
1557 </xsl:choose> |
|
1558 <xsl:text> }, |
|
1559 </xsl:text> |
|
1560 </xsl:template> |
|
1561 <xsl:template mode="widget_defs" match="widget[@type='Meter']"> |
|
1562 <xsl:param name="hmi_element"/> |
|
1563 <xsl:text> frequency: 10, |
|
1564 </xsl:text> |
|
1565 <xsl:call-template name="defs_by_labels"> |
|
1566 <xsl:with-param name="hmi_element" select="$hmi_element"/> |
|
1567 <xsl:with-param name="labels"> |
|
1568 <xsl:text>needle range</xsl:text> |
|
1569 </xsl:with-param> |
|
1570 </xsl:call-template> |
|
1571 <xsl:call-template name="defs_by_labels"> |
|
1572 <xsl:with-param name="hmi_element" select="$hmi_element"/> |
|
1573 <xsl:with-param name="labels"> |
|
1574 <xsl:text>value min max</xsl:text> |
|
1575 </xsl:with-param> |
|
1576 <xsl:with-param name="mandatory" select="'no'"/> |
|
1577 </xsl:call-template> |
|
1578 <xsl:text> dispatch: function(value) { |
|
1579 </xsl:text> |
|
1580 <xsl:text> if(this.value_elt) |
|
1581 </xsl:text> |
|
1582 <xsl:text> this.value_elt.textContent = String(value); |
|
1583 </xsl:text> |
|
1584 <xsl:text> let [min,max,totallength] = this.range; |
|
1585 </xsl:text> |
|
1586 <xsl:text> let length = Math.max(0,Math.min(totallength,(Number(value)-min)*totallength/(max-min))); |
|
1587 </xsl:text> |
|
1588 <xsl:text> let tip = this.range_elt.getPointAtLength(length); |
|
1589 </xsl:text> |
|
1590 <xsl:text> this.needle_elt.setAttribute('d', "M "+this.origin.x+","+this.origin.y+" "+tip.x+","+tip.y); |
|
1591 </xsl:text> |
|
1592 <xsl:text> }, |
|
1593 </xsl:text> |
|
1594 <xsl:text> origin: undefined, |
|
1595 </xsl:text> |
|
1596 <xsl:text> range: undefined, |
|
1597 </xsl:text> |
|
1598 <xsl:text> init: function() { |
|
1599 </xsl:text> |
|
1600 <xsl:text> let min = this.min_elt ? |
|
1601 </xsl:text> |
|
1602 <xsl:text> Number(this.min_elt.textContent) : |
|
1603 </xsl:text> |
|
1604 <xsl:text> this.args.length >= 1 ? this.args[0] : 0; |
|
1605 </xsl:text> |
|
1606 <xsl:text> let max = this.max_elt ? |
|
1607 </xsl:text> |
|
1608 <xsl:text> Number(this.max_elt.textContent) : |
|
1609 </xsl:text> |
|
1610 <xsl:text> this.args.length >= 2 ? this.args[1] : 100; |
|
1611 </xsl:text> |
|
1612 <xsl:text> this.range = [min, max, this.range_elt.getTotalLength()] |
|
1613 </xsl:text> |
|
1614 <xsl:text> this.origin = this.needle_elt.getPointAtLength(0); |
|
1615 </xsl:text> |
|
1616 <xsl:text> }, |
|
1617 </xsl:text> |
|
1618 </xsl:template> |
|
1619 <func:function name="func:escape_quotes"> |
627 <func:function name="func:escape_quotes"> |
1620 <xsl:param name="txt"/> |
628 <xsl:param name="txt"/> |
1621 <xsl:variable name="frst" select="substring-before($txt,'"')"/> |
629 <xsl:variable name="frst" select="substring-before($txt,'"')"/> |
1622 <xsl:variable name="frstln" select="string-length($frst)"/> |
630 <xsl:variable name="frstln" select="string-length($frst)"/> |
1623 <xsl:choose> |
631 <xsl:choose> |
1748 </xsl:text> |
846 </xsl:text> |
1749 </xsl:for-each> |
847 </xsl:for-each> |
1750 <xsl:text> ], |
848 <xsl:text> ], |
1751 </xsl:text> |
849 </xsl:text> |
1752 </xsl:template> |
850 </xsl:template> |
1753 <xsl:template mode="widget_defs" match="widget[@type='Jump']"> |
851 <xsl:template match="/"> |
1754 <xsl:param name="hmi_element"/> |
852 <xsl:comment> |
1755 <xsl:text> on_click: function(evt) { |
853 <xsl:text>Made with SVGHMI. https://beremiz.org</xsl:text> |
1756 </xsl:text> |
854 </xsl:comment> |
1757 <xsl:text> switch_page(this.args[0], this.indexes[0]); |
855 <xsl:comment> |
1758 </xsl:text> |
856 <xsl:text> |
1759 <xsl:text> }, |
857 </xsl:text> |
1760 </xsl:text> |
858 <xsl:text>debug_hmitree: |
1761 <xsl:text> init: function() { |
859 </xsl:text> |
1762 </xsl:text> |
860 <xsl:call-template name="debug_hmitree"/> |
1763 <xsl:text> this.element.setAttribute("onclick", "hmi_widgets['</xsl:text> |
861 <xsl:text> |
1764 <xsl:value-of select="$hmi_element/@id"/> |
862 </xsl:text> |
1765 <xsl:text>'].on_click(evt)"); |
863 </xsl:comment> |
1766 </xsl:text> |
864 <xsl:comment> |
1767 <xsl:text> }, |
865 <xsl:text> |
|
866 </xsl:text> |
|
867 <xsl:text>debug_geometry: |
|
868 </xsl:text> |
|
869 <xsl:call-template name="debug_geometry"/> |
|
870 <xsl:text> |
|
871 </xsl:text> |
|
872 </xsl:comment> |
|
873 <xsl:comment> |
|
874 <xsl:text> |
|
875 </xsl:text> |
|
876 <xsl:text>debug_detachables: |
|
877 </xsl:text> |
|
878 <xsl:call-template name="debug_detachables"/> |
|
879 <xsl:text> |
|
880 </xsl:text> |
|
881 </xsl:comment> |
|
882 <xsl:comment> |
|
883 <xsl:text> |
|
884 </xsl:text> |
|
885 <xsl:text>debug_unlink: |
|
886 </xsl:text> |
|
887 <xsl:call-template name="debug_unlink"/> |
|
888 <xsl:text> |
|
889 </xsl:text> |
|
890 </xsl:comment> |
|
891 <html xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/1999/xhtml"> |
|
892 <head/> |
|
893 <body style="margin:0;overflow:hidden;"> |
|
894 <xsl:copy-of select="$result_svg"/> |
|
895 <script> |
|
896 <xsl:call-template name="scripts"/> |
|
897 </script> |
|
898 </body> |
|
899 </html> |
|
900 </xsl:template> |
|
901 <xsl:template name="scripts"> |
|
902 <xsl:text> |
|
903 </xsl:text> |
|
904 <xsl:text>id = idstr => document.getElementById(idstr); |
|
905 </xsl:text> |
|
906 <xsl:text> |
|
907 </xsl:text> |
|
908 <xsl:text>var hmi_hash = [</xsl:text> |
|
909 <xsl:value-of select="$hmitree/@hash"/> |
|
910 <xsl:text>]; |
|
911 </xsl:text> |
|
912 <xsl:text>var hmi_widgets = { |
|
913 </xsl:text> |
|
914 <xsl:apply-templates mode="hmi_elements" select="$hmi_elements"/> |
|
915 <xsl:text>} |
|
916 </xsl:text> |
|
917 <xsl:text> |
|
918 </xsl:text> |
|
919 <xsl:text>var heartbeat_index = </xsl:text> |
|
920 <xsl:value-of select="$indexed_hmitree/*[@hmipath = '/HEARTBEAT']/@index"/> |
|
921 <xsl:text>; |
|
922 </xsl:text> |
|
923 <xsl:text> |
|
924 </xsl:text> |
|
925 <xsl:text>var hmitree_types = [ |
|
926 </xsl:text> |
|
927 <xsl:for-each select="$indexed_hmitree/*"> |
|
928 <xsl:text> /* </xsl:text> |
|
929 <xsl:value-of select="@index"/> |
|
930 <xsl:text> </xsl:text> |
|
931 <xsl:value-of select="@hmipath"/> |
|
932 <xsl:text> */ "</xsl:text> |
|
933 <xsl:value-of select="substring(local-name(), 5)"/> |
|
934 <xsl:text>"</xsl:text> |
|
935 <xsl:if test="position()!=last()"> |
|
936 <xsl:text>,</xsl:text> |
|
937 </xsl:if> |
|
938 <xsl:text> |
|
939 </xsl:text> |
|
940 </xsl:for-each> |
|
941 <xsl:text>] |
|
942 </xsl:text> |
|
943 <xsl:text> |
|
944 </xsl:text> |
|
945 <xsl:text>var detachable_elements = { |
|
946 </xsl:text> |
|
947 <xsl:for-each select="$detachable_elements"> |
|
948 <xsl:text> "</xsl:text> |
|
949 <xsl:value-of select="@id"/> |
|
950 <xsl:text>":[id("</xsl:text> |
|
951 <xsl:value-of select="@id"/> |
|
952 <xsl:text>"), id("</xsl:text> |
|
953 <xsl:value-of select="../@id"/> |
|
954 <xsl:text>")]</xsl:text> |
|
955 <xsl:if test="position()!=last()"> |
|
956 <xsl:text>,</xsl:text> |
|
957 </xsl:if> |
|
958 <xsl:text> |
|
959 </xsl:text> |
|
960 </xsl:for-each> |
|
961 <xsl:text>} |
|
962 </xsl:text> |
|
963 <xsl:text> |
|
964 </xsl:text> |
|
965 <xsl:text>var page_desc = { |
|
966 </xsl:text> |
|
967 <xsl:apply-templates mode="page_desc" select="$hmi_pages"/> |
|
968 <xsl:text>} |
|
969 </xsl:text> |
|
970 <xsl:text> |
|
971 </xsl:text> |
|
972 <xsl:text>var default_page = "</xsl:text> |
|
973 <xsl:value-of select="$default_page"/> |
|
974 <xsl:text>"; |
|
975 </xsl:text> |
|
976 <xsl:text>var svg_root = id("</xsl:text> |
|
977 <xsl:value-of select="/svg:svg/@id"/> |
|
978 <xsl:text>"); |
|
979 </xsl:text> |
|
980 <xsl:text>// svghmi.js |
|
981 </xsl:text> |
|
982 <xsl:text> |
|
983 </xsl:text> |
|
984 <xsl:text>var cache = hmitree_types.map(_ignored => undefined); |
|
985 </xsl:text> |
|
986 <xsl:text>var updates = {}; |
|
987 </xsl:text> |
|
988 <xsl:text> |
|
989 </xsl:text> |
|
990 <xsl:text>function dispatch_value_to_widget(widget, index, value, oldval) { |
|
991 </xsl:text> |
|
992 <xsl:text> try { |
|
993 </xsl:text> |
|
994 <xsl:text> let idx = widget.offset ? index - widget.offset : index; |
|
995 </xsl:text> |
|
996 <xsl:text> let idxidx = widget.indexes.indexOf(idx); |
|
997 </xsl:text> |
|
998 <xsl:text> let d = widget.dispatch; |
|
999 </xsl:text> |
|
1000 <xsl:text> console.log(index, idx, idxidx, value); |
|
1001 </xsl:text> |
|
1002 <xsl:text> if(typeof(d) == "function" && idxidx == 0){ |
|
1003 </xsl:text> |
|
1004 <xsl:text> d.call(widget, value, oldval); |
|
1005 </xsl:text> |
|
1006 <xsl:text> } |
|
1007 </xsl:text> |
|
1008 <xsl:text> else if(typeof(d) == "object" && d.length >= idxidx){ |
|
1009 </xsl:text> |
|
1010 <xsl:text> d[idxidx].call(widget, value, oldval); |
|
1011 </xsl:text> |
|
1012 <xsl:text> } |
|
1013 </xsl:text> |
|
1014 <xsl:text> /* else dispatch_0, ..., dispatch_n ? */ |
|
1015 </xsl:text> |
|
1016 <xsl:text> /*else { |
|
1017 </xsl:text> |
|
1018 <xsl:text> throw new Error("Dunno how to dispatch to widget at index = " + index); |
|
1019 </xsl:text> |
|
1020 <xsl:text> }*/ |
|
1021 </xsl:text> |
|
1022 <xsl:text> } catch(err) { |
|
1023 </xsl:text> |
|
1024 <xsl:text> console.log(err); |
|
1025 </xsl:text> |
|
1026 <xsl:text> } |
|
1027 </xsl:text> |
|
1028 <xsl:text>} |
|
1029 </xsl:text> |
|
1030 <xsl:text> |
|
1031 </xsl:text> |
|
1032 <xsl:text>function dispatch_value(index, value) { |
|
1033 </xsl:text> |
|
1034 <xsl:text> let widgets = subscribers[index]; |
|
1035 </xsl:text> |
|
1036 <xsl:text> |
|
1037 </xsl:text> |
|
1038 <xsl:text> let oldval = cache[index]; |
|
1039 </xsl:text> |
|
1040 <xsl:text> cache[index] = value; |
|
1041 </xsl:text> |
|
1042 <xsl:text> |
|
1043 </xsl:text> |
|
1044 <xsl:text> if(widgets.size > 0) { |
|
1045 </xsl:text> |
|
1046 <xsl:text> for(let widget of widgets){ |
|
1047 </xsl:text> |
|
1048 <xsl:text> dispatch_value_to_widget(widget, index, value, oldval); |
|
1049 </xsl:text> |
|
1050 <xsl:text> } |
|
1051 </xsl:text> |
|
1052 <xsl:text> } |
|
1053 </xsl:text> |
|
1054 <xsl:text>}; |
|
1055 </xsl:text> |
|
1056 <xsl:text> |
|
1057 </xsl:text> |
|
1058 <xsl:text>function init_widgets() { |
|
1059 </xsl:text> |
|
1060 <xsl:text> Object.keys(hmi_widgets).forEach(function(id) { |
|
1061 </xsl:text> |
|
1062 <xsl:text> let widget = hmi_widgets[id]; |
|
1063 </xsl:text> |
|
1064 <xsl:text> let init = widget.init; |
|
1065 </xsl:text> |
|
1066 <xsl:text> if(typeof(init) == "function"){ |
|
1067 </xsl:text> |
|
1068 <xsl:text> try { |
|
1069 </xsl:text> |
|
1070 <xsl:text> init.call(widget); |
|
1071 </xsl:text> |
|
1072 <xsl:text> } catch(err) { |
|
1073 </xsl:text> |
|
1074 <xsl:text> console.log(err); |
|
1075 </xsl:text> |
|
1076 <xsl:text> } |
|
1077 </xsl:text> |
|
1078 <xsl:text> } |
|
1079 </xsl:text> |
|
1080 <xsl:text> }); |
|
1081 </xsl:text> |
|
1082 <xsl:text>}; |
|
1083 </xsl:text> |
|
1084 <xsl:text> |
|
1085 </xsl:text> |
|
1086 <xsl:text>// Open WebSocket to relative "/ws" address |
|
1087 </xsl:text> |
|
1088 <xsl:text>var ws = new WebSocket(window.location.href.replace(/^http(s?:\/\/[^\/]*)\/.*$/, 'ws$1/ws')); |
|
1089 </xsl:text> |
|
1090 <xsl:text>ws.binaryType = 'arraybuffer'; |
|
1091 </xsl:text> |
|
1092 <xsl:text> |
|
1093 </xsl:text> |
|
1094 <xsl:text>const dvgetters = { |
|
1095 </xsl:text> |
|
1096 <xsl:text> INT: (dv,offset) => [dv.getInt16(offset, true), 2], |
|
1097 </xsl:text> |
|
1098 <xsl:text> BOOL: (dv,offset) => [dv.getInt8(offset, true), 1], |
|
1099 </xsl:text> |
|
1100 <xsl:text> STRING: (dv, offset) => { |
|
1101 </xsl:text> |
|
1102 <xsl:text> size = dv.getInt8(offset); |
|
1103 </xsl:text> |
|
1104 <xsl:text> return [ |
|
1105 </xsl:text> |
|
1106 <xsl:text> String.fromCharCode.apply(null, new Uint8Array( |
|
1107 </xsl:text> |
|
1108 <xsl:text> dv.buffer, /* original buffer */ |
|
1109 </xsl:text> |
|
1110 <xsl:text> offset + 1, /* string starts after size*/ |
|
1111 </xsl:text> |
|
1112 <xsl:text> size /* size of string */ |
|
1113 </xsl:text> |
|
1114 <xsl:text> )), size + 1]; /* total increment */ |
|
1115 </xsl:text> |
|
1116 <xsl:text> } |
|
1117 </xsl:text> |
|
1118 <xsl:text>}; |
|
1119 </xsl:text> |
|
1120 <xsl:text> |
|
1121 </xsl:text> |
|
1122 <xsl:text>// Apply updates recieved through ws.onmessage to subscribed widgets |
|
1123 </xsl:text> |
|
1124 <xsl:text>function apply_updates() { |
|
1125 </xsl:text> |
|
1126 <xsl:text> for(let index in updates){ |
|
1127 </xsl:text> |
|
1128 <xsl:text> // serving as a key, index becomes a string |
|
1129 </xsl:text> |
|
1130 <xsl:text> // -> pass Number(index) instead |
|
1131 </xsl:text> |
|
1132 <xsl:text> dispatch_value(Number(index), updates[index]); |
|
1133 </xsl:text> |
|
1134 <xsl:text> delete updates[index]; |
|
1135 </xsl:text> |
|
1136 <xsl:text> } |
|
1137 </xsl:text> |
|
1138 <xsl:text>} |
|
1139 </xsl:text> |
|
1140 <xsl:text> |
|
1141 </xsl:text> |
|
1142 <xsl:text>// Called on requestAnimationFrame, modifies DOM |
|
1143 </xsl:text> |
|
1144 <xsl:text>var requestAnimationFrameID = null; |
|
1145 </xsl:text> |
|
1146 <xsl:text>function animate() { |
|
1147 </xsl:text> |
|
1148 <xsl:text> // Do the page swith if any one pending |
|
1149 </xsl:text> |
|
1150 <xsl:text> if(current_subscribed_page != current_visible_page){ |
|
1151 </xsl:text> |
|
1152 <xsl:text> switch_visible_page(current_subscribed_page); |
|
1153 </xsl:text> |
|
1154 <xsl:text> } |
|
1155 </xsl:text> |
|
1156 <xsl:text> apply_updates(); |
|
1157 </xsl:text> |
|
1158 <xsl:text> requestAnimationFrameID = null; |
|
1159 </xsl:text> |
|
1160 <xsl:text>} |
|
1161 </xsl:text> |
|
1162 <xsl:text> |
|
1163 </xsl:text> |
|
1164 <xsl:text>function requestHMIAnimation() { |
|
1165 </xsl:text> |
|
1166 <xsl:text> if(requestAnimationFrameID == null){ |
|
1167 </xsl:text> |
|
1168 <xsl:text> requestAnimationFrameID = window.requestAnimationFrame(animate); |
|
1169 </xsl:text> |
|
1170 <xsl:text> } |
|
1171 </xsl:text> |
|
1172 <xsl:text>} |
|
1173 </xsl:text> |
|
1174 <xsl:text> |
|
1175 </xsl:text> |
|
1176 <xsl:text>// Message reception handler |
|
1177 </xsl:text> |
|
1178 <xsl:text>// Hash is verified and HMI values updates resulting from binary parsing |
|
1179 </xsl:text> |
|
1180 <xsl:text>// are stored until browser can compute next frame, DOM is left untouched |
|
1181 </xsl:text> |
|
1182 <xsl:text>ws.onmessage = function (evt) { |
|
1183 </xsl:text> |
|
1184 <xsl:text> |
|
1185 </xsl:text> |
|
1186 <xsl:text> let data = evt.data; |
|
1187 </xsl:text> |
|
1188 <xsl:text> let dv = new DataView(data); |
|
1189 </xsl:text> |
|
1190 <xsl:text> let i = 0; |
|
1191 </xsl:text> |
|
1192 <xsl:text> try { |
|
1193 </xsl:text> |
|
1194 <xsl:text> for(let hash_int of hmi_hash) { |
|
1195 </xsl:text> |
|
1196 <xsl:text> if(hash_int != dv.getUint8(i)){ |
|
1197 </xsl:text> |
|
1198 <xsl:text> throw new Error("Hash doesn't match"); |
|
1199 </xsl:text> |
|
1200 <xsl:text> }; |
|
1201 </xsl:text> |
|
1202 <xsl:text> i++; |
|
1203 </xsl:text> |
|
1204 <xsl:text> }; |
|
1205 </xsl:text> |
|
1206 <xsl:text> |
|
1207 </xsl:text> |
|
1208 <xsl:text> while(i < data.byteLength){ |
|
1209 </xsl:text> |
|
1210 <xsl:text> let index = dv.getUint32(i, true); |
|
1211 </xsl:text> |
|
1212 <xsl:text> i += 4; |
|
1213 </xsl:text> |
|
1214 <xsl:text> let iectype = hmitree_types[index]; |
|
1215 </xsl:text> |
|
1216 <xsl:text> if(iectype != undefined){ |
|
1217 </xsl:text> |
|
1218 <xsl:text> let dvgetter = dvgetters[iectype]; |
|
1219 </xsl:text> |
|
1220 <xsl:text> let [value, bytesize] = dvgetter(dv,i); |
|
1221 </xsl:text> |
|
1222 <xsl:text> updates[index] = value; |
|
1223 </xsl:text> |
|
1224 <xsl:text> i += bytesize; |
|
1225 </xsl:text> |
|
1226 <xsl:text> } else { |
|
1227 </xsl:text> |
|
1228 <xsl:text> throw new Error("Unknown index "+index); |
|
1229 </xsl:text> |
|
1230 <xsl:text> } |
|
1231 </xsl:text> |
|
1232 <xsl:text> }; |
|
1233 </xsl:text> |
|
1234 <xsl:text> // register for rendering on next frame, since there are updates |
|
1235 </xsl:text> |
|
1236 <xsl:text> requestHMIAnimation(); |
|
1237 </xsl:text> |
|
1238 <xsl:text> } catch(err) { |
|
1239 </xsl:text> |
|
1240 <xsl:text> // 1003 is for "Unsupported Data" |
|
1241 </xsl:text> |
|
1242 <xsl:text> // ws.close(1003, err.message); |
|
1243 </xsl:text> |
|
1244 <xsl:text> |
|
1245 </xsl:text> |
|
1246 <xsl:text> // TODO : remove debug alert ? |
|
1247 </xsl:text> |
|
1248 <xsl:text> alert("Error : "+err.message+"\nHMI will be reloaded."); |
|
1249 </xsl:text> |
|
1250 <xsl:text> |
|
1251 </xsl:text> |
|
1252 <xsl:text> // force reload ignoring cache |
|
1253 </xsl:text> |
|
1254 <xsl:text> location.reload(true); |
|
1255 </xsl:text> |
|
1256 <xsl:text> } |
|
1257 </xsl:text> |
|
1258 <xsl:text>}; |
|
1259 </xsl:text> |
|
1260 <xsl:text> |
|
1261 </xsl:text> |
|
1262 <xsl:text> |
|
1263 </xsl:text> |
|
1264 <xsl:text>function send_blob(data) { |
|
1265 </xsl:text> |
|
1266 <xsl:text> if(data.length > 0) { |
|
1267 </xsl:text> |
|
1268 <xsl:text> ws.send(new Blob([new Uint8Array(hmi_hash)].concat(data))); |
|
1269 </xsl:text> |
|
1270 <xsl:text> }; |
|
1271 </xsl:text> |
|
1272 <xsl:text>}; |
|
1273 </xsl:text> |
|
1274 <xsl:text> |
|
1275 </xsl:text> |
|
1276 <xsl:text>const typedarray_types = { |
|
1277 </xsl:text> |
|
1278 <xsl:text> INT: (number) => new Int16Array([number]), |
|
1279 </xsl:text> |
|
1280 <xsl:text> BOOL: (truth) => new Int16Array([truth]), |
|
1281 </xsl:text> |
|
1282 <xsl:text> STRING: (str) => { |
|
1283 </xsl:text> |
|
1284 <xsl:text> // beremiz default string max size is 128 |
|
1285 </xsl:text> |
|
1286 <xsl:text> str = str.slice(0,128); |
|
1287 </xsl:text> |
|
1288 <xsl:text> binary = new Uint8Array(str.length + 1); |
|
1289 </xsl:text> |
|
1290 <xsl:text> binary[0] = str.length; |
|
1291 </xsl:text> |
|
1292 <xsl:text> for(var i = 0; i < str.length; i++){ |
|
1293 </xsl:text> |
|
1294 <xsl:text> binary[i+1] = str.charCodeAt(i); |
|
1295 </xsl:text> |
|
1296 <xsl:text> } |
|
1297 </xsl:text> |
|
1298 <xsl:text> return binary; |
|
1299 </xsl:text> |
|
1300 <xsl:text> } |
|
1301 </xsl:text> |
|
1302 <xsl:text> /* TODO */ |
|
1303 </xsl:text> |
|
1304 <xsl:text>}; |
|
1305 </xsl:text> |
|
1306 <xsl:text> |
|
1307 </xsl:text> |
|
1308 <xsl:text>function send_reset() { |
|
1309 </xsl:text> |
|
1310 <xsl:text> send_blob(new Uint8Array([1])); /* reset = 1 */ |
|
1311 </xsl:text> |
|
1312 <xsl:text>}; |
|
1313 </xsl:text> |
|
1314 <xsl:text> |
|
1315 </xsl:text> |
|
1316 <xsl:text>// subscription state, as it should be in hmi server |
|
1317 </xsl:text> |
|
1318 <xsl:text>// hmitree indexed array of integers |
|
1319 </xsl:text> |
|
1320 <xsl:text>var subscriptions = hmitree_types.map(_ignored => 0); |
|
1321 </xsl:text> |
|
1322 <xsl:text> |
|
1323 </xsl:text> |
|
1324 <xsl:text>// subscription state as needed by widget now |
|
1325 </xsl:text> |
|
1326 <xsl:text>// hmitree indexed array of Sets of widgets objects |
|
1327 </xsl:text> |
|
1328 <xsl:text>var subscribers = hmitree_types.map(_ignored => new Set()); |
|
1329 </xsl:text> |
|
1330 <xsl:text> |
|
1331 </xsl:text> |
|
1332 <xsl:text>// artificially subscribe the watchdog widget to "/heartbeat" hmi variable |
|
1333 </xsl:text> |
|
1334 <xsl:text>// Since dispatch directly calls change_hmi_value, |
|
1335 </xsl:text> |
|
1336 <xsl:text>// PLC will periodically send variable at given frequency |
|
1337 </xsl:text> |
|
1338 <xsl:text>subscribers[heartbeat_index].add({ |
|
1339 </xsl:text> |
|
1340 <xsl:text> /* type: "Watchdog", */ |
|
1341 </xsl:text> |
|
1342 <xsl:text> frequency: 1, |
|
1343 </xsl:text> |
|
1344 <xsl:text> indexes: [heartbeat_index], |
|
1345 </xsl:text> |
|
1346 <xsl:text> dispatch: function(value) { |
|
1347 </xsl:text> |
|
1348 <xsl:text> // console.log("Heartbeat" + value); |
|
1349 </xsl:text> |
|
1350 <xsl:text> change_hmi_value(heartbeat_index, "+1"); |
|
1351 </xsl:text> |
|
1352 <xsl:text> } |
|
1353 </xsl:text> |
|
1354 <xsl:text>}); |
|
1355 </xsl:text> |
|
1356 <xsl:text> |
|
1357 </xsl:text> |
|
1358 <xsl:text>function update_subscriptions() { |
|
1359 </xsl:text> |
|
1360 <xsl:text> let delta = []; |
|
1361 </xsl:text> |
|
1362 <xsl:text> for(let index = 0; index < subscribers.length; index++){ |
|
1363 </xsl:text> |
|
1364 <xsl:text> let widgets = subscribers[index]; |
|
1365 </xsl:text> |
|
1366 <xsl:text> |
|
1367 </xsl:text> |
|
1368 <xsl:text> // periods are in ms |
|
1369 </xsl:text> |
|
1370 <xsl:text> let previous_period = subscriptions[index]; |
|
1371 </xsl:text> |
|
1372 <xsl:text> |
|
1373 </xsl:text> |
|
1374 <xsl:text> // subscribing with a zero period is unsubscribing |
|
1375 </xsl:text> |
|
1376 <xsl:text> let new_period = 0; |
|
1377 </xsl:text> |
|
1378 <xsl:text> if(widgets.size > 0) { |
|
1379 </xsl:text> |
|
1380 <xsl:text> let maxfreq = 0; |
|
1381 </xsl:text> |
|
1382 <xsl:text> for(let widget of widgets) |
|
1383 </xsl:text> |
|
1384 <xsl:text> if(maxfreq < widget.frequency) |
|
1385 </xsl:text> |
|
1386 <xsl:text> maxfreq = widget.frequency; |
|
1387 </xsl:text> |
|
1388 <xsl:text> |
|
1389 </xsl:text> |
|
1390 <xsl:text> if(maxfreq != 0) |
|
1391 </xsl:text> |
|
1392 <xsl:text> new_period = 1000/maxfreq; |
|
1393 </xsl:text> |
|
1394 <xsl:text> } |
|
1395 </xsl:text> |
|
1396 <xsl:text> |
|
1397 </xsl:text> |
|
1398 <xsl:text> if(previous_period != new_period) { |
|
1399 </xsl:text> |
|
1400 <xsl:text> subscriptions[index] = new_period; |
|
1401 </xsl:text> |
|
1402 <xsl:text> delta.push( |
|
1403 </xsl:text> |
|
1404 <xsl:text> new Uint8Array([2]), /* subscribe = 2 */ |
|
1405 </xsl:text> |
|
1406 <xsl:text> new Uint32Array([index]), |
|
1407 </xsl:text> |
|
1408 <xsl:text> new Uint16Array([new_period])); |
|
1409 </xsl:text> |
|
1410 <xsl:text> } |
|
1411 </xsl:text> |
|
1412 <xsl:text> } |
|
1413 </xsl:text> |
|
1414 <xsl:text> send_blob(delta); |
|
1415 </xsl:text> |
|
1416 <xsl:text>}; |
|
1417 </xsl:text> |
|
1418 <xsl:text> |
|
1419 </xsl:text> |
|
1420 <xsl:text>function send_hmi_value(index, value) { |
|
1421 </xsl:text> |
|
1422 <xsl:text> let iectype = hmitree_types[index]; |
|
1423 </xsl:text> |
|
1424 <xsl:text> let tobinary = typedarray_types[iectype]; |
|
1425 </xsl:text> |
|
1426 <xsl:text> send_blob([ |
|
1427 </xsl:text> |
|
1428 <xsl:text> new Uint8Array([0]), /* setval = 0 */ |
|
1429 </xsl:text> |
|
1430 <xsl:text> new Uint32Array([index]), |
|
1431 </xsl:text> |
|
1432 <xsl:text> tobinary(value)]); |
|
1433 </xsl:text> |
|
1434 <xsl:text> |
|
1435 </xsl:text> |
|
1436 <xsl:text> cache[index] = value; |
|
1437 </xsl:text> |
|
1438 <xsl:text>}; |
|
1439 </xsl:text> |
|
1440 <xsl:text> |
|
1441 </xsl:text> |
|
1442 <xsl:text>function change_hmi_value(index, opstr) { |
|
1443 </xsl:text> |
|
1444 <xsl:text> let op = opstr[0]; |
|
1445 </xsl:text> |
|
1446 <xsl:text> let given_val = opstr.slice(1); |
|
1447 </xsl:text> |
|
1448 <xsl:text> let old_val = cache[index] |
|
1449 </xsl:text> |
|
1450 <xsl:text> let new_val; |
|
1451 </xsl:text> |
|
1452 <xsl:text> switch(op){ |
|
1453 </xsl:text> |
|
1454 <xsl:text> case "=": |
|
1455 </xsl:text> |
|
1456 <xsl:text> eval("new_val"+opstr); |
|
1457 </xsl:text> |
|
1458 <xsl:text> break; |
|
1459 </xsl:text> |
|
1460 <xsl:text> case "+": |
|
1461 </xsl:text> |
|
1462 <xsl:text> case "-": |
|
1463 </xsl:text> |
|
1464 <xsl:text> case "*": |
|
1465 </xsl:text> |
|
1466 <xsl:text> case "/": |
|
1467 </xsl:text> |
|
1468 <xsl:text> if(old_val != undefined) |
|
1469 </xsl:text> |
|
1470 <xsl:text> new_val = eval("old_val"+opstr); |
|
1471 </xsl:text> |
|
1472 <xsl:text> break; |
|
1473 </xsl:text> |
|
1474 <xsl:text> } |
|
1475 </xsl:text> |
|
1476 <xsl:text> if(new_val != undefined && old_val != new_val) |
|
1477 </xsl:text> |
|
1478 <xsl:text> send_hmi_value(index, new_val); |
|
1479 </xsl:text> |
|
1480 <xsl:text> return new_val; |
|
1481 </xsl:text> |
|
1482 <xsl:text>} |
|
1483 </xsl:text> |
|
1484 <xsl:text> |
|
1485 </xsl:text> |
|
1486 <xsl:text>var current_visible_page; |
|
1487 </xsl:text> |
|
1488 <xsl:text>var current_subscribed_page; |
|
1489 </xsl:text> |
|
1490 <xsl:text> |
|
1491 </xsl:text> |
|
1492 <xsl:text>function prepare_svg() { |
|
1493 </xsl:text> |
|
1494 <xsl:text> for(let eltid in detachable_elements){ |
|
1495 </xsl:text> |
|
1496 <xsl:text> let [element,parent] = detachable_elements[eltid]; |
|
1497 </xsl:text> |
|
1498 <xsl:text> parent.removeChild(element); |
|
1499 </xsl:text> |
|
1500 <xsl:text> } |
|
1501 </xsl:text> |
|
1502 <xsl:text>}; |
|
1503 </xsl:text> |
|
1504 <xsl:text> |
|
1505 </xsl:text> |
|
1506 <xsl:text>function switch_page(page_name, page_index) { |
|
1507 </xsl:text> |
|
1508 <xsl:text> if(current_subscribed_page != current_visible_page){ |
|
1509 </xsl:text> |
|
1510 <xsl:text> /* page switch already going */ |
|
1511 </xsl:text> |
|
1512 <xsl:text> /* TODO LOG ERROR */ |
|
1513 </xsl:text> |
|
1514 <xsl:text> return; |
|
1515 </xsl:text> |
|
1516 <xsl:text> } else if(page_name == current_visible_page){ |
|
1517 </xsl:text> |
|
1518 <xsl:text> /* already in that page */ |
|
1519 </xsl:text> |
|
1520 <xsl:text> /* TODO LOG ERROR */ |
|
1521 </xsl:text> |
|
1522 <xsl:text> return; |
|
1523 </xsl:text> |
|
1524 <xsl:text> } |
|
1525 </xsl:text> |
|
1526 <xsl:text> switch_subscribed_page(page_name, page_index); |
|
1527 </xsl:text> |
|
1528 <xsl:text>}; |
|
1529 </xsl:text> |
|
1530 <xsl:text> |
|
1531 </xsl:text> |
|
1532 <xsl:text>function* chain(a,b){ |
|
1533 </xsl:text> |
|
1534 <xsl:text> yield* a; |
|
1535 </xsl:text> |
|
1536 <xsl:text> yield* b; |
|
1537 </xsl:text> |
|
1538 <xsl:text>}; |
|
1539 </xsl:text> |
|
1540 <xsl:text> |
|
1541 </xsl:text> |
|
1542 <xsl:text>function switch_subscribed_page(page_name, page_index) { |
|
1543 </xsl:text> |
|
1544 <xsl:text> let old_desc = page_desc[current_subscribed_page]; |
|
1545 </xsl:text> |
|
1546 <xsl:text> let new_desc = page_desc[page_name]; |
|
1547 </xsl:text> |
|
1548 <xsl:text> |
|
1549 </xsl:text> |
|
1550 <xsl:text> if(new_desc == undefined){ |
|
1551 </xsl:text> |
|
1552 <xsl:text> /* TODO LOG ERROR */ |
|
1553 </xsl:text> |
|
1554 <xsl:text> return; |
|
1555 </xsl:text> |
|
1556 <xsl:text> } |
|
1557 </xsl:text> |
|
1558 <xsl:text> |
|
1559 </xsl:text> |
|
1560 <xsl:text> if(page_index == undefined){ |
|
1561 </xsl:text> |
|
1562 <xsl:text> page_index = new_desc.page_index; |
|
1563 </xsl:text> |
|
1564 <xsl:text> } |
|
1565 </xsl:text> |
|
1566 <xsl:text> |
|
1567 </xsl:text> |
|
1568 <xsl:text> if(old_desc){ |
|
1569 </xsl:text> |
|
1570 <xsl:text> for(let widget of old_desc.absolute_widgets){ |
|
1571 </xsl:text> |
|
1572 <xsl:text> /* remove subsribers */ |
|
1573 </xsl:text> |
|
1574 <xsl:text> for(let index of widget.indexes){ |
|
1575 </xsl:text> |
|
1576 <xsl:text> subscribers[index].delete(widget); |
|
1577 </xsl:text> |
|
1578 <xsl:text> } |
|
1579 </xsl:text> |
|
1580 <xsl:text> } |
|
1581 </xsl:text> |
|
1582 <xsl:text> for(let widget of old_desc.relative_widgets){ |
|
1583 </xsl:text> |
|
1584 <xsl:text> /* remove subsribers */ |
|
1585 </xsl:text> |
|
1586 <xsl:text> for(let index of widget.indexes){ |
|
1587 </xsl:text> |
|
1588 <xsl:text> let idx = widget.offset ? index + widget.offset : index; |
|
1589 </xsl:text> |
|
1590 <xsl:text> subscribers[idx].delete(widget); |
|
1591 </xsl:text> |
|
1592 <xsl:text> } |
|
1593 </xsl:text> |
|
1594 <xsl:text> /* lose the offset */ |
|
1595 </xsl:text> |
|
1596 <xsl:text> delete widget.offset; |
|
1597 </xsl:text> |
|
1598 <xsl:text> } |
|
1599 </xsl:text> |
|
1600 <xsl:text> } |
|
1601 </xsl:text> |
|
1602 <xsl:text> for(let widget of new_desc.absolute_widgets){ |
|
1603 </xsl:text> |
|
1604 <xsl:text> /* add widget's subsribers */ |
|
1605 </xsl:text> |
|
1606 <xsl:text> for(let index of widget.indexes){ |
|
1607 </xsl:text> |
|
1608 <xsl:text> subscribers[index].add(widget); |
|
1609 </xsl:text> |
|
1610 <xsl:text> } |
|
1611 </xsl:text> |
|
1612 <xsl:text> } |
|
1613 </xsl:text> |
|
1614 <xsl:text> var new_offset = page_index == undefined ? 0 : page_index - new_desc.page_index; |
|
1615 </xsl:text> |
|
1616 <xsl:text> for(let widget of new_desc.relative_widgets){ |
|
1617 </xsl:text> |
|
1618 <xsl:text> /* set the offset because relative */ |
|
1619 </xsl:text> |
|
1620 <xsl:text> widget.offset = new_offset; |
|
1621 </xsl:text> |
|
1622 <xsl:text> /* add widget's subsribers */ |
|
1623 </xsl:text> |
|
1624 <xsl:text> for(let index of widget.indexes){ |
|
1625 </xsl:text> |
|
1626 <xsl:text> subscribers[index + new_offset].add(widget); |
|
1627 </xsl:text> |
|
1628 <xsl:text> } |
|
1629 </xsl:text> |
|
1630 <xsl:text> } |
|
1631 </xsl:text> |
|
1632 <xsl:text> |
|
1633 </xsl:text> |
|
1634 <xsl:text> update_subscriptions(); |
|
1635 </xsl:text> |
|
1636 <xsl:text> |
|
1637 </xsl:text> |
|
1638 <xsl:text> current_subscribed_page = page_name; |
|
1639 </xsl:text> |
|
1640 <xsl:text> |
|
1641 </xsl:text> |
|
1642 <xsl:text> requestHMIAnimation(); |
|
1643 </xsl:text> |
|
1644 <xsl:text>} |
|
1645 </xsl:text> |
|
1646 <xsl:text> |
|
1647 </xsl:text> |
|
1648 <xsl:text>function switch_visible_page(page_name) { |
|
1649 </xsl:text> |
|
1650 <xsl:text> |
|
1651 </xsl:text> |
|
1652 <xsl:text> let old_desc = page_desc[current_visible_page]; |
|
1653 </xsl:text> |
|
1654 <xsl:text> let new_desc = page_desc[page_name]; |
|
1655 </xsl:text> |
|
1656 <xsl:text> |
|
1657 </xsl:text> |
|
1658 <xsl:text> if(old_desc){ |
|
1659 </xsl:text> |
|
1660 <xsl:text> for(let eltid in old_desc.required_detachables){ |
|
1661 </xsl:text> |
|
1662 <xsl:text> if(!(eltid in new_desc.required_detachables)){ |
|
1663 </xsl:text> |
|
1664 <xsl:text> let [element, parent] = old_desc.required_detachables[eltid]; |
|
1665 </xsl:text> |
|
1666 <xsl:text> parent.removeChild(element); |
|
1667 </xsl:text> |
|
1668 <xsl:text> } |
|
1669 </xsl:text> |
|
1670 <xsl:text> } |
|
1671 </xsl:text> |
|
1672 <xsl:text> for(let eltid in new_desc.required_detachables){ |
|
1673 </xsl:text> |
|
1674 <xsl:text> if(!(eltid in old_desc.required_detachables)){ |
|
1675 </xsl:text> |
|
1676 <xsl:text> let [element, parent] = new_desc.required_detachables[eltid]; |
|
1677 </xsl:text> |
|
1678 <xsl:text> parent.appendChild(element); |
|
1679 </xsl:text> |
|
1680 <xsl:text> } |
|
1681 </xsl:text> |
|
1682 <xsl:text> } |
|
1683 </xsl:text> |
|
1684 <xsl:text> }else{ |
|
1685 </xsl:text> |
|
1686 <xsl:text> for(let eltid in new_desc.required_detachables){ |
|
1687 </xsl:text> |
|
1688 <xsl:text> let [element, parent] = new_desc.required_detachables[eltid]; |
|
1689 </xsl:text> |
|
1690 <xsl:text> parent.appendChild(element); |
|
1691 </xsl:text> |
|
1692 <xsl:text> } |
|
1693 </xsl:text> |
|
1694 <xsl:text> } |
|
1695 </xsl:text> |
|
1696 <xsl:text> |
|
1697 </xsl:text> |
|
1698 <xsl:text> for(let widget of chain(new_desc.absolute_widgets,new_desc.relative_widgets)){ |
|
1699 </xsl:text> |
|
1700 <xsl:text> for(let index of widget.indexes){ |
|
1701 </xsl:text> |
|
1702 <xsl:text> /* dispatch current cache in newly opened page widgets */ |
|
1703 </xsl:text> |
|
1704 <xsl:text> let cached_val = cache[index]; |
|
1705 </xsl:text> |
|
1706 <xsl:text> if(cached_val != undefined) |
|
1707 </xsl:text> |
|
1708 <xsl:text> dispatch_value_to_widget(widget, index, cached_val, cached_val); |
|
1709 </xsl:text> |
|
1710 <xsl:text> } |
|
1711 </xsl:text> |
|
1712 <xsl:text> } |
|
1713 </xsl:text> |
|
1714 <xsl:text> |
|
1715 </xsl:text> |
|
1716 <xsl:text> svg_root.setAttribute('viewBox',new_desc.bbox.join(" ")); |
|
1717 </xsl:text> |
|
1718 <xsl:text> current_visible_page = page_name; |
|
1719 </xsl:text> |
|
1720 <xsl:text>}; |
|
1721 </xsl:text> |
|
1722 <xsl:text> |
|
1723 </xsl:text> |
|
1724 <xsl:text> |
|
1725 </xsl:text> |
|
1726 <xsl:text>// Once connection established |
|
1727 </xsl:text> |
|
1728 <xsl:text>ws.onopen = function (evt) { |
|
1729 </xsl:text> |
|
1730 <xsl:text> init_widgets(); |
|
1731 </xsl:text> |
|
1732 <xsl:text> send_reset(); |
|
1733 </xsl:text> |
|
1734 <xsl:text> // show main page |
|
1735 </xsl:text> |
|
1736 <xsl:text> prepare_svg(); |
|
1737 </xsl:text> |
|
1738 <xsl:text> switch_page(default_page); |
|
1739 </xsl:text> |
|
1740 <xsl:text>}; |
|
1741 </xsl:text> |
|
1742 <xsl:text> |
|
1743 </xsl:text> |
|
1744 <xsl:text>ws.onclose = function (evt) { |
|
1745 </xsl:text> |
|
1746 <xsl:text> // TODO : add visible notification while waiting for reload |
|
1747 </xsl:text> |
|
1748 <xsl:text> console.log("Connection closed. code:"+evt.code+" reason:"+evt.reason+" wasClean:"+evt.wasClean+" Reload in 10s."); |
|
1749 </xsl:text> |
|
1750 <xsl:text> // TODO : re-enable auto reload when not in debug |
|
1751 </xsl:text> |
|
1752 <xsl:text> //window.setTimeout(() => location.reload(true), 10000); |
|
1753 </xsl:text> |
|
1754 <xsl:text> alert("Connection closed. code:"+evt.code+" reason:"+evt.reason+" wasClean:"+evt.wasClean+"."); |
|
1755 </xsl:text> |
|
1756 <xsl:text> |
|
1757 </xsl:text> |
|
1758 <xsl:text>}; |
1768 </xsl:text> |
1759 </xsl:text> |
1769 </xsl:template> |
1760 </xsl:template> |
1770 </xsl:stylesheet> |
1761 </xsl:stylesheet> |