3047 </xsl:text> |
3157 </xsl:text> |
3048 <xsl:text>var need_cache_apply = []; |
3158 <xsl:text>var need_cache_apply = []; |
3049 </xsl:text> |
3159 </xsl:text> |
3050 <xsl:text> |
3160 <xsl:text> |
3051 </xsl:text> |
3161 </xsl:text> |
3052 <xsl:text>function dispatch_value_to_widget(widget, index, value, oldval) { |
3162 <xsl:text> |
|
3163 </xsl:text> |
|
3164 <xsl:text>function dispatch_value(index, value) { |
|
3165 </xsl:text> |
|
3166 <xsl:text> let widgets = subscribers[index]; |
|
3167 </xsl:text> |
|
3168 <xsl:text> |
|
3169 </xsl:text> |
|
3170 <xsl:text> let oldval = cache[index]; |
|
3171 </xsl:text> |
|
3172 <xsl:text> cache[index] = value; |
|
3173 </xsl:text> |
|
3174 <xsl:text> |
|
3175 </xsl:text> |
|
3176 <xsl:text> if(widgets.size > 0) { |
|
3177 </xsl:text> |
|
3178 <xsl:text> for(let widget of widgets){ |
|
3179 </xsl:text> |
|
3180 <xsl:text> widget.new_hmi_value(index, value, oldval); |
|
3181 </xsl:text> |
|
3182 <xsl:text> } |
|
3183 </xsl:text> |
|
3184 <xsl:text> } |
|
3185 </xsl:text> |
|
3186 <xsl:text>}; |
|
3187 </xsl:text> |
|
3188 <xsl:text> |
|
3189 </xsl:text> |
|
3190 <xsl:text>function init_widgets() { |
|
3191 </xsl:text> |
|
3192 <xsl:text> Object.keys(hmi_widgets).forEach(function(id) { |
|
3193 </xsl:text> |
|
3194 <xsl:text> let widget = hmi_widgets[id]; |
|
3195 </xsl:text> |
|
3196 <xsl:text> let init = widget.init; |
|
3197 </xsl:text> |
|
3198 <xsl:text> if(typeof(init) == "function"){ |
|
3199 </xsl:text> |
|
3200 <xsl:text> try { |
|
3201 </xsl:text> |
|
3202 <xsl:text> init.call(widget); |
|
3203 </xsl:text> |
|
3204 <xsl:text> } catch(err) { |
|
3205 </xsl:text> |
|
3206 <xsl:text> console.log(err); |
|
3207 </xsl:text> |
|
3208 <xsl:text> } |
|
3209 </xsl:text> |
|
3210 <xsl:text> } |
|
3211 </xsl:text> |
|
3212 <xsl:text> }); |
|
3213 </xsl:text> |
|
3214 <xsl:text>}; |
|
3215 </xsl:text> |
|
3216 <xsl:text> |
|
3217 </xsl:text> |
|
3218 <xsl:text>// Open WebSocket to relative "/ws" address |
|
3219 </xsl:text> |
|
3220 <xsl:text>var ws = new WebSocket(window.location.href.replace(/^http(s?:\/\/[^\/]*)\/.*$/, 'ws$1/ws')); |
|
3221 </xsl:text> |
|
3222 <xsl:text>ws.binaryType = 'arraybuffer'; |
|
3223 </xsl:text> |
|
3224 <xsl:text> |
|
3225 </xsl:text> |
|
3226 <xsl:text>const dvgetters = { |
|
3227 </xsl:text> |
|
3228 <xsl:text> INT: (dv,offset) => [dv.getInt16(offset, true), 2], |
|
3229 </xsl:text> |
|
3230 <xsl:text> BOOL: (dv,offset) => [dv.getInt8(offset, true), 1], |
|
3231 </xsl:text> |
|
3232 <xsl:text> NODE: (dv,offset) => [dv.getInt8(offset, true), 1], |
|
3233 </xsl:text> |
|
3234 <xsl:text> STRING: (dv, offset) => { |
|
3235 </xsl:text> |
|
3236 <xsl:text> size = dv.getInt8(offset); |
|
3237 </xsl:text> |
|
3238 <xsl:text> return [ |
|
3239 </xsl:text> |
|
3240 <xsl:text> String.fromCharCode.apply(null, new Uint8Array( |
|
3241 </xsl:text> |
|
3242 <xsl:text> dv.buffer, /* original buffer */ |
|
3243 </xsl:text> |
|
3244 <xsl:text> offset + 1, /* string starts after size*/ |
|
3245 </xsl:text> |
|
3246 <xsl:text> size /* size of string */ |
|
3247 </xsl:text> |
|
3248 <xsl:text> )), size + 1]; /* total increment */ |
|
3249 </xsl:text> |
|
3250 <xsl:text> } |
|
3251 </xsl:text> |
|
3252 <xsl:text>}; |
|
3253 </xsl:text> |
|
3254 <xsl:text> |
|
3255 </xsl:text> |
|
3256 <xsl:text>// Apply updates recieved through ws.onmessage to subscribed widgets |
|
3257 </xsl:text> |
|
3258 <xsl:text>function apply_updates() { |
|
3259 </xsl:text> |
|
3260 <xsl:text> for(let index in updates){ |
|
3261 </xsl:text> |
|
3262 <xsl:text> // serving as a key, index becomes a string |
|
3263 </xsl:text> |
|
3264 <xsl:text> // -> pass Number(index) instead |
|
3265 </xsl:text> |
|
3266 <xsl:text> dispatch_value(Number(index), updates[index]); |
|
3267 </xsl:text> |
|
3268 <xsl:text> delete updates[index]; |
|
3269 </xsl:text> |
|
3270 <xsl:text> } |
|
3271 </xsl:text> |
|
3272 <xsl:text>} |
|
3273 </xsl:text> |
|
3274 <xsl:text> |
|
3275 </xsl:text> |
|
3276 <xsl:text>// Called on requestAnimationFrame, modifies DOM |
|
3277 </xsl:text> |
|
3278 <xsl:text>var requestAnimationFrameID = null; |
|
3279 </xsl:text> |
|
3280 <xsl:text>function animate() { |
|
3281 </xsl:text> |
|
3282 <xsl:text> // Do the page swith if any one pending |
|
3283 </xsl:text> |
|
3284 <xsl:text> if(current_subscribed_page != current_visible_page){ |
|
3285 </xsl:text> |
|
3286 <xsl:text> switch_visible_page(current_subscribed_page); |
|
3287 </xsl:text> |
|
3288 <xsl:text> } |
|
3289 </xsl:text> |
|
3290 <xsl:text> |
|
3291 </xsl:text> |
|
3292 <xsl:text> while(widget = need_cache_apply.pop()){ |
|
3293 </xsl:text> |
|
3294 <xsl:text> widget.apply_cache(); |
|
3295 </xsl:text> |
|
3296 <xsl:text> } |
|
3297 </xsl:text> |
|
3298 <xsl:text> |
|
3299 </xsl:text> |
|
3300 <xsl:text> if(jumps_need_update) update_jumps(); |
|
3301 </xsl:text> |
|
3302 <xsl:text> |
|
3303 </xsl:text> |
|
3304 <xsl:text> apply_updates(); |
|
3305 </xsl:text> |
|
3306 <xsl:text> requestAnimationFrameID = null; |
|
3307 </xsl:text> |
|
3308 <xsl:text>} |
|
3309 </xsl:text> |
|
3310 <xsl:text> |
|
3311 </xsl:text> |
|
3312 <xsl:text>function requestHMIAnimation() { |
|
3313 </xsl:text> |
|
3314 <xsl:text> if(requestAnimationFrameID == null){ |
|
3315 </xsl:text> |
|
3316 <xsl:text> requestAnimationFrameID = window.requestAnimationFrame(animate); |
|
3317 </xsl:text> |
|
3318 <xsl:text> } |
|
3319 </xsl:text> |
|
3320 <xsl:text>} |
|
3321 </xsl:text> |
|
3322 <xsl:text> |
|
3323 </xsl:text> |
|
3324 <xsl:text>// Message reception handler |
|
3325 </xsl:text> |
|
3326 <xsl:text>// Hash is verified and HMI values updates resulting from binary parsing |
|
3327 </xsl:text> |
|
3328 <xsl:text>// are stored until browser can compute next frame, DOM is left untouched |
|
3329 </xsl:text> |
|
3330 <xsl:text>ws.onmessage = function (evt) { |
|
3331 </xsl:text> |
|
3332 <xsl:text> |
|
3333 </xsl:text> |
|
3334 <xsl:text> let data = evt.data; |
|
3335 </xsl:text> |
|
3336 <xsl:text> let dv = new DataView(data); |
|
3337 </xsl:text> |
|
3338 <xsl:text> let i = 0; |
3053 </xsl:text> |
3339 </xsl:text> |
3054 <xsl:text> try { |
3340 <xsl:text> try { |
3055 </xsl:text> |
3341 </xsl:text> |
3056 <xsl:text> let idx = widget.offset ? index - widget.offset : index; |
3342 <xsl:text> for(let hash_int of hmi_hash) { |
3057 </xsl:text> |
3343 </xsl:text> |
3058 <xsl:text> let idxidx = widget.indexes.indexOf(idx); |
3344 <xsl:text> if(hash_int != dv.getUint8(i)){ |
3059 </xsl:text> |
3345 </xsl:text> |
3060 <xsl:text> let d = widget.dispatch; |
3346 <xsl:text> throw new Error("Hash doesn't match"); |
3061 </xsl:text> |
3347 </xsl:text> |
3062 <xsl:text> if(typeof(d) == "function" && idxidx == 0){ |
3348 <xsl:text> }; |
3063 </xsl:text> |
3349 </xsl:text> |
3064 <xsl:text> d.call(widget, value, oldval); |
3350 <xsl:text> i++; |
|
3351 </xsl:text> |
|
3352 <xsl:text> }; |
|
3353 </xsl:text> |
|
3354 <xsl:text> |
|
3355 </xsl:text> |
|
3356 <xsl:text> while(i < data.byteLength){ |
|
3357 </xsl:text> |
|
3358 <xsl:text> let index = dv.getUint32(i, true); |
|
3359 </xsl:text> |
|
3360 <xsl:text> i += 4; |
|
3361 </xsl:text> |
|
3362 <xsl:text> let iectype = hmitree_types[index]; |
|
3363 </xsl:text> |
|
3364 <xsl:text> if(iectype != undefined){ |
|
3365 </xsl:text> |
|
3366 <xsl:text> let dvgetter = dvgetters[iectype]; |
|
3367 </xsl:text> |
|
3368 <xsl:text> let [value, bytesize] = dvgetter(dv,i); |
|
3369 </xsl:text> |
|
3370 <xsl:text> updates[index] = value; |
|
3371 </xsl:text> |
|
3372 <xsl:text> i += bytesize; |
|
3373 </xsl:text> |
|
3374 <xsl:text> } else { |
|
3375 </xsl:text> |
|
3376 <xsl:text> throw new Error("Unknown index "+index); |
|
3377 </xsl:text> |
|
3378 <xsl:text> } |
|
3379 </xsl:text> |
|
3380 <xsl:text> }; |
|
3381 </xsl:text> |
|
3382 <xsl:text> // register for rendering on next frame, since there are updates |
|
3383 </xsl:text> |
|
3384 <xsl:text> requestHMIAnimation(); |
|
3385 </xsl:text> |
|
3386 <xsl:text> } catch(err) { |
|
3387 </xsl:text> |
|
3388 <xsl:text> // 1003 is for "Unsupported Data" |
|
3389 </xsl:text> |
|
3390 <xsl:text> // ws.close(1003, err.message); |
|
3391 </xsl:text> |
|
3392 <xsl:text> |
|
3393 </xsl:text> |
|
3394 <xsl:text> // TODO : remove debug alert ? |
|
3395 </xsl:text> |
|
3396 <xsl:text> alert("Error : "+err.message+"\nHMI will be reloaded."); |
|
3397 </xsl:text> |
|
3398 <xsl:text> |
|
3399 </xsl:text> |
|
3400 <xsl:text> // force reload ignoring cache |
|
3401 </xsl:text> |
|
3402 <xsl:text> location.reload(true); |
|
3403 </xsl:text> |
|
3404 <xsl:text> } |
|
3405 </xsl:text> |
|
3406 <xsl:text>}; |
|
3407 </xsl:text> |
|
3408 <xsl:text> |
|
3409 </xsl:text> |
|
3410 <xsl:text> |
|
3411 </xsl:text> |
|
3412 <xsl:text>function send_blob(data) { |
|
3413 </xsl:text> |
|
3414 <xsl:text> if(data.length > 0) { |
|
3415 </xsl:text> |
|
3416 <xsl:text> ws.send(new Blob([new Uint8Array(hmi_hash)].concat(data))); |
|
3417 </xsl:text> |
|
3418 <xsl:text> }; |
|
3419 </xsl:text> |
|
3420 <xsl:text>}; |
|
3421 </xsl:text> |
|
3422 <xsl:text> |
|
3423 </xsl:text> |
|
3424 <xsl:text>const typedarray_types = { |
|
3425 </xsl:text> |
|
3426 <xsl:text> INT: (number) => new Int16Array([number]), |
|
3427 </xsl:text> |
|
3428 <xsl:text> BOOL: (truth) => new Int16Array([truth]), |
|
3429 </xsl:text> |
|
3430 <xsl:text> NODE: (truth) => new Int16Array([truth]), |
|
3431 </xsl:text> |
|
3432 <xsl:text> STRING: (str) => { |
|
3433 </xsl:text> |
|
3434 <xsl:text> // beremiz default string max size is 128 |
|
3435 </xsl:text> |
|
3436 <xsl:text> str = str.slice(0,128); |
|
3437 </xsl:text> |
|
3438 <xsl:text> binary = new Uint8Array(str.length + 1); |
|
3439 </xsl:text> |
|
3440 <xsl:text> binary[0] = str.length; |
|
3441 </xsl:text> |
|
3442 <xsl:text> for(var i = 0; i < str.length; i++){ |
|
3443 </xsl:text> |
|
3444 <xsl:text> binary[i+1] = str.charCodeAt(i); |
3065 </xsl:text> |
3445 </xsl:text> |
3066 <xsl:text> } |
3446 <xsl:text> } |
3067 </xsl:text> |
3447 </xsl:text> |
3068 <xsl:text> else if(typeof(d) == "object" && d.length >= idxidx){ |
3448 <xsl:text> return binary; |
3069 </xsl:text> |
3449 </xsl:text> |
3070 <xsl:text> d[idxidx].call(widget, value, oldval); |
3450 <xsl:text> } |
|
3451 </xsl:text> |
|
3452 <xsl:text> /* TODO */ |
|
3453 </xsl:text> |
|
3454 <xsl:text>}; |
|
3455 </xsl:text> |
|
3456 <xsl:text> |
|
3457 </xsl:text> |
|
3458 <xsl:text>function send_reset() { |
|
3459 </xsl:text> |
|
3460 <xsl:text> send_blob(new Uint8Array([1])); /* reset = 1 */ |
|
3461 </xsl:text> |
|
3462 <xsl:text>}; |
|
3463 </xsl:text> |
|
3464 <xsl:text> |
|
3465 </xsl:text> |
|
3466 <xsl:text>// subscription state, as it should be in hmi server |
|
3467 </xsl:text> |
|
3468 <xsl:text>// hmitree indexed array of integers |
|
3469 </xsl:text> |
|
3470 <xsl:text>var subscriptions = hmitree_types.map(_ignored => 0); |
|
3471 </xsl:text> |
|
3472 <xsl:text> |
|
3473 </xsl:text> |
|
3474 <xsl:text>// subscription state as needed by widget now |
|
3475 </xsl:text> |
|
3476 <xsl:text>// hmitree indexed array of Sets of widgets objects |
|
3477 </xsl:text> |
|
3478 <xsl:text>var subscribers = hmitree_types.map(_ignored => new Set()); |
|
3479 </xsl:text> |
|
3480 <xsl:text> |
|
3481 </xsl:text> |
|
3482 <xsl:text>// artificially subscribe the watchdog widget to "/heartbeat" hmi variable |
|
3483 </xsl:text> |
|
3484 <xsl:text>// Since dispatch directly calls change_hmi_value, |
|
3485 </xsl:text> |
|
3486 <xsl:text>// PLC will periodically send variable at given frequency |
|
3487 </xsl:text> |
|
3488 <xsl:text>subscribers[heartbeat_index].add({ |
|
3489 </xsl:text> |
|
3490 <xsl:text> /* type: "Watchdog", */ |
|
3491 </xsl:text> |
|
3492 <xsl:text> frequency: 1, |
|
3493 </xsl:text> |
|
3494 <xsl:text> indexes: [heartbeat_index], |
|
3495 </xsl:text> |
|
3496 <xsl:text> new_hmi_value: function(index, value, oldval) { |
|
3497 </xsl:text> |
|
3498 <xsl:text> apply_hmi_value(heartbeat_index, value+1); |
|
3499 </xsl:text> |
|
3500 <xsl:text> } |
|
3501 </xsl:text> |
|
3502 <xsl:text>}); |
|
3503 </xsl:text> |
|
3504 <xsl:text> |
|
3505 </xsl:text> |
|
3506 <xsl:text>function update_subscriptions() { |
|
3507 </xsl:text> |
|
3508 <xsl:text> let delta = []; |
|
3509 </xsl:text> |
|
3510 <xsl:text> for(let index = 0; index < subscribers.length; index++){ |
|
3511 </xsl:text> |
|
3512 <xsl:text> let widgets = subscribers[index]; |
|
3513 </xsl:text> |
|
3514 <xsl:text> |
|
3515 </xsl:text> |
|
3516 <xsl:text> // periods are in ms |
|
3517 </xsl:text> |
|
3518 <xsl:text> let previous_period = subscriptions[index]; |
|
3519 </xsl:text> |
|
3520 <xsl:text> |
|
3521 </xsl:text> |
|
3522 <xsl:text> // subscribing with a zero period is unsubscribing |
|
3523 </xsl:text> |
|
3524 <xsl:text> let new_period = 0; |
|
3525 </xsl:text> |
|
3526 <xsl:text> if(widgets.size > 0) { |
|
3527 </xsl:text> |
|
3528 <xsl:text> let maxfreq = 0; |
|
3529 </xsl:text> |
|
3530 <xsl:text> for(let widget of widgets){ |
|
3531 </xsl:text> |
|
3532 <xsl:text> let wf = widget.frequency; |
|
3533 </xsl:text> |
|
3534 <xsl:text> if(wf != undefined && maxfreq < wf) |
|
3535 </xsl:text> |
|
3536 <xsl:text> maxfreq = wf; |
|
3537 </xsl:text> |
|
3538 <xsl:text> } |
|
3539 </xsl:text> |
|
3540 <xsl:text> |
|
3541 </xsl:text> |
|
3542 <xsl:text> if(maxfreq != 0) |
|
3543 </xsl:text> |
|
3544 <xsl:text> new_period = 1000/maxfreq; |
3071 </xsl:text> |
3545 </xsl:text> |
3072 <xsl:text> } |
3546 <xsl:text> } |
3073 </xsl:text> |
3547 </xsl:text> |
3074 <xsl:text> /* else dispatch_0, ..., dispatch_n ? */ |
3548 <xsl:text> |
3075 </xsl:text> |
3549 </xsl:text> |
3076 <xsl:text> /*else { |
3550 <xsl:text> if(previous_period != new_period) { |
3077 </xsl:text> |
3551 </xsl:text> |
3078 <xsl:text> throw new Error("Dunno how to dispatch to widget at index = " + index); |
3552 <xsl:text> subscriptions[index] = new_period; |
3079 </xsl:text> |
3553 </xsl:text> |
3080 <xsl:text> }*/ |
3554 <xsl:text> delta.push( |
3081 </xsl:text> |
3555 </xsl:text> |
3082 <xsl:text> } catch(err) { |
3556 <xsl:text> new Uint8Array([2]), /* subscribe = 2 */ |
3083 </xsl:text> |
3557 </xsl:text> |
3084 <xsl:text> console.log(err); |
3558 <xsl:text> new Uint32Array([index]), |
|
3559 </xsl:text> |
|
3560 <xsl:text> new Uint16Array([new_period])); |
|
3561 </xsl:text> |
|
3562 <xsl:text> } |
3085 </xsl:text> |
3563 </xsl:text> |
3086 <xsl:text> } |
3564 <xsl:text> } |
3087 </xsl:text> |
3565 </xsl:text> |
|
3566 <xsl:text> send_blob(delta); |
|
3567 </xsl:text> |
|
3568 <xsl:text>}; |
|
3569 </xsl:text> |
|
3570 <xsl:text> |
|
3571 </xsl:text> |
|
3572 <xsl:text>function send_hmi_value(index, value) { |
|
3573 </xsl:text> |
|
3574 <xsl:text> let iectype = hmitree_types[index]; |
|
3575 </xsl:text> |
|
3576 <xsl:text> let tobinary = typedarray_types[iectype]; |
|
3577 </xsl:text> |
|
3578 <xsl:text> send_blob([ |
|
3579 </xsl:text> |
|
3580 <xsl:text> new Uint8Array([0]), /* setval = 0 */ |
|
3581 </xsl:text> |
|
3582 <xsl:text> new Uint32Array([index]), |
|
3583 </xsl:text> |
|
3584 <xsl:text> tobinary(value)]); |
|
3585 </xsl:text> |
|
3586 <xsl:text> |
|
3587 </xsl:text> |
|
3588 <xsl:text> // DON'T DO THAT unless read_iterator in svghmi.c modifies wbuf as well, not only rbuf |
|
3589 </xsl:text> |
|
3590 <xsl:text> // cache[index] = value; |
|
3591 </xsl:text> |
|
3592 <xsl:text>}; |
|
3593 </xsl:text> |
|
3594 <xsl:text> |
|
3595 </xsl:text> |
|
3596 <xsl:text>function apply_hmi_value(index, new_val) { |
|
3597 </xsl:text> |
|
3598 <xsl:text> let old_val = cache[index] |
|
3599 </xsl:text> |
|
3600 <xsl:text> if(new_val != undefined && old_val != new_val) |
|
3601 </xsl:text> |
|
3602 <xsl:text> send_hmi_value(index, new_val); |
|
3603 </xsl:text> |
|
3604 <xsl:text> return new_val; |
|
3605 </xsl:text> |
3088 <xsl:text>} |
3606 <xsl:text>} |
3089 </xsl:text> |
3607 </xsl:text> |
3090 <xsl:text> |
3608 <xsl:text> |
3091 </xsl:text> |
3609 </xsl:text> |
3092 <xsl:text>function dispatch_value(index, value) { |
3610 <xsl:text>quotes = {"'":null, '"':null}; |
3093 </xsl:text> |
3611 </xsl:text> |
3094 <xsl:text> let widgets = subscribers[index]; |
3612 <xsl:text> |
3095 </xsl:text> |
3613 </xsl:text> |
3096 <xsl:text> |
3614 <xsl:text>function change_hmi_value(index, opstr) { |
3097 </xsl:text> |
3615 </xsl:text> |
3098 <xsl:text> let oldval = cache[index]; |
3616 <xsl:text> let op = opstr[0]; |
3099 </xsl:text> |
3617 </xsl:text> |
3100 <xsl:text> cache[index] = value; |
3618 <xsl:text> let given_val; |
3101 </xsl:text> |
3619 </xsl:text> |
3102 <xsl:text> |
3620 <xsl:text> if(opstr.length < 2) |
3103 </xsl:text> |
3621 </xsl:text> |
3104 <xsl:text> if(widgets.size > 0) { |
3622 <xsl:text> return undefined; // TODO raise |
3105 </xsl:text> |
3623 </xsl:text> |
3106 <xsl:text> for(let widget of widgets){ |
3624 <xsl:text> if(opstr[1] in quotes){ |
3107 </xsl:text> |
3625 </xsl:text> |
3108 <xsl:text> dispatch_value_to_widget(widget, index, value, oldval); |
3626 <xsl:text> if(opstr.length < 3) |
|
3627 </xsl:text> |
|
3628 <xsl:text> return undefined; // TODO raise |
|
3629 </xsl:text> |
|
3630 <xsl:text> if(opstr[opstr.length-1] == opstr[1]){ |
|
3631 </xsl:text> |
|
3632 <xsl:text> given_val = opstr.slice(2,opstr.length-1); |
3109 </xsl:text> |
3633 </xsl:text> |
3110 <xsl:text> } |
3634 <xsl:text> } |
3111 </xsl:text> |
3635 </xsl:text> |
|
3636 <xsl:text> } else { |
|
3637 </xsl:text> |
|
3638 <xsl:text> given_val = Number(opstr.slice(1)); |
|
3639 </xsl:text> |
3112 <xsl:text> } |
3640 <xsl:text> } |
3113 </xsl:text> |
3641 </xsl:text> |
|
3642 <xsl:text> let old_val = cache[index]; |
|
3643 </xsl:text> |
|
3644 <xsl:text> let new_val; |
|
3645 </xsl:text> |
|
3646 <xsl:text> switch(op){ |
|
3647 </xsl:text> |
|
3648 <xsl:text> case "=": |
|
3649 </xsl:text> |
|
3650 <xsl:text> new_val = given_val; |
|
3651 </xsl:text> |
|
3652 <xsl:text> break; |
|
3653 </xsl:text> |
|
3654 <xsl:text> case "+": |
|
3655 </xsl:text> |
|
3656 <xsl:text> new_val = old_val + given_val; |
|
3657 </xsl:text> |
|
3658 <xsl:text> break; |
|
3659 </xsl:text> |
|
3660 <xsl:text> case "-": |
|
3661 </xsl:text> |
|
3662 <xsl:text> new_val = old_val - given_val; |
|
3663 </xsl:text> |
|
3664 <xsl:text> break; |
|
3665 </xsl:text> |
|
3666 <xsl:text> case "*": |
|
3667 </xsl:text> |
|
3668 <xsl:text> new_val = old_val * given_val; |
|
3669 </xsl:text> |
|
3670 <xsl:text> break; |
|
3671 </xsl:text> |
|
3672 <xsl:text> case "/": |
|
3673 </xsl:text> |
|
3674 <xsl:text> new_val = old_val / given_val; |
|
3675 </xsl:text> |
|
3676 <xsl:text> break; |
|
3677 </xsl:text> |
|
3678 <xsl:text> } |
|
3679 </xsl:text> |
|
3680 <xsl:text> if(new_val != undefined && old_val != new_val) |
|
3681 </xsl:text> |
|
3682 <xsl:text> send_hmi_value(index, new_val); |
|
3683 </xsl:text> |
|
3684 <xsl:text> // TODO else raise |
|
3685 </xsl:text> |
|
3686 <xsl:text> return new_val; |
|
3687 </xsl:text> |
|
3688 <xsl:text>} |
|
3689 </xsl:text> |
|
3690 <xsl:text> |
|
3691 </xsl:text> |
|
3692 <xsl:text>var current_visible_page; |
|
3693 </xsl:text> |
|
3694 <xsl:text>var current_subscribed_page; |
|
3695 </xsl:text> |
|
3696 <xsl:text>var current_page_index; |
|
3697 </xsl:text> |
|
3698 <xsl:text> |
|
3699 </xsl:text> |
|
3700 <xsl:text>function prepare_svg() { |
|
3701 </xsl:text> |
|
3702 <xsl:text> for(let eltid in detachable_elements){ |
|
3703 </xsl:text> |
|
3704 <xsl:text> let [element,parent] = detachable_elements[eltid]; |
|
3705 </xsl:text> |
|
3706 <xsl:text> parent.removeChild(element); |
|
3707 </xsl:text> |
|
3708 <xsl:text> } |
|
3709 </xsl:text> |
3114 <xsl:text>}; |
3710 <xsl:text>}; |
3115 </xsl:text> |
3711 </xsl:text> |
3116 <xsl:text> |
3712 <xsl:text> |
3117 </xsl:text> |
3713 </xsl:text> |
3118 <xsl:text>function init_widgets() { |
3714 <xsl:text>function switch_page(page_name, page_index) { |
3119 </xsl:text> |
3715 </xsl:text> |
3120 <xsl:text> Object.keys(hmi_widgets).forEach(function(id) { |
3716 <xsl:text> if(current_subscribed_page != current_visible_page){ |
3121 </xsl:text> |
3717 </xsl:text> |
3122 <xsl:text> let widget = hmi_widgets[id]; |
3718 <xsl:text> /* page switch already going */ |
3123 </xsl:text> |
3719 </xsl:text> |
3124 <xsl:text> let init = widget.init; |
3720 <xsl:text> /* TODO LOG ERROR */ |
3125 </xsl:text> |
3721 </xsl:text> |
3126 <xsl:text> if(typeof(init) == "function"){ |
3722 <xsl:text> return false; |
3127 </xsl:text> |
|
3128 <xsl:text> try { |
|
3129 </xsl:text> |
|
3130 <xsl:text> init.call(widget); |
|
3131 </xsl:text> |
|
3132 <xsl:text> } catch(err) { |
|
3133 </xsl:text> |
|
3134 <xsl:text> console.log(err); |
|
3135 </xsl:text> |
|
3136 <xsl:text> } |
|
3137 </xsl:text> |
|
3138 <xsl:text> } |
|
3139 </xsl:text> |
|
3140 <xsl:text> }); |
|
3141 </xsl:text> |
|
3142 <xsl:text>}; |
|
3143 </xsl:text> |
|
3144 <xsl:text> |
|
3145 </xsl:text> |
|
3146 <xsl:text>// Open WebSocket to relative "/ws" address |
|
3147 </xsl:text> |
|
3148 <xsl:text>var ws = new WebSocket(window.location.href.replace(/^http(s?:\/\/[^\/]*)\/.*$/, 'ws$1/ws')); |
|
3149 </xsl:text> |
|
3150 <xsl:text>ws.binaryType = 'arraybuffer'; |
|
3151 </xsl:text> |
|
3152 <xsl:text> |
|
3153 </xsl:text> |
|
3154 <xsl:text>const dvgetters = { |
|
3155 </xsl:text> |
|
3156 <xsl:text> INT: (dv,offset) => [dv.getInt16(offset, true), 2], |
|
3157 </xsl:text> |
|
3158 <xsl:text> BOOL: (dv,offset) => [dv.getInt8(offset, true), 1], |
|
3159 </xsl:text> |
|
3160 <xsl:text> NODE: (dv,offset) => [dv.getInt8(offset, true), 1], |
|
3161 </xsl:text> |
|
3162 <xsl:text> STRING: (dv, offset) => { |
|
3163 </xsl:text> |
|
3164 <xsl:text> size = dv.getInt8(offset); |
|
3165 </xsl:text> |
|
3166 <xsl:text> return [ |
|
3167 </xsl:text> |
|
3168 <xsl:text> String.fromCharCode.apply(null, new Uint8Array( |
|
3169 </xsl:text> |
|
3170 <xsl:text> dv.buffer, /* original buffer */ |
|
3171 </xsl:text> |
|
3172 <xsl:text> offset + 1, /* string starts after size*/ |
|
3173 </xsl:text> |
|
3174 <xsl:text> size /* size of string */ |
|
3175 </xsl:text> |
|
3176 <xsl:text> )), size + 1]; /* total increment */ |
|
3177 </xsl:text> |
3723 </xsl:text> |
3178 <xsl:text> } |
3724 <xsl:text> } |
3179 </xsl:text> |
3725 </xsl:text> |
3180 <xsl:text>}; |
3726 <xsl:text> |
3181 </xsl:text> |
3727 </xsl:text> |
3182 <xsl:text> |
3728 <xsl:text> if(page_name == undefined) |
3183 </xsl:text> |
3729 </xsl:text> |
3184 <xsl:text>// Apply updates recieved through ws.onmessage to subscribed widgets |
3730 <xsl:text> page_name = current_subscribed_page; |
3185 </xsl:text> |
3731 </xsl:text> |
3186 <xsl:text>function apply_updates() { |
3732 <xsl:text> |
3187 </xsl:text> |
3733 </xsl:text> |
3188 <xsl:text> for(let index in updates){ |
3734 <xsl:text> |
3189 </xsl:text> |
3735 </xsl:text> |
3190 <xsl:text> // serving as a key, index becomes a string |
3736 <xsl:text> let old_desc = page_desc[current_subscribed_page]; |
3191 </xsl:text> |
3737 </xsl:text> |
3192 <xsl:text> // -> pass Number(index) instead |
3738 <xsl:text> let new_desc = page_desc[page_name]; |
3193 </xsl:text> |
3739 </xsl:text> |
3194 <xsl:text> dispatch_value(Number(index), updates[index]); |
3740 <xsl:text> |
3195 </xsl:text> |
3741 </xsl:text> |
3196 <xsl:text> delete updates[index]; |
3742 <xsl:text> if(new_desc == undefined){ |
|
3743 </xsl:text> |
|
3744 <xsl:text> /* TODO LOG ERROR */ |
|
3745 </xsl:text> |
|
3746 <xsl:text> return false; |
3197 </xsl:text> |
3747 </xsl:text> |
3198 <xsl:text> } |
3748 <xsl:text> } |
3199 </xsl:text> |
3749 </xsl:text> |
3200 <xsl:text>} |
3750 <xsl:text> |
3201 </xsl:text> |
3751 </xsl:text> |
3202 <xsl:text> |
3752 <xsl:text> if(page_index == undefined){ |
3203 </xsl:text> |
3753 </xsl:text> |
3204 <xsl:text>// Called on requestAnimationFrame, modifies DOM |
3754 <xsl:text> page_index = new_desc.page_index; |
3205 </xsl:text> |
|
3206 <xsl:text>var requestAnimationFrameID = null; |
|
3207 </xsl:text> |
|
3208 <xsl:text>function animate() { |
|
3209 </xsl:text> |
|
3210 <xsl:text> // Do the page swith if any one pending |
|
3211 </xsl:text> |
|
3212 <xsl:text> if(current_subscribed_page != current_visible_page){ |
|
3213 </xsl:text> |
|
3214 <xsl:text> switch_visible_page(current_subscribed_page); |
|
3215 </xsl:text> |
3755 </xsl:text> |
3216 <xsl:text> } |
3756 <xsl:text> } |
3217 </xsl:text> |
3757 </xsl:text> |
3218 <xsl:text> |
3758 <xsl:text> |
3219 </xsl:text> |
3759 </xsl:text> |
3220 <xsl:text> while(widget = need_cache_apply.pop()){ |
3760 <xsl:text> if(old_desc){ |
3221 </xsl:text> |
3761 </xsl:text> |
3222 <xsl:text> widget.apply_cache(); |
3762 <xsl:text> old_desc.widgets.map(([widget,relativeness])=>widget.unsub()); |
3223 </xsl:text> |
3763 </xsl:text> |
3224 <xsl:text> } |
3764 <xsl:text> } |
3225 </xsl:text> |
3765 </xsl:text> |
3226 <xsl:text> |
|
3227 </xsl:text> |
|
3228 <xsl:text> if(jumps_need_update) update_jumps(); |
|
3229 </xsl:text> |
|
3230 <xsl:text> |
|
3231 </xsl:text> |
|
3232 <xsl:text> apply_updates(); |
|
3233 </xsl:text> |
|
3234 <xsl:text> requestAnimationFrameID = null; |
|
3235 </xsl:text> |
|
3236 <xsl:text>} |
|
3237 </xsl:text> |
|
3238 <xsl:text> |
|
3239 </xsl:text> |
|
3240 <xsl:text>function requestHMIAnimation() { |
|
3241 </xsl:text> |
|
3242 <xsl:text> if(requestAnimationFrameID == null){ |
|
3243 </xsl:text> |
|
3244 <xsl:text> requestAnimationFrameID = window.requestAnimationFrame(animate); |
|
3245 </xsl:text> |
|
3246 <xsl:text> } |
|
3247 </xsl:text> |
|
3248 <xsl:text>} |
|
3249 </xsl:text> |
|
3250 <xsl:text> |
|
3251 </xsl:text> |
|
3252 <xsl:text>// Message reception handler |
|
3253 </xsl:text> |
|
3254 <xsl:text>// Hash is verified and HMI values updates resulting from binary parsing |
|
3255 </xsl:text> |
|
3256 <xsl:text>// are stored until browser can compute next frame, DOM is left untouched |
|
3257 </xsl:text> |
|
3258 <xsl:text>ws.onmessage = function (evt) { |
|
3259 </xsl:text> |
|
3260 <xsl:text> |
|
3261 </xsl:text> |
|
3262 <xsl:text> let data = evt.data; |
|
3263 </xsl:text> |
|
3264 <xsl:text> let dv = new DataView(data); |
|
3265 </xsl:text> |
|
3266 <xsl:text> let i = 0; |
|
3267 </xsl:text> |
|
3268 <xsl:text> try { |
|
3269 </xsl:text> |
|
3270 <xsl:text> for(let hash_int of hmi_hash) { |
|
3271 </xsl:text> |
|
3272 <xsl:text> if(hash_int != dv.getUint8(i)){ |
|
3273 </xsl:text> |
|
3274 <xsl:text> throw new Error("Hash doesn't match"); |
|
3275 </xsl:text> |
|
3276 <xsl:text> }; |
|
3277 </xsl:text> |
|
3278 <xsl:text> i++; |
|
3279 </xsl:text> |
|
3280 <xsl:text> }; |
|
3281 </xsl:text> |
|
3282 <xsl:text> |
|
3283 </xsl:text> |
|
3284 <xsl:text> while(i < data.byteLength){ |
|
3285 </xsl:text> |
|
3286 <xsl:text> let index = dv.getUint32(i, true); |
|
3287 </xsl:text> |
|
3288 <xsl:text> i += 4; |
|
3289 </xsl:text> |
|
3290 <xsl:text> let iectype = hmitree_types[index]; |
|
3291 </xsl:text> |
|
3292 <xsl:text> if(iectype != undefined){ |
|
3293 </xsl:text> |
|
3294 <xsl:text> let dvgetter = dvgetters[iectype]; |
|
3295 </xsl:text> |
|
3296 <xsl:text> let [value, bytesize] = dvgetter(dv,i); |
|
3297 </xsl:text> |
|
3298 <xsl:text> updates[index] = value; |
|
3299 </xsl:text> |
|
3300 <xsl:text> i += bytesize; |
|
3301 </xsl:text> |
|
3302 <xsl:text> } else { |
|
3303 </xsl:text> |
|
3304 <xsl:text> throw new Error("Unknown index "+index); |
|
3305 </xsl:text> |
|
3306 <xsl:text> } |
|
3307 </xsl:text> |
|
3308 <xsl:text> }; |
|
3309 </xsl:text> |
|
3310 <xsl:text> // register for rendering on next frame, since there are updates |
|
3311 </xsl:text> |
|
3312 <xsl:text> requestHMIAnimation(); |
|
3313 </xsl:text> |
|
3314 <xsl:text> } catch(err) { |
|
3315 </xsl:text> |
|
3316 <xsl:text> // 1003 is for "Unsupported Data" |
|
3317 </xsl:text> |
|
3318 <xsl:text> // ws.close(1003, err.message); |
|
3319 </xsl:text> |
|
3320 <xsl:text> |
|
3321 </xsl:text> |
|
3322 <xsl:text> // TODO : remove debug alert ? |
|
3323 </xsl:text> |
|
3324 <xsl:text> alert("Error : "+err.message+"\nHMI will be reloaded."); |
|
3325 </xsl:text> |
|
3326 <xsl:text> |
|
3327 </xsl:text> |
|
3328 <xsl:text> // force reload ignoring cache |
|
3329 </xsl:text> |
|
3330 <xsl:text> location.reload(true); |
|
3331 </xsl:text> |
|
3332 <xsl:text> } |
|
3333 </xsl:text> |
|
3334 <xsl:text>}; |
|
3335 </xsl:text> |
|
3336 <xsl:text> |
|
3337 </xsl:text> |
|
3338 <xsl:text> |
|
3339 </xsl:text> |
|
3340 <xsl:text>function send_blob(data) { |
|
3341 </xsl:text> |
|
3342 <xsl:text> if(data.length > 0) { |
|
3343 </xsl:text> |
|
3344 <xsl:text> ws.send(new Blob([new Uint8Array(hmi_hash)].concat(data))); |
|
3345 </xsl:text> |
|
3346 <xsl:text> }; |
|
3347 </xsl:text> |
|
3348 <xsl:text>}; |
|
3349 </xsl:text> |
|
3350 <xsl:text> |
|
3351 </xsl:text> |
|
3352 <xsl:text>const typedarray_types = { |
|
3353 </xsl:text> |
|
3354 <xsl:text> INT: (number) => new Int16Array([number]), |
|
3355 </xsl:text> |
|
3356 <xsl:text> BOOL: (truth) => new Int16Array([truth]), |
|
3357 </xsl:text> |
|
3358 <xsl:text> NODE: (truth) => new Int16Array([truth]), |
|
3359 </xsl:text> |
|
3360 <xsl:text> STRING: (str) => { |
|
3361 </xsl:text> |
|
3362 <xsl:text> // beremiz default string max size is 128 |
|
3363 </xsl:text> |
|
3364 <xsl:text> str = str.slice(0,128); |
|
3365 </xsl:text> |
|
3366 <xsl:text> binary = new Uint8Array(str.length + 1); |
|
3367 </xsl:text> |
|
3368 <xsl:text> binary[0] = str.length; |
|
3369 </xsl:text> |
|
3370 <xsl:text> for(var i = 0; i < str.length; i++){ |
|
3371 </xsl:text> |
|
3372 <xsl:text> binary[i+1] = str.charCodeAt(i); |
|
3373 </xsl:text> |
|
3374 <xsl:text> } |
|
3375 </xsl:text> |
|
3376 <xsl:text> return binary; |
|
3377 </xsl:text> |
|
3378 <xsl:text> } |
|
3379 </xsl:text> |
|
3380 <xsl:text> /* TODO */ |
|
3381 </xsl:text> |
|
3382 <xsl:text>}; |
|
3383 </xsl:text> |
|
3384 <xsl:text> |
|
3385 </xsl:text> |
|
3386 <xsl:text>function send_reset() { |
|
3387 </xsl:text> |
|
3388 <xsl:text> send_blob(new Uint8Array([1])); /* reset = 1 */ |
|
3389 </xsl:text> |
|
3390 <xsl:text>}; |
|
3391 </xsl:text> |
|
3392 <xsl:text> |
|
3393 </xsl:text> |
|
3394 <xsl:text>// subscription state, as it should be in hmi server |
|
3395 </xsl:text> |
|
3396 <xsl:text>// hmitree indexed array of integers |
|
3397 </xsl:text> |
|
3398 <xsl:text>var subscriptions = hmitree_types.map(_ignored => 0); |
|
3399 </xsl:text> |
|
3400 <xsl:text> |
|
3401 </xsl:text> |
|
3402 <xsl:text>// subscription state as needed by widget now |
|
3403 </xsl:text> |
|
3404 <xsl:text>// hmitree indexed array of Sets of widgets objects |
|
3405 </xsl:text> |
|
3406 <xsl:text>var subscribers = hmitree_types.map(_ignored => new Set()); |
|
3407 </xsl:text> |
|
3408 <xsl:text> |
|
3409 </xsl:text> |
|
3410 <xsl:text>// artificially subscribe the watchdog widget to "/heartbeat" hmi variable |
|
3411 </xsl:text> |
|
3412 <xsl:text>// Since dispatch directly calls change_hmi_value, |
|
3413 </xsl:text> |
|
3414 <xsl:text>// PLC will periodically send variable at given frequency |
|
3415 </xsl:text> |
|
3416 <xsl:text>subscribers[heartbeat_index].add({ |
|
3417 </xsl:text> |
|
3418 <xsl:text> /* type: "Watchdog", */ |
|
3419 </xsl:text> |
|
3420 <xsl:text> frequency: 1, |
|
3421 </xsl:text> |
|
3422 <xsl:text> indexes: [heartbeat_index], |
|
3423 </xsl:text> |
|
3424 <xsl:text> dispatch: function(value) { |
|
3425 </xsl:text> |
|
3426 <xsl:text> apply_hmi_value(heartbeat_index, value+1); |
|
3427 </xsl:text> |
|
3428 <xsl:text> } |
|
3429 </xsl:text> |
|
3430 <xsl:text>}); |
|
3431 </xsl:text> |
|
3432 <xsl:text> |
|
3433 </xsl:text> |
|
3434 <xsl:text>function update_subscriptions() { |
|
3435 </xsl:text> |
|
3436 <xsl:text> let delta = []; |
|
3437 </xsl:text> |
|
3438 <xsl:text> for(let index = 0; index < subscribers.length; index++){ |
|
3439 </xsl:text> |
|
3440 <xsl:text> let widgets = subscribers[index]; |
|
3441 </xsl:text> |
|
3442 <xsl:text> |
|
3443 </xsl:text> |
|
3444 <xsl:text> // periods are in ms |
|
3445 </xsl:text> |
|
3446 <xsl:text> let previous_period = subscriptions[index]; |
|
3447 </xsl:text> |
|
3448 <xsl:text> |
|
3449 </xsl:text> |
|
3450 <xsl:text> // subscribing with a zero period is unsubscribing |
|
3451 </xsl:text> |
|
3452 <xsl:text> let new_period = 0; |
|
3453 </xsl:text> |
|
3454 <xsl:text> if(widgets.size > 0) { |
|
3455 </xsl:text> |
|
3456 <xsl:text> let maxfreq = 0; |
|
3457 </xsl:text> |
|
3458 <xsl:text> for(let widget of widgets){ |
|
3459 </xsl:text> |
|
3460 <xsl:text> let wf = widget.frequency; |
|
3461 </xsl:text> |
|
3462 <xsl:text> if(wf != undefined && maxfreq < wf) |
|
3463 </xsl:text> |
|
3464 <xsl:text> maxfreq = wf; |
|
3465 </xsl:text> |
|
3466 <xsl:text> } |
|
3467 </xsl:text> |
|
3468 <xsl:text> |
|
3469 </xsl:text> |
|
3470 <xsl:text> if(maxfreq != 0) |
|
3471 </xsl:text> |
|
3472 <xsl:text> new_period = 1000/maxfreq; |
|
3473 </xsl:text> |
|
3474 <xsl:text> } |
|
3475 </xsl:text> |
|
3476 <xsl:text> |
|
3477 </xsl:text> |
|
3478 <xsl:text> if(previous_period != new_period) { |
|
3479 </xsl:text> |
|
3480 <xsl:text> subscriptions[index] = new_period; |
|
3481 </xsl:text> |
|
3482 <xsl:text> delta.push( |
|
3483 </xsl:text> |
|
3484 <xsl:text> new Uint8Array([2]), /* subscribe = 2 */ |
|
3485 </xsl:text> |
|
3486 <xsl:text> new Uint32Array([index]), |
|
3487 </xsl:text> |
|
3488 <xsl:text> new Uint16Array([new_period])); |
|
3489 </xsl:text> |
|
3490 <xsl:text> } |
|
3491 </xsl:text> |
|
3492 <xsl:text> } |
|
3493 </xsl:text> |
|
3494 <xsl:text> send_blob(delta); |
|
3495 </xsl:text> |
|
3496 <xsl:text>}; |
|
3497 </xsl:text> |
|
3498 <xsl:text> |
|
3499 </xsl:text> |
|
3500 <xsl:text>function send_hmi_value(index, value) { |
|
3501 </xsl:text> |
|
3502 <xsl:text> let iectype = hmitree_types[index]; |
|
3503 </xsl:text> |
|
3504 <xsl:text> let tobinary = typedarray_types[iectype]; |
|
3505 </xsl:text> |
|
3506 <xsl:text> send_blob([ |
|
3507 </xsl:text> |
|
3508 <xsl:text> new Uint8Array([0]), /* setval = 0 */ |
|
3509 </xsl:text> |
|
3510 <xsl:text> new Uint32Array([index]), |
|
3511 </xsl:text> |
|
3512 <xsl:text> tobinary(value)]); |
|
3513 </xsl:text> |
|
3514 <xsl:text> |
|
3515 </xsl:text> |
|
3516 <xsl:text> // DON'T DO THAT unless read_iterator in svghmi.c modifies wbuf as well, not only rbuf |
|
3517 </xsl:text> |
|
3518 <xsl:text> // cache[index] = value; |
|
3519 </xsl:text> |
|
3520 <xsl:text>}; |
|
3521 </xsl:text> |
|
3522 <xsl:text> |
|
3523 </xsl:text> |
|
3524 <xsl:text>function apply_hmi_value(index, new_val) { |
|
3525 </xsl:text> |
|
3526 <xsl:text> let old_val = cache[index] |
|
3527 </xsl:text> |
|
3528 <xsl:text> if(new_val != undefined && old_val != new_val) |
|
3529 </xsl:text> |
|
3530 <xsl:text> send_hmi_value(index, new_val); |
|
3531 </xsl:text> |
|
3532 <xsl:text> return new_val; |
|
3533 </xsl:text> |
|
3534 <xsl:text>} |
|
3535 </xsl:text> |
|
3536 <xsl:text> |
|
3537 </xsl:text> |
|
3538 <xsl:text>quotes = {"'":null, '"':null}; |
|
3539 </xsl:text> |
|
3540 <xsl:text> |
|
3541 </xsl:text> |
|
3542 <xsl:text>function change_hmi_value(index, opstr) { |
|
3543 </xsl:text> |
|
3544 <xsl:text> let op = opstr[0]; |
|
3545 </xsl:text> |
|
3546 <xsl:text> let given_val; |
|
3547 </xsl:text> |
|
3548 <xsl:text> if(opstr.length < 2) |
|
3549 </xsl:text> |
|
3550 <xsl:text> return undefined; // TODO raise |
|
3551 </xsl:text> |
|
3552 <xsl:text> if(opstr[1] in quotes){ |
|
3553 </xsl:text> |
|
3554 <xsl:text> if(opstr.length < 3) |
|
3555 </xsl:text> |
|
3556 <xsl:text> return undefined; // TODO raise |
|
3557 </xsl:text> |
|
3558 <xsl:text> if(opstr[opstr.length-1] == opstr[1]){ |
|
3559 </xsl:text> |
|
3560 <xsl:text> given_val = opstr.slice(2,opstr.length-1); |
|
3561 </xsl:text> |
|
3562 <xsl:text> } |
|
3563 </xsl:text> |
|
3564 <xsl:text> } else { |
|
3565 </xsl:text> |
|
3566 <xsl:text> given_val = Number(opstr.slice(1)); |
|
3567 </xsl:text> |
|
3568 <xsl:text> } |
|
3569 </xsl:text> |
|
3570 <xsl:text> let old_val = cache[index]; |
|
3571 </xsl:text> |
|
3572 <xsl:text> let new_val; |
|
3573 </xsl:text> |
|
3574 <xsl:text> switch(op){ |
|
3575 </xsl:text> |
|
3576 <xsl:text> case "=": |
|
3577 </xsl:text> |
|
3578 <xsl:text> new_val = given_val; |
|
3579 </xsl:text> |
|
3580 <xsl:text> break; |
|
3581 </xsl:text> |
|
3582 <xsl:text> case "+": |
|
3583 </xsl:text> |
|
3584 <xsl:text> new_val = old_val + given_val; |
|
3585 </xsl:text> |
|
3586 <xsl:text> break; |
|
3587 </xsl:text> |
|
3588 <xsl:text> case "-": |
|
3589 </xsl:text> |
|
3590 <xsl:text> new_val = old_val - given_val; |
|
3591 </xsl:text> |
|
3592 <xsl:text> break; |
|
3593 </xsl:text> |
|
3594 <xsl:text> case "*": |
|
3595 </xsl:text> |
|
3596 <xsl:text> new_val = old_val * given_val; |
|
3597 </xsl:text> |
|
3598 <xsl:text> break; |
|
3599 </xsl:text> |
|
3600 <xsl:text> case "/": |
|
3601 </xsl:text> |
|
3602 <xsl:text> new_val = old_val / given_val; |
|
3603 </xsl:text> |
|
3604 <xsl:text> break; |
|
3605 </xsl:text> |
|
3606 <xsl:text> } |
|
3607 </xsl:text> |
|
3608 <xsl:text> if(new_val != undefined && old_val != new_val) |
|
3609 </xsl:text> |
|
3610 <xsl:text> send_hmi_value(index, new_val); |
|
3611 </xsl:text> |
|
3612 <xsl:text> // TODO else raise |
|
3613 </xsl:text> |
|
3614 <xsl:text> return new_val; |
|
3615 </xsl:text> |
|
3616 <xsl:text>} |
|
3617 </xsl:text> |
|
3618 <xsl:text> |
|
3619 </xsl:text> |
|
3620 <xsl:text>var current_visible_page; |
|
3621 </xsl:text> |
|
3622 <xsl:text>var current_subscribed_page; |
|
3623 </xsl:text> |
|
3624 <xsl:text>var current_page_index; |
|
3625 </xsl:text> |
|
3626 <xsl:text> |
|
3627 </xsl:text> |
|
3628 <xsl:text>function prepare_svg() { |
|
3629 </xsl:text> |
|
3630 <xsl:text> for(let eltid in detachable_elements){ |
|
3631 </xsl:text> |
|
3632 <xsl:text> let [element,parent] = detachable_elements[eltid]; |
|
3633 </xsl:text> |
|
3634 <xsl:text> parent.removeChild(element); |
|
3635 </xsl:text> |
|
3636 <xsl:text> } |
|
3637 </xsl:text> |
|
3638 <xsl:text>}; |
|
3639 </xsl:text> |
|
3640 <xsl:text> |
|
3641 </xsl:text> |
|
3642 <xsl:text>function switch_page(page_name, page_index) { |
|
3643 </xsl:text> |
|
3644 <xsl:text> if(current_subscribed_page != current_visible_page){ |
|
3645 </xsl:text> |
|
3646 <xsl:text> /* page switch already going */ |
|
3647 </xsl:text> |
|
3648 <xsl:text> /* TODO LOG ERROR */ |
|
3649 </xsl:text> |
|
3650 <xsl:text> return false; |
|
3651 </xsl:text> |
|
3652 <xsl:text> } |
|
3653 </xsl:text> |
|
3654 <xsl:text> |
|
3655 </xsl:text> |
|
3656 <xsl:text> if(page_name == undefined) |
|
3657 </xsl:text> |
|
3658 <xsl:text> page_name = current_subscribed_page; |
|
3659 </xsl:text> |
|
3660 <xsl:text> |
|
3661 </xsl:text> |
|
3662 <xsl:text> |
|
3663 </xsl:text> |
|
3664 <xsl:text> let old_desc = page_desc[current_subscribed_page]; |
|
3665 </xsl:text> |
|
3666 <xsl:text> let new_desc = page_desc[page_name]; |
|
3667 </xsl:text> |
|
3668 <xsl:text> |
|
3669 </xsl:text> |
|
3670 <xsl:text> if(new_desc == undefined){ |
|
3671 </xsl:text> |
|
3672 <xsl:text> /* TODO LOG ERROR */ |
|
3673 </xsl:text> |
|
3674 <xsl:text> return false; |
|
3675 </xsl:text> |
|
3676 <xsl:text> } |
|
3677 </xsl:text> |
|
3678 <xsl:text> |
|
3679 </xsl:text> |
|
3680 <xsl:text> if(page_index == undefined){ |
|
3681 </xsl:text> |
|
3682 <xsl:text> page_index = new_desc.page_index; |
|
3683 </xsl:text> |
|
3684 <xsl:text> } |
|
3685 </xsl:text> |
|
3686 <xsl:text> |
|
3687 </xsl:text> |
|
3688 <xsl:text> if(old_desc){ |
|
3689 </xsl:text> |
|
3690 <xsl:text> old_desc.absolute_widgets.map(w=>w.unsub()); |
|
3691 </xsl:text> |
|
3692 <xsl:text> old_desc.relative_widgets.map(w=>w.unsub()); |
|
3693 </xsl:text> |
|
3694 <xsl:text> } |
|
3695 </xsl:text> |
|
3696 <xsl:text> new_desc.absolute_widgets.map(w=>w.sub()); |
|
3697 </xsl:text> |
|
3698 <xsl:text> var new_offset = page_index == undefined ? 0 : page_index - new_desc.page_index; |
3766 <xsl:text> var new_offset = page_index == undefined ? 0 : page_index - new_desc.page_index; |
3699 </xsl:text> |
3767 </xsl:text> |
3700 <xsl:text> new_desc.relative_widgets.map(w=>w.sub(new_offset)); |
3768 <xsl:text> new_desc.widgets.map(([widget,relativeness])=>widget.sub(new_offset,relativeness)); |
3701 </xsl:text> |
3769 </xsl:text> |
3702 <xsl:text> |
3770 <xsl:text> |
3703 </xsl:text> |
3771 </xsl:text> |
3704 <xsl:text> update_subscriptions(); |
3772 <xsl:text> update_subscriptions(); |
3705 </xsl:text> |
3773 </xsl:text> |