6893 </xsl:text> |
6907 </xsl:text> |
6894 <xsl:text>// Apply updates recieved through ws.onmessage to subscribed widgets |
6908 <xsl:text>// Apply updates recieved through ws.onmessage to subscribed widgets |
6895 </xsl:text> |
6909 </xsl:text> |
6896 <xsl:text>function apply_updates() { |
6910 <xsl:text>function apply_updates() { |
6897 </xsl:text> |
6911 </xsl:text> |
6898 <xsl:text> for(let index in updates){ |
6912 <xsl:text> updates.forEach((value, index) => { |
6899 </xsl:text> |
6913 </xsl:text> |
6900 <xsl:text> // serving as a key, index becomes a string |
6914 <xsl:text> dispatch_value(index, value); |
6901 </xsl:text> |
6915 </xsl:text> |
6902 <xsl:text> // -> pass Number(index) instead |
6916 <xsl:text> }); |
6903 </xsl:text> |
6917 </xsl:text> |
6904 <xsl:text> dispatch_value(Number(index), updates[index]); |
6918 <xsl:text> updates.clear(); |
6905 </xsl:text> |
6919 </xsl:text> |
6906 <xsl:text> delete updates[index]; |
6920 <xsl:text>} |
|
6921 </xsl:text> |
|
6922 <xsl:text> |
|
6923 </xsl:text> |
|
6924 <xsl:text>// Called on requestAnimationFrame, modifies DOM |
|
6925 </xsl:text> |
|
6926 <xsl:text>var requestAnimationFrameID = null; |
|
6927 </xsl:text> |
|
6928 <xsl:text>function animate() { |
|
6929 </xsl:text> |
|
6930 <xsl:text> // Do the page swith if any one pending |
|
6931 </xsl:text> |
|
6932 <xsl:text> if(current_subscribed_page != current_visible_page){ |
|
6933 </xsl:text> |
|
6934 <xsl:text> switch_visible_page(current_subscribed_page); |
6907 </xsl:text> |
6935 </xsl:text> |
6908 <xsl:text> } |
6936 <xsl:text> } |
6909 </xsl:text> |
6937 </xsl:text> |
|
6938 <xsl:text> |
|
6939 </xsl:text> |
|
6940 <xsl:text> while(widget = need_cache_apply.pop()){ |
|
6941 </xsl:text> |
|
6942 <xsl:text> widget.apply_cache(); |
|
6943 </xsl:text> |
|
6944 <xsl:text> } |
|
6945 </xsl:text> |
|
6946 <xsl:text> |
|
6947 </xsl:text> |
|
6948 <xsl:text> if(jumps_need_update) update_jumps(); |
|
6949 </xsl:text> |
|
6950 <xsl:text> |
|
6951 </xsl:text> |
|
6952 <xsl:text> apply_updates(); |
|
6953 </xsl:text> |
|
6954 <xsl:text> |
|
6955 </xsl:text> |
|
6956 <xsl:text> pending_widget_animates.forEach(widget => widget._animate()); |
|
6957 </xsl:text> |
|
6958 <xsl:text> pending_widget_animates = []; |
|
6959 </xsl:text> |
|
6960 <xsl:text> |
|
6961 </xsl:text> |
|
6962 <xsl:text> requestAnimationFrameID = null; |
|
6963 </xsl:text> |
6910 <xsl:text>} |
6964 <xsl:text>} |
6911 </xsl:text> |
6965 </xsl:text> |
6912 <xsl:text> |
6966 <xsl:text> |
6913 </xsl:text> |
6967 </xsl:text> |
6914 <xsl:text>// Called on requestAnimationFrame, modifies DOM |
6968 <xsl:text>function requestHMIAnimation() { |
6915 </xsl:text> |
6969 </xsl:text> |
6916 <xsl:text>var requestAnimationFrameID = null; |
6970 <xsl:text> if(requestAnimationFrameID == null){ |
6917 </xsl:text> |
6971 </xsl:text> |
6918 <xsl:text>function animate() { |
6972 <xsl:text> requestAnimationFrameID = window.requestAnimationFrame(animate); |
6919 </xsl:text> |
|
6920 <xsl:text> // Do the page swith if any one pending |
|
6921 </xsl:text> |
|
6922 <xsl:text> if(current_subscribed_page != current_visible_page){ |
|
6923 </xsl:text> |
|
6924 <xsl:text> switch_visible_page(current_subscribed_page); |
|
6925 </xsl:text> |
6973 </xsl:text> |
6926 <xsl:text> } |
6974 <xsl:text> } |
6927 </xsl:text> |
6975 </xsl:text> |
6928 <xsl:text> |
6976 <xsl:text>} |
6929 </xsl:text> |
6977 </xsl:text> |
6930 <xsl:text> while(widget = need_cache_apply.pop()){ |
6978 <xsl:text> |
6931 </xsl:text> |
6979 </xsl:text> |
6932 <xsl:text> widget.apply_cache(); |
6980 <xsl:text>// Message reception handler |
|
6981 </xsl:text> |
|
6982 <xsl:text>// Hash is verified and HMI values updates resulting from binary parsing |
|
6983 </xsl:text> |
|
6984 <xsl:text>// are stored until browser can compute next frame, DOM is left untouched |
|
6985 </xsl:text> |
|
6986 <xsl:text>ws.onmessage = function (evt) { |
|
6987 </xsl:text> |
|
6988 <xsl:text> |
|
6989 </xsl:text> |
|
6990 <xsl:text> let data = evt.data; |
|
6991 </xsl:text> |
|
6992 <xsl:text> let dv = new DataView(data); |
|
6993 </xsl:text> |
|
6994 <xsl:text> let i = 0; |
|
6995 </xsl:text> |
|
6996 <xsl:text> try { |
|
6997 </xsl:text> |
|
6998 <xsl:text> for(let hash_int of hmi_hash) { |
|
6999 </xsl:text> |
|
7000 <xsl:text> if(hash_int != dv.getUint8(i)){ |
|
7001 </xsl:text> |
|
7002 <xsl:text> throw new Error("Hash doesn't match"); |
|
7003 </xsl:text> |
|
7004 <xsl:text> }; |
|
7005 </xsl:text> |
|
7006 <xsl:text> i++; |
|
7007 </xsl:text> |
|
7008 <xsl:text> }; |
|
7009 </xsl:text> |
|
7010 <xsl:text> |
|
7011 </xsl:text> |
|
7012 <xsl:text> while(i < data.byteLength){ |
|
7013 </xsl:text> |
|
7014 <xsl:text> let index = dv.getUint32(i, true); |
|
7015 </xsl:text> |
|
7016 <xsl:text> i += 4; |
|
7017 </xsl:text> |
|
7018 <xsl:text> let iectype = hmitree_types[index]; |
|
7019 </xsl:text> |
|
7020 <xsl:text> if(iectype != undefined){ |
|
7021 </xsl:text> |
|
7022 <xsl:text> let dvgetter = dvgetters[iectype]; |
|
7023 </xsl:text> |
|
7024 <xsl:text> let [value, bytesize] = dvgetter(dv,i); |
|
7025 </xsl:text> |
|
7026 <xsl:text> updates.set(index, value); |
|
7027 </xsl:text> |
|
7028 <xsl:text> i += bytesize; |
|
7029 </xsl:text> |
|
7030 <xsl:text> } else { |
|
7031 </xsl:text> |
|
7032 <xsl:text> throw new Error("Unknown index "+index); |
|
7033 </xsl:text> |
|
7034 <xsl:text> } |
|
7035 </xsl:text> |
|
7036 <xsl:text> }; |
|
7037 </xsl:text> |
|
7038 <xsl:text> // register for rendering on next frame, since there are updates |
|
7039 </xsl:text> |
|
7040 <xsl:text> requestHMIAnimation(); |
|
7041 </xsl:text> |
|
7042 <xsl:text> } catch(err) { |
|
7043 </xsl:text> |
|
7044 <xsl:text> // 1003 is for "Unsupported Data" |
|
7045 </xsl:text> |
|
7046 <xsl:text> // ws.close(1003, err.message); |
|
7047 </xsl:text> |
|
7048 <xsl:text> |
|
7049 </xsl:text> |
|
7050 <xsl:text> // TODO : remove debug alert ? |
|
7051 </xsl:text> |
|
7052 <xsl:text> alert("Error : "+err.message+"\nHMI will be reloaded."); |
|
7053 </xsl:text> |
|
7054 <xsl:text> |
|
7055 </xsl:text> |
|
7056 <xsl:text> // force reload ignoring cache |
|
7057 </xsl:text> |
|
7058 <xsl:text> location.reload(true); |
6933 </xsl:text> |
7059 </xsl:text> |
6934 <xsl:text> } |
7060 <xsl:text> } |
6935 </xsl:text> |
7061 </xsl:text> |
6936 <xsl:text> |
7062 <xsl:text>}; |
6937 </xsl:text> |
7063 </xsl:text> |
6938 <xsl:text> if(jumps_need_update) update_jumps(); |
7064 <xsl:text> |
6939 </xsl:text> |
7065 </xsl:text> |
6940 <xsl:text> |
7066 <xsl:text>hmi_hash_u8 = new Uint8Array(hmi_hash); |
6941 </xsl:text> |
7067 </xsl:text> |
6942 <xsl:text> apply_updates(); |
7068 <xsl:text> |
6943 </xsl:text> |
7069 </xsl:text> |
6944 <xsl:text> |
7070 <xsl:text>function send_blob(data) { |
6945 </xsl:text> |
7071 </xsl:text> |
6946 <xsl:text> pending_widget_animates.forEach(widget => widget._animate()); |
7072 <xsl:text> if(data.length > 0) { |
6947 </xsl:text> |
7073 </xsl:text> |
6948 <xsl:text> pending_widget_animates = []; |
7074 <xsl:text> ws.send(new Blob([hmi_hash_u8].concat(data))); |
6949 </xsl:text> |
7075 </xsl:text> |
6950 <xsl:text> |
7076 <xsl:text> }; |
6951 </xsl:text> |
7077 </xsl:text> |
6952 <xsl:text> requestAnimationFrameID = null; |
7078 <xsl:text>}; |
|
7079 </xsl:text> |
|
7080 <xsl:text> |
|
7081 </xsl:text> |
|
7082 <xsl:text>const typedarray_types = { |
|
7083 </xsl:text> |
|
7084 <xsl:text> INT: (number) => new Int16Array([number]), |
|
7085 </xsl:text> |
|
7086 <xsl:text> BOOL: (truth) => new Int16Array([truth]), |
|
7087 </xsl:text> |
|
7088 <xsl:text> NODE: (truth) => new Int16Array([truth]), |
|
7089 </xsl:text> |
|
7090 <xsl:text> REAL: (number) => new Float32Array([number]), |
|
7091 </xsl:text> |
|
7092 <xsl:text> STRING: (str) => { |
|
7093 </xsl:text> |
|
7094 <xsl:text> // beremiz default string max size is 128 |
|
7095 </xsl:text> |
|
7096 <xsl:text> str = str.slice(0,128); |
|
7097 </xsl:text> |
|
7098 <xsl:text> binary = new Uint8Array(str.length + 1); |
|
7099 </xsl:text> |
|
7100 <xsl:text> binary[0] = str.length; |
|
7101 </xsl:text> |
|
7102 <xsl:text> for(let i = 0; i < str.length; i++){ |
|
7103 </xsl:text> |
|
7104 <xsl:text> binary[i+1] = str.charCodeAt(i); |
|
7105 </xsl:text> |
|
7106 <xsl:text> } |
|
7107 </xsl:text> |
|
7108 <xsl:text> return binary; |
|
7109 </xsl:text> |
|
7110 <xsl:text> } |
|
7111 </xsl:text> |
|
7112 <xsl:text> /* TODO */ |
|
7113 </xsl:text> |
|
7114 <xsl:text>}; |
|
7115 </xsl:text> |
|
7116 <xsl:text> |
|
7117 </xsl:text> |
|
7118 <xsl:text>function send_reset() { |
|
7119 </xsl:text> |
|
7120 <xsl:text> send_blob(new Uint8Array([1])); /* reset = 1 */ |
|
7121 </xsl:text> |
|
7122 <xsl:text>}; |
|
7123 </xsl:text> |
|
7124 <xsl:text> |
|
7125 </xsl:text> |
|
7126 <xsl:text>var subscriptions = []; |
|
7127 </xsl:text> |
|
7128 <xsl:text> |
|
7129 </xsl:text> |
|
7130 <xsl:text>function subscribers(index) { |
|
7131 </xsl:text> |
|
7132 <xsl:text> let entry = subscriptions[index]; |
|
7133 </xsl:text> |
|
7134 <xsl:text> let res; |
|
7135 </xsl:text> |
|
7136 <xsl:text> if(entry == undefined){ |
|
7137 </xsl:text> |
|
7138 <xsl:text> res = new Set(); |
|
7139 </xsl:text> |
|
7140 <xsl:text> subscriptions[index] = [res,0]; |
|
7141 </xsl:text> |
|
7142 <xsl:text> }else{ |
|
7143 </xsl:text> |
|
7144 <xsl:text> [res, _ign] = entry; |
|
7145 </xsl:text> |
|
7146 <xsl:text> } |
|
7147 </xsl:text> |
|
7148 <xsl:text> return res |
6953 </xsl:text> |
7149 </xsl:text> |
6954 <xsl:text>} |
7150 <xsl:text>} |
6955 </xsl:text> |
7151 </xsl:text> |
6956 <xsl:text> |
7152 <xsl:text> |
6957 </xsl:text> |
7153 </xsl:text> |
6958 <xsl:text>function requestHMIAnimation() { |
7154 <xsl:text>function get_subscription_period(index) { |
6959 </xsl:text> |
7155 </xsl:text> |
6960 <xsl:text> if(requestAnimationFrameID == null){ |
7156 <xsl:text> let entry = subscriptions[index]; |
6961 </xsl:text> |
7157 </xsl:text> |
6962 <xsl:text> requestAnimationFrameID = window.requestAnimationFrame(animate); |
7158 <xsl:text> if(entry == undefined) |
|
7159 </xsl:text> |
|
7160 <xsl:text> return 0; |
|
7161 </xsl:text> |
|
7162 <xsl:text> let [_ign, period] = entry; |
|
7163 </xsl:text> |
|
7164 <xsl:text> return period; |
|
7165 </xsl:text> |
|
7166 <xsl:text>} |
|
7167 </xsl:text> |
|
7168 <xsl:text> |
|
7169 </xsl:text> |
|
7170 <xsl:text>function set_subscription_period(index, period) { |
|
7171 </xsl:text> |
|
7172 <xsl:text> let entry = subscriptions[index]; |
|
7173 </xsl:text> |
|
7174 <xsl:text> if(entry == undefined){ |
|
7175 </xsl:text> |
|
7176 <xsl:text> subscriptions[index] = [new Set(), period]; |
|
7177 </xsl:text> |
|
7178 <xsl:text> } else { |
|
7179 </xsl:text> |
|
7180 <xsl:text> entry[1] = period; |
6963 </xsl:text> |
7181 </xsl:text> |
6964 <xsl:text> } |
7182 <xsl:text> } |
6965 </xsl:text> |
7183 </xsl:text> |
6966 <xsl:text>} |
7184 <xsl:text>} |
6967 </xsl:text> |
7185 </xsl:text> |
6968 <xsl:text> |
7186 <xsl:text> |
6969 </xsl:text> |
7187 </xsl:text> |
6970 <xsl:text>// Message reception handler |
7188 <xsl:text>// artificially subscribe the watchdog widget to "/heartbeat" hmi variable |
6971 </xsl:text> |
7189 </xsl:text> |
6972 <xsl:text>// Hash is verified and HMI values updates resulting from binary parsing |
7190 <xsl:text>// Since dispatch directly calls change_hmi_value, |
6973 </xsl:text> |
7191 </xsl:text> |
6974 <xsl:text>// are stored until browser can compute next frame, DOM is left untouched |
7192 <xsl:text>// PLC will periodically send variable at given frequency |
6975 </xsl:text> |
7193 </xsl:text> |
6976 <xsl:text>ws.onmessage = function (evt) { |
7194 <xsl:text>subscribers(heartbeat_index).add({ |
6977 </xsl:text> |
7195 </xsl:text> |
6978 <xsl:text> |
7196 <xsl:text> /* type: "Watchdog", */ |
6979 </xsl:text> |
7197 </xsl:text> |
6980 <xsl:text> let data = evt.data; |
7198 <xsl:text> frequency: 1, |
6981 </xsl:text> |
7199 </xsl:text> |
6982 <xsl:text> let dv = new DataView(data); |
7200 <xsl:text> indexes: [heartbeat_index], |
6983 </xsl:text> |
7201 </xsl:text> |
6984 <xsl:text> let i = 0; |
7202 <xsl:text> new_hmi_value: function(index, value, oldval) { |
6985 </xsl:text> |
7203 </xsl:text> |
6986 <xsl:text> try { |
7204 <xsl:text> apply_hmi_value(heartbeat_index, value+1); |
6987 </xsl:text> |
7205 </xsl:text> |
6988 <xsl:text> for(let hash_int of hmi_hash) { |
7206 <xsl:text> } |
6989 </xsl:text> |
7207 </xsl:text> |
6990 <xsl:text> if(hash_int != dv.getUint8(i)){ |
7208 <xsl:text>}); |
6991 </xsl:text> |
7209 </xsl:text> |
6992 <xsl:text> throw new Error("Hash doesn't match"); |
7210 <xsl:text> |
6993 </xsl:text> |
7211 </xsl:text> |
6994 <xsl:text> }; |
7212 <xsl:text>function svg_text_to_multiline(elt) { |
6995 </xsl:text> |
7213 </xsl:text> |
6996 <xsl:text> i++; |
7214 <xsl:text> return(Array.prototype.map.call(elt.children, x=>x.textContent).join("\n")); |
6997 </xsl:text> |
7215 </xsl:text> |
6998 <xsl:text> }; |
7216 <xsl:text>} |
6999 </xsl:text> |
7217 </xsl:text> |
7000 <xsl:text> |
7218 <xsl:text> |
7001 </xsl:text> |
7219 </xsl:text> |
7002 <xsl:text> while(i < data.byteLength){ |
7220 <xsl:text>function multiline_to_svg_text(elt, str) { |
7003 </xsl:text> |
7221 </xsl:text> |
7004 <xsl:text> let index = dv.getUint32(i, true); |
7222 <xsl:text> str.split('\n').map((line,i) => {elt.children[i].textContent = line;}); |
7005 </xsl:text> |
7223 </xsl:text> |
7006 <xsl:text> i += 4; |
7224 <xsl:text>} |
7007 </xsl:text> |
7225 </xsl:text> |
7008 <xsl:text> let iectype = hmitree_types[index]; |
7226 <xsl:text> |
7009 </xsl:text> |
7227 </xsl:text> |
7010 <xsl:text> if(iectype != undefined){ |
7228 <xsl:text>function switch_langnum(langnum) { |
7011 </xsl:text> |
7229 </xsl:text> |
7012 <xsl:text> let dvgetter = dvgetters[iectype]; |
7230 <xsl:text> langnum = Math.max(0, Math.min(langs.length - 1, langnum)); |
7013 </xsl:text> |
7231 </xsl:text> |
7014 <xsl:text> let [value, bytesize] = dvgetter(dv,i); |
7232 <xsl:text> |
7015 </xsl:text> |
7233 </xsl:text> |
7016 <xsl:text> updates[index] = value; |
7234 <xsl:text> for (let translation of translations) { |
7017 </xsl:text> |
7235 </xsl:text> |
7018 <xsl:text> i += bytesize; |
7236 <xsl:text> let [objs, msgs] = translation; |
7019 </xsl:text> |
7237 </xsl:text> |
7020 <xsl:text> } else { |
7238 <xsl:text> let msg = msgs[langnum]; |
7021 </xsl:text> |
7239 </xsl:text> |
7022 <xsl:text> throw new Error("Unknown index "+index); |
7240 <xsl:text> for (let obj of objs) { |
|
7241 </xsl:text> |
|
7242 <xsl:text> multiline_to_svg_text(obj, msg); |
|
7243 </xsl:text> |
|
7244 <xsl:text> obj.setAttribute("lang",langnum); |
|
7245 </xsl:text> |
|
7246 <xsl:text> } |
|
7247 </xsl:text> |
|
7248 <xsl:text> } |
|
7249 </xsl:text> |
|
7250 <xsl:text> return langnum; |
|
7251 </xsl:text> |
|
7252 <xsl:text>} |
|
7253 </xsl:text> |
|
7254 <xsl:text> |
|
7255 </xsl:text> |
|
7256 <xsl:text>// backup original texts |
|
7257 </xsl:text> |
|
7258 <xsl:text>for (let translation of translations) { |
|
7259 </xsl:text> |
|
7260 <xsl:text> let [objs, msgs] = translation; |
|
7261 </xsl:text> |
|
7262 <xsl:text> msgs.unshift(svg_text_to_multiline(objs[0])); |
|
7263 </xsl:text> |
|
7264 <xsl:text>} |
|
7265 </xsl:text> |
|
7266 <xsl:text> |
|
7267 </xsl:text> |
|
7268 <xsl:text>var lang_local_index = hmi_local_index("lang"); |
|
7269 </xsl:text> |
|
7270 <xsl:text>var langcode_local_index = hmi_local_index("lang_code"); |
|
7271 </xsl:text> |
|
7272 <xsl:text>var langname_local_index = hmi_local_index("lang_name"); |
|
7273 </xsl:text> |
|
7274 <xsl:text>subscribers(lang_local_index).add({ |
|
7275 </xsl:text> |
|
7276 <xsl:text> indexes: [lang_local_index], |
|
7277 </xsl:text> |
|
7278 <xsl:text> new_hmi_value: function(index, value, oldval) { |
|
7279 </xsl:text> |
|
7280 <xsl:text> let current_lang = switch_langnum(value); |
|
7281 </xsl:text> |
|
7282 <xsl:text> let [langname,langcode] = langs[current_lang]; |
|
7283 </xsl:text> |
|
7284 <xsl:text> apply_hmi_value(langcode_local_index, langcode); |
|
7285 </xsl:text> |
|
7286 <xsl:text> apply_hmi_value(langname_local_index, langname); |
|
7287 </xsl:text> |
|
7288 <xsl:text> switch_page(); |
|
7289 </xsl:text> |
|
7290 <xsl:text> } |
|
7291 </xsl:text> |
|
7292 <xsl:text>}); |
|
7293 </xsl:text> |
|
7294 <xsl:text> |
|
7295 </xsl:text> |
|
7296 <xsl:text>function setup_lang(){ |
|
7297 </xsl:text> |
|
7298 <xsl:text> let current_lang = cache[lang_local_index]; |
|
7299 </xsl:text> |
|
7300 <xsl:text> let new_lang = switch_langnum(current_lang); |
|
7301 </xsl:text> |
|
7302 <xsl:text> if(current_lang != new_lang){ |
|
7303 </xsl:text> |
|
7304 <xsl:text> apply_hmi_value(lang_local_index, new_lang); |
|
7305 </xsl:text> |
|
7306 <xsl:text> } |
|
7307 </xsl:text> |
|
7308 <xsl:text>} |
|
7309 </xsl:text> |
|
7310 <xsl:text> |
|
7311 </xsl:text> |
|
7312 <xsl:text>setup_lang(); |
|
7313 </xsl:text> |
|
7314 <xsl:text> |
|
7315 </xsl:text> |
|
7316 <xsl:text>function update_subscriptions() { |
|
7317 </xsl:text> |
|
7318 <xsl:text> let delta = []; |
|
7319 </xsl:text> |
|
7320 <xsl:text> for(let index in subscriptions){ |
|
7321 </xsl:text> |
|
7322 <xsl:text> let widgets = subscribers(index); |
|
7323 </xsl:text> |
|
7324 <xsl:text> |
|
7325 </xsl:text> |
|
7326 <xsl:text> // periods are in ms |
|
7327 </xsl:text> |
|
7328 <xsl:text> let previous_period = get_subscription_period(index); |
|
7329 </xsl:text> |
|
7330 <xsl:text> |
|
7331 </xsl:text> |
|
7332 <xsl:text> // subscribing with a zero period is unsubscribing |
|
7333 </xsl:text> |
|
7334 <xsl:text> let new_period = 0; |
|
7335 </xsl:text> |
|
7336 <xsl:text> if(widgets.size > 0) { |
|
7337 </xsl:text> |
|
7338 <xsl:text> let maxfreq = 0; |
|
7339 </xsl:text> |
|
7340 <xsl:text> for(let widget of widgets){ |
|
7341 </xsl:text> |
|
7342 <xsl:text> let wf = widget.frequency; |
|
7343 </xsl:text> |
|
7344 <xsl:text> if(wf != undefined && maxfreq < wf) |
|
7345 </xsl:text> |
|
7346 <xsl:text> maxfreq = wf; |
7023 </xsl:text> |
7347 </xsl:text> |
7024 <xsl:text> } |
7348 <xsl:text> } |
7025 </xsl:text> |
7349 </xsl:text> |
7026 <xsl:text> }; |
7350 <xsl:text> |
7027 </xsl:text> |
7351 </xsl:text> |
7028 <xsl:text> // register for rendering on next frame, since there are updates |
7352 <xsl:text> if(maxfreq != 0) |
7029 </xsl:text> |
7353 </xsl:text> |
7030 <xsl:text> requestHMIAnimation(); |
7354 <xsl:text> new_period = 1000/maxfreq; |
7031 </xsl:text> |
7355 </xsl:text> |
7032 <xsl:text> } catch(err) { |
7356 <xsl:text> } |
7033 </xsl:text> |
7357 </xsl:text> |
7034 <xsl:text> // 1003 is for "Unsupported Data" |
7358 <xsl:text> |
7035 </xsl:text> |
7359 </xsl:text> |
7036 <xsl:text> // ws.close(1003, err.message); |
7360 <xsl:text> if(previous_period != new_period) { |
7037 </xsl:text> |
7361 </xsl:text> |
7038 <xsl:text> |
7362 <xsl:text> set_subscription_period(index, new_period); |
7039 </xsl:text> |
7363 </xsl:text> |
7040 <xsl:text> // TODO : remove debug alert ? |
7364 <xsl:text> if(index <= last_remote_index){ |
7041 </xsl:text> |
7365 </xsl:text> |
7042 <xsl:text> alert("Error : "+err.message+"\nHMI will be reloaded."); |
7366 <xsl:text> delta.push( |
7043 </xsl:text> |
7367 </xsl:text> |
7044 <xsl:text> |
7368 <xsl:text> new Uint8Array([2]), /* subscribe = 2 */ |
7045 </xsl:text> |
7369 </xsl:text> |
7046 <xsl:text> // force reload ignoring cache |
7370 <xsl:text> new Uint32Array([index]), |
7047 </xsl:text> |
7371 </xsl:text> |
7048 <xsl:text> location.reload(true); |
7372 <xsl:text> new Uint16Array([new_period])); |
|
7373 </xsl:text> |
|
7374 <xsl:text> } |
|
7375 </xsl:text> |
|
7376 <xsl:text> } |
7049 </xsl:text> |
7377 </xsl:text> |
7050 <xsl:text> } |
7378 <xsl:text> } |
7051 </xsl:text> |
7379 </xsl:text> |
|
7380 <xsl:text> send_blob(delta); |
|
7381 </xsl:text> |
7052 <xsl:text>}; |
7382 <xsl:text>}; |
7053 </xsl:text> |
7383 </xsl:text> |
7054 <xsl:text> |
7384 <xsl:text> |
7055 </xsl:text> |
7385 </xsl:text> |
7056 <xsl:text>hmi_hash_u8 = new Uint8Array(hmi_hash); |
|
7057 </xsl:text> |
|
7058 <xsl:text> |
|
7059 </xsl:text> |
|
7060 <xsl:text>function send_blob(data) { |
|
7061 </xsl:text> |
|
7062 <xsl:text> if(data.length > 0) { |
|
7063 </xsl:text> |
|
7064 <xsl:text> ws.send(new Blob([hmi_hash_u8].concat(data))); |
|
7065 </xsl:text> |
|
7066 <xsl:text> }; |
|
7067 </xsl:text> |
|
7068 <xsl:text>}; |
|
7069 </xsl:text> |
|
7070 <xsl:text> |
|
7071 </xsl:text> |
|
7072 <xsl:text>const typedarray_types = { |
|
7073 </xsl:text> |
|
7074 <xsl:text> INT: (number) => new Int16Array([number]), |
|
7075 </xsl:text> |
|
7076 <xsl:text> BOOL: (truth) => new Int16Array([truth]), |
|
7077 </xsl:text> |
|
7078 <xsl:text> NODE: (truth) => new Int16Array([truth]), |
|
7079 </xsl:text> |
|
7080 <xsl:text> REAL: (number) => new Float32Array([number]), |
|
7081 </xsl:text> |
|
7082 <xsl:text> STRING: (str) => { |
|
7083 </xsl:text> |
|
7084 <xsl:text> // beremiz default string max size is 128 |
|
7085 </xsl:text> |
|
7086 <xsl:text> str = str.slice(0,128); |
|
7087 </xsl:text> |
|
7088 <xsl:text> binary = new Uint8Array(str.length + 1); |
|
7089 </xsl:text> |
|
7090 <xsl:text> binary[0] = str.length; |
|
7091 </xsl:text> |
|
7092 <xsl:text> for(let i = 0; i < str.length; i++){ |
|
7093 </xsl:text> |
|
7094 <xsl:text> binary[i+1] = str.charCodeAt(i); |
|
7095 </xsl:text> |
|
7096 <xsl:text> } |
|
7097 </xsl:text> |
|
7098 <xsl:text> return binary; |
|
7099 </xsl:text> |
|
7100 <xsl:text> } |
|
7101 </xsl:text> |
|
7102 <xsl:text> /* TODO */ |
|
7103 </xsl:text> |
|
7104 <xsl:text>}; |
|
7105 </xsl:text> |
|
7106 <xsl:text> |
|
7107 </xsl:text> |
|
7108 <xsl:text>function send_reset() { |
|
7109 </xsl:text> |
|
7110 <xsl:text> send_blob(new Uint8Array([1])); /* reset = 1 */ |
|
7111 </xsl:text> |
|
7112 <xsl:text>}; |
|
7113 </xsl:text> |
|
7114 <xsl:text> |
|
7115 </xsl:text> |
|
7116 <xsl:text>var subscriptions = []; |
|
7117 </xsl:text> |
|
7118 <xsl:text> |
|
7119 </xsl:text> |
|
7120 <xsl:text>function subscribers(index) { |
|
7121 </xsl:text> |
|
7122 <xsl:text> let entry = subscriptions[index]; |
|
7123 </xsl:text> |
|
7124 <xsl:text> let res; |
|
7125 </xsl:text> |
|
7126 <xsl:text> if(entry == undefined){ |
|
7127 </xsl:text> |
|
7128 <xsl:text> res = new Set(); |
|
7129 </xsl:text> |
|
7130 <xsl:text> subscriptions[index] = [res,0]; |
|
7131 </xsl:text> |
|
7132 <xsl:text> }else{ |
|
7133 </xsl:text> |
|
7134 <xsl:text> [res, _ign] = entry; |
|
7135 </xsl:text> |
|
7136 <xsl:text> } |
|
7137 </xsl:text> |
|
7138 <xsl:text> return res |
|
7139 </xsl:text> |
|
7140 <xsl:text>} |
|
7141 </xsl:text> |
|
7142 <xsl:text> |
|
7143 </xsl:text> |
|
7144 <xsl:text>function get_subscription_period(index) { |
|
7145 </xsl:text> |
|
7146 <xsl:text> let entry = subscriptions[index]; |
|
7147 </xsl:text> |
|
7148 <xsl:text> if(entry == undefined) |
|
7149 </xsl:text> |
|
7150 <xsl:text> return 0; |
|
7151 </xsl:text> |
|
7152 <xsl:text> let [_ign, period] = entry; |
|
7153 </xsl:text> |
|
7154 <xsl:text> return period; |
|
7155 </xsl:text> |
|
7156 <xsl:text>} |
|
7157 </xsl:text> |
|
7158 <xsl:text> |
|
7159 </xsl:text> |
|
7160 <xsl:text>function set_subscription_period(index, period) { |
|
7161 </xsl:text> |
|
7162 <xsl:text> let entry = subscriptions[index]; |
|
7163 </xsl:text> |
|
7164 <xsl:text> if(entry == undefined){ |
|
7165 </xsl:text> |
|
7166 <xsl:text> subscriptions[index] = [new Set(), period]; |
|
7167 </xsl:text> |
|
7168 <xsl:text> } else { |
|
7169 </xsl:text> |
|
7170 <xsl:text> entry[1] = period; |
|
7171 </xsl:text> |
|
7172 <xsl:text> } |
|
7173 </xsl:text> |
|
7174 <xsl:text>} |
|
7175 </xsl:text> |
|
7176 <xsl:text> |
|
7177 </xsl:text> |
|
7178 <xsl:text>// artificially subscribe the watchdog widget to "/heartbeat" hmi variable |
|
7179 </xsl:text> |
|
7180 <xsl:text>// Since dispatch directly calls change_hmi_value, |
|
7181 </xsl:text> |
|
7182 <xsl:text>// PLC will periodically send variable at given frequency |
|
7183 </xsl:text> |
|
7184 <xsl:text>subscribers(heartbeat_index).add({ |
|
7185 </xsl:text> |
|
7186 <xsl:text> /* type: "Watchdog", */ |
|
7187 </xsl:text> |
|
7188 <xsl:text> frequency: 1, |
|
7189 </xsl:text> |
|
7190 <xsl:text> indexes: [heartbeat_index], |
|
7191 </xsl:text> |
|
7192 <xsl:text> new_hmi_value: function(index, value, oldval) { |
|
7193 </xsl:text> |
|
7194 <xsl:text> apply_hmi_value(heartbeat_index, value+1); |
|
7195 </xsl:text> |
|
7196 <xsl:text> } |
|
7197 </xsl:text> |
|
7198 <xsl:text>}); |
|
7199 </xsl:text> |
|
7200 <xsl:text> |
|
7201 </xsl:text> |
|
7202 <xsl:text>function svg_text_to_multiline(elt) { |
|
7203 </xsl:text> |
|
7204 <xsl:text> return(Array.prototype.map.call(elt.children, x=>x.textContent).join("\n")); |
|
7205 </xsl:text> |
|
7206 <xsl:text>} |
|
7207 </xsl:text> |
|
7208 <xsl:text> |
|
7209 </xsl:text> |
|
7210 <xsl:text>function multiline_to_svg_text(elt, str) { |
|
7211 </xsl:text> |
|
7212 <xsl:text> str.split('\n').map((line,i) => {elt.children[i].textContent = line;}); |
|
7213 </xsl:text> |
|
7214 <xsl:text>} |
|
7215 </xsl:text> |
|
7216 <xsl:text> |
|
7217 </xsl:text> |
|
7218 <xsl:text>function switch_langnum(langnum) { |
|
7219 </xsl:text> |
|
7220 <xsl:text> langnum = Math.max(0, Math.min(langs.length - 1, langnum)); |
|
7221 </xsl:text> |
|
7222 <xsl:text> |
|
7223 </xsl:text> |
|
7224 <xsl:text> for (let translation of translations) { |
|
7225 </xsl:text> |
|
7226 <xsl:text> let [objs, msgs] = translation; |
|
7227 </xsl:text> |
|
7228 <xsl:text> let msg = msgs[langnum]; |
|
7229 </xsl:text> |
|
7230 <xsl:text> for (let obj of objs) { |
|
7231 </xsl:text> |
|
7232 <xsl:text> multiline_to_svg_text(obj, msg); |
|
7233 </xsl:text> |
|
7234 <xsl:text> obj.setAttribute("lang",langnum); |
|
7235 </xsl:text> |
|
7236 <xsl:text> } |
|
7237 </xsl:text> |
|
7238 <xsl:text> } |
|
7239 </xsl:text> |
|
7240 <xsl:text> return langnum; |
|
7241 </xsl:text> |
|
7242 <xsl:text>} |
|
7243 </xsl:text> |
|
7244 <xsl:text> |
|
7245 </xsl:text> |
|
7246 <xsl:text>// backup original texts |
|
7247 </xsl:text> |
|
7248 <xsl:text>for (let translation of translations) { |
|
7249 </xsl:text> |
|
7250 <xsl:text> let [objs, msgs] = translation; |
|
7251 </xsl:text> |
|
7252 <xsl:text> msgs.unshift(svg_text_to_multiline(objs[0])); |
|
7253 </xsl:text> |
|
7254 <xsl:text>} |
|
7255 </xsl:text> |
|
7256 <xsl:text> |
|
7257 </xsl:text> |
|
7258 <xsl:text>var lang_local_index = hmi_local_index("lang"); |
|
7259 </xsl:text> |
|
7260 <xsl:text>var langcode_local_index = hmi_local_index("lang_code"); |
|
7261 </xsl:text> |
|
7262 <xsl:text>var langname_local_index = hmi_local_index("lang_name"); |
|
7263 </xsl:text> |
|
7264 <xsl:text>subscribers(lang_local_index).add({ |
|
7265 </xsl:text> |
|
7266 <xsl:text> indexes: [lang_local_index], |
|
7267 </xsl:text> |
|
7268 <xsl:text> new_hmi_value: function(index, value, oldval) { |
|
7269 </xsl:text> |
|
7270 <xsl:text> let current_lang = switch_langnum(value); |
|
7271 </xsl:text> |
|
7272 <xsl:text> let [langname,langcode] = langs[current_lang]; |
|
7273 </xsl:text> |
|
7274 <xsl:text> apply_hmi_value(langcode_local_index, langcode); |
|
7275 </xsl:text> |
|
7276 <xsl:text> apply_hmi_value(langname_local_index, langname); |
|
7277 </xsl:text> |
|
7278 <xsl:text> switch_page(); |
|
7279 </xsl:text> |
|
7280 <xsl:text> } |
|
7281 </xsl:text> |
|
7282 <xsl:text>}); |
|
7283 </xsl:text> |
|
7284 <xsl:text> |
|
7285 </xsl:text> |
|
7286 <xsl:text>function setup_lang(){ |
|
7287 </xsl:text> |
|
7288 <xsl:text> let current_lang = cache[lang_local_index]; |
|
7289 </xsl:text> |
|
7290 <xsl:text> let new_lang = switch_langnum(current_lang); |
|
7291 </xsl:text> |
|
7292 <xsl:text> if(current_lang != new_lang){ |
|
7293 </xsl:text> |
|
7294 <xsl:text> apply_hmi_value(lang_local_index, new_lang); |
|
7295 </xsl:text> |
|
7296 <xsl:text> } |
|
7297 </xsl:text> |
|
7298 <xsl:text>} |
|
7299 </xsl:text> |
|
7300 <xsl:text> |
|
7301 </xsl:text> |
|
7302 <xsl:text>setup_lang(); |
|
7303 </xsl:text> |
|
7304 <xsl:text> |
|
7305 </xsl:text> |
|
7306 <xsl:text>function update_subscriptions() { |
|
7307 </xsl:text> |
|
7308 <xsl:text> let delta = []; |
|
7309 </xsl:text> |
|
7310 <xsl:text> for(let index in subscriptions){ |
|
7311 </xsl:text> |
|
7312 <xsl:text> let widgets = subscribers(index); |
|
7313 </xsl:text> |
|
7314 <xsl:text> |
|
7315 </xsl:text> |
|
7316 <xsl:text> // periods are in ms |
|
7317 </xsl:text> |
|
7318 <xsl:text> let previous_period = get_subscription_period(index); |
|
7319 </xsl:text> |
|
7320 <xsl:text> |
|
7321 </xsl:text> |
|
7322 <xsl:text> // subscribing with a zero period is unsubscribing |
|
7323 </xsl:text> |
|
7324 <xsl:text> let new_period = 0; |
|
7325 </xsl:text> |
|
7326 <xsl:text> if(widgets.size > 0) { |
|
7327 </xsl:text> |
|
7328 <xsl:text> let maxfreq = 0; |
|
7329 </xsl:text> |
|
7330 <xsl:text> for(let widget of widgets){ |
|
7331 </xsl:text> |
|
7332 <xsl:text> let wf = widget.frequency; |
|
7333 </xsl:text> |
|
7334 <xsl:text> if(wf != undefined && maxfreq < wf) |
|
7335 </xsl:text> |
|
7336 <xsl:text> maxfreq = wf; |
|
7337 </xsl:text> |
|
7338 <xsl:text> } |
|
7339 </xsl:text> |
|
7340 <xsl:text> |
|
7341 </xsl:text> |
|
7342 <xsl:text> if(maxfreq != 0) |
|
7343 </xsl:text> |
|
7344 <xsl:text> new_period = 1000/maxfreq; |
|
7345 </xsl:text> |
|
7346 <xsl:text> } |
|
7347 </xsl:text> |
|
7348 <xsl:text> |
|
7349 </xsl:text> |
|
7350 <xsl:text> if(previous_period != new_period) { |
|
7351 </xsl:text> |
|
7352 <xsl:text> set_subscription_period(index, new_period); |
|
7353 </xsl:text> |
|
7354 <xsl:text> if(index <= last_remote_index){ |
|
7355 </xsl:text> |
|
7356 <xsl:text> delta.push( |
|
7357 </xsl:text> |
|
7358 <xsl:text> new Uint8Array([2]), /* subscribe = 2 */ |
|
7359 </xsl:text> |
|
7360 <xsl:text> new Uint32Array([index]), |
|
7361 </xsl:text> |
|
7362 <xsl:text> new Uint16Array([new_period])); |
|
7363 </xsl:text> |
|
7364 <xsl:text> } |
|
7365 </xsl:text> |
|
7366 <xsl:text> } |
|
7367 </xsl:text> |
|
7368 <xsl:text> } |
|
7369 </xsl:text> |
|
7370 <xsl:text> send_blob(delta); |
|
7371 </xsl:text> |
|
7372 <xsl:text>}; |
|
7373 </xsl:text> |
|
7374 <xsl:text> |
|
7375 </xsl:text> |
|
7376 <xsl:text>function send_hmi_value(index, value) { |
7386 <xsl:text>function send_hmi_value(index, value) { |
7377 </xsl:text> |
7387 </xsl:text> |
7378 <xsl:text> if(index > last_remote_index){ |
7388 <xsl:text> if(index > last_remote_index){ |
7379 </xsl:text> |
7389 </xsl:text> |
7380 <xsl:text> updates[index] = value; |
7390 <xsl:text> updates.set(index, value); |
7381 </xsl:text> |
7391 </xsl:text> |
7382 <xsl:text> |
7392 <xsl:text> |
7383 </xsl:text> |
7393 </xsl:text> |
7384 <xsl:text> if(persistent_indexes.has(index)){ |
7394 <xsl:text> if(persistent_indexes.has(index)){ |
7385 </xsl:text> |
7395 </xsl:text> |