WebSockUrl = "8013/P22671-Demo-9302/v0/StateChanged";
RestUrl = "8000/P24999-Example/v0/";
//ABOVE AUTOGENERATED
//lines above is inserted by Mint when starting web server; do noi delete line ABOVE AUTOGENERATE
IsRequestDone = true;
IsOffline = false;
FailedCalls = 0;
PoolingPeriod = 1500;
function HandleFailedCall() {
FailedCalls++;
if (FailedCalls > 3) {
setOffline();
}
}
function setOffline() {
console.log('setting offline');
IsOffline = true;
if (window.location.hash == "#offline") return;
setHash('#offline');
}
function setOnline() {
FailedCalls = 0;
IsOffline = false;
if (window.location.hash != "#offline") return;
setHash("");
}
function prepareWebSocketAddress() {
var a = window.location.origin;
var ndx = a.indexOf(":") + 1;
var afterHttp = a.substr(ndx, a.length - ndx);
var ndx2 = afterHttp.indexOf(":");
cleaned = afterHttp;
if (ndx2 != -1) {
cleaned = afterHttp.substr(0, ndx2);//webpage url port isn't 80 and is included in url
}
var result = "ws:" + cleaned + ":" + WebSockUrl;
return result;
}
function setHash(newHash) {
$.mobile.navigate(newHash);
}
function moveMotorRelative(motorIndex, steps) {
makeCorsRequest('Motors/TargetPositionRelative?id=' + motorIndex.toString(), 'PUT', steps.toString(), doNothing);
}
function doNothing(state) {
}
LastPerfCall = null;
window.onload = function start() {
//$("#MessagesInnerDiv").empty().append("OKK");
//var container = document.getElementById("MessagesInnerDiv");
//var content = container.innerHTML
//container.innerHTML = "OKKKK";
var webSockAddress = prepareWebSocketAddress();
console.log(webSockAddress);
//https://github.com/joewalnes/reconnecting-websocket
WebSock = new ReconnectingWebSocket(webSockAddress);
WebSock.debug = false;
WebSock.timeoutInterval = 3000;
WebSock.reconnectInterval = 2000;
WebSock.onmessage = function (evt) {
//auto-generated from C# static \Topas4\Web\Generators\WebSocketMessageIds.linq, todo put in build ?
//todo move out from function
WebSockIds = {};
WebSockIds["Shutter"] = 100;
WebSockIds["Interlock"] = 110;
WebSockIds["WavelengthOutputFullData"] = 202;
var messages = $.parseJSON(evt.data).Messages;
//console.log((performance.now() - LastPerfCall) + " ms");
for (var i = 0; i < messages.length; i++) {
var m = messages[i];
var d = m.Data;
switch (m.Id) {
case WebSockIds["Shutter"]:
ShutterVue.IsShutterOpen = d;
MessagesVue.IsShutterOpen = d;
break;
case WebSockIds["Interlock"]:
ShutterVue.IsInterlockOpen = d;
MessagesVue.IsInterlockOpen = d;
break;
case WebSockIds["WavelengthOutputFullData"]:
console.log("wl state");
WavelengthVue.updateWavelengthState(d.Output);
break;
}
}
};
WebSock.onclose = function (event) {
console.log("WebSock is closed");
setOffline();
}
WebSock.onopen = function (event) {
console.log("WebSock is open");
LoadAllData();//maybe we missed something while there was no connection?
setOnline();
};
LoadAllData();
initPooling();
}
function LoadAllData() {
//used when page is loaded and when websocket is reconnected to catch any changes
makeCorsRequest('GeneralInformation/GeneralInformation', 'GET', '', setDeviceSerialNumber);
MotorsVue.update();//build, no updates later
ShutterVue.update();//initial state, later on websocket will push all notificationson shutter and interlock
MessagesVue.update();//initial state, later on websocket will push all notificationson shutter and interlock
WavelengthVue.update();//intitial state, later websocket
AuthenticationVue.update();
}
function setDeviceSerialNumber(state) {
$("#SerialNumberHeader").text(state.SerialNumber);
}
function initPooling() {
window.setInterval(function () {
if (IsRequestDone == true) {
IsRequestDone = false;
if (AuthenticationVue.IsAuthenticated) {
//if authenticated, websocket is used, todo move this loop to authentication
} else {
AuthenticationVue.update();//can't use websocket for unauthenticated devices, because wheter caller has access depends on caller
}
}
}, PoolingPeriod); // repeat forever
}
MotorsVueClass = Vue.component('motors-class', {
template: '#motors-template',
data:
function () {
return {
Index: 0,
ActiveMotor: null,
Motors: []
}
}
,
computed: {
MotorsByTwo: function () {
var chunked = _.chunk(this.Motors, 2);
return chunked;
}
},
methods: {
setActiveMotor: function (index) {
var matchIndex = _.findIndex(this.Motors, function (x) { return x.Index == index; });
this.Index = matchIndex;
this.ActiveMotor = this.Motors[matchIndex];
try {
$("#selectMotorDiv").panel("close"); //todo nasty, IE does not like at all and even does not work in IE
} catch (err) { }
},
moveMotorRelative: function (steps) {
makeCorsRequest('Motors/TargetPositionRelative?id=' + this.ActiveMotor.Index.toString(), 'PUT', steps.toString(), doNothing);
},
moveMotorAbsolute: function (position) {
//named positions use steps
makeCorsRequest('Motors/TargetPosition?id=' + this.ActiveMotor.Index.toString(), 'PUT', position.toString(), doNothing);
},
constructMotors: function (state) {
this.ActiveMotor = null;
this.Motors.length = 0;
for (i = 0; i < state.Motors.length; i++) {
var m = state.Motors[i];
this.Motors.push(m);
}
makeCorsRequest('GeneralInformation/DefaultUserPreferences', 'GET', "", this.setUserPrefernces);
},
setUserPrefernces: function (state) {
for (var i = 0; i < state.Motors.length; i++) {
var m = state.Motors[i];
var matches = _.filter(this.Motors, function (x) { return x.Index == m.Index; });
for (var k = 0; k < matches.length; k++) {
//find matching motor
var matchingMotor = matches[k];
matchingMotor.StepSize = m.SliderStepSize;
if (m.ShowMotorAs == null) {
matchingMotor.ShowNamed = false;
} else {
matchingMotor.ShowNamed = m.ShowMotorAs != "0";// todo C# serializes enum as int
}
}
}
for (i = 0; i < this.Motors.length; i++) {
var m = this.Motors[i];
if (m.StepSize == null) m.StepSize = state.MotorSliderStepSizeFallback;//not all motors may have explicit step size
if (m.ShowNamed == null) m.ShowNamed = false;
if (m.ShowNamed) {
console.log("show motor " + m.Index + " with name positions: " + m.NamedPositions.length);
}
}
this.setActiveMotor(this.Motors[0].Index);
},
update: function () {
makeCorsRequest('Motors/', 'GET', "", this.constructMotors);
}
}
})
ShutterVueClass = Vue.component('shutter-class', {
template: '#shutter-template',
data:
function () {
return {
IsInterlockOpen: false,
IsShutterOpen: true
}
}
,
methods: {
switchShutter: function () {
if (this.IsShutterOpen) {
makeCorsRequest('ShutterInterlock/CloseShutter', 'PUT', "", doNothing);
} else {
LastPerfCall = performance.now();
makeCorsRequest('ShutterInterlock/OpenShutter', 'PUT', "", doNothing);
}
},
updateShutterState: function (state) {
this.IsInterlockOpen = state.IsInterlockOpen;
this.IsShutterOpen = state.IsShutterOpen;
},
update: function () {
makeCorsRequest('ShutterInterlock/State', 'GET', "", this.updateShutterState);
}
}
})
PublicAPIVueClass = Vue.component('PublicAPI-class', {
template: '#PublicAPI-template',
data:
function () {
return {
ShutterControl: "",
WavelengthControl: "",
MotorsControl: "",
SmoothWavelengthSetter: "",
Authentiction: "",
SavedMotorPositions: ""
}
}
,
methods: {
getUrlStart: function () {
var urlStart = window.location.origin;
var portIndex = urlStart.lastIndexOf(":");
if (portIndex > 5) {
urlStart = urlStart.substring(0, portIndex);
}
var fullUrl = urlStart + ":" + RestUrl;
return fullUrl;
},
setUrls: function () {
var u = this.getUrlStart();
this.ShutterControl = u + "PublicAPI/ShutterInterlock/help"
this.WavelengthControl = u + "PublicAPI/Optical/WavelengthControl/help"
this.MotorsControl = u + "PublicAPI/Motors/help"
this.SmoothWavelengthSetter = u + "PublicAPI/SmoothWavelengthSetter/help"
this.Authentiction = u + "PublicAPI/Authentication/help"
this.SavedMotorPositions = u + "PublicAPI/SavedMotorPositions/help"
},
openWavelengthLink: function () {
this.openLink(this.WavelengthControl);
},
openShutterLink: function () {
this.openLink(this.ShutterControl);
},
openAuthenticationLink: function () {
this.openLink(this.Authentiction);
},
openDocumentationLink: function () {
this.openLink("http://topas4api.lightcon.com/current/index.html?utm_source=Software&utm_medium=Topas4WebApp");
},
openMotorsLink: function () {
this.openLink(this.MotorsControl);
},
openSmoothSetterLink: function () {
this.openLink(this.SmoothWavelengthSetter);
},
openSavedMotorPositionsLink: function () {
this.openLink(this.SavedMotorPositions);
},
openLink: function (url) {
var win = window.open(url, '_blank');
win.focus();
}
}
})
MessagesVueClass = Vue.component('messages-class', {
template: '#messages-template',
data:
function () {
return {
IsInterlockOpen: false,
IsShutterOpen: true,
MessagesMarkdown: ""
}
}
,
methods: {
switchShutter: function () {
if (this.IsShutterOpen) {
makeCorsRequest('ShutterInterlock/CloseShutter', 'PUT', "", doNothing);
} else {
makeCorsRequest('ShutterInterlock/OpenShutter', 'PUT', "", doNothing);
}
},
updateShutterState: function (state) {
this.IsInterlockOpen = state.IsInterlockOpen;
this.IsShutterOpen = state.IsShutterOpen;
},
messagesAreDone: function () {
var options = { RestoreShutter: true };
makeCorsRequest('Optical/WavelengthControl/FinishOutputSettingAfterUserActions', 'PUT', JSON.stringify(options), doNothing);
setHash('#main');
},
update: function () {
makeCorsRequest('ShutterInterlock/State', 'GET', "", this.updateShutterState);
}
}
})
WavelengthVueClass = Vue.component('wavelength-class', {
template: '#wavelength-template',
data:
function () {
return {
IsWavelengthSettingInProgress: false,
ChangesAreMine: false,
Wavelength: 0,
LastWavelength: -9
}
}
,
methods: {
setWavelength: function () {
var data = { Wavelength: this.Wavelength, Interaction: "*" };
this.LastWavelength = this.Wavelength;
makeCorsRequest('Optical/WavelengthControl/Output', 'PUT', JSON.stringify(data), this.checkSetWavelengthOk);
this.ChangesAreMine = true;
},
setWavelengthRelative: function (stepSize) {
this.Wavelength += stepSize;
this.setWavelength();
},
updateWavelengthState: function (state) {
this.IsWavelengthSettingInProgress = state.IsWavelengthSettingInProgress;
if (this.LastWavelength != state.Wavelength) {//if user is entering wavelength Vue value will be different and we will revert
this.Wavelength = state.Wavelength;
}
this.LastWavelength = state.Wavelength;
if (this.ChangesAreMine) {
if (state.IsWavelengthSettingInProgress == false || state.IsWaitingForUserAction) {
this.ChangesAreMine = false;
}
this.checkWavelength(state);
}
},
toHtml: function (markdown) {
var md = window.markdownit();
return md.render(markdown);
},
checkSetWavelengthOk: function (answer) {
if (answer.IsSuccess == false) {
this.update();
}
},
checkWavelength: function (state) {
if (state.IsWaitingForUserAction) {
var md = window.markdownit();
var newM = _.filter(state.Messages, 'IsNew');
var nonHiddenNewM = _.filter(newM, function (x) { return x.Text.startsWith("//") == false; });//filter off hidden messages
var hiddenMessages = _.filter(newM, function (x) { return x.Text.startsWith("//") == true; });
if (nonHiddenNewM.length > 0) {
var messages = _.map(nonHiddenNewM, 'Text');
var markdowns = _.map(messages, this.toHtml);
var result = markdowns.join("
");
result = result.substr(3, result.length - 3 - 5);
setHash('#messages');
MessagesVue.MessagesMarkdown = result;
console.log("messages are: " + this.MessagesMarkdown);
//var mDiv = document.getElementById("MessagesInnerDiv");
//while (mDiv.firstChild) {
// mDiv.removeChild(mDiv.firstChild);
//}
//var newdiv = document.createElement("div");
//newdiv.innerHTML = "whatever";
//mDiv.appendChild(newdiv);
//$("#MessagesInnerDiv").empty().append(result);//does not work on IE11
} else if (hiddenMessages.length > 0) {
//copy-paste from messages Vue
var options = { RestoreShutter: true };
makeCorsRequest('Optical/WavelengthControl/FinishOutputSettingAfterUserActions', 'PUT', JSON.stringify(options), doNothing);
}
//if (result.IsSuccess && result.Messages.Any (x => x.IsNew)) {
// UserActionsRequiredWavelengthCalculationResult = result;
//}
this.update();
}
},
update: function () {
makeCorsRequest('Optical/WavelengthControl/Output', 'GET', "", this.updateWavelengthState);
},
}
})
AuthenticationVueClass = Vue.component('authentication-class', {
template: '#authentication-template',
data:
function () {
return {
IsAuthenticated: false,
IsAuthenticationInProgress: false,
IsWaitingForAuthenticationToStart: false,
AuthenticationProgress: 0
}
}
,
methods: {
startAuthentication: function () {
this.IsWaitingForAuthenticationToStart = true;
makeCorsRequest('Authentication/StartAuthenticationByInterlock', 'POST', "", doNothing);
window.setTimeout(this.update, 200);
},
update: function () {
makeCorsRequest('Authentication/AuthenticationStatus', 'GET', "", this.updateAuthentication);
},
updateAuthentication: function (state) {
this.IsAuthenticated = state.CallerHasAccess;
this.IsAuthenticationInProgress = state.IsAuthenticationInProgress;
if (state.IsAuthenticationInProgress) {
this.IsWaitingForAuthenticationToStart = false;
window.setTimeout(this.update, 80);//update often, so that bar fills up nicely
} else {
}
this.AuthenticationProgress = state.AuthenticationProgressPercent;
var authSection = '#authenticate';
if (this.IsAuthenticated == false && window.location.hash != authSection) {
setHash(authSection);
return;
} else if (this.IsAuthenticated == true && window.location.hash == authSection) {
setHash('#main');
return;
}
}
}
})
ShutterVue = new ShutterVueClass({ el: '#shutterDiv' });
WavelengthVue = new WavelengthVueClass({ el: '#wavelengthDiv' });
AuthenticationVue = new AuthenticationVueClass({ el: '#authenticationDiv' });
MessagesVue = new MessagesVueClass({ el: '#messagesDiv' })
MotorsVue = new MotorsVueClass({ el: '#motorsDiv' });
PublicAPIVue = new PublicAPIVueClass({ el: '#PublicAPIDiv' });
PublicAPIVue.setUrls();
// Create the XHR object.
function createCORSRequest(method, url) {
var xhr = new XMLHttpRequest();
var urlWithRandom = url;
if (method == "GET") {
if (url.indexOf("?") == -1) {
urlWithRandom = url + "?time=" + new Date().getTime();
} else {
urlWithRandom = url + "&time=" + new Date().getTime();
}
}
if ("withCredentials" in xhr) {
// XHR for Chrome/Firefox/Opera/Safari.
xhr.open(method, urlWithRandom, true);
} else if (typeof XDomainRequest != "undefined") {
// XDomainRequest for IE.
xhr = new XDomainRequest();
xhr.open(method, urlWithRandom);
} else {
// CORS not supported.
xhr = null;
alert("Can't make Cors Ajax request. Please use another browser.");
setOffline();
}
xhr.timeout = 4000;
return xhr;
}
// Helper method to parse the title tag from the response.
function getTitle(text) {
return text.match('